Gebruiker:Dnlweijers/common.js

Uit Wikipedia, de vrije encyclopedie

Opmerking: nadat u de wijzigingen hebt gepubliceerd is het wellicht nodig uw browsercache te legen.

  • Firefox / Safari: houd Shift ingedrukt terwijl u op Vernieuwen klikt of druk op Ctrl-F5 of Ctrl-R (⌘-Shift-R op een Mac)
  • Google Chrome: druk op Ctrl-Shift-R (⌘-Shift-R op een Mac)
  • Internet Explorer / Edge: houd Ctrl ingedrukt terwijl u op Vernieuwen klikt of druk op Ctrl-F5
  • Opera: druk op Ctrl-F5.
//<nowiki>
importScript( 'User:Jeph_paul/common.js' ); importStylesheet( 'User:Jeph_paul/common.css' );
//{{subst:Lusc|User:GhostInTheMachine/TalkHelper.js}}
$(document).ready(function() {
	switch (mw.config.get("skin")) {
		case "timeless": $("#simpleSearch").append('<input type="button" id="googleSearchButton" class="searchButton" title="Search using Google">');
		$("#simpleSearch").append('<input type="button" id="bingSearchButton" class="searchButton" title="Search using Bing">');
		$("#mw-searchButton").css("right", "7.5em");
		$("#searchButton").css("right", "7.5em");
		$("#googleSearchButton").css({
			    position:  "absolute",
			    top: 0,
			    margin: 0,
			    padding: 0,
			    right: "4.5em",
			    width: "2.5em",
			    height: "2.5em",
			    "text-indent": "-99999px",
			    border: "0",
			    "background-color": "transparent",
			    "background-repeat": "no-repeat",
			    "background-image": 'url("https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/21px-Google_%22G%22_Logo.svg.png")',
			    "background-position": "center 40%",
			    "box-shadow": "none",
			    "cursor": "pointer"
		});
		$("#bingSearchButton").css({
			    position: "absolute",
			    top: 0,
			    margin: 0,
			    padding: 0,
			    right: "1.5em",
			    width: "2.5em",
			    height: "2.5em",
			    "text-indent": "-99999px",
			    border: "0",
			    "background-color": "transparent",
			    "background-repeat": "no-repeat",
			    "background-image": 'url("https://upload.wikimedia.org/wikipedia/commons/thumb/0/07/Bing_favicon.svg/21px-Bing_favicon.svg.png")',
			    "background-position": "center 40%",
			    "box-shadow": "none",
			    "cursor": "pointer"
		});
		break;
		case "vector": 
		$("#simpleSearch").append('<input type="button" id="googleSearchButton" class="searchButton" title="Search using Google">');
		$("#simpleSearch").append('<input type="button" id="bingSearchButton" class="searchButton" title="Search using Bing">');
		$("#mw-searchButton").css("right", "50px");
		$("#searchButton").css("right", "50px");
		$("#googleSearchButton").css({
		    'background-color': "transparent",
		    position: "absolute",
		    top: '1px',
		    bottom: '1px',
		    right: '30px',
		    'min-width': '12px',
		    width: '1.84615385em',
		    border: 0,
		    padding: 0,
		    cursor: 'pointer',
		    'font-size': '0.8125em',
		    direction: 'ltr',
		    'text-indent': '-99999px',
		    'white-space': 'nowrap',
		    overflow: 'hidden',
		    'z-index': 1,
		    'background-image': 'url("https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/15px-Google_%22G%22_Logo.svg.png")',
		    'background-position': 'center center',
		    'background-repeat': 'no-repeat'
		});
		$("#bingSearchButton").css({
		    'background-color': "transparent",
		    position: "absolute",
		    top: '1px',
		    bottom: '1px',
		    right: '10px',
		    'min-width': '12px',
		    width: '1.84615385em',
		    border: 0,
		    padding: 0,
		    cursor: 'pointer',
		    'font-size': '0.8125em',
		    direction: 'ltr',
		    'text-indent': '-99999px',
		    'white-space': 'nowrap',
		    overflow: 'hidden',
		    'z-index': 1,
		    'background-image': 'url("https://upload.wikimedia.org/wikipedia/commons/thumb/0/07/Bing_favicon.svg/15px-Bing_favicon.svg.png")',
		    'background-position': 'center center',
		    'background-repeat': 'no-repeat'
		});
		break;
	}
	$("#googleSearchButton").click(function(e) {
		e.preventDefault();
		window.location.href = "https://www.google.com/search?q=site:" + encodeURI(window.location.host + " " + $("#searchInput").val());
	});
	$("#bingSearchButton").click(function(e) {
		e.preventDefault();
		window.location.href = "https://www.bing.com/search?q=site:" + encodeURI(window.location.host + " " + $("#searchInput").val());
	});
});
//{{subst:js|User:Theopolisme/Scripts/autocompleter.js}}
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 <b>$1</b>',
	'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 [[Special:Contributions/$1|$1]] ([[User talk:$1|talk]]) 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': '[Previously rolled back this editor] $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 [[$2|badwords]]:'
		// FIXME: Remove this hack once [[bugzilla:47395]] 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:        'Wikipedia:Lists_of_common_misspellings/For_machines',
	liveSpellcheckPage:   'User:Lupin/Live_spellcheck',
	safePages:            '([Ww]ikipedia:([Ii]ntroduction|[Ss]andbox|[Tt]utorial[^/]*/sandbox)|[Tt]emplate:(X[1-9]|Template sandbox))',
	linkify:              true,
	updateSeconds:        30,
	// FIXME: Use <ul> and add a border to each <li>'s
	outputSeparator:      '<hr>',
	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} [bundle.onSuccess] (xmlhttprequest, bundle) Function to be executed when the download is done
 * @param {Function} [bundle.onFailure] (xmlhttprequest, bundle) Function to be executed when the download fails
 * @param {string} [bundle.otherStuff] 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 + '/wiki/';
	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 class="diff-marker">\+<\/td>\s*<td\b[^>]*>\s*<div>\s*(.*?)\s*<\/div>\s*<\/td>/gi;


// processBadWords: generate the badWords RegExp from
// the downloaded data.
// d is the xmlhttprequest object from the download
recent2.processBadWords=function(d) {
	var data=d.responseText.split('\n');
	var phrase=[];
	var string=[];
	for (var i=0; i<data.length; ++i) {
		var s=data[i];

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

		// lines beginning and ending with a (back-)slash (and possibly trailing
		// whitespace) are treated as regexps
		if (/^([\\\/]).*\1\s*$/.test(s)) {
			var isPhrase=(s.charAt(0)=='/');
			// remove slashes and trailing whitespace
			s=s.replace(/^([\\\/])|([\\\/]\s*$)/g, '');
			// escape opening parens: ( -> (?:
			s=s.replace(/\(?!\?/g, '(?:');
			// check that s represents a valid regexp
			try { 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( $( '<pre>' ).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('|') + ')|(^|[^/\\w])(' + phrase.join('|') + ')(?![/\\w]))', '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[i].indexOf('<textarea id="mw-input-wpTitles"') !== -1) {
			if (inList && lines[i].indexOf('</textarea>') !== -1) {
				window.watchlist =  watchlist;
				return;
			}
			if (!inList) {
				inList = true;
				article = lines[i].replace (/^.*>/, '');
			} else {
				article=lines[i];
			}
			watchlist[article] = true;
		}
	}
};

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

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

recent2.processSpelldict=function(req, bundle) {
	var spelldict={};
	var lines=req.responseText.split('\n');
	var a=[];
	// Parse each line, remove unnecessary spaces and discard those lines which have an invalid format
	for (var i=0; i<lines.length; ++i) {
		var split=lines[i].split('->');
		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(mw.util.escapeRegExp(split[0]));
	}
	window.spelldict=spelldict;
	window.spellRe=new RegExp('\\b(' + a.join('|') + ')\\b', 'i');
};

recent2.feed=recent2.scriptPath + 'index.php?title=Special:Recentchanges&feed=rss&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( mw.msg( 'avt-unknown-position', position ) );
			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=[];
var greyFont='<span style="color:#777">';

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);
	}
	if (typeof(recent2.sysopRegExp) == 'undefined') {
		if (! recent2.gettingSysops) {
			recent2.getSysops();
		}
		return setTimeout(function(){grabRecentChanges(feed);}, 500);
	}

	var pos=recent2.outputPosition;
	var output;
	var status;
	if (pos=='top') {
		output=newOutputDiv('recent2.lines', pos);
		status=newOutputDiv('recent2.status', pos);
	} else {
		status=newOutputDiv('recent2.status', pos);
		output=newOutputDiv('recent2.lines', pos);
	}
	status.style.borderStyle='solid';
	status.style.borderColor='orange';
	status.innerHTML=greyFont + mw.msg( 'avt-updating', recent2.count ) + '</span>';

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

window.feedFailed=function(x,bundle) {
	try {
		bundle.status.innerHTML+=greyFont + mw.msg( 'avt-failed', x.statusText ) + '</span>';
	} catch (err) {
		// FIXME: Is this even possible?
		bundle.status.innerHTML+=greyFont + mw.msg( 'avt-failed-badly', err ) + '</span>';
	}
	return true;
};

recent2.newWindows=true;

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

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

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

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

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

	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('</tr>').join('</tr>\n');
	var editSummary=diffText.replace( /^<p>(.*?)<\/p>[\s\S]*/, '$1');
	var editor=getFirstTagContent(items[i], 'creator') || getFirstTagContent(items[i], 'dc:creator');

	if (recent2.ignore_my_edits && mw.config.get('wgUserName')==editor) { return; }

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

	if (recent2.filter_anonsOnly && !mw.util.isIPAddress(editor)) {
		nextChangeSoon(true);
		return;
	}

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

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

	if (recent2.hideNonArticles) {
		var nsName=articleTitle.replace(/:.*/, '').replace( / /g, '_' ).toLowerCase();
		if (mw.config.get('wgNamespaceIds')[nsName] &&
	( ( recent2.showTemplates && mw.config.get('wgNamespaceIds')[nsName] !== /* Template */ 10 ) ||
		! 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;
	}

	// 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;
		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) {
			badWords.lastIndex=0;
			var reMatch;
			while (diffCell && (reMatch=badWords.exec(diffCell[1]))) {
				var badWord=reMatch[2] || reMatch[4] || reMatch[6] || '';
				// avoid legit article title occurrences
				if (articleTitle.toLowerCase().indexOf(badWord.toLowerCase())<0) {
					badWord=badWord.replace(/^\s+|\s+$/g, '');
					if (badWord!=='') {
						matchesPlain+=badWord+', ';
						badWord=badWord.replace(/([^\w ])/g, '\\$1');
						matchesRe+=badWord+'|';
					}
				}
			}
			matchesRe=matchesRe.replace(/\|$/, '');
			matchesPlain=matchesPlain.replace(/, $/, '');
			if (!previousVandal && matchesRe==='') { nextChangeSoon(); return; }
			// highlighting
			var highlighted=diffCell && diffCell[1];
			if (matchesRe) {
				highlighted=highlighted.replace(new RegExp('('+matchesRe+')', 'g'),
					'<span style="background-color: #FF6">$1</span>');
			}
			articleTitle=getFirstTagContent(items[i], 'title');
			// linkify
			highlighted=recent2.doLinkify(highlighted);
			diffText=recent2.doLinkify(diffText);

			if (previousVandal) {
				matchesPlain = mw.msg( 'avt-rolled-back', 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);
			// .replace(/^\s*/, '');
			var misspelling = splMatch[1];
			var badWord = '<a href=\'javascript:recent2.correctSpelling("' + articleTitle.split('\'').join('%27') +
				'","'+misspelling.split('\'').join('%27')+'")\'>'+ misspelling + '</a>';
			diffText = diffText.replace(new RegExp('('+misspelling+')', 'gi'),
				'<span style="background-color: #FF6">$1</span>');
			// 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 {
		article=getFirstTagContent(items[i], 'link');
		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
	// - 1;
	processRecentChanges.lastDate=recent2.latest;
	var statusTail=greyFont + mw.msg( 'avt-done', formatTime( recent2.latest ) ) + '</span>';
	if (processRecentChanges.id > recent2.initialId) {
		statusTail+=' <a href="javascript:showHideDetailRange(' + recent2.initialId + ',' +
			processRecentChanges.id  + ')">' + mw.msg( 'avt-toggle-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 + ')">' + mw.msg( 'avt-remove-output' ) + '</a>';
	if (recent2.wait_for_output) {
		statusTail += ' | <a href="javascript:recent2.unpauseOutputOnce()">' + mw.msg( 'avt-show-new' ) + '</a>';
	}
	statusTail+='<br>';
	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<span style="color:red">]] badword</span> blah blah')
	// gives '<a href=/wiki/123 ... >[[123<span style="color:red">]]</a> badword</span> blah blah'
	// and the browser closes the <span> inside the </a>, so the badword is not red!

	txt=txt.replace(/((\[\[)([^\|\[\]\{\}\n]*)([^\]\n]*)(\]\]))/g, function (p,p1,p2,p3) {
		p3=p3.replace(/<[^>]*>/g, '');
		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( mw.msg( 'avt-missing-div', start ) );
		return;
	}
	// hide
	var state=false;
	if (div.style.display=='none') {
		// show
		state=true;
	}
	for (var i=start; i<end; ++i) {
		showHideDetail(i, true, state);
	}
};

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

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

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(recent2.sysopRegExp != 'undefined')) {
		sysop=recent2.sysopRegExp.test(bundle.editor);
	}
	var lastDiffPage=bundle.article + '&diff=cur&oldid=prev';
	bundle.url=lastDiffPage;
	saveBundle(bundle);

	var div='';
	var group='';
	if (window.vandals[bundle.editor]) {
		if (window.vandals[bundle.editor] > 0) {
			div='<div style="background-color:' + vandalColour(bundle.editor) + '">';
		}
	}
	else if (sysop) {
		group = ' <i>(Admin)</i>';
		if (recent2.hide_sysop_edits) {
			div='<div class="sysop_edit_line" style="display: none;">';
		}
		else {
			div='<div class="sysop_edit_line">';
		}
	}
	return(
		div +
		'<li>' +
		'[<a href="javascript:showHideDetail(' + bundle.id + ')" id="showdiff_link_' + bundle.id + '">' + mw.msg( 'avt-show' ) + '</a>] ' +
		formatTime(bundle.timestamp) + ' ' +
		// latest + ' ' + processRecentChanges.lastDate + ' ' +
		'(' + linkmaker( lastDiffPage, mw.msg( 'avt-last' ) ) + ')' +
		' (' + linkmaker( bundle.article + '&action=history', mw.msg( 'avt-hist' ) ) + ')' +
		' ' + linkmaker(bundle.article, bundle.articleTitle) +
		( bundle.badWord ? mw.msg( 'avt-matched', bundle.badWord ) : '') + ' . . ' +
		linkmaker(recent2.articlePath + 'User:' + bundle.editor, bundle.editor) +
		group + ' (' +
		linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor, mw.msg( 'avt-talk' ) ) + ' | ' +
		linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor + '?action=edit' +
			'&avtautoedit=s♫$♫\\n{'+'{subst:uw-test1|' + bundle.articleTitle +
			'}}%20~~' + '~~♫&avtautosummary=Your%20recent%20edits%20to%20[[' + bundle.articleTitle + ']]',
			mw.msg( 'avt-uw-test' ) ) + ' | ' +
		linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor + '?action=edit' +
			'&avtautoedit=s♫$♫\\n{'+'{subst:uw-vandalism1|' + bundle.articleTitle +
			'}}%20~~' + '~~♫&avtautosummary=Your%20recent%20edits%20to%20[[' + bundle.articleTitle + ']]',
			mw.msg( 'avt-uw-vand' ) )     + ' | ' +
		linkmaker(recent2.articlePath + 'Special:Contributions/' + bundle.editor, mw.msg( 'avt-contribs' ) ) + ' | ' +
		linkmaker(recent2.articlePath + 'Special:Blockip/' + bundle.editor, mw.msg( 'avt-block' ) ) + ') . . ' +
		( bundle.summary ? '<i>('+bundle.summary+')</i> . . ' : '') +
		'[<a href="javascript:tryRollback(' + bundle.id + ')" class="recent2_rollback">' + mw.msg( 'avt-rollback' ) + '</a>]' +
		'<p><div id="diff_div_' + bundle.id + '" style="display: none">' +
		'</div></li>' +
		( div ? '</div>' : '')
	);
};

recent2.correctSpelling=function (article, badword) {
	var url=recent2.articlePath + article + '?action=edit&avtautoclick=wpDiff&avtautominor=true';
	var wl=badword.toLowerCase();
	var cor=spelldict[wl];
	var c0, wl0, b;
	if (!cor|| !cor.length) {
		alert( mw.msg( 'avt-entry-not-found', wl ) );
		return;
	}
	if (cor.length > 1) {
		var q= mw.msg( 'avt-select-correction' );
		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 += '&avtautosummary=Correcting%20spelling:%20' + wl + '->' + cor;
	url += '&avtautoedit=';
	c0=cor.charAt(0);
	wl0 = wl.charAt(0);
	b='\\b';
	// s♫\bexample\b♫test♫g;
	url += ['s', b + wl + b, cor, 'g;'].join('♫');
	wl=wl0.toUpperCase() + wl.substring(1);
	cor=c0.toUpperCase() + cor.substring(1);
	// s♫\bExample\b♫Test♫g;
	url += ['s', b + wl + b, cor, 'g;'].join('♫');
	wl=wl.toUpperCase();
	cor=cor.toUpperCase();
	// s♫\bEXAMPLE\b♫TEST♫g;
	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( mw.msg( 'avt-error-no-bundle' ) );
		return null;
	}
	var vandal=b.editor;
	if (window.vandals[vandal]===undefined) {
		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) {
		// FIXME: This will fail if wgScript is not "/w/index.php" or if the link has a class before href, etc...
		// Use API instead?
		var rollRe=/<a href="(\/w\/index\.php[^"]*?action=rollback[^"]*?from=([^&]*)[^"]*?)".*?(<span class="comment">(.*?)<\/span>)?/;
		// 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( mw.msg( 'avt-error-no-rollback-link' ) );
			return;
		}
		var lastEditor=match[2].split('+').join(' ');
		var lastSummary=match[4] || '';
		if (lastEditor != vandal) {
			var summary=lastSummary.replace( /<[^>]*?>/g, '' );
			if (!summary) {
				summary=lastSummary;
			}
			alert( mw.msg( 'avt-error-unable-to-rollback', b.articleTitle, vandal, lastEditor, summary ) );
			return;
		}
		var rollbackUrl=match[1].split('&amp;').join('&');
		recent2.openBackgroundWindow(rollbackUrl);
	};
	var onFailure = function(x,bundle) {
		alert( mw.msg( 'avt-error-HTTP-rollback', bundle.url, x.statusText ) );
		return true;
	};
	recent2.download({ url:b.url, onSuccess: onSuccess, id: b.id, onFailure:onFailure});
};

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

recent2.tryNonAdminRollback=function(id) {
	var b=recent2.getBundleVandal(id);
	if (!b) { return; }
	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); };
	// fixme: onFailure
	recent2.download({ url: url, onSuccess: onSuccess, id: b.id});
};

recent2.processHistoryQuery=function(x,downloadBundle, bundle) {
	var json=x.responseText, edits;
	try {
		// FIXME: Eval is evil
		eval('var o='+json);
		// FIXME: Use indexpageids=1 on API
		edits=recent2.anyChild(o.query.pages).revisions;
	} catch ( someError ) {
		alert( mw.msg( 'avt-error-JSON', json.substring( 0, 200 ) ) );
		return;
	}
	var i;
	for (i=0; i<edits.length; ++i) {
		if (edits[i].user!=bundle.editor) { break; }
	}
	if (i===0) {
		alert( mw.msg( 'avt-error-unable-to-rollback', bundle.articleTitle, bundle.editor, edits[0].user, edits[0].comment ) );
		return;
	}
	if (i==edits.length) {
		alert( mw.msg( 'avt-rollback-aborted', bundle.editor, bundle.articleTitle ) ); return;
	}
	var prevEditor=edits[i].user;
	var prevRev=edits[i].revid;
	var summary= mw.msg( 'avt-reverted-edits', escape(bundle.editor), escape(prevEditor) );
	summary=summary.split(' ').join('%20');
	var url=bundle.article + '&action=edit&avtautosummary=' + summary + '&oldid=' + prevRev +
		'&avtautoclick=wpSave&avtautominor=true&avtautowatch=false';
	recent2.openBackgroundWindow(url);
};

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 = mw.msg( 'avt-hide' );
	} else {
		div.style.display='none';
		lk.innerHTML = mw.msg( 'avt-show' );
	}

};

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 === 0) {
		// 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);

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

	// create controls
	recent2.newCheckbox( mw.msg( 'avt-ignore-talk-pages' ), !recent2.show_talkpages,
			function() { recent2.show_talkpages=!this.checked; }, 'talk');
	recent2.newCheckbox( mw.msg( 'avt-ignore-outside-main' ), recent2.hideNonArticles,
			function() { recent2.hideNonArticles = this.checked; }, 'hidenonarticles');
	recent2.newCheckbox( mw.msg( 'avt-except-templates' ), recent2.showTemplates,
			function() { recent2.showTemplates = this.checked; }, 'showtemplates');
	recent2.newCheckbox( mw.msg( 'avt-expand-content' ), recent2.autoexpand,
			function() { recent2.autoexpand = this.checked; }, 'autoexpand');
	recent2.newCheckbox( mw.msg( 'avt-only-unchanged' ), recent2.delay_preset,
			function() { recent2.delay = (this.checked) ? 4 : 0; }, 'delayby4');
	recent2.newCheckbox( mw.msg( 'avt-non-admin-rollback' ), recent2.non_admin_rollback,
			function() { recent2.non_admin_rollback = this.checked; }, 'nonadminrollback');
	recent2.newCheckbox( mw.msg( 'avt-ignore-my-edits' ), recent2.ignore_my_edits,
			function() { recent2.ignore_my_edits = this.checked; }, 'ignoremyedits');
	recent2.newCheckbox( mw.msg( 'avt-ignore-safe-pages' ), recent2.ignore_safe_pages,
			function() { recent2.ignore_safe_pages = this.checked; }, 'ignoresafepages');
	recent2.newCheckbox( mw.msg( 'avt-ignore-sysop-edits' ), recent2.hide_sysop_edits,
			function() { recent2.hide_sysop_edits = this.checked; hideSysopEdits(recent2.hide_sysop_edits); }, 'hidesysopedits');

	var b=document.createElement('input');
	b.type='button';
	b.value = mw.msg( 'avt-pause' );
	b.onclick=function(){
		b.value = recent2.paused ? mw.msg( 'avt-pause' ) : mw.msg( 'avt-resume' );
		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( mw.msg( 'avt-continue-question' ) ) ) {
			return;
		}
		recent2.count %= iterations; loopRecentChanges(url, iterations);
	}, recent2.updateSeconds * 1000);
};

