User:Anderjef/common.js

Source: 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>
/* jshint leanswitch: true, nonbsp: true, nonew: true, singleGroups: true, strict: true, undef: true, unused: true */
/* globals $, console, document, Event, mw, pathoschild, performance, window */
$(function() {
	"use strict";
	try {
		var forbiddenPages = [];
		var dayOfWeek = (new Date()).getDay();
		if (1 <= dayOfWeek) {
			forbiddenPages = ['Main_Page', 'Special:Search', 'Special:Homepage', 'Special:Watchlist', 'User:Uhai/Pages_without_short_descriptions_by_view_count'];
		}
		else {
			var hour = (new Date()).getHours();
			if (hour < 7 || 19 <= hour) {
				forbiddenPages = ['User:Uhai/Pages_without_short_descriptions_by_view_count'];
			}
		}
		for (var i = 0; i < forbiddenPages.length; i++) {
			if (window.location.pathname.includes(forbiddenPages[i]) || window.location.search.includes(forbiddenPages[i])) {
				window.location.assign("https://www.wikipedia.org");
				break;
			}
		}
	}
	catch (e) { //continue even if an error occurred
		 console.error(e.message);
	}
	function reportCompletion(description, duration, notificationType, storageName, storageAdditionalSum, textSize) { //duration is assumed to be in milliseconds; notificationType being 'info', 'warn', 'error', or 'success'; notification becomes description + " in " + duration + "ms"
		var notificationString = description + " in " + duration + "ms";
		if (notificationType !== 'info') {
			mw.notify(notificationString, { type: notificationType });
		}
		switch (notificationType) {
			case 'error':
				console.error(notificationString);
				break;
			case 'info':
			case 'success':
				console.info(notificationString);
				break;
			case 'warn':
				console.warn(notificationString);
				break;
		}
		if (storageName !== undefined) {
			var attributeName = notificationType === 'info' ? 'count' : 'tries';
			var existingItem = window.localStorage.getItem(storageName + ' ' + notificationType);
			if (existingItem !== null) {
				var existingStorageAdditionalSum = parseInt(existingItem.substring(existingItem.indexOf(attributeName + ': ') + (attributeName + ': ').length));
				var existingTextSize = existingItem.indexOf('bytes: ') < 0 ? 'NaN' : parseInt(existingItem.substring(existingItem.indexOf('bytes: ') + 'bytes: '.length));
				window.localStorage.setItem(storageName + ' ' + notificationType, 'milliseconds: ' + (parseInt(existingItem.substring(existingItem.indexOf('milliseconds: ') + 'milliseconds: '.length)) + duration) + ', ' + attributeName + ': ' + (isNaN(existingStorageAdditionalSum) ? (!isNaN(storageAdditionalSum) ? storageAdditionalSum : 'NaN') : existingStorageAdditionalSum + storageAdditionalSum) + ', tally: ' + (parseInt(existingItem.substring(existingItem.indexOf('tally: ') + 'tally: '.length)) + 1) + (isNaN(existingTextSize) ? (!isNaN(textSize) ? ', bytes: ' + textSize : '') : ', bytes: ' + (existingTextSize + textSize)));
			}
			else {
				window.localStorage.setItem(storageName + ' ' + notificationType, 'milliseconds: ' + duration + ', ' + attributeName + ': ' + storageAdditionalSum + ', tally: 1' + (!isNaN(textSize) ? ', bytes: ' + textSize : ''));
			}
		}
	}
	Number.prototype.round = function(numPlaces) {
		return +(Math.round(this + "e+" + numPlaces) + "e-" + numPlaces); //+ casts string back to number to strip excess zeroes
	};
	function filterCurrentEvents(numTries, startTime) {
		if ($('.p-current-events-headlines ul').length && $('.itn-img').length) {
			var currentEventsFilter = ["basketball", "cricket", "Cricket World Cup", "Eurovision Song Contest", "filmmaker", "Film Awards", "Film Festival", "football", "golf", "hockey", "In television, ", "In tennis, ", "Japan Series", "NASCAR Cup Series", "PDC World Championship", "rugby", "World Baseball Classic"];
			for (var i = 0; i < currentEventsFilter.length; i++) {
				for (var j = 0; j < $('.p-current-events-headlines ul li:contains(' + currentEventsFilter[i] + ')').length; j++) {
					if ($('.p-current-events-headlines ul li:contains(' + currentEventsFilter[i] + ')')[j].innerText.match(/\([^\(\)]*[pP]ictured[^\(\)]*\)/)) {
						$('.itn-img')[0].remove();
					}
				}
				$('.p-current-events-headlines ul li:contains(' + currentEventsFilter[i] + ')').hide();
			}
			reportCompletion(numTries + " tries before current events were filtered", (performance.now() - startTime).round(3), 'success', 'current-events-filtering', numTries, 'NaN');
		}
		else if (++numTries < 10000) { window.setTimeout(filterCurrentEvents, 5, numTries, startTime); } //try for 10000*5 milliseconds (that is 50 seconds)
		else { reportCompletion("Failed to filter current events", (performance.now() - startTime).round(3), 'error', 'current-events-filtering', numTries, 'NaN'); }
	}
	function scrollToEditWindow(numTries, startTime) {
		if ($('#wikiEditor-ui-toolbar').length) {
			$('#wikiEditor-ui-toolbar')[0].scrollIntoView();
			reportCompletion(numTries + " tries before editor window was scrolled to", (performance.now() - startTime).round(3), 'success', 'editor-window-focusing', numTries, 'NaN');
		}
		else if (++numTries < 5000) { window.setTimeout(scrollToEditWindow, 5, numTries, startTime); } //try for 5000*5 milliseconds (that is 25 seconds)
		else { reportCompletion("Failed to scroll to editor window", (performance.now() - startTime).round(3), 'error', 'editor-window-focusing', numTries, 'NaN'); }
	}
	var watchlistExpiryIsSet = false; //basically a mutex for the watchlist expiry
	function setWatchlistExpiry(numTries, startTime, expiryValue) {
		if (expiryValue === undefined ? $('#wpWatchthis').length : $('#wpWatchlistExpiry').length) {
			if (expiryValue === undefined) {
				if ($('#wpWatchthis')[0].checked) {
					$('#wpWatchthis').first().click(); //unset watching
				}
			}
			else {
				$('#wpWatchlistExpiry').val(expiryValue).trigger('change');
			}
			reportCompletion(numTries + " tries before watchlist expiry was set", (performance.now() - startTime).round(3), 'success', 'watchlist-expiry-setting', numTries, 'NaN');
			watchlistExpiryIsSet = true;
		}
		else if (++numTries < 1000) { window.setTimeout(setWatchlistExpiry, 5, numTries, startTime, expiryValue); } //try for 1000*5 milliseconds (that is 5 seconds)
		else { reportCompletion("Failed to set watchlist expiry", (performance.now() - startTime).round(3), 'error', 'watchlist-expiry-setting', numTries, 'NaN'); }
	}
	function filterWatchlistExpiryOptions(numTries, startTime) {
		var watchlistExpiryElements = document.getElementsByClassName('oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement oo-ui-optionWidget oo-ui-decoratedOptionWidget oo-ui-menuOptionWidget');
		if ($('#wpWatchlistExpiry').length && watchlistExpiryElements.length) {
			var filteredWatchlistExpiryOptions = ["6 months", "1 year"];
			for (var i = 0; i < $('#wpWatchlistExpiry')[0].children.length; i++) {
				for (var j = 0; j < filteredWatchlistExpiryOptions.length; j++) {
					if ($('#wpWatchlistExpiry')[0].children[i].innerText === filteredWatchlistExpiryOptions[j]) {
						$('#wpWatchlistExpiry')[0].children[i].remove();
					}
				}
			}
			for (var k = 0; k < watchlistExpiryElements.length; k++) {
				for (var l = 0; l < filteredWatchlistExpiryOptions.length; l++) {
					if (watchlistExpiryElements[k].innerText === filteredWatchlistExpiryOptions[l]) {
						watchlistExpiryElements[k].remove();
					}
				}
			}
			reportCompletion(numTries + " tries before watchlist expiry options were filtered", (performance.now() - startTime).round(3), 'success', 'watchlist-expiry-filtering', numTries, 'NaN');
		}
		else if (++numTries < 5000) { window.setTimeout(filterWatchlistExpiryOptions, 5, numTries, startTime); } //try for 5000*5 milliseconds (that is 25 seconds)
		else { reportCompletion("Failed to filter watchlist expiry options", (performance.now() - startTime).round(3), 'error', 'watchlist-expiry-filtering', numTries, 'NaN'); }
	}
	function increaseEditorSpecialCharactersBarHeight(numTries, startTime) {
		if ($('#editpage-specialchars').length) {
            $('#editpage-specialchars').css('padding-top', '6.25vh');
			window.dispatchEvent(new Event('resize'));
			reportCompletion(numTries + " tries before wikitext editor's special characters bar was heightened", (performance.now() - startTime).round(3), 'success', 'special-characters-bar-heightening', numTries, 'NaN');
		}
		else if (++numTries < 5000) { window.setTimeout(increaseEditorSpecialCharactersBarHeight, 10, numTries, startTime); } //try for 5000*10 milliseconds (that is 50 seconds)
		else { reportCompletion("Failed to increase height of wikitext editor's special characters bar", (performance.now() - startTime).round(3), 'error', 'special-characters-bar-heightening', numTries, 'NaN'); }
	}
	function increaseWikitextEditorHeight(numTries, startTime) {
		if ($('.wikiEditor-ui-view-wikitext').length) {
			$('.wikiEditor-ui-view-wikitext').height('75vh');
			window.dispatchEvent(new Event('resize'));
			reportCompletion(numTries + " tries before wikitext editor was heightened", (performance.now() - startTime).round(3), 'success', 'wikitext-editor-heightening', numTries, 'NaN');
			increaseEditorSpecialCharactersBarHeight(0, performance.now());
		}
		else if (++numTries < 5000) { window.setTimeout(increaseWikitextEditorHeight, 10, numTries, startTime); } //try for 5000*10 milliseconds (that is 50 seconds)
		else { reportCompletion("Failed to increase height of wikitext editor", (performance.now() - startTime).round(3), 'error', 'wikitext-editor-heightening', numTries, 'NaN'); }
	}
	var allowLaterModificationOfWatchlistExpiry;
	try {
		if (['edit', 'submit'].includes(mw.config.get('wgAction'))) {
			allowLaterModificationOfWatchlistExpiry = !['User', 'User_talk'].includes(mw.config.get('wgCanonicalNamespace'));
			scrollToEditWindow(0, performance.now());
			if (!allowLaterModificationOfWatchlistExpiry) {
				if (mw.config.get('wgUserName') === mw.config.get('wgTitle').substring(0, mw.config.get('wgUserName').length)) { setWatchlistExpiry(0, performance.now(), 'infinite'); }
				else { setWatchlistExpiry(0, performance.now(), "1 week"); }
			}
			else { setWatchlistExpiry(0, performance.now(), "3 months"); }
			$('.editButtons').insertBefore('#editpage-copywarn');
			filterWatchlistExpiryOptions(0, performance.now());
			if (mw.config.get('wgPageContentModel') === 'wikitext') { //if editing wikitext
				increaseWikitextEditorHeight(0, performance.now());
			}
		}
		else {
			allowLaterModificationOfWatchlistExpiry = false;
			if (mw.config.get('wgCanonicalSpecialPageName') === 'Homepage') { //hide suggested edits
				$('.growthexperiments-homepage-module-header')[0].remove();
				$('.growthexperiments-homepage-module-body')[1].style.display = 'none';
				$('.growthexperiments-homepage-module-mobile-summary activated')[0].remove();
			}
			else if (mw.config.get('wgCanonicalSpecialPageName') === 'Watchlist') { //add current events headlines
				var element = document.createElement('div');
				element.setAttribute('class', 'my-class');
				document.getElementById('mw-content-text').insertBefore(element, document.getElementsByClassName('mw-rcfilters-head')[0]);
				$('.my-class').load('https://en.wikipedia.org/wiki/Portal:Current_events .p-current-events-headlines');
				filterCurrentEvents(0, performance.now());
			}
			else if (mw.config.get('wgPageName') === 'User:' + mw.config.get('wgUserName')) { //add (primitive-looking) impact view counts
				var element = document.createElement('div');
				element.setAttribute('class', 'my-class');
				document.getElementById('bodyContent').insertBefore(element, document.getElementById('catlinks')[0]);
				$('.my-class').load('https://en.wikipedia.org/wiki/Special:Homepage .growthexperiments-homepage-group-sidebar-subgroup-primary');
			}
		}
	}
	catch (e) { //continue even if an error occurred
		 console.error(e.message);
	}

	/**
	 * TemplateScript adds configurable templates and scripts to the sidebar, and adds an example regex editor.
	 * @see https://meta.wikimedia.org/wiki/TemplateScript
	 * @update-token [[File:Pathoschild/templatescript.js]]
	 */
	mw.config.set('userjs-templatescript', { regexEditor: false }); //disable TemplateScript's regex editor
	$.ajax('//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js', { dataType:'script', cache:true }).then(function() {
		var watchlistExpiryMode = 0; //0 for don't watch, 1 for regular, 2 for extended
		function waitForWatchlistIndicatorForAutomaticCleanup(numTries, startTime) {
			if ($('#ca-watch').length || $('#ca-unwatch').length) {
				reportCompletion(numTries + " tries before watchlist indicator was detected", (performance.now() - startTime).round(3), 'success', 'watchlist-indicator-detection', numTries, 'NaN');
				if ($('#ca-watch').length) { //presence of the #ca-watch element indicates the ability to watch the page because it is not already on the watchlist
					var expiryValue;
					switch (watchlistExpiryMode) {
						case 0:
							//leave expiryValue undefined
							break;
						case 1:
							expiryValue = '1 week';
							break;
						case 2:
							expiryValue = '1 month';
							break;
					}
					setWatchlistExpiry(0, performance.now(), expiryValue);
				}
			}
			else if (++numTries < 1000) { window.setTimeout(waitForWatchlistIndicatorForAutomaticCleanup, 5, numTries, startTime); } //try for 1000*5 milliseconds (that is 5 seconds)
			else { reportCompletion("Failed to detect watchlist indicator", (performance.now() - startTime).round(3), 'error', 'watchlist-indicator-detection', numTries, 'NaN'); }
		}
		function setWatchlistExpiryForCleanupHelper(numTries, startTime) {
			if (watchlistExpiryIsSet) {
				watchlistExpiryIsSet = false;
				reportCompletion(numTries + " tries before setting watchlist expiry on cleanup", (performance.now() - startTime).round(3), 'success', 'cleanup-watchlist-expiry-setting', numTries, 'NaN');
				waitForWatchlistIndicatorForAutomaticCleanup(0, performance.now());
			}
			else if (++numTries < 1000) { window.setTimeout(setWatchlistExpiryForCleanupHelper, 5, numTries, startTime); } //try for 1000*5 milliseconds (that is 5 seconds)
			else { reportCompletion("Failed to set watchlist expiry on cleanup", (performance.now() - startTime).round(3), 'error', 'cleanup-watchlist-expiry-setting', numTries, 'NaN'); }
		}
		function setWatchlistExpiryForCleanup(numTries, startTime) {
			if (allowLaterModificationOfWatchlistExpiry !== undefined) {
				reportCompletion(numTries + " tries before permission to set watchlist expiry on cleanup was detected", (performance.now() - startTime).round(3), 'success', 'cleanup-watchlist-expiry-permission-detection', numTries, 'NaN');
				if (allowLaterModificationOfWatchlistExpiry) {
					setWatchlistExpiryForCleanupHelper(0, performance.now());
				}
			}
			else if (++numTries < 1000) { window.setTimeout(setWatchlistExpiryForCleanup, 5, numTries, startTime); } //try for 1000*5 milliseconds (that is 5 seconds)
			else { reportCompletion("Failed to detect permission to set watchlist expiry on cleanup", (performance.now() - startTime).round(3), 'error', 'cleanup-watchlist-expiry-permission-detection', numTries, 'NaN'); }
		}
		var summaryAppendix; //globalized due to use in conductRegexReplacement() which is used in both cleanup and HTML cleanup
		var count; //globalized as callback functions require variable to exist where callback functions are scoped rather than inside calling function
		function conductRegexReplacement(arrayOfMatchPatternsAndReplacementFunctions, editor, description, storageName, newWatchlistExpiryMode) { //arrayOfMatchPatternsAndReplacementFunctions is an array of arrays where each subarray has exactly two elements, the first being the regex pattern to match and the second being the regex replacement callback function; summaryAppendix relies on description being an explanatory wiki link; notification becomes count + description + " in " + duration + "ms"
			var startTime = performance.now();
			var textLength = editor.get().length;
			count = 0;
			for (var i = 0; i < arrayOfMatchPatternsAndReplacementFunctions.length; i++) {
				editor.replace(arrayOfMatchPatternsAndReplacementFunctions[i][0], arrayOfMatchPatternsAndReplacementFunctions[i][1]);
			}
			if (count) {
				summaryAppendix += "[[" + description + "]] (" + count + "), ";
				watchlistExpiryMode = Math.max(watchlistExpiryMode, newWatchlistExpiryMode);
			}
			reportCompletion(count + " " + description, (performance.now() - startTime).round(3), 'info', storageName, count, textLength);
		}
		function pluralize(num) {
			return num === 1 ? "" : "s";
		}
		pathoschild.TemplateScript.add(
			[
				{
					name: 'Cleanup',
					script: function(editor) {
						summaryAppendix = "";
						conductRegexReplacement([[/((?:.|\r|\n)+)(\{\{\s*short\s+description\s*\|[^\}]+\}\})(?:\r\n)?/i, function(match, p1, p2) { count++; return p2 + "\r\n" + p1; }]], editor, "MOS:ORDER", 'reorder-short-description', 1);
						conductRegexReplacement([[/(?<=\n)(=+)([^=\r\n\[\]\|\{\}]*)\[\[([^=\r\n\[\]\|\{\}]*)\]\]([^=\r\n\[\]\|\{\}]*)\1(?=\n)/g, function(match, p1, p2, p3, p4) { count++; return p1 + p2 + p3 + p4 + p1; }]], editor, "MOS:HEAD", 'remove-heading-wiki-links', 1); //should happen before any escaping of wiki links as well as geographical wiki links combination and wiki-link shortening


						var templateEscapeString = '';
						for (var i = 0; i < expansionDepthLimit * 2 - 1; i++) { //the expansion-depth limit is multiplied by two for each level requiring two nested curly braces and minus one for the outermost two pairs being supplied later; note the assumption that no single-paired nested braces will be used to surpass the limit of eighty nested pairs of curly braces
							templateEscapeString += '(?:[^\\{\\}]|\\{';
						}
						templateEscapeString += '[^\\{\\}]*';
						for (var i = 0; i < expansionDepthLimit * 2 - 1; i++) { //the expansion-depth limit is multiplied by two for each level requiring two nested curly braces and minus one for the outermost two pairs being supplied later; note the assumption that no single-paired nested braces will be used to surpass the limit of eighty nested pairs of curly braces
							templateEscapeString += '\\})*';
						}
						var wikiLinkEscapeRegex = RegExp('\[\[[^\|\]]*(?:\|(?=[^\|\]]*\]\])|\]\])', 'g');
						var HTMLEscapeRegex = RegExp(/<(?!(?:br|references)\s*\/?>)([a-zA-Z]+)[^<>]*>(?:(?!<\/\1\s*>)(?:.|\r|\n))*<\/\1\s*>/gi);
						var escaped1 = editor.escape(wikiLinkEscapeRegex);
						var escaped2 = editor.escape(HTMLEscapeRegex);
						var escaped3 = editor.escape(RegExp('\\{\\{' + templateEscapeString + '\\}\\}', 'g')); //note that this assumes that no single-paired nested braces will be used to surpass the expansion-depth limit

						conductRegexReplacement([
							[RegExp('(?<=\\d)\\s+(?=' + compoundNumbersRegex + '|(?:[km]g|[aApP][mM])(?![a-zA-Z]))', 'g'), function() { count++; return "&nbsp;"; }],
							[/(?<=[ \r\n])\u00A0|\u00A0(?=[ \r\n])/g, function() { count++; return ""; }],
							[/\u00A0/g, function() { count++; return "&nbsp;"; }]
						], editor, "MOS:NBSP", 'insert-nbsps', 1); //should happen before any regex relying on "\s" is used

						editor
							.unescape(escaped1)
							.unescape(escaped2)
							.unescape(escaped3);


						var headingEscapeRegex = /(?<=\n)(=+)[^=\r\n]*\1(?=\n)/g;
						escaped1 = editor.escape(wikiLinkEscapeRegex);
						escaped2 = editor.escape(HTMLEscapeRegex);
						escaped3 = editor.escape(RegExp('\\{\\{' + templateEscapeString + '\\}\\}', 'g')); //note that this assumes that no single-paired nested braces will be used to surpass the expansion-depth limit
						var escaped4 = editor.escape(headingEscapeRegex);

						conductRegexReplacement([[/ (?:\{\{\s*[nN]dash\s*\}\}|&ndash;)(?: |\{\{\s*[nN]dash\s*\}\}|&ndash;)/g, function() { count++; return "{{snd}}"; }]], editor, "MOS:ENDASH", 'fix-ndashes', 1); //should happen after literal non-breaking space removal and replacement

						editor
							.unescape(escaped1)
							.unescape(escaped2)
							.unescape(escaped3)
							.unescape(escaped4);


						var fileRegex = /(?:File|Image):[^\|\]\r\n]+\|/g;
						var imageParameterRegex = /\|\s*image\s*=(?:(?!\||\}\})[^\|])*/g;
						// var escaped5 = editor.escape(fileRegex);
						// var escaped6 = editor.escape(imageParameterRegex);
						// var escaped7 = editor.escape(/„[^“]*“/);

						// conductRegexReplacement([
						// 	[/[“”]/g, function(match) { count++; return '"'; }],
						// 	[/(?<![‘\'])‘(?![‘\'])/g, function(match) { count++; return "'"; }], //future consideration: add nowiki tags if preceding or proceeding text is a non-curly single quotation mark
						// 	[/(?<![’\'])’(?![’\'])/g, function(match) { count++; return "'"; }]  //future consideration: add nowiki tags if preceding or proceeding text is a non-curly single quotation mark
						// ], editor, "MOS:CURLY", 'replace-curlies', 0);

						// editor
						// 	.unescape(escaped5)
						// 	.unescape(escaped6)
						// 	.unescape(escaped7);


						var nowikiEscapeRegex = RegExp('(?:<nowiki\s*>(?:(?!<\/' + 'nowiki\s*>)(?:.|\r|\n))*<\/' + 'nowiki\s*>|\{\{\s*#tag:\s*nowiki\s*\}\})', 'gi'); //string is broken at closing nowiki tags to avoid throwing off Wikipedia wherever this document is loaded
						var escaped8 = editor.escape(nowikiEscapeRegex);
						var escaped9 = editor.escape(/(?:<!--(?:(?!-->)(?:.|\r|\n))*-->|\{\{\s*#tag:\s*!--\s*\}\})/g);

						var startTime = performance.now();
						var textLength = editor.get().length;
						count = 0;
						var emptyCitationBlacklistRegex = 'Gaia +DR2';
						editor
							.replace(RegExp('<[rR][eE][fF][^>\\/]*>\\s*(?:\\{\\{\\s*[cC]ite +(?!' + emptyCitationBlacklistRegex + ')\\w+\\s*(?:\\|(?:\\s*[\\w-]+\\s*=)?\\s*)*\\}\\}\\s*)?<\\/[rR][eE][fF]\\s*>', 'g'), function() { count++; return ""; })
							.replace(RegExp('\\{\\{\\s*[cC]ite +(?!' + emptyCitationBlacklistRegex + ')\\w+\\s*(?:\\|(?:\\s*[\\w-]+\\s*=)?\\s*)*\\}\\}\\s*', 'g'), function() { count++; return ""; });
						if (count) {
							summaryAppendix += "rm " + (count !== 1 ? count + " " : "") + "[[Category:CS1 errors: empty citation|empty CS1 citation]]" + pluralize(count) + ", ";
							watchlistExpiryMode = 2;
						}
						reportCompletion(count + " empty CS1 citation" + pluralize(count), (performance.now() - startTime).round(3), 'info', 'remove-empty-citations', count, textLength);
						var openingRefHTMLRegex = '<[rR][eE][fF][^>]*>';
						var closingRefHTMLRegex = '<\\/[rR][eE][fF]\\s*>';
						var openingRefParserRegex = '\\{\\{\\s*#tag:\\s*ref\\s*\\|';
						var closingRefParserRegex = '\\}\\}';
						var problematicIntermediaryRegex = '<nowiki\\s*>|<\\/' + 'nowiki\\s*>|<nowiki\\s*\\/>|\\{\\{\\s*#tag:\\s*(?:nowiki|!--)\\s*\\}\\}'; //string is broken at closing nowiki tag to avoid throwing off Wikipedia wherever this document is loaded
						conductRegexReplacement([
							[RegExp('\\s+(' + openingRefHTMLRegex + '(?:(?!\\|' + problematicIntermediaryRegex + '|' + closingRefHTMLRegex + ')(?:.|\r|\n))*' + closingRefHTMLRegex + ')', 'g'), function(match, p1) { count++; return p1; }],
							[RegExp('\\s+(' + openingRefParserRegex + '(?:(?!\\|' + problematicIntermediaryRegex + '|' + closingRefParserRegex + ')(?:.|\r|\n))*' + closingRefParserRegex + ')', 'g'), function(match, p1) { count++; return p1; }]
						], editor, "MOS:REFSPACE", 'add-spaces-before-refs', 1);

						// // The following was removed for being broken (the conducted replacement of duplicate references is nonsensical) for unknown reasons.
						// startTime = performance.now();
						// textLength = editor.get().length;
						// count = 0;
						// var referencesArray = Array.from(editor.get().matchAll(RegExp('<ref(\s*(?:[^<>]+(?<!\/))?)>(?:(?!<\/ref\s*>)(?:.|\r|\n))*<\/ref\s*>', 'gi')));
						// for (var i = 0; referencesArray.length; i++) {
						// 	var firstReplacement = true;
						// 	editor.replace(RegExp(referencesArray[0][0].replaceAll(/([\^\$\*\(\)\+\{\}\[\]\\\.\?\/\-])/g, function(match, p1) { return '\\' + p1; }), 'g'), function(match, p1) {
						// 		if (firstReplacement) {
						// 			firstReplacement = false;
						// 			return match;
						// 		}
						// 		count++;
						// 		return "<ref" + (p1 === undefined ? " name=\":" + (i + 10) + "\"" : p1) + " />";
						// 	});
						// 	var captureGroup1 = referencesArray[0][1];
						// 	var referenceMatch = (captureGroup1 ? referencesArray[0][0].replace(captureGroup1, '') : referencesArray[0][0]);
						// 	referencesArray = referencesArray.filter(function(value) { return (captureGroup1 && value[0].indexOf(captureGroup1) === 4 ? value[0].replace(captureGroup1, '') : value[0]) !== referenceMatch; });
						// }
						// reportCompletion(count + " duplicate reference" + pluralize(count), (performance.now() - startTime).round(3), 'info', 'merge-duplicate-references', count, textLength);

						editor
							.unescape(escaped8)
							.unescape(escaped9);


						escaped8 = editor.escape(nowikiEscapeRegex);

						var openingCitationTemplateRegex = '\\{\\{\\s*[cC]ite +\\w+\\s*';
						var closingCitationTemplateRegex = '\\}\\}';
						var openingCitationHTMLRegex = openingRefHTMLRegex + '\\s*' + openingCitationTemplateRegex;
						var closingCitationHTMLRegex = closingCitationTemplateRegex + '\\s*' + closingRefHTMLRegex;
						var openingCitationParserRegex = openingRefParserRegex + '\\s*' + openingCitationTemplateRegex;
						var closingCitationParserRegex = '(?:\\|[^\\|\\}])*' + closingCitationTemplateRegex + '\\s*' + closingRefParserRegex;
						startTime = performance.now();
						textLength = editor.get().length;
						count = 0;
						var postscriptArg = '\\s*postscript\\s*=';
						editor
							.replace(RegExp('(' + openingCitationHTMLRegex + '(?:(?!\\|' + postscriptArg + '|' + problematicIntermediaryRegex + '|' + closingCitationHTMLRegex + ')(?:.|\r|\n))*)\\|' + postscriptArg + '\\s*\\.\\s*((?:\\|(?:(?!' + postscriptArg + '|' + problematicIntermediaryRegex + '|\\s*' + closingCitationHTMLRegex + ')[^\\|])*)*' + closingCitationHTMLRegex + ')', 'g'), function(match, p1, p2) { count++; return p1 + p2; })
							.replace(RegExp('(' + openingCitationParserRegex + '(?:(?!\\|' + postscriptArg + '|' + problematicIntermediaryRegex + '|' + closingCitationParserRegex + ')(?:.|\r|\n))*)\\|' + postscriptArg + '\\s*\\.\\s*((?:\\|(?:(?!' + postscriptArg + '|' + problematicIntermediaryRegex + '|\\s*' + closingCitationParserRegex + ')[^\\|])*)*' + closingCitationParserRegex + ')', 'g'), function(match, p1, p2) { count++; return p1 + p2; });
						if (count) {
							summaryAppendix += "rm " + (count !== 1 ? count + " " : "") + "[[Category:CS1 maint: postscript|redundant CS1 postscript]]" + pluralize(count) + ", ";
							watchlistExpiryMode = 2;
						}
						reportCompletion(count + " redundant CS1 postscript" + pluralize(count), (performance.now() - startTime).round(3), 'info', 'fix-postscript-period', count, textLength);
						startTime = performance.now();
						textLength = editor.get().length;
						count = 0;
						var urlStatusArg = '\\s*url-status\\s*=';
						var archiveArgs = '\\s*archive-(?:url|date)\\s*=\\s*\\w+';
						editor
							.replace(RegExp('(' + openingCitationTemplateRegex + '(?:(?!\\|' + urlStatusArg + '|\\|' + archiveArgs + '|' + problematicIntermediaryRegex + '|' + closingCitationTemplateRegex + ')(?:.|\r|\n))*)\\|' + urlStatusArg + '\\s*live\\s*((?:\\|(?:(?!' + urlStatusArg + '|' + archiveArgs + '|' + problematicIntermediaryRegex + '|\\s*' + closingCitationTemplateRegex + ')[^\\|])*)*\\s*' + closingCitationTemplateRegex + ')', 'g'), function(match, p1, p2) { count++; return p1 + p2; });
						if (count) {
							summaryAppendix += "rm " + (count !== 1 ? count + " " : "") + "[[Category:CS1 maint: url-status|live CS1 url status]]" + (count === 1 ? "" : "es") + ", ";
						}
						reportCompletion(count + " live CS1 url status" + (count === 1 ? "" : "es"), (performance.now() - startTime).round(3), 'info', 'remove-live-url-status', count, textLength);
						conductRegexReplacement([[/\[\[\s*([\w-]+(?:\s+[\w-]+)?),\s*([\w-]+(?:\s+[\w-]+)?)\s*\|\s*((?:[\w-]+(?:\s+[\w-]+)?)?)\s*\]\],\s*\[\[\s*([\w-]+(?:\s+[\w-]+)?)((?:,(?:\s+[\w-]+)*|\s+\([\w-]+(?:\s+[\w-]+)*\))?\s*(?:\|\s*[\w-]+(?:\s+[\w-]+)?\s*)?)\]\]/g, function(match, p1, p2, p3, p4, p5) {
							var p2Temp = p2.replaceAll(/[‘’]/g, '\'').replaceAll(/[“”]/g, '\"');
							if (p1.replaceAll(/[‘’]/g, '\'').replaceAll(/[“”]/g, '\"') === p3.replaceAll(/[‘’]/g, '\'').replaceAll(/[“”]/g, '\"') && p2Temp === p4.replaceAll(/[‘’]/g, '\'').replaceAll(/[“”]/g, '\"') && (!p5.length || (p5.indexOf('|') >= 0 && (p5.indexOf('|') === p5.length - 1 || p5.substring(p5.indexOf('|') + 1).trim().replaceAll(/[‘’]/g, '\'').replaceAll(/[“”]/g, '\"') === p2Temp)))) {
								count++;
								return "[[" + p1 + ", " + p2 + "]]";
							}
							return "[[" + p1 + ", " + p2 + "|" + p3 + "]], [[" + p4 + p5 + "]]";
						}]], editor, "MOS:GEOLINK", 'combine-geographical-wiki-links', 0);
						conductRegexReplacement([[/<u\s*>((?:(?!<\/?u\s*>)(?:.|\r|\n))*.?)<\/u\s*>/gi, function(match, p1) { count++; return "{{em|" + p1 + "}}"; }]], editor, "MOS:EMPHASIS", 'fix-HTML-underlining', 2);
						startTime = performance.now();
						textLength = editor.get().length;
						var countArray = [0]; //an array style for two replacement segments sharing a summary excerpt
						editor
							.replace(/(?<!\n;[^\n]*)\n:\s*<math(?:\s*(\s[^<>]*))?>((?:(?!<\/math\s*>)(?:.|\r|\n))*.?)<\/math\s*>\s*\n/gi, function(match, p1, p2) { countArray[0]++; return "\n<math " + (p1 !== undefined || /\sdisplay\s*=\s*([\"\'])\s*block\s*\1/gi.test(p1) ? "" : "display=\"block\"") + (p1 === undefined ? "" : p1) + ">" + p2 + "</math>\n"; });
						//note: appending message to summary is handled by the replacement segment after next
						reportCompletion(countArray[0] + " HTML math tag" + pluralize(countArray[0]) + " display as block", (performance.now() - startTime).round(3), 'info', 'fix-colon-indent-math', countArray[0], textLength);
						conductRegexReplacement([[/(?<=\n)([\*#]*)(:+|;[^\n]*\n|)([^\n]*)\n(?=[:\*#])/g,
							function(match, p1, p2, p3) {
								if (p2.length && p2[0] === ":") {
									count++;
									return p1 + new Array(p2.length + 1).join('*') + p3 + "\n";
								}
								else {
									return p1 + p2 + p3 + "\n";
								}
							}]], editor, "MOS:DLIST", 'fix-dlist', 1);
						startTime = performance.now();
						textLength = editor.get().length;
						countArray.push(0);
						editor
							.replace(/(?<=\n)([\*#]*)(:+|;[^\n]*\n|)([^\n]*)\n(?=\s*\n)/g, function(match, p1, p2, p3) {
								if (p2.length && p2[0] === ":") {
									countArray[1]++;
									return p1 + "{{block indent" + (p2.length === 2 ? "" : "|left=" + (1.5 * p2.length)) + "|1=" + p3 + "}}\n";
								}
								else {
									return p1 + p2 + p3 + "\n";
								}
							})
							.replace(/(?<=\n)([\*#]*)(:+|;[^\n]*\n|)([^\n]*)\n/g, function(match, p1, p2, p3) {
								if (p2.length && p2[0] === ":") {
									countArray[1]++;
									return p1 + "{{block indent" + (p2.length === 2 ? "" : "|left=" + (1.5 * p2.length)) + "|1=" + p3 + (p3.length >= "<br>".length && p3.match(/<br\s*\/?>/g) !== null && p3.lastIndexOf(p3.match(/<br\s*\/?>/g).slice(-1)[0]) === p3.length - p3.match(/<br\s*\/?>/g).slice(-1)[0].length ? "" : "<br>") + "}}\n";
								}
								else {
									return p1 + p2 + p3 + "\n";
								}
							});
						if (countArray[0] || countArray[1]) {
							summaryAppendix += "[[MOS:INDENTGAP]] (" + (countArray[0] ? countArray[0] : "") + (countArray[0] && countArray[1] ? "+" : "") + (countArray[1] ? countArray[1] : "") + "), ";
							watchlistExpiryMode = 2;
						}
						reportCompletion(countArray[1] + " colon indentation" + pluralize(countArray[1]), (performance.now() - startTime).round(3), 'info', 'fix-colon-indent', countArray[1], textLength);
						conductRegexReplacement([[/\[\[\s*([^\]\|\{\}<>]+(?<!\s))\s*\|\s*([^\]\|\{\}<>]+)\s*\]\]/g, function(match, p1, p2) {
							var p2Temp = p2.charAt(0).toLowerCase() + p2.substring(1).replaceAll(/[‘’]/g, '\'').replaceAll(/[“”]/g, '\"');
							if (p2Temp.indexOf((p1.charAt(0).toLowerCase() + p1.substring(1)).replaceAll(/[‘’]/g, '\'').replaceAll(/[“”]/g, '\"')) === 0 && (p2Temp.length === p1.length || (p2Temp.substring(p1.length).match(/[^\s`~!@#$%^&*\(\)\-–—_=+\[\]\{\}\\\|;:\'\"‘’“”,\.<>\/?A-Z\d][^\s`~!@#$%^&*\(\)\-–—_=+\[\]\{\}\\\|;:\'\"‘’“”,\.<>\/?\d]*/g) !== null && p2Temp.substring(p1.length).match(/[^\s`~!@#$%^&*\(\)\-–—_=+\[\]\{\}\\\|;:\'\"‘’“”,\.<>\/?A-Z\d][^\s`~!@#$%^&*\(\)\-–—_=+\[\]\{\}\\\|;:\'\"‘’“”,\.<>\/?\d]*/g)[0].length === p2Temp.length - p1.length))) {
								count++;
								return "[[" + p2.charAt(0) + p2Temp.substring(1, p1.length) + "]]" + p2Temp.substring(p1.length);
							}
							return "[[" + p1 + "|" + p2 + "]]";
						}]], editor, "MOS:PIPESTYLE", 'shorten-wiki-links', 0);

						var tableEscapeString = '';
						var expansionDepthLimit = 40; //40 is the expansion-depth limit (and is assumed to apply to table nesting)
						for (var i = 0; i < expansionDepthLimit; i++) {
							tableEscapeString += '(?:(?:(?!\\{\\||\\|\\})(?:.|\\r|\\n))*.?|\\{\\|';
						}
						tableEscapeString += '(?:(?!\\{\\||\\|\\})(?:.|\\r|\\n))*.?';
						for (var i = 0; i < expansionDepthLimit; i++) {
							tableEscapeString += '\\|\\})*';
						}
						var escaped10 = editor.escape(RegExp(tableEscapeString, 'g'));

						conductRegexReplacement([[/\[\[\s*(?:[Ff][Ii][Ll][Ee]|[Ii][Mm][Aa][Gg][Ee])\s*:\s*([^\|\[\]]+)\s*\|((?:(?:(?!\d+\s*px|upright)[^\|\[\]])*\|)*)(\d+)\s*px((?:\s*\|(?:(?!\d+\s*px|upright)[^\|\[\]])*)*)\]\]/g, function(match, p1, p2, pixelValue, p4) { count++; return "[[File:" + p1 + "|" + p2 + "upright=" + (Number(pixelValue) / 220).round(3) + p4 + "]]"; }]], editor, "MOS:IMGSIZE", 'convert-image-pixel-sizes', 1); //convert pixels to upright (rounded to nearest thousandth due to the three significant digits of 220)

						editor
							.unescape(escaped8)
							.unescape(escaped10);


						// escaped1 = editor.escape(wikiLinkEscapeRegex);
						// escaped2 = editor.escape(HTMLEscapeRegex);
						// escaped3 = editor.escape(RegExp('\\{\\{' + templateEscapeString + '\\}\\}', 'g')); //note that this assumes that no single-paired nested braces will be used to surpass the expansion-depth limit
						// var escaped11 = editor.escape(/\"[^\"]*\"/g); //requires curly quotation marks to have been converted, though is of highly varying accuracy anyway

						// var monthOfYearRegex = 'January|February|March|April|May|June|July|August|September|October|November|December|(?:Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
						var compoundNumbersRegex = '(?:m|b|tr|quadr)illion';
						// conductRegexReplacement([
						// 	[RegExp('(?<=[a-z](?<!' + monthOfYearRegex + '|version))\\s+(\\d)(?=\\s+(?!' + monthOfYearRegex + '|' + compoundNumbersRegex + ')[a-z])', 'gi'), function(match, p1) {
						// 		count++;
						// 		switch (p1) {
						// 			case '0':
						// 				return " zero";
						// 			case '1':
						// 				return " one";
						// 			case '2':
						// 				return " two";
						// 			case '3':
						// 				return " three";
						// 			case '4':
						// 				return " four";
						// 			case '5':
						// 				return " five";
						// 			case '6':
						// 				return " six";
						// 			case '7':
						// 				return " seven";
						// 			case '8':
						// 				return " eight";
						// 			case '9':
						// 				return " nine";
						// 			default: //should never occur, but replace with original if it does
						// 				return " " + p1;
						// 		}
						// 	}]
						// ], editor, "MOS:SPELL09", 'spell-digits', 1);

						// editor
						// 	.unescape(escaped1)
						// 	.unescape(escaped2)
						// 	.unescape(escaped3)
						// 	.unescape(escaped11);


						escaped1 = editor.escape(wikiLinkEscapeRegex);
						escaped4 = editor.escape(headingEscapeRegex);
						var escaped5 = editor.escape(fileRegex);
						var escaped6 = editor.escape(imageParameterRegex);
						escaped8 = editor.escape(nowikiEscapeRegex);
						var escaped12 = editor.escape('\\{\\{\\s*[cC]ite +\\w+\\s*' + templateEscapeString + '\\}\\}');

						var discouragedCircaRegex = '(?:circa|ca?(?:\\.|\\s)|C\\.|around|approx(?:imately|\\.))';
						var eraAbbreviationRegex = 'BCE?|AD|CE|B\\.\\s?C\\.\\s?(?:E\\.)?|A\\.\\s?D\\.|C\\.\\s?E\\.';
						var nbspRegex = '\\{\\{\\s*[nN]bsp\\s*\\}\\}|&nbsp;';
						var ndashRegex = '\\{\\{\\s*[eE]n +[dD]ash\\s*\\}\\}|&ndash;|–';
						var mdashRegex = '\\{\\{\\s*[mM]dash\\s*\\}\\}|&mdash;|—';
						conductRegexReplacement([[RegExp('(?<=[^\\w])' + discouragedCircaRegex + '\\s*(\\d+)(?:\\s+(' + eraAbbreviationRegex + ')(?!(?:(?:' + nbspRegex + ')|(?:\\s{0,}(?:-|' + ndashRegex + '))|\\s{0,}(?:' + mdashRegex + ')|\\s+to\\s)|E\\.?(?:(?:' + nbspRegex + ')|(?:\\s{0,}(?:-|' + ndashRegex + '))|\\s{0,}(?:' + mdashRegex + ')|\\s+to\\s))|(?!\\d*(?:(?:(?:' + nbspRegex + ')|(?:\\s{0,}(?:-|' + ndashRegex + '))|\\s{0,}(?:' + mdashRegex + '))|[a-zA-Z%°\/]|[,\\.:]\\d+|\\s+(?:%|per\\s?cent|kilo|milli|' + compoundNumbersRegex + '|to\\s|' + eraAbbreviationRegex + '))))', 'g'), function(match, p1, p2) { count++; return "{{circa|" + p1 + (p2 === undefined ? "" : "&nbsp;" + p2.replaceAll(' ', '').replaceAll('.', '')) + "}}"; }]], editor, "MOS:CIRCA", 'fix-circa-formatting', 1); //note that to minimize false positives, spelling single-digit numbers and non-breaking space insertion should be handled before this circa formatting

						editor
							.unescape(escaped1)
							.unescape(escaped4)
							.unescape(escaped5)
							.unescape(escaped6)
							.unescape(escaped8)
							.unescape(escaped12);


						// var escaped13 = editor.escape(/(?:<!--(?:(?!-->)(?:.|\r|\n))*-->|\{\{\s*#tag:\s*!--\s*\}\})/g);
						// var escaped14 = editor.escape(RegExp('(?:<ref\s*(?:[^<>]+(?<!\/))?>(?:(?!<\/ref\s*>)(?:.|\r|\n))*<\/ref\s*>|\{\{\s*#tag:\s*ref\s*\}\})', 'gi'));
						// var escaped15 = editor.escape(RegExp('(?:<gallery\s*(?:[^<>]+)>(?:(?!<\/gallery\s*>)(?:.|\r|\n))*<\/gallery\s*>|\{\{\s*#tag:\s*gallery\s*\}\})', 'gi'));

						// editor
						// 	.unescape(escaped13)
						// 	.unescape(escaped14)
						// 	.unescape(escaped15);


						startTime = performance.now();
						textLength = editor.get().length;
						countArray = [0, 0, 0, 0]; //ol then li in ol then ul then li in ul
						editor
							.replace(/<ol\s*>((?:(?!<\/?[ou]l\s*>)(?:.|\r|\n))*.?)<\/ol\s*>/gi, function(match, p1) { countArray[0]++; return p1.replace(/\n?<li\s*>((?:(?!<\/?li\s*>)(?:.|\r|\n))*.?)<\/li\s*>/gi, function(match, p1) { countArray[1]++; return "\n# " + p1; }); })
							.replace(/<ul\s*>((?:(?!<\/?[ou]l\s*>)(?:.|\r|\n))*.?)<\/ul\s*>/gi, function(match, p1) { countArray[2]++; return p1.replace(/\n?<li\s*>((?:(?!<\/?li\s*>)(?:.|\r|\n))*.?)<\/li\s*>/gi, function(match, p1) { countArray[3]++; return "\n* " + p1; }); });
						if (countArray[0] || countArray[2]) {
							summaryAppendix += "converted " + (countArray[0] ? countArray[0] + " HTML ol tag" + pluralize(countArray[0]) + " containing " + countArray[1] + " list item" + pluralize(countArray[1]) : "") + (countArray[0] && countArray[2] ? " and " : "") + (countArray[2] ? countArray[2] + " HTML ul tag" + pluralize(countArray[2]) + " containing " + countArray[3] + " list item" + pluralize(countArray[3]) : "") + " to wikitext, ";
							watchlistExpiryMode = 2;
						}
						reportCompletion((countArray[0] || !countArray[2] ? countArray[0] + " HTML ol tag" + pluralize(countArray[0]) + " containing " + countArray[1] + " HTML li tag" + pluralize(countArray[1]) : "") + (countArray[0] === countArray[2] ? " and " : "") + (!countArray[0] || countArray[2] ? countArray[2] + " HTML ul tag" + pluralize(countArray[2]) + " containing " + countArray[3] + " HTML li tag" + pluralize(countArray[3]) : "") + " converted", (performance.now() - startTime).round(3), 'info', 'fix-HTML-lists', countArray[0] + " containing " + countArray[1] + " and " + countArray[2] + " containing " + countArray[3], textLength);

						startTime = performance.now();
						textLength = editor.get().length;
						count = 0;
						editor
							.replace(/(?:\n\s*)?<tr(?:\s+([^<>]*))?>(?=\s*<th(?:\s+[^<>]*)?>)\s*((?:(?!\s*<\/?t(?:r|able)\s*>)(?:.|\r|\n))*.?)\s*<\/tr\s*>\s*/gi, function(match, p1, p2) { count++; return "\n|-" + (p1 !== undefined ? " " + p1 : "") + "\n!" + p2; })
							.replace(/(?:\n\s*)?<tr(?:\s+([^<>]*))?>\s*((?:(?!\s*<\/?t(?:r|able)\s*>)(?:.|\r|\n))*.?)\s*<\/tr\s*>\s*/gi, function(match, p1, p2) { count++; return "\n|-" + (p1 !== undefined ? " " + p1 : "") + "\n|" + p2; });
						if (count) {
							summaryAppendix += "converted " + count + " HTML tr tag" + pluralize(count) + " to wikitext, ";
							watchlistExpiryMode = 2;
						}
						reportCompletion(count + " HTML tr conversion" + pluralize(count), (performance.now() - startTime).round(3), 'info', 'fix-HTML-tr', count, textLength);
						startTime = performance.now();
						textLength = editor.get().length;
						count = 0;
						editor
							.replace(/<td(?:\s+([^<>]*))?>\s*((?:(?!\s*<\/?t(?:[dhr]|able)\s*>)(?:.|\r|\n))*.?)\s*<\/td\s*>\s*(?=\s*<td(?:\s+[^<>]*)?>)/gi, function(match, p1, p2) { count++; return (p1 !== undefined ? " " + p1 + " |" : "") + " " + p2 + " ||"; })
							.replace(/<td(?:\s+([^<>]*))?>\s*((?:(?!\s*<\/?t(?:[dhr]|able)\s*>)(?:.|\r|\n))*.?)\s*<\/td\s*>\s*/gi, function(match, p1, p2) { count++; return (p1 !== undefined ? " " + p1 + " |" : "") + " " + p2 + "\n"; });
						if (count) {
							summaryAppendix += "converted " + count + " HTML td tag" + pluralize(count) + " to wikitext, ";
							watchlistExpiryMode = 2;
						}
						reportCompletion(count + " HTML td conversion" + pluralize(count), (performance.now() - startTime).round(3), 'info', 'fix-HTML-td', count, textLength);
						startTime = performance.now();
						textLength = editor.get().length;
						count = 0;
						editor
							.replace(/<th(?:\s+([^<>]*))?>\s*((?:(?!\s*<\/?t(?:[dhr]|able)\s*>)(?:.|\r|\n))*.?)\s*<\/th\s*>\s*(?=<th(?:\s+[^<>]*)?>)/gi, function(match, p1, p2) { count++; return (p1 !== undefined ? " " + p1 + " |" : "") + " " + p2 + " !!"; })
							.replace(/<th(?:\s+([^<>]*))?>\s*((?:(?!\s*<\/?t(?:[dhr]|able)\s*>)(?:.|\r|\n))*.?)\s*<\/th\s*>\s*/gi, function(match, p1, p2) { count++; return (p1 !== undefined ? " " + p1 + " |" : "") + " " + p2 + "\n"; });
						if (count) {
							summaryAppendix += "converted " + count + " HTML th tag" + pluralize(count) + " to wikitext, ";
							watchlistExpiryMode = 2;
						}
						reportCompletion(count + " HTML th conversion" + pluralize(count), (performance.now() - startTime).round(3), 'info', 'fix-HTML-th', count, textLength);
						startTime = performance.now();
						textLength = editor.get().length;
						count = 0;
						editor
							.replace(/<table\s*>\s*((?:(?!\s*<\/?t(?:[dhr]|able)\s*>)(?:.|\r|\n))*.?)\s*<\/table\s*>/gi, function(match, p1) { count++; return "{| \n|+ \n" + p1 + "\n|}"; })
							.replace(/<table(?:\s+([^<>]*))>\s*((?:(?!\s*<\/?t(?:[dhr]|able)\s*>)(?:.|\r|\n))*.?)\s*<\/table\s*>/gi, function(match, p1, p2) { count++; return "{| " + p1 + "\n|+ \n" + p2 + "\n|}"; });
						if (count) {
							summaryAppendix += "converted " + count + " HTML table tag" + pluralize(count) + " to wikitext, ";
							watchlistExpiryMode = 2;
						}
						reportCompletion(count + " HTML table conversion" + pluralize(count), (performance.now() - startTime).round(3), 'info', 'fix-HTML-table', count, textLength);


						startTime = performance.now();
						if (summaryAppendix.substr(summaryAppendix.length - 2) === ", ") {
							summaryAppendix = summaryAppendix.substr(0, summaryAppendix.length - 2);
						}
						if (summaryAppendix.length) {
							var existingSummaryLength = $('#wpSummary').val().length;
							var capitalize = !existingSummaryLength || /^(?:\s*\/\*(?:(?!\*\/)(?:.|\r|\n))*\*\/\s*)+$/.test($('#wpSummary').val());
							if (capitalize) {
								summaryAppendix = summaryAppendix.charAt(0).toUpperCase() + summaryAppendix.slice(1);
								if (summaryAppendix.split(",").length === 2 - existingSummaryLength) { summaryAppendix = !existingSummaryLength ? summaryAppendix.replace(",", " and") : "and " + summaryAppendix; }
								else if (summaryAppendix.split(",").length > 2 - existingSummaryLength) { summaryAppendix = summaryAppendix.slice(0, summaryAppendix.lastIndexOf(",") + 1) + " and" + summaryAppendix.slice(summaryAppendix.lastIndexOf(",") + 1); }
								if (!$('#wpMinoredit').checked) {
									$('#wpMinoredit').prop( 'checked', true );
								}
							}
							editor.appendEditSummary(summaryAppendix);
							setWatchlistExpiryForCleanup(0, performance.now());
							summaryAppendix = "";
						}
						editor.clickDiff();
						reportCompletion("completed cleanup operation", (performance.now() - startTime).round(3), 'info', 'complete-cleanup', 'NaN');
	  				}
	  			},
				{
					name: 'NBSP cleanup',
					script: function(editor) {
						summaryAppendix = "";
						// var textLength = editor.get().length;

						var templateEscapeString = '';
						var expansionDepthLimit = 40; //40 is the expansion-depth limit (and is assumed to apply to table nesting)
						for (var i = 0; i < expansionDepthLimit * 2 - 1; i++) { //the expansion-depth limit is multiplied by two for each level requiring two nested curly braces and minus one for the outermost two pairs being supplied later; note the assumption that no single-paired nested braces will be used to surpass the limit of eighty nested pairs of curly braces
							templateEscapeString += '(?:[^\\{\\}]|\\{';
						}
						templateEscapeString += '[^\\{\\}]*';
						for (var i = 0; i < expansionDepthLimit * 2 - 1; i++) { //the expansion-depth limit is multiplied by two for each level requiring two nested curly braces and minus one for the outermost two pairs being supplied later; note the assumption that no single-paired nested braces will be used to surpass the limit of eighty nested pairs of curly braces
							templateEscapeString += '\\})*';
						}
						templateEscapeString = '\\{\\{' + templateEscapeString + '\\}\\}';
						var wikiLinkEscapeRegex = RegExp('\[\[[^\|\]]*(?:\|(?=[^\|\]]*\]\])|\]\])', 'g');
						var HTMLEscapeRegex = RegExp(/<(?!(?:br|references)\s*\/?>)([a-zA-Z]+)[^<>]*>(?:(?!<\/\1\s*>)(?:.|\r|\n))*<\/\1\s*>/gi);
						var escaped1 = editor.escape(wikiLinkEscapeRegex);
						var escaped2 = editor.escape(HTMLEscapeRegex);
						var escaped3 = editor.escape(RegExp(templateEscapeString, 'g')); //note that this assumes that no single-paired nested braces will be used to surpass the expansion-depth limit

						var monthOfYearRegex = 'January|February|March|April|May|June|July|August|September|October|November|December|(?:Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
						conductRegexReplacement([[RegExp('(?<=' + monthOfYearRegex + ')\\s+(?=\\d{3,})', 'g'), function() { count++; return "&nbsp;"; }]], editor, "MOS:NBSP", 'insert-date-nbsps', 2);

						editor
							.unescape(escaped1)
							.unescape(escaped2)
							.unescape(escaped3);


						var startTime = performance.now();
						if (summaryAppendix.substr(summaryAppendix.length - 2) === ", ") {
							summaryAppendix = summaryAppendix.substr(0, summaryAppendix.length - 2);
						}
						if (summaryAppendix.length) {
							var existingSummaryLength = $('#wpSummary').val().length;
							var capitalize = !existingSummaryLength || /^(?:\s*\/\*(?:(?!\*\/)(?:.|\r|\n))*\*\/\s*)+$/.test($('#wpSummary').val());
							if (capitalize) {
								summaryAppendix = summaryAppendix.charAt(0).toUpperCase() + summaryAppendix.slice(1);
								if (summaryAppendix.split(",").length === 2 - existingSummaryLength) { summaryAppendix = !existingSummaryLength ? summaryAppendix.replace(",", " and") : "and " + summaryAppendix; }
								else if (summaryAppendix.split(",").length > 2 - existingSummaryLength) { summaryAppendix = summaryAppendix.slice(0, summaryAppendix.lastIndexOf(",") + 1) + " and" + summaryAppendix.slice(summaryAppendix.lastIndexOf(",") + 1); }
								if (!$('#wpMinoredit').checked) {
									$('#wpMinoredit').prop( 'checked', true );
								}
							}
							editor.appendEditSummary(summaryAppendix);
							setWatchlistExpiryForCleanup(0, performance.now());
							summaryAppendix = "";
						}
						editor.clickDiff();
						reportCompletion("completed NBSP cleanup operation", (performance.now() - startTime).round(3), 'info', 'complete-nbsp-cleanup', 'NaN');
					}
				},
				{
					name: 'Insert MiszaBot config',
					template: '{{User:MiszaBot/config\n| algo                = old(30d)\n| archive             = {{SUBST:FULLPAGENAME}}/Archive %(counter)d\n| counter             = 1\n| maxarchivesize      = 150K\n| archiveheader       = {{Automatic archive navigator}}\n| minthreadstoarchive = 1\n| minthreadsleft      = 4\n}}', //[[User:MiszaBot/config]]
					position: 'cursor',
					editSummary: 'WARNING: The magic word FULLPAGENAME does not work when the article title contains special characters (\"&\').',
					editSummaryPosition: 'after',
					script: function() {
						if (!($('#wpSummary').val().length || /^(?:\s*\/\*(?:(?!\*\/)(?:.|\r|\n))*\*\/\s*)+$/.test($('#wpSummary').val())) && $('#wpMinoredit').checked) {
							$('#wpMinoredit').prop( 'checked', false );
						}
					}
				},
				{
					name: 'Insert parser function–wrappable {{Test case|_format=tablerows}} replacement',
					template: '<!-- Test # -->\n{{indent|2}}<code><nowiki>{{#expr:{{formatnum:{{{{SUBST:BASEPAGENAME}}}}|R}}/1e6 round 0}} million</' + 'nowiki></code>\n\n{| style="text-align: left;"\n|-\n! scope="row" | \'\'\'{{tl|Anderjef}}\'\'\'\n| style="padding:0 1em" | {{Right-arrow}}\n| {{#expr:{{formatnum:{{Template:Anderjef}}|R}}/1e6 round 0}} million\n|-\n! scope="row" | \'\'\'{{tl|Anderjef/sandbox}}\'\'\'\n| style="padding:0 1em" | {{Right-arrow}}\n| {{#expr:{{formatnum:{{Template:Anderjef/sandbox}}|R}}/1e6 round 0}} million\n|}</syntaxhighlight>', //string is broken at closing nowiki tag to avoid throwing off Wikipedia wherever this document is loaded
					position: 'cursor',
					script: function() {
						if (!($('#wpSummary').val().length || /^(?:\s*\/\*(?:(?!\*\/)(?:.|\r|\n))*\*\/\s*)+$/.test($('#wpSummary').val())) && $('#wpMinoredit').checked) {
							$('#wpMinoredit').prop( 'checked', false );
						}
					}
				}
			],
			{ forActions: 'edit' }
		);
	});
});
// </nowiki>
importScript('User:SuperHamster/CiteUnseen.js'); // Backlink: [[User:SuperHamster/CiteUnseen.js]]
importScript('User:Novem_Linguae/Scripts/CiteHighlighter.js'); // Backlink: [[User:Novem_Linguae/Scripts/CiteHighlighter.js]]
importScript('User:Headbomb/unreliable.js'); // Backlink: [[User:Headbomb/unreliable.js]]