The Wiki for Tale 4 is in read-only mode and is available for archival and reference purposes only. Please visit the current Tale 11 Wiki in the meantime.

If you have any issues with this Wiki, please post in #wiki-editing on Discord or contact Brad in-game.

User:Kasiya/monobook.js

From A Tale in the Desert
< User:Kasiya
Revision as of 22:19, 4 November 2009 by Kasiya (talk | contribs) (New page: /** Collapsible tables ********************************************************* * * Description: Allows tables to be collapsed, showing only the header. See * [[Wikipedi...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

Note: After saving, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
/** Collapsible tables *********************************************************
 *
 *  Description: Allows tables to be collapsed, showing only the header. See
 *               [[Wikipedia:NavFrame]].
 *  Maintainers: [[User:R. Koot]]
 */

var autoCollapse = 2;
var collapseCaption = "hide";
var expandCaption = "show";

function collapseTable( tableIndex )
{
    var Button = document.getElementById( "collapseButton" + tableIndex );
    var Table = document.getElementById( "collapsibleTable" + tableIndex );

    if ( !Table || !Button ) {
        return false;
    }

    var Rows = Table.rows;

    if ( Button.firstChild.data == collapseCaption ) {
        for ( var i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = "none";
        }
        Button.firstChild.data = expandCaption;
    } else {
        for ( var i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = Rows[0].style.display;
        }
        Button.firstChild.data = collapseCaption;
    }
}

function createCollapseButtons()
{
    var tableIndex = 0;
    var NavigationBoxes = new Object();
    var Tables = document.getElementsByTagName( "table" );

    for ( var i = 0; i < Tables.length; i++ ) {
        if ( hasClass( Tables[i], "collapsible" ) ) {

            /* only add button and increment count if there is a header row to work with */
            var HeaderRow = Tables[i].getElementsByTagName( "tr" )[0];
            if (!HeaderRow) continue;
            var Header = HeaderRow.getElementsByTagName( "th" )[0];
            if (!Header) continue;

            NavigationBoxes[ tableIndex ] = Tables[i];
            Tables[i].setAttribute( "id", "collapsibleTable" + tableIndex );

            var Button     = document.createElement( "span" );
            var ButtonLink = document.createElement( "a" );
            var ButtonText = document.createTextNode( collapseCaption );

            Button.className = "collapseButton";  //Styles are declared in Common.css

            ButtonLink.style.color = Header.style.color;
            ButtonLink.setAttribute( "id", "collapseButton" + tableIndex );
            ButtonLink.setAttribute( "href", "javascript:collapseTable(" + tableIndex + ");" );
            ButtonLink.appendChild( ButtonText );

            Button.appendChild( document.createTextNode( "[" ) );
            Button.appendChild( ButtonLink );
            Button.appendChild( document.createTextNode( "]" ) );

            Header.insertBefore( Button, Header.childNodes[0] );
            tableIndex++;
        }
    }

    for ( var i = 0;  i < tableIndex; i++ ) {
        if ( hasClass( NavigationBoxes[i], "collapsed" ) || ( tableIndex >= autoCollapse && hasClass( NavigationBoxes[i], "autocollapse" ) ) ) {
            collapseTable( i );
        } 
        else if ( hasClass( NavigationBoxes[i], "innercollapse" ) ) {
            var element = NavigationBoxes[i];
            while (element = element.parentNode) {
                if ( hasClass( element, "outercollapse" ) ) {
                    collapseTable ( i );
                    break;
                }
            }
        }
    }
}

addOnloadHook( createCollapseButtons );

var popScript     = 'http://en.wikipedia.org/w/index.php?action=raw&ctype=text/javascript&title=MediaWiki:Gadget-popups.js';
var popStyleSheet = 'http://en.wikipedia.org/w/index.php?action=raw&ctype=text/css&title=User:Lupin/navpopdev.css';
if ( window.localCSS ) { popStyleSheet = 'http://localhost:8080/js/navpop.css'; }

function popups_importScriptURI(url) {
	var s = document.createElement('script');
	s.setAttribute('src',url);
	s.setAttribute('type','text/javascript');
	document.getElementsByTagName('head')[0].appendChild(s);
	return s;
}

function popups_importStylesheetURI(url) {
	return document.createStyleSheet ? document.createStyleSheet(url) : popups_appendCSS('@import "' + url + '";');
}

function popups_appendCSS(text) {
	var s = document.createElement('style');
	s.type = 'text/css';
	s.rel = 'stylesheet';
	if (s.styleSheet) s.styleSheet.cssText = text //IE
	else s.appendChild(document.createTextNode(text + '')) //Safari sometimes borks on null
	document.getElementsByTagName('head')[0].appendChild(s);
	return s;
}

popups_importStylesheetURI(popStyleSheet);
popups_importScriptURI(popScript);

/*

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

*/


// <pre><nowiki>

recent2={
	// Edit these to your liking.
	// Make sure there's a comma at the end of each line.
	badwordsUrl:          'User:Lupin/badwords',
	filterPage:           'User:Lupin/Filter_recent_changes',
	allRecentPage:        'User:Lupin/All_recent_changes',
	recentIPPage:         'User:Lupin/Recent_IP_edits',
	monitorWatchlistPage: 'User:Lupin/Monitor_my_watchlist',
	spelldictUrl:         'Wikipedia:Lists_of_common_misspellings/For_machines',
	spelldictPage:        'User:Lupin/Live_spellcheck',
	safePages:            '([Ww]ikipedia:([Ii]ntroduction|[Ss]andbox|[Tt]utorial[^/]*/sandbox)|[Tt]emplate:(X[1-9]|Template sandbox))',
	linkify:              true,
	updateSeconds:        30,
	ipUserRegex:          RegExp('(User:)?((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
													'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])'),
	outputSeparator:      '<hr>',
	templateNamespace:    'Template',
	namespaces:           { 'Media':1, "Special":1, "User":1, "User talk":1, "Wikipedia":1,
													"Wikipedia talk":1, "Image":1, "Image talk":1, "MediaWiki":1,
													"MediaWiki talk":1, "Template":1, "Template talk":1, "Help":1,
													"Help talk":1, "Category":1, "Category talk":1, "Portal":1, "Portal talk":1 },
	apiAulimitUser:        500,
	apiAulimitSysop:       5000,
	backgroundWindowsMax:  10,
	dummy: null // leave this last one alone
};

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

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

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

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

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

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

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

window.diffCellRe=RegExp("<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.innerHTML='Warning: ignoring odd-looking regexp on line '+i
		+' of <a href="/wiki/' + recent2.badwordsUrl + '">badwords</a>:<pre>' + s + '</pre>';
	continue;
			}
			if (isPhrase) phrase.push(s); else string.push(s);
		} else {
			// treat this line as a non-regexp and escape it.
			phrase.push(s.replace(RegExp('([-|.()\\+:!,?*^${}\\[\\]])', 'g'), '\\$1'));
		}
	}
	//                      123                                3       2|4                        4|5         56                        67        71
	//                      (((    repeated char               )       )|(   ... | strings | ...  )|( border  )(   ... | phrases | ...  )( border ))
	window.badWords=RegExp("((([^\\-\\|\\{\\}\\].\\s'=wI:*#0-9a-f])\\3{2,})|(" + string.join('|') + ")|(^|[^/\\w])(" + phrase.join('|') + ")(?![/\\w]))", 'gi');
};

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

