Fandom Developers Wiki
Register
m (port to SCM)
m (use `dev.showCustomModal.closeModal` and drop `window`)
Line 825: Line 825:
 
var sweeperForm = createForm(currPages, options);
 
var sweeperForm = createForm(currPages, options);
 
sweepedPages = 0;
 
sweepedPages = 0;
window.dev.showCustomModal("Link Sweeper", sweeperForm, {
+
dev.showCustomModal("Link Sweeper", sweeperForm, {
 
id: "backLinkForm",
 
id: "backLinkForm",
 
width: 325,
 
width: 325,
Line 895: Line 895:
 
handler: function () {
 
handler: function () {
 
bContinueProcess = false;
 
bContinueProcess = false;
$("#backLinkForm").closeModal();
+
dev.showCustomModal.closeModal($("#backLinkForm"));
 
}
 
}
 
}, {
 
}, {

Revision as of 23:45, 27 April 2021

// <pre>
//Link sweeper v1.1d
//By Dessamator
//2016/06/15 Bug fix made sure anchors(link#anchor) are handled properly and change "_" to space in links
//add regex to clean link in articles (e.g. myarticle in "<gallery> foo.png|link=myarticle</gallery>"
//2016/05/26 Added separate localization and config file and sanitizer
//2016/05/05 Added button to find all redlinks in a page
//2016/04/01 Added custom list of categories to fetch pages
//Removes backlinks from pages, works from Special:WantedPages / Special:Whatlinkshere & Special:wantedfiles
//Ideas and some code from from https://dev.wikia.com/wiki/MediaWiki:AutoEditPages/code.js & dev.wikia.com/masscategorization
//Change log:
//2016/04/01 Added support for cleaning templates, Added category, button for batch removal in special pages
//2016/03/17 Added support for cleaning links in  galleries
//2016/03/16 Added support for removing many links at once, added show backlinks button
//2016/03/12 Added Link to Wikia oasis skin (mytools) and "?action=delete"
//2016/03/10 Bug fix (improve detection of links on pages)
/* jshint -W097 , browser:true, devel:true, jquery:true */
/*global mediaWiki */
(function (window, mw, $) {
	"use strict";
	var defaultSettings = defaultSettings || {
		"settings": {
			"linkSweepConfirmation": window.linkSweepConfirmation || true,
			"linkSweeperDelay": 1000,
			"categoryList": ["Custom"].concat(window.CategoryList || ["Candidates for deletion", "Pages with broken file links", "Article stubs"]),
			"specialpages": window.linkSweeperRedlist || {
				"wantedpages": 1,
				"whatlinkshere": 1,
				"wantedfiles": 1,
				"listredirects": 1,
				"uncategorizedpages":1
			}
		}
	};
	var i18n = window.linksweeper_i18n || {
		"en": {
			"btn_show_back_links": "Show backlinks",
			"btn_add_category_content": "Add category content",
			"btn_fetch_redlinks": "Fetch red links",
			"btn_cancel": "Exit",
			"btn_remove_backlinks": "Remove backlinks",
			"label_pagelinks": "Page links (add one per line)",
			"label_reason": "Reason",
			"label_namespace": "Namespace",
			"label_fetch_from_category": "Fetch from Category",
			"label_delete_links": "Delete link?",
			"label_report": "Report:",
			"label_submit_warning": "This action will remove links from several pages. Are you sure?",
			"label_category_fetch": "Enter a category (without 'category:' prefix):",
			"label_report_search_backlinks": "Searching for backlinks ...",
			"report_found_backlinks": "Found backlinks",
			"report_link_not_found": "link not found in page",
			"report_action_cancel": "Cancelled",
			"report_no_links_found": "No links found",
			"report_fail_request": "Failed to retrieve information",
			"label_report_nobacklinks": "No links to sweep.",
			"label_error_fetch_links": "Error: Could not fetch links",
			"label_error_fetch_links_server": "Error: Server or connectivity problem.",
			"label_success_linksremoved": "- link(s) removed",
			"label_success": "pages processed.",
			"item_dropdown_allnamespaces": "all",
			"label_fetching_backlinks": "Fetching...",
			"default_removalsummary": "Removing link(s)"
		}
	};
	var userLang = defaultSettings[mw.config.get("wgUserLanguage")] || "en";
	var allowedSpecialPage;
	var linkSweepConfirmation;
	var delay;
	var categoryList;
	var defaultRemovalMsg;
	var sweepedPages = 0;
	var sweepedLinks = 0;
	var fileNamespace = {};
	var templateNamespace = {};
	var removableLinkList = [];
	var debug = false;
	var bContinueProcess = true;
	// Gets and escapes messages
	function msg(msgString) {
		return mw.html.escape(i18n[userLang][msgString] || i18n.en[msgString] || "");
	}
	//Capitalizes string
	function ucFirst(string) {
		return string.charAt(0).toUpperCase() + string.slice(1);
	}
	//escapes regExp string
	var escapeRegExp = function (text) {
		var regx = new RegExp("[-[\\]{}()*+?.,\\^$|#\\s]", "g");
		return text.replace(regx, "\\$&");
	};
	//Obtains links to clean from textarea
	function getLinksToClean() {
		var linkList = $("#pageNameId").val();
		if ($("#pageNameId").val().trim() === "") {
			return [];
		}
		var linkArray = linkList.split("\n");
		linkArray = linkArray.filter(function (n) {
			if (n && String(n).trim() !== "") {
				return true;
			}
		});
		return linkArray;
	}
	//Adds a message on actions to textarea
	function sendMsg(msg, bReplace) {
		if ($("#idReport").length) {
			if (bReplace) {
				$("#idReport").text(msg);
			} else {
				$("#idReport").append(msg + "\n");
			}
		} else {
			console.log(msg);
		}
	}
	//Adds a report on status
	function reportStatus(msg) {
		if ($("#lblReport").length) {
			$("#lblReport").text("Report:\t" + msg);
		} else {
			console.log(msg);
		}
	}
	//Gets possible file namespaces
	function setNamespaces() {
		var namespaceIds = mw.config.get("wgNamespaceIds");
		fileNamespace = {};
		Object.keys(namespaceIds).forEach(function (name) {
			if (namespaceIds[name] === 6) {
				fileNamespace[name] = true;
			}
			if (namespaceIds[name] === 10) {
				templateNamespace[name] = true;
			}
		});
	}
	// Compares two titles
	function compareTitle(title1, title2) {
		try {
			title1 = decodeURIComponent(title1);
			title2 = decodeURIComponent(title2);
			var tmpTitle1 = new mw.Title(title1);
			var tmpTitle2 = new mw.Title(title2);
			var isEqualNs = tmpTitle1.getNamespaceId() === tmpTitle2.getNamespaceId();
			return ucFirst(tmpTitle1.getName()) === ucFirst(tmpTitle2.getName()) && isEqualNs;
		} catch (e) {
			console.log(e);
			return false;
		}
	}
	//Updates progress bar
	function setProgress(action, value, max) {
		var maxProgress = $("#idProgressBar").attr("max");
		if (action === "add") {
			sweepedPages += 1;
			$("#idProgressBar").attr("value", sweepedPages);
		} else if (action === "set") {
			if (value) {
				$("#idProgressBar").attr("value", value);
			}
			if (max) {
				$("#idProgressBar").attr("max", max || maxProgress);
			}
		} else if (action === "wait") {
			$("#idProgressBar").removeAttr("value");
		} else if (action === "done") {
			$("#idProgressBar").attr("value", maxProgress);
		} else if (action === "reducemax" && maxProgress >= 2) {
			$("#idProgressBar").attr("max", maxProgress - 1);
		} else {
			$("#idProgressBar").attr("value", 0);
			$("#idProgressBar").attr("max", 1);
		}
	}
	//Gets current link to remove e.g. from special page
	function getPageLink($currItem) {
		var specialPage = mw.config.get("wgCanonicalSpecialPageName");
		var pageName = mw.config.get("wgPageName");
		if (specialPage && allowedSpecialPage[specialPage.toLowerCase()]) {
			if (specialPage.toLowerCase() === "whatlinkshere") {
				pageName = $("#mw-whatlinkshere-target").val();
			} else {
				pageName = $currItem.parent().children(":first-child").text();
			}
		}
		return pageName;
	}
	//Checks for valid namespace to remove links
	function getNamespacesFilter() {
		var objNamespace = mw.config.get("wgNamespaceIds");
		var nsNameList = $("#namespaceDropdown").val() || [];
		var nsFilters = [];
		var nsName;
		var ns;
		for (ns = 0; ns < nsNameList.length; ns += 1) {
			nsName = nsNameList[ns];
			if (nsName === "main") {
				nsFilters.push(0);
			}
			if (objNamespace[nsName]) {
				nsFilters.push(objNamespace[nsName]);
			}
		}
		return nsFilters.join("|");
	}
	//Creates a combo box with the namespace
	function createNamespaceCmbList(dropOptions) {
		var Html = mw.html;
		var cmbOutput = "";
		var objNamespace = mw.config.get("wgNamespaceIds");
		var i;
		for (i = 0; i < dropOptions.length; i += 1) {
			cmbOutput += Html.element("option", {
				"value": dropOptions[i]
			}, dropOptions[i]);
		}
		Object.keys(objNamespace).forEach(function (key) {
			if (objNamespace.hasOwnProperty(key) && key.length > 0) {
				cmbOutput += Html.element("option", {
					"value": key
				}, key);
			}
		});
		return cmbOutput;
	}

	function createCmbBox(options) {
		var Html = mw.html;
		var cmbTextOptions = "";
		options.forEach(function (main) {
			cmbTextOptions += Html.element("option", {
				"value": main
			}, main);
		});
		return new Html.Raw(cmbTextOptions);
	}
	//Creates a form and adds current page name
	function createForm(curPageName, options) {
		var Html = mw.html;
		var output = "";
		var formOutput = "";
		var dropList = createNamespaceCmbList([msg("item_dropdown_allnamespaces"), "main"]);
		var namespaceFieldset = new Html.Raw(Html.element("label", {
			"for": "namespaceDropdown"
		}, msg("label_namespace")) + Html.element("select", {
			id: "namespaceDropdown",
			style: "float:right",
			multiple: "",
			size: "3"
		}, new Html.Raw(dropList)));
		var formContent = [{
			tag: "label",
			attribs: {
				"for": "pageNameId"
			},
			content: msg("label_pagelinks")
		}, {
			tag: "textarea",
			attribs: {
				"id": "pageNameId",
				style: "height: 5em; width: 100%;"
			},
			content: curPageName
		}, {
			tag: "label",
			attribs: {
				"for": "linkRemovalReasonId"
			},
			content: msg("label_reason"),
			"newline": true
		}, {
			tag: "input",
			attribs: {
				"id": "linkRemovalReasonId",
				style: "float:right",
				type: "text",
				placeholder: "Enter a reason"
			},
			content: ""
		}, {
			tag: "fieldset",
			attribs: {},
			content: namespaceFieldset,
			generated: 1
		}, {
			tag: "label",
			attribs: {
				"for": "cmbCategories"
			},
			content: msg("label_fetch_from_category")
		}, {
			tag: "select",
			attribs: {
				"id": "cmbCategories",
				style: "max-width:60%; float: right"
			},
			content: createCmbBox(categoryList)
		}, {
			tag: "label",
			attribs: {
				"for": "chkBoxDeleteId"
			},
			content: msg("label_delete_links"),
			newline: true
		}, {
			tag: "input",
			attribs: {
				"id": "chkBoxDeleteId",
				type: "checkbox",
				value: "delete"
			},
			content: ""
		}, {
			tag: "label",
			attribs: {
				"id": "lblReport",
				"for": "idReport"
			},
			content: msg("label_report") + (options.reportStatus || ""),
			"newline": true
		}, {
			tag: "textarea",
			attribs: {
				"id": "idReport",
				style: "height: 5em; width: 100%;"
			},
			content: ""
		}, {
			tag: "progress",
			attribs: {
				"id": "idProgressBar",
				style: "width:100%",
				max: 100,
				value: (options.progress || 0)
			},
			content: ""
		}];
		var formAttribs = {
			"class": "WikiaForm"
		};
		var i;
		for (i = 0; i < formContent.length; i += 1) {
			if (formContent[i].newline) {
				formOutput += "<br>";
			}
			formOutput += Html.element(formContent[i].tag, formContent[i].attribs, formContent[i].content);
		}
		output = Html.element("form", formAttribs, new Html.Raw(formOutput));
		return output;
	}
	//https://github.com/spencermountain/wtf_wikipedia
	//Finds template transclusions in text
	function recursive_matches(opener, closer, text) {
		var out = [];
		var last = [];
		var chars = text.split("");
		var open = 0;
		var characterIndex = 0;
		var charGroup;
		var extraChars = opener.length - 1;
		var openerLocations = [];
		var lastOpener = 0;
		if (closer.length > 2) {
			return;
		}
		while (characterIndex < chars.length) {
			charGroup = chars[characterIndex];
			if (extraChars > 0) {
				charGroup = chars[characterIndex] + (chars[characterIndex + extraChars] || "");
			}
			//increment open tag
			if (charGroup === opener) {
				open += 1;
				openerLocations.push(characterIndex);
				if (extraChars > 0) {
					last.push("{");
					characterIndex += 1;
					continue;
				}
			}
			//decrement close tag
			if (charGroup === closer) {
				if (open > 0) {
					//Internal search
					lastOpener = openerLocations.pop();
					out.push(text.substring(lastOpener, characterIndex + extraChars + 1));
				}
				open -= 1;
				if (open < 0) {
					open = 0;
				}
			}
			if (open >= 0) {
				last.push(chars[characterIndex]);
			}
			characterIndex += 1;
		}
		return out;
	}
	//Performs query on wiki servers
	function processAction(type, actionConfig, successMsg, failMsg, runMethod) {
		var mwApi = (new mw.Api());
		var promise;
		if (actionConfig) {
			if (type === "post") {
				promise = mwApi.post(actionConfig);
			} else {
				promise = mwApi.get(actionConfig);
			}
			promise.then(function (data) {
				if (!runMethod) {
					if (!data.error) {
						sendMsg(successMsg);
					} else {
						failMsg = failMsg || "Failed : " + data.error.info + "<br/>";
						sendMsg(failMsg);
					}
				} else {
					runMethod(data, successMsg, failMsg);
				}
			}).fail(function (data) {
				sendMsg(data.statusText);
			});
		}
		return promise;
	}
	//Looks for backLinks and returns results
	function processBackLinks(page, runMethod, succ, fail) {
		var ns = page.split(":")[0] || "";
		var namespaceFilter = getNamespacesFilter();
		var config = {
			action: "query",
			list: "backlinks",
			bltitle: page,
			blnamespace: namespaceFilter,
			bot: true,
			bllimit: 500
		};
		if (fileNamespace[ns.toLowerCase()]) {
			config = {
				action: "query",
				list: "imageusage|backlinks",
				iutitle: page,
				bltitle: page,
				iunamespace: namespaceFilter,
				blnamespace: namespaceFilter,
				bot: true,
				iulimit: 500
			};
		}
		if (templateNamespace[ns.toLowerCase()]) {
			config = {
				action: "query",
				list: "embeddedin|backlinks",
				eititle: page,
				bltitle: page,
				einamespace: namespaceFilter,
				blnamespace: namespaceFilter,
				bot: true,
				iulimit: 500,
				eilimit: 500
			};
		}
		return processAction("post", config, succ, fail, runMethod);
	}
	//Shows backlinks to a page
	function showBackLinks() {
		var linkList = getLinksToClean();
		if (linkList.length > 0) {
			var counter = 0;
			reportStatus(msg("label_report_search_backlinks"));
			sendMsg("", true);
			setProgress("wait");
			setProgress("set", null, linkList.length);
			var i;
			var checkLinks = function (data) {
				if (data && data.query && data.query.backlinks) {
					var fullQueryResults = data.query.backlinks.concat(data.query.imageusage || data.query.embeddedin || []);
					var backLinkList = fullQueryResults.filter(function (item, pos) {
						return fullQueryResults.indexOf(item) === pos;
					});
					var listLength = backLinkList.length;
					var outputBacklinks = "";
					Object.keys(backLinkList).forEach(function (id) {
						if (backLinkList[id]) {
							outputBacklinks += backLinkList[id].title + "\n";
						}
					});
					if (listLength > 0) {
						sendMsg(outputBacklinks);
					}
					counter += 1;
					setProgress("set", counter);
					if (counter === linkList.length) {
						setProgress("done");
						reportStatus("");
					} else {
						reportStatus(msg("label_fetching_backlinks"));
					}
				} else {
					reportStatus(msg("label_error_fetch_links"));
				}
			};
			for (i = 0; i < linkList.length; i += 1) {
				processBackLinks(linkList[i], checkLinks, "", msg("label_error_fetch_links_server"));
			}
		} else {
			reportStatus(msg("label_report_nobacklinks"));
		}
	}
	//Searches for links in content
	function sweepLinks(content) {
		var linkParts;
		var targetlinkId;
		var targetLink;
		var fileLink;
		var newContent;
		var tmpTitle1;
		var titlePrefix;
		var hasColon;
		var prefixNs = "";
		var count = 0;
		var arrLinks = content.match(/\[\[(.*?)\]\]/g);
		var galLinks = content.match(new RegExp("\\<gallery.*?\\>[\\S\\s]*?<.*?\\/.*?gallery.*?\\>", "g"));
		var templateLinks = recursive_matches("{{", "}}", content);
		Object.keys(fileNamespace).forEach(function (name) {
			prefixNs += name + ":|";
		});
		var invalidTitleChars = ["#", "<", ">", "[", "]", "|", "{", "}"];
		if (templateLinks) {
			var templateName = "";
			var templateParts;
			var tmpID;
			var ns = "";
			for (tmpID = 0; tmpID < templateLinks.length; tmpID += 1) {
				for (targetlinkId = 0; targetlinkId < removableLinkList.length; targetlinkId += 1) {
					targetLink = removableLinkList[targetlinkId];
					if (targetLink) {
						templateParts = templateLinks[tmpID].split(":");
						templateName = templateParts[0];
						ns = templateParts[0].toLowerCase().substring(2);
						if (templateNamespace[ns]) {
							templateName = "{{" + templateParts[1];
						}
						templateName = templateName.substring(2, templateName.length - 2);
						templateName = templateName.split("|")[0];
						if (invalidTitleChars.indexOf(templateName.substring(0, 1)) > -1) {
							continue;
						}
						if (compareTitle("Template:" + templateName, targetLink)) {
							content = content.replace(templateLinks[tmpID], "");
							count += 1;
						}
					}
				}
			}
		}
		if (galLinks) {
			var galID;
			for (galID = 0; galID < galLinks.length; galID += 1) {
				newContent = galLinks[galID];
				for (targetlinkId = 0; targetlinkId < removableLinkList.length; targetlinkId += 1) {
					targetLink = removableLinkList[targetlinkId];
					fileLink = escapeRegExp(targetLink.split(":")[1] || targetLink);
					newContent = newContent.replace(new RegExp("(\\n)?(" + prefixNs + ")?" + fileLink + "(\\n)?", "g"), "");
					newContent = newContent.replace(new RegExp("(\\|link.*?\=.*?" + fileLink + ".*?)(\\n|\\||<)", "g"), "$2");
					count += 1;
				}
				content = content.replace(galLinks[galID], newContent);
				content = content.replace(new RegExp("\\<gallery.*\\>\\s*<\\/gallery\\>"), "");
			}
		}
		if (arrLinks && arrLinks.length) {
			var plainLink;
			var replaceText;
			var storedLinkedID;
			var linkLength;
			var isLinkRemovable = $("#chkBoxDeleteId").prop("checked");
			for (storedLinkedID = 0; storedLinkedID < arrLinks.length; storedLinkedID += 1) {
				linkParts = arrLinks[storedLinkedID].split("|");
				for (targetlinkId = 0; targetlinkId < removableLinkList.length; targetlinkId += 1) {
					targetLink = removableLinkList[targetlinkId];
					linkLength = linkParts[0].indexOf("]]");
					if (linkParts[0].indexOf("]]") < 0) {
						linkLength = linkParts[0].length;
					}
					plainLink = linkParts[0].substring(2, linkLength);
					//Account for the colon trick
					hasColon = linkParts[0].substring(2, 3) === ":";
					if (hasColon) {
						plainLink = linkParts[0].substring(3, linkLength);
					}
					if (compareTitle(plainLink, targetLink)) {
						tmpTitle1 = decodeURIComponent(plainLink);
						tmpTitle1 = new mw.Title(tmpTitle1);
						if ((tmpTitle1.getNamespaceId() === 14 && hasColon) || tmpTitle1.getNamespaceId() !== 14) {
							titlePrefix = ":";
							targetLink = targetLink.replace("_", " ");
							replaceText = targetLink;
							if (isLinkRemovable) {
								replaceText = "";
							}
							content = content.replace(arrLinks[storedLinkedID], replaceText);
							count += 1;
						}
					}
				}
			}
		}
		return [count, content];
	}
	//Performs the actual link removal using api;
	//@page string (pagename)
	function deleteInternalLinks(page, content) {
		if (content) {
			var results;
			var count;
			var replacedContent;
			var ns = page.split(":")[0] || "";
			var namespaces = mw.config.get("wgNamespaceIds");
			if (namespaces[ns.toLowerCase()] === 828) {
				replacedContent = "--<nowiki>\n" + content;
				count = 1;
			} else {
				results = sweepLinks(content);
				count = results[0];
				replacedContent = results[1];
			}
			if (count > 0) {
				var reason = $("#linkRemovalReasonId").val() || defaultRemovalMsg;
				var config = {
					minor: true,
					bot: true,
					format: "json",
					summary: reason,
					action: "edit",
					title: page,
					token: mw.user.tokens.get("editToken"),
					watchlist: "nochange",
					text: replacedContent
				};
				var saveNewContent = function (data) {
					if (data && data.edit && data.edit.result === "Success" && !data.edit.nochange) {
						sendMsg(page + msg("label_success_linksremoved"));
						sweepedLinks += 1;
						setProgress("add");
						reportStatus(sweepedPages + " " + msg("label_success"));
					} else {
						if (data.error){
							if (data.error.code === "unknownerror") {
								sendMsg("Unknown Error, links are probably in an unsupported page or namespace. Page:" + page);
							}else if (data.error.code === "protectedpage") {
								sendMsg(data.error.info + ". Page: " + page);
							}
						}
						else{
							sendMsg("Unknown error: " + JSON.stringify(data.error));
						}
					}
				};
				//Submits call to edit page and removes link
				if (!debug && bContinueProcess) {
					processAction("post", config, "", "", saveNewContent);
				}
				if (debug) {
					console.log(replacedContent);
					setProgress("add");
				}
			} else {
				sendMsg(msg("report_link_not_found") + " - " + page);
				setProgress("reducemax");
			}
		}
	}
	//Preps links for removal
	function removeBackLinks() {
		var linkList = getLinksToClean();
		bContinueProcess = true;
		if (linkList.length) {
			var processedPages = {};
			var confirmAction = true;
			removableLinkList = linkList;
			var startCleaning;
			setProgress("set", 0);
			if ($("#removeButton").attr("disabled") === "disabled") {
				return;
			}
			if (linkSweepConfirmation) {
				confirmAction = window.confirm(msg("label_submit_warning"));
				if (!confirmAction) {
					return;
				}
			}
			startCleaning = function (tmpResultList) {
				if (tmpResultList.length) {
					var id = 0;
					var pageSlices = [];
					var queuePos;
					var timeoutObj;
					var delayedRemove;
					var queryResultList;
					var pageIds;
					var listLength;
					var pageListing = [];
					for (id = 0; id < tmpResultList.length; id += 1) {
						pageListing.push(tmpResultList[id].title);
					}
					sendMsg(msg("report_found_backlinks"));
					setProgress("set", 0, pageListing.length);
					var currBatch = 0;
					if (currBatch < pageListing.length) {
						pageSlices = pageListing.slice(currBatch, currBatch + 10);
						currBatch += 10;
						if (pageSlices.length > 0) {
							$.get(mw.util.wikiScript("api"), {
								action: "query",
								prop: "revisions",
								titles: pageSlices.join("|"),
								rvprop: "content",
								indexpageids: "true",
								format: "json"
							}).then(function (data) {
								if (data.query && data.query.pages) {
									queryResultList = data.query.pages;
									pageIds = data.query.pageids;
									listLength = pageIds.length;
									if (listLength > 0) {
										if (confirmAction) {
											$("#removeButton").attr("disabled", true);
											queuePos = 0;
											delayedRemove = function () {
												if (queuePos < listLength) {
													timeoutObj = setTimeout(delayedRemove, delay);
												} else if (queuePos >= listLength) {
													clearTimeout(timeoutObj);
													timeoutObj = undefined;
													if (tmpResultList.slice(currBatch)) {
														startCleaning(tmpResultList.slice(currBatch));
													}
												}
												var pageContents = queryResultList[pageIds[queuePos]];
												if (pageContents && bContinueProcess && !processedPages[pageContents.title]) {
													var revContent = pageContents.revisions[0];
													processedPages[pageContents.title] = 1;
													if (revContent && revContent["*"]) {
														deleteInternalLinks(pageContents.title, revContent["*"]);
													}
												} else {
													setProgress("reducemax");
													setProgress("set", sweepedPages);
												}
												queuePos += 1;
											};
											delayedRemove();
											$("#removeButton").attr("disabled", false);
										} else {
											reportStatus(msg("report_action_cancel"));
										}
									}
								} else {
									reportStatus(msg("report_no_links_found"));
									setProgress("reducemax");
									setProgress("done", sweepedPages);
								}
							}).fail(function () {
								reportStatus("report_fail_request");
								setProgress("set", 0);
							});
						} else {
							setProgress("done");
						}
					}
				}
			};
			sendMsg("", true);
			var targetLink = 0;
			var storedBackLinks = [];
			var processLinkList;
			processLinkList = function (queuePos) {
				if (linkList[targetLink] && linkList[targetLink].trim() !== "") {
					processBackLinks(linkList[queuePos], function (data) {
						if (data.query) {
							var fullQueryResults = data.query.backlinks.concat(data.query.imageusage || data.query.embeddedin || []);
							var tmpResultList = fullQueryResults.filter(function (item, pos) {
								return fullQueryResults.indexOf(item).title === pos.title;
							});
							storedBackLinks = storedBackLinks.concat(tmpResultList);
							if (linkList[queuePos + 1]) {
								setProgress("wait");
								processLinkList(queuePos + 1);
							} else {
								startCleaning(storedBackLinks);
							}
						}
					});
				}
			};
			processLinkList(0);
		} else {
			reportStatus(msg("label_report_nobacklinks"));
		}
	}
	//Shows modal for LinkSweeper
	var showLinkSweeperModal = function (event) {
		var currPages = getPageLink($(this));
		var options = {};
		if (event.data.type === "batch") {
			var specialPage = mw.config.get("wgCanonicalSpecialPageName");
			options = {
				"progress": undefined,
				reportStatus: ""
			};
			if (allowedSpecialPage[specialPage.toLowerCase()]) {
				$("#mw-content-text li > a:nth-child(1)").each(function () {
					currPages += $(this).text() + "\n";
				});
				if ($("#pageNameId")) {
					$("#pageNameId").text(currPages);
					setProgress("set", 0);
				}
			}
		}
		var sweeperForm = createForm(currPages, options);
		sweepedPages = 0;
		dev.showCustomModal("Link Sweeper", sweeperForm, {
			id: "backLinkForm",
			width: 325,
			buttons: [{
				message: msg("btn_show_back_links"),
				id: "showBacklinks",
				handler: function () {
					showBackLinks(currPages);
				}
			}, {
				message: msg("btn_add_category_content"),
				id: "addCatMembersButton",
				handler: function () {
					var category = $("#cmbCategories").val();
					if (category === "Custom") {
						category = prompt(msg("label_category_fetch, category"));
					}
					if (category.length > 3) {
						var config = {
							action: "query",
							list: "categorymembers",
							cmtitle: "category:" + category,
							format: "json",
							cmlimit: "500"
						};
						var addPages = function (data) {
							if (data.query && data.query.categorymembers) {
								var queryResultList = data.query.categorymembers;
								var pageNames = [];
								queryResultList.forEach(function (categoryMember) {
									pageNames.push(categoryMember.title);
								});
								$("#pageNameId").text(pageNames.join("\n"));
							}
						};
						processAction("post", config, "", "", addPages);
					}
				}
			}, {
				message: msg("btn_fetch_redlinks"),
				id: "fetchRedlinksButton",
				defaultButton: false,
				handler: function () {
					var redLinks = [];
					var currLink = "";
					var contentSelector = ".mw-content-text";
					if (mw.config.get("wgCanonicalSpecialPageName")) {
						contentSelector = ".mw-spcontent";
					}
					$(contentSelector + " a.new").each(function (index, $htmlElement) {
						currLink = $($htmlElement).attr("href");
						currLink = currLink.replace("/wiki/", "").split("?")[0];
						if (currLink.toLowerCase() === "special:upload") {
							currLink = $($htmlElement).attr("title");
						}
						currLink = decodeURI(currLink);
						if (redLinks.indexOf(currLink) === -1) {
							redLinks.push(currLink);
						}
					});
					$("#pageNameId").text(redLinks.join("\n"));
					reportStatus("");
					sendMsg("", true);
					setProgress();
				}
			}, {
				message: msg("btn_cancel"),
				id: "cancelButton",
				handler: function () {
					bContinueProcess = false;
					dev.showCustomModal.closeModal($("#backLinkForm"));
				}
			}, {
				message: msg("btn_remove_backlinks"),
				id: "removeButton",
				defaultButton: true,
				handler: function () {
					removeBackLinks();
				}
			}]
		});
	};
	//Sanitize config
	function sanitizeCfg() {
		if (defaultSettings && defaultSettings.settings) {
			var arrSettings = defaultSettings.settings;
			if (arrSettings.specialpages) {
				Object.keys(arrSettings.specialpages).forEach(function (name) {
					if (typeof (arrSettings.specialpages[name]) !== "number") {
						delete defaultSettings.settings.specialpages[name];
					}
				});
			}
			if (arrSettings.categoryList.constructor !== Array) {
				defaultSettings.settings.categoryList = ["Custom"];
			}
			if (typeof (arrSettings.linkSweeperDelay) !== "number" || arrSettings.linkSweeperDelay < 500) {
				defaultSettings.settings.linkSweeperDelay = 1000;
			}
			defaultSettings.settings.linkSweepConfirmation = defaultSettings.settings.linkSweepConfirmation || true;
		}
	}
	//Initializes script
	function main() {
		sanitizeCfg();
		var specialPage = mw.config.get("wgCanonicalSpecialPageName");
		var Html = mw.html;
		var mwAction = mw.config.get("wgAction");
		allowedSpecialPage = defaultSettings.settings.specialpages;
		linkSweepConfirmation = defaultSettings.settings.linkSweepConfirmation || 1;
		delay = defaultSettings.settings.linkSweeperDelay || 1000;
		categoryList = defaultSettings.settings.categoryList;
		defaultRemovalMsg = msg("default_removalsummary");
		setNamespaces();
		var removeLinks = Html.element("input", {
			"type": "button",
			"value": "Remove backlinks",
			"class": "classRemoveLinks"
		});
		var removeBatchLinks = Html.element("input", {
			"type": "button",
			"value": "Remove all links below",
			"class": "classRemoveBatchLinks"
		});
		if (mw.config.get("skin") === "oasis" && $("#my-tools-menu").length) {
			$("#my-tools-menu").prepend("<li class=\"custom\"><a style=\"cursor:pointer\" id=\"LinkSweeperToolbar\">Link Sweeper</a></li>");
			$("#LinkSweeperToolbar").on("click", {
				type: "toolbarclick"
			}, showLinkSweeperModal);
		}
		if (specialPage && allowedSpecialPage[specialPage.toLowerCase()]) {
			$(".emptymwmsg.mediawiki_showingresults").append(removeBatchLinks);
			if (specialPage.toLowerCase() === "whatlinkshere") {
				$("#mw-whatlinkshere-target").parent().append(" " + removeLinks);
			} else {
				$("#mw-content-text li").each(function () {
					$(this).append(removeLinks);
				});
			}
		} else if (mwAction === "delete") {
			$(".mw-submit").append(" " + removeLinks);
		} else {
			return;
		}
		$(".classRemoveLinks").on("click", {
			type: "normal"
		}, showLinkSweeperModal);
		$(".classRemoveBatchLinks").on("click", {
			type: "batch"
		}, showLinkSweeperModal);
	}
	mw.hook('dev.showCustomModal').add(function() {
		main();
	});
	importArticle({
	    type: 'script',
	    article: 'u:dev:MediaWiki:ShowCustomModal.js'
	});
}(window, mw, $));