window.marvin=function() {

	// check if user is a sysop
	recent2.userIsSysop = $.inArray('sysop', mw.config.get( 'wgUserGroups' )) !== -1;

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

	// setup checkboxes
	recent2.controlUI();

	// start fetching recent changes
	mw.loader.using( 'mediawiki.diff.styles', function(){
		loopRecentChanges(recent2.feed, 200);
	} );
};

// get the full sysop list in chunks
recent2.getSysops = function(startUser) {
	recent2.gettingSysops = true;
	var param = '';
	if (typeof(startUser) == 'string') {
		param = '&aufrom=' + encodeURIComponent(startUser);
	}
	recent2.download({
		url: recent2.scriptPath + 'api.php?action=query&list=allusers&augroup=sysop&aulimit=' + recent2.apiAulimit + '&format=json' + param,
		onSuccess: recent2.processSysops,
		onFailure: function() { setTimeout(recent2.getSysopList, 15000); return true;}
	});
};

recent2.sysopList = '';
recent2.processSysops = function(s) {
	var json = s.responseText, users;
	try {
		// FIXME: Eval is evil
		eval('var o = ' + json);
		users = o.query.allusers;
	}
	catch(someError) {
		alert( mw.msg( 'avt-error-sysop-list', json.substring( 0, 400 ) ) );
		return;
	}
	for (var i = 0; i < users.length; i++) {
		if (recent2.sysopList !== '') {
			recent2.sysopList += '|';
		}
		recent2.sysopList += users[i].name.replace(/(\W)/g, '\\$1');
	}
	if (users.length < recent2.apiAulimit) {
		recent2.sysopRegExp = new RegExp( '\\b(' + recent2.sysopList + ')\\b' );
	}
	else {
		recent2.getSysops(users[recent2.apiAulimit - 1].name);
	}
	return;
};

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

window.addMarvin=function() {
	mw.util.addPortletLink( 'p-tb', mw.util.getUrl( recent2.filterPage ),
		mw.msg( 'avt-filter-rc' ), 'toolbox_filter_changes');
	mw.util.addPortletLink( 'p-tb', mw.util.getUrl( recent2.allRecentPage ),
		mw.msg( 'avt-all-rc' ), 'toolbox_all_changes');
	mw.util.addPortletLink( 'p-tb', mw.util.getUrl( recent2.recentIPPage ),
		mw.msg( 'avt-ip-rc' ), 'toolbox_IP_edits');
	mw.util.addPortletLink( 'p-tb', mw.util.getUrl( recent2.monitorWatchlistPage ),
		mw.msg( 'avt-watched-rc' ), 'toolbox_watchlist_edits');
	mw.util.addPortletLink( 'p-tb', mw.util.getUrl( recent2.liveSpellcheckPage ),
		mw.msg( 'avt-spelling-rc' ), 'toolbox_spelling');
};

window.maybeStart=function() {
	switch (mw.config.get('wgPageName')) {
	case recent2.filterPage:
		recent2.filter_badwords=true;
		break;
	case recent2.allRecentPage:
		recent2.filter_badwords=false;
		break;
	case recent2.recentIPPage:
		recent2.filter_anonsOnly=true;
		break;
	case recent2.monitorWatchlistPage:
		recent2.filter_watchlist=true;
		break;
	case recent2.liveSpellcheckPage:
		recent2.filter_spelling=true;
		break;
	default:
		return;
	}
	setTimeout(marvin, 1000);
};

/**
 * autoedit code, streamlined from User:Lupin/autoedit.js, added autowatch
 * User:Lupin/autoedit.js is no longer needed
 */

recent2.substitute = function(data,cmdBody) {
	// alert('sub\nfrom: ' + cmdBody.from + '\nto: ' + cmdBody.to + '\nflags: ' + cmdBody.flags);
	var fromRe = new RegExp(cmdBody.from, cmdBody.flags);
	return data.replace(fromRe, cmdBody.to);
};

recent2.execCmds = function(data, cmdList) {
	for (var i = 0; i<cmdList.length; ++i) {
		data = cmdList[i].action(data, cmdList[i]);
	}
	return data;
};

recent2.parseCmd = function(str) {
	// returns a list of commands
	if (!str.length) {
		return [];
	}
	var p = false;
	switch (str.charAt(0)) {
	case 's':
		p = recent2.parseSubstitute(str);
		break;
	case 'j':
		p = recent2.parseJavascript(str);
		break;
	default:
		return false;
	}
	if (p) {
		return [p].concat(recent2.parseCmd(p.remainder));
	}
	return false;
};

recent2.unEscape = function(str, sep) {
	return str.split('\\\\').join('\\')
		.split('\\' + sep).join(sep)
		.split('\\n').join('\n');
};


recent2.runJavascript = function(data, argWrapper) {
	// flags aren't used (yet)

	// from the user's viewpoint,
	// data is a special variable may appear inside code
	// and gets assigned the text in the edit box

	// alert('eval-ing ' + argWrapper.code);

	return eval(argWrapper.code);
};

recent2.parseJavascript = function(str) {
	// takes a string like j/code/;othercmds and parses it

	var tmp, code, flags;

	if (str.length<3) {
		return false;
	}
	var sep = str.charAt(1);
	str = str.substring(2);

	tmp = recent2.skipOver(str, sep);
	if (tmp) {
		code = tmp.segment.split('\n').join('\\n');
		str = tmp.remainder;
	} else {
		return false;
	}

	flags = '';
	if (str.length) {
		tmp = recent2.skipOver(str, ';') || recent2.skipToEnd(str, ';');
		if (tmp) {flags = tmp.segment; str = tmp.remainder; }
	}

	return { action: recent2.runJavascript, code: code, flags: flags, remainder: str };
};

recent2.parseSubstitute = function(str) {
	// takes a string like s/a/b/flags;othercmds and parses it

	var from, to, flags, tmp;

	if (str.length<4) {
		return false;
	}
	var sep = str.charAt(1);
	str = str.substring(2);

	tmp = recent2.skipOver(str, sep);
	if (tmp) {
		from = tmp.segment;
		str = tmp.remainder;
	} else {
		return false;
	}

	tmp = recent2.skipOver(str, sep);
	if (tmp) {
		to = tmp.segment;
		str = tmp.remainder;
	} else {
		return false;
	}

	flags = '';
	if (str.length) {
		tmp = recent2.skipOver(str, ';') || recent2.skipToEnd(str, ';');
		if (tmp) {flags = tmp.segment; str = tmp.remainder; }
	}

	return {action: recent2.substitute, from: from, to: to, flags: flags, remainder: str};
};

recent2.skipOver = function(str, sep) {
	var endSegment = recent2.findNext(str, sep);
	if (endSegment<0) {
		return false;
	}
	var segment = recent2.unEscape(str.substring(0, endSegment), sep);
	return {segment: segment, remainder: str.substring(endSegment + 1)};
};

recent2.skipToEnd = function(str, sep) {
	return {segment: str, remainder: ''};
};

recent2.findNext = function(str, ch) {
	for (var i = 0; i<str.length; ++i) {
		if (str.charAt(i) == '\\') {
			i += 2;
		}
		if (str.charAt(i) == ch) {
			return i;
		}
	}
	return -1;
};

var AVTAutoEditLoader = function() {

	if (typeof(window.AVTAutoEdit) != 'undefined') {
		if (window.AVTAutoEdit.alreadyRan) {
			return false;
		}
	} else {
		window.AVTAutoEdit = {};
	}
	window.AVTAutoEdit.alreadyRan = true;
	var editbox, cmdString = mw.util.getParamValue('avtautoedit');
	if (cmdString) {
		try {
			editbox = document.editform.wpTextbox1;
		} catch (dang) { return; }
		var cmdList = recent2.parseCmd(cmdString);
		var input = editbox.value;
		var output = recent2.execCmds(input, cmdList);
		editbox.value = output;
		// wikEd user script compatibility
		if (typeof(wikEdUseWikEd) != 'undefined') {
			if (wikEdUseWikEd === true) {
				/*jshint newcap: false*/
				WikEdUpdateFrame();
				/*jshint newcap: true*/
			}
		}
	}

	var summary = mw.util.getParamValue('avtautosummary');
	if (summary) {
		document.editform.wpSummary.value = summary;
	}

	var minor = mw.util.getParamValue('avtautominor');
	if (minor) {
		switch (minor) {
		case '1':
		case 'yes':
		case 'true':
			document.editform.wpMinoredit.checked = true;
			break;
		case '0':
		case 'no':
		case 'false':
			document.editform.wpMinoredit.checked = false;
		}
	}

	var watch = mw.util.getParamValue('avtautowatch');
	if (watch) {
		switch (watch) {
		case '1':
		case 'yes':
		case 'true':
			document.editform.wpWatchthis.checked = true;
			break;
		case '0':
		case 'no':
		case 'false':
			document.editform.wpWatchthis.checked = false;
		}
	}

	var btn = mw.util.getParamValue('avtautoclick');
	if (btn) {
		if (document.editform && document.editform[btn]) {
			var headings = document.getElementsByTagName('h1');
			if (headings) {
				var div = document.createElement('div');
				var button = document.editform[btn];
				div.innerHTML = '<span style="font-size: 115%; font-weight: bold;">' +
					mw.msg( 'avt-auto-click', button.value ) + '</span>';
				document.title = '(' + document.title + ')';
				headings[0].parentNode.insertBefore(div, headings[0]);
				button.click();
			}
		} else {
			alert( mw.msg( 'avt-auto-click-button-missing', btn ) );
		}
	}
};

$.when( mw.loader.using( [ 'mediawiki.util' ] ), $.ready ).done( function(){
	// onload
	maybeStart();
	addMarvin();
	AVTAutoEditLoader();
} );
mw.loader.load( '//en.wikipedia.org/w/index.php?title=User:MarkS/XEB/live.css&action=raw&ctype=text/css', 'text/css' );

if(typeof XEBPopups== 'undefined')XEBPopups=true;
if(typeof XEBHideDelay== 'undefined')XEBHideDelay=0.5; //Time before the popup disappears after the mouse moves out
if(typeof XEBExtendEditSummary == 'undefined')XEBExtendEditSummary=true; // Is the edit summary extended after a popup

//fills the variable mwCustomEditButtons (s. function in /wikibits.js), with buttons for the Toolbar  
function addCustomButton(imageFile, speedTip, tagOpen, tagClose, sampleText){
mwCustomEditButtons.push({
  "imageFile": imageFile,
  "speedTip": speedTip,
  "tagOpen": tagOpen,
  "tagClose": tagClose,
  "sampleText": sampleText});
}

if (typeof usersignature == 'undefined') var usersignature = '-- \~\~\~\~';

var Isrc='//upload.wikimedia.org/wikipedia/commons/';

// English Wikipedia creates 11 extra buttons which are stored in mwCustomEditButtons
//  rather than mwEditButtons. However, there is no guarantee it will always be 11
//  so we count them here. 
var enExtraButtons=typeof mwCustomEditButtons !== 'undefined' && mwCustomEditButtons.length;


var BDict={
'A':['e/e9/Button_headline2.png','Secondary headline','\n===','===','Secondary headline'],
'B':['1/13/Button_enter.png','Line break','<br />','',''],
'C':['5/5f/Button_center.png','Center','<div style="text-align: center;">\n','\n<\/div>','Centred text'],
'D':['e/ea/Button_align_left.png','Left-Align','<div style="text-align: left; direction: ltr; margin-left: 1em;">\n','\n<\/div>','Left-aligned text'],
'D1':['a/a5/Button_align_right.png','Right-Align','<div style="text-align: right; direction: ltr; margin-left: 1em;">\n','\n<\/div>','Right-aligned text'],
'E':['0/04/Button_array.png','Table','\n{| class="wikitable" \n|- \n| 1 || 2\n|- \n| 3 || 4','\n|}\n',''],
'F':['1/1e/Button_font_color.png','Insert coloured text','<span style="color: ','">Coloured text<\/span>','ColourName'],
'FS':['1/1b/Button_miss_signature.png','Unsigned post','<small>– De voorgaande bijdrage [[Wikipedia:Ondertekening|werd geplaatst]] door [[Gebruiker:','|',']] ([[Overleg gebruiker:','|overleg]]&thinsp;·&thinsp;[[Speciaal:Bijdragen/','|bijdragen]]) date</small>','user name or IP'],
'G':['9/9e/Btn_toolbar_gallery.png','Picture gallery',"\n<gallery>\nImage:","|[[M63]]\nImage:Mona Lisa.jpg|[[Mona Lisa]]\nImage:Truite arc-en-ciel.jpg|Eine [[Forelle ]]\n<\/gallery>",'M63.jpg'],
'H':['7/74/Button_comment.png','Comment',"<!--","-->",'Comment'],
'I1':['6/6a/Button_sup_letter.png','Superscript','<sup>','<\/sup>','Superscript text'],
'I2':['a/aa/Button_sub_letter.png','Subscript','<sub>','<\/sub>','Subscript text'],
'J1':['5/58/Button_small.png','Small','<small>','<\/small>','Small Text'],
'J2':['5/56/Button_big.png','Big text','<big>','<\/big>','Big text'],
'K':['b/b4/Button_category03.png','Category','Category name'],
'L':['8/8e/Button_shifting.png','Insert tab(s)',':','',':'],
'M':['f/fd/Button_blockquote.png','Insert block of quoted text','<blockquote style="border: 1px solid blue; padding: 2em;">\n','\n<\/blockquote>','Block quote'],
'N':['4/4b/Button_nbsp.png','nonbreaking space','&nbsp;','',''],
'O':['2/23/Button_code.png','Insert code','<code>','<\/code>','Code'],
'P':['3/3c/Button_pre.png','Pre formatted Text','<pre>','<\/pre>','Pre formatted text'],
'P1':['9/93/Button_sub_link.png','Insert link to sub-page','[[','/Sub_Page]]','Page'],
'Q':['d/d3/Button_definition_list.png','Insert definition list','\n; ','\n: Item 1\n: Item 2','Definition'],
'R':['7/79/Button_reflink.png','Insert a reference','<ref>','<\/ref>','Insert reference material'],
'R1':['7/79/Button_reflink.png','Start a reference','<ref name="','','Reference name'],
'R2':['9/99/Button_reflink_advanced_2.png','Insert reference material','">','</ref>','Reference material'],
'R3':['1/1a/Button_reflink_advanced_3.png','No reference material','','"/>',''],
'R4':['9/9a/Button_references.png','Reference footer',"\n==Notes==\n<!--See http://en.wikipedia.org/wiki/Wikipedia:Footnotes for an explanation of how to generate footnotes using the <ref(erences/)> tags-->\n<div class=\'references-small\'>\n<references/>\n</div>",'',''],
'S':['c/c9/Button_strike.png','Strikeout','<s>','<\/s>','Struck out text'],
'T':['e/eb/Button_plantilla.png','Template','{{','}}','Template name'],
'TS':['a/a4/TableStart.png','Start a table','{|','',''],
'TC':['7/71/TableCell.png','Table cell','|','',''],
'TE':['0/06/TableEnd.png','End a table','','|}',''],
'TR':['4/4c/TableRow.png','Start a table row','|-','',''],
'T1':['3/30/Tt_icon.png','Teletype text','<tt>','<\/tt>','Teletype Text'],
'TL':['3/37/Button_tl_template.png','Template link',"{{subst:"+"tl|",'}}','Template name'],
'U':['f/fd/Button_underline.png','Underlined',"<u>","<\/u>",'Underlined text'],
'V':['c/c8/Button_redirect.png','Redirect',"#REDIRECT [[","]]",'Article Name'],
'W':['8/88/Btn_toolbar_enum.png','Numbering',"\n# ","\n# Element 2\n# Element 3",'Element 1'],
'X':['1/11/Btn_toolbar_liste.png','List',"\n* ","\n* Element B\n* Element C",'Element A'],
'Y1':['c/ce/Button_no_include.png','No Include',"<noinclude>","<\/noinclude>",'Text'],
'Y2':['7/79/Button_include.png','Include only',"<includeonly>","<\/includeonly>",'Text'],
'Z':['3/35/Button_substitute.png','Substitute',"{{subst:","}}",'Template'],
'AI':['1/1c/Button_advanced_image.png','Advanaced Image',"[[Image:","|thumb|right|px|Caption]]",'FileName.jpg'],
'GEO':['b/b8/Button_Globe.png','Geo location',"","",""],
'TALK':['4/49/Button_talk.png','Add talk template',"","",""]
};

var XEBOrder2=[];




if(typeof mwCustomEditButtons !== 'undefined') {
	$(initButtons);
	if(!mw.config.get('wgIsArticle'))// only if edit
	{ 

		if(XEBPopups)hookEvent("load", extendButtons);
	}
}

function initButtons(){

	var bc,d;

	if (typeof XEBOrder!='string') // can be modified
		XEBOrder2="A,D,C,D1,F,U,J1,E,G,Q,W,X,K,L,H,O,R,T".split(",");
	else if (XEBOrder.toLowerCase()=='all') 
		for (b in BDict) XEBOrder2.push(b);
	else XEBOrder2=XEBOrder.toUpperCase().split(",");

	for (b in BDict) BDict[b][0] = Isrc+BDict[b][0]; // // Add the start of the URL (Isrc) to the XEB buttons
	// If the user has defined any buttons then add them into the available button lists 

	if (typeof myButtons=='object')
	  for (b in myButtons) BDict[b] = myButtons[b];	// custom user buttons
	// Add the media wiki standard buttons into the available buttons 

	for (b in mwEditButtons) { // add standard buttons for full XEB order changing

	//	BDict[b]=[];
BDict[b]=[mwEditButtons[b].imageFile,mwEditButtons[b].speedTip,mwEditButtons[b].tagOpen,mwEditButtons[b].tagClose,mwEditButtons[b].sampleText];

//		for (d in mwEditButtons[b]) BDict[b].push(mwEditButtons[b][d]);
	}

	// Build the new buttons 

	for (i=0;i<XEBOrder2.length;i++) {
		bc = BDict[XEBOrder2[i]];

		//Check if bc is an object 
		// - protects if user specified a non-existant buttons
		// - IE causes a javascript error when viewing a page
		if(typeof bc=='object')
		{

			//Call addCustomButton in wikibits
			addCustomButton(bc[0],bc[1],bc[2],bc[3],bc[4]);
		}
	}

	// Remove the default buttons (if requested by the user)
	eraseButtons();
}


/** en: Removes arbitrary standard buttons from the toolbar
* @author: [[:de:User:Olliminatore]]
* @version: 0.1 (01.10.2006) **/

function eraseButtons(){

	//Remove the buttons the user doesn't want

	if (typeof rmEditButtons!='object') return;

	if (typeof rmEditButtons[0] == 'string' && rmEditButtons[0].toLowerCase() == 'all') 
	{
		mwEditButtons=[];
		for(i=0;i<enExtraButtons;i++){mwCustomEditButtons.shift();}
	}
	//Sort the user's requests so we remove the button with the highest index first
	//- This ensures we remove the buttons the user expects whatever order he requested the buttons in
	rmEditButtons.sort(sortit);

	//Remove individual buttons the user doesn't want 

	for(i=0;i<rmEditButtons.length;i++){
		var n=rmEditButtons[i];
		//Standard Wikimedia buttons
		if(n>=0 && n<mwEditButtons.length){
			if(n<mwEditButtons.length){
				var x = -1;
				while((++x)<mwEditButtons.length)
					if(x>=n)
						mwEditButtons[x] = mwEditButtons[x+1];
			}
		mwEditButtons.pop();
		}
		//Extra buttons in English Wikipedia
		n=n-mwEditButtons.length;
		if(n>0 && n<mwCustomEditButtons.length){
		if(n<mwCustomEditButtons.length){
				var x = -1;
				while((++x)<mwCustomEditButtons.length)
					if(x>=n)
						mwCustomEditButtons[x] = mwCustomEditButtons[x+1];
			}
		mwCustomEditButtons.pop();
		}
	}
};

//Function:
//	sortit
//Purpose:
//	Used to sort the rmEditButtons array into descending order
function sortit(a,b){
	return(b-a)
}


//Function:
//Purpose:
//	Adds extended onclick-function to some buttons 
function extendButtons(){

	if(!(allEditButtons = document.getElementById('toolbar'))) return false;
	if(typeof editform != 'undefined')
		if(!(window.editform = document.forms['editform'])) return false;

	//  table
	extendAButton(Isrc+"0/04/Button_array.png",XEBPopupTable)
	extendAButton(Isrc+"7/79/Button_reflink.png",XEBPopupRef)
	extendAButton(Isrc+"b/b8/Button_Globe.png",XEBPopupGeoLink)
	extendAButton(Isrc+"4/49/Button_talk.png",XEBPopupTalk)
	extendAButton(Isrc+"1/1c/Button_advanced_image.png",XEBPopupImage)
	//extendAButton(Isrc+"6/6a/Button_sup_letter.png",XEBPopupFormattedText)

	// redirect -##IE doesn't like this line. Object doesn't support this property or method
	//c=XEBOrder2.getIndex('V');

//	if(c != -1)
//		allEditButtons[bu_len+c].onclick=function(){
//		var a='#REDIRECT \[\['+prompt("Which page do you want to redirect to\?")+'\]\]';
//		document.editform.elements['wpTextbox1'].value=a;
//		document.editform.elements['wpSummary'].value=a;
//		document.editform.elements['wpWatchthis'].checked=false
//  };
};

function extendAButton(url,newfunc)
{
	if(!(allEditButtons = document.getElementById('toolbar'))) return false;
	if(typeof editform != 'undefined')
		if(!(window.editform = document.forms['editform'])) return false;
	allEditButtons = allEditButtons.getElementsByTagName('img');
	for(i=0;i<allEditButtons.length;i++)
	{
		if(allEditButtons[i].src==url)
		{
			allEditButtons[i].onclick=newfunc;
		}
	}
}

//==========================================================================================================
// General purpose popup code
//==========================================================================================================

function getXEBPopupDiv(name)
{
	XEBMainDiv= document.getElementById("XEB");
	if(XEBMainDiv==null){
		XEBMainDiv=document.createElement("div");
		document.body.appendChild(XEBMainDiv);
		XEBMainDiv.id="XEB";
	}

	me= document.getElementById("XEBPopup" & name);
	if(!(me==null))return me;
	me=document.createElement("div");
	XEBMainDiv.appendChild(me);

	me.id="XEBPopup";
	me.style.position='absolute';
	me.display='none';
	me.visibility='hidden';
	me.onmouseout=CheckHideXEBPopup;
	me.onmouseover=cancelHidePopup;
	return me;
}
 