recent2.getWatchlist=function() {
	window.gettingWatchlist=true;
	recent2.download({url: recent2.articlePath + 'Special:Watchlist/raw',
	onSuccess: recent2.processWatchlist,
	onFailure: function () { setTimeout(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="titles"')== 0) {
			if (inList && lines[i].indexOf('</textarea>') == 0) {
				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;
	recent2.download({url: recent2.scriptPath + 'index.php?title=' + recent2.spelldictUrl + '&action=raw&ctype=text/css',
	onSuccess: recent2.processSpelldict,
	onFailure: function () { setTimeout(getSpelldict, 15000); return true; }});
};

recent2.processSpelldict=function(req, bundle) {
	var spelldict={};
	var lines=req.responseText.split('\n');
	var a=[];
	for (var i=0; i<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(split[0]);
	}
	window.spelldict=spelldict;
	window.spellRe=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('Unknown position '+position+' in recent2.js, newOutputDiv');
			window.newOutputDiv.alerted=true;
		}
		return newOutputDiv(klass, 'bottom');
	}
	if (!immortal) { ret.id=newOutputDiv.uid++; }
	window.outputDivs.push(ret);
	return ret;
};
window.newOutputDiv.alerted=false;
window.newOutputDiv.uid=0;
window.outputDivs=[];

window.grabRecentChanges=function(feed) {
	if (! window.badWords && recent2.filter_badwords ) {
		if ( ! window.gettingBadWords ) { recent2.getBadWords(); }
		return setTimeout(function(){grabRecentChanges(feed);}, 500);
	}
	if (! window.watchlist && recent2.filter_watchlist) {
		if (! window.gettingWatchlist ) recent2.getWatchlist();
		return setTimeout(function(){grabRecentChanges(feed);}, 500);
	}
	if (! window.spelldict && recent2.filter_spelling) {
		if (! window.gettingSpelldict) recent2.getSpelldict();
		return setTimeout(function(){grabRecentChanges(feed);}, 500);
	}
	if (typeof(recent2.sysopRegExp) == 'undefined') {
		if (! recent2.gettingSysops) recent2.getSysops();
		return setTimeout(function(){grabRecentChanges(feed);}, 500);
	}

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

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

var greyFont='<font color="#777">';

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

recent2.newWindows=true;

window.linkmaker=function(url, text) {
	var s='<a href="' + url + '"';
	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;
	if (doc=req.responseXML.documentElement) {
		if (recent2.items=doc.getElementsByTagName('item')) {
			if ((recent2.itemsCurrent=recent2.items.length) > 0) {
				recent2.bundleRef = bundle;
				processRecentChangesSingle(); // start processing one diff every 50 ms
				return;
			}
		}
	}
	processRecentChangesDisplay(bundle);
	return;
}

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

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

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

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

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

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

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

	// articleTitle is the wgTitle thingy with spaces and all that
	var 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 namespace=articleTitle.replace(/:.*/, '');
		if (recent2.namespaces[namespace] &&
	( ( recent2.showTemplates && namespace != recent2.templateNamespace ) ||
		! recent2.showTemplates )) {
			nextChangeSoon(true); return;
		}
	}

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

	// perhaps restrict to watchlist articles
	if (recent2.filter_watchlist && articleTitle &&
			! window.watchlist[articleTitle.replace(/^Talk:/, '').replace(/[ _]talk:/, ':')]) {
		nextChangeSoon(true); return;
	}

	// 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] || '';
				if (articleTitle.toLowerCase().indexOf(badWord.toLowerCase())<0) { // avoid legit article title occurrences
					badWord=badWord.replace(/^\s+|\s+$/g, '');
					if (badWord!='') {
						matchesPlain+=badWord+', ';
						badWord=badWord.replace(/([^\w ])/g, '\\$1');
						matchesRe+=badWord+'|';
					}
				}
			}
			matchesRe=matchesRe.replace(/\|$/, '');
			matchesPlain=matchesPlain.replace(/, $/, '');
			if (!previousVandal && matchesRe=='') { nextChangeSoon(); return; }
			// highlighting
			var highlighted=diffCell && diffCell[1];
			if (matchesRe) {
				highlighted=highlighted.replace(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 = '[Previously rolled back this editor] ' + matchesPlain;
			}

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



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

// linkify and popupsify wikilinks
recent2.doLinkify=function(txt) {
	if (!txt || !recent2.linkify) { 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('no such div: diff_div_' + start); return; }
	var state=false; // hide
	if (div.style.display=='none') state=true; // show
	for (var i=start; i<end; ++i) {
		showHideDetail(i, true, state);
	}
}

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

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


recent2.pendingLines=[];

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

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

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

recent2.togglePausedOutput=function() {
	if (!recent2.pausedOutput) { recent2.pausedOutput = true; return true; }
	else recent2.pausedOutput=false;
	var outputBuffer='';
	while (recent2.pendingLines.length) {
		outputBuffer+=recent2.doLine(recent2.pendingLines.pop());
		if (recent2.pendingLines.length) { outputBuffer+=recent2.outputSeparator; }
	}
	var pos=recent2.outputPosition;
	var output=newOutputDiv('recent2.lines', pos);
	output.innerHTML=outputBuffer;
	setTimeout(function() {recent2.doPopups(output)}, 300);
	return false;
}

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

recent2.doLine=function(bundle) {
	if (recent2.pausedOutput) {
		recent2.pendingLines.push(bundle);
		return '';
	}
	//if (recent2.filter_spelling) { return recent2.doSpellLine(bundle); }
	var sysop = null;
	if (typeof(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 + '">Show</a>] ' +
		formatTime(bundle.timestamp) + ' ' +
		//latest + ' ' + processRecentChanges.lastDate + ' ' +
		'(' + linkmaker(lastDiffPage, 'last') + ')' +
		' (' + linkmaker(bundle.article+'&action=history', 'hist') + ')' +
		' ' + linkmaker(bundle.article, bundle.articleTitle) +
		( bundle.badWord ? ' matched <b>' + bundle.badWord + '</b> . . ' : ' . . ') +
		linkmaker(recent2.articlePath + 'User:' + bundle.editor, bundle.editor) +
		group + ' (' +
		linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor, 'talk') + ' | ' +
		linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor + '?action=edit' +
			'&autoedit=s#$#\\n{'+'{subst:uw-test1|' + bundle.articleTitle +
			'}}%20~~' + '~~#&autosummary=Your%20recent%20edits%20to%20[[' + bundle.articleTitle + ']]',
			'uw-test') + ' | ' +
		linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor + '?action=edit' +
			'&autoedit=s#$#\\n{'+'{subst:uw-vandalism1|' + bundle.articleTitle +
			'}}%20~~' + '~~#&autosummary=Your%20recent%20edits%20to%20[[' + bundle.articleTitle + ']]',
			'uw-vand')     + ' | ' +
		linkmaker(recent2.articlePath + 'Special:Contributions/' + bundle.editor, 'contribs') + ' | ' +
		linkmaker(recent2.articlePath + 'Special:Blockip/' + bundle.editor, 'block') + ') . . ' +
		( bundle.summary ? '<i>('+bundle.summary+')</i> . . ' : '') +
		'[<a href="javascript:tryRollback(' + bundle.id + ')" class="recent2_rollback">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&autoclick=wpDiff&autominor=true';
	var wl=badword.toLowerCase();
	var cor=spelldict[wl];
	if (!cor|| !cor.length) { alert('Could not find an entry for ' + wl); return; }
	if (cor.length > 1) {
		var q='Which correction should I use?\nPlease either type a number or another correction.\n';
		for (var i=0; i<cor.length; ++i) { q += '\n' + i + ': ' + cor[i]; }
		var ans=prompt(q);
		if (!ans) {return;}
		var num=parseInt(ans, 10);
		if (num > -1 && num < cor.length) { cor = cor[num]; }
		else { cor = ans; }
	} else {
		cor = cor[0];
	}
	cor=cor.replace(/^ *| *$/g, '');
	url += '&autosummary=Correcting%20spelling:%20' + wl + '->' + cor;
	url += '&autoedit=';
	c0=cor.charAt(0);
	wl0 = wl.charAt(0);
	b='\\b';
	url += ['s', b + wl + b, cor, 'g;'].join('#');
	wl=wl0.toUpperCase() + wl.substring(1);
	cor=c0.toUpperCase() + cor.substring(1);
	url += ['s', b + wl + b, cor, 'g;'].join('#');
	wl=wl.toUpperCase();
	cor=cor.toUpperCase();
	url += ['s', b + wl + b, cor, 'g;'].join('#');
	window.open(url);
};

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

window.vandals={};

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

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

recent2.tryAdminRollback=function(id){
	var b=recent2.getBundleVandal(id);
	if (!b) { return; }
	var vandal=b.editor;
	var onSuccess=function (x, bundle) {
		var rollRe=RegExp('<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('No rollback link found.' +
			'\nMaybe you should try the non-admin rollback by checking the checkbox above?\n' +
			'Alternatively, this may be a bug.');
			return;
		}
		var lastEditor=match[2].split('+').join(' ');
		var lastSummary=match[4] || '';
		if (lastEditor != vandal) {
			var summary=lastSummary.replace(RegExp('<[^>]*?>','g'),'');
			if (!summary) summary=lastSummary;
			alert( 'Could not rollback - someone else has edited since the vandal.\n\nPage: '+ b.articleTitle +
				'\nVandal: '+vandal+'\nLast editor: '+lastEditor+'\nEdit summary: '+summary);
			return;
		}
		var rollbackUrl=match[1].split('&amp;').join('&');
		recent2.openBackgroundWindow(rollbackUrl);
	}
	var onFailure = function(x,bundle) {
		alert('HTTP failed when trying to get rollback link in url\n' + bundle.url +
		'\n\nHTTP status text: ' + x.statusText);
		return true;
	}
	recent2.download({ url:b.url, onSuccess: onSuccess, id: b.id, onFailure:onFailure});
};

recent2.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); }
	recent2.download({ url: url, onSuccess: onSuccess, id: b.id}); // fixme: onFailure
};

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

	}
	catch ( someError ) { alert('JSON business failed.\n\n' + json.substring(0,200)
		+ '\n\nCannot rollback.'); return; }
	var i;
	for (i=0; i<edits.length; ++i) {
		if (edits[i]['user']!=bundle.editor) { break; }
	}
	if (i===0) {
		alert( 'Could not rollback - someone else has edited since the vandal.\n\nPage: ' +
		bundle.articleTitle +
			'\nVandal: '+bundle.editor+'\nLast editor: '+edits[0]['user']+
			'\nEdit summary: '+edits[0]['comment']);
		return;
	}
	if (i==edits.length) {
		alert(bundle.editor + ' seems to be the only editor to ' + bundle.articleTitle +
			'.\n\nRollback aborted.'); return;
	}
	var prevEditor=edits[i]['user'];
	var prevRev=edits[i]['revid'];
	var summary='Reverted edits by [[Special:Contributions/' + escape(bundle.editor) + '|' +
		escape(bundle.editor) + ']] to last version by ' + escape(prevEditor);
	summary=summary.split(' ').join('%20');
	var url=bundle.article + '&action=edit&autosummary=' + summary + '&oldid=' + prevRev +
		'&autoclick=wpSave&autominor=true&autowatch=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='Hide'; }
	else { div.style.display='none';   lk.innerHTML='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)) {
		// 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('Ignore talk pages', !recent2.show_talkpages,
			function() { recent2.show_talkpages=!this.checked; }, 'talk');
	recent2.newCheckbox('Ignore pages outside the article namespace', recent2.hideNonArticles,
			function() { recent2.hideNonArticles = this.checked; }, 'hidenonarticles');
	recent2.newCheckbox('... except for the Template namespace', recent2.showTemplates,
			function() { recent2.showTemplates = this.checked; }, 'showtemplates');
	recent2.newCheckbox('Automatically expand new content', recent2.autoexpand,
			function() { recent2.autoexpand = this.checked; }, 'autoexpand');
	recent2.newCheckbox('Only show edits unchanged after four updates', recent2.delay_preset,
			function() { recent2.delay = (this.checked) ? 4 : 0; }, 'delayby4');
	recent2.newCheckbox('Use non-admin rollback', recent2.non_admin_rollback,
			function() { recent2.non_admin_rollback = this.checked; }, 'nonadminrollback');
	recent2.newCheckbox('Ignore my edits', recent2.ignore_my_edits,
			function() { recent2.ignore_my_edits = this.checked; }, 'ignoremyedits');
	recent2.newCheckbox('Ignore safe pages', recent2.ignore_safe_pages,
			function() { recent2.ignore_safe_pages = this.checked; }, 'ignoresafepages');
	recent2.newCheckbox('Hide admin edits', recent2.hide_sysop_edits,
			function() { recent2.hide_sysop_edits = this.checked; hideSysopEdits(recent2.hide_sysop_edits); }, 'hidesysopedits');

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

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

