Jump to content

User:Danski454/RMcloser.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/* <nowiki>
 * Some of this code comes from RMCDbot and the UI is based on Evad37's XFDcloser
 */
var $box;
var form, interfaceWindow;
var moves = [];
var badMoves = [];
var overwritePages = [];
var associatedPagesChecked = 0;
var basePages = 0;
$.when(
	window.Morebits || mw.loader.getScript('https://en.wikipedia.org/enwiki/w/index.php?title=MediaWiki:Gadget-morebits.js&action=raw&ctype=text/javascript'),
	$.ready,
	importStylesheet( 'MediaWiki:Gadget-morebits.css' )
).then(function(){
	
	//var form, interfaceWindow;
	var moveGroups = {};
	var loaded = false;
	var noSubpageNamespaces = [0, 6, 8, 14, 446];
	/*var moves = [];
	var badMoves = [];
	var overwritePages = [];
	var associatedPagesChecked = 0;
	var basePages = 0;*/
	var defaultConfig = {
		relistSummary: "Relisted requested move using [[User:Danski454/RMcloser|RMcloser]]",
		loadDiff: true,
		outcomes: {
			"moved": {
				wikitext:"'''moved''' ",
				summary: "Closed requested move as moved using [[User:Danski454/RMcloser|RMcloser]]"
			},
			"not moved": {
				wikitext:"'''not moved''' ",
				summary: "Closed requested move as not moved using [[User:Danski454/RMcloser|RMcloser]]"
			},
			"no consensus": {
				wikitext:"'''no consensus''' ",
				summary: "Closed requested move as no consensus using [[User:Danski454/RMcloser|RMcloser]]"
			},
			"withdrawn": {
				wikitext:"'''withdrawn''' ",
				summary: "Closed requested move as withdrawn using [[User:Danski454/RMcloser|RMcloser]]"
			}
		},
		customOutcomeSummary: "Closed requested move using [[User:Danski454/RMcloser|RMcloser]]",
		customSuffix: null,
		overwriteMode: "auto" //off, swap, delete
	};
	
	function makeLink(target, text=null){
		console.log('makeLink');
		if (!text) {
			text = target;
		}
		return '<a href="/wiki/' + target.replace(/ /g, '_') + '">' + text + '</a>';
	}
	
	function display(){
		console.log('display');
		if (moves.length === 0) {
			//no moves
			form.append({
				type: 'div',
				label: 'Unable to get pages'
			});
		} else if (badMoves.indexOf(':*') !== -1) {
			//something has gone wrong
			form.append({
				type: 'div',
				label: 'Unable to validate moves'
			});
		} else {
			var baseGroups = [];
			for (var i = 0; i < moves.length; i++) {
				if (moveGroups[moves[i][2]]) {
					moveGroups[moves[i][2]].moves.push(moves[i]);
					if (overwritePages.indexOf(moves[i][1]) !== -1) {
						//we are overwriting a redirect
						moveGroups[moves[i][2]].overwrite = true;
					}
				} else {
					moveGroups[moves[i][2]] = {
						moves: [moves[i]],
						overwrite: overwritePages.indexOf(moves[i][1]) !== -1,
						bad: badMoves.indexOf(moves[i][2]) !== -1
					};
					if (/^(:\/|t\/|t:)/.test(moves[i][2])) {
						var parent = moves[i][2].slice(2);
						moveGroups[moves[i][2]].parent = parent;
						if (!moveGroups[parent]) {
							moveGroups[parent] = {
								moves: [],
								overwrite: overwritePages.indexOf(parent) !== -1,
								bad: badMoves.indexOf(parent) !== -1,
								children: [moves[i][2]]
							};
							baseGroups.push(parent);
						} else {
							moveGroups[parent].children.push(moves[i][2]);
						}
					} else {
						baseGroups.push(moves[i][2]);
						moveGroups[moves[i][2]].children = [];
					}
				}
			}
			var movefield = form.append({
				type: 'field',
				label: 'Moves'
			});
			var moveList = [];
			for (var j = 0; j < baseGroups.length; j++) {
				var source = moveGroups[baseGroups[j]].moves[0][0];
				var target = moveGroups[baseGroups[j]].moves[0][1];
				var flag = '';
				if (moveGroups[baseGroups[j]].bad) {
					flag = ' [cannot move]';
				} else if (moveGroups[baseGroups[j]].overwrite) {
					if ((getConfig('overwriteMode') === 'delete' ||
						getConfig('overwriteMode') === 'auto') &&
						Morebits.userIsInGroup("sysop")) {
						flag = ' [will delete target]';
					} else {
						flag = ' [swap]';
					}
				}
				moveList.push({
					name: 'moves-' + baseGroups[j],
					label: source + ' → ' + target + flag,
					checked: !moveGroups[baseGroups[j]].bad,
					disabled: moveGroups[baseGroups[j]].bad
				});
				if (moveGroups[baseGroups[j]].children.length) {
					moveList[moveList.length-1].subgroup = {
						type: 'checkbox',
						list: []
					};
					for (var k = 0; k < moveGroups[baseGroups[j]].children.length; k++) {
						var cflag = '';
						if (moveGroups[moveGroups[baseGroups[j]].children[k]].bad) {
							cflag = ' [cannot move]';
						} else if (moveGroups[moveGroups[baseGroups[j]].children[k]].overwrite) {
							if ((getConfig('overwriteMode') === 'delete' ||
								getConfig('overwriteMode') === 'auto') &&
								Morebits.userIsInGroup("sysop")) {
								cflag = ' [will delete target]';
							} else {
								cflag = ' [swap]';
							}
						}
						if (moveGroups[baseGroups[j]].children[k].charAt(1) === '/') {
							//subpages
							var pages = moveGroups[moveGroups[baseGroups[j]].children[k]].moves.length.toString();
							if (moveGroups[baseGroups[j]].children[k].charAt(0) === 't') {
								pages = pages + ' talk';
							}
							var subpageMoves = [];
							for (var l = 0; l < moveGroups[moveGroups[baseGroups[j]].children[k]].moves.length; l++) {
								var move = moveGroups[moveGroups[baseGroups[j]].children[k]].moves[l];
								subpageMoves.push(move[0] + ' → ' + move[1]);
							}
							subpageMoves = subpageMoves.join('\n');
							moveList[moveList.length-1].subgroup.list.push({
								name: 'moves-' + moveGroups[baseGroups[j]].children[k],
								label: pages + ' subpage(s)' + cflag,
								checked: !moveGroups[moveGroups[baseGroups[j]].children[k]].bad,
								disabled: moveGroups[moveGroups[baseGroups[j]].children[k]].bad,
								subgroup: {
									type: 'div',
									label: subpageMoves
								}
							});
						} else {
							//talk
							var talk = moveGroups['t:' + baseGroups[j]].moves[0][0];
							var talkTarget = moveGroups['t:' + baseGroups[j]].moves[0][1];
							moveList[moveList.length-1].subgroup.list.push({
								name: 'moves-t:' + baseGroups[j],
								label: talk + ' → ' + talkTarget + cflag,
								checked: !moveGroups['t:' + baseGroups[j]].bad,
								disabled: moveGroups['t:' + baseGroups[j]].bad
							});
						}
					}
				}
			}
			movefield.append({
				type: 'checkbox',
				name: 'moves',
				list: moveList
			});
		}
		form.append({
			type: 'submit',
			label: 'Close Discussion'
		});
		var result = form.render();
		interfaceWindow.setContent(result);
		interfaceWindow.display();
	}
	
	function getValidMoves(){
		console.log('getValidMoves');
		var sysop = Morebits.userIsInGroup("sysop");
		var te = sysop || Morebits.userIsInGroup("templateeditor");
		var extconfirmed = sysop || Morebits.userIsInGroup("extendedconfirmed");
		var overwrite = (sysop || Morebits.userIsInGroup("extendedmover")) &&
			getConfig("overwriteMode") !== "off";
		checkTitles(moves, true, extconfirmed, sysop, te, overwrite, display);
	}
	
	function getBaseMove(title, index = 0){
		console.log('getBaseMove');
		var f = moves.filter(function(t){return t[index] === title;});
		if (f.length > 0) {
			return f[0][2];
		}
		return null;
	}
	
	function startClose(e){
		console.log('startClose');
	}
	
	function closeDiscussion(outcome, comment){
		console.log('closeDiscussion');
		var outcomes = getConfig("outcomes");
		var suffix = getConfig("customSuffix");
		var top, summary;
		var bottom = "{{subst:RMbottom}}";
		if (suffix === null) {
			if (mw.config.get("wgUserGroups").indexOf("sysop") !== -1) {
				suffix = "";//sysops don't need a suffix
			} else if (mw.config.get("wgUserGroups").indexOf("extendedmover") !== -1) {
				suffix = " {{subst:RMpmc}}";
			} else {
				suffix = " {{subst:RMnac}}";
			}
		}
		if (outcomes[outcome]) {
			top = "{{subst:RMtop|" + outcomes[outcome].wikitext + comment
				+ suffix + "}}";
			summary = outcomes[outcome].summary;
		} else {
			top = "{{subst:RMtop|" + comment + suffix + "}}";
			summary = getConfig("customOutcomeSummary");
		}
		var re = /((=+)[^\n]+\2\s*\n(?:[^\n]|\n[^=])*\n?)\{\{\s?(?:requested move\/dated?|movereq)\s?\}\}\s*((?:[^=]|[^\n=]=|\2=+)+)/i;
		$.ajax({
			url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
			"/api.php?action=query&format=json&prop=revisions&titles=" + 
			mw.config.get("wgPageName") + "&rvprop=ids%7Ccontent&rvslots=main",
			dataType: "json",
			success: function(data){
				for (var page in data.query.pages) {
					var text = data.query.pages[page].revisions[0].slots.main["*"];
					text = text.replace(re, "$1" + top + "$3\n" + bottom + "\n");
					$.ajax({
						url: mw.util.wikiScript('api'),
						type: 'POST',
						dataType: 'json',
						data: {
							format: 'json',
							action: 'edit',
							title: mw.config.get('wgPageName'),
							text: text, 
							summary: summary,
							token: mw.user.tokens.get('editToken')
						}
					})
					.done(function(data){
						if (data && data.edit && data.edit.result && data.edit.result == 'Success') {
							if (getConfig("loadDiff")) {
								if (data.edit.oldrevid !== null && data.edit.newrevid !== undefined){
									window.location.href = mw.config.get('wgScript') + '?diff=' + data.edit.newrevid + '&oldid=' + data.edit.oldrevid;//load diff
								} else {
									alert("Unable to load diff");
								}
							}
						} else {
							alert('Edit failed');
						}
					})
					.fail(function(){
						alert('Edit failed');
					});
				}
			},
			error: function(){
				alert('Edit failed');
			}
		});
	}
	
	function confirmRelist(){
		console.log('confirmRelist');
		if (confirm("Press OK to relist")) {
			relist();
		}
	}
	
	function getConfig(option){
		console.log('getConfig');
		if (typeof(window.RMcloserConfig) === "object" && window.RMcloserConfig
			&& window.RMcloserConfig[option] !== undefined) {
			if (defaultConfig[option] === undefined) {
				// if user did not set an option, error would occur
				console.warn("Config option '" + option + "' has no default setting.");
			}
			return window.RMcloserConfig[option];
		} else if (defaultConfig[option] !== undefined) {
			return defaultConfig[option];
		} else {
			throw Error("Config option '" + option + "' has no default setting and has not been set by the user.");
		}
	}
	
	function relist(){
		console.log('relist');
		$("#RMcloser-relist-button").off("click");
		$.ajax({
			url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
			"/api.php?action=query&format=json&prop=revisions&titles=" + 
			mw.config.get("wgPageName") + "&rvprop=ids%7Ccontent&rvslots=main",
			dataType: "json",
			success: function(data){
				for (var page in data.query.pages) {
					var text = data.query.pages[page].revisions[0].slots.main["*"];
					//regex based on RMCD bot's source code
					var re = /\{\{\s?(requested[_ ]move\/dated?|movereq)\s?\}\}.*?([0-2]\d):([0-5]\d),\s(\d{1,2})\s(\w*)\s(\d{4})\s\([A-Z]{3}\)[^\n\r]*/is;
					text = text.replace(re, "$&{{subst:relisting}}");
					$.ajax({
						url: mw.util.wikiScript('api'),
						type: 'POST',
						dataType: 'json',
						data: {
							format: 'json',
							action: 'edit',
							title: mw.config.get('wgPageName'),
							text: text, 
							summary: getConfig("relistSummary"),
							token: mw.user.tokens.get('editToken')
						}
					})
					.done(function(data){
						if (data && data.edit && data.edit.result && data.edit.result == 'Success') {
							if (getConfig("loadDiff")) {
								if (data.edit.oldrevid !== null && data.edit.newrevid !== undefined){
									window.location.href = mw.config.get('wgScript') + '?diff=' + data.edit.newrevid + '&oldid=' + data.edit.oldrevid;//load diff
								} else {
									alert("Unable to load diff");
								}
							}
						} else {
							alert('Edit failed');
						}
					})
					.fail(function(){
						alert('Edit failed');
					});
				}
			},
			error: function(){
				alert('Edit failed');
			}
		});
	}
	
	function getAssociatedPages(title, target){
		console.log('getAssociatedPages');
		var titleObj = new mw.Title(title);
		var ns = titleObj.getNamespaceId();
		var pageName = titleObj.getMainText();
		if (ns < 0) {
			associatedPagesChecked += 2;
			if (associatedPagesChecked >= 0) { getValidMoves(); }
			return;
		}
		if (noSubpageNamespaces.indexOf(ns) === -1) {
			$.ajax({
				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
				"/api.php?action=query&format=json&list=allpages&requestid=" + 
				encodeURIComponent(title) + "%7C" + encodeURIComponent(target) + 
				"&apprefix=" + encodeURIComponent(pageName) + "%2F&apnamespace=" + 
				ns.toString() + "&aplimit=101",
				dataType: "json",
				success: function(data){
					if (data.query.allpages.length > 100) {
						//too many subpages
						associatedPagesChecked++;
						if (associatedPagesChecked >= 0) { getValidMoves(); }
						return;
					}
					var [base, target] = data.requestid.split("|");
					for (var i = 0; i < data.query.allpages.length; i++) {
						moves.push([data.query.allpages[i].title, 
						data.query.allpages[i].title.replace(base, target), ":/" + base]);
					}
					associatedPagesChecked++;
					if (associatedPagesChecked >= 0) { getValidMoves(); }
				}
			});
		} else associatedPagesChecked++;
		if (ns % 2 === 0) {
			var titleTalk = titleObj.getTalkPage().getPrefixedText();
			var targetTalk = new mw.Title(target).getTalkPage().getPrefixedText();
			$.ajax({
				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
				"/api.php?action=query&format=json&list=allpages&requestid=" + 
				encodeURIComponent(titleTalk) + "%7C" + encodeURIComponent(targetTalk) + 
				"%7C" + encodeURIComponent(title) + "&prop=info&titles=" +
				encodeURIComponent(titleTalk) +
				"&apprefix=" + encodeURIComponent(pageName) + "%2F&apnamespace=" + 
				(ns+1).toString() + "&aplimit=101",
				dataType: "json",
				success: function(data){
					if (data.query.allpages.length > 100) {
						//too many subpages
						associatedPagesChecked++;
						if (associatedPagesChecked >= 0) { getValidMoves(); }
						return;
					}
					var [base, target, subject] = data.requestid.split("|");
					for (var i = 0; i < data.query.allpages.length; i++) {
						moves.push([data.query.allpages[i].title, 
						data.query.allpages[i].title.replace(base, target), "t/" + subject]);
					}
					for (var id in data.query.pages) {
						if (id !== "-1") {
							//talk page itself exists
							moves.push([base, target, "t:" + subject]);
						}
					}
					associatedPagesChecked++;
					if (associatedPagesChecked >= 0) { getValidMoves(); }
				}
			});
		} else associatedPagesChecked++;
		if (associatedPagesChecked >= 0) { getValidMoves(); }
	}
	
	function generateButtons(){
		console.log('generateButtons');
		if (loaded) return;
		loaded = true;
		$("#RMcloser-close-button").after('loading').remove();
		interfaceWindow = new Morebits.simpleWindow(
			Math.min(900, Math.floor(window.innerWidth*0.8)),
			Math.floor(window.innerHeight*0.9)
		);
		interfaceWindow.setTitle( 'RMcloser' );
		interfaceWindow.addFooterLink('script documentation', 'User:Danski454/RMcloser');
		form = new Morebits.quickForm(startClose);
		var closurefield = form.append({
			type: 'field',
			label: 'Closure'
		});
		var outcomes = getConfig('outcomes');
		var outcomesList = [{value:'', label:'other', selected: true}];
		for (var o in outcomes) {
			outcomesList.push({value:o, label:o});
		}
		closurefield.append({
			type: 'select',
			label: 'Outcome',
			name: 'type',
			list: outcomesList
		});
		closurefield.append({
			type: 'textarea',
			name: 'comment',
			label: 'Additional comments',
			rows: 2,
			cols: 50
		});
		if($box.next().is("p")){
			//single item
			var match = $box.next().text().match(/(.+) → (.+) –/);
			if (match) {
				associatedPagesChecked = -2;//count up to zero, one for talk, one for subject
				moves.push([match[1], match[2], match[1]]);
				getAssociatedPages(match[1], match[2])
			} else {
				display();
			}
		} else if ($box.next().is("ul")){
			associatedPagesChecked = $box.next().children().length * -2;
			$box.next().children().each(function(){
				var match = $(this).text().match(/(.+) → (.+)/);
				if (match) {
					moves.push([match[1], match[2], match[1]]);
					getAssociatedPages(match[1], match[2])
				} else {
					associatedPagesChecked += 2;
					if (associatedPagesChecked >= 0) { getValidMoves(); }
				}
			});
		} else {
			display();
		}
	}
	
	function start(){
		console.log('start');
		mw.loader.load( '//en.wikipedia.org/enwiki/w/index.php?title=User:Danski454/RMcloser.css&action=raw&ctype=text/css', 'text/css' );
		if (mw.config.get("wgNamespaceNumber") % 2 === 0) {
			return;//we are not on a talk page
		}
		var boxes = 0;
		$(".tmbox-move").each(function(index){
			var $this = $(this);
			var $b = $this.find("td.mbox-text b");
			if($b.text().match(/^It has been proposed in this section that /)) {
				boxes++;
				$box = $this;
			}
		});
		if (boxes !== 1) {
			//we do not have exactly one RM, exiting
			return;
		}
		if (mw.config.get("wgUserGroups").indexOf("autoconfirmed") === -1 && 
			mw.config.get("wgUserGroups").indexOf("confirmed") === -1 && 
			mw.config.get("wgUserGroups").indexOf("sysop") === -1) {
			//must be autoconfirmed
			console.warn("You must be autoconfirmed to use RMcloser");
			return;
		}
		//add button
		$box.find(".mbox-text").children(":last").children().append(' • <a href="#=!=" class="external text" role="button" id="RMcloser-close-button">close</a> • <a href="#=!=" class="external text" role="button" id="RMcloser-relist-button">relist</a>');
		$("#RMcloser-close-button").click(generateButtons);
		$("#RMcloser-relist-button").click(confirmRelist);
	}
	
	function checkTitles(titlepairs, tboverride, extconfirmed, sysop, te, overwrite, next){
		console.log('checkTitles');
		var titles = [];
		var sources = [];
		var targets = [];
		var checked = 0;
		var protdone = false;
		var redirsToCheck = [];
		for (var i = 0; i < titlepairs.length; i++) {
			var source = titlepairs[i][0];
			var target = titlepairs[i][1];
			if (titles.indexOf(source) !== -1) {
				badMoves.push(titlepairs[i][2]);
				console.log(448);
				checked += 2;
				continue;
			}
			if (titles.indexOf(target) !== -1) {
				console.log(452);
				badMoves.push(titlepairs[i][2]);
				checked += 2;
				continue;
			}
			titles.push(source);
			sources.push(sources);
			targets.push(target);
			titles.push(target);
			/*$.ajax({
				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
				"/api.php?action=titleblacklist&format=json&tbtitle=" + 
				encodeURIComponent(target) + "&tbaction=move&tbnooverride=1",
				dataType: "json",
				success: function(data){
					checked++;
					if (data.error) {
						console.log("Could not get titleblacklist data for " + target);
					} else if (data.titleblacklist.result === "blacklisted") {
						if (tboverride) {
							console.log(target + " matches the titleblacklist");
						} else {
							console.warn(target + " matches the titleblacklist");
							//issues.push({title:target, role:"target", 
								message:target + " matches the titleblacklist"});
						}
					}
					if (checked >= titlepairs.length * 2 && protdone) {
						next();
					}
				},
				error: function() {
					checked++;
					console.log("Could not get titleblacklist data for " + target);
					if (checked >= titlepairs.length * 2 && protdone) {
						next();
					}
				}
			});
			$.ajax({
				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
				"/api.php?action=titleblacklist&format=json&tbtitle=" + 
				encodeURIComponent(source) + "&tbaction=edit&tbnooverride=1",
				dataType: "json",
				success: function(data){
					checked++;
					if (data.error) {
						console.log("Could not get titleblacklist data for " + source);
						return;
					} else if (data.titleblacklist.result === "blacklisted") {
						if (tboverride) {
							console.log(source + " matches the titleblacklist");
						} else {
							console.warn(source + " matches the titleblacklist");
							//issues.push({title:source, role:"source", 
								message:source + " matches the titleblacklist"});
						}
					}
					if (checked >= titlepairs.length * 2 && protdone) {
						next();
					}
				},
				error: function() {
					checked++;
					console.log("Could not get titleblacklist data for " + source);
					if (checked >= titlepairs.length * 2 && protdone) {
						next();
					}
				}
			});// */
		}
		var loops = 0;
		while (titles.length){
			loops++;
			var titlesToCheck;
			if (titles.length > 50) {
				titlesToCheck = titles.slice(0, 50);
				titles.splice(0, 50);
			} else {
				titlesToCheck = titles;
				titles = [];
			}
			titlesToCheck = encodeURIComponent(titlesToCheck.join("|"));
			$.ajax({
				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
				"/api.php?action=query&format=json&prop=info&titles=" + 
				titlesToCheck + "&inprop=protection",
				dataType: "json",
				success: function(data){
					var pages = data.query.pages;
					for(var page in pages){
						if (page[0] === "-" && sources.indexOf(pages[page].title) !== -1) {
							//source does not exist
							console.log(543);
							badMoves.push(getBaseMove(pages[page].title));
						}
						if (page[0] !== "-" && targets.indexOf(pages[page].title) !== -1) {
							redirsToCheck.push(pages[page].title);
						}
						for (var i = 0; i < pages[page].protection.length; i++) {
							if (pages[page].protection[i].type === "move" || 
								pages[page].protection[i].type === "create") {
								if ((pages[page].protection[i].level === "sysop" && !sysop) ||
									(pages[page].protection[i].level === "templateeditor" && !te) ||
									(pages[page].protection[i].level === "extendedconfirmed" && !extconfirmed)) {
									console.log(554);
									badMoves.push(getBaseMove(pages[page].title) ||
										getBaseMove(pages[page].title, 1));
								}
							}
						}
					}
					loops--;
					if (!loops && !titles.length) {
						checkRedirects(overwrite, next, titlepairs, redirsToCheck);
					}
				},
				error: function() {
					loops--;
					console.warn("Could not get protection/existence data for many pages.");
					badMoves.push(':*');
					if (!loops && !titles.length) {
						checkRedirects(overwrite, next, titlepairs, redirsToCheck);
					}
				}
			});
		}
	}
	
	function checkRedirects(overwrite, next, titlepairs, titles){
		console.log('checkRedirects');
		var latestRevisions = {};
		var todo = titles.length;
		if (titles.length === 0) {
			//nothing to do
			next();
		}
		while(titles.length){
			var titlesToCheck;
			if (titles.length > 50) {
				titlesToCheck = titles.slice(0, 50);
				titles.splice(0, 50);
			} else {
				titlesToCheck = titles;
				titles = [];
			}
			titlesToCheck = encodeURIComponent(titlesToCheck.join("|"));
			console.log('AJAX1');
			$.ajax({
				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
				"/api.php?action=query&format=json&prop=revisions&titles=" + 
				titlesToCheck + "&rvprop=ids%7Ccontent&rvslots=main",
				dataType: "json",
				success: function(data){
					console.log('success1');
					var pages = data.query.pages;
					for(var page in pages){
						latestRevisions[page] = pages[page];
						console.log('AJAX2');
						$.ajax({
							url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
							"/api.php?action=query&format=json&prop=revisions&pageids=" + 
							page + "&rvprop=ids%7Ccontent&rvslots=main&rvlimit=1&rvdir=newer",
							dataType: "json",
							success: function(data){
								console.log('success2');
								var pages = data.query.pages;
								var redirectRegExp = /^\s*#REDIRECT\s*\[\[ *([^\][<>|{}#\n]+?) *(?:#[^\][<>|{}\n]*)?]]/i;
								for(var page in pages){
									console.log(todo);
									if (!(pages[page].revisions[0].slots.main["*"].match(redirectRegExp) && 
									latestRevisions[page].revisions[0].slots.main["*"].match(redirectRegExp))){
										todo--;
										//either current or first revision is not a redir
										console.log(611);
										badMoves.push(getBaseMove(pages[page].title, 1));
									} else if (pages[page].revisions[0].revid !== latestRevisions[page].revisions[0].revid && !overwrite) {
										// page has non trivial history we cannot overwrite
										todo--;
										console.log(616);
										badMoves.push(getBaseMove(pages[page].title, 1));
									} else {
										var target = latestRevisions[page].revisions[0].slots.main["*"].match(redirectRegExp)[1].replace("_", " ");
										target = target.replace(/^./, target.charAt(0).toUpperCase());
										var correct = false;
										for (var i = 0; i < titlepairs.length; i++) {
											if (titlepairs[i][0] === target && titlepairs[i][1] === pages[page].title){
												correct = true;
												break;
											}
										}
										if (!correct) {
											//does not redirect to source
											todo--;
											console.log(629);
											badMoves.push(getBaseMove(pages[page].title, 1));
										} else if (pages[page].revisions[0].revid !== latestRevisions[page].revisions[0].revid) {
											overwritePages.push(pages[page].title);
											console.log('AJAX3');
											$.ajax({
												url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 
												"/api.php?action=query&format=json&prop=revisions&pageids=" + 
												page + "&rvprop=ids&rvslots=main&rvlimit=1&rvtag=mw-removed-redirect",
												dataType: "json",
												success: function(data){
													console.log('success3');
													var pages = data.query.pages;
													for(var x in pages){
														if(pages[x].revisions &&
														pages[x].revisions.length){
															console.log(642);
															badMoves.push(getBaseMove(pages[page].title, 1));
														}
													}
													todo--;
													if (todo === 0) {
														next();
													}
												},
												error: function() {
													todo--;
													console.warn("Could not get a page's history.");
													if (todo === 0) {
														next();
													}
												}
											});
										} else {
											todo--;
										}
									}
									if (todo === 0) {
										next();
									}
								}
							},
							error: function() {
								todo--;
								console.warn("Could not get a page's history.");
								if (todo === 0) {
									next();
								}
							}
						});
					}
				},
				error: function() {
					console.log(678);
					badMoves.push(':*');
					todo-=50;
					if (todo <= 0) {
						next();
					}
				}
			});
		}
	}
	
	//everything is ready
	start();
	
});
//</nowiki>