//Function:
//	CheckHideXEBPopup
//Purpose:
//	Looks at the cursor position and if it has moved outside the popup it will close the popup
//Called:
//	When the onMouseEvent is fired on the popup

function CheckHideXEBPopup(e){
	m= document.getElementById("XEBmnu");
	if(is_gecko)
	{
		ph=m.offsetHeight;
		var x=e.clientX + window.scrollX;
		var y=e.clientY + window.scrollY;;
		s=window.getComputedStyle(m,"");
		ph=s.height;
		ph=Number(ph.substring(0,ph.length-2));
	}
	else
	{
		var x=event.clientX+ document.documentElement.scrollLeft + document.body.scrollLeft;
		var y=event.clientY+ document.documentElement.scrollTop + document.body.scrollTop;
		ph=m.offsetHeight;
	}
	pl=curPopup.x;
	pt=curPopup.y;
	pw=m.style.width;
	pw=Number(pw.substring(0,pw.length-2));

	if(x>(pl+2)&&x<(pl+pw-5)&&y>(pt+2)&&y<(pt+ph-5))return;
	curPopup.hideTimeout=setTimeout('hideXEBPopup()',XEBHideDelay*1000);
}

function cancelHidePopup()
{
	clearTimeout(curPopup.hideTimeout)
}

function hideXEBPopup(){
	XEBMainDiv= document.getElementById("XEB");
	m= document.getElementById("XEBPopup");
	XEBMainDiv.removeChild(m);
}

function XEBstartDrag(e)
{
	m=new GetPos(e||event);
	curPopup.startDrag.mouse=m;
	curPopup.startDrag.floatpopup.y=parseInt(curPopup.div.style.top);
	curPopup.startDrag.floatpopup.x=parseInt(curPopup.div.style.left);
	curPopup.dragging=true;
}

function XEBstopDrag(e)
{
	if(curPopup.dragging==false)return;
	curPopup.dragging=false;
}

function XEBDrag(e)
{
	if(curPopup.dragging==false)return;

	m=new GetPos(e||event);
	x=parseInt(curPopup.startDrag.floatpopup.x+(m.x-curPopup.startDrag.mouse.x));
	y=parseInt(curPopup.startDrag.floatpopup.y+(m.y-curPopup.startDrag.mouse.y));

	curPopup.div.style.top=y+"px";
	curPopup.div.style.left=x+"px";

	curPopup.x=x;
	curPopup.y=y;
}

//=============================================================================
// Popup: Table
//=============================================================================

function XEBPopup(name,x,y)
{
	// Make sure the popup can appear on the screen

	this.IESelectedRange=XEBgetIESelectedRange();

	winW=(is_gecko)?window.innerWidth:document.body.offsetWidth;
	if((winW-this.width)<x)x=(winW-this.width);

	this.div=getXEBPopupDiv(name);
	this.div.style.zIndex=2000;
	this.div.display="inline";
	this.div.visibility="visible";
	this.div.style.top=y + "px";
	this.x=x;
	this.y=y;
	this.name=name;

	this.startDrag=new Object;
	this.startDrag.floatpopup=new Object;
}

function setInnerHTML(text)
{
	winW=(is_gecko)?window.innerWidth:document.body.offsetWidth;
	if((winW-this.width)<this.x)this.x=(winW-this.width);
	this.div.style.left=this.x+ "px";

	mt="<div id='XEBmnu' style='width:" + this.width + "px' >";
	mt+='<div id="XEBmnuTitle" class="XEBPopupTitle" onmousedown="XEBstartDrag(event)" onmouseup="XEBstopDrag(event)" onmousemove="XEBDrag(event)">Title</div>'
	mt+=text;
	mt+="</div>";
	this.div.innerHTML=mt;
//Turn off autocomplete. If the mouse moves over the autocomplete popup then x,y in CheckHidePopup is relative to the
// autocomplete popup and our popup is hidden
	var InTexts = this.div.getElementsByTagName('input');
	for (var i = 0; i < InTexts.length; i++) {
        	var theInput = InTexts[i];
		if (theInput.type == 'text'){theInput.setAttribute('autocomplete','off');}
	}
//Add rollover features to menu items. Doing it here means we don't have to do it for each menu
	x=XEBgetElementsByClassName(this.div,'XEBMnuItm','span');
	for (var i = 0; i < x.length; i++) {
        	var theItm = x[i];
		theItm.onmouseout=XEBMenuMouseOut;
		theItm.onmouseover=XEBMenuMouseOver;
	}

	this.div.style.borderWidth='thin';
	this.div.style.borderStyle='solid';
	this.div.style.backgroundColor='#D0D0D0';
}
XEBPopup.prototype.width=250;
XEBPopup.prototype.dragging=false;
XEBPopup.prototype.setInnerHTML=setInnerHTML;

var curPopup;

function GetPos(e)
{
	this.x=e.clientX-10+ document.documentElement.scrollLeft + document.body.scrollLeft;
	this.y=e.clientY-10+ document.documentElement.scrollTop + document.body.scrollTop;
}

function XEBPopupTable(e){
	m=new GetPos(e||event);

	curPopup=new XEBPopup("table",m.x,m.y);

	mt='<p>Enter the table parameters below: <\/p>'
		+'<form name="XEBPopupTableForm">'
		+'Table caption: <input type="checkbox" name="inputCaption"><p\/>'
		+'Table alignment: center<input type="checkbox" name="inputAlign"><p\/>'
		+'Table headline: colored<input type="checkbox" name="inputHead"><p\/>'
		+'Number of rows: <input type="text" name="inputRow" value="3" size="2"><p\/>'
		+'Number of columns: <input type="text" name="inputCol" value="3" size="2"><p\/>'
		//+'Alternating grey lines: <input type="checkbox" name="inputLine" checked="1" ><p\/>'
		+'Item column: <input type="checkbox" name="inputItems" ><p\/>'
		+'Sortable: <input type="checkbox" name="inputSort" ><p\/>'
		+'<\/form>'
		+'<i>The default table allows for fields and values only.<\/i><p\/>'
		+'Check "Item column" to allow for the table to have fields, items, and values.<\/i><p\/>'
		+'<p><button onClick="javascript:insertTableCode()">Insert</button>'
		+'<button onClick="hideXEBPopup()">Cancel</button>'

	curPopup.setInnerHTML(mt);

	return true;
}

function insertTableCode(){
	f=document.XEBPopupTableForm;
	var caption = (f.inputCaption.checked)?"|+ TABLE CAPTION \n":""; 
	var exhead = (f.inputHead.checked)?'|- style="background: #DDFFDD;"\n':""; 
	var nbRow = parseInt(f.inputRow.value); 
	var nbCol = parseInt(f.inputCol.value); 
	var exfield = f.inputItems.checked; 
	var align = (f.inputAlign.checked)?'align="center"':""; 

	//generateTable(caption, exhead, nbCol, nbRow, exfield, align);

	var code = "\n";
	code += '{| {{prettytable}} ' + align + ' '; // en: class="wikitable"
	code+=(f.inputSort.checked)?'class="sortable" \n':'\n';
	code += caption + exhead;
	if (exfield) code += '!\n';
	for (i=1;i<nbCol+1;i++) code += '! FELD ' + i + '\n';
	var items = 0;
	for (var j=0;j<nbRow;j++){
		if (exfield) { 
			items++;
			code += '|-\n! style="background: #FFDDDD;"|ITEM ' + items + '\n';
		}	else code += '|-\n';
		for (i=0;i<nbCol;i++) code += '| Element\n';
	}
	code += '|}\n';
	hideXEBPopup();
	insertTags('','', code);
	extendSummary('table');

	return false;
}  

// Get the text currently selected by user in the textAra
// This code is based on part of the insertTags function in wikibits.js

function XEBGetSelectedText()
{
	var txtarea;
	if (document.editform) {
		txtarea = document.editform.wpTextbox1;
	} else {
		// some alternate form? take the first one we can find
		var areas = document.getElementsByTagName('textarea');

		txtarea = areas[0];
	}
	// IE & Opera
	if (document.selection  && !is_gecko)
	{
		var theSelection = document.selection.createRange().text;
		if (!theSelection) theSelection='';
	}
	// Mozilla
	else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
		var replaced = false;
		var startPos = txtarea.selectionStart;
		var endPos = txtarea.selectionEnd;
		var theSelection = (txtarea.value).substring(startPos, endPos);
		if (!theSelection) theSelection='';
	}
	return theSelection;
}

//Notes:
//	IE loses the cursor position in the textarea when the popup is used. 
//	So we save the cursor position here
function XEBgetIESelectedRange(){
	var IESel=new Object;
	var txtarea;
	if (document.editform) {
		txtarea = document.editform.wpTextbox1;
	} else {
		// some alternate form? take the first one we can find
		var areas = document.getElementsByTagName('textarea');

		txtarea = areas[0];
	}
	// IE & Opera

	if (document.selection  && !is_gecko)
	{
		txtarea.focus();
		IESel.Rng=document.selection.createRange();
		return IESel;
	}
}

function XEBinsertText(beforeText,selText,afterText,IESelectedRange) {
	var newText=beforeText + selText + afterText;
	var txtarea;
	if (document.editform) {
		txtarea = document.editform.wpTextbox1;
	} else {
		// some alternate form? take the first one we can find
		var areas = document.getElementsByTagName('textarea');
		txtarea = areas[0];
	}

	// IE
	if (document.selection  && !is_gecko) {

		tr=IESelectedRange.Rng;
		tr.text=newText;
		txtarea.focus();
		//txtarea.caretpos=tr.duplicate();
		tr.select();

		return;

	// Mozilla
	} else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
		var replaced = false;
		var startPos = txtarea.selectionStart;
		var endPos = txtarea.selectionEnd;

		if (endPos-startPos) {
			replaced = true;
		}
		var scrollTop = txtarea.scrollTop;
//		var myText = (txtarea.value).substring(startPos, endPos);
//		if (!myText) {
//			myText=sampleText;
//		}
//		if (myText.charAt(myText.length - 1) == " ") { // exclude ending space char, if any
//			subst = tagOpen + myText.substring(0, (myText.length - 1)) + tagClose + " ";
//		} else {
//			subst = tagOpen + myText + tagClose;
//		}
		txtarea.value = txtarea.value.substring(0, startPos) + newText +
			txtarea.value.substring(endPos, txtarea.value.length);
		txtarea.focus();
		//set new selection
		if (!replaced) {
			var cPos = startPos+(newText.length);
			txtarea.selectionStart = cPos;
			txtarea.selectionEnd = cPos;
		} else {
			txtarea.selectionStart = startPos+beforeText.length;
			txtarea.selectionEnd = startPos+beforeText.length+selText.length;
		}
		txtarea.scrollTop = scrollTop;

	// All other browsers get no toolbar.
	// There was previously support for a crippled "help"
	// bar, but that caused more problems than it solved.
	}
	// reposition cursor if possible
	if (txtarea.createTextRange) {

		txtarea.caretPos = document.selection.createRange().duplicate();
//txtarea.caretPos =IESelectedRange.Rng;
	}
txtarea.focus();
}


//============================================================
// Table generator 
//============================================================
/** en: Generate an array using Mediawiki syntax
* @author: originally from fr:user:dake
* @version: 0.2 */
function generateTable(caption, exhead, nbCol, nbRow, exfield, align){
	
};


function XEBPopupRef(e){

	m=new GetPos(e||event);

	curPopup=new XEBPopup("ref",m.x,m.y);
	curPopup.width=500;
	mt='<p>Enter the reference parameters below: <\/p>'
		+'<form name="XEBPopupRefForm">'
		+'Name:<input type="text" name="refName" value="" size="10"><p\/>'
		+'Material:<input type="text" name="refMaterial" value="' + XEBGetSelectedText() + '" size="20">'
		+'<\/form>'
		+'<p><button onClick="javascript:insertRef()">Insert</button>'
		+'<button onClick="hideXEBPopup()">Cancel</button>';

	curPopup.setInnerHTML(mt);
//	document.XEBPopupRefForm.refName.focus();
	return true;
}

function insertRef(){
	f=document.XEBPopupRefForm;
	var refName = f.refName.value;
	var refMaterial=f.refMaterial.value;
	
	hideXEBPopup();
	var code1='<ref';
	code1+=(refName)?' name="'+refName+'">':'>'; 
	code2=refMaterial;
	code3='<\/ref>'
	XEBinsertText(code1,code2,code3,curPopup.IESelectedRange);

	extendSummary('ref');
	return false;
} 

//===GEO LINK Function==================================================

function XEBPopupGeoLink(e)
{
	m=new GetPos(e||event);

	curPopup=new XEBPopup("geo",m.x,m.y);
	curPopup.width=300;
	mt='<p>Enter the location parameters below: <\/p>'
		+'<form name="XEBPopupGeoLinkForm">'
		+'Loction:<p\/>'
		+'<table style="background: transparent;">'
		+'<tr><td>Latitude:<\/td><td><input type="text" autocomplete="off" name="geoLatDeg" value="" size="4"><\/td>'
		+'<td><input type="text" name="geoLatMin" size="4"><\/td>'
		+'<td><input type="text" name="geoLatSec" size="4"><\/td>'
		+'<td><select name="geoLatNS"><option value="N">N<option value="S">S</select><\/td><\/tr>'
		+'<tr><td>Longitude:<\/td><td><input type="text" name="geoLonDeg" value="" size="4"><\/td>'
		+'<td><input type="text" name="geoLonMin" value="" size="4"><\/td>'
		+'<td><input type="text" name="geoLonSec" value="" size="4"><\/td>'
		+'<td><select name="geoLonEW"><option value="E">E<option value="W">W</select><\/td><\/tr>'
		+'<\/table>'
		+'Region:<input type="text" name="geoRegion" value="" size="4"><p\/>'
		+'Type:'
		+'<SELECT NAME="geoType" size="5">'
		+'<OPTION VALUE="country">Country<OPTION VALUE="state">State'
		+'<OPTION VALUE="adm1st">Admin unit, 1st level<OPTION VALUE="adm2st">Admin unit, 2nd level'
		+'<OPTION VALUE="city">City<OPTION VALUE="airport">Airport'
		+'<OPTION VALUE="mountain">Mountain<OPTION VALUE="isle">Isle'
		+'<OPTION VALUE="waterbody">Waterbody<OPTION VALUE="landmark" SELECTED>Landmark'
		+'<OPTION VALUE="forest">forest</SELECT><br>'
		+'Title: <input type="checkbox" name="geoTitle" ><p\/>'
		+'<\/form>'
		+'<p><button onClick="javascript:insertGeoLink()">Insert</button>'
		+'<button onClick="hideXEBPopup()">Cancel</button>';

	curPopup.setInnerHTML(mt);
	document.paramForm.refName.focus();
	return true;

}
function insertGeoLink()
{
	f=document.XEBPopupGeoLinkForm;

	var code='{{Coor ';
	if(f.geoTitle.checked)code+='title ';
	ft='dms';
	if(f.geoLatSec.value==''&&f.geoLonSec.value=='')ft='dm';
	if(ft=='dm'&&f.geoLatMin.value==''&&f.geoLonMin.value=='')ft='d';
	code+=ft;
	code+='|'+f.geoLatDeg.value;
	code+=(ft=='dm'||ft=='dms')?'|'+f.geoLatMin.value:'';
	code+=(ft=='dms')?'|'+f.geoLatSec.value:'';
	code+='|'+f.geoLatNS.value;
	code+='|'+f.geoLonDeg.value;
	code+=(ft=='dm'||ft=='dms')?'|'+f.geoLonMin.value:'';
	code+=(ft=='dms')?'|'+f.geoLonSec.value:'';
	code+='|'+f.geoLonEW.value;
	code+='|type:'+f.geoType.value+'_region:'+f.geoRegion.value
	code+='}}';
	insertTags('','', code);
	extendSummary('geo-location');
	hideXEBPopup();
	return false;
}

//===Talk Page entry Function===========================================

function XEBPopupTalk(e)
{
	m=new GetPos(e||event);

	curPopup=new XEBPopup("talk",m.x,m.y);
	curPopup.width=200;
	mt='<div style="font-size:medium"><p>Please choose:<\/p>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(1)">Test1<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(2)">Self Test<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(3)">Nonsense<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(4)">Please stop<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(5)">Last chance<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(6)">Blanking<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(7)">Blatant<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(8)">*BLOCKED*<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(9)">Spam<\/span><br>'
	mt+='<span class="XEBMnuItm" onclick="XEBInsertTalk(10)">Npov<\/span></div>'

	curPopup.setInnerHTML(mt);

	return true;

}
function XEBInsertTalk(itm)
{
	hideXEBPopup();
	if(itm==1)code='{{subst:test1-n|}}';
	if(itm==2)code='{{subst:selftest-n|}}';
	if(itm==3)code='{{subst:test2-n|}}';
	if(itm==4)code='{{subst:test3-n|}}';
	if(itm==5)code='{{subst:test4-n|}}';
	if(itm==6)code='{{subst:test2a-n|}}';
	if(itm==7)code='{{subst:bv-n|}}';
	if(itm==8)code='{{subst:blantant|}}';
	if(itm==9)code='{{subst:spam-n|}}';
	if(itm==10)code='{{subst:NPOV user}}';

	insertTags('','', code);
	return false;
}
function XEBPopupImage(e)
{
	m=new GetPos(e||event);

	curPopup=new XEBPopup("image",m.x,m.y);
	curPopup.width=300;

	mt='<p>Enter the image parameters below: <\/p>'
		+'<form name="XEBPopupImageForm">'
		+'File:<input type="text" name="imgFile" value="' + XEBGetSelectedText() + '" size="30"><br>'
		+'Type:<SELECT NAME="imgType">'
		+'<OPTION VALUE="thumb">Thumbnail'
		+'<OPTION VALUE="frame">Frame'
		+'<OPTION VALUE="none">[not specified]'
		+'</SELECT><br>'
		+'Location:<SELECT NAME="imgLocation">'
		+'<OPTION VALUE="left">Left'
		+'<OPTION VALUE="center">Centre'
		+'<OPTION VALUE="right">Right'
		+'<OPTION VALUE="none">None'
		+'</SELECT><br>'
		+'Size:<input type="text" name="imgSize" value="100" size="3">px<br>'
		+'Caption:<input type="text" name="imgCaption" value="" size="30"><\/p>'
		+'<\/form>'
		+'<p><button onClick="javascript:XEBInsertImage()">Insert</button>'
		+'<button onClick="hideXEBPopup()">Cancel</button>';

	curPopup.setInnerHTML(mt);

	return true;
}
function XEBInsertImage()
{
	f=document.XEBPopupImageForm;
	hideXEBPopup();
	var code='[[Image:';
	code+=f.imgFile.value;
	code+='|'+f.imgType.value;
	code+='|'+f.imgLocation.value;
	code+='|'+f.imgSize.value;
	code+='|'+f.imgCaption.value;
	code+=']]';
	insertTags('','', code);
	extendSummary('image');

	return false;
}

function XEBPopupFormattedText(e)
{
	m=new GetPos(e||event);

	curPopup=new XEBPopup("image",m.x,m.y);
	curPopup.width=300;
	
	mt='<form name="XEBPopupImageForm">'
		+'<table  style="background: transparent;">'
		+'<tr><td>Bold:<\/td><td><input type="checkbox" name="textBold"><\/td>'
		+'<td>Superscript:<\/td><td><input type="checkbox" name="textSuperscript"><\/td><\/tr>'
		+'<tr><td>Italic:<\/td><td><input type="checkbox" name="textItalic"><\/td>'
		+'<td>Subscript:<\/td><td><input type="checkbox" name="textSubscript"><\/td><\/tr>'
		+'<tr><td>Strike:<\/td><td><input type="checkbox" name="textStrike"><\/td>'
		+'<td>&nbsp;<\/td><\/tr>'
		+'</table>'
		+'Size:<SELECT NAME="textSize">'
		+'<OPTION VALUE="small">small'
		+'<OPTION VALUE="normal">[Normal]'
		+'<OPTION VALUE="big">big'
		+'</SELECT><br><table style="background:transparent;"><tr><td>Colour:<\/td><td>'
		+'<table width="100px">'
		+'<tr><td colspan="4">None<\/td></tr>'
		+'<tr><td bgcolor="aqua">&nbsp;<\/td><td bgcolor="gray"> &nbsp;<\/td>'
		+'<td bgcolor="olive">&nbsp;<\/td><td bgcolor="navy">&nbsp;<\/td><\/tr>'
		+'<tr><td bgcolor="black">&nbsp;<\/td><td bgcolor="green"> &nbsp;<\/td>'
		+'<td bgcolor="purple">&nbsp;<\/td><td bgcolor="teal">&nbsp;<\/td><\/tr>'
		+'<tr><td bgcolor="blue">&nbsp;<\/td><td bgcolor="lime">&nbsp;<\/td>'
		+'<td bgcolor="red">&nbsp;<\/td><td bgcolor="white">&nbsp;<\/td><\/tr>'
		+'<tr><td bgcolor="fuchsia">&nbsp;<\/td><td bgcolor="maroon">&nbsp;<\/td>'
		+'<td bgcolor="silver">&nbsp;<\/td><td bgcolor="yellow">&nbsp;<\/td><\/tr>'
		+'</table><\/td><\/tr>'
		+'<\/form>'
		+'Sample:'
		+'<span id="sampleText">Text</span>"'
		+'<p><button onClick="javascript:XEBInsertFormattedText()">Insert</button>'
		+'<button onClick="hideXEBPopup()">Cancel</button>';

	curPopup.setInnerHTML(mt);

	return true;
}

function XEBUpdateSampleText()
{
	f=document.XEBPopupImageForm;
}

//====================

function XEBMenuMouseOut(e)
{
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;

	targ.style.color='black';
}

function XEBMenuMouseOver(e)
{	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;

	targ.style.color='red';
}

//=======================================================================
// Other functions
//=======================================================================

function XEBgetElementsByClassName(parent,clsName,htmltag){ 
	var arr = new Array(); 
	var elems = parent.getElementsByTagName(htmltag);
	for ( var cls, i = 0; ( elem = elems[i] ); i++ ){
		if ( elem.className == clsName ){
			arr[arr.length] = elem;
		}
	}
	return arr;
}

function extendSummary(newText)
{
	if(!XEBExtendEditSummary)return;
	s=document.editform.elements['wpSummary'].value;
	s+=(s=='')?newText:' +'+newText;
	document.editform.elements['wpSummary'].value=s;
}