window.marvin=function() {

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

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

// setup checkboxes
	recent2.controlUI();

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

// get the full sysop list in chunks
recent2.getSysops = function(startUser) {
	recent2.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;
	try {
		eval('var o = ' + json);
		var users = o.query.allusers;
	}
	catch(someError) {
		alert('Could not process admin list.\n\n"' + 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(recent2.sysopList);
	}
	else {
		recent2.getSysops(users[recent2.apiAulimit - 1].name);
	}
	return;
}

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

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

recent2.addToolboxLink=function(url, name, id){
		var tb = document.getElementById('p-tb').getElementsByTagName('ul')[0];
		recent2.addlilink(tb, url, name, id);
}

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

recent2.testPage = function (str) {
	return RegExp(str.split(/[_ ]/).join('[_ ]'), 'i').test(document.location.href);
};

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

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


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

recent2.getParamValue = function(paramName) {
	var cmdRe = RegExp('[&?]' + paramName + '=([^&]*)');
	var h = document.location;
	var m;
	if (m = cmdRe.exec(h)) {
		try {
			return decodeURI(m[1]);
		} catch (someError) {}
	}
	return null;
};

recent2.substitute = function(data,cmdBody) {
	// alert('sub\nfrom: ' + cmdBody.from + '\nto: ' + cmdBody.to + '\nflags: ' + cmdBody.flags);
	var fromRe = 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 = 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;
};

recent2.runOnLoad = function(f) {
	if (window.addEventListener) {
		window.addEventListener("load", f, false);
	}
	else if (window.attachEvent) {
		window.attachEvent("onload", f);
	}
	else {
		window._old_popup_autoedit_onload = window.onload;
		window.onload = function() {
			window._old_popup_autoedit_onload();
			f();
		}
	}
};

window.AVTAutoEdit = function() {

	if (typeof(window.autoEdit) != 'undefined') {
		if (window.autoEdit.alreadyRan) return false;
	}
	else {
		window.autoEdit = {};
	}
	window.autoEdit.alreadyRan = true;
	var cmdString = recent2.getParamValue('autoedit');
	if (cmdString) {
		try {
			var 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) {
				WikEdUpdateFrame();
			}
		}
	}

	var summary = recent2.getParamValue('autosummary');
	if (summary) document.editform.wpSummary.value = summary;

	var minor = recent2.getParamValue('autominor');
	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 = recent2.getParamValue('autowatch');
	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 = recent2.getParamValue('autoclick');
	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 = '<font size="+1"><b>The "' + button.value +
					'" button has been automatically clicked.' +
					' Please wait for the next page to load.</b></font>';
				document.title = '(' + document.title + ')';
				headings[0].parentNode.insertBefore(div, headings[0]);
				button.click();
			}
		} else {
			alert('Anti-Vandal Tool\n\nautoclick: could not find button "' + btn + '".');
		}
	}
};

recent2.runOnLoad(AVTAutoEdit);

// </nowiki></pre>