function bug(msg)
{
	if(mw.config.get('wgUserName')=='MarkS')alert(msg);
}
function ecAddTab(url, name, id, title, key){
    return mw.util.addPortletLink('p-cactions', url, name, id, title, key);
}

var aecwpajax;
// From [[WP:US]] mainpage (wpajax renamed to aecwpajax), some comments removed
aecwpajax={
        download:function(bundle) {
                var x = window.XMLHttpRequest ? new XMLHttpRequest()
                : window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP")
                : false;
                
                if (x) {
                        x.onreadystatechange=function() {
                                x.readyState==4 && aecwpajax.downloadComplete(x,bundle);
                        };
                        x.open("GET",bundle.url,true);
                        x.send(null); 
                }
                return x;
        },

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

function ecQuickCountComplete(xmlreq,data)
{
  alert("Edit count: "+xmlreq.responseText.split('editcount="')[1].split('"')[0]);
}

$(function() {
  if(location.href.indexOf("Special")!=-1&&location.href.indexOf("Contributions")!=-1)
    ecAddTab("javascript:ais523quickcount()","quick count","ca-ais523qc","Quick Count","");
});

function ais523quickcount()
{
  var trg;
  trg=ecGetParamValue('target');
  if(trg==null) trg=location.href.substr(location.href.lastIndexOf("/")+1);
  aecwpajax.download({url:'//en.wikipedia.org/w/api.php?action=query&list=users&ususers='
    +encodeURIComponent(trg)+'&usprop=editcount&format=xml', onSuccess:ecQuickCountComplete});
}

$(function() {
  if(location.href.indexOf("Special")!=-1&&location.href.indexOf("Contributions")!=-1)
    ecAddTab("javascript:ais523contrib()","count","ca-ais523count","Count","");
});

//This function was orignally taken from [[User:Lupin/autoedit.js]]. I've renamed it
//because I know many users use popups, and I don't want to cause a naming conflict.
//Edited to decode + to space as well, and to use a decoding function that handles
//a broader range of characters.
function ecGetParamValue(paramName) {
  var cmdRe=RegExp('[&?]'+paramName+'=([^&]*)');
  var h=document.location;
  var m;
  if ((m=cmdRe.exec(h))) {
    try { 
      while(m[1].indexOf('+')!=-1)
      {
        m[1]=m[1].substr(0,m[1].indexOf('+'))+" "+m[1].substr(m[1].indexOf('+')+1);
      }
      return decodeURIComponent(m[1]);
    } catch (someError) {}
  }
  return null;
}

// TODO: Can we autogenerate this from somewhere?
var namespaces = {
	0: "Article",
	1: "Talk",
	2: "User",
	3: "User talk",
	4: "Wikipedia",
	5: "Wikipedia talk",
	6: "File",
	7: "File talk",
	8: "MediaWiki",
	9: "MediaWiki talk",
	10: "Template",
	11: "Template talk",
	12: "Help",
	13: "Help talk",
	14: "Category",
	15: "Category talk",
	100: "Portal",
	101: "Portal talk",
	108: "Book",
	109: "Book talk",
	118: "Draft",
	119: "Draft talk",
	446: "Education Program",
	447: "Education Program talk",
	710: "TimedText",
	711: "TimedText talk",
	828: "Module",
	829: "Module talk",
	2600: "Topic",
};

function nextnamespace(n)
{
	var flag = false;
	for (var nsnum in namespaces) {
		if (flag)
			return nsnum;
		if (nsnum == n)
			flag = true;
	}
	return null;
}

function ais523contrib()
{
  var u;
  if(location.href.indexOf("?")!=-1) u=ecGetParamValue("target");
  else u=location.href.substr(location.href.lastIndexOf("/")+1);
  location.href="//en.wikipedia.org/w/index.php?title=Special:Contributions&limit=5000&target="+u+"&ais523count=1&namespace=0";
}

//Analyses an edit summary and returns a two-letter code indicating what the edit seems
//to be doing. The edit summary is passed with parens round it, written in HTML. This
//doesn't yet work for section edits, which will have to be parsed out in the main
//function.
function ecAnalyseSummary(edsum)
{
  while(edsum.indexOf(" ")===0) edsum=edsum.substr(1);
  edsum="("+edsum+")";
  edsum=edsum.toLowerCase();
  if(edsum.indexOf("→")!==-1) return 'se'; //section edit, can't say any more than that
  if(edsum==="") return 'se'; //section edit, no summary

  if(edsum.indexOf("(rvv")===0) return 'rv'; //vandalism revert
  if(edsum.indexOf("(rv vand")===0) return 'rv'; //vandalism revert
  if(edsum.indexOf("(revv")===0) return 'rv'; //vandalism revert
  if(edsum.indexOf("(rev vand")===0) return 'rv'; //vandalism revert
  if(edsum.indexOf("(revert vand")===0) return 'rv'; //vandalism revert

  if(edsum.indexOf("(rv ")===0&&edsum.indexOf("vandal")!=-1) return 'rv';
  if(edsum.indexOf("(rev ")===0&&edsum.indexOf("vandal")!=-1) return 'rv';

  if(edsum.indexOf("(rv ")===0) return 'ro'; //other manual revert
  if(edsum.indexOf("(rev ")===0) return 'ro'; //other manual revert

  if(edsum.indexOf("(reverted ")===0) return 'ra'; //automatic revert
  if(edsum.indexOf("(revert to ")===0) return 'ra'; //automatic revert, probably
  if(edsum.indexOf("(revert edit(s) ")===0) return 'ra'; //per [[User:Qxz]]

  if(edsum.indexOf("(revert")===0) return 'ro'; //guess manual for time being;
                                                //I need more examples of this sort of rv

  if(edsum.indexOf("(rm ")===0) return 'rm'; //removal
  if(edsum.indexOf("(rem ")===0) return 'rm'; //removal
  if(edsum.indexOf("(remove ")===0) return 'rm'; //removal

  if(edsum.indexOf("(redir")===0) return 'rd'; //redirect, including redir auto-summary
  if(edsum.indexOf("(#redir")===0) return 'rd'; //redirect, including redir auto-summary

  if(edsum.indexOf('(<a href="/w')===0) return 'li'; //edit summary was a link
  if(edsum.indexOf("(<a href='/w")===0) return 'li'; //edit summary was a link
  if(edsum.indexOf('(<a href=/w')===0) return 'li'; //edit summary was a link

  if(edsum.indexOf('{{welcome')!=-1) return 'we'; //welcome
  if(edsum.indexOf('welcome}}')!=-1) return 'we'; //welcome
  if(edsum.indexOf('(welcome')!=-1) return 'we'; //welcome
  if(edsum.indexOf('welcome)')!=-1) return 'we'; //welcome
  if(edsum.indexOf('(welcoming')!=-1) return 'we'; //welcome (Huggle)

  //User warnings are sorted by level. Other warnings and edit summaries are used;
  //this is just a small beginning for now.
  if(edsum.indexOf('test0')!=-1) return 'w0'; //warning 1
  if(edsum.indexOf('test1')!=-1) return 'w1'; //warning 1
  if(edsum.indexOf('test2')!=-1) return 'w2'; //warning 2
  if(edsum.indexOf('test3')!=-1) return 'w3'; //warning 3
  if(edsum.indexOf('test4')!=-1) return 'w4'; //warning 4
  if(edsum.indexOf('test5')!=-1) return 'w5'; //warning 5
  if(edsum.indexOf('test6')!=-1) return 'w5'; //warning 5

  //Twinkle's edit summaries by level
  if(edsum.indexOf('(general note:')!=-1) return 'a1'; //warning 1
  if(edsum.indexOf('(caution:')!=-1) return 'a2'; //warning 2
  if(edsum.indexOf('(warning:')!=-1) return 'a3'; //warning 3
  if(edsum.indexOf('(final warning:')!=-1) return 'a4'; //warning 4
  if(edsum.indexOf('(only warning:')!=-1) return 'a4'; //warning 4
  if(edsum.indexOf('blocked from editing')!=-1) return 'a5'; //warning 5

  //Huggle's are a lot simpler
  if(edsum.indexOf('level 1 warning')!=-1) return 'a1'; //warning 1
  if(edsum.indexOf('level 2 warning')!=-1) return 'a2'; //warning 2
  if(edsum.indexOf('level 3 warning')!=-1) return 'a3'; //warning 3
  if(edsum.indexOf('level 4 warning')!=-1) return 'a4'; //warning 4
  if(edsum.indexOf('level 5 warning')!=-1) return 'a5'; //warning 5

  //Unknown warnings
  if(edsum.indexOf('(warning user using')!=-1) return 'au'; //automated warning
  if(edsum.indexOf('warning')!=-1) return 'wu'; //unknown warning

  //Notifications
  if(edsum.indexOf('notify')!=-1) return 'no'; //notification
  if(edsum.indexOf('(notice')!=-1) return 'no'; //notification
  if(edsum.indexOf('notification')!=-1) return 'no'; //notification

  //Prodding
  if(edsum.indexOf('{'+'{prod')!=-1) return 'pr'; //prod
  if(edsum.indexOf('(prod')!=-1) return 'pr'; //prod

  //Some XfD-tagging summaries. So far I've only included the deletion-debates
  //I'm familiar with.
  if(edsum.indexOf('{'+'{afd}}')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('{'+'{afd1')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('(afd)')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('{'+'{tfd}}')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('(tfd)')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('{'+'{md}}')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('{'+'{md1')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('(mfd)')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('{'+'{rfd}}')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('(rfd)')!=-1) return 'xf'; //XfD tagging
  if(edsum.indexOf('for deletion')!=-1) return 'xf'; //XfD tagging

  //Speedy deletions
  if(edsum.indexOf('db-')!=-1) return 'sp'; //Speedy
  if(edsum.indexOf('db|')!=-1) return 'sp'; //Speedy
  if(edsum.indexOf('speedy')!=-1) return 'sp'; //Speedy (probably)
  if(edsum.indexOf('{{delete}}')!=-1) return 'sp'; //override group de below

  //Other deletion-related (removal of text, delete votes, etc.
  if(edsum.indexOf('(del')!=-1) return 'de';
  if(edsum.indexOf('delete')!=-1) return 'de';
  if(edsum.indexOf('(d)')!=-1) return 'de';
  if(edsum.indexOf('(d ')!=-1) return 'de';
  if(edsum.indexOf('(-')==0) return 'de'; // as in -link

  //Marked as additions
  if(edsum.indexOf('(add ')!=-1) return 'ad';
  if(edsum.indexOf(' add ')!=-1) return 'ad';
  if(edsum.indexOf('(add)')!=-1) return 'ad';
  if(edsum.indexOf(' add)')!=-1) return 'ad';
  if(edsum.indexOf('(+')==0) return 'ad'; // as in +1

  //Probable XfD votes to keep
  if(edsum.indexOf('(k)')!=-1) return 'ke';
  if(edsum.indexOf('(keep')!=-1) return 'ke';
  if(edsum.indexOf("'keep'")!=-1) return 'ke'; //for when the user just copies their
                                               //vote to the summary; also produced by
                                               //WikiVoter

  //Votes somewhere to support
  if(edsum.indexOf('(support')!=-1) return 'su';
  //to oppose
  if(edsum.indexOf('(oppose')!=-1) return 'op';

  if(edsum.indexOf("{"+"{")!=-1) return 'ta'; //unknown, marked as a tag

  if(edsum.length<=6) return 'ab'; //unknown abbreviation <=4 chars + parens

  return 'uk'; //unknown
}

//The main function; this actually counts the edits. The section at the end displays
//the results.
$(function() {
  if(location.href.indexOf("ais523count")!=-1&&location.href.indexOf("ais523countres")==-1)
  {
	// Counting edits. New, simpler jQuery version.
    var contribs=$(".mw-contributions-title").length;
    var contribsWithSummary=0;
    var sortcount=[];
    $(".comment").each(function(idx, element){
        contribsWithSummary++;
        var edsum=element.innerHTML;
        edsum=ecAnalyseSummary(edsum);
        if(edsum=='se')
        {
          //find the part beyond the last "</span>"
          edsum=element.innerHTML;
          sumloc=edsum.lastIndexOf("</span>");
          edsum=edsum.substr(sumloc+8);
          edsum=ecAnalyseSummary(edsum);
        }
        if(sortcount[edsum]===undefined) sortcount[edsum]=0;
        sortcount[edsum]++;
    });

    var namespace=ecGetParamValue("namespace");
    var scres="";
    var scit;
    for (scit in sortcount)
    {
      scres+="&cns"+namespace+scit+"="+sortcount[scit];
    }
    if(nextnamespace(namespace) !== null)
      location.href=location.href.substr(0,location.href.lastIndexOf("namespace="))+
        "countns"+namespace+"="+contribs+scres+"&countnosum"+namespace+"="+(contribs-contribsWithSummary)+"&namespace="+nextnamespace(namespace);
    else
    { 
      var lh=location.href;
      location.href="//en.wikipedia.org/wiki/User:ais523/results?ais523countres="+lh+"&countns"+namespace+"="+contribs+scres+"&countnosum"+namespace+"="+(contribs-contribsWithSummary)
      //You can use a page other than [[User:ais523/results]] as long as it's in the
      //correct format.
    }
  }
  else if(location.href.indexOf("ais523countres=")!=-1)
  { //This relies on the template page [[User:ais523/results]] being in the
    //correct format.
    document.getElementById("ais523echead").style.display="none";
    $(".ais523ecbody").each(function(idx,element) {
    var h=element.innerHTML;
    // First replacement: loop over namespaces.
    var hComponents = h.split("((pernamespace))");
    for (var hci = 1; hci < hComponents.length; hci++) {
    	var hcc = hComponents[hci].split("((/pernamespace))");
    	var hcp = "";
    	for (var nsnum in namespaces) {
    		if (ecGetParamValue('countns'+nsnum) > 0)
    			hcp += hcc[0].split("((NSNUM))").join(nsnum).split("((NSNAME))").join(namespaces[nsnum])
    	}
    	hComponents[hci] = hcp + hcc[1]
    }
    h = hComponents.join("")
    
    // Second replacement: substitute in individual results.
    while(h.indexOf("((")!=-1)
    {
      var i=h.indexOf("((");
      var f=h.substr(0,i);
      var g=h.substr(i+2,h.indexOf("))")-i-2);
      if(g.indexOf('#d')==0)
      { //delete unwanted lines to remove clutter
        var j=h.indexOf("((/#d))");
        var v=false;
        j=h.substr(i+6,j-i-2);
        while(j.indexOf("((")!=-1)
        {
          var ii=j.indexOf("((");
          var gg=j.substr(ii+2,j.indexOf("))")-ii-2);
          j=j.substr(ii+2);
          gg=ecGetParamValue(gg);
          if(gg!=null&&gg!=0&&gg!='0') v=true;
        }
        if(v) g="";
        else {h=h.substr(h.indexOf("((/#d")); g="";}
      }
      else if(g.indexOf("/#d")==0)
        g="";
      else if(g.indexOf("total")==0)
      {
      	g = 0;
      	for (var nsnum in namespaces)
      		g += new Number(ecGetParamValue('countns'+nsnum));
      }
      else if(g.indexOf("(")==-1)
        g=ecGetParamValue(g);
      if(g==null) g='0';
      f+=g+h.substr(h.indexOf("))")+2);
      h=f;
    }
    element.innerHTML=h;
    });
  }

});

//JavaScript diff finder. By [[User:ais523]]
$(function() {
  if(location.href.indexOf("Special")!=-1&&location.href.indexOf("Contributions")!=-1)
  {
    ecAddTab("javascript:ais523l1000()","last 1000","ca-ais523sort","Random diffs from recent edits","");
    ecAddTab("javascript:ais523l2000()","2000","ca-ais523sort","Random diffs from recent edits","");
  }
  if(location.href.indexOf("&ais523sort=last")!=-1)
    window.setTimeout("ais523sort();",500); //work around IE bug
});

function ais523l1000()
{
  var trg;
  trg=ecGetParamValue('target');
  if(trg==null) trg=location.href.substr(location.href.lastIndexOf("/")+1);
  location.href="//en.wikipedia.org/w/index.php?title=Special:Contributions"+
    "&limit=1000&target="+trg+"&ais523sort=last1000";
}

function ais523l2000()
{
  var trg;
  trg=ecGetParamValue('target');
  if(trg==null) trg=location.href.substr(location.href.lastIndexOf("/")+1);
  location.href="//en.wikipedia.org/w/index.php?title=Special:Contributions"+
    "&limit=2000&target="+trg+"&ais523sort=last2000";
}

function ais523sort()
{
  var s=document.body.innerHTML;
  var re=/href="(\/w\/index\.php\?title=([^"/]*)((\/[^"]*)?)&amp;diff=prev&amp;oldid=[0-9]*)"/i;
  var a=new Array();
  var b=new Array();
  var c=new Array();
  var nc=new Array();
  var d=new Array();
  while(s.search(re)!=-1)
  {
    var m=s.match(re);
    var m2="";
    var q;
    if(m[3]!='') m2=" subpages";
    m[4]=decodeURIComponent(m[2])+m2;
    m[5]=m2;
    if(c[m[4]]==undefined) c[m[4]]=0;
    if(c[m[4]]<10) q=c[m[4]];
    else if(Math.random()<10/(c[m[4]]+1)) q=Math.floor(Math.random()*10);
    else q=-1;
    c[m[4]]++;
    if(d[m[4]]==undefined) d[m[4]]=new Array();
    if(q>-1) d[m[4]][q]=m;
    s=s.substr(s.search(re)+2);
  }
  var i;
  var j;
  for(i in c)
  {
    if(c[i]<5)
    {
      for(j in d[i])
      {
        var ns="(main)";
        var q;
        if(d[i][j][4].indexOf(":")!=-1) ns=d[i][j][4].substr(0,d[i][j][4].indexOf(":"));
        m=d[i][j];
        m[2]="Others in namespace "+ns;
        m[3]="";
        m[4]=m[2];
        m[5]="";
        if(nc[m[4]]==undefined) nc[m[4]]=0;
        if(nc[m[4]]<10) q=nc[m[4]];
        else if(Math.random()<10/(nc[m[4]]+1)) q=Math.floor(Math.random()*10);
        else q=-1;
        nc[m[4]]++;
        if(d[m[4]]==undefined) d[m[4]]=new Array();
        if(q>-1) d[m[4]][q]=m;
      }
    }
  }
  for(i in d)
  {
    if(nc[i]!=undefined||c[i]>=5)
    for(j in d[i])
    {
      var m=d[i][j];
      m[2]=decodeURIComponent(m[2]);
      if(a[m[4]]==undefined) a[m[4]]="*[[:"+m[2].split("_").join(" ")+"]]"+m[5]+":";
      if(b[m[4]]==undefined) b[m[4]]="<li><a href='//en.wikipedia.org/wiki/"+
        m[2]+"'>"+m[2].split("_").join(" ")+"</a>"+m[5]+":";
      a[m[4]]+=" [http://en.wikipedia.org"+m[1]+"]";
      b[m[4]]+=" <a href='http://en.wikipedia.org"+m[1]+"'>["+(new Number(j)+1)+"]</a>";
    }
  }
  var e=0;
  for(i in c)
  {
    if(c[i]>=5)
    {
      a[i]+=(c[i]>10?"...":"")+" ("+c[i]+" edit(s))\n";
      b[i]+=(c[i]>10?"...":"")+" ("+c[i]+" edit(s))\n";
      if(c[i]>e) e=c[i]+1;
    }
  }
  for(i in nc)
  {
    if(nc[i]>=5)
    {
      a[i]+=(nc[i]>10?"...":"")+" ("+nc[i]+" edit(s))\n";
      b[i]+=(nc[i]>10?"...":"")+" ("+nc[i]+" edit(s))\n";
    }
  }
  var trg=ecGetParamValue('target');
  var h="<h1>Contribution breakdown for <a href='//en.wikipedia.org/wiki/User:"+trg;
  h+="'>User:"+trg+"</a></h1>\n";
  h+="<h2>HTML output</h2><ul>";
  var j=e;
  while(--j>=5)
  {
    for(i in c)
    {
      if(c[i]==j) h+=b[i];
    }
  }
  for(i in nc) if(nc[i]>=5) h+=b[i];
  j=e;
  h+="</ul>\n<h2>Wikimarkup output</h2><pr"+"e>";
  while(--j>=5)
  {
    for(i in c)
    {
      if(c[i]==j) h+=a[i];
    }
  }
  for(i in nc) if(nc[i]>=5) h+=a[i];
  h+="</p"+"re>";
  document.body.innerHTML=h;
}

// Log counter.
function ais523log()
{
  location.href="//en.wikipedia.org/w/index.php?title=Special:Log&type=&user="+
    document.getElementById('user').value+"&page=&limit=5000&offset=0&ais523log=count";
}

$(function() {
  if(location.href.indexOf("ais523log")!=-1&&location.href.indexOf("ais523logres")==-1)
  {
    var h=document.body.innerHTML;
    var i;
    var j=new Array();
    h=h.toLowerCase().split("<li>");
    i=h.length;
    while(--i)
    {
      if(h[i].indexOf("block</a>)")!=-1)
        h[i]=h[i].split("block</a>)")[1];
      else
        h[i]=h[i].split("contribs</a>)")[1];
      h[i]=h[i].split("<")[0].split('"').join("").split(" ").join("");
      if(h[i]==""&&i+1==h.length) h[i]="newuseraccount";
      else if(h[i]=="") h[i]="renamed"; //renames and original account creation are both ""
      if(j[h[i]]==null||j[h[i]]==undefined) j[h[i]]=0;
      j[h[i]]++;
    }
    h="";
    for(i in j)
      h+="<tr><td>"+i+"</td><td>"+j[i]+"</td></tr>";
    location.href="//en.wikipedia.org/wiki/User:ais523/logresults?ais523logres="+
      encodeURIComponent(h);
  }
  else if(location.href.indexOf("ais523logres")!=-1)
    document.getElementById("ais523ecbody").innerHTML=
      "<table>"+decodeURIComponent(ecGetParamValue('ais523logres'))+"</table>";
  else if(mw.config.get('wgPageName')=="Special:Log")
    ecAddTab("javascript:ais523log()","count","ca-ais523log","Count","");    
});

// Contribution day/time counter
$(function(){
  if(mw.config.get('wgPageName')=="Special:Contributions")
    ecAddTab("javascript:ais523dtc()","day/time","ca-ais523dtc","Summarizes what times on what days this editor edits","");
});

var ais523dtc_counts=null;
var ais523dtc_max=0;
var ais523dtc_rschn=false;
var ais523dtc_prog=0;
var ais523drc_sg=false;

var ais523dtc_nybbles=['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];

function ais523dtc_v(n)
{
  if(ais523dtc_max<n)
  {
    ais523dtc_max=n;
    ais523dtc_rschn=true;
  }
  var f=n/ais523dtc_max;
  var r=0, g=0;
  if(f<1/3) g=Math.floor(f*3*255);
  else if(f<2/3) {g=255; r=Math.floor((f*3-1)*255);}
  else {r=255; g=Math.floor((3-f*3)*255);}
  return ais523dtc_nybbles[Math.floor(r/16)]+ais523dtc_nybbles[r%16]+
         ais523dtc_nybbles[Math.floor(g/16)]+ais523dtc_nybbles[g%16]+"00";
}

function ais523dtcStep(xmlreq,data)
{
  var trg;
  trg=ecGetParamValue('target');
  if(trg==null) trg=location.href.substr(location.href.lastIndexOf("/")+1);
  var a=xmlreq.responseText.split('timestamp="')
  var i=a.length;
  ais523dtc_prog+=i-1;
  while(--i)
  {
    var q=a[i].split('"')[0];
    var d=new Date();
    d.setUTCFullYear(+q.substr(0,4));
    d.setUTCMonth((+q.substr(5,2))-1);
    d.setUTCDate(+q.substr(8,2));
    var wday=d.getUTCDay();
    var hper=4*+q.substr(11,2)+Math.floor((+q.substr(14,2))/15);
    document.getElementById('a5w'+wday+'q'+hper).style.backgroundColor=
      "#"+ais523dtc_v(++ais523dtc_counts[wday*96+hper]);
  }
  if(ais523dtc_rschn)
  {
    ais523dtc_rschn=false;
    var wday=7;
    while(wday--)
    {
      var hper=96;
      while(hper--)
        document.getElementById('a5w'+wday+'q'+hper).style.backgroundColor=
          "#"+ais523dtc_v(ais523dtc_counts[wday*96+hper]);
    }
  }
  document.getElementById('a5dtcp').innerHTML=ais523dtc_prog;
  document.getElementById('a5dtck1').innerHTML=Math.floor(ais523dtc_max/3);
  document.getElementById('a5dtck2').innerHTML=Math.floor(2*ais523dtc_max/3);
  document.getElementById('a5dtck3').innerHTML=ais523dtc_max;
  if(xmlreq.responseText.indexOf("query-continue")==-1) // finished
  {
    ais523dtc_sg=true;
    document.getElementById('a5dtco').innerHTML='(finished)';
    return;
  }
  aecwpajax.download({url:'//en.wikipedia.org/w/api.php?action=query&list=usercontribs&'+
    'ucuser='+encodeURIComponent(trg)+'&ucprop=timestamp&format=xml&uclimit=100&ucstart='+
    xmlreq.responseText.split('ucstart="')[1].split('"')[0],onSuccess:ais523dtcStep});
}

function ais523dtcook(xmlreq,data)
{
  if(!ais523dtc_sg) return;
  document.getElementById('a5dtco').innerHTML="(out of an overestimated "+
    xmlreq.responseText.split('editcount="')[1].split('"')[0]+")";
}

function ais523dtc()
{
  var trg;
  trg=ecGetParamValue('target');
  if(trg==null) trg=location.href.substr(location.href.lastIndexOf("/")+1);
  var h="<table class='wikitable'><tr><th>&nbsp;</th>";
  var i=0;
  while(i<24) h+="<th colspan=4>"+i++ +"</th>"
  h+="</tr><tr><th>Sun</th>"; i=0; while(i<96) h+="<td id='a5w0q"+i++ +"'>&thinsp;</td>";
  h+="</tr><tr><th>Mon</th>"; i=0; while(i<96) h+="<td id='a5w1q"+i++ +"'>&thinsp;</td>";
  h+="</tr><tr><th>Tue</th>"; i=0; while(i<96) h+="<td id='a5w2q"+i++ +"'>&thinsp;</td>";
  h+="</tr><tr><th>Wed</th>"; i=0; while(i<96) h+="<td id='a5w3q"+i++ +"'>&thinsp;</td>";
  h+="</tr><tr><th>Thu</th>"; i=0; while(i<96) h+="<td id='a5w4q"+i++ +"'>&thinsp;</td>";
  h+="</tr><tr><th>Fri</th>"; i=0; while(i<96) h+="<td id='a5w5q"+i++ +"'>&thinsp;</td>";
  h+="</tr><tr><th>Sat</th>"; i=0; while(i<96) h+="<td id='a5w6q"+i++ +"'>&thinsp;</td>";
  h+="</tr></table><div>Processed <span id='a5dtcp'>0</span> edits so far <span id='a5dtco'></span>";
  h+=". Key: <span style='background-color:#000000'>0 edits, </span>";
  h+="<span style='background-color:#00FF00'><span id='a5dtck1'>0</span> edits, </span>";
  h+="<span style='background-color:#FFFF00'><span id='a5dtck2'>0</span> edits, </span>";
  h+="<span style='background-color:#FF0000'><span id='a5dtck3'>0</span> edits.</span></div>";
  document.getElementById('contentSub').innerHTML=h;
  if(ais523dtc_counts==null) ais523dtc_counts=new Array();
  ais523dtc_max=null;
  ais523dtc_prog=0;
  ais523dtc_sg=true;
  i=7*96; while(i--) ais523dtc_counts[i]=0;
  aecwpajax.download({url:'//en.wikipedia.org/w/api.php?action=query&list=usercontribs&'+
    'ucuser='+encodeURIComponent(trg)+'&ucprop=timestamp&format=xml&uclimit=100',
    onSuccess:ais523dtcStep});
  aecwpajax.download({url:'//en.wikipedia.org/w/api.php?action=query&list=users&ususers='
    +encodeURIComponent(trg)+'&usprop=editcount&format=xml', onSuccess:ais523dtcook});
}{{subst:iusc|User:Kaniivel/IPLabeller.js}}
importScript( 'User:Dschwen/MegapixelDisplay.js' );
$.when(
    mw.loader.using([ "mediawiki.api"]),
    $.ready
).then(function() {

	var config = mw.config.get(['wgPageName', 'wgArticleId', 'wgNamespaceNumber']);
	if (config.wgNamespaceNumber !== 0 || config.wgArticleId === 0) {
		return;
	}

	var minMax = function(number, min, max) {
		if (number < min) {
			return min;
		} else if (number > max) {
			return max;
		}
		return number;
	};

	config.hours = minMax(window.EWC_hours || 24, 2, 72);
		
	var api = new mw.Api({
		ajax: {
			headers: { 
				"Api-User-Agent": "EditWarChecker ( https://en.wikipedia.org/wiki/User:Evad37/EditWarChecker )"
			}
		}
	});
	
	api.get({
		"action": "query",
		"format": "json",
		"prop": "revisions",
		"titles": config.wgPageName,
		"rvprop": "ids|timestamp|flags|comment|user|tags|size",
		"rvslots": "*",
		"rvlimit": "max",
		"rvend": new Date(Date.now() - config.hours*60*60*1000).toISOString()
	})
	.then(function(response) {
	    var revertUsers = {};
	    var maxUserReverts = 0;
	    function addRevert(user) {
	        if (revertUsers[user]) {
	            revertUsers[user] = revertUsers[user]++;
	        } else {
	            revertUsers[user] = 1;
	        }
	        if (revertUsers[user] > maxUserReverts) {
	            maxUserReverts = revertUsers[user] ;
	        }
	    }
	    var reverts = (response.query.pages[config.wgArticleId].revisions || [])
	        .filter(function(revision, index, arr) {
	            // Edits tagged by system
	            if (revision.tags.includes("mw-undo") || revision.tags.includes("mw-rollback")) {
	                addRevert(revision.user);
	                return true;
	            }
	            // Edits with undo/revert comment
	            if (/(?:undo|undid|revert)/i.test(revision.comment)) {
	                addRevert(revision.user);
	                return true;
	            }
	            // Edits with same kB change but opposite signs
	            var thisEditDiffSize = revision.size - (arr[index+1] ? arr[index+1].size : NaN);
	            var prevEditDiffSize = (arr[index+1] ? arr[index+1].size : NaN) - (arr[index+2] ? arr[index+2].size : NaN);
	            var minDiffSize = 10; // prevent false positives from minor copyedits
	            if (thisEditDiffSize && prevEditDiffSize && thisEditDiffSize > minDiffSize && thisEditDiffSize === prevEditDiffSize * -1) {
	                addRevert(revision.user);
	                return true;
	            }
	            
	            return false;
	        });
	    
	    var revertCount = reverts.length;
	    var revertUsersCount = Object.keys(revertUsers).length;
	    var usersToList = $.map(revertUsers, function(reverts, user) {
	        return reverts > 3 ? user + " (" + reverts + ")" : null;
	    }).join(", ");
	    
	    var message = "Approximately " + revertCount +
	        (revertCount===1
	            ? " revert"
	            : " reversions"
	        ) +
	        (revertUsersCount>0
	            ? " by " + revertUsersCount +
	                (revertUsersCount===1
	                    ? " user"
	                    : " users"
	                )
	            : ""
	        ) +
	        " in the last " + config.hours + " hours." + 
	        (usersToList
	            ? "\nUsers with 4+ reverts: " + usersToList
	            : ""
	        );
	    
	    var $symbol = $("<img width='20' height='20' style='float:right;cursor:pointer;' title='Edit war checker'>");
	
	    switch(true) {
	        // Grey (non-alert): No reverts, or single revert
	        case revertCount <= 1:
	            $symbol.attr("src", "https://upload.wikimedia.org/wikipedia/commons/e/e1/ButtonGray.svg");
	            break;
	        // Green alert: Everyone well within 3RR; not too many total reverts
	        case maxUserReverts <= 2 && revertCount <= 5:
	            $symbol.attr("src", "https://upload.wikimedia.org/wikipedia/commons/3/3c/ButtonGreen.svg");
	            break;
	        // Orange alert: One or more users at the 3RR limit; and/or quite a few reverts
	        case maxUserReverts <= 3 && revertCount <= 10:
	            $symbol.attr("src", "https://upload.wikimedia.org/wikipedia/commons/a/a6/ButtonOrange.svg");
	            break;
	        // Red alert: One or more users are passed the 3RR limit; and/or there are a lot of reverts
	        default:
	            $symbol.attr("src", "https://upload.wikimedia.org/wikipedia/commons/9/97/ButtonRed.svg");
	            break;
	    }
	
	    $symbol.click(function() {
	        mw.notify( message, { title: "Edit war checker", tag: "editWarCheck" } ); 
	    });
	
	    $('h1').append($symbol);
	
	})
	.catch(function(code, errorInfo) {
		console.error("EditWarChecker error", errorInfo || code || "Unknown error");
		var message = errorInfo && errorInfo.info || code.toString() || "Unknown error";
		mw.notify(message, {title: "EditWarChecker failed to load"});
	});
});
mw.loader.load( 'https://en.wikipedia.org/w/index.php?title=User:Volker_E._(WMF)/dark-mode.css&action=raw&ctype=text/css', 'text/css' );
// User has dark color scheme preference set in operating system.
if ( window.matchMedia( '( prefers-color-scheme: dark )' ).matches || mw.storage.get( 'client-dark-mode-toggle' ) === '1' ) {
	document.documentElement.className = 'client-dark-mode';
}

$( function () {
	// Fix logos.
	$( '#p-logo' ).clone().addClass( 'mw-no-invert' ).insertAfter( '#p-logo' );
	
	mw.util.addPortletLink( 'p-personal', '#', 'Dark mode', 'pt-darkmode', '', 'np', '#pt-watchlist' );

    $( '#pt-darkmode' ).on( 'click', function( e ) {
    	var $html = $( 'html' );
        e.preventDefault();
        
        $html.toggleClass( 'client-dark-mode' );
        mw.storage.set(
			'client-dark-mode-toggle',
			String( Number( $html.hasClass( 'client-dark-mode' ) ) )
		);
    } );
} );
( function() {

	// works on any display element
	$.fn.swlActivateColorPicker = function( callback ) {    
		if (this.length > 0 && !$colorPalette) {
			constructPalette();
		}
		return this.each( function() { 
			attachColorPicker( this, callback );
		} );
	};

	$.fn.swlDeactivateColorPicker = function() {    
		return this.each( function() { 
			deattachColorPicker( this );
		} );
	};

	// set background color of elements using the palette within this class
	$.fn.swlSetColor = function( paletteIndex ) {    
		return this.each( function() { 
			setColor( this, paletteIndex );
		} );
	};

	var colorPickerOwner;
	var $colorPalette = null;
	var paletteVisible = false;
	var onChangeCallback = null;  // should be able to vary for each color picker using a subclosure (not today)

	var constructPalette = function() {
		$colorPalette = $( "<div />" )
		.css( {
			width: '97px',
			position: 'absolute',
			border: '1px solid #0000bf',
			'background-color': '#f2f2f2',
			padding: '1px'
		} );

		// add each color swatch to the pallete
		$.each( colors, function(i) {
			$("<div>&nbsp;</div>").attr("flag", i)
			.css( {
				height: '12px',
				width: '12px',
				border: '1px solid #000',
				margin: '1px',
				float: 'left',
				cursor: 'pointer',
				'line-height': '12px',
				'background-color': "#" + this
			} )
			.bind( "click", function() { 
				changeColor( $(this).attr("flag"), $(this).css("background-color") )
			} )
			.bind( "mouseover", function() { 
				$(this).css("border-color", "#598FEF"); 
			} ) 
			.bind( "mouseout", function() { 
				$(this).css("border-color", "#000");
			} )
			.appendTo( $colorPalette );
		} );
		$("body").append( $colorPalette );
		$colorPalette.hide();
	};

	var attachColorPicker = function( element, callback ) {
		onChangeCallback = callback;
		$( element )
		.css( {
			border: '1px solid #303030',
			cursor: 'pointer'
		} )
		.bind("click", togglePalette);
	};

	var deattachColorPicker = function(element) {
		if ($colorPalette) {
			$( element )
			.css( {
				border: 'none',  // should restore previous value
				cursor: 'default'  // should restore previous value
			} )
			.unbind("click", togglePalette);
			hidePalette();
		}
	};

	var setColor = function( element, paletteIndex ) {
		$(element).css( {
			'background-color': '#' + colors[ paletteIndex ]
		} );
		var bright = brightness( colors[ paletteIndex ] );
		if ( bright < 128 ) {
			$(element).css( "color", "#ffffff" );  // white text on dark background
		}
		else {
			$(element).css( "color", "" );
		}
	};

	var checkMouse = function(event) {
	
		// check if the click was on the palette or on the colorPickerOwner
		var selectorParent = $(event.target).parents($colorPalette).length;
		if (event.target == $colorPalette[0] || event.target == colorPickerOwner || selectorParent > 0) {
			return;
		}
		hidePalette();   
	};

	var togglePalette = function() {
		colorPickerOwner = this; 
		paletteVisible ? hidePalette() : showPalette();
	};

	var hidePalette = function(){
		$(document).unbind( "mousedown", checkMouse );
		$colorPalette.hide();
		paletteVisible = false;
	};

	var showPalette = function() {
		$colorPalette
		.css( {
			top: $(colorPickerOwner).offset().top + ( $(colorPickerOwner).outerHeight() ),
			left: $(colorPickerOwner).offset().left
		} )
		.show();

		//bind close event handler
		$(document).bind("mousedown", checkMouse);
		paletteVisible = true;
	};

	var changeColor = function( paletteIndex, newColor) {
		setColor( colorPickerOwner, paletteIndex );
		hidePalette();
		if ( typeof(onChangeCallback) === "function" ) {
			onChangeCallback.call( colorPickerOwner, paletteIndex );
		}
	};
	
	var brightness = function( hexColor ) {
		// returns brightness value from 0 to 255
		// algorithm from http://www.w3.org/TR/AERT

		var c_r = parseInt( hexColor.substr(0, 2), 16);
		var c_g = parseInt( hexColor.substr(2, 2), 16);
		var c_b = parseInt( hexColor.substr(4, 2), 16);

		return ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
	};

	var colors = [
		'ffffff', 'ffffbd','bdffc2', 'bdf7ff', 'b3d6f9', 'ffbdfa',
		'feb88a', 'ffff66','a3fe8a', '8afcfe', 'c1bdff', 'ff80e9',
		'ff7f00', 'ffd733','39ff33', '33fffd', '0ea7dd', 'cf33ff',
		'db0000', 'e0b820','0edd1f', '0ba7bf', '3377ff', 'a60edd',
		'990c00', '997500','0c9900', '008499', '1a0edd', '800099',
		'743436', '737434','347440', '346674', '1b0099', '743472' ];
} ) ();


/** Smart watchlist settings
*
* All settings are grouped together to support save, load, undo, import and export.
* Child objects are read from local storage or created on the fly.
* Structure of the settings object:
*
* settings: {
*    controls: {},
*       Used for control of the GUI and meta data about the settings object.
*       Not subject to undo or import operations, but it is saved, loaded and exported.
*
*    userCategories: [ (displayed category names in menu order, 1 based with no gaps)
*       1: {
*          key: category key,
*          name: category display name
*       },
*       2: ...
*    ],
*    nextCategoryKey: 1 (monotonically increasing key to link page categories with display names)
*    rebuildCategoriesOnUndo: "no" or "rebuild" (optimization for undo)
*
*    wikiList: [ (in display order when sorted by wiki)
*       0: {
*          domain: wiki domain (e.g., "en.wikipedia.org")
*          displayName: "English Wikipedia"
*       },
*       1: ...
*    ],
*    wikis: {
*       wiki domain 1: {
*          watchlistToken: [  // not included for home wiki/account
*             0: { token: tokenID,
*                  userName: username on remote wiki }
*             1: ...
*          ],
*          active: boolean,
*          expanded: boolen,
*          lastLoad: time,
*          pages {  // contains only pages with settings, not everything on a watchlist
*             pageID1: {
*                category: category key,
*                patrolled: revision ID,
*                flag: page flag key,
*                hiddenSections: {
*                   section 1 title: date hidden,
*                   ...
*                   }
*                hiddenRevs: {
*                   revID1: date hidden,
*                   ...
*                }
*             },
*             pageID2: ...
*          },
*          users {
*             username1: {
*                flag: user flag key,
*                hidden: date hidden
*             },
*             username2: ...
*          }
*       },
*       wiki domain 2: ...
*    }
* }
*/

// create a closure so the methods aren't global but we can still directly reference them
( function() {

	// global hooks for event handler callbacks into functions within the closure scope
	SmartWatchlist = {
		changeDisplayedCategory: function() {
			changeDisplayedCategory.apply(this, arguments);
		},
		changePageCategory: function() {
			changePageCategory.apply(this, arguments);
		},
		hideRev: function() {
			hideRev.apply(this, arguments);
		},
		patrolRev: function() {
			patrolRev.apply(this, arguments);
		},
		hideUser: function() {
			hideUser.apply(this, arguments);
		},
		processOptionCheckbox: function() {
			processOptionCheckbox.apply(this, arguments);
		},
		clearSettings: function() {
			clearSettings.apply(this, arguments);
		},
		undo: function() {
			undo.apply(this, arguments);
		},
		setupCategories: function() {
			if (setupCategories) {
				setupCategories.apply(this, arguments);
			}
			else {
				alert("Category editor did not load. Try reloading the page.");
			}
		}
	};
	
	var settings = {};
	var lastSettings = [];
	var maxSettingsSize = 2000000;
	var maxUndo = 100;  // dynamically updated
	var maxSortLevels = 4;
	
	// for local storage - use separate settings for each wiki user account
	var storageKey = "SmartWatchlist." + mw.config.get( 'wgUserName' );  
	var storage = null;
	
	var initialize = function() {
	
		// check for local storage availability
		try {
			if ( typeof(localStorage) === "object"  && typeof(JSON) === "object" ) {
				storage = localStorage;
			}
		}
		catch(e) {}  // ignore error in FF 3.6 with dom.storage.enabled=false
		
		readLocalStorage();  // load saved user settings
		initSettings();
		createSettingsPanel();
		
		// build menu to change the category of a page
		var $categoryMenuTemplate = $constructCategoryMenu( "no meta" )
				// no attributes other than onChange allowed so the menu can be rebuilt in setupCategories()!
				.attr( "onChange", "javascript:SmartWatchlist.changePageCategory(this, value);" );
			
		var lastPageID = null;
		var rowsProcessed = 0;
		
		// process each displayed change row
		$("table.mw-enhanced-rc tr").each( function() {
		
			rowsProcessed++;
			var $tr = $(this);
			var $td = $tr.find("td:last-child");
			var isHeader = false;
			
			// check if this is the header for an expandable list of changes
			if ( $tr.find(".mw-changeslist-expanded").length > 0 ) {
				isHeader = true;
				lastPageID = null;  // start of a new page section
			}

			/* Parse IDs from the second link. The link text can be of the following forms:
			     1. "n changes" - used on a header row for a collapsable list of changes
				 2. "cur" - an individual change within a list of changes to the same page
				 3. "diff" - single change with no header row 
				 4. "talk" - deleted revision. No page ID is present on such a row. */
				 
			var $secondLink = $td.find("a:eq(1)");  // get second <a> tag in the cell
			var href = $secondLink.attr("href");
			var linkText = $secondLink.text();
			var pageID = href.replace( /.*&curid=/, "" ).replace( /&.*/, "" );
			var revID = href.replace( /.*&oldid=/, "" ).replace( /&.*/, "" );
			var user = $td.find(".mw-userlink").text();
			
			// check if we were able to parse the page ID
			if ( !isNaN(parseInt(pageID)) ) {
				lastPageID = pageID;
			}
			// check for a deleted revision
			else if ( $td.find(".history-deleted").length > 0 && lastPageID ) {
				pageID = lastPageID;  // use page ID from the previous row in the same page, if any
			}
			// unable to determine type of row
			else {
				pageID = null;
				if (console) {
					console.log("SmartWatchlist: unable to parse row " + $td.text());
				}
			}
				
			if (pageID) {
					
				$tr.attr( {
					pageID: pageID,
					wiki: document.domain
				} );

				// check if we were able to parse the rev ID and have an individual change row
				if ( !isNaN(parseInt(revID) ) &&  
					 (linkText == "cur" || linkText == "diff") ) {

					// add the hide change link
					$tr.attr( "revID", revID );
					var $revLink = $("<a/>", {
						href: "javascript:SmartWatchlist.hideRev('" + pageID + "', '" + revID + "');",
						title: "Hide this change",
						text: "hide change"
					});
					$td.append( $( "<span/>" )
						.addClass( "swlRevisionButton" )
						.append( " [" ).append( $revLink ).append( "]" )
					);

					// add the patrol prior changes link
					var $patrolLink = $("<a/>", {
						href: "javascript:SmartWatchlist.patrolRev('" + pageID + "', '" + revID + "');",
						title: "Hide previous changes",
						text: "patrol"
					});
					$td.append( $( "<span/>" )
						.addClass( "swlRevisionButton" )
						.append( " [" ).append( $patrolLink ).append( "]" )
					);
				}

				// check if this is the top-level row for a page
				if ( isHeader || linkText == "diff") {
				
					// add the category menu with the current page category pre-selected
					$newMenu = $categoryMenuTemplate.clone();
					$td.prepend( $newMenu );
					
					// add the page attribute to the link to the page to support highlighting specific pages
					$td.find("a:eq(0)")  // get first <a> tag in the cell
						.attr( {
							pageID: pageID,
							wiki: document.domain
						} )
						.addClass( "swlPageTitleLink" );
				}
			}
			
			// check if we parsed a user for an individual change row
			if (user && !isHeader) {
				
				// mark change row for possible hiding/flagging
				$tr.attr( "wpUser", user );
				if ( !$tr.attr("wiki") ) {
					$tr.attr( "wiki", document.domain );
				}

				// add the hide user link
				var $hideUserLink = $("<a/>", {
					href: "javascript:SmartWatchlist.hideUser('" + user + "');",
					title: "Hide changes by " + user + " on all pages",
					text: "hide user"
				});
				$td.append( $( "<span/>" )
					.addClass( "swlHideUserButton" )
					.append( " [" ).append( $hideUserLink ).append( "]" )
				);
			}
		});  // close each()
		
		// set the user attribute for each username link to support highlighting specific users
		$(".mw-userlink").each( function() {
			var $userLink = $(this);
			$userLink.attr( {
				wiki: document.domain,
				wpUser: $userLink.text() 
			} )
			.addClass("swlUserLink");
		});
		
		initDisplayControls();
		
		// restore last displayed category and apply display settings
		changeDisplayedCategory( 
			selectCategoryMenu( $( "#swlSettingsPanelCategorySelector" ), getSetting("controls", "displayedCategory" ) ) );

		// check if we were able to do anything
		if (rowsProcessed == 0) {
			$("#SmartWatchlistOptions")
				.append( $( "<p/>", {
						text: 'To use Smart Watchlist, enable "enhanced recent changes" in your user preferences.' 
					} )
					.css("color", "#cc00ff")
				);
		}
	};

	var initDisplayControls = function() {
		// set visibility of buttons and pulldowns shown on each change row
		$( ".swlOptionCheckbox" ).each( function() {
			$checkbox = $(this);
			
			// restore saved checkbox setting
			$checkbox.attr( "checked", getSetting("controls", [ $checkbox.attr("controlsProperty") ] ) );
			
			// apply checkbox value to buttons
			processOptionCheckbox( this );
		} );
	};

	// if the desired category exists, pre-select it in the menu
	// otherwise, fallback to the default selection
	var selectCategoryMenu = function( $selector, category ) {
	
		// check if page category has been deleted
		if ( typeof( category ) === "undefined" ) {
			$selector.attr("selectedIndex", "0");  // fallback to first option
		}
		else {
			// attempt to use set page category
			$selector.val( category );
			if ( $selector.val() == null ) {
				// desired category not in the menu, fallback to first option
				$selector.attr("selectedIndex", "0");
			}
		}
		return $selector.val();  // return actual category selected
	};

	// called when the displayed category menu setting is changed
	var changeDisplayedCategory = function(category) {
		setSetting( "controls", "displayedCategory", category );
		applySettings();
		writeLocalStorage();
	};
	
	// called when the category for a page is changed
	var changePageCategory = function( td, category ) {
	
		var $tr = $( td.parentNode.parentNode );
		var pageID = $tr.attr( "pageID" );
		var wiki = $tr.attr( "wiki" );
		
		// convert category to a number if possible
		if ( typeof( category ) === "string" ) {
			var intCategory = parseInt( category );
			if ( !isNaN( intCategory ) ) {
				category = intCategory;
			}
		}
		
		// update category selection menus for all other instances of the page
		$( 'tr[wiki="' + document.domain + '"][pageID="' + pageID + '"] select' ).val( category );
		
		// update settings
		snapshotSettings("change page category");
		
		if ( category == "uncategorized" ) {
			deleteSetting("wikis", document.domain, "pages", pageID, "category")
		} else {
			setSetting("wikis", document.domain, "pages", pageID, "category", category);
		}
		writeLocalStorage();

		// hide the page immediately if auto refresh
		applySettings();
	};
	
	// callback for "hide change"
	var hideRev = function( pageID, revID ) {

		var mode = getSetting( "controls", "displayedCategory" );
		
		// hide the rows unless displaying everything currently
		if ( mode != "all+" ) {
			var $tr = $( 'tr[wiki="' + document.domain + '"][revID="' + revID + '"]' );  // retrieve individual change row
			hideElements($tr);
			suppressHeaders();
		}
		
		// update settings
		snapshotSettings("hide change");
		if ( mode == "hide" ) {
			deleteSetting( "wikis", document.domain, "pages", pageID, "hiddenRevs", revID );  // unhide
		}
		else {
			setSetting( "wikis", document.domain, "pages", pageID, "hiddenRevs", revID, new Date() );  // hide
		}
		writeLocalStorage();
	};
	
	// callback for "patrol"
	var patrolRev = function( pageID, revID ) {

		var mode = getSetting( "controls", "displayedCategory" );
		
		// hide the rows unless displaying everything currently
		if ( mode != "all+" ) {
			var $tr = $( 'tr[wiki="' + document.domain + '"][pageID="' + pageID + '"]' ).filter( function() {  // filter all rows for the page
				var rowRevID = $(this).attr("revID");
				return (rowRevID <= revID);
			});
			hideElements($tr);
			suppressHeaders();
		}
		
		// update settings
		snapshotSettings("patrol action");
		setSetting("wikis", document.domain, "pages", pageID, "patrolled", revID);
		writeLocalStorage();
	};
	
	// callback for "hide user"
	var hideUser = function( user ) {
	
		var mode = getSetting( "controls", "displayedCategory" );

		// hide the rows unless displaying everything currently
		if ( mode != "all+" ) {
			var $tr = $( 'tr[wiki="' + document.domain + '"][wpUser="' + user + '"]' );  // retrieve all changes by user
			hideElements($tr);
			suppressHeaders();
		}

		// update settings
		snapshotSettings("hide user");
		if ( mode == "hide" ) {
			deleteSetting( "wikis", document.domain, "users", user, "hide" );  // unhide
		}
		else {
			setSetting( "wikis", document.domain, "users", user, "hide", new Date() );  // hide
		}
		writeLocalStorage();
	};
	
	// toggle the state of a given class of user interface elements
	var processOptionCheckbox = function( checkbox ) {
		var $checkbox = $(checkbox);
		var $elements = $( "." + $checkbox.attr("controlledClass") );
		if ( checkbox.checked ) {
			if ( $checkbox.hasClass("swlColorPickerControl") ) {
				$elements
				.attr( "onClick", "javascript:return false;")  // disable links so color picker can activate
				.swlActivateColorPicker( setFlag );
			}
			else {
				$elements.show();
			}
		} else {
			if ( $checkbox.hasClass("swlColorPickerControl") ) {
				$elements
				.attr( "onClick", "")  // re-enable links
				.swlDeactivateColorPicker();
			}
			else {
				$elements.hide();
			}
		}
		setSetting( "controls", $checkbox.attr("controlsProperty"), checkbox.checked );
		writeLocalStorage();
	};
	
	// callback from the color picker to flag a user or page
	var setFlag = function( flag ) {
	
		$this = $(this);  // element to be flagged
		var $tr = $this.parents( "tr[wiki]" );
		var wiki = $tr.attr( "wiki" );
		var idLabel;
		var settingPath;
		var $idElement;
		
		if ( $this.hasClass("swlUserLink") ) {
			idLabel = "wpUser";
			$idElement = $this;
			settingPath = "users";
		}
		else {
			idLabel = "pageID";
			$idElement = $tr;
			settingPath = "pages";
		}
		
		var id = $idElement.attr( idLabel );
		
		if ( typeof(id) === "string" ) {
			snapshotSettings("highlight");

			// update the color on all other instances of the element
			$( 'a[wiki="' + wiki + '"][' + idLabel + '="' + id + '"]' ).swlSetColor( flag );

			// update settings
			flag = parseInt( flag );
			if ( !isNaN( flag ) && flag > 0 ) {
				setSetting( "wikis", wiki, settingPath, id, "flag", flag );
			}
			else {
				deleteSetting("wikis", wiki, settingPath, id, "flag");
			}
			writeLocalStorage();
		}
	};
	
	// hide header rows that don't have any displayed changes
	var suppressHeaders = function() {
	
		// process all change list tables (page headers + changes)
		var $tables = $("table.mw-enhanced-rc");
		$tables.each( function( index ) {
		
			var $table = $(this);
			
			// check if this is a header table with a following table
			if ( $table.filter( ":has(.mw-changeslist-expanded)" ).length > 0 &&
			     index + 1 < $tables.length ) {

				// check if the following table has visible changes
				var $visibleRows = $tables.filter( ":eq(" + (index + 1) + ")" )
					.find( "tr" )
					.not( ".swlHidden" );
					
				if ( $visibleRows.length == 0 ) {
					hideElements($table);
				}
			}
		});
	};
	
	// hide a set of jQuery elements and apply our own class 
	// to support header suppression and later unhiding
	var hideElements = function( $elements ) {
		$elements.hide();
		$elements.addClass("swlHidden");
	};

	// reinitialize displayed content using current settings
	var applySettings = function() {
	
		var displayedCategory = getSetting( "controls", "displayedCategory" );
		
		// show all changes, including heading tables
		$( ".swlHidden" ).each( function() {
			var $element = $(this);
			$element.show()
			$element.removeClass("swlHidden");
		});

		if ( displayedCategory != "all+" && displayedCategory != "hide" ) {  // XXX should showing these be a new option?
		
			// hide changes by set users
			$( 'tr[wiki="' + document.domain + '"][wpUser]').each( function() {
				var $tr = $(this);
				if ( getSetting( "wikis", document.domain, "users", $tr.attr("wpUser"), "hide" ) ) {
					hideElements($tr);
				}
			});
		}
		
		// process each change row
		$( 'tr[wiki="' + document.domain + '"][pageID]').each( function() {
			var $tr = $(this);
			var pageID = $tr.attr("pageID");
			var revID = $tr.attr("revID");
			var pageCategory = getSetting( "wikis", document.domain, "pages", pageID, "category" );
			var pageFlag = getSetting( "wikis", document.domain, "pages", pageID, "flag" );
			
			// check if there is a page category menu on the row
			var $select = $tr.find( 'select' );
			if ( $select.length == 1 ) {
			
				// select proper item in the menu
				var newCategoryKey = selectCategoryMenu( $select, pageCategory );
					
				// reset page category if the current category has been deleted
				if ( pageCategory && pageCategory != newCategoryKey ) {
					deleteSetting( "wikis", document.domain, "pages", pageID, "category");
					pageCategory = newCategoryKey;
				}
			}

			// check if change should be hidden
			// XXX should we show changes by hidden users when in "hidden" display mode? Maybe a new option.
			var visible;
			
			if (displayedCategory == "all+") {
				visible = true;
			}
			else if ( revID &&
				(  getSetting( "wikis", document.domain, "pages", pageID, "hiddenRevs", revID ) ||  // specific revision is hidden
				  getSetting( "wikis", document.domain, "pages", pageID, "patrolled" ) >= revID  // revision has been patrolled
				) ) {
				visible = false;
			}
			// check if page is hidden
			else if ( pageCategory == "hide" && displayedCategory != "hide" ) {
				visible = false;
			} 
			else if (displayedCategory == "all") {
				visible = true;
			}
			// check for no category
			else if ( displayedCategory == "uncategorized" ) {
				if (pageCategory) {
					visible = false;
				} else {
					visible = true;
				}
			}
			// check if page is flagged
			else if ( displayedCategory == "flag" && typeof(pageFlag) !== "undefined" ) {
				visible = true;
			}
			// check for selected category
			else if ( pageCategory && displayedCategory == pageCategory ) {
				visible = true;
			} 
			else {
				visible = false;
			}
			
			if ( !visible ) {
				hideElements($tr);
			}
		});
		
		// hide changes to unknown pages if not displaying all pages
		if ( displayedCategory != "all+" && displayedCategory != "all" && displayedCategory != "uncategorized" ) {
			hideElements( $("table.mw-enhanced-rc tr").not( '[pageID]') );
		}
		
		// decorate user links
		$(".mw-userlink").each( function() {
			var $userLink = $(this);
			var user = $userLink.attr( "wpUser" );
			var flag =  getSetting( "wikis", document.domain, "users", user, "flag" );
			if ( typeof( flag ) == "number" ) {
				$userLink.swlSetColor( flag );
			} else {
				$userLink.swlSetColor( 0 );
			}
		});
		
		// decorate page titles
		$( 'a[pageID]').each( function() {
			var $pageTitleLink = $(this);
			var flag = getSetting( "wikis", document.domain, "pages", [ $pageTitleLink.attr("pageID") ], "flag" );
			if ( typeof( flag ) == "number" ) {
				$pageTitleLink.swlSetColor( flag );
			} else {
				$pageTitleLink.swlSetColor( 0 );
			}
		});
		
		suppressHeaders();
	};
	
	// add smart watchlist settings panel below the standard watchlist options panel
	var createSettingsPanel = function() {
	
		// construct panel column 1
		var $column1 = $( "<td />" ).attr("valign", "top")
			.append( 
				$( "<input>", {
					type: "checkbox",
					"class": "swlOptionCheckbox",
					controlledClass: "swlRevisionButton",
					controlsProperty: "showRevisionButtons",
					onClick: "javascript:SmartWatchlist.processOptionCheckbox(this);"
				} )
			)
			.append("Enable hide/patrol change buttons")
			.append( "<br />" )
			.append( 
				$( "<input>", {
					type: "checkbox",
					"class": "swlOptionCheckbox",
					controlledClass: "swlHideUserButton",
					controlsProperty: "showUserButtons",
					onClick: "javascript:SmartWatchlist.processOptionCheckbox(this);"
				} )
			)
			.append("Enable hide user buttons")
			.append( "<br />" )
			.append( 
				$( "<input>", {
					type: "checkbox",
					"class": "swlOptionCheckbox swlColorPickerControl",
					controlledClass: "swlUserLink",
					controlsProperty: "showUserColorPickers",
					onClick: "javascript:SmartWatchlist.processOptionCheckbox(this);"
				} )
			)
			.append("Assign user highlight colors")
			.append( "<br />" )
			.append( 
				$( "<input>", {
					type: "checkbox",
					"class": "swlOptionCheckbox swlColorPickerControl",
					controlledClass: "swlPageTitleLink",
					controlsProperty: "showPageColorPickers",
					onClick: "javascript:SmartWatchlist.processOptionCheckbox(this);"
				} )
			)
			.append("Assign page highlight colors")
			.append( "<br />" )
			.append( 
				$( "<input>", {
					type: "checkbox",
					"class": "swlOptionCheckbox",
					controlledClass: "swlPageCategoryMenu",
					controlsProperty: "showPageCategoryButtons",
					onClick: "javascript:SmartWatchlist.processOptionCheckbox(this);"
				} )
			)
			.append("Assign page categories");
		
		// construct panel column 2
		var $column2 = $( "<div />" )
			.attr("style", "padding-left: 25pt;")
			.append( 
				$( "<div />" ).attr("align", "center")
				.append(
					$("<input />", {
						type: "button",
						onClick: "javascript:SmartWatchlist.clearSettings();",
						title: "Reset all page and user settings and remove all custom categories",
						value: "Clear settings"
					} ) 
				)
				.append("&nbsp;&nbsp;")
				.append(
					$("<input />", {
						type: "button",
						onClick: "javascript:SmartWatchlist.setupCategories();",
						title: "Create, change and delete custom category names",
						value: "Setup categories"
					} )
				)
				.append("&nbsp;&nbsp;")
				.append(
					$("<input />", {
						type: "button",
						id: "swlUndoButton",
						onClick: "javascript:SmartWatchlist.undo();",
						title: "Nothing to undo",
						disabled: "disabled",
						value: "Undo"
					} ) 
				)
				.append( "<p />" )
				.append( "Display pages in:&nbsp;" )
				.append( 
					$constructCategoryMenu( "meta" )
						// no attributes other than onChange allowed so the menu can be rebuild in setupCategories()!
						.attr( "onChange", "javascript:SmartWatchlist.changeDisplayedCategory(value);" )
				)
			);

		$sortPanel = $( "<div />" ).attr("align", "right")
			.append( "Sort order:&nbsp;" );
		
		for (var i = 0; i < maxSortLevels; i++) {
			$sortPanel
			.append( $constructSortMenu().attr("selectedIndex", i) )
			.append( "<br />" );
			if (i == 0) {
				$sortPanel.append( "(not yet)&nbsp;&nbsp;" );
			}
		}
		
		// construct panel column 3
		var $column3 = $( "<div />" )
			.attr("style", "padding-left: 25pt;")
			.append( $sortPanel );
			
		// construct main settings panel
		$("#mw-watchlist-options")
			.after( 
				$( "<fieldset />", {
					id: "SmartWatchlistOptions"
				} )
				.append( 
					$( "<legend />", {
						text: "Smart watchlist settings"
					} ) 
				)
				.append( 
					$( "<table />" )
					.append( 
						$( "<tr />" )
						.append( $column1 )
						.append( 
							$( "<td />", {
								valign: "top"
							} )
							.append( $column2 )
						)
						.append( 
							$( "<td />", {
								valign: "top"
							} )
							.append( $column3 )
						)
					)
				)
			);
		
		if ( !storage ) {
			$("#SmartWatchlistOptions")
			.append( 
				$( "<p />", {
					text: "Your browser does not support saving settings to local storage. " +
					"Items hidden or highlighted will not be retained after reloading the page."
				} )
				.css("color", "red")
			);
		}
	};

	// construct a page category menu
	var $constructCategoryMenu = function( metaOptionString ) {

		var $selector = 
			$( "<select />", {
				"class": "namespaceselector swlCategoryMenu",
				withMeta: metaOptionString  // flag so the menu can be rebuilt in setupCategories()
			} );

		if (metaOptionString == "meta") {
			// for updating the displayed category selection
			$selector.attr( "id", "swlSettingsPanelCategorySelector");
		}
		else {
			// for hiding/showing page category menus
			$selector.addClass( "swlPageCategoryMenu" );
		}

		// create default category, must be first in the menu!!!
		var categories = [
			{ value: "uncategorized", text: "uncategorized" }
		];
		
		
		// add user categories, if any
		var userCategories = getSetting("userCategories");
		if ( typeof(userCategories) === "object" ) {
			for (var i = 0; i < userCategories.length && userCategories[i]; i++) {
				var key = userCategories[i].key;
				if ( typeof(key) !== "number" ) {
					alert("Smart watchlist user category definitions are corrupt. You will need to clear your settings. Sorry.");
					break;
				}
				else {
					categories.push( { value: userCategories[i].key, text: userCategories[i].name } )
				}
			}
		}
		
		// add special categories to settings menu
		if (metaOptionString == "meta") {
			categories.push(
				{ value: "all", text: "all except hidden" },
				{ value: "flag", text: "highlighted" }
			);
		}

		categories.push( { value: "hide", text: "hidden" } );
		
		if (metaOptionString == "meta") {
			categories.push( { value: "all+", text: "everything" } );
		}

		// construct all <option> elements
		for (var i in categories) {
			$selector.append( $( "<option />", categories[i] ) );
		}
		return $selector;
	};

	// construct a page category menu
	var $constructSortMenu = function() {

		var $selector = 
			$( "<select />", {
				"class": "namespaceselector swlSortMenu"
			} );

		var sortCriteria = [
			{ value: "wiki", text: "Wiki" },
			{ value: "title", text: "Title" },
			{ value: "timeDec", text: "Time (newest first)" },
			{ value: "timeInc", text: "Time (oldest first)" },
			{ value: "risk", text: "Vandalism risk" },
			{ value: "namespace", text: "Namespace" },
			{ value: "flagPage", text: "Highlighted pages" },
			{ value: "flagUser", text: "Highlighted users" }
		];
		
		// construct all <option> elements
		for (var i in sortCriteria) {
			$selector.append( $( "<option />", sortCriteria[i] ) );
		}
		return $selector;
	};

	// save settings for later undo
	var snapshotSettings = function( currentAction, rebuildOption ) {

		if (typeof(rebuildOption) === "undefined") {
			rebuildOption = "no";
		}
		setSetting("rebuildCategoriesOnUndo", rebuildOption);
		
		var settingsClone = $.extend( true, {}, settings );
		lastSettings.push( settingsClone );
		while (lastSettings.length > maxUndo) {
			lastSettings.shift();
		}
		
		if (currentAction) {
			currentAction = "Undo " + currentAction;
		} else {
			currentAction = "Undo last change";
		}
		setSetting("undoAction", currentAction);
		$( "#swlUndoButton" )
			.attr("disabled", "")
			.attr( "title", currentAction );
	};

	// restore previous settings
	var undo = function() {
		if (lastSettings.length > 0) {
		
			var currentControls = settings.controls;
			settings = lastSettings.pop();
			settings.controls = currentControls;  // controls aren't subject to undo
			
			// only rebuild menus when needed because it takes several seconds
			if (getSetting("rebuildCategoriesOnUndo") == "rebuild") {
				rebuildCategoryMenus();  // also updates display and local storage
			}
			else {
				writeLocalStorage();
				applySettings();
			}
			
			var lastAction = getSetting("undoAction");
			if (!lastAction) {
				lastAction = "";
			}
			$( "#swlUndoButton" ).attr( "title", lastAction );
			
			if (lastSettings.length == 0) {
				$( "#swlUndoButton" )
					.attr( "disabled", "disabled" )
					.attr( "title", "Nothing to undo" );
			}
		}
	};
	
	// for use after a change to the category settings
	var rebuildCategoryMenus = function() {
	
		// rebuild existing category menus
		$( '.swlCategoryMenu' ).each( function() {
			var $newMenu = $constructCategoryMenu( $(this).attr('withMeta') );
			$newMenu.attr( "onChange", $(this).attr("onChange") );  // retain old menu action
			this.parentNode.replaceChild( $newMenu.get(0), this );
		} );
		
		// update menu selections and save settings
		changeDisplayedCategory( 
			selectCategoryMenu( $( "#swlSettingsPanelCategorySelector" ), getSetting("controls", "displayedCategory" ) ) );
			
		initDisplayControls();
	};

	// read from local storage to current in-work settings during initialization
	var readLocalStorage = function() {
		if (storage) {
		
			var storedString = storage.getItem(storageKey);
			if (storedString) {

				try {
					settings = JSON.parse( storedString );
				}
				catch (e) {
					alert( "Smart watchlist: error loading stored settings!" );
					settings = {};
				}
			}
		
			// delete all obsolete local storage keys from prior versions and bugs
			// this can eventually go away
			var obsoleteKeys = [
				"undefinedmarkedUsers",
				"undefinedmarkedPages",
				"undefinedpatrolledRevs",
				"undefinedhiddenRevs",
				"undefinedGUI",
				"SmartWatchlist.flaggedPages",
				"SmartWatchlist.flaggedUsers",
				"SmartWatchlist.hiddenPages",
				"SmartWatchlist.hiddenUsers",
				"SmartWatchlist.markedUsers",
				"SmartWatchlist.markedPages",
				"SmartWatchlist.patrolledRevs",
				"SmartWatchlist.hiddenRevs",
				"SmartWatchlist.GUI",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".markedUsers",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".markedPages",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".patrolledRevs",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".userFlag",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".pageCategory",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".pageFlag",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".patrolledRevision",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".hiddenRevs",
				"SmartWatchlist." + mw.config.get( "wgUserName" ) + ".GUI",
				"length"
			];
			for (var i in obsoleteKeys) {
				if ( typeof( storage.getItem( obsoleteKeys[i]) ) !== "undefined" ) {
					storage.removeItem( obsoleteKeys[i] );
				}
			}
		}
	};
	
	// update local storage to current in-work settings
	var writeLocalStorage = function() {
		if (storage) {
			var storeString = JSON.stringify( settings );
			var size = storeString.length;
			if ( size > maxSettingsSize ) {
				storeString = "";
				alert( "Smart watchlist: new settings are too large to be saved (" + size + " bytes)!" )
				return;
			}
				
			var lastSaveString = storage.getItem(storageKey);

			try {
				storage.setItem( storageKey, storeString );
			}
			catch (e) {
				storeString = "";				
				alert( "Smart watchlist: error saving new settings!" );
				
				// revert to previously saved settings that seemed to work
				storage.setItem( storageKey, lastSaveString );
			}
			maxUndo = Math.floor( maxSettingsSize / size ) + 2;
		}
	};
	
	// erase all saved settings
	var clearSettings = function() {
		snapshotSettings("clear settings", "rebuild");
		var currentControls = settings.controls;
		settings = {};
		settings.controls = currentControls;  // controls aren't subject to clearing
		initSettings();
		rebuildCategoryMenus();  // also updates display and local storage
	};
	
	// lookup a setting path passed as a series of arguments
	// returns undefined if no setting exists
	var getSetting = function() {
		var obj = settings;
		for (var index in arguments) {
			if (typeof( obj ) !== "object") {
				return undefined;  // part of path is missing
			}
			obj = obj[ arguments[ index ] ];
		}
		return obj;
	};
	
	// set the value of a setting path passed as a series of argument strings
	// creates intermediate objects as needed
	// number arguments reference arrays and string arguments reference associative array properties
	// the last argument is the value to be set (can be any type)
	var setSetting = function() {
		if (arguments.length < 2) {
			throw "setSetting: insufficient arguments";
		}
		var obj = settings;
		for (var index = 0; index < arguments.length - 2; index++) {
			var nextObj = obj[ arguments[ index] ];
			if (typeof( nextObj ) !== "object") {
				if ( typeof( arguments[ index + 1 ] ) === "number" ) {
					nextObj = obj[ arguments[ index ] ] = [];
				} else {
					nextObj = obj[ arguments[ index ] ] = {};
				}
			}
			obj = nextObj;
		}
		obj[ arguments[ arguments.length - 2 ] ] = arguments[ arguments.length - 1 ];
	};
	
	// delete a setting path passed as a series of argument strings if the entire path exists
	var deleteSetting = function() {
		if (arguments.length < 1) {
			throw "deleteSetting: insufficient arguments";
		}
		var obj = settings;
		for (var index = 0; index < arguments.length - 1; index++) {
			// check if we hit a snag and still have more arguments to go
			if (typeof( obj ) !== "object") {
				return;
			}
			obj = obj[ arguments[ index ] ];
		}
		if (typeof( obj ) === "object") {
			delete obj[ arguments[ index ] ];
		}
	};

	var initSettings = function() {
	
		// check if home domain already exists
		if ( !getSetting("wikis", document.domain) ) {
			setSetting("wikis", document.domain, "active", true);
			var wikiNumber = 0;
			var wikiList = getSetting("wikiList");
			if (wikiList) {
				wikiNumber = wikiList.length;
			}
			setSetting("wikiList", wikiNumber, {
				domain: document.domain,
				displayName: document.domain
			} );
		}
		
		if ( !settings.nextCategoryKey ) {
			settings.nextCategoryKey = 1;
		}
	};

	// dialog windows
	var setupCategories = null;
	mw.loader.using( ['jquery.ui'], function() {

		setupCategories = function () {
		
			// construct a category name row for editing
			var addCategory = function ( key, name ) {
				$editTable.append( 
					$( '<tr />' )
					.append( 
						$( '<td />' ).append( $( '<span />' ).addClass( 'ui-icon ui-icon-arrowthick-2-n-s' ) )
					)
					.append(
						$( '<td />' ).append(
							$( '<input />', {
								type: 'text',
								size: '20',
								categoryKey: key,
								value: name
							} )
						)
					)
				);
			};
		
			// jQuery UI sortable() seems to only like <ul> top-level elements
			var $editTable = $( '<ul />' ).sortable( { axis: 'y' } );
			
			for (var i in settings.userCategories) {
				addCategory( settings.userCategories[i].key,
				             settings.userCategories[i].name );
			}
			if ( !getSetting( 'userCategories', 0 ) ) {
				addCategory( settings.nextCategoryKey++, '' );  // pre-add first category if needed
			}
			
			var $interface = $('<div />')
				.css( {
					'position': 'relative',
					'margin-top': '0.4em'
				} )
				.append( 
					$( '<ul />')
					.append( $( '<li />', { text: "Renamed categories retain current pages." } ) )
					.append( $( '<li />', { text: "Dragging lines changes the order in category menus." } ) )
					.append( $( '<li />', { text: "To delete a category, blank its name." } ) )
					.append( $( '<li />', { text: "Pages in deleted categories revert to uncategorized." } ) )
				)
				.append( $( '<br />' ) )
				.append( $editTable )
				.append( $( '<br />' ) )
				.dialog( {
					width: 400,
					autoOpen: false,
					title: 'Custom category setup',
					modal: true,
					buttons: { 
						'Save': function() { 
							$(this).dialog('close');
							snapshotSettings('category setup', 'rebuild');
							
							// replace category names in saved settings
							deleteSetting( 'userCategories' );
							var index = 0;
							$editTable.find('input').each( function() {

								var name = $.trim(this.value);
								if (name.length > 0) {  // skip blank categories
								
									// convert category key back into a number
									var key = $(this).attr('categoryKey');
									if ( typeof( key ) === "string" ) {
										var intKey = parseInt( key );
										if ( !isNaN( intKey ) ) {
											setSetting( 'userCategories', index++, {
												key: intKey,
												name: name
											} );
										}
									}
								}
							} );
							rebuildCategoryMenus();
						},
						'Add category': function() {
							addCategory( settings.nextCategoryKey++, '' );
						},
						'Cancel': function() { 
							$(this).dialog('close');
						}
					}
				} );
			$interface.dialog('open');
		}
	} );
	
	// activate only on the watchlist page
	if ( mw.config.get("wgNamespaceNumber") == -1 && mw.config.get("wgTitle") == "Watchlist" ) {
		$(document).ready(initialize);
	};
} ) ();
(function() {

/*
*	create <li> list item for one article
*	right now, just a link to the page & the date
*/
function makeCreatedPageItem(contrib) {
	var item = document.createElement("li");

	var link = document.createElement("a");
	link.href = mw.util.getUrl(contrib.title);
	link.innerHTML = contrib.title;

	item.appendChild(link);

	item.innerHTML += " . . " + new Date(contrib.timestamp).toDateString();

	return item;
}

/*
*	looks through all of a user's non-minor namespace 0 edits,
*	looking for edits tagged as "new"
*
*	the arguments all in one object so that it can be expanded
*	in the future without having to add a bunch more parameters
*/
function findPagesCreated(bundle) {
	bundle.api.get({
		action: "query",
		rawcontinue: '',
		list: "usercontribs",
		ucuser: bundle.user,
		ucstart: bundle.start,
		ucprop: "flags|title|timestamp",
		ucshow: "!minor",
		uclimit: 500,
		ucnamespace: 0
	}).done( function(data) {
		$.each(data.query.usercontribs, function(index, contrib) {
			if (contrib.new != undefined) {
				bundle.list.appendChild(makeCreatedPageItem(contrib));
				bundle.count++;
			}
		});

		if (data["query-continue"]) {	// more contributions
			bundle.start = data["query-continue"].usercontribs.ucstart
			setTimeout(function() { findPagesCreated(bundle); }, 3000);
		} else {	// done
			$("#pc-status")[0].innerHTML = "<br />" + bundle.user + " has created " + bundle.count + " articles";
		}
	}).fail( function(error) {
		alert(error);
	});
}

/*
*	change title, clear content area, etc.
*/
function setupPagesCreated(user) {
	// set new title
	mw.util.$content.find("#firstHeading")[0].innerHTML = "Pages created by " + user;

	// status bar (text + waiting gif)
	var status = document.createElement("span");
	status.id = "pc-status";
	status.innerHTML = "<br />Fetching user data...";

	// heading for results
	var heading = document.createElement("h3");
	heading.innerHTML = "Articles";

	// list of results
	var articles = document.createElement("ul");
	articles.id = "pc-articles";

	var body = mw.util.$content.find("#bodyContent")[0];
	body.innerHTML = "";
	body.appendChild(status);
	body.appendChild(heading);
	body.appendChild(articles);

	var api = new mw.Api();
	api.get({
		action: "query",
		list: "users",
		ususers: user,
		usprop: "editcount"
	}).done(function(data) {
		// 500 results per request, 1 request every 3 seconds
		var count = data.query.users[0].editcount;
		status.innerHTML = "<br />User has " + count + " edits, this should take less than ";
		status.innerHTML += (3 * Math.round(count / 500)) + " seconds &nbsp;&nbsp;&nbsp;";

		var waitgif = document.createElement("img");
		waitgif.src = "https://upload.wikimedia.org/wikipedia/commons/4/42/Loading.gif";
		status.appendChild(waitgif);

		findPagesCreated(
			{"api": api,
			"user": user,
			"list": articles,
			"start": "",
			"count": 0}
		);
	}).fail(function(error) {
		alert(error);
	});

	return false;
}

mw.loader.using("mediawiki.util", function() {
	// add portlet when page is User or User_talk, but not on subpages
	if ((mw.config.get('wgNamespaceNumber') == 2 || mw.config.get('wgNamespaceNumber') == 3) && (mw.config.get('wgTitle').indexOf("/") == -1)) {
		if (mw.util.getParamValue("pagesCreated")) {
			setupPagesCreated(mw.config.get('wgTitle'));
		} else {
			mw.util.addPortletLink("p-tb",
				document.location.toString() + "?pagesCreated=true",
				"Pages created", "pc-pages",
				"Get a list of all pages created by this user"
			);
		}
	}
});

})();
if ( mw.config.get( 'wgNamespaceNumber', 0 ) >= 0 ) {
	mw.hook( 'wikipage.content' ).add( function ( $content ) {
		var uploadBaseRe = /^(https:)?\/\/upload\.wikimedia\.org\/wikipedia\/commons/,
			localFileNSString = mw.config.get( 'wgFormattedNamespaces' )['6'] + ':',
			localBasePath = new RegExp( '^' + mw.util.escapeRegExp( mw.util.getUrl( localFileNSString ) ) ),
			localBaseScript = new RegExp( '^' + mw.util.escapeRegExp(
				mw.util.wikiScript() + '?title=' + mw.util.wikiUrlencode( localFileNSString )
			) ),
			commonsBasePath = 'https://commons.wikimedia.org/wiki/File:',
			commonsBaseScript = 'https://commons.wikimedia.org/w/index.php?title=File:';

		$content.find( 'a.image, a.mw-file-description' ).attr( 'href', function ( i, currVal ) {
			if ( uploadBaseRe.test( $( this ).find( 'img' ).attr( 'src' ) ) ) {
				return currVal
					.replace( localBasePath, commonsBasePath )
					.replace( localBaseScript, commonsBaseScript );
			}
		} );
	} );
}
/**
 * Display templates on protected pages.
 * @author Erwin ([[m:User:Erwin]]), 2010
 * @author Krinkle, 2010-2015
 * @source https://nl.wikipedia.org/wiki/MediaWiki:Gadget-ProtectionTemplates.js
 */
/* Depends on:
 * [[Template:Beveiligd]]
 * [[Template:Semibeveiligd]]
 * [[Template:TitelBeveiligd]]
 */
( function () {
	var templateTitle;

	if (
		// Ignore main page.
		mw.config.get( 'wgIsMainPage' ) ||
		// Ignore nonexistent pages
		!mw.config.get( 'wgArticleId' )
	 ) {
		return;
	}

	// Determine which template to use
	if ( mw.config.get( 'wgRestrictionEdit' ) && mw.config.get( 'wgRestrictionEdit' ).indexOf( 'sysop' ) !== -1 ) {
		// Editing is limited to sysops
		templateTitle = 'Beveiligd';
	} else if ( mw.config.get( 'wgRestrictionEdit' ) && mw.config.get( 'wgRestrictionEdit' ).indexOf( 'autoconfirmed' ) !== -1 ) {
		// Editing is limited to autoconfirmed users
		templateTitle = 'Semibeveiligd';
	} else if ( mw.config.get( 'wgRestrictionMove', [] ).length ) {
		// Moving the page is limited
		templateTitle = 'TitelBeveiligd';
	} else {
		// The page is not protected, ignore.
		return;
	}

	// Get template from API
	$.get( mw.util.wikiScript( 'api' ), {
		format: 'json',
		action: 'parse',
		text: '\{\{' + templateTitle + '\}\}',
		title: mw.config.get( 'wgPageName' ),
		contentmodel: 'wikitext'
	}, function ( json ) {
		if ( json && json.parse && json.parse.text && json.parse.text['*'] ) {
			var $icon = $( '<div class="Top_icon_dynamic"></div>' ).html( json.parse.text['*'] );
			// Wait for DOM ready
			$( function () {
				$( '#firstHeading' ).prepend( $icon );
			} );
		}
	} );

}() );
$( function() {
	'use strict';
	var
		iwlinks = $( 'div.interProject' ),
		wb = $( '#p-wikibase-otherprojects' ),
		pproject,
		wbul;
	if ( !iwlinks.length ) {
		return;
	}
	if ( wb.length ) {
		wbul = wb.find( 'ul' );
		if ( !wbul.length ) {
			return;
		}
		iwlinks.each( function( i, e ) {
			var
				t = $( e ),
				a = t.find( 'a' );
			if ( t.hasClass( 'commons' ) && !wb.find( '.wb-otherproject-commons' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wiktionary' ) && !wb.find( '.wb-otherproject-wiktionary' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikinews' ) && !wb.find( '.wb-otherproject-wikinews' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikisource' ) && !wb.find( '.wb-otherproject-wikisource' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikibooks' ) && !wb.find( '.wb-otherproject-wikibooks' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikiquote' ) && !wb.find( '.wb-otherproject-wikiquote' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikiversity' ) && !wb.find( '.wb-otherproject-wikiversity' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikidata' ) && !wb.find( '.wb-otherproject-wikidata' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikivoyage' ) && !wb.find( '.wb-otherproject-wikivoyage' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikispecies' ) && !wb.find( '.wb-otherproject-species' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'mediawiki' ) && !wb.find( '.wb-otherproject-mediawiki' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'meta' ) && !wb.find( '.wb-otherproject-meta' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'incubator' ) && !wb.find( '.wb-otherproject-incubator' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikimania' ) && !wb.find( '.wb-otherproject-wikimania' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			/*DEBUG[
			else {
				a.unwrap().wrap( '<li>' ).parent().appendTo( wbul );
			}]*/
		} );
	} else {
		pproject = $( '#p-tb' ).clone().attr( 'id', 'p-wikibase-otherprojects' ).insertAfter( '#p-tb' );
		if ( pproject.attr( 'aria-labelledby' ) ) {
			pproject.attr( 'aria-labelledby', 'p-wikibase-otherprojects-label' );
		}
		pproject.find( 'h3' ).attr( 'id', 'p-wikibase-otherprojects-label' ).text( 'In andere projecten' );
		pproject.find( 'ul' ).addClass( 'interProject' ).empty().append( iwlinks );
		iwlinks.replaceWith( function() {
			return $( '<li>' ).addClass( $(this).attr( 'class' ) ).append( $(this).contents() );
		} );
	}
} );
$( function() {
	'use strict';
	var
		iwlinks = $( 'div.interProject' ),
		wb = $( '#p-wikibase-otherprojects' ),
		pproject,
		wbul;
	if ( !iwlinks.length ) {
		return;
	}
	if ( wb.length ) {
		wbul = wb.find( 'ul' );
		if ( !wbul.length ) {
			return;
		}
		iwlinks.each( function( i, e ) {
			var
				t = $( e ),
				a = t.find( 'a' );
			if ( t.hasClass( 'commons' ) && !wb.find( '.wb-otherproject-commons' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wiktionary' ) && !wb.find( '.wb-otherproject-wiktionary' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikinews' ) && !wb.find( '.wb-otherproject-wikinews' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikisource' ) && !wb.find( '.wb-otherproject-wikisource' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikibooks' ) && !wb.find( '.wb-otherproject-wikibooks' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikiquote' ) && !wb.find( '.wb-otherproject-wikiquote' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikiversity' ) && !wb.find( '.wb-otherproject-wikiversity' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikidata' ) && !wb.find( '.wb-otherproject-wikidata' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikivoyage' ) && !wb.find( '.wb-otherproject-wikivoyage' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikispecies' ) && !wb.find( '.wb-otherproject-species' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'mediawiki' ) && !wb.find( '.wb-otherproject-mediawiki' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'meta' ) && !wb.find( '.wb-otherproject-meta' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'incubator' ) && !wb.find( '.wb-otherproject-incubator' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			else if ( t.hasClass( 'wikimania' ) && !wb.find( '.wb-otherproject-wikimania' ).length ) {
				a.unwrap().wrap( '<li class="' + t.attr( 'class' ) + '"></li>' ).parent().appendTo( wbul );
			}
			/*DEBUG[
			else {
				a.unwrap().wrap( '<li>' ).parent().appendTo( wbul );
			}]*/
		} );
	} else {
		pproject = $( '#p-tb' ).clone().attr( 'id', 'p-wikibase-otherprojects' ).insertAfter( '#p-tb' );
		if ( pproject.attr( 'aria-labelledby' ) ) {
			pproject.attr( 'aria-labelledby', 'p-wikibase-otherprojects-label' );
		}
		pproject.find( 'h3' ).attr( 'id', 'p-wikibase-otherprojects-label' ).text( 'In andere projecten' );
		pproject.find( 'ul' ).addClass( 'interProject' ).empty().append( iwlinks );
		iwlinks.replaceWith( function() {
			return $( '<li>' ).addClass( $(this).attr( 'class' ) ).append( $(this).contents() );
		} );
	}
} );
/**
 * Popups bij referenties
 * Overgenomen van: https://en.wikipedia.org/w/index.php?title=MediaWiki:Gadget-ReferenceTooltips.js&oldid=807945391
 *r
 */

// See [[mw:Reference Tooltips]]

window.pg || $(function() {

    // Make sure we are in article, project, or help namespace
    if ( $.inArray(mw.config.get('wgCanonicalNamespace'), [ "", "Project", "Help" ]) !== -1 ) {
		mw.messages.set( {
			"RT-enable" : "Referentietooltips inschakelen",
			"RT-disable" : "Referentietooltips uitschakelen",
			"RT-disablenote" : "Eenmaal uitgeschakeld kunnen referentietooltips weer worden ingeschakeld met de link aan de onderzijde van de pagina.",
			"RT-delay" : "Vertraging voordat tooltip verschijnt (in milliseconden): ",
			"RT-activationmethod" : "Tooltips reageren op:",
			"RT-hovering" : "de muisfocus",
			"RT-clicking" : "een muisklik",
			"RT-options" : "Referentietooltips-voorkeuren",
			"RT-options-save" : "Voorkeuren opslaan",
			"RT-settings" :"Tooltip-voorkeuren"
		} );
        function toggleRT(o){
            mw.loader.using("mediawiki.cookie",function(){
                    mw.cookie.set("RTsettings",o+"|"+ settings[1] + "|" + settings[2], {path:"/",expires:90});
                    location.reload();
            });
        }
        var settings = document.cookie.split("RTsettings=")[1];
        settings = settings ? settings.split(";")[0].split("%7C") : [1, 200, +("ontouchstart" in document.documentElement)];
        if( settings[0] == 0 ) {
            var footer = $("#footer-places, #f-list");
            if( footer.length === 0 ) {
                    footer = $("#footer li").parent();
            }
            footer.append($("<li>").append(
            	$("<a>")
            		.text( mw.msg( "RT-enable" ) )
            		.attr("href","")
            		.click(function(e){
            			e.preventDefault();
            			toggleRT(1);
            		})
            ));
            return;
        }
        var isTouchscreen = +settings[2],
            timerLength = isTouchscreen ? 0 : +settings[1],
            settingsMenu;
        $(".reference").each( function() {
            var tooltipNode, hideTimer, showTimer, checkFlip = false;
            function findRef( h ){
                    h = h.firstChild; h = h && h.getAttribute && h.getAttribute("href"); h = h && h.split("#"); h = h && h[1];
                    h = h && document.getElementById( h );
                    h = h && h.nodeName == "LI" && h;
                    return h;
            }
            function hide( refLink ) {
                    if( tooltipNode && tooltipNode.parentNode == document.body ) {
                            hideTimer = setTimeout( function() {
                                    $(tooltipNode).animate({opacity: 0}, 100, function(){
                                    	if(tooltipNode.parentNode) {
	                                    	tooltipNode.parentNode.removeChild( tooltipNode );
                                    	}
                                    });
                            }, isTouchscreen ? 16 : 100);
                    } else {
                            $( findRef( refLink ) ).removeClass("RTTarget");
                    }
            }
            function show(){
                    if( !tooltipNode.parentNode || tooltipNode.parentNode.nodeType === 11 ){
                            document.body.appendChild( tooltipNode );
                            checkFlip = true;
                    }
                    $(tooltipNode).stop().animate({opacity: 1}, 100);
                    clearTimeout( hideTimer );
            }
            function openSettingsMenu(){
                    if( settingsMenu ) {
                            settingsMenu.dialog( "open" );
                    } else {
                            settingsMenu = $("<form>")
                            	.append(
                                    $("<button>").css("width","100%").text( mw.msg( "RT-disable", mw.user ) ).button().click(function(e){
                                    		e.preventDefault();
                                    		toggleRT(0);
	                                    }),
                                    $("<br>"),
                                    $("<small>").text( mw.msg( "RT-disablenote") ),
                                    $("<hr>"),
                                    $("<label>").text( mw.msg( "RT-delay" ) ).append($("<input>").attr({"type":"number","value":settings[1],step:50,min:0,max:5000})),
                                    $("<br>"),
                                    $("<span>").text( mw.msg( "RT-activationmethod", mw.user ) ),
                                    $("<label>").append(
                                            $("<input>").attr({"type":"radio", "name":"RTActivate", "checked":settings[2]==0&&"checked", "disabled":"ontouchstart" in document.documentElement&&"disabled"}),
                                            mw.msg( "RT-hovering", mw.user )
                                    ),
                                    $("<label>").append(
                                            $("<input>").attr({"type":"radio", "name":"RTActivate", "checked":settings[2]==1&&"checked"}),
                                            mw.msg( "RT-clicking", mw.user )
                                    )
                            	)
                            .submit(function(e){e.preventDefault()})
                            .dialog({
                            	modal:true,
                            	width:500,
                            	title: mw.msg( "RT-options" ),
                            	buttons:[ { text: mw.msg( "RT-options-save", mw.user ), click: function(){
                                    var a = this.getElementsByTagName("input"),
                                            b = +a[0].value;
                                    mw.cookie.set("RTsettings","1|"+ (b > -1 && b < 5001 ? b : settings[1]) + (a[1].checked ? "|0" : "|1"), {path:"/",expires:90});
                                    location.reload();
                            	}}]
                            });
                    }
            }
            function onStartEvent( e ){
                    var _this = this;
                    if( window.pg ) {
                    	return;
                    }
                    if( isTouchscreen ) {
                            e.preventDefault();
                            (tooltipNode && tooltipNode.parentNode == document.body) || setTimeout( function(){
                                    $( document.body ).on("click touchstart", function( e ) {
                                            e = e || event;
                                            e = e.target || e.srcElement;
                                            for( ; e && !$( e ).hasClass( "referencetooltip" ) ; )
                                                    e = e.parentNode;
                                            if( !e ){
                                                    clearTimeout( showTimer );
                                                    hide( _this );
                                                    $(document.body).off("click touchstart", arguments.callee);
                                            }
                                    });
                            }, 0);
                    }
                    hideTimer && clearTimeout( hideTimer );
                    showTimer && clearTimeout( showTimer );
                    showTimer = setTimeout( function() {
                            var h = findRef( _this );
                            if( !h ){return}
                            var windowTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0,
                                    hOffsetTop = $( h ).offset().top;
                            if( !isTouchscreen && windowTop < hOffsetTop && windowTop + $(window).height() > hOffsetTop + h.offsetHeight ) {
                                    $( h ).addClass("RTTarget");
                                    return;
                            }
                            if(!tooltipNode){
                                    tooltipNode = document.createElement("ul");
                                    tooltipNode.className = "referencetooltip";
                                    var c = tooltipNode.appendChild( $( h ).clone( true )[ 0 ] );
                                    try {
                                            if( c.firstChild.nodeName != "A" ) {
                                                    while( c.childNodes[1].nodeName == "A" && c.childNodes[1].getAttribute( "href" ).indexOf("#cite_ref-") !== -1 ) {
                                                            do { c.removeChild( c.childNodes[1] ) } while ( c.childNodes[1].nodeValue == " " );
                                                    }
                                            }
                                    } catch (e) { mw.log(e) }
                                    c.removeChild( c.firstChild );
                                    $( tooltipNode.firstChild.insertBefore( document.createElement( "span" ), tooltipNode.firstChild.firstChild ) ).addClass("RTsettings").attr("title", mw.msg( "RT-settings" )).click(function(){
                                            mw.loader.using(["mediawiki.cookie","jquery.ui"], openSettingsMenu);
                                    });
                                    tooltipNode.appendChild( document.createElement( "li" ) );
                                    isTouchscreen || $(tooltipNode).on("mouseenter", show).on("mouseleave", hide);
                            }
                            show();
                            var o = $(_this).offset(), oH = tooltipNode.offsetHeight;
                            $(tooltipNode).css({top: o.top - oH, left: o.left - 7 });
                            if( tooltipNode.offsetHeight > oH ) { // is it squished against the right side of the page?
                                    $(tooltipNode).css({left:'auto',right:0});
                                    tooltipNode.lastChild.style.marginLeft = (o.left - tooltipNode.offsetLeft) + "px";
                            }
                            if( checkFlip ) {
                                    if( o.top < tooltipNode.offsetHeight + ( window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 ) ) { // is part of it above the top of the screen?
                                            $(tooltipNode).addClass("RTflipped").css({top: o.top + 12});
                                    } else if( tooltipNode.className === "referencetooltip RTflipped" ) { // cancel previous
                                            $(tooltipNode).removeClass("RTflipped");
                                    }
                                    checkFlip = false;
                            }
                    }, timerLength);
            }
            function onEndEvent(){
                    clearTimeout(showTimer); hide(this);
            }
            $(this).on( isTouchscreen ? { click: onStartEvent } : { mouseenter: onStartEvent, mouseleave: onEndEvent } );
        } );   
    }
} );
/**
 * An easy gadget to use https://en.wikipedia.org/wiki/MediaWiki:Wdsearch.js
 **/

if ( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Search' ||  ( mw.config.get( 'wgArticleId' ) === 0 && mw.config.get( 'wgCanonicalSpecialPageName' ) === false ) ) {
	mw.loader.load("//en.wikipedia.org/w/index.php?title=MediaWiki:Wdsearch.js&action=raw&ctype=text/javascript");
}
// GlobalUsage tracking: [[File:Krinkle OpenStreetMapFrame.js]]
window.openStreetMapFrame_id = 'tpl_Coordinaten';
window.openStreetMapFrame_label = 'Kaart';
mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:Krinkle/OpenStreetMapFrame.js&action=raw&ctype=text/javascript');
/**
 * Extra bewerkingsknoppen
 * Afkomstig van http://de.wikipedia.org/w/index.php?title=MediaWiki:Gadget-Extra-Editbuttons.js&oldid=100643998
 */

(function(){

	// Return early if not on edit page
	var action = mw.config.get( 'wgAction' );
	if( action !== 'edit' && action !== 'submit' ) {
		return;
	}

	// By registering our dependency after document.ready, we make sure
	// that the toolbar is fully loaded before we manipulate it
	mw.loader.using( [ 'mediawiki.toolbar', 'user' ], function() { $( function() {

		// Check that the toolbar is present on the page
		if( !$( '#toolbar' ).length ) {
			return;
		}

		// Define available buttons
		var sig = window.usersignature || '--\~\~\~\~';
		var commonsUrl = "//upload.wikimedia.org/wikipedia/commons/";
		var styleUrl = mw.config.get( 'stylepath' ) + '/common/images/';

var buttons={
'A':[commonsUrl +'e/e9/Button_headline2.png','Tussenkopje (tweede niveau)','\n===','===','Kop van de tweede niveau'],
'B':[commonsUrl +'1/13/Button_enter.png','Regel afbreken','<br />','',''],
'C':[commonsUrl +'5/5f/Button_center.png','Centreren','<div style="text-align: center;">\n','\n<\/div>','Tekst die gecentreerd wordt'],
'D':[commonsUrl +'e/ea/Button_align_left.png','Links uitlijnen','<div style="text-align: left; direction: ltr; margin-left: 1em;">\n','\n<\/div>','Tekst die links wordt uitgelijnd'],
'D1':[commonsUrl +'a/a5/Button_align_right.png','Rechts uitlijnen','<div style="text-align: right; direction: ltr; margin-left: 1em;">\n','\n<\/div>','Tekst die rechts wordt uitgelijnd'],
'F':[commonsUrl +'1/1e/Button_font_color.png','Gekleurde tekst invoegen','<span style="color: ','">Gekleurde tekst<\/span>','ColourName'],
'FS':[commonsUrl +'1/1b/Button_miss_signature.png','Niet ondertekend','{{afzender|','}}','Gebruikersnaam of IP'],
'G':[commonsUrl +'9/9e/Btn_toolbar_gallery.png','Afbeeldingengalerij',"\n<gallery\>\nImage:","|[[Omschrijving]]\nImage:Mona Lisa.jpg|[[Omschrijving]]\nImage:Truite arc-en-ciel.jpg|[[Omschrijving]]\n<\/gallery>",'M63.jpg'],
'H':[commonsUrl +'7/74/Button_comment.png','Verborgen commentaar',"<!--","-->",'Verborgen commentaar. Gebruik de overlegpagina voor opmerkingen en discussie.'],
'I1':[commonsUrl +'6/6a/Button_sup_letter.png','Superscript','<sup>','<\/sup>','Tekst'],
'I2':[commonsUrl +'a/aa/Button_sub_letter.png','Subscript','<sub>','<\/sub>','Tekst'],
'J1':[commonsUrl +'5/58/Button_small.png','Kleiner','<small>','<\/small>','Tekst die kleiner wordt weergegeven'],
'J2':[commonsUrl +'5/56/Button_big.png','Groter','<big>','<\/big>','Tekst die groter wordt weergegeven'],
'K':[commonsUrl +'b/b4/Button_category03.png','Categorie',"[\[Categorie:","]]",'Naam'],
'L':[commonsUrl +'8/8e/Button_shifting.png','Inspringen',':','','Tekst dat wordt ingesprongen. Gebruik nogmaals een : om verder in te springen.'],
'M':[commonsUrl +'f/fd/Button_blockquote.png','Voeg een tekstblok of een quote in','<blockquote style="border: 1px solid blue; padding: 2em;">\n','\n<\/blockquote>','Tekstblok of een quote.'],
'N':[commonsUrl +'4/4b/Button_nbsp.png','Niet-afbrekende spatie','&nbsp;','',''],
'O':[commonsUrl +'2/23/Button_code.png','Voeg broncode in','<code>','<\/code>','Broncode'],
'P':[commonsUrl +'3/3c/Button_pre.png','Voorgeformateerde tekst','<pre>','<\/pre>','Voorgeformateerde tekst'],
'Q':[commonsUrl +'d/d3/Button_definition_list.png','Lijst met definities invoegen','\n; ','\n: Item 1\n: Item 2','Definitie'],
'R':[commonsUrl +'7/79/Button_reflink.png','Voeg een referentie, bron of voetnoot toe','<ref>','<\/ref>','[http:\/\/www.voorbeeld.com Bijvoorbeeld een referentie e.d. naar een website] of een referentie naar een boek'],
'R1':[commonsUrl +'9/99/Button_reflink_advanced_2.png','Referentie met naam: naam','<ref name="','','Naam van referentie'],
'R2':[commonsUrl +'9/99/Button_reflink_advanced_2.png','Referentie met naam: de referentie zelf','">','<\/ref>','[http:\/\/www.voorbeeld.com Bijvoorbeeld een referentie e.d. naar een website] of een referentie naar een boek'],
'R3':[commonsUrl +'1/1a/Button_reflink_advanced_3.png','Referentie met naam: eindmarkering herhaling','','"/>',''],
'R4':[commonsUrl +'9/9a/Button_references.png','Overzicht referenties e.d.: met nieuw bronnensjabloon',"{{Appendix|bron|2=\n{{References}}\n}}\n",'',''],
'R5':[commonsUrl +'9/9a/Button_references.png','Overzicht referenties e.d.: in al bestaand bronnensjabloon',"\n----\n{{References}}\n",'',''],
'R6':[commonsUrl +'9/9a/Button_references.png','Overzicht referenties e.d.: los met tussenkopje',"==Referenties==\n{{References}}\n",'',''],
'S':[commonsUrl +'c/c9/Button_strike.png','Doorstrepen','<s>','<\/s>','Doorgestreepte tekst'],
//<nowiki>
'T':[commonsUrl +'e/eb/Button_plantilla.png','Sjabloon invoegen','{{','}}','Sjabloonnaam'],
//</nowiki>
'TS':[commonsUrl +'a/a4/TableStart.png','Begin van tabel','{| ','',''],
'TC':[commonsUrl +'7/71/TableCell.png','Kolom','| ','',''],
'TE':[commonsUrl +'0/06/TableEnd.png','Einde van tabel','','|}',''],
'TR':[commonsUrl +'4/4c/TableRow.png','Rij','|- ','',''],
'T1':[commonsUrl +'3/30/Tt_icon.png','Tekst in teletype','<tt>','<\/tt>','Tekst met het teletype-lettertype'],
'U':[commonsUrl +'f/fd/Button_underline.png','Onderstrepen',"<u>","<\/u>",'Tekst die onderstreept wordt'],
'V':[commonsUrl +'c/c8/Button_redirect.png','Redirect',"#REDIRECT [[","]]",'Artikelnaam'],
'W':[commonsUrl +'8/88/Btn_toolbar_enum.png','Genummerde lijst',"\n# ","\n# Element 2\n# Element 3",'Element 1'],
'X':[commonsUrl +'1/11/Btn_toolbar_liste.png','Lijst',"\n* ","\n* Element B\n* Element C",'Element A'],
'Y1':[commonsUrl +'c/ce/Button_no_include.png','Sjabloon: noinclude',"<noinclude>","<\/noinclude>",'Tekst'],
'Y2':[commonsUrl +'7/79/Button_include.png','Sjabloon: includeonly',"<includeonly>","<\/includeonly>",'Tekst'],
'Z':[commonsUrl +'3/35/Button_substitute.png','Sjabloon substitueren',"{{subst:","}}",'Sjabloonnaam'],
'AI':[commonsUrl +'1/1c/Button_advanced_image.png','Geavanceerde afbeelding',"[\[Bestand:","|thumb|right|px|Caption]]",'FileName.jpg'],
'TALK':[commonsUrl +'4/49/Button_talk.png','Sjabloon aan overlegpagina IP/gebruiker toevoegen',"","",""]
};

		// Add user defined buttons to the available buttons
		if( window.myButtons ) {
			$.extend( buttons, window.myButtons );
		}

		// Add buttons to the toolbar in the specified order
		var order = window.customEditButtons || 'A,B,S,U,C,D,D1,F,FS,AI,G,H,I1,J1,K,L,M,N,W,X,Q,R,R4,R5,T,V';
		if( order === 'all' ) {
			order = [];
			for( var key in buttons ) {
				if( isNaN( key ) ) {
					order.push( key );
				}
			}
		} else {
			order = order.split( ',' );
		}

		for( var i = 0, l = order.length; i < l; i++ ) {
			var button = buttons[order[i]];
			if( !button ) {
				throw new Error( 'Unknown button: ' + order[i] );
			}
			mw.toolbar.addButton.apply( mw.toolbar, button );
		}

		// Remove some default buttons if requested
		if( window.rmEditButtons && mw.toolbar.$toolbar ) {
			var remove = window.rmEditButtons;
			if( ( remove[0] + '' ).toLowerCase() === 'all' ) {
				remove = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
			}
			remove.sort().reverse();

			var $toolbarButtons = mw.toolbar.$toolbar.children();
			for( var i = 0, l = remove.length; i < l; i++ ) {
				$toolbarButtons.eq( remove[i] ).remove();
			}
		}

		// Customize signature button
		if( sig !== '--\~\~\~\~' ) {
			$( '#mw-editbutton-signature' ).off( 'click' ).click( function() {
				mw.toolbar.insertTags( sig, '', '' );
				return false;
			} ).attr( 'src', commonsUrl + 'd/d1/Button_mysignature.png' );
		}

		// Add special click handlers
		$( '#mw-editbutton-redirect' ).off( 'click' ).click( function() {
			var target = window.prompt( 'Wohin soll der Redirect?', '' );
			if( target ) {
				var text = '#WEITERLEITUNG [[' + target + ']]';
				$( '#wpTextbox1' ).val( text );
				$( '#wpSummary' ).val( text );
				$( '#wpWatchthis' ).prop( 'checked', false );
			}
		} );			
		$( '#mw-editbutton-tablegenerator' ).click( popupTable );

		// Adjust width of spacer images
		if( mw.toolbar.$toolbar ) {
			mw.toolbar.$toolbar.children( 'img[src="' + buttons['T1'][0] + '"]' ).width( 6 ).off( 'click' );
		}

		//============================================================
		// Table generator 
		//============================================================
		/** en: Generate an array using Mediawiki syntax
		 * @author: originally from fr:user:dake
		 * @version: 0.2 */
		mw.toolbar.generateTable = function( caption, exhead, nbCol, nbRow, exfield, align ) {
			var code = '\n';
			code += '{| class="wikitable" ' + align + '\n' + caption + exhead;
			if( exfield ) {
				code += '!\n';
			}
			for( var i = 1; i < nbCol + 1; i++ ) {
				code += '! FELD ' + i + '\n';
			}
			var items = 0;
			for( var j = 0; j < nbRow; j++ ) {
				if( exfield ) {
					items++;
					code += '|-\n! style="background: #FFDDDD;" | ITEM ' + items + '\n';
				} else {
					code += '|-\n';
				}
				for( var i = 0; i < nbCol; i++ ) {
					code += '| Element\n';
				}
				code += '|}\n';
			}
			mw.toolbar.insertTags( '', '', code );
		};

		/** en: Open a popup with parameters to generate an array. 
		 * The number of rows/columns can be modified.
		 * @author: originally fr:user:dake 
		 * @version: 0.2 */
		function popupTable() {
			var popup = window.open( 'about:blank', 'WPtable', 'height=400,width=400,scrollbars=yes' );
			var javaCode = '<script type="text\/javascript">function insertCode(){'
				+'var caption = (document.paramForm.inputCaption.checked)?"\|\+ TABLE CAPTION \\n":""; '
				+'var exhead = (document.paramForm.inputHead.checked)?\'\|\- style=\"background: #DDFFDD;\"\\n\':""; '
				+'var row = parseInt(document.paramForm.inputRow.value); '
				+'var col = parseInt(document.paramForm.inputCol.value); '
				+'var exfield = document.paramForm.inputItems.checked; '
				+'var align = (document.paramForm.inputAlign.checked)?\'align="center"\':""; '
				+'window.opener.mw.toolbar.generateTable(caption,exhead,col,row,exfield,align); '
				+'window.close()}<\/script>';

			popup.document.write( '<html><head><title>Make table<\/title>'
					+ javaCode +'<\/head><body>'
					+'<p>Enter the table parameters below: <\/p>'
					+'<form name="paramForm">'
					+'Table caption: <input type="checkbox" name="inputCaption"><p\/>'
					+'Table alignment: center<input type="checkbox" name="inputAlign"><p\/>'
					+'Table headline: colored<input type="checkbox" name="inputHead"><p\/>'
					+'Number of rows: <input type="text" name="inputRow" value="3" size="2"><p\/>'
					+'Number of columns: <input type="text" name="inputCol" value="3" size="2"><p\/>'
					+'Item column: <input type="checkbox" name="inputItems" ><p\/>'
					+'<\/form">'
					+'<i>The default table allows for fields and values only.<\/i><p\/>'
					+'Check "Item column" to allow for the table to have fields, items, and values.<\/i><p\/>'
					+'<p><a href="javascript:insertCode()"> Insert table into window<\/a> &nbsp;&nbsp;&nbsp; |'
					+' &nbsp;&nbsp;&nbsp;<a href="javascript:self.close()">Cancel<\/a><\/p>'
					+'<\/body><\/html>'
			);
			popup.document.close();
			return false;
		}
	} ) } );
})();
/**
 * Add "Purge" content action link.
 *
 * Dependencies: mediawiki.util, mediawiki.api
 *
 * @source https://www.mediawiki.org/wiki/Snippets/Purge_action
 * @revision 2016-08-26
 */
$( function () {
	if ( $( '#ca-purge' ).length || mw.config.get( 'wgNamespaceNumber' ) < 0 ) return;
	var node = mw.util.addPortletLink(
		'p-cactions',
		mw.util.getUrl( null, { action: 'purge' } ),
		mw.config.get( 'skin' ) == 'vector' ? 'Verversen' : '*',
		'ca-purge',
		'De servercache van deze pagina verversen',
		'*'
	);
	$(node).on( 'click', function (e) {
		new mw.Api().post( { action: 'purge', titles: mw.config.get( 'wgPageName' ) } ).then(function () {
			location.reload();
		}, function () {
			mw.notify( 'Purge mislukt', { type: 'error' } );
		});
		e.preventDefault();
	});
});
/**
 * Voeg een link to aan de Hulpmiddelen-sectie voor subpagina's. 
 * Gebaseerd op de code in [[:commons:MediaWiki:Common.js]].
 *
 * Onderhoud: [[User:Krinkle]]
 */ 
( function () {
    var i18n = {
        de: "Unterseiten",
        en: "Subpages",
        fr: "Sous-pages",
        nl: "Subpagina's"
    };
    if ( [ 'Special', 'File', 'Category', '' ].indexOf( mw.config.get( 'wgCanonicalNamespace' ) ) === -1 ) {
        var text = i18n[ mw.config.get( 'wgUserLanguage' ) ] || i18n.nl;
        var link = mw.util.getUrl( 'Speciaal:Voorvoegselindex/' + mw.config.get( 'wgPageName' ) + '/' );
        mw.util.addPortletLink( 'p-tb', link, text );
    }
}() );
window.hotcat_translations_from_commons = true;

/*
 Hier importeren we de meest recente versie van HotCat van Commons.
 HotCat is een uitbreiding om veel gemakkelijker categorieën te wijzigen.
 De Engelstalige documentatie is te vinden op https://commons.wikimedia.org/wiki/Help:Gadget-HotCat
*/
mw.loader.load( '//commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&action=raw&ctype=text/javascript' );
/**
 * Plaats de focus in het zoekvenster op [[Hoofdpagina]]
 *
 * Overgenomen van http://en.wikipedia.org/wiki/MediaWiki:Gadget-searchFocus.js
 */

if ( mw.config.get( 'wgIsMainPage' ) ) {
  $( function () {
    $( '#searchInput' ).focus();
  } );
}
//</nowiki>