User:Sarang/cleanup/sandbox.js

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/**
@Revision: 13:00, 20 November 2022 (UTC)
@Description:
Script for cleanup of file description pages. Script does NOT work with Internet Explorer 8 or lower.
To use, add this line to your common.js file: importScript('User:Sarang/cleanup.js');
mto_cleanup tagger (initially developed by Patstuart/Magog the Ogre)

There are 3 hook events
* "gadget.cleanup.run"
* "gadget.cleanup.done"
* "gadget.cleanup.loaded"

* ToDo: https://commons.wikimedia.org/wiki/File:Ringerike_komm.svg
*/
// <nowiki>
/* eslint no-var:"error"*/
/* eslint-env es6*/
/* eslint-disable camelcase, no-underscore-dangle, valid-jsdoc*/
/* jslint browser:true, bitwise:true, plusplus:true, regexp:true*/
(function ($, mw) {
'use strict';
let fileNamespace = mw.config.get('wgNamespaceNumber') === 6,
	filename = mw.config.get('wgTitle'),
	linksShown = [],
	INFORMATION_FIELDS_REGEX,
	sum, // summary
	pn = mw.config.get('wgPageName'), // own tool section
	cleanBox = $('<div id="p-cl" class="portal" role="navigation"><h3 id="p-cl-label">Cleanup</h3><div class="body"><ul></ul></div></div>');

function regexify(elements, action) {
	let regex = '(?:';
	elements.forEach(function (el, i) {
		if (i) regex += '|';
		regex += action ? action(el) : el;
	});
	regex += ')';
	return regex;
}

function quoteNoCase(string) {
	let char,
		i,
		upper,
		lower,
		newString = '';

	for (i = 0; i < string.length; i++) {
		char = string[i];
		upper = char.toUpperCase();
		lower = char.toLowerCase();

		if (upper !== lower) newString += '[${upper}${lower}]';
		else newString += char;
	}

	return newString;
}

function regexifyTemplates(templates, insensitive) {
	return regexify(templates, (element) => {
		let string;
		if (insensitive) {
			string = mw.util.escapeRegExp(element);
			string = string.replace(/./, quoteNoCase);
		} else {
			string = quoteNoCase(mw.util.escapeRegExp(element.substring(0, 1))) + mw.util.escapeRegExp(element.substring(1));
		}
		return string.replace(' ', '[ _]+');
	});
}
INFORMATION_FIELDS_REGEX = regexifyTemplates([
	'description',
	'date',
	'source',
	'author', 'artist',
	'image',  'tincture',
	'permission',
	'other versions', 'versions',
	'other fields',   'fields'
]);

/**
* Need to reinitialize because the upload form creates a new textbox asynchronously
*/
function getTextbox() {
	sum = ((sum = document.forms.editform)) ? sum.wpSummary : '';
	return $('#wpTextbox1,#wpUploadDescription,#wpDescText1').first();
}

function bot_move_checked(text) {
	let isvg = (filename.slice(-3).toLowerCase() == 'svg');			// true when 'svg'
	let coai = /\{\{ *COAInformation\s*\n?/.test(text);	// COAInformation ?
	let chrg = coai && /\| *element *=/.test(text);		// COAI param present ?
//	let eccl =  ? ? ?									// ecclesiasticals		 
 	let imag = (/\| *image *=/.test(text) || /fields *= *\{\{Igen/.test(text));	// COAI param missing ?
	let tinc = /\| *tincture *=/.test(text);			// COAI param missing ?
	let addb = /\| *description *= *[^\n]/.test(text) ? ' ' : '';	// longest COAI string, 11 chars
	let coab = /\{\{coa blazon\}\}/i.test(text);		// coablazon (Ashoppio)
	let coac = '';						// coai user category (svg)
	let coas = /\{\{insignia\}\}/.test(text) ? '{{insignia}}' : '';		//  (Ashoppio et al.)
	let coau = '';						// coai user name
	let coax = '';						// coai user category (raster)
	let	coaiuser =						// should better be an external table 
			//	a table of (normally) 2-letter-items, 
			//	standing for an abbreviation of user names.
			//	A third letter may express 
			//	"c" — charge (coa element)
			//	"e" — ecclesial
			//	"r" — raster image 
		{	'Adalric67'		: 'Ad', 	// 
			'Aiwe'			: 'Aw', 	// 
			'Aliman5040'	: 'Al', 	// 
			'Ambroix'		: 'Mo',		// = Manoillon
			'Anenja'		: 'An',		//
			'ANGELUS'		: 'Ag',		//
			'Archmedus'		: 'As',		// 
			'ARK'			: 'Ak', 	// 
			'Aroche'		: 'Ar', 	// 
			'Ashoppio'		: 'Ao', 	// 
			'A. ter Hoek'	: 'AH',		// 
			'Aubisse'		: 'Ai', 	// 
			'Aups'			: 'Au', 	// 
			'Aviz2000'		: 'Av',		//  ?? only a few r
			'B1mbo'			: 'B1',		// 
			'Balmung0731'	: 'Bg',		// 
			'Bastianow'		: 'Ba',		// 
			'Bear17'		: 'Be',		// 	
			'Benzebuth198'	: 'Bz',		// 	
			'Bernina'		: 'Bn',		// 	
			'Bibar'			: 'Bi',		// 	
			'Binnette'		: 'Bt',		// 	
			'Bluebear2'		: 'Bl',		// 	
			'BrCaLeTo'		: 'Bo',		// 
			'Brian Boru'	: 'BB',		// 	
			'Brieg'			: 'Br',		// 	
			'Bruno Vallette': 'Va',		// 	
			'bvs-aca'		: 'Bv',		// 	
			'Caranorn'		: 'Cn',		//
			'Care'			: 'C2',		// (fi)
			'Carlos yo'		: 'CL',		// 
			'Celbusro'      : 'Cb',		//
			'CatChess'		: 'CC',		// 
			'Charles Matthews'	: 'CM', 	// 
			'Chatsam'		: 'Ch', 	// + Che
			'Chris die Seele':'CS', 'Gliwi' : 'Gl',
			'Christhoforos'	: 'Cv',		// 
			'COAmaker17'	: 'C1',		// 
			'CommanderPhoenix'	: 'CP',	//	= eh
			'Csavil'		: 'Cv',		// 
			'Cyygma'		: 'Cy',		//
			"D'Arch"		: 'Da', 	// 
			'Dan Koehl'		: 'DK', 	//  + DKr
			'David Liuzzo'	: 'DL', 	// 
			'Delta-9'		: 'D9',		// 		Δ9 (Steve Perucchi )
			'Doc Taxon'		: 'DTx', 	// ←←←
			'Domaleixo'		: 'DOr', 	// 
			'Ddurbmonnejg'	: 'Dd',		//
			'Echando una mano': 'EM',	// 
			'Ekpah'			: 'Ek', 	// 
			'ElSeñorDeLaNoche': 'SN', 	// 
			'Enekorga'		: 'En', 	// 
			'Erlenmeyer'	: 'Er', 	// 
			'Espandero'		: 'Es', 	//
			'Etxeko'		: 'Et', 	// 
			'Euryrel'		: 'Eu', 	//	+ Eue
			'F5JMH'			: 'F5',		// 
			'Facquis'		: 'Fq',		// 
			'FDRMRZUSA'		: 'FMU', 	// 
			'Fenn-O-maniC'	: 'Fo',		// (fi)
			'Flow2'			: 'F2',		// 
			'Flying jacket'	: 'Fj',		//
			'Fränsmer'		: 'Fr', 	// 
			'Fulvio314'		: 'Fu', 	// 
			'Geraldiker'	: 'Gk',		// 
			'Gerhard Bräunlich'	: 'GB', // 
			'GiovanniYosh12': 'GY', 	// = eh
			'GJo'			: 'GJ', 	// 
			'Gretaz'		: 'Gr', 	// 
			'Gunnar.offel'	: 'GO', 	// 
			'Henrysalome'	: 'HS',  	// 
			'Heralder'		: 'He',  	// 
			'Honneur-Patrie': 'HP', 	// 
			'Ipankonin'		: 'Ip',  	// 
			'Isidor Welti'	: 'IW',		// 
			'James2813'		: 'J2',		// 
			'J.C EVEN'		: 'JCr',	//
			'Jean-Claude EVEN': 'JCr',	// (only raster)
			'J-Ronn'		: 'JR',		// = eh (P-JR)
			'Jean92 dit le Comte': 'J9', 	// 
			'Jean-Mahmoud'	: 'JM',		// 
			'Jimmy44'		: 'JN',		// + JNe		(Jimmy Nicolle)
			'Jpgibert'		: 'JPG',  	// + JPc +JPe
			'Jürgen Krause'	: 'JK',		//
			'Jwnabd'		: 'Jw',  	// + Jwc		{Jörg)
			'Ketipmaig'		: 'KE',		// 
			'Kontributor 2K': 'K2',		// 
			'Kontrollstellekundl': 'KKr',	//
			'Krumpi'		: 'Ki',		//
			'Lamberto99'	: 'LA',		//		
			'LeMorvandiau'	: 'LM',		//
			'Leonid 2'		: 'L2',		//
			'Лобачев Владимир': 'LV',	//	Vladimir Lobachev
			'Lokal Profil'	: 'LP',		//
			'Lokal_Profil'	: 'LP',		//
			'Lothar520'		: 'L5',		//
			'Losch'			: 'LH',		//	LHr  only
			'Lynxlynx'		: 'Ly',		// + Lyr 
			'Macucal'		: 'Mc',		//
			'Madboy74'		: 'Mb',		//
			'Magul'			: 'MG',		// 
			'Manassas'		: 'Ma',		// 
			'Manoillon'		: 'Mf',		// or 'Mo' when not French styled
			'Massimop'		: 'Mm',		// 
			'MaxxL'			: 'ML',		//
			'Maxwxyz'		: 'Mx',		//
			'Miguillen'		: 'Mn',		//
//			'Mimich'		: 'Mh',		// 
			'Milenioscuro'	: 'Mi',		// 
			'MostEpic'		: 'ME',		// 
			'MyChevalier'	: 'Eu', 	// + Eue
			'Nanin7'		: 'N7',		// 
			'Nescd'			: 'Ne',		// 
			'Ng556'			: 'NG',		// 
			'N.Português'	: 'NP',		//	(Bragancihno)
			'Odejea'		: 'Od',		// + Ode			+ Odr (PNG)
			'Ollemarkeagle'	: 'TR',		// 
			'P-JR'			: 'JR',		// = eh (J-Ronn)
			'Perhelion'		: 'Ph',		// 
			'Peter17'		: 'P7',		//  )
			'Piom'			: 'Pi',		// + Pie +Pir	(Piotr Michał Jaworski/Piotr Jaworski/P. Jaworski))
//			'Jaworski'		: 'Pi',		// + Pie +Pir
//			'Piotr Jaworski': 'Pi',		// + Pie +Pir
			'Potrowl'		: 'P7',		//  )
			'Puck04'		: 'P4',		// 
			'Popadius'		: 'Po',		// 
			'RaFaDa20631'	: 'R2',		// 
			'Ragimiri'		: 'Ra',		// 
			'Regicollis'	: 'RC',		// 
			'Repgow'		: 'Rgr',	// only raster
			'Rinaldum'		: 'Rn',		// 
			'RoboQwezt0x7CB': 'RQ',		// 
			'RootOfAllLight': 'RL',		// c & r
			'Roqz'			: 'RQ',		// 
			'Ruthven'		: 'Rv',		// + Rue
			'SajoR'			: 'Sa',		// + Sae	(Alejandro Rojas)
			'Sidonius'		: 'SI',		// 
			'Simon.eller'	: 'SE',		// 
			'Samhanin'		: 'SM',		// 
			'SanchoPanzaXXI': 'Sq',		// 
			'SanglierT'     : 'Sr',		// + Sre	(|Mathieu CHAINE_)
			'Sanguinez'     : 'Sg',		// + Sge
			'Sa-se'			: 'SS', 	// Sando Senn
			'Skjoldbro'		: 'S3',		// 
			'Sodacan'		: 'So',		// 
			'Spax89'		: 'S0',		// + 
			'Spedona'		: 'Sp',		// 
			'SpinnerLaserzthe2nd':'SL',	//
			'Srfortes'		: 'Sf',		// 
			'Ssire'			: 'S7',		// + S7c, + S7e
			'Ssolbergj'		: 'SJ', 	// 
			'SteveK'		: 'SK', 	// + SKw
			'C.Sundin'		: 'Su', 	//
			'Syriatsu'		: 'Sy',		//					+ Syr (PNG)
			'Taktaal'		: 'Ta',		// 
			'TFerenczy'		: 'TF',		// (cs)
			'The Eloquent Peasant': 'EP',	//
			'Thom.Lanaud'	: 'TL',		// + TLw
			'Tomas.urban'	: 'TU',		// 
			'TomKr'			: 'TK',		// 
			'Tom-L'			: 'TO',		// 
			'Tretinville'	: 'Tv',		// 
			'T.Rystau'		: 'TR',		// 
			'Vale93b'		: 'V9',		// 
			'Vddbert'		: 'Vd',		// + Vde
			'ViniciusBR11'	: 'V1',		// 
			'Vity OKM'		: 'VO',		//	VOr  only 
			'Wagner51'		: 'Wa',		// + Wac
			'Wikimandia'	: 'WK',		// 
			'Xavigivax'		: 'Xv',		// 
			'Yricordel'		: 'Yr',		//
			'Zardoz91'		: 'Za',		//			(Louis Brun)
			'Orror'			: 'Zi',		//	= Zigeuner
			'Zigeuner'		: 'Zi',		// 
			'ZigeunerAlt'	: 'Zi',		// 
			'Zorlot'		: 'Zo' 		// 
		};
		
 	let	coacateg =						// should better be an external table
	{	'DK' : 'Coats of arms by Dan Koehl',
		'Er' : 'Contribuciones de Erlenmeyer', 
		'Su' : 'Images by User:C.Sundin', 
		'Ch' : 'Blason chatsam', 
		'Ip' : 'Images by Ipankonin', 
		'JN' : 'Files by User:Jimmy from fr.wikipedia', 
		'JPG': 'Products of jpgibert/heraldry/charge', // ..... JPc / JPe
		'Ki' : 'Coats of arms by Krumpi',
		'LP' : 'Images by Lokal_Profil/CoA',
		'Mc' : 'User:Macucal',
		'Mi' : 'Coats of arms by User:Milenioscuro',
		'Od' : 'Files by Odejea/SVG coat of arms',	
		'Odc': 'Files by Odejea/charges',				//	<=== 
		'Ode': 'Files by Odejea/SVG ecclesiastical coat of arms',
		'Odr': 'Files by Odejea/PNG coat of arms',
		'P7' : 'Images by Peter Potrowl',				// in user template 
		'Pi' : 'Pictures by PioM', 
		'SK' : 'COA by SteveK', 
		'Sq' : 'Contribuciones de SanchoPanzaXXI',
		'Sy' : 'Blason par Syryatsu',
		'Tv' : 'Blasons Tretinville',
		'V9' : 'U:Vale93b',
	//	'' : '',
	//	'' : '',
		'Za' : 'SVG coats of arms of France'		// obsolete
	};

	return text.replace(/\{\{BotMoveToCommons.*\}\}\s*\n/, '')
		.replace(/tingture/, 'tinCture')				// test whether has run
		.replace(/\[\[:ru:U:Лобачев Владимир\]\]/, '{{U|Лобачев Владимир}}')	// Lobachev
		.replace(/\(of \.svg file\)/, '')				// Tomas.urban 
		.replace(/\|@ther fields/g, '|other fields')	// unmask
//		.replace(/\* ?\[\[Utilisateur\:([^\|\]]+)\|?[^\]]*\]\]/g, '@@$1%%')		// Projet:Blasons

	// final corrections (some changes had been wrong)|source={{own}}
 		.replace(/(\{\{Attribs?\|[f|t|s]=)(?:\w\w? *\n *s\|t=?)/, '$1')		// doubling 
 		.replace(/uthor=Aubisse/, 'uthor={{Uw|Aubisse}}')					// Aubisse
 		.replace(/ource=dessin perso[^\n]*\n/, 'ource={{Own}}\n')			// Aubisse	
		.replace(/(\{\{[a-z]{2,3}\|)1= ?(of|von|vom|des|der|de|del|du|sobre)? ?/g, function (m, lang, expl)
			{ if (coai)
//				return lang+' '+expl;	// COAInformation only !!!
				return lang;			// COAInformation only !!!
			  else
				return m;
			})
		.replace(/\[\[Category:SVG coats of arms of France\]\] *\n/, function (m)	//
			{ if (coai)
				return '';				// COAInformation only !!!
			  else
				return m;
			})
	.replace(/( *\| *artist *= *)Design:\{\{unknown\|author\}\}; vector version:\{\{own\}\}/, '$1{{AutVec|u||}}')	
//		.replace(/( *\| *)author( *= *)/, function (m, p1='', p2='')	
//			{ if (coai)
//				return p1+'artist'+p2;	// COAInformation only !!!
//			  else
//				return m;
//			})
 		.replace(/\|\|c:\}\}/, '||c}}')	// error with permalink
// ===================================================================
//tst	.replace(/\[\[:([a-z]{2,3}):(?:([^:]+?):)? ?([^\]\|]+?)?\|?([^\]]+?)?\]\]/g, '{«$2|$3|$4|$1»}')	
//	.replace(/(\{\{([a-z]{2,3}\|) '') /g, "$1''")			// remove spaces
 	.replace(/\[\[:?[Uu]ser: *([^\]\|]+)(?:\|\1)?\]\]/g, '{{U|$1}}')	// all users
 	.replace(/\[\[:?[Uu]ser: *([^\]\|]+)(?:(\| *[^\]]*))?\]\]/g, '{{U|$1$2}}')	// single or not
		.replace(/ *\| *by *= *[Uu]ser:/, '/')		// Commons User template with namespace
		.replace(/\{\{[Uu]\|[Uu]ser:/, '{{U|')		// Commons User template with namespace
	.replace(/'?'?'?\[\[:en: *([^\]\|]+?)(?:\|\1)?\]\]'?'?'?/g, '{{W|$1}}')		// :en:
	.replace(/'?'?'?\[\[:de: *([^\]\|]+?)(?:\|\1)?\]\]'?'?'?/g, '{{Wd|$1}}')	// :de:
	.replace(/'?'?'?\[\[:es: *([^\]\|]+?)(?:\|\1)?\]\]'?'?'?/g, '{{We|$1}}')	// :es:
	.replace(/'?'?'?\[\[:fr: *([^\]\|]+?)(?:\|\1)?\]\]'?'?'?/g, '{{Wf|$1}}')	// :fr:
	.replace(/'?'?'?\[\[:ir: *([^\]\|]+?)(?:\|\1)?\]\]'?'?'?/g, '{{Wi|$1}}')	// :it:
	.replace(/\[\[:en: *([^\]\|]+?)(?:(\| *[^\]]*))?\]\]/g, '{{W|$1$2}}')	// :en:
	.replace(/\[\[:de: *([^\]\|]+?)(?:(\| *[^\]]*))?\]\]/g, '{{Wd|$1$2}}')	// :de:

 	.replace(/\{\{ *([UFTCMW]|Ut|Ud|Uc|Wt) *\|(?:1 *= *)?([^\|\}]*?)(?:\|([^\}\|]*?))?(?:\|([a-z]{2,3})?)?\}\}/g, function (m, ns, link, disp='', lang='') 
		{	if (lang !== '')				lang = "|" + lang;	// pipe only when present

 			let ssts = "";									// sub-string suffix
			if (link.length <= disp.length) {
 	 			for (let i = 1; i < link.length; i++)
  				{	if ( link.substring(i, i+1) === "_")  
  						link = link.substring(0, i) + " " + link.substring(i + 1);	// replace 
  					if ( disp.substring(i, i+1) === "_")  
  						disp = disp.substring(0, i) + " " + disp.substring(i + 1);	// replace, except {{U}}
  				}
	
 				ssts += disp.substring(0, link.length);	// part of possible sameness
 				if (ssts ===  link
				 || ssts ===  link.substring(0,1).toUpperCase() + link.substring(1)
				 || ssts ===  link.substring(0,1).toLowerCase() + link.substring(1)  )
 				{	link = ssts;					// case_correct from disp
 					ssts = "" + disp.substring(link.length, disp.length);	// suffix
 					disp = "";						// strip 
 				} 
 				else ssts = "";						// without suffix
 			}
		 	if (disp !== "" || lang !== "")		disp = "|" + disp; 
// ===================================================================
//		user_exist
// ===================================================================
			return "{{" + ns + "|" + link  + disp + lang + "}}" + ssts;
		})

/*		// Full replace (was not intentional by oalb) using Langtab
	.replace(/\{\{[Ww]\|([^\|\}]*?)(?:\|\||(\|[^\|]*?)\|)([a-z]{2,3})\}\}/g, function (m, link, disp = "", lang = "")
		{	//if (!disp)	 disp =  "";
		 	//if (!lang)	 lang =  "";
			let diss = "" + disp;
			if (diss === "")		diss =  "|";
			let ns = lang;
			ns = Langtab[lang] ? Langtab[lang] : lang;
			if (ns !== lang) 
				return "{{" + ns + "|" + link  + disp + "}}";		// language replaced🔺
			else
				return "{{W|" + link  + diss + "|" + lang + "}}";	// language not found 
		})
// ===================================================================.toLowerCase()
*/		
		.replace(/\[\[Walter Käch\]\], \[\[Fritz Brunner\]\]/, '{{Ul|{{Wd|Walter Käch}}|Fritz Brunner|m=n}}')	// G.B.
		.replace(/\|m=n\}\}\| ?\{\{U\|Gerhard Bräunlich\}\}\}\}/, '|m=n}}|Gerhard Bräunlich}}')					// G.B.
		.replace(/\{\{Created with Blender(?:\|coa)?\}\} *\n?/, '') // Odejea
	.replace(/\{\{LangSwitch\|en=\{\{en\|It's a blazon done for \[\[:fr:Projet:Blasons\|Blazon Project\]\] of French-speaking Wikipédia \}\}\|fr=\{\{fr\|C'est un blason réalisé pour le \[\[:fr:Projet:Blasons\|Projet Blasons\]\] du Wikipédia francophone\}\} \}\} *\n?/, '') // Odejea
	//	.replace(/\{\{ *AutVec *\| *([^ \|]?) *\| *\{\{ *Uc? *([^\|\}]+)(?: *\| *\2)? *(\|[^\}]*)? *\}\}/i, '{{AutVec|$1|$2$3}}')
		.replace(/\{\{ *AutVec *\|([^ \|]*) *\| *\{\{Uc?n? *\| *([^\}]+)\}\} *\}\}/i, '{{AutVec|$1|$2}}') 
		.replace(/\| *(?:artist|[Aa]uthor) *= *\{\{(?:[Uu][cdnw]?t?|Creator:|AutVec\|[^\|]*\| ?(?:\{\{[Uu][cdn]?w?t?)?) *\|? *([^\}\|]+)/, function (m, artaut=' ')
			{	coau = coaiuser[artaut] ? coaiuser[artaut] : '';		// COAInformation only !!!
				coac = coacateg[coau] ? '[[Category:'+coacateg[coau]+']]' : '';
				if (coau == '')			// when no author parm found, try
				{	coau  =	(/Piotr Michał Jaworski/.test(text))		? 'P2' : ''; 	//PioM
					coau  =	(/P. Jaworski/.test(text))		? 'P0' : coau; 	//PioM
					coau  =	(/Piotr Jaworski/.test(text))		? 'P1' : coau; 	//PioM
	//				coau  =	/(Piotr Michał Jaworski|Piotr Jaworski|P. Jaworski)/.test(text)	? 'P3' : coau; 	//PioM 

					coau  =	/ser:Adelbrecht\//.test(text)	? 'TO' : coau;	// Tom-L
					coau  =	/Bruno Vallette/.test(text)		? 'Va' : coau;	// Bruno
					coao  =	/Gerhard Bräunlich/.test(text)	? 'GB' : coau;	// G.B.
		if (/P. Jaworski/.test(text))
			coau = 'Px';
		if (/Piotr Jaworski/.test(text))
			coau = 'Py';
		if (/Piotr Michał Jaworski/.test(text))
			coau = 'Pz';
		if (/Peter17/.test(text))
			coau = 'P7';
				}
				if (coau.length==2 && !isvg)
					coau += 'r';						// raster suffix
				else									// charges: only SVG 
				if (coau.length==2 && chrg)
					coau += 'c';						// charge suffix
				coax = coacateg[coau] ? '[[Category:'+coacateg[coau]+']]' : '';
				return m;								//  +'--'+artaut+'--'+coau
			})
	//	ecclasticals cannot be detected machinally
 		.replace(coac, '')	// remove user-categories, when found CoA-user (by the table "coacateg")
 		.replace(coax, '')	// remove user-categories, when found CoA-user (by the table "coacateg")
 		
 		.replace(/\[\[Category:U:Vale93b\]\]/, '')				// remove test cat
		.replace(/\[\[Category:Created with Heraldicon\]\]/, '')		// Ashoppio
	.replace(/\{\{Created with\|logo=Heraldicon logo.svg\|toolcat=Heraldicon\|text=created with Heraldicon\}\}/, '') // Ashoppio		
 		.replace(/\|(source *= *\{\{)F\|/, '|$1Based|')					// ed_wdh 
	.replace(/\| *source( *= *)(?:\{?\{?(?:[Oo]wn based|{Oo]wn|[Bb]ased|[Bb]ased on)\}?\}?)? *(\[?https?)/, '|source$1{{Using}} $2')	// ed_wdh change template w/o parm
		.replace(/[Oo]wn work by (BKchem|ChemDraw|Chemtool|ChemSketch|GChemPaint|N2S)/, '{{Using}} $1')	// + tools
		.replace(/\n?trabajo propio \(own work\)/, '{{Own}}')			// ed_wdh (Weinelt & others)
		.replace(/\{\{[Oo]wn work\}\}/, '{{Own}}')						// ed_wdh normalize
		.replace(/\{\{Own\}\}, à partir de \{\{F\|/, '{{Using|')		// ed_wdh
		.replace(/[Oo]wn work using/, '{{Using}}')						// ed_wdh
	.replace(/(?:\{\{U\|Bratwurst mit Sauerkraut\}\},)? ?[Mm]anuell vektorisiert basierend auf https/, '{{Using}} https') //
		.replace(/Based on \{\{F\|/ig, '{{Based|')						// ed_wdh
		.replace(/\{\{(?:Own )?Based\}\}\n? *\*?\{\{F\|/igm, '{{Based|')	// ed_wdh
		.replace(/\{\{[Oo]wn\}\}\n?•? ?\{\{Based/, '{{Based')			// ed_wdh (TUBS)
		.replace(/Own work by uploader \(based on (\[[^\]]+\])\)/,'{{Based}} $1')		// ed_wdh
		.replace(/(\| *source *= *)\{\{[Oo]wn based/, '$1{{Based')		// ed_wdh simplify template 
		.replace(/\}\}==(\s*)\{\{(\s*)int:/, '}}\n==$1{{$2int:')		// int-license
		.replace(/\{\{es\|(?:1=)? *''\[?-\]?''\}\}/g, '')		// Erlenmeyer
	.replace(/\{\{Information *\n\|description=\{\{fr\|1=\{\{COAInformation/m, '{{COAInformation')
	//	.replace(/\}\n\}{4}\n\|date/, '}×\n|date'	)	//  Thom.lanaud
		.replace(/\|[Dd]ate( *= *)(\d\d)[.-/](\d\d)[.-/](\d\d\d\d)/, '|date$1$4-$3-$2')	//  //ed_wdh Date (by Syryatsu and others)
		.replace(/\|[Dd]ate( *= *)(\d?\d)[.-](\d?\d)[.-](\d\d\d\d)/, '|date$1$4-$3-$2')	//  //ed_wdh Date (by Syryatsu and others)
 		.replace(/(\d+x\d+)x +(\(\d+ Bytes\))/g, '$1 $2')								// ed_wdh  superfluous "x"
		.replace(/(\d{2,6}[×x]\d{2,5})[×x][08]?( \()/g, '$1$2')							//superfluous ×
		.replace(/\{\{Upload date\|(\d{4}\-\d{2}\-\d{2})\}\}/, '$1' )					// ed_wdh simplify 
	.replace (/( *\| *[Dd]ate *= *\d{4}-\d{1,2}-\d{1,2}) *(?:\d{2}:\d{2}(?::\d{2})?)?(?: *\(?(?:UTC *[\+-]?\d?d?)?\)?)?/, '$1')	// ed_wdh: clock does NOT belong to date
	.replace (/( *\| *[Dd]ate *= *\d{4}-\d{1,2}-\d{1,2})approval\)/, '$1 (approval)')// restore
		.replace(/\[\[Category:Blason par Syryatsu\]\]\s*/, '')						// ed_wdh  remove / uncat 
	//	.replace(/\[\[Category:Files by User:Jimmy from fr\.wikipedia\]\]\s*/, '')		// ed_wdh  remove / uncat 
//		.replace(/\[\[Category:COA by SteveK\]\] *\n?/, function (m)
//			{ if (coai && /image *= *SK/.test(text))
//				return '';								// COAInformation only !!!		remove / uncat 
//			  else
//				return m;
//			})
		.replace(/\[\[Category:Valid SVG created with Adobe Illustrator\]\]\s*/, '')	// ed_wdh  remove / uncat (Molgreen)[[
		.replace(/\{\{[Cc]dang\}\} *\n{0,2}/, '')										// ed_wdh  remove / uncat			
		.replace(/\|[Aa]uthor=Syryatsu/, '|author={{U|Syryatsu}}')						// ed_wdh lf got missed (after Blason) ⚡
		.replace(/ *\|permission *= *GFDL & BSD & GPL(?: & LGPL)?(?: & CC-by-SA)?(?: & CC)?\s*\n/m, ' ')	// ed_wdh  remove nonsense (chess)
		.replace(/ *\|permission *= *(?:Hagar66, )(?:Commonist)\s*\n/m, ' ')			// ed_wdh  remove nonsense 
		.replace(/\|permission=\|other fields=/, '|other fields')						//Breeze icons
		.replace(/ *\| *[Pp]ermission *= *(cc-by-sa-2.5|self) *\n/, '')					// nonsense comment
		.replace(/ *\| *[Pp]ermission *= *Own work, all rights released \(Public domain\) *\n/, '')	// Stannered
		.replace(/ *\| *[Pp]ermission *= *[Cr]reated with Heraldicon *\n/, '')			// Ashoppio
 		.replace(/\|permission=libre de droits/, '')									// Aubisse 
		.replace(/\[\[\Category:Created with DrawShield\]\]\s*/, '')					// ed_wdh  remove / uncat (Radicuil)
		.replace(/\{\{(?:Created with )?[Ii]nkscape\}\}\s*/, '')					// ed_wdh  remove / uncat (Radicuil)
		.replace(/\|source=\s*\n\{\{own\}\}/, '|source={{own}}')						// useless lf
		.replace(/\{\{self\|author=\{\{U\|Hagar66\}\}\|GFDL\|cc\-by\-3\.0,2\.5,2\.0,1\.0\|cc\-by\-sa\-3\.0,2\.5,2\.0,1\.0\}\}\n *\{\{self\|author=\{\{U\|Hagar66\}\}\|Cc\-zero\|PD\-self\|FAL\|Attribution\}\}/m, '{{self|author={{U|Hagar66}}|GFDL|Cc-zero|cc-by-3.0,2.5,2.0,1.0|cc-by-sa-3.0,2.5,2.0,1.0|FAL|Attribution}}' )
		.replace(/\{\{self\|author=\{\{U\|TUBS\}\}\|GFDL\|cc\-by\-3\.0,2\.5,2\.0,1\.0\|cc\-by\-sa\-3\.0,2\.5,2\.0,1\.0\}\}\n *\{\{self\|author=\{\{U\|TUBS\}\}\|Cc\-zero\|PD\-self\|FAL\|Attribution\}\}/m, '{{self|author={{U|TUBS}}|GFDL|Cc-zero|PD-self|cc-by-3.0,2.5,2.0,1.0|cc-by-sa-3.0,2.5,2.0,1.0|FAL|Attribution}}' )		
		.replace(/\{\{Cols\|\{\{Attrib([^\}]+)\}\}\n*(?:\|&nbsp;){0,5}\}\}\n*/, '{{Attrib$1}}\n')	// TUBS
		.replace(/ *\|permission=released (?:in)?(?:to)? the public domain\s*\n/im, '')	// ed_wdh  remove nonsense |
		.replace(/ *\{\{COA\}\}\n/, '')												// ed_wdh  remove COA
		.replace(/\{\{BKchem\|errnum=[^\n]+\n/, '')			// remove old tag
		.replace(/\|r=-🎨-\|/, '|')						// remove useless "r=-"
		.replace(/<sup>\{\{Ut?\|[^\|]*\|talk\}\}<\/sup>/, '')		// remove
		.replace(/\}\} \|source      =/, '}}\n |source      =')   // ed_wdh (nl)
		.replace(/(\| *)imgen( *=)/, '$1image$2')		// ed_wdh imgen/image
		.replace(/\| *image( *= *[^\n]*\n)\|other fields *= *\{\{Igen\|Im?\|\+\|s=c\}\} *\n/, '|image$1')	// ed_wdh remove new igen
//		.replace(/\}\} \|image       =/g, '}}\n |image       =')	// ed_wdh (nl)
		.replace(/\{\{U\|juergenk59[^}]*\}\}/g, '{{U|Jürgen Krause}}')		// ed_wdh user:JK ✨
		.replace(/Lokal Profil\}\}/ig, 'Lokal_Profil}}')			// ed_wdh user:Lokal_Profil (| & by=)
		.replace(/\[\[Prussia\|Preußischen\]\]/, '{{Wd|Preussen|preußischen}}')	// Liuzzo
		.replace(/\[\[Prussia\|Prussian\]\]/, '{{W|Prussia}}n')					// Liuzzo
		.replace(/\{\{[Uu][tc]\|Gliwi\}\}/g, '{{U|Gliwi}}')						// ed_wdh user:Duscha
		.replace(/\{\{[Uu]t\|Chris die Seele\}\}/g, '{{U|Chris die Seele}}')	// ed_wdh user:Duscha
		.replace(/\{\{U\|Cburnett\|\|en\}\}/, '{{U|Cburnett}}')					// ed_wdh Chess commonsuser:Cburnett/
		.replace(/\{\{U\|Rfc1394\|\|en\}\}/, '{{U|Rfc1394}}')					// ed_wdh Chess commonsuser:Rfc1394
		.replace(/\{\{U\|([^\|]*)\|\|en\|\|en\}\}/, '{{U|$1||en}}')					// ed_wdh comes from  [[:w:en:User:...]]
		.replace(/\{\{\{Ut\|MarianSigler\|bla\}\}\}/, '({{Ut|MarianSigler|bla}})')	// ed_wdh 
		.replace(/\|permission=I release all rights to the public domain *\n/, '')	// ed_wdh
		.replace(/\|s=chesspi/ig, '|s:=chess pi')								// ed_wdh Chess piece
		.replace(/\|s=OOUI\}\}/ig, '|su:=OOUI icon}}')							// ed_wdh OOUI
		.replace(/\|description=\{\{en\| /, '|description={{en|')				// ed_wdh user:Cburnett space
		.replace(/\{\{AutVec(.+)\{\{U\|Gliwi\}\}/, '{{AutVec$1Gliwi')			// ed_wdh user:Duscha
		.replace(/Original work of the Czech Government - public domain/, '')	// Tomáš Urban										// empty editor= (retouched)
		.replace(/\|s=\|s:=/, '|s:=')				// remove superfluous |s=	// ed_wdh (military symbols)
		.replace(/\{\{Igen\|T\|([1-9][^:]+):=Military map symbol/, '{{Igen|u|$1:=Military map symbol')	// inval textedit
		.replace(/\|other versions *= *\{\{other\|([\w| ]+)\.png\}\}/, '|other versions = {{G|$1.png}}')	// CS
		.replace(/= ?'''(Trabajo prop[^']+)'''/, '=$1')		// Sajor
		.replace(/\|editor=\|/, '|')											// empty editor= (retouched)
		.replace(/<span style="background-color: #008000;">/, '<span style="background-color:#008000;color:#FFF">')		// SanoAK
		.replace(/<span style="background-color: #008000;">/, '<span style="background-color:#008000;color:#FFF">')		// SanoAK 2nd
		.replace(/<span style="background-color: #008000;">/, '<span style="background-color:#008000;color:#FFF">')		// SanoAK 3rd
//		.replace(/\{Igen\|U\|1000\|\+(?:\|>)?|\s=m\|w=Natural Earth map(?:\|>)?\}/, '{Igen|NE|1000|+|>|s=m}')			// SanoAK Natural Earth
		.replace(/\{Igen\|U\|1000\|\+\|>\|s=m\|w=Natural Earth map\}/, '{Igen|NE|1000|+|>|w=m}')			// SanoAK Natural Earth
		.replace(/\{Igen\|U\|1000\|\+\|s=m\|w=Natural Earth map\|>\}/, '{Igen|NE|1000|+|>|w=m}')			// SanoAK Natural Earth
//		.replace(/\|image          = ?/, '|image       =')	// COAInformation (& Ibox)
		.replace(/description\t=/, 'description =')		// ed_wdh Togopic
		.replace(/\{\{de\|1=Siehe Dateiname, Grafikinhalt und \{\{Ud\|Summer ... hier!\/COVID-19-Pandemie\}\}\}\}/, '{{F|F}}')
		.replace(/\{\{en\|1=Arms of/, '{{en| of')		// COAInf
		.replace(/ Varialben /, ' Variablen ')			///	Summer ... hier!
		.replace(/ ebendalls /, ' ebenfalls ')			//
		.replace(/uperced/g, 'upersed')					//		
		.replace(/\|date\t\t=/, '|date =')				// ed_wdh Togopic
		.replace(/\|date =( ?20\d\d)(\d\d)/, '|date =$1-$2')	// ed_wdh Togopic
		.replace(/Tax ID:-\n/, '')						// ed_wdh Togopic
//		.replace(/desc\}\} ==\n\n{{Info/, 'desc}} ==\n{{Info')	// ed_wdh Togopic
		.replace(/\n\n/, '\n')							// ed_wdh Togopic
	//	.replace(/\|(ci|lh|nu|ov|re|tr|up)=([\|\}])/g, '|$1$2')	// ed_wdh do re mi fa so la
//👎		.replace(/\[\[hdbg\|(\d+)\]\]/, '{{hdbg|$1}}')					// ed_wdh hdbg
		.replace(/\[\{\{hdbg\|(\d+)\}\}\]/, '{{hdbg|$1}}')				// ed_wdh hdbg
//👎		.replace(/rce=\[\[hdbg\|(\d+)\n/, 'rce={{own}}\n|other fields 1={{hdbg|$1|+}}')		//temp hdbg
//👎		.replace(/rce=\[\[hdbg\|(\d+)\]\]\n/, 'rce={{own}}\n|other fields 1={{hdbg|$1|+}}')	//temp hdbg
//👎		.replace(/\{\{hdbg\|(\d)(\d)(\d\d)(\d\d\d)\}\}/, '{{hdbg| $1 $2 $3 $4}}')	//ed_wdh: edit|blazon of  = {{en| 
//👎		.replace(/\{\{hdbg\|(\d\d)(\d)(\d\d)(\d\d\d)\}\}/, '{{hdbg|$1 $2 $3 $4}}')	//ed_wdh: edit
//👎		.replace(/\[\[Category:Coats of arms from Haus der Bayerischen Geschichte\]\] */, '')	//ed_wdh: edit
//👎		.replace(/references( *= *)\[*http:\/\/www\.hdbg\.eu\/gemeinden\/web\/index.php\/detail\?rschl=(\d+)\]*/, 'references$1{{$2}}')
		.replace(/\|( ?blazon of| ?element ) *= *\n( *\{\{(?:[^\n]*?)\n *\|)/m, '|$1      = $2')	// remove linefeed (when just one)
	//	.replace(/\| ?blazon of *= *\n *\{\{/m, '|blazon of      = {{')	// remove linefeed 
		.replace(/ \|blazon of *= *\n *\|/m, ' |blazon of  = {{F|F}}\n |')	// Tomas.urban 
		.replace(/\{\{coa blazon\}\}/, '')				// coablazon (Ashoppio)
		.replace(/\{\{Coa blazon\}\}/, '{{Coablazon}}')	// coablazon
 		.replace(/\n *\w{3,6} *\|image/, '\n |image')	// ed_wdh (no explanation for that strange error)^📞
	//	.replace(/(\| ?blazon of *= *\{\{[a-z]{2,3}\|) /, '$1')			// remove superfluous space  {{fr|1=}}
			.replace(/references *= *\n/,  function (m)		// COAInformation only !!!
				{ if (/\{\{fr\|1=Armorial Général\}\}/.test(text))
					return 'references = {{fr|Armorial Général}}\n';
				  else
				  if (/\{\{fr\|1=Armorial du Gévaudan du vicomte de Lescure\}\}/.test(text))
					return 'references = {{fr|Armorial du Gévaudan du vicomte de Lescure}}\n';
				  else
				  if (coab)
					return 'references = {{coablazon}}\n';		// coablazon (Ashoppio)
				  else
					return m;
				}) 
			.replace(/ *\{\{fr\|1=Armorial Général\}\} */, ' ') // COAInformation only !!!
			.replace(/ *\{\{fr\|1=Armorial du Gévaudan du vicomte de Lescure\}\} */, ' ') // COAInformation only !!!
		
			.replace(/({\{[a-z]{2,3}\|) /g, function (m, nat)
			{ if (coai)
				return nat;	      // COAInformation only: remove all superfluous spaces
			  else
				return m;
			})
		.replace(/\{\{[a-z]{2,3}\|(\{\{[a-z]{2,3}\|)/g, function (m, nat)
			{ if (coai)
				return nat;	      // COAInformation only: remove first one of double [Lokal_Profile]
			  else
				return m;
			})
		.replace(/ *\| *source *=/, function (m)
			{ if (coai && !tinc)			// COAInformation only !!!  
				return ' |tincture    =\n' + m;		// insert
			  else
				return m;
			}) 
		.replace(/fields(?: *= *\{\{Igen)/,  function (m)
			{ if (coai)
				return 'image      = {{ Igen';		// COAInformation only !!!
			  else
				return m;
			}) 
		.replace(/ *\| *source *=/, function (m)
			{ if (coai && !imag)			// COAInformation only !!!		 && isvg
				return ' |image     =\n' + m;		// insert (esp. for PNG ?)
			  else
				return m;
			}) 
 		.replace(/ \|image     =\n/, function (m)	// empty "image"  ?
			{ if (coai && !isvg)			// COAInformation only !!! 
				return ' |image     = {{Igen|U|+|s=c}}\n';		// replace (only for PNG ! )
			  else
				return m;
			}) 
 
		.replace(/ *\| *description *= *\n/m, function (m)
			{ if (coai)								// COAInformation only !!!
				return '';							// remove empty description
			  else
				return m;
			}) 
		.replace(/( ?\| *)(?:[Oo]ther[ _])?versions *=( *)/, function (m, ind, tr)
			{ if (coai)
				return ind + 'versions       =' + tr;	// COAInformation only !!!
			  else
				return m;
			})
		.replace(/ *\| *(?:other )?versions *= *\n? *\{\{insignia\}\} *\n/, ''	)	// Ashoppio
		.replace(/der\}\} ==\n\{\{self/, 'der}} ==\n'+coas+'{\{self')	// Ashoppio et al.:license
		.replace(/\| *[Pp]ermission *= *\{\{[Aa]ttrib/, '{{Attrib')		// (Ashoppio et al.:attrib
		.replace(/( ?\| *)(?:[Oo]ther[ _])?fields *= */g, function (m, ind)
			{ if (coai)
				return ind + 'fields         =';	// COAInformation only !!!
			  else
				return m;
			})                    //     fields         =
		.replace(/\{\{([a-z]{2,3}) ?\| ?1 ?= */g, function (m, land) // only temporar
			{ if (coai)
				return '{{'+land+'| ';		// COAInformation only !!!
			  else
				return m;
			}) 
		.replace(/(?:\|fields *= ?)?\{\{Information field\|SVG\| *\n\}\}\n?/, '')
		.replace(/\{\{fr\|Armorial Général\}\} \{\{Own\}\}/, '{{Own}}')	// 2nd try	
		.replace(/\{\{fr\|Armorial du Gévaudan du vicomte de Lescure\}\} \{\{Own\}\}/, '{{Own}}')	// 2nd try	
		.replace(/\{\{Information field\|SVG\|\n\}\}/, '')			//	🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻
	// Validation:             tttttttt     eeeeee >>>>>>>>>>> 
		.replace(/= ?\{\{Igen\|([^\|]+)(?:\|(\d+))?[^\}]*\}\}/, function (m, tool, err='' )
			{  if (coai)
				{	tool = (tool === 'Sodipodi') ? 'Inkscape' : tool;
					tool = (tool === 'Inkscape') ? 'I' : tool;
					tool = (tool === 'Adobe'   ) ? 'A' : tool;
	//				if (tool.slice(-6) === '|s=c}}')
	//					tool = tool.slice(0,-6)+'<';	// ---test---
					let retv = (coau !== '') ? coau : tool;
					err = err.trim();					//	test
	//				if (err.slice(-6) === "|s=c}}")
	//					err = err.slice(0,-6);			// ---test---
	//				if (err.slice(-5,-2) === 's=c')
	//					err = err.slice(0,-6)+'2';		// ---test---
	//				if (err.slice(-5,-4) === 's')
	//					err = err.slice(0,-6)+'4';		// ---test---
					if (err !== '')
						retv +=  '/' + err;				// with  '/err' <<<<<<<<<<<<<
	//				if (retv.slice(-2) === '}}')
	//					retv = retv.slice(0,-2)+'..';	// ---test---
					return '= ' + retv;			// shortened [+/err]
				}
				else	
					return m;
			}) 
		.replace (/\|s=c\}\}\n/, function (m)			// image topic 
			{	if (coai)
					return '\n';
				else	
					return m;
			}) 
/*
	// Validation:             tttttttt     eeeeee     + >>>>>>>>>>>	🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺
		.replace(/= ?\{\{Igen\|([^\|]+)(?:\|(\d+))?\|?\+[^\}]*\}\}/, function (m, tool, err='' )
			{  if (coai)
				{	tool = (tool === 'Sodipodi') ? 'Inkscape' : tool;
					tool = (tool === 'Inkscape') ? 'I' : tool;
					tool = (tool === 'Adobe'   ) ? 'A' : tool;
					let retv = (coau !== '') ? coau : tool;
					if (err !== '')
						retv +=  '/' + err;			// with  '/err'
					return '= ' + retv;			// shortened [+/err]
				}
				else	
					return m;
			}) 		

	// Validation:             tttttttt     eeeeee     +     1111111111  >>>>>>>>>>> 
		.replace(/= ?\{\{Igen\|([^\|]+)(?:\|(\d+))?\|\?+(?:\|([^\|\}]*))?[^\}]*\}\}/, function (m, tool, err='', p1='' )
			{  if (coai)
				{	tool = (tool === 'Sodipodi') ? 'Inkscape' : tool;
					tool = (tool === 'Inkscape') ? 'I' : tool;
					tool = (tool === 'Adobe'   ) ? 'A' : tool;
					let retv = (coau !== '') ? coau : tool;
					let retz = retv + '/';			//
					if (err !== '')
					{	retv +=  '/' + err;			// with  '/err'
					 	retz = retv;
					}
					if (p1 === '')
						return '= ' + retv;			// shortened [+/err]
					else
					{//	retz += '//'+p1;
	//					return '= ' + retz; 		//   +/[err]
						return '= ' + retv; 		// 
					}
				}
				else	
					return m;
			}) 		


	// Validation:             tttttttt     eeeeee           11111111       22222222       33333333  
		.replace(/= ?\{\{Igen\|([^\|]+)(?:\|(\d+))?\|\+(?:\|([^\|]+?))?(?:\|([^\|]+?))?(?:\|([^\|]+?))?(?:\|s=ce?)?\}\}/, function (m, tool, err='', p1='', p2='', p3='' )
			{  if (coai)
				{	tool = (tool === 'Sodipodi') ? 'Inkscape' : tool;
					tool = (tool === 'Inkscape') ? 'I' : tool;
					tool = (tool === 'Adobe'   ) ? 'A' : tool;
					tool = (tool === 'D'       ) ? 'CorelDRAW' : tool;
					err  = (err.slice(-5) === 's=c}}') ? err.slice(0, -5) : err;
		//			if (tool !== 'I') 
		//				tool = 'U/tool~' + tool;		// other tools ?
					let retv = (coau !== '') ? coau : tool;
					let retz = retv + '/';			//
					if (err > ' ')
					{	retv +=  '/' + err;			// with  '/err'
					 	retz = retv;
				}

//	test	p1= '§' + p1 + '/' + p2 + '/' + p3 +'§';
					if (p1 === '')
						return '= ' + retv;			// shortened [+/err]
					else
					{	retz += '//'+p1;
						if (p2 !== '' )
						{	if (p2.slice(-6) === '|s=c}}')
								p2 = '^'+p2+'^';	// ^^^test^^^ 	
							retz += '/'+p2;
						}	
						retz = retz + '/3/' + p3;
						return '= ' + retz; 		//   +/[err]
					}
				}
				else 
//	test			return  ': ' + err + '/' + p1 + '/' + p2 + '/' + p3;			//
					return m;
			}) 
*/

/*		.replace(/ *\[\[Category:SVG coats of arms of France[^\]]*\]\] *\n?/, function (m)
			{ if (coai)								// COAInformation only !!!
				return '';							// remove categorizarion when gf
			  else
				return m;
			})
*/
 	 	.replace(/( *\| *image *= *[A-Za-z0-9]{2})( *[\}\n])/, function (m, p, t)	// PNG  ?
	 		{ if (coai && !isvg)			// COAInformation only !!! 
	 			return p+'r'+t;				// replace (only for PNG ! ) raster
	 		  else
	 			return m;
	 		}) 
		.replace(/\n  \|image/, '\n |image')	// ed_wdh (1 space, not 2)

 		.replace(/\[\[Category:CoA Lab-sv\]\]\ *\n?/, function (m)	// 
			{ if (coai)						// COAInformation only !!! 
				return '';					// remove
			  else
				return m;
			}) 
		.replace(/AttribSVG\|File:([^\|]+)\|User:/, 'Attrib|$1|')	// Sundin
//		.replace(/\|(?:image|fields) *=\{\{Igen\|([A-Z]{1,2})\|(\d{1,3})?\|?\+\|s=c\}\}/, 't1|image       =$1/$2')
//		.replace(/\|fields *=\{\{Igen/, 't2|image       ={{Igen')			// later 7 to 5 spaces
//		.replace(/  \|image /, ' t3|image ')
		.replace(/\}\}\|[Dd]ate=/, '}}\n|date=')						// ed_wdh lf got missed (after Blason) ⚡
//		.replace(/\{\{Igen\|A(\|\d+)?\|\+\|\$/, '{{Igen|A$$1|+')		//temp commonist
		.replace(/\{\{Igen\|A\|\+\|\$/, '{{Igen|A$|+')					//temp commonist
		.replace(/\{\{Igen\|A\|(\d+)\|\+\|\$/, '{{Igen|A$|$1|+')		//temp commonist
			.replace(/\[\[File:([^\|]+)\|1\d\dpx\]\]/g, '{{G|$1}}')			//change to gallery
			.replace(/\[\[File:([^\|]+)\|50px]\]/g, '{{F|$1||48}}')			//change the display
			.replace(/\|display=(?:50|50px)/g, '|d=48')						//change the display
			.replace(/\|display=100(?:px)?/g, '|d=96')						//change the display
//		.replace(/sd=\(US\)/, 'sd=&#x20;(US)')							//edit subcategory
			.replace(/other versions=\n\{\{G\|\n/, 'other versions={{G|\n')		// gallery
	.replace(/(\{\{[Igen|Image generation][^\}]+\}\} *\n)(\{\{Credit line \|Author = © Henry Salomé[^\}]+\}\} *\n)/m, '\n$2$1')	//other fields
	.replace(/( *artist *= *\{\{U\|Henrysalome\|[ℍenry|&#x210d;enry Salomé]\}\}) *\(?\d\d\/\d\d\/\d+\)? *(\|source *= *\{\{Own\}\}) */, '$1💄\n $2ℍ')
	.replace(/( *artist *= *\{\{U\|Henrysalome\|&#x210d;enry Salomé\}\}) *\(?\d\d\/\d\d\/\d+\)? *(\|source *= *\{\{Own\}\}) */, '$1⛉\n $2ℍ')
	.replace(/( *artist *= *\{\{U\|Henrysalome\|ℍenry\}\}) *\(?\d\d\/\d\d\/\d+\)? *(\|source *= *\{\{Own\}\}) */, '$1🍷\n $2ℍ')
	.replace(/( *artist *= *\{\{U\|Henry Salomé\}\}) *(\|source *= *\{\{Own\}\}) */, '$1⛊\n $2ℍ')		// ⛊⛊
	.replace(/ℍ\n ?\|other fields *= *\{\{Igen\|[I|Im]\|\+\|gf\|s=c\}\}/m, 'ℍ\n |image = HS')				// ???
	.replace(/ℍ\n ?\|other fields *= *\{\{Igen\|Im?(?:\|(\d+))\|\+\|gf\|s=c\}\}/, '\n |image=HS/$1')		//  ??
	.replace(/other fields=\{\{Igen\|Im\|\+\|gf\|s=c\}\}/gm, 'image = HS📞')			//Henrysalome
	.replace(/\|author=\{\{U\|Chatsam\}\}\n\|other fields=\{\{Igen\|I[mA]?(\|\d+)?\|\+\|s=c/m, '|author={{U|Chatsam}}\n|other fields={{Igenc|Ch$1')	// Chatsam
	.replace(/\n\[\[Category:Blason chatsam\]\]/m, '')	// Chatsam
	.replace(/\s*\|\s*[Pp]ermission\s*=\s*(\}\})?$/gm, '$1')		// ed_wdh (empty param)
	.replace(/\{\{W\|Suisse\}\}/g, '{{Wf|Suisse}}')		// ed_wdh +Aiwe+
	.replace(/\}\|permission=/, '}\n|permission=')		// ed_wdh (+nl)
	.replace(/\|permission= \{\{Insignia\}\}/, '|permission={{Insignia}}')	// ed_wdh
	.replace(/\{\{fr\|(?:1=)?\{\{Own(?: work)?\}\}\}\}/, '{{Own}}')			// Jimmy44
	.replace(/ \(\{\{Ut\|Jimmy44\|<span class="signature\-talk">talk<\/span>\}\}\) [^\|]+\|source/, '\n |source')
		.replace(/ +\(\{\{Ut\|(?:[^\|]+)\|<span class="signature-talk">[^>]+>\}\}\)/, '')	// untalk (summer)
		.replace(/1=Siehe Dateiname und Grafikinhalt/, '{{F|F}}')	// summer hier
		.replace(/see file ?name/i, '{{F|F}}')								// ed_wdh: Rafael Zink
//		.replace(/\|author=\{\{U\|Erlenmeyer\}\}(\}\})? ?\|im/, '$1\n |author     = {{U|Erlenmeyer}}\n |im')
		.replace(/\}\}\}\} ?\|imag/, '}}}}\n |imag')		// Erlenmeyer" + lf

//		.replace(/\}\}\/\{\{W\|/, '}} / {{W|')								// singasong signatures
		.replace(/<br>\nVectorized/, '\nVectorized')						// singasong signatures
		.replace(/=Derived from\: \[http/, '={{Derived from}} [http')		// singasong signatures
		.replace(/\|display=150\}/, '|d=144|o=b}')							// singasong signatures
////	.replace(/\|author=\{\{W\|([^\n]*)\n\|/, '|author={{AutVec|{{W|$1|{{U|SINGmeAsadSONG}}||w}}\n|')// **temp** singasong signatures 
//		.replace(/\|author( *)=([^\n]*)\nVectorized by: ([^\n]*)/, '|author$1={{AutVec|$2|$3||w}}')	// singasong signatures
//		.replace(/\{\{W\|Rock and Roll Hall of Fame\|Rock & Roll/, '{{W|Rock & Roll')	// singasong signatures
		.replace(/\|author( *= *){\{[Uu]ser\|/, '|author$1{{U|')						// change template
		.replace(/\{\{(U|Ut|Uw|Uwt|Un|Uc)\|([^\}]+)\}\} +\d\d:\d\d, +(?:[^\(]+)\(UTC\)/, '{{$1|$2}}')	// remove authors timestamp 
		.replace(/\{\{own work\}\}<br\/?><small>(\{\{Based\|[^\}]+\}\})<\/small>/, '$1')// ed_wdh: Kuru 
		.replace(/\{\{own work\}\}<br\/?><small>based on /, '{{Based}} <small>')		// ed_wdh: Kuru 
		
		.replace(/\{\{Information field\|Name=Remarks\|Value=/, '{{InFi|Remarks|')		// ed_wdh: Marsupilami (shorter)
		.replace(/\|source( *= *)(Self-drawn using)/, '|source$1{{Own using}}')			// ed_wdh: insert template
		.replace(/\{\{[Oo]wn work\}\} *[Mm]ade with/, '{{Own using}}')					// ed_wdh: change template
		.replace(/\{\{Igen\|Wpdc\|(\d+\|)?\+\|s=[d\s]?\}\}/, '{{Igen|Wpdc|$1+}}')		// ed_wdh: remove empty topic "d"
		.replace(/\{\{U\|GoldRingChip\}\}\|other versions = *\n/, '')					// remove WPDC nonsense
		.replace(/\{\{Igen\|ArcMap\|(\d+\|)?\+\|s=[m\s]?\}\}/, '{{Igen|ArcMap|$1+}}')	// ed_wdh: remove empty topic "m"
		.replace(/\{\{Igen\|Qt\|(\d+\|)?\+\|s=[m\s]?\}\}/, '{{Igen|Qt|$1+}}')			// ed_wdh: remove empty topic "m"
		.replace(/\{\{Igen\|G\|(\d+\|)?\+(\|%)?\|s=[\w\s]?\}\}/, '{{Igen|G|$1+$2| }}')	// ed_wdh: remove empty topic from new Gnuplot 
		.replace(/\|Perl\|/, '|perl|')													// ed_wdh: tool error
		.replace(/\}\} by \{\{Ut?\|/, '|by=')	// ed_wdh: by when only one " " inbetween
	.replace(/\{\{Based\|Soccer field \- empty\.svg\|by=Nuno Tavares\|user\:Nuno Tavares\|en\}\}/g, '{{F|Soccer field - empty.svg|by=Nuno Tavares}}') 
	.replace(/\{\{Based\|Soccer_field_\-_empty\.svg\|by=Nuno Tavares\|user\:Nuno Tavares\|en\}\}/g, '{{F|Soccer field - empty.svg|by=Nuno Tavares}}') // ?
	.replace(/\{\{F\|Empty_soccer_line-up\.svg\}\} by \{\{U\|NielsF\|user\:NielsF\|en\}\}/g, '{{F|Empty soccer line-up.svg|by=NielsF}}')
		.replace(/[Ii]mage:Soccer field \- empty\.svg by user:Nuno Tavares/g, '{{F|Soccer field - empty.svg|by=Nuno Tavares}}') 
		.replace(/[Ii]mage:Empty[ _]soccer[ _]line-up\.svg by user:NielsF/g, '{{F|Empty soccer line-up.svg|by=NielsF}}')
	.replace(/ *Own work, pitch \{\{F\|Soccer field \- empty\.svg\|by=Nuno Tavares\}\} and \{\{F\|Empty soccer line\-up\.svg\|by=NielsF\}\}\.\}\}\n\|date *= *([^\n]+?)\n\|source *= *\{\{own\}\}/gm, '}}\n|date=$1\n|source={{Based|Soccer field - empty.svg|by1=Nuno Tavares|Empty soccer line-up.svg|by2=NielsF}}') //
//	.replace(/\{\{Own\}\} \(\{\{original text\|1=Own work by uploader \(based on( [^\)]+)\)\|nobold=1\}\}\)/, '{{Based}}$1')
	.replace(/ *\(\{\{original text\|1=self-made\|nobold=1\}\}\)/, '')			// Own
	.replace(/Own work by uploader \(based on( \[[^\)]+)\)/, '{{Based}}$1')		// 
	.replace(/Own work by uploader;? *\(?based on( \[[^\]]+\])\)?/, '{{Based}}$1')		// 
		.replace(/(?:\[?https?:)?\/\/www\.hdbg\.eu\/gemeinden\/index\.php\/detail\?rschl=(\d{7})\]?/, '{{hdbg|$1}}')	// ed_wdh: hdbg

	// remove superfluous topic "sf"
// 		.replace(/\{\{(Igen|Image generation)\|(BKchem|B|ChemDraw|C|Chemtool|ChemSketch|GChemPaint|N2S)\|(\d+\|)?\+(\|%)?(\|<)?\|s=(?:sf)?\}\}/, '{{$1|$2|$3+$4$5}}')	// ed_wdh

		.replace(/\{\{U\|([^\}]+?)\}\|/g, '{{U|$1|🏮}}|')	// ed_wdh: unfixed ugu
	//	.replace(/G\|\+\|%\| \}\} *\n\}\}/, 'G|+|%|c1=\n')		// gnuplot
		.replace(/\.csv \] \|\| /g, '.csv ] {{!!}} ')			// gnuplot pipes

// change all structural fomulas to "FGD"  .csv ] || 

	// change all topic "sf"  tp 'FGD'     1         2         3      +               4           5       
		.replace(/\{\{(Igen|Image generation)\|([^\|]+)\|(?:(\d+)\|)?\+\|!=f\|u=([^\|]+)(?:\|s=(sf|eq))\}\}/, 	'{{File generation description\n | SVG tool = $2\n | W3C error count = $3\n | Fake SVG = yes\n | User = $4\n | Topic = $5}}')

	// change all topic "sf"  tp 'FGD'     1         2         3      +      4           5             6
		.replace(/\{\{(Igen|Image generation)\|([^\|]+)\|(?:(\d+)\|)?\+(?:\|(%)r?)?(?:\|(<))?(?:\|s=(sf|eq))\}\}/, 	'{{File generation description\n | SVG tool = $2\n | W3C error count = $3\n | Text embedded = $4\n | Text as path = $5\n | Topic = $6}}')
	// standardize; SVG tool
		.replace(/Topic = eq/, 'Topic = chemical equation')
		.replace(/Topic = sf/, 'Topic = structural formula')
		.replace(/^ *\|\s*W3C error count\s*= *\n( *[\|\}])/m, '$1')		// remove empty param 
		.replace(/Text embedded = % */, 'Text embedded = yes')				// convert to text
		.replace(/Text as path = < */, 'Text as path = yes')				// convert to text 
	.replace(/Text embedded = \n \| Text as path = \n/m, 'Text as path = yes\n')	// assume path 
	.replace(/embedded = yes\n \| Text as path = \n/m, 'embedded = yes\n')			// remove path 

		.replace(/SVG tool\s*=\s*(IA|A|A\$)\b/, 'SVG tool = Illustrator')	// TODO: diversificate
		.replace(/SVG tool\s*=\s*(Im|I)\b/, 'SVG tool = Inkscape')			// TODO: diversificate
		.replace(/SVG tool\s*=\s*D\b/, 'SVG tool = CorelDRAW')				// 
		.replace(/SVG tool\s*=\s*B\b/, 'SVG tool = BKchem')					// 
		.replace(/SVG tool\s*=\s*C\b/, 'SVG tool = ChemDraw')				// 
		.replace(/SVG tool\s*=\s*U\b/, 'SVG tool = Unknown tool')			// 
		.replace(/SVG tool\s*=\s*O\b/, 'SVG tool = Other tools')			//  or
//		.replace(/SVG tool\s*=\s*(O|U)\b/, 'SVG tool = Other tools | Othertool=')	// 
		.replace(/SVG tool\s*=\s*T\b/, 'SVG tool = Text Editor')			// 
			.replace(/\|t=c\|U=/g, '|')			// Brigante mandrogno

	/* --- now unwanted cleanup ---   ("Law of 9/22")
//		remove the "1=" from the Language templates when text is without "="
	//	1) plain text without equal sign		✓
	//	2) plain text with equal sign			メ
	//	3) text with templates - ignore "=" in template		✓
	//	4) any series of text and templates - ignore "=" in template but not in text	メ
	//     error - does not always find the final end
	.replace(/(\{\{[A-Za-z][a-z]{1,2}(?:-[a-z]{2,6})? ?\|) ?1 ?= ?([^=\}]+\})/g, '$1$2')	// remove when no '=' (simple case)
	.replace(/(\{\{[A-Za-z][a-z]{1,3}(?:-[a-z]{2,6})? ?\|) ?1 ?= ?(.+)\}\}/gm, function (m, temp, text) 
		{	let shrt = true;	
			let scnd = 0; 

	 		for (let i = 0; i < text.length; i++)
  				{	if ( text.substring(i, i+2) === "{{")  
					{	scnd++;
						i++;
					}
					if ( text.substring(i, i+2) === "))")  
					{	scnd--;
						if ( scnd < 0)				// out ?
							break;
						i++;
					}
					if ( text.substring(i, i+1) === "=")  
					{	if (scnd === 0)
						{	shrt = false;
							break;
						}
					}
				}
			if ( shrt )
				return temp + text + "}}";			//✓ remove "=" 
			else
				return m;							//メ			
		})
	*/ 

	//	replace the "click" template
		.replace(/\{\{[Cc]lick\|image=([^\|]+)\|link=([^\|]+)\|width=([^\|\}]+)\|(?:[^\}]+)\}\}/, '{{F|$1|Z|$3|link=$2}}')	// ed_wdh: remove click

		.replace(/\{\{G\|([^\}]+)\}\}/mg,  function (m, flst)		// gallery: remove namespaces
		{	let inpt = flst;
			let leng = flst.length;
			
			for (let i = 0; i < leng; i++)
			{	if ( inpt.substring(i, i+5) === "File:" || inpt.substring(i, i+5) === "file:")
					{	inpt = inpt.substring(0, i ) + inpt.substring(i+5, leng );
						leng = leng - 5;
					}
				if ( inpt.substring(i, i+6) === "Image:" || inpt.substring(i, i+6) === "image:")
					{	inpt = inpt.substring(0, i ) + inpt.substring(i+6, leng );
						leng = leng - 6;
					}
			}
			return "{{G|" + inpt + "}}";
		})

		.replace(/\{\{Igen\|Graphviz\|\+\|%\|s=\}\}/, '{{Igen|Graphviz|+|%}}')		// ed_wdh: remove empty topic
			.replace(/\{\{[Oo]wn\}\}; using/, '{{Own using}}')			// ed_wdh: combine
	//		.replace(/\{\{U\|Impru20\|\|en\}\}/g, '{{U|Impru20}}')		// ed_wdh: remove en User  + |sub=/Impru
			.replace(/\{\{W\|User:Impru20\|Impru20\}\}/g, '{{U|Impru20}}')	// ed_wdh: Impru{{W|User:Impru20|Impru20}}	
			.replace(/ 185×/g, '185')			// ed_wdh: Impru
			.replace(/<nowiki>Fix\.<\/nowiki>/g, 'Fix.')			// ed_wdh: Impru
 			
	//  finish here the Graphic lab templates 💈💈💈💈
			.replace(/\{\{\w+werkstatt[^\|\n\}]*\}\}/g, '|gd')					// ed_wdh Karten/Wappen/Fahnen/Foto/Grafik/
			.replace(/\{\{[Tt]aller[\s_\w]+Cartografía[^\|\n\}]*\}\}/, '|ge')	// ed_wdh 
			.replace(/\{\{[Tt]HV[^\|\n\}]*\}\}/, '|ge')							// ed_wdh 
			.replace(/\{\{[Aa]telier[ _]graphique[^\|\n\}]*\}\}/, '|gf')		// ed_wdh 
			.replace(/\{\{[Pp]rojet[ _]Blasons\}\}/, '|gf')						// ed_wdh 
			.replace(/\{\{[Ll]aboratorio[ _]grafico\|?[^\|\n\}]*\}\}/, '|gi')	// ed_wdh 
			  .replace(/\{\{[Gg]raphic[ _][Ll]ab[^\|\n\}]*\}\}/, '|gl')			// ed_wdh ???
			.replace(/\{\{[Ww]PGW[ _]vector[^\|\n\}]+\}\}/, '|gP')				// ed_wdh 
			.replace(/\|T=\|/, '|')												// ed_wdh empty "T"
			.replace(/[Ii]gen\/Nil/, 'Igen/nil')								// ed_wdh change
 		
		.replace (/([Ss]ource *= *)(?:\{\{[Oo]wn\}\});? *\/? *\[?(http)/, '$1{{Using}} $2')	// ed_wdh	using when http
//			.replace(/(\{\{\w\w\w?\|)1=/g, '$1')	// ed_edh language (take care for equation sign within text)
//			.replace(/(\{\{\w\w\w?\|)([^=\}]+=)/g, '$11=$2')	// ed_wdh restore the '='

	//	.replace(/\{\{de\|Wappen von ([\w ÄäÖöÜüß]+)\}\}/, '{{de|Wappen von {{Wd|$1}}}}')				//ed_wdh Gimli
	//	.replace(/\{\{en\|Coat of Arms of ([\w ÄäÖöÜüß]+)\}\}/, '{{en|Coat of Arms of {{W|$1}}}}')	//ed_wdh Gimli
	//	.replace(/=Gliwi\n\|other fields=\{\{Igen\|Im?\|\+\|s=c/m, '={{U|Gliwi}}\n|other fields={{Igenc|Gl')		//ed_wdh Gimli
	//	.replace(/Gliwi\n\|other fields=\{\{Igen\|Im?\|\+\|%\|s=c/m, '{{U|Gliwi}}\n|other fields={{Igenc|Gl|||%w')	//ed_wdh Gimli
	//	.replace(/\[\[Category:Coats of arms from Haus der Bayerischen Geschichte\]\]\s*/, '')		// ed_wdh  remove / uncat
	.replace(/\|artist *= *\*? *[Uu]nknown\n\n? *\* *vectorized by \[\[Gl(?:iwi)?\]\]/, '|artist  = {{AutVec||Gliwi}}') // ed_wdh
	.replace(/\|artist *= *\*? *\{\{[Uu]nknown(?:\|artist)?\}\}\n\n? *\* *vectorized by \[\[Gl(?:iwi)?\]\]/, '|artist      = {{AutVec||Gliwi}}') // ed_wdh
	.replace(/\|artist *= *\*? *[\{\{[Uu]nknown\}\}|[Uu]nknown] *\n\n? *\* *\{\{author\|Vectorization\|\{\{U\|Gliwi\}\}\}\}/, '|artist      = {{AutVec||Gliwi}}') // ed_wdh
	.replace(/\|artist *= *\*? *\{\{[Uu]nknown(?:\|artist)?\}\} *\n\n? *\* *\{\{author\|Vectorization\|\{\{U\|Gliwi\}\}\}\}/, '|artist      = {{AutVec||Gliwi}}') // ed_wdh
		.replace(/\|source=\{\{Based\}\}\n\*\{\{F\|Municipalities_in_Bavaria\.svg(?:\}\} by \{\{U\||\|by=)TUBS\}\}\n\*\{\{F\|Bavaria AM\.svg\|Bavaria_AM\.svg(?:\}\} by \{\{U\||\|by=)TUBS\}\}/m, '|source={{Based|Municipalities in Bavaria.svg|Bavaria AM.svg|by=TUBS}}')	// ed_wdh: Hagar66 maps
	.replace(/\{\{Cols\|\{\{Attribs\|t=c *\n *\|Bavaria location map.svg\|TUBS\}\} *\n\|&nbsp;\|&nbsp;\|&nbsp;\|&nbsp;\}\}/m, '{{Attrib|t=m|Bavaria location map.svg|TUBS}}') // ed_wdh
	.replace(/\[?http:\/\/www\.hdbg\.eu\/gemeinden\/web\/index\.php\/detail\?rschl=(\d+)\]?/, '{{hdbg|$1}}')	// ed_wdh	
	.replace(/\{\{U\|(ChemNerd|Deli nk|Edgeweyes|Gnome de plume|PCock|Slideshow Bob|TimBuck2)\}\} *\n/m, '{{U|Edgar181}}\n')	// ed_wdh sock puppets
	.replace(/\{\{U\|Edgar181\}\}\n\|permission=\{\{PD-self\}\}\n/m, '{{U|Edgar181}}\n|permission={{PD-chem}}\n')	// ed_wdh 2021-03-22
		.replace(/\|~\|s=lm\|u=NordNordWest/g, '|~|s=lm|u=TUBS')	// ed_wdh 
	.replace(/\{\{Attribs\|t=lm\n \|Usa edcp location map.svg\|Uwe Dedering\}\}/g, '{{Attrib|t=lm|Usa edcp location map.svg|Uwe Dedering}}')	// ed_wdh 
		.replace(/\{\{([Aa]ttribs?)\|t=([a-z]{1,3})\|t=\2/g, '{{$1|t=$2')		// correct double topic error (there are other errors !)

		.replace(/\|source=\{\{([^\}]+?)\}\}\n\{\{Attr([^\}]+?)\}\}\n\|date=([^\n]+?)\n/m, '|date=$3\n|source={{$1}}\n{{Attr$2}}\n')	// ed_wdh
		.replace(/\|T=\{\{tracedSVG\}\}\|/, '|§|')	// ed_wdh 
		.replace(/\{\{CH2MoveToCommons\|[-a-z]+.w[a-z]+\|year=\d+\|month=\w+\|day=\d\}\}\s*\n/, '')
//\\		.replace('The tool and the bot are operated by [[User:Jan Luca]] and [[User:Magnus Manske]].', '')
//\\		.replace('The upload bot is [[User:CommonsHelper2 Bot]] which is called by [http://toolserver.org/~commonshelper2/index.php CommonsHelper2].', '')
	.replace(/\{\{Using\}\} https?:\/\/commons\.wikimedia\.org\/wiki\/File:([^\n]+)/g, '{{Based|$1}}')
	.replace(/\{\{Based\|([^\|]+?)\|by=\}\} *\n\|author( *= *)\*?\{\{F\|\1\}\}:? *\{\{U\|([^\}]+?)\}\} *\/? *\{\{Derivative\}\}:? */, '{{Based|$1|by=$3}}\n|author$2')
	.replace(/\{\{Based\|([^\|]+)\|by=\}\}\n\|author=\*\{\{F\|\1\}\}: \{\{U\|([^\}]+)\}\} \/ \{\{Derivative\}\}: /, '{{Based|$1|by=$2}}\n|author=')		// ed_wdh

	// Finally shorten distance, with space before and after equal sign - COAInformation only !!!
	.replace(/([\w ]{10}) *= */g, function (m, pn)				// when 12/15 chars inbetween |…=
		{ if (coai && /\|blazon of      =/.test(text) || /\|blazon of   =/.test(text)) 
			return pn + addb + ' = ';			// COAInformation only !!!
		  else
			return m;
		}) 
	.replace(/tincture  *=/, function (m)
		{ if (coai)
			return 'tincture  ' + addb + ' =';	// COAInformation only !!!
		  else
			return m;
		})
	.replace(/(tincture *=) ?\n/, function (m, eee)		// empty tinct ?
		{ if (coau === 'Za')			// Louis Brun [francaise]    || coau === 'Va'
			return eee + ' /FR\n';			// COAInformation only !!!
		  else
			return m;
		})
	.replace(/image  *=/, function (m)				// COAInformation only !!!
		{ if (coai)
			return 'image     ' + addb + ' =';		// COAInformation only !!!
		  else
			return m;
		})		
	.replace(/element  *=/, function (m)			// COAInformation only !!!
		{ if (coai)
			return 'element   ' + addb + ' =';		// COAInformation only !!!
		  else
			return m;
		})		
	.replace(/ecclesial  *=/, function (m)			// COAInformation only !!!
		{ if (coai)
			return 'ecclesial ' + addb + ' =';		// COAInformation only !!!
		  else
			return m;
		})		
	.replace(/versions  *=/, function (m)			// COAInformation only !!!
		{ if (coai)
			return 'versions  ' + addb + ' =';		// COAInformation only !!!
		  else
			return m;
		})		
	.replace(/description  =/, function (m)
		{ if (coai)
			return 'description =';					// COAInformation only !!!
		  else
			return m;
		})		
	.replace(/\|references = \{\{es\| /, '|references = {{es|1=') // COAInformation

//	Change file display to Filelist
			.replace(/\[\[(?:[Ff]ile:|[Ii]mage: *)([^\|]+?)\|(\d+)px\]\] */gm, '{{F|$1|Z|$2}}')			// ed_wdh: display image
			.replace(/\{\{F\|([^\|]+?)\|Z?\|(\d+)\}\} ?\{\{F\|([^\|]+?)\|Z?\|(\2)\}\}/gm, '{{F1|$1|Z|$2}}{{F2|$3|Z|$2}}')
			.replace(/\{\{F2\|([^\|]+?)\|Z?\|(\d+)\}\} ?\{\{F[12]?\|([^\|]+?)\|Z?\|(\2)\}\}/gm, '{{F2|$1||$2}}{{F2|$3||$2}}')
			.replace(/\{\{F2\|([^\|]+?)\|Z?\|\d+\}\}/gm, '{{F2|$1}}')		// rem. |Z|dd
			.replace(/\}\}\{\{F2\|([^\|\}]+?)(\|?\}*)/gm, '|$1$2')			// rem. }}{{F2		  
			.replace(/\{\{F1(\|[^\|]+?)\|Z?\|(\d+)\|/g, '{{Fl|z=$2|o=Z$1|')	// change to {{Fl
		.replace(/\}\} ?\n\}\} ?\n ?\n? ?\|/, '}}}}\n|')							// U:museo9bits

	/*	//	 clean text switch part of files treated with toolforge translator
		//	 =================================================================
		// id="text1900-el" systemLanguage="el"><tspan id="tspan1893-el"
//	.replace(/ *\n*id="(text|trsvg|tspan|switc)[^"]+"/gm, '')									//  ed_wdh (useless id's)
		.replace(/ *\n*id="text[^"]+"/g, '')									//  ed_wdh (useless id's)
		.replace(/ *\n*id="trsvg[^"]+"/g, '')									//  ed_wdh (useless id's)
		.replace(/ *\n*id="tspan[^"]+"/g, '')									//  ed_wdh (useless id's)
		.replace(/ *\n*id="switc[^"]+"/g, '')									//  ed_wdh (useless id's)
	.replace(/<text id="trsvg[^"]+"><tspan id="trsvg[^"]+">([^<]+)<\/tspan>/g, '<text>$1')		// ed_wdh (first tspan)
	.replace(/ id="trsvg[^"]+"><tspan id="trsvg[^"]+">([^<]+)<\/tspan><\/text>/g, '>$1</text>')	// ed_wdh (last  tspan)
	.replace(/<\/tspan><tspan x="(\d+)" y="(\d+)" id="trsvg[^"]+">([^<]+)<\/tspan><\/text>/g, '</tspan><tspan x="$1" y="$2">$3</tspan></text>')   // ed_wdh 
	.replace(/<text(?: id="trsvg[^"]+")? systemLanguage="(\w+)"(?: id="trsvg[^"]+")?><tspan id="trsvg[^"]+">([^<]+)<\/tspan><\/text>/g, '<text systemLanguage="$1">$2</text>')   // ed_wdh
	.replace(/<text(?: id="trsvg[^"]+")? systemLanguage="(\w+)"(?: id="trsvg[^"]+")?><tspan id="trsvg[^"]+">([^<]+)<\/tspan><tspan([^<]+)<\/tspan><\/text>/g, '<text systemLanguage="$1">$2</tspan><tspan>$3</tspan></text>')   // ed_wdh 
		// <text systemLanguage="it"><tspan>Sistemici;</tspan>
	.replace(/systemLanguage="([^"]+)">\n?<tspan>([^<]+)<\/tspan>/g, 'systemLanguage="$1">$2')	// ed_wdh (first tspan)
		// <text><tspan>Systemic:</tspan>
	.replace(/<text><tspan>([^<]+)<\/tspan>/g, '<text>$1')										// ed_wdh (empty tspan)
	.replace(/<\/tspan>\n<\/text>/g, '</tspan></text>')  										// ed_wdh (remove newline)
	.replace(/<\/text><text/g, '</text>\n<text')  												// ed_wdh (insert newline)
	.replace(/<\/text><\/switch/g, '</text>\n</switch')  										// ed_wdh (insert newline)
	*/

	/*		replace polylines
	.replace(/<polyline points='(\d+\.\d+),(\d+\.\d+) (\d+\.\d+),(\d+\.\d+)'\/>/g, function (m, x1, y1, x2, y2)
		{	x1 = Math.round(x1);
			x2 = Math.round(x2);	
			y1 = Math.round(y1);	
			y2 = Math.round(y2);	
			if (x1 === x2 && y1 > y2)
				return "d='M" + x1 + "," + y2  + "v" + (y1 - y2) + "'";			// hor
			else
			if (x1 === x2 && y2 > y1)
				return "d='M" + x1 + "," + y1  + "v" + (y2 - y2) + "'";		// hor
			else
			if (y1 === y2 && x2 > x1)
				return "d='M" + x1 + "," + y1  + "h" + (x2 - x1) + "'";			// ver
			else
			if (y1 === y2 && x1 > x2)
				return "d='M" + x2 + "," + y1  + "h" + (x1 - x2) + "'";		// ver
			else
				return	m;		// cannot
		})
	*/

	// reset date template
	;}

// ===========================================================================1

/** ******************** 
* Function which removes <nowiki> and <!--…--> from the text, and replaces them with a set of
*   temporary strings. This is necessary, because functions which change text will may to leave the
*   nowikis/comments intact. The temporary strings function as "markers", so that
*   rebuild_comments_nowikis() can later be called and the nowikis/comments will return to the correct
*   position, untouched.
* Returns: An array, whose 0th element contains the altered string, and whose 1st element contains
*   another array which will focus as a set "tokens", which should be kept unaltered and passed to
*   rebuild_comments_nowikis() later.
********************* */
function parse_comments_nowikis(text) {
	// constants
	let nw_regex = /<nowiki>((?:.|\n)*?)<\/nowiki>/,
		c_regex = /<!--((?:.|\n)*?)-->/,

		/* Special one for CommonSense… causing grouping headaches */
		cs_regex = /<!--(\s*categories\s*by\s*(?:commonsense|checkusage)\s*)-->/i,

		// variables
		comments = [],
		nowikis = [],
		comments_cs = [],
		nw_com_order = [],
		next_nw,
		next_c,
		next_cs,
		wLimit = 20,
		index;
	while (wLimit--) {
		next_nw = text.search(nw_regex);
		next_c = text.search(c_regex);
		next_cs = text.search(cs_regex);
		if (next_nw === -1 && next_c === -1) {
			/* cs_regex will be -1 if c_regex is -1 */
			break;
		}

		if (next_nw === -1 || (next_c !== -1 && next_c < next_nw)) {
			if (next_cs === next_c) {
				index = comments_cs.length;
				comments_cs.push(cs_regex.exec(text)[1]);
				text = text.replace(c_regex, `%%%MTOCSCOMMENT${index}%%%`);
				nw_com_order.push('s');
			} else {
				index = comments.length;
				comments.push(c_regex.exec(text)[1]);
				text = text.replace(c_regex, `%%%MTOCOMMENT${index}%%%`);
				nw_com_order.push('c');
			}
		} else {
			index = nowikis.length;
			nowikis.push(nw_regex.exec(text)[1]);
			text = text.replace(nw_regex, `%%%MTONOWIKI${index}%%%`);
			nw_com_order.push('n');
		}
	}

	return [text, [comments, nowikis, comments_cs, nw_com_order]];
}

/** ********************
* See function immediately above for explanation
* remove_whitespace: when rebuilding nowikis, remove unnecessary whitespace (not shown in final version anyway)
* Returns: Rebuilt text.
********************* */
function rebuild_comments_nowikis(text, tokens, remove_whitespace) {
	let comments = tokens[0],
		nowikis = tokens[1],
		comments_cs = tokens[2],
		nw_com_order = tokens[3],
		next,
		text_tmp;
	while (nw_com_order.length > 0) {
		next = nw_com_order.pop();
		if (next === 's') {
			text = text.replace(`%%%MTOCSCOMMENT${comments_cs.length - 1}%%%`, `<!--${comments_cs.pop()}-->`);
		} else if (next === 'c') {
			text = text.replace(`%%%MTOCOMMENT${comments.length - 1}%%%`, `<!--${comments.pop()}-->`);
		} else {
			/* i.e., nw_com_order.pop was 'n'; also, mediawiki registers all whitespace inside nw's as just one space */
			text_tmp = nowikis.pop();
			if (remove_whitespace) text_tmp = text_tmp.replace(/\s+/g, ' ');
			// eslint-disable-next-line no-useless-escape
			text = text.replace(`%%%MTONOWIKI${nowikis.length}%%%`, `<nowiki>${text_tmp}<\/nowiki>`);
		}
	}
	return text;
}

/** ********************
* Function which iterates over 'text' and replaces each instance of 'code' with 'replacement' until
*   running the iteration no longer results in a change of the text.
* Parameter code: Should be a string or regular expression (both are allowed by the
*   replace() function in JavaScript).
* Parameter replacement: Should be a string or a functor (both are allowed by the replace() function
*   in JavaScript).
* Parameter text: The text that will be altered.
* Returns: The altered text.
********************* */
function _ucfirst(s) { // Uppercase
	return s.slice(0, 1).toUpperCase() + s.slice(1);
	}
function _lcfirst(k) { // Lowercase
	return k.slice(0, 1).toLowerCase() + k.slice(1);
	}

function iterative_replace(code, replacement, text) {
	let temptext,
		wLimit = 20;
		// only headers that aren't already inside a template will suffice, per what seems right to me
	while (wLimit--) {
		temptext = text.replace(code, replacement);
		if (temptext === text) break;

		text = temptext;
	}
	return text;
}

/** ***********
* month_name_re = case insensitive
* month_number = 1-12
************ */
function mto_parse_date(input_string, month_name_re, month_number) {
	let regexp1 = new RegExp(`(^\\s*\\|\\s*[Dd]ate\\s*=\\s*)${month_name_re
		}[-\\\\\\.\\s,]+(\\d{1,2})(?:st|nd|rd|th)?[-\\\\\\.\\s,]+(\\d{4})(?!\\d)`, 'mi'),
		regexp2 = new RegExp(`(^\\s*\\|\\s*[Dd]ate\\s*=\\s*)(\\d{1,2})(?:st|nd|rd|th)?[-\\\\\\.\\s,]+${month_name_re
		}[-\\\\\\.\\s,]+(\\d{4})(?!\\d)`, 'mi'),
		regexp3 = new RegExp(`(^\\s*\\|\\s*[Dd]ate\\s*=\\s*)(\\d{4})[-\\\\\\.\\s,]+${month_name_re
		}[-\\\\\\.\\s,]+(\\d{1,2})(?:st|nd|rd|th)?(?!\\d)`, 'mi'),
		regexp4 = new RegExp(`(^\\s*\\|\\s*[Dd]ate\\s*=\\s*)(\\d{4})[-\\\\\\.\\s,]+(\\d{1,2})(?:st|nd|rd|th)?[-\\\\\\.\\s,]+${month_name_re}`, 'mi'),
		regexp5 = new RegExp(`(^\\s*\\|\\s*[Dd]ate\\s*=\\s*)(\\d{4})[-\\\\\\.\\s,]+${month_name_re}`, 'mi'),
		regexp6 = new RegExp(`(^\\s*\\|\\s*[Dd]ate\\s*=\\s*)${month_name_re}[-\\\\\\.\\s,]+(\\d{4})(?!(?:[-\\\\\\.\\s,]+\\d{1,2}|\\d))`, 'mi');

	return input_string
		.replace(regexp1, `$1{{Date|$3|${month_number}|$2}}`)
		.replace(regexp2, `$1{{Date|$3|${month_number}|$2}}`)
		.replace(regexp3, `$1{{Date|$2|${month_number}|$3}}`)
		.replace(regexp4, `$1{{Date|$2|${month_number}|$3}}`)
		.replace(regexp5, `$1{{Date|$2|${month_number}}}`)
		.replace(regexp6, `$1{{Date|$2|${month_number}}}`)
		// reset date template
	;}


/*
 * returns author information, as found. Returns in vector form, as {(Entire author string), (author project code), (author username)}.
 * If unable to parse project code or username, then they will be null
 */
function mto_parse_author_info(text) {
	let return_array = new Array(3),
		authorstart = text.search(/^\s*\|\s*[Aa]uthor\s*=\s*(.+?)\.?\s*$/m),
		username_array;

	return_array[0] = text.substring(authorstart, text.indexOf('\n', authorstart)).replace(/^\s*\|\s*[Aa]uthor\s*=\s*(.+?)\s*.?(?:\s*<\s*[Bb][Rr]\s*\/?>\s*)?\s*?$/, '$1');

	username_array = /^.*\[\[(?:\s*:?\s*([a-z]{1,2}))\s*:[A-Za-z]?[a-z]+:(.*?)[|\]](?:.\n*)*$/.exec(return_array[0]);
	if (username_array) {
		// successful username extraction match
		return_array[1] = username_array[1];
		return_array[2] = username_array[2];
	}
	return return_array;
}

function mto_uploader_is_author(text) {
	return text.replace(/(\|\s*[Aa]uthor\s*=\s*.*?)\s*\.{0,2}(?:\s*<\s*[Bb][Rr]\s*\/?>)?\s*\n\s*\*? ?([^\s|}].*\n\s*[|}])/, '$1 / $2');
}

function mto_uploader_isnt_author(text) {
	// original upload date is useless
	return text.replace(/(\|[Dd]ate\s*=.*?)\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)?\*?\s*\{\{\s*[Oo]riginal +upload +date\s*\|\s*[\d-]+\s*\}\}(?:; *\{\{[Dd]ate[\d|\s]+\}\} *\(last +version\)*)?/, '$1')
		// original author info is useless
		.replace(/(\|\s*[Aa]uthor\s*=\s*?.*?).?(?:\s*<\s*[Bb][Rr]\s*\/?>)?\s*(?:[Uu]ploaded\s+by\s+)?\[\[\s*:?\s*([a-z]{1,2})\s*:(?:[A-Za-z]?[a-z]+):(?:.+?)\s*(?:\|(?:.*?))\]\] at (?:\[http:\/\/)?\2.w[\w. ]+\]?.*\n/, '$1\n')
		.replace(/(\|\s*[Aa]uthor\s*=\s*?.*?).?(?:\s*<\s*[Bb][Rr]\s*\/?>)?\s*Later +version\(s\) +were +uploaded +by\s*?\n?/, '$1\n');
}

// must process source cleanup in order to continue
function own_replace_pre(text) {
	return text.replace(/Transferred from \[(http:\/\/[\w-. ]+)\]; transferr?ed to Commons/im, 'Transferred from [$1] to Commons')
		.replace(/Transferred from \[(http:\/\/[\w-. ]+)\]; Transfer was stated to be made by \[\[(.+?)\]\]/im, 'Transferred from [$1] to Commons by [[$2]]')
		.replace(/\|(\s*source\s*=\s*)transferred from \[(http:\/\/[\w-. ]+)\]((?: to commons by \[\[.+?\]\].?)?)\s*?(?:<\s*br\s*\/?>)?((?:\susing .+?)?)\s*.?(?:\s*<\s*br\s*\/?\s*>)?\n+\s*\(original\stext\s*:\s*('')?([\S\s]*?)\5\)(\s*(?:\|\s*(?:description|date|author|permission|other[ _]+versions|other[ _]+fields)\s*=|\}\}))/im, '|$1$6<br>\nTransferred from [$2]$3$4.$7');
}

/* internal function only */
function own_replace(text, username, projcode, force) {
	let re_anything = '[^\\S\\n]*[^\\|\\s].*?',
		username_test_re = (projcode && username) ? `|\\[\\[\\s*:\\s*${projcode ? projcode.replace('-', '\\-') : /* dummy */ ''}\\s*:\\s*[^\\s\\d\\+:\\|\\[\\]]+?\\s*:\\s*${username}\\s*\\|\\s*(?:[^\\s\\d\\+:\\|\\[\\]]+?\\s*:\\s*)?${username}\\s*\\]\\].*?` : '',
		sp_re_const = force === 'selfphoto' ? re_anything : '(?:selb(?:st|er)[\\s-]*(?:(?:ph|f)oto(?:gra(?:f|ph)ie(?:rt)?)?|aufgenommen|geknipst)|self[-\\s+]taken|i[^a-z]took[^a-z]this[^a-z](?:photo(?:graph)?|picture))\\.?',
		own_re_const = force ? re_anything : `(?:I,? .*created this (?:work|image) entirely by myself\\.?|self[-\\s+]made|own(?:\\s+work)?|(?:selb(?:st|er)|eigen(?:e|es)?)(?:\\s*(?:werk|(?:ph|f)oto(?:gra(?:f|ph)ie(?:rt)?)?|archiv|gezeichnet|erstellt|aufnahme|bild(?:er)?|arbeit))?|opera propria${username_test_re})\\.?`,
		text_bak = text,
		replacement;
		// define functions
	function own_replace_1(text, internal_re, replace_text) {
		let re = new RegExp(`(\\|\\s*source\\s*=[^\\S\\n]*(?!\\|))(${internal_re}.*?)(?:\\s*?<\\s*[Bb][Rr]\\s*\\/?>)?\\s*?(\\n?(?:transferr?ed +from +\\[http|\\{\\{\\s*[Tt]ransferred +from|\\(?Original(?:ly)? +uploaded +on +[a-z-]{2,}\\.wik).+)?\\n`, 'i'),
			re_parsed = re.exec(text);

		if (re_parsed !== null) {
			if (re_parsed[2].search(/(?:\s*transferr?ed +from +\[http|\{\{\s*[Tt]ransferred +from|\(?Original(?:ly)? +uploaded +on + [a-z-]{2,}.wik)/i) === 0) {
				// just prepend under circumstances
				return text.replace(re, `$1${replace_text}<br>$2\n`);
			}

			if (re_parsed[2].search(/\s*(?:\*\s*)?\{\{\s*(?:[Oo]wn|[Ss]elf(?:-| +)photographed|[Oo]wn[_ ]+work[_ ]+by[_ ]+original[_ ]+uploader)\s*\}\}/) !== 0) {
				// prepend plus original text				nobold
	//			return text.replace(re, `$1${replace_text} ({{original text|1=$2|nobold=1}})<br>$3\n`);
				return text.replace(re, `$1${replace_text}\n`);
			}
		}
		// we've decided not to parse for whatever reason
		return text;
	}
	function own_replace_2(text, internal_re, replace_text) {
		let re = new RegExp(`(\\|\\s*source\\s*=[^\\S\\n]*)(?!\\|)(${internal_re}.*?)(?:\\s*?<\\s*[Bb][Rr]\\s*\\/?>)?\\n`, 'i'),
			re_parsed = re.exec(text);
		if (re_parsed !== null) {
			if (re_parsed[2].search(/\s*(?:\*\s*)?\{\{\s*(?:[Oo]wn|[Ss]elf(?:-| +)photographed|[Oo]wn[_ ]+work[_ ]+by[_ ]+original[_ ]+uploader)\s*\}\}/) !== 0 &&
	//		re_parsed[2].search(/(?:\s*transferr?ed +from +\[http|\{\{\s*[Tt]ransferred +from|\(?Original(?:ly)? +uploaded +on + [a-z-]{2,}.wik)/i) !== 0) return text.replace(re, `$1${replace_text} ({{original text|1=$2|nobold=1}})\n`);
			re_parsed[2].search(/(?:\s*transferr?ed +from +\[http|\{\{\s*[Tt]ransferred +from|\(?Original(?:ly)? +uploaded +on + [a-z-]{2,}.wik)/i) !== 0) return text.replace(re, `$1${replace_text}\n`);

			if (re_parsed[2].search(/\|\s*source\s*=\s*\*?\s*\{\{\s*(?:[Oo]wn|[Ss]elf(?:-| +)photographed|[Oo]wn[_ ]+work[_ ]+by[_ ]+original[_ ]+uploader)\s*\}\}/) === 0) {
				// just prepend under circumstances			nobold
				return text.replace(re, `$1${replace_text}<br>$2\n`);
			}
		}
		return text;
	}

	text = own_replace_pre(text);

	if (force === 'ownoriginaluploader') {
		text_bak = own_replace_1(text, re_anything, '{{Own work by original uploader}}');
		if (text_bak === text) text_bak = own_replace_2(text, re_anything, '{{Own work by original uploader}}');
	}
	if (text_bak === text) {
		text_bak = own_replace_1(text, sp_re_const, '{{Self-photographed}}');
		if (text_bak === text) {
			text_bak = own_replace_2(text, sp_re_const, '{{Self-photographed}}');
	//		if (text_bak === text) {
	//	leyo		text_bak = own_replace_1(text, own_re_const, '{{Own }}');
	//			if (text_bak === text) {
	//				text_bak = own_replace_2(text, own_re_const, '{{ Own}}');
					if (text_bak === text && force) {
						switch (force) {
							case 'selfphoto':
								replacement = '$1{{Self-photographed}}';
								break;
							case 'ownoriginaluploader':
								replacement = '$1{{Own work by original uploader}}';
								break;
							default:
								replacement = '$1{{Own}}';
						}
						text = text.replace(/(\|\s*source\s*=(?!\s*\{\{\s*(?:[Oo]wn|[Ss]elf(?:-| +)photographed|[Oo]wn[_ ]+work[_ ]+by[_ ]+original[_ ]+uploader)\s*\}\}))/i, replacement);
						text_bak = text;
					}
	//			}
	//		}
		}
	}

	return text_bak.replace(/\|(\s*[Ss]ource\s*=\s*\{\{(?:[Oo]wn|[Ss]elf(?:-| +)photographed|[Oo]wn[_ ]+work[_ ]+by[_ ]+original[_ ]+uploader)\}\}(?:\s*\(?\s*\{\{\s*[Oo]riginal +text\s*\|.+?\|\s*nobold\s*=\s*1\s*\}\}\s*\)?)?)(?:\s*<\s*[Bb][Rr]\s*\/?>){2,}\s*/, '|$1<br>\n')
		.replace(/\|(\s*[Ss]ource\s*=\s*\{\{(?:[Oo]wn|[Ss]elf(?:-| +)photographed|[Oo]wn[_ ]+work[_ ]+by[_ ]+original[_ ]+uploader)\}\}(?:\s*\(?\s*\{\{\s*[Oo]riginal +text\s*\|.+?\|\s*nobold\s*=\s*1\s*\}\}\s*\)?)?)\s*<\s*[Bb][Rr]\s*\/?>(\s*(?:\|\s*(?:description|date|author|permission|other[ _]+versions|other[ _]+fields)\s*=|\}\}|\*))/, '|$1$2');
}
     //						nobold
function mto_own(text, type) {
	// author cleanup
	text = mto_uploader_is_author(text);

	let authorinfo = mto_parse_author_info(text),
		author = authorinfo[0],
		project = authorinfo[1],
		user = authorinfo[2];

	text = own_replace(text, user, project, type);

	// specific license cleanup
	if (user) text = text.replace(/\{\{\s*pd-release\s*\}\}/mi, `{{PD-user-w|${project}|wikipedia|${user}}}`);

	// {{Self}} cleanup										ed_wdh: license		
//	text = text.replace(/\{\{\s*([Ss]elf2?|[Pp]ropio|[Сс]ебе|[Mm]ultilicense +replacing +placeholder(?: +new)?)\s*\|(?!.*\s*[Aa]uthor\s*=[^|]+\|\s*)(.*?)\}\}/m, '{{$1|author=${author}}|$2}}🐸');	// test: 2nd "}" 🐸🐸🐸🐸
	text = iterative_replace(/\|(\s*[Ss]ource\s*=\s*\{\{(?:[Oo]wn|[Ss]elf(?:-| +)photographed|[Oo]wn[_ ]+work[_ ]+by[_ ]+original[_ ]+uploader)\}\}<br\/?>)\s*<\s*[Bb][Rr]\s*\/?>\s*/, '|$1\n', text);
	return text;
}


function mto_wrapper(type) {
	let textbox = getTextbox(),
		cleanup = textbox.val(),

		// parse comments/nowikis
		cnwt = parse_comments_nowikis(cleanup),
		cleanup2 = cnwt[0],
		tokens = cnwt[1];

	cleanup2 = mto_own(cleanup2, type);
	// rebuild comments/nowikis
	cleanup2 = rebuild_comments_nowikis(cleanup2, tokens, false);
	textbox.val(cleanup2);
}

function remove_useless_permission_messages(cleanup) {
	// remove nonsense: various useless permission messages
	let USELESS_TEXT_REGEX = regexify([
			'PD[A-Z0-9-\\| ]*\\d*',
			'CC-(?:ZERO|BY(?:[\\w-,.]*)?)',
			'(?:BILD-)?GFDL[A-Z-\\|]*',
			'ATTRIBUTION',
			'NORIGHTSRESERVED',
			'BILD-BY',
			'BSD',
			'This image is in the (?:\\[\\[)?public domain(?:\\]\\])?(?: due to its age| because it is ineligible for copyright)?',
			'Released under the \\[\\[GNU Free Documentation License\\]\\]',
			'GNU Free Documentation License 1\\.2',
			'Released into the public domain \\(?by the author\\)?',
			'Licensed under the \\[\\[GFDL\\]\\] \\(?by the author\\)?',
			'{{Own}}', '{{own}}',
			'See below', 'see below',
			'Look below', 'look below',
			'Voir ci-dessous', 'voir ci-dessous',
			'Olhe abaixo', 'olhe abaixo',
			'-+'
		]),
		OTRS_TEMPLATES_REGEX = regexifyTemplates([
			'PermissionOTRS',
			'Permission OTRS',
			'OTRSPermission',
			'Crediti',
			'Разрешение OTRS'
		]);

	cleanup = cleanup.replace(/(\|\s*[Pp]ermission\s*=[^|\n]*?)(?:\s*?<\s*[Bb][Rr]\s*\/?>)?\n\(Original +text *: *(?:'')?(?:[Pp]ublic\s+[Dd]omain|GNU\s*-?\s*FDL|[Yy][Ee][Ss]|[Jj][Aa]|[Ss][Ee][Ee]\s+[Bb][Ee][Ll][Oo][Ww]|[Bb]ild-frei|[Ss]ee +license +section|(?:[Cc][Cc]-(?:[Bb][Yy](?:-[Ss][Aa])?(?:-\d.\d)?|zero),?\s*)+)\.?\s*(?:'')?\)\s*?\n(\s*?\|)/, '$1\n$2');
	cleanup = iterative_replace(new RegExp(`(\\|\\s*Permission\\s*=)(?:(.+?);)?\\s*${
		USELESS_TEXT_REGEX
	}\\s*(;.*?)?\\.?(?:<\\s*[Bb][Rr]\\s*\\/?>)?\\s*?(?:\\s*\\(Original +text\\s*:\\s*('')([\\S\\s]*?)\\4\\)|\\s*(\\{\\{\\s*${
		OTRS_TEMPLATES_REGEX
	}\\s*\\|[\\s\\S]*?\\}\\})\\s*)?(\\n\\s*\\|${
		INFORMATION_FIELDS_REGEX
	})`, 'i'), '$1$2$3; $5$6$7', cleanup); // \| included due to early buggy bot moves

	return cleanup
		.replace(/(\|\s*[Pp]ermission\s*=)\s*('')?(?:see +license +section|see +below)\.?\s*\2\s*\n/i, '$1\n')
		.replace(/(\|\s*[Pp]ermission\s*=\s*);+\s*([^\s|])/, '$1$2')
		.replace(/(\|\s*[Pp]ermission\s*=.*);+(?:\s*(.))?(\s*\|(?:description|date|author|permission|other[ _]+(?:versions|fields)))/i, '$1$2$3');
}

// mto_cleanup tagger (initially developed by Patstuart/Magog the Ogre)
function mto_cleanup(fileContent) 
{	let cbText,
		hascat = false;
	if (typeof fileContent === 'object') cbText = fileContent.text;
	else fileContent = 0;
	let textbox = getTextbox(),
		cleanupTmp = (typeof cbText === 'string' ? cbText.trim() : 0) || textbox.val().trim(),
  //	cleanupSum   = (/\{\{\s*PD-chem/i.txt) ? 'cleanupChem' : 'cleanup',		// ed_wdh
		orgText = cleanupTmp,
		// store comments, nowikis as is to avoid changing them
		cnwt = parse_comments_nowikis(cleanupTmp),
		cleanup = cnwt[0],
		tokens = cnwt[1],

		de_wp_re = /\{\{Bild-GFDL-Neu\}\}\s*\r?\n\{\{(?:\s*self\s*\|\s*author=.*💲\|)?Cc-by-sa-3.0\}\}\s*\r?\n\{\{Cc-by-sa-3.0-de\}\}\s*\r?\n\{\{GFDL(?:-user-de(?:\|.*)?)?\}\}/i,

		// uploader statuses
		uploader_status = 0,
		UNKNOWN = 1,
		AUTHOR = 2,
		NOTAUTHOR = 4,
		PD_UNKNOWN = 'release|text(?: *logo)?|ineligible|trivial|uegnet|markenrecht|shape|simple|geometry|link|chem|author|because|reason|porque|reden',
		PD_AUTHOR = 'self|users?|utente|own',
		GFDL_AUTHOR = 'users?|self',
		project_code,
		username,
		authorinfo,
		author,
		cleanup2,
		i,
		self_author_text,
		headercheck,
		re_unc,
		unc,
		nextcat,
		re_ds,
		ds,
		re_chc,
		chc,
		re_cs_chc,
		cs_chc,
		infoend,
		infosry, // test
		opennext,
		closenext,
		template_level,
		cleanup_pre,
		cleanup_post,
	//	summary = "([[User:Magog the Ogre/cleanup.js|Script]]) cleanup",
		summary = '[[User:Sarang/cleanup/sandbox.js|cleanUp]]',				// ed_wdh (sandbox)🍎🍎🍎🍎🍎🍎🍎
//		igensum = '+SVG-[[Template:Image generation|template]]';	// summary text passed too early by simpleSVGcheck
		categories = [],
		re_cat = /^([\s\S]*[^=])\[\[\s*[Cc]ategory\s*:\s*([^[\]]?)([^|[\]]*?)\s*(\|[^|[\]]*)?\]\](?:(\s*%%%MTOCOMMENT\d+%%%)?\s*?\n)?([\s\S]*?)$/,
		catLimit = 30; // max cats;

	// interwikis_regex = new RegExp('(\\{\\{\\s*' + getInterwikisRegex() + '\\s*\\|\\s*(?:1\\s*=\\s*)?)(\\S)(\\S*)', 'gm');

	/*  function getInterwikisRegex() 
	{	return regexifyTemplates(Object.keys(window.wpAvailableLanguages));
	} */

	cleanup = bot_move_checked(cleanup);
	cleanup = cleanup.replace(/&times;/g, '×')
		.replace(/&#x7C;/g, '|')
		// because the new bot really is this dumb
		.replace(/(\|\s*[Dd]escription\s*=\s*(.+)[\s\S]+)==\s*[Ss]ummary\s*==\n+\2/, '$1')
		// Excessive wordage/markup in description
		.replace(/\{\{\s*([a-z]{2,4}(?:-[a-z]{5})?)\s*\|\s*(1\s*=)?\s*(?:this\s+is\s+)?(?:an?\s+|the\s+)?(?:photo(?:graph)?|picture|image)\s+(?:taken\s+)?(?:of|depicting)\s+(\S)/ig, '{{$1|$2$3');
	cleanup = iterative_replace(/\{\{\s*([a-z]{2,4}(?:-[a-z]{5})?)\s*\|\s*((?:.|\n?)+?)(?:\s*<\s*[Bb][Rr]\s*\/?>\n+)?(?:\s*\[\[\s*:\s*\1\s*:\s*(?:[Cc]ategor(?:y|ia)|[Cc]atégorie|[Kk]ategorie|[Кк]атегория)\s*:[^\]]+\]\])+\s*\}\}/, '{{$1|$2}}', cleanup);
	cleanup = cleanup.replace(/\{\{(\s*[a-z]{2,4}(?:-[a-z]{5})?\s*\|\s*(.+?))\s*(?:<\s*[Bb][Rr]\s*\/?>)?(\s*\n+==[^=]+==)*\s*\}\}/m, '{{$1}}')
		.replace(/\{\{\s*[a-z]{2,4}(?:-[a-z]{5})?\s*\|\s*(=+).*?\1\s*\}\}/, '');
	// pipe wikilinks in descriptions
	cleanup = iterative_replace(/\|(\s*[Dd]escription\s*=(?:.|\n?)*?)\{\{\s*([a-z]{2,4}(?:-[a-z]{5})?)\s*\|((?:.|\n?)*)\[\[\s*:\s*\2\s*:\s*([^|\]]*?)\]\]((?:.|\n?)*\}\}(?:.|\n?)*?\|\s*(?:[Ss]ource|[Dd]ate)\s*)=/, '|$1{{$2|$3[[:$2:$4|$4]]$5=', cleanup);

	// Empty descriptions
	cleanup = cleanup.replace(/(?:(=)\s*)?\{\{\s*[a-z]{2,4}(?:-[a-z]{5})?\s*\|\s*(?:1\s*=\s*)?(?:''no original description''\s*)?\s*\}\}\s*$/mg, '$1')
		// internationalization
		.replace(/\s*original upload date\s*(?!(?:\}\}|\|))/im, '{{original upload date}}')
		.replace(/^(=+) *li[cz]en(?:s(?:e|ing)(?:\s+information)?|za(?: +d'uso)?|z) *:? *\1\s*$/mig, '$1{{int:license-header}}$1')
		.replace(/^(=+) *\{\{\s*(?:[Ii][Nn][Tt]|[Mm][Ee][Dd][Ii][Aa][Ww][Ii][Kk][Ii])\s*:\s*[Ll]icense\s*\}\} *:? *\1\s*?$/mg, '$1{{int:license-header}}$1')
		.replace(/^(=+) *original upload (?:log|history) *:? *\1\s*$/mig, '$1{{Original upload log}}$1')
		.replace(/^(=+) *(?:summary|dettagli|beschreibung|beschreibung\W+quelle) *:? *\1\s*$/mig, '$1{{int:filedesc}}$1')
		// Magnus bot bug where it's transferring over a second and incorrect license
		.replace(/((=+) *\{\{int:licens[e|ing](?:-header)?\}\} *\2[\s\S]+?)((=+) *\{\{int:licens[e|ing](?:-header)?\}\} *\4)(?:\s*\{\{.+?\}\})+\s*$/, '$1');

	// because the new bot by Jan Luca and Magnus Manske spams itself like crazy and thus creates two headers
	cleanup = iterative_replace(/^(=+) *\{\{int:filedesc\}\} *(?:\1(?:\s*\n)+)\1 *\{\{int:filedesc\}\} *\1\s*\n/mig, '$1{{int:filedesc}}$1\n', cleanup);

	// rm various useless permission messages
	cleanup = remove_useless_permission_messages(cleanup);

	// wordage changes
	cleanup = cleanup.replace(/\|(\s*source\s*=.*\s*(?:\(?\s*)Original uploaded on [a-z]+.wiki[a-z]+)(?:\s*\)?)((?:\s*\|\s*date\s*=\s*.*)?)\s*\|\s*(\s*author\s*=\s*.+)\s*\(transferr?ed\s*by\s*(.+?)\)/im, '|$1 (transferred to commons by $4)$2\n|$3')
		.replace(/\|(\s*source\s*=.+\s*(?:\(\s*)Original(?:ly)? uploaded on [a-z]+.wiki[a-z]+(?:\s*\)))((?:.|\n)+?)\/Original(?:ly)?\s+uploaded\s+by\s+.+?(\){0,1}\s*$)/im, '|$1 - $2$3')
		// author cleanup
		.replace(/(\|\s*[Aa]uthor\s*=\s*\[\[(?:\s*:\s*[a-z]+){1,2}\s*:[^\]|]+\|[^\]|]+\]\]) \d{2}:\d{2}, (?:\d.? [^\]|(\d]+|\d.? [^\]|(\d]+) \d+ \((?:CES?T|UTC)\).?/g, '$1')
		.replace(/^(\|\s*author\s*=\s*)(?:Original upload(?:er was|ed by)|Uploaded by)/mi, '$1')
		.replace(/^(\|\s*[Aa]uthor\s*=\s*)(.+?)\.?\s*(?:<\s*br\s*\/?>\s*)?\n+\s*\[\[\s*:\s*([a-z]{2,4}(?:-[a-z]{5})?\s*:\s*.+?\s*:\s*\2(?:\s*\|.*))\s*\]\]/mi, '$1[[:$3]]')
		.replace(/^(\|\s*[Aa]uthor\s*=\s*)-*\s*\[\[\s*:\s*([a-z]{2,4}(?:-[a-z]{5})?)\s*:\s*[^\]]+?\s*:\s*([^\]]+?)(?:\s*\|[^\]]*)?\s*\]\](?:\s*\(?\[\[:\2:[^|\]:]+?:\s*\3\s*\|[^|\]:]+\]\]\)?)?(.*?)\s*\)?\s*\.?\s*(?:<\s*br\s*\/?>\s*)?\n+\s*(\[\[\s*:\s*\2\s*:\s*.+?\s*:\s*\3(?:\s*\|.*)\s*\]\] .*)$/mi, '$1$5$4')
		.replace(/^(\|\s*[Aa]uthor\s*=.*) 0?\d{1,2}:\d{2}, \d{1,2}.? [^\d[\].,\s()';"]{3}.? \d{4} \((?:UTC|CES?T)\)?.?/m, '$1');

	// check if original uploader appears not to be the author
	if (cleanup.match(new RegExp(`\\{\\{\\s*(?:pd-(?:${PD_UNKNOWN})|cc|gfdl(?!-)|gfdl-(?!${GFDL_AUTHOR})|bild-pd-(?:frei|auteur))|attribution`, 'i'))) uploader_status = UNKNOWN;

	if (cleanup.match(new RegExp(`\\{\\{\\s*(?:pd-(?:${PD_AUTHOR})|Јв-ја|مالکیت عمومی-خود |گنو-خود |Pd=self|self|gfdl-(?:${GFDL_AUTHOR}))`, 'i')) || de_wp_re.test(cleanup)) uploader_status |= AUTHOR;

	if (cleanup.match(new RegExp(`\\{\\{\\s*(?:pd-(?!${PD_UNKNOWN}|review|${PD_AUTHOR}).+|larsencopyright)\\s*(?:\\}\\}|\\|)`, 'i'))) uploader_status |= NOTAUTHOR;

	if (uploader_status === AUTHOR) cleanup = mto_uploader_is_author(cleanup);

	if (uploader_status === NOTAUTHOR) cleanup = mto_uploader_isnt_author(cleanup);

	authorinfo = mto_parse_author_info(cleanup);
	if (authorinfo) 
	{	author = authorinfo[0];
		project_code = authorinfo ? authorinfo[1] : null;
		username = authorinfo ? authorinfo[2] : null;

		// {{Multilicense}} cleanup									ed_wdh: license 
		cleanup = cleanup.replace(/\{\{\s*([Mm]ultilicense +replacing +placeholder(?: +new)?)\s*\|(?!.*\s*[Aa]uthor\s*=[^|]+\|\s*)(.*?)\}\}/m, `{{$1|author=${author}|$2}}`);
	}

	// own template (internationalization/standardization). I am perfectly aware that some code is run twice, but whatever
	cleanup = own_replace_pre(cleanup);
	cleanup2 = own_replace(cleanup, username, project_code, false);
	if (cleanup !== cleanup2) 
	{	cleanup = mto_own(cleanup2, 'own');

		// own_replace may have altered the text; worth a retry
		if (!username) 
		{	authorinfo = mto_parse_author_info(cleanup);
			if (authorinfo) 
			{	project_code = authorinfo ? authorinfo[1] : null;
				username = authorinfo ? authorinfo[2] : null;
			}
		}
	}

	// LOC-image automatic detection
	cleanup = cleanup.replace(/(?:\s*?<\s*[Bb][Rr]\s*\/?>)*\s*(?:http:\/\/)?[\w-]+.loc.gov\/loc.pnp\/([\w.]+\w*\d).?(?:\s*?<\s*[Bb][Rr]\s*\/?>)*\s*/, '{{LOC-image|id=$1}}')
		.replace(/([\s\S]+)(\{\{\s*[Ll]OC-image\s*\|\s*(?:(?:1|id)=)?[\w.]+\w\}\})([\s\S]+)(\|\s*[Ss]ource\s*=)([\s\S]+)/, '$1$3$4$2$5')
		.replace(/([\s\S]+)(\|\s*[Ss]ource\s*=)([\s\S]+)(\{\{\s*[Ll]OC-image\s*\|\s*(?:(?:1|id)=)?[\w.]+\w\}\})([\s\S]+)/, '$1$2$4$3$5')
		// OTRS template from de.wp cleanup
		// eslint-disable-next-line no-useless-escape
		.replace(/\{\{\s*[Oo](?:TRS(?:[ _]+permission)?|trs)\s*\|\s*(?:1\s*=)?(\d+)\s*(\|.*?\}\}|\}\})/, '{&#x7b;PermissionOTRS|id=$1$2')
		.replace(/ *\|permission *= *\n/m, '')					// ed_wdh  remove empty permission			// date fixup (rm needless text)
		.replace(/(\|\s*[Dd]ate\s*=\s*)\d\d:\d\d, (\d\d?) ([A-Z][a-z]+) (\d{4}) \(UTC\)(?:\s*<\s*[Bb][Rr]\s*\/?>)?\s*[\s\n]\s*\(?(\{\{\s*[Dd]ate\s*\|\s*\4\s*\|\s*\d\d\s*\|\s*0?\2\s*\}\})\s*\(\s*\{\{\s*[Oo]riginal +upload +date\s*\}\}\s*\)\s*\)?/, '$1$5 ({{original upload date}})')
		.replace(/\|(\s*date\s*=\s*.*?\s*?)(?:\(Uploaded on Commons at )?[\d- :]+\(UTC\)\s*[(/]original(?:ly)? uploaded at (\d{4})-(\d{2})-(\d{2})[\s\d:]+\)?/i, '|$1{{Date|$2|$3|$4}} ({{original upload date}})')
		// circa/or/before/early/late/between date (also remove upload date if this is provided)
		.replace(/(\|\s*[Dd]ate\s*=\s*)[Cc](?:(?:[Ii][Rr][Cc])?[Aa])?\.?\s*(\d{4}(?:\s*-\s*\d{1,2}(?:\s*-\s*\d{1,2})?)?)(\W)/, '$1{{circa|$2}}$3')
		.replace(/(\|\s*[Dd]ate\s*=\s*)(\d{4})(?:\s*(-)\s*(\d{1,2}))?(?:\s*(-)\s*(\d{1,2}))?\s*[Oo][Rr]\s*(\d{4})(?:\s*(-)\s*(\d{1,2}))?(?:\s*(-)\s*(\d{1,2}))?\s*\.?\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)?\s*(?:\(?\{\{\s*[Dd]ate\s*\|\s*\d{4}s*\|\s*\d{1,2}s*\|\s*?\d{1,2}\s*\}\}\s*\(\s*\{\{\s*[Oo]riginal +upload +date\s*\}\}\s*\)\s*\)?)?\s*/, '$1{{other date|or|$2$3$4$5$6|$7$8$9$10$11}}\n')
		.replace(/(\|\s*[Dd]ate\s*=\s*\{\{circa\|.+\}\})\s*\.?\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)?\s*\(?\{\{\s*[Dd]ate[|\s\d]+\}\}\s*\(\s*(?:\{\{\s*[Oo]riginal +upload +date\s*\}\}|first +version)\s*?\)(?:\s*;?\s*\{\{\s*[Dd]ate[|\s\d]+\}\}\s*\(\s*last\s+version\))?\s*?\)?/, '$1')
		.replace(/(\|\s*[Dd]ate\s*=\s*)(?:[Bb][Ee][Ff][Oo][Rr][Ee]\s+|[Pp][Rr][Ee]\s*-)(\d{4}(?:\s*-\s*\d{1,2}(?:\s*-\s*\d{1,2})?)?)\s*\.?\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)?\n\s*\(?\{\{\s*[Dd]ate\s*\|\s*\d{4}s*\|\s*\d{1,2}s*\|\s*?\d{1,2}\s*\}\}\s*\(\s*\{\{\s*[Oo]riginal +upload +date\s*\}\}\s*\)\s*\)?/, '$1{{other date|before|$2}}')
		.replace(/(\|\s*[Dd]ate\s*=\s*)(?:[Ll][Aa][Tt][Ee]\s+|[Pp][Rr][Ee]\s*-)(\d{4}(?:\s*-\s*\d{1,2}(?:\s*-\s*\d{1,2})?)?)\s*\.?\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)?\n\s*\(?\{\{\s*[Dd]ate\s*\|\s*\d{4}s*\|\s*\d{1,2}s*\|\s*?\d{1,2}\s*\}\}\s*\(\s*\{\{\s*[Oo]riginal +upload +date\s*\}\}\s*\)\s*\)?/, '$1{{other date|late|$2}}')
		.replace(/(\|\s*[Dd]ate\s*=\s*)(?:[Ee][Aa][Rr][Ll][Yy]\s+|[Pp][Rr][Ee]\s*-)(\d{4}(?:\s*-\s*\d{1,2}(?:\s*-\s*\d{1,2})?)?)\s*\.?\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)?\n\s*\(?\{\{\s*[Dd]ate\s*\|\s*\d{4}s*\|\s*\d{1,2}s*\|\s*?\d{1,2}\s*\}\}\s*\(\s*\{\{\s*[Oo]riginal +upload +date\s*\}\}\s*\)\s*\)?/, '$1{{other date|early|$2}}')
		.replace(/(\|\s*[Dd]ate\s*=\s*)(?:[Bb][Ee][Tt][Ww][Ee]{2}[Nn])\s*(\d{4})(?:\s*(-)\s*(\d{1,2}))?(?:\s*(-)\s*(\d{1,2}))?\s*(?:[Aa][Nn][Dd]|\W)\s*(\d{4})(?:\s*(-)\s*(\d{1,2}))?(?:\s*(-)\s*(\d{1,2}))?\s*\.?\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)?\s*(?:\(?\{\{\s*[Dd]ate\s*\|\s*\d{4}s*\|\s*\d{1,2}s*\|\s*?\d{1,2}\s*\}\}\s*\(\s*\{\{\s*[Oo]riginal +upload +date\s*\}\}\s*\)\s*\)?)?\s*/, '$1{{other date|between|$2$3$4$5$6|$7$8$9$10$11}}\n');

	cleanup = mto_parse_date(cleanup, '(?:jan(?:uary?|\\.)?|gennaio)', '01');
	cleanup = mto_parse_date(cleanup, '(?:feb(?:ruary?|\\.)?|febbraio)', '02');
	cleanup = mto_parse_date(cleanup, '(?:(?:mar(?:ch|\\.)?)|mär[z\\.]?|marzo)', '03');
	cleanup = mto_parse_date(cleanup, '(?:apr(?:il|\\.)?|aprile)', '04');
	cleanup = mto_parse_date(cleanup, '(?:ma[iy]|maggio)', '05');
	cleanup = mto_parse_date(cleanup, '(?:jun[ie\\.]?|giugno)', '06');
	cleanup = mto_parse_date(cleanup, '(?:jul[iy\\.]?|luglio)', '07');
	cleanup = mto_parse_date(cleanup, '(?:aug(?:ust|\\.)?|agosto)', '08');
	cleanup = mto_parse_date(cleanup, '(?:sept?(?:ember|\\.)?|settembre)', '09');
	cleanup = mto_parse_date(cleanup, '(?:o[ck]t(?:ober|\\.)?|ottobre)', '10');
	cleanup = mto_parse_date(cleanup, '(?:nov(?:ember|\\.)?|novembre)', '11');
	cleanup = mto_parse_date(cleanup, '(?:de[cz](?:ember|\\.)?|dicembre)', '12')
	// reset date template
	;

	// {{date}} for single year dates
	cleanup = cleanup.replace(/(\|\s*[Dd]ate\s*=\s*)\{\{Date\|(\d{4})\}\}(\s*\.?\s*(?:<\s*BR\s*\/?>\s*)?)$/im, '$1$2$3')
		// fix single digit dates (first run)
		.replace(/(\|\s*[Dd]ate\s*=\s*\{\{\s*[Dd]ate\s*\|\d{4}\s*\|)(\d)(?!\d)/, '$10$2')
		.replace(/(\|\s*[Dd]ate\s*=\s*\{\{\s*[Dd]ate\s*\|\d{4}\s*\|\s*\d{2}\s*\|\s*)(\d)(?!\d)/, '$10$2')
		// rm parens around entirely original upload date
		.replace(/\((\{\{[Dd]ate[^}]+\}\}\s*\(\{\{original upload date\}\}\))\)/, '$1')
		// reset date template
	;

	// run twice to get both possible instances of date (the regex can conflict with itself); avoid changing format when undesired
	for (i = 0; i < 2; i++) 
	{	// A date written in format \d\d.\d\d.\d\d\d\d or \d\d.\d\d.\d\d is very common in Germany, apparently
		if (project_code === 'de') 
		{	cleanup = cleanup.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(0?\d|[1-2][0-9]|3[0-1]).(0?\d|1[0-2]|\d).(\d{4})(?!\d)/, '|$1{{Date|$4|$3|$2}}')
			// FIXME: change this in the year 2020!!
				.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(0?\d|[1-2][0-9]|3[0-1]).(0?\d|1[0-2]|\d).([01]\d)(?!\d)/, '|$1{{Date|20$4|$3|$2}}')
				.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(0?\d|[1-2][0-9]|3[0-1]).(0?\d|1[0-2]|\d).([2-9]\d)(?!\d)/, '|$1{{Date|19$4|$3|$2}}')
		// reset date template
		;}

		cleanup = cleanup.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(1[3-9]|2\d|3[01])[-\\/.\s,]+(0?\d|1[0-2]|\d)[-\\/.\s,]+(\d{4})(?!\d)/g, '|$1{{Date|$4|$3|$2}}')
			.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(0?\d|1[0-2]|\d)[-\\/.\s,]+(1[3-9]|2\d|3[01])[-\\/.\s,]+(\d{4})(?!\d)/g, '|$1{{Date|$4|$2|$3}}')
			.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(0?\d|1[0-2]|\d)[-\\/.\s,]+\2[-\\/.\s,]+(\d{4})(?!\d)/g, '|$1{{Date|$3|$2|$2}}')
			.replace(/\|(\s*[Dd]ate\s*=[^{|]*\s*)(\d{4})\s*([/\\.\s])\s*(\d{1,2})\s*\3\s*(\d{2})(?!\d)/g, '|$1{{Date|$2|$4|$5}}')
			.replace(/\|(\s*[Dd]ate\s*=[^{|]*\s*)(\d{4})\s*-\s*(\d)\s*-\s*(\d{2})(?!\d)/g, '|$1$2-0$3-$4')
			.replace(/\|(\s*[Dd]ate\s*=[^{|]*\s*)(\d{4}) ?- ?(\d{2}) ?- ?(\d{2})(?!\d)(?!\s*\n)(?![|}])/g, '|$1{{Date|$2|$3|$4}}')
//	Because a date followed by more text made troubles, Perhelion corrected it by changing it to the template. 
//  Since that action, the date had been made able to handle when it is followed by text, and the template is not anymore necessary. 
//  Perhelions date change occurs at so many different places and is difficult to analyze; so it is let as-it-does 
//  and just finally reset from the template to the standard form:
			.replace(/[Dd]ate(\s*=\s*)\{\{Date\|(\d\d\d\d)\|(\d\d)\|(\d\d)\}\}/g, 'date$1$2-$3-$4')	//reset date template
	;}

	// run twice to get both possible instances of date (the regex can conflict with itself); avoid changing format when undesired
	for (i = 0; i < 2; i++) {
		cleanup = cleanup.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(\d|1[0-2]|\d(?!\d))[-\\/.\s,]+(\d{4})(?!(?:[-\\/.\s,]+\d{1,2}|\d))/mg, '|$1{{Date|$3|$2}}')
			.replace(/\|(\s*[Dd]ate\s*=\s*(?:[^{|]*\n)?)(\d{4})[-\\/.\s,]+(0\d|1[0-2]|\d(?!\d))(?!(?:[-\\/.\s,]+\d{1,2}|\d))/mg, '|$1{{Date|$2|$3}}')
	// reset date template
	;}

	// don't need to have upload date if a date is already provided
	cleanup = cleanup.replace(/(\|\s*[Dd]ate\s*=\s*\{\{\s*[Dd]ate[|\s\d]+\}\})\s*.?(?:\s*;?\s*<\s*[Bb][Rr]\s*\/?>)?\s*\(?(?:\{\{\s*[Dd]ate[|\s\d]+\}\}\s*\(\s*(?:\{\{\s*[Oo]riginal +upload +date\s*\}\}|first +version)\s*?\)|\{\{\s*[Oo]riginal +upload +date\s*\|[\d-\s]+\}\})(?:\s*;?\s*\{\{\s*[Dd]ate[|\s\d]+\}\}\s*\(\s*last\s+version\))?\s*?\)?/, '$1')
		// fix single digit dates, which confuses the parser below
		.replace(/\{\{\s*[Dd]ate\s*\|\s*(\d+)\s*\|\s*(\d)\s*((?:\|\s*\d+\s*)?)\}\}/g, '{{Date|$1|0$2$3}}')
		.replace(/\{\{\s*[Dd]ate\s*\|\s*(\d+)\s*\|\s*(\d{2})\s*\|\s*(\d)\s*\}\}/g, '{{Date|$1|$2|0$3}}')

		.replace(/\{\{\s*[Dd]ate\s*\|\s*(\d{4})\s*\|(\d{1,2})\s*\|(\d{1,2})\s*\}\}\s*\(first version\)\s*;?\s*\{\{\s*[Dd]ate\s*\|\s*\1\s*\|\2\s*\|\3\s*\}\}\s*\(last version\)/, '{{Date|$1|$2|$3}} ({{original upload date}})')
		.replace(/\{\{\s*[Dd]ate\s*\|\s*(\d{4})\s*\|(\d{1,2})\s*\|(\d{1,2})\s*\}\}\s*\(first version\)\s*/m, '{{Date|$1|$2|$3}} ({{original upload date}})')
		.replace(/\|(\s*[Dd]ate\s*=\s*(\{\{[Dd]ate\|.+\}\}))\s*(?:<\s*br\s*\/?>)?\s*\n*\s*\(?\2\s*\(\s*\{\{\s*[Oo]riginal upload date\s*\}\}\s*\)?\)?/, '|$1')
		.replace(/(?:\(\s*)?\{\{\s*[Dd]ate\s*\|\s*(\d{4})\s*\|(\d{1,2})\s*\|(\d{1,2})\s*\}\}\s+\(?\{\{\s*[Oo]riginal upload date\s*\}\}\)?(?:\s*\))?/mg, '{{original upload date|$1-$2-$3}}')
		.replace(/\d{2}:\d{2}, +\d{1,2} +[A-Z][a-z]+ +\d{4} +\([A-Z]{3,4}\)(?:<\s*br\s*\/?>)?\s*\n*\s*(?:\(\s*)?(\{\{\s*[Oo]riginal +upload +date\s*\|\s*[\d-]+\s*\}\})(?:\s*\))?/mg, '$1') // ~~~~~ for date is ignored if upload date present

		// {{date}} format -> xxxx-yy-zz format where possible
		.replace(/\|(\s*[Dd]ate\s*=\s*)(\d{1,2})\/(\d{1,2})\/(\d{1,2})/, '|$120$4-$3-$2') // Ashoppio
		.replace(/=(\s*)\{\{\s*[Dd]ate\s*\|\s*(\d{4})\s*\|(\d{2})\s*\|(\d{2})\s*\}\}(\s*\|)/mg, '=$1$2-$3-$4$5')
		.replace(/=(\s*)\{\{\s*[Dd]ate\s*\|\s*(\d{4})\s*\|(\d{1,2})\s*\}\}(\s*\|)/mg, '=$1$2-$3$4')
		.replace(/=(\s*)\{\{\s*[Dd]ate\s*\|\s*(\d{4})\s*\}\}(\s*\|)/mg, '=$1$2$3')
		// |date=YYYY-MM-DD HH:MM:SS is improperly parsed above; fix that ISOdate
		.replace(/(\|\s*[Dd]ate\s*=\s*)\{\{\s*[Dd]ate\s*\|\s*(\d{4})\s*\|\s*(\d{2})\s*\|(\d{2})\s*\}\}\s*(\d{2}:\d{2}(?::\d{2})?)/, '$1$2-$3-$4 $5')
		.replace(/(\|\s*[Dd]ate\s*=\s*)\{\{\s*[Ii]SOdate\s*\|\s*(\d{4}-\d{2}-\d{2} +\d{2}:\d{2}(?::\d{2})?)\s*\}\}(\s*(?:\|\s*(?:[Dd]escription|[Dd]ate|[Aa]uthor|[Pp]ermission|[Oo]ther[ _]+versions|[Oo]ther[ _]+fields)\s*=|\}\}))/, '$1$2$3')
		.replace(/\{\{[Ii]SOdate *\| *(\d{4}-\d{1,2}-\d{1,2}) *(?:\d{2}:\d{2}:\d{2})? *\}\}/g, '$1') 
		// rm unnecessary comments, templates that upload bot needlessly transports over; __NOTOC__
		.replace(/^\s*\{\{\s*(?:[Tt]emplate[\s_]+other|[Tt]ls?x?p?|FULLROOTPAGENAME|[Nn]s[\s_]+has[\s_]+subpages|!\)|\(!|!!?|[a-zA-Z]?[Mm]box|[Jj]ULIANDAY|[Ll]an|[Rr]ed|[Ee]n|[Mm]ax(?:\/2)?|[Cc]ite[\s_]+book|[Cc]itation\/core|[Cc]itation\/make[\s_]+link)\s*\|?\s*\}\}\s*$/gm, '')
		.replace(/\{\{\s*([Pp][Dd]-ineligible|[Pp]D-[Tt]rivial|[Pp]D-uegnet|[Pp]D-Ineligible)\s*\|\s*Commons\s*=[^}|]+\}\}/, '{{$1}}')
		.replace(/\{\{P[Dd]-user-w\s*\|\s*(als|ar|az|bg|ca|cs|da|de|en|es||fa|fi|fr|he|hi|hr|hu|it|ja|lt|ml|nl|nn|no|pl|pt|ro|ru|sk|sl|th|uk|vls|zh)\s*\|\s*wikipedia\s*\|\s*([^|}]+)\s*\}\}/m, '{{PD-user-$1|$2}}')
		.replace(/\{\{P[Dd]-user\s*\|\s*([^|}]+)\s*\|\s*(als|ar|az|bg|ca|cs|da|de|en|es|fa|fi|fr|he|hi|hr|hu|it|ja|lt|ml|nl|nn|no|pl|pt|ro|ru|sk|sl|th|uk|vls|zh)\s*\}\}/m, '{{PD-user-$2|$1}}');

	if (!cleanup.match(/\{\{\s*[Cc]heck\s+categories/)) cleanup = cleanup.replace(/^__NOTOC__\s*?\n/gm, '')
	// reset date template
	;
	// specific silliness reserved for de.wp transfers
	if (username && project_code) {
		self_author_text = `[[:${project_code}:User:${username}|${username}]] at [http://${project_code}.wikipedia.org ${project_code}.wikipedia]`;
		cleanup = cleanup.replace(de_wp_re, `{{Self|author=${self_author_text}|cc-by-sa-3.0|cc-by-sa-3.0-de|GFDL|migration=redundant}}`)
	// reset date template
	;}
	
	// Template loop for GFDL fix
	cleanup = cleanup
		.replace(/(\{\{\s*[Ss]elf2?\s*(?:\|[^|}]+)*\|)\s*GFDL-(?:user-[a-z]+-with-disclaimers|self-with-disclaimers|self-en)/,
			'$1GFDL-with-disclaimers')
		.replace(/(\{\{\s*[Ss]elf2?\s*(?:\|[^|}]+)*\|)\s*GFDL-(?:self|user(?:-[a-z]+)?)(?:-no-disclaimers)?/,
			'$1GFDL')
		.replace(/(\{\{\s*[Ss]elf2?\s*(?:\|[^|}]+)*\|GFDL)\s*\|\s*GFDL\|/,
			'$1|')
		// unknown template
		.replace(/(\|\s*date\s*=\s*)(?:unknown(?: +date)?|not known|desconocido|desconhecido|unbekannt)\s*\.?\s*(?:(?:<\s*br\/?>)?\n?|\n)(?:(?!\|).+\n)?(\s*(?:\||\}\}))/i,
			'$1{{unknown|date}}\n$2')
		.replace(/(\|\s*author\s*=\s*(?:[^{}<>|]+)?)(?:unknown(?: +author)?|not known|desconocido|desconhecido|unbekannt)\s*\.?\s*(?:(?:<\s*br\/?>)?\n?|\n)((?!\|).+\n)?(\s*(?:\||\}\}))/i,
			'$1{{unknown|author}}\n$2$3')
		// useless other_versions messages
		.replace(/(\|\s*other[_ ]versions\s*=\s*)(?:no|none(?:\s+known)?|nein|yes|keine|-+)\s*\.?\s*\n(\s*(?:\||\}\}))/i,
			'$1\n$2')
		// duplicate templates
		.replace(/\{\{\s*(?:[Cc]c-by-sa-3.0-migrated|[Ll]icense +migration +not +eligible)\s*\}\}\s*?\n?/, '');

	cleanup = iterative_replace(/\{\{\s*(?:[Tt]rademark(?:ed)?|[Tt][Mm]|[Ss]VG-Logo|®)\s*\}\}([\s\S]*)\{\{\s*(?:[Tt]rademark(?:ed)?|[Tt][Mm]|[Ss]VG-Logo|®)\s*\}\}/, '{{Trademarked}}$1', cleanup);
	cleanup = iterative_replace(/\{\{\s*(?:[Mm]oney-US|[Pp]D-USGov-money)\s*\}\}([\s\S]*)\{\{\s*(?:[Mm]oney-US|[Pp]D-USGov-money)\s*\}\}/, '{{PD-USGov-money}}$1', cleanup);
	cleanup = iterative_replace(/\{\{\s*(?:[Pp]D-UKGov|[Pp]D-BritishGov|[Dd]omaine[ _]+public[ _]+UK|[Dd]omainePublicGouvUK|[Pp]D-UK-Gov|نگاره[ _]+بریتانیا)\s*\}\}([\s\S]*)\{\{\s*(?:[Pp]D-UKGov|[Pp]D-BritishGov|[Dd]omaine[ _]+public[ _]+UK|[Dd]omainePublicGouvUK|[Pp]D-UK-Gov|نگاره[ _]+بریتانیا)\s*\}\}/, '{{PD-UKGov}}$1', cleanup);
	cleanup = iterative_replace(/(\{\{\s*(?:[Cc]c-by-sa-3.0,2.5,2.0,1.0|[Cc]c-by-sa-all)\s*\}\}\s*?){2,}/, '{{Cc-by-sa-3.0,2.5,2.0,1.0}}', cleanup);
	cleanup = iterative_replace(/\{\{\s*(?:[Pp]D-China|[Pp]D-cn|[Cc]hina-PD)\}\}([\s\S]*?)\{\{\s*(?:[Pp]D-China|[Pp]D-cn|[Cc]hina-PD)\s*\}\}/, '{{PD-China}}$1', cleanup);
	cleanup = iterative_replace(/\{\{\s*(?:PD-USGov(-[A-Za-z]+)(-[A-Za-z-]+?))\s*\}\}([\s\S]*?)\{\{\s*PD-USGov\1?\2\s*\}\}/, '{{PD-USGov$2}}$3', cleanup);
	cleanup = iterative_replace(/\{\{\s*bad *jpe?g\s*\}\}([\s\S]*)\{\{\s*bad *jpe?g\s*\}\}/i, '{{badjpeg}}$1', cleanup);
	cleanup = iterative_replace(/\{\{\s*(?:(?:convert *to|to|should *be)? *svg|vectorize)\s*\}\}([\s\S]*)\{\{\s*(?:(?:convert *to|to|should *be)? *svg|vectorize)\s*\}\}/i, '{{Convert to SVG}}$1', cleanup);
	if (cleanup.search(/\{\{\s*(?:[Pp]D-text(?: *|-)logo|[Pp]d-textlogo|[Tt]extlogo|[Pp]D-Markenrecht)\s*(?:\|[\s\S]*?)?\}\}/) !== -1 && cleanup.search(/\{\{\s*(?:[Tt]rademark(?:ed)?|[Tt][Mm]|[Ss]VG-Logo|®)\s*(?:\|[\s\S]*?)?\}\}/) !== -1) cleanup = iterative_replace(/\{\{\s*Bild-LogoSH\s*(?:\|[\s\S]*?)?\}\}\s*?\n?/, '', cleanup);

	// templates which are unneeded immediately after license header FIXME: IT FREEZES
	// cleanup = iterative_replace(/^((==)\s*.+\s*\2\s*(?:{{.+?}}\s*)*\n)\s*{{\s*(?:description +missing|date|-|clr)\s*}}\s*?$/im, '$1', cleanup);
	// cleanup = iterative_replace(/^((==)\s*.+\s*\2\s*\n(?:\s*\{\{.+\}\}\s*\n)*)\s*\{\{\s*hidden\s*\}\}\s*?$/im, '$1', cleanup);

	// redundant pd-old
	cleanup = cleanup.replace(/\{\{\s*[Pp]D-old\s*\}\}\s*?\n?\s*\{\{\s*[Pp]D-old-100\s*\}\}/, '{{PD-old-100}}')
		// pd-art cleanup
		.replace(/\{\{\s*([Pp][Dd]-[Aa]rte?|[Bb]ild-PD-Kunst|[Pp]D-kunst)\s*\}\}\s*?\n?\s*\{\{\s*([Pp]D-[^|]+)\s*\}\}/, '{{$1|$2}}')
		.replace(/\{\{\s*(?:[Pp][Dd]-[Aa]rte?|[Bb]ild-PD-Kunst|[Pp]D-kunst)\s*\|\s*[Pp][Dd]-old-(70|100)\s*\}\}/, '{{PD-art-$1}}')
		.replace(/\{\{\s*(?:[Pp][Dd]-[Aa]rte?|[Bb]ild-PD-Kunst|[Pp]D-kunst)\s*\|\s*[Pp][Dd]-old\s*\}\}/, '{{PD-art-70}}')
		.replace(/\{\{\s*[Pp][Dd]-US\s*\}\}\s*\{\{\s*([Pp][Dd]-[Aa]rte?|[Bb]ild-PD-Kunst|[Pp]D-kunst)\s*\}\}/, '{{$1|PD-US}}');

	// insert header, but only above an {{information}} template and if there is at least one other header
	// on the page already, for asthetic purposes (request by Leyo)
	// TODO: what in the world is going on here?
	headercheck = cleanup.replace(/([^{|]|\s)\{(?!\{)/, '$1MTOWASHERE'); // removing markings which will confuse parser
	// headercheck = cleanup.replace(/((?:\{\{)+)\{(?!\{)/, "$1MTOWASHERE");
	headercheck = cleanup.replace(/([^}]|\s)\}(?!\})/, '$1MTOWASHERE');
	// headercheck = cleanup.replace(/((?:\}\})+)\}(?!\})/, "$1MTOWASHERE");
	headercheck = iterative_replace(/\{\{(?:[^{|]|\n)+?\}\}/g, 'MTOTEMPLATE', headercheck); // only headers that aren't already inside a template will suffice, per what seems right to me
	if (headercheck.match(/\n(=+).+\1\s*\n/)) cleanup = cleanup.replace(/^\s*(\{\{[Ii]nformation\s*\|)/, '== {{int:filedesc}} ==\n$1');

	/* uploader_status was set wayyyy above; this is a second run (lazy coding) */
	if (uploader_status === NOTAUTHOR) cleanup = mto_uploader_isnt_author(cleanup);

	// further cleanup second upload type
	cleanup = cleanup.replace(/(\|\s*[Ss]ource\s*=\s*.*)(\n\s*\|(?:[\s\S]*\|)?\s*[Aa]uthor\s*=\s*.*)\s*(\([Tt]ransferr?ed +by +\[\[.+\]\]\))/, '$1 $3$2')
		.replace(/(\(Originally uploaded on [a-z-]{1,6}.w[a-z]+)\)?\s*-\s*\(?([Tt]ransferr?ed +by +\[\[.+\]\]\))/, '$1 - $2')
		.replace(/([Aa]uthor\s*=.*?)\s*(?:\([Tt]ransferr?ed +by +\[\[.+\]\]\))/, '$1') // sometimes leftover text from {{Self}} template
		// {{original file page|…}}
		.replace(/The\s+original\s+description\s+page\s+(?:is\/was|is|was)\s+\[http:\/\/([a-z-]+.wik[a-z]+).org\/w\/index.php\?title=.+?(?::|%3[Aa])(.+?)\s+here(?:\].|.\])\s+All\s+following\s+user\s+names\s+refer\s+to\s+\1./g, '{{original file page|$1|$2}}')
		.replace(/This file was originally uploaded at ([a-z-]+.wik[a-z]+) as \[http:\/\/\1.org\/wiki\/.+?(?::|%3[Aa])(\S+?)\s+[^\]]+\], before it was transferr?ed to Commons./, '{{original file page|$1|$2}}')
		// {{transferred from|…}}
		.replace(/(\|\s*[Ss]ource\s*=\s*|<\s*br\s*\/?\s*>\s*|\n\s*\*)Transferr?ed from \[(?:https?:)?\/\/([a-z-]{2,}.w[a-z]+).org\/? \2\](?:(?:; transferr?ed|; transfer was stated to be made)?(?: to Commons)? by \[\[User:([^\]]+)\]\])?(?: using (\[\[?.+?\]\]?))?.?/g, '$1{{transferred from|1=$2|2=$3|3=$4}}')
		.replace(/\{\{transferred from\|1=([^|=}]*)\|2=([^|=}]*)\|3=([^|=}]*)\}\}/g, '{{transferred from|$1|$2|$3}}')
		.replace(/\{\{transferred from\|(1=)?(.*)\|(2=)?(.*)\|(3=)?\}\}/g, '{{transferred from|$1$2|$3$4}}')
		.replace(/\{\{transferred from\|(1=)?(.*)\|(2=)?\}\}/g, '{{transferred from|$1$2}}')
		.replace(/(\{\{transferred from\|.*?\|.*?\|\s*(?:3\s*=)?\s*)\[(?:https?:)?\/\/(?:tools.wikimedia.de|toolserver.org)\/~magnus\/commonshelper.php ([Cc][Oo][Mm][Mm][Oo][Nn][Ss][Hh][Ee][Ll][Pp][Ee][Rr])\](\s*\}\})/, '$1$2$3')
		// {{Self|self|self…|license}} bug
		.replace(/\{\{(?:\s*[Ss]elf2?\s*\|){2,}/g, '{{Self|')

		// {{original description page|…}} Fix urlencode filename QUERY
		.replace(/\{\{\s?[Oo]riginal description(?: page)?\s?\|\s?[^|\n]+\|\s*([^\n]+)\s?\}\}/, function (m, p1) {
			return m.replace(p1, p1.replace(/ /g, '+'));
		})

		// {{user at project|…}}
		.replace(/\[\[:([a-z-]{2,})(?::[A-Za-z]+)?:[Uu][Ss][Ee][Rr]:([^|[\]]+)\|\2\]\]\s+at\s+\[(?:https?:)?\/\/\1.(w[a-z]+).org\/? [^|[\]]+\]/g, '{{user at project|1=$2|2=$3|3=$1}}')
		.replace(/\[\[:[A-Za-z]+:([a-z-]{2,}):[Uu][Ss][Ee][Rr]:([^|[\]]+)\|\2\]\]\s+at\s+\[(?:https?:)?\/\/\1.(w[a-z]+).org\/? [^|[\]]+\]/g, '{{user at project|1=$2|2=$3|3=$1}}')
		.replace(/\{\{user at project\|1=([^\]|=]+?)\|2=([^\]|]+?)\|3=([^\]|]+?)\}\}/g, '{{user at project|$1|$2|$3}}');

	// move category related stuff to bottom
	/* jslint unparam: true */
	function replaceCat($0, $1, $2, $3, $4, $5, $6) {
		$5 = $5 || '';
		$6 = $6 || '';
		cleanup = $1 + $6;
		return `[[Category:${$2.toUpperCase()}${$3}${$4 || ''}]]${$5}`;
	}
	// cleanup += "\n";
	while (catLimit--) 
	{	nextcat = cleanup.replace(re_cat, replaceCat);
		if (nextcat === cleanup) break;
		// if (/\[\[Category:Uploaded with UploadWizard\]\]/.test(nextcat)) continue; // exclude deprecated cat
		categories.push(nextcat);
		hascat = true;
		// cleanup = cleanup.replace(re_cat, "$1$6");
	}
	categories = categories.sort().join('\n');

	re_unc = /^([\s\S]*)\{\{\s*([Uu]ncat(?:egorized)?(?:\s*\|(?:.|\n)*?)*?)\s*\}\}(?:(\s*%%%MTOCOMMENT\d+%%%)?\s*?\n)?([\s\S]*?)$/;
	unc = cleanup.replace(re_unc, '{{$2}}$3\n');
	if (unc !== cleanup) {
		// positive match: uncat present
		// even if categories isn't empty, they might be hidden categories, so we don't want to remove {{uncat}}
		categories = unc + categories;
		cleanup = cleanup.replace(re_unc, '$1$4');
	}
	re_ds = /^([\s\S]*)\{\{\s*DEFAULTSORT\s*:([^[\]{]*?)\}\}(?:(\s*%%%MTOCOMMENT\d+%%%)?\s*?\n)?([\s\S]*?)$/;
	ds = cleanup.replace(re_ds, '{{DEFAULTSORT:$2}}$3\n');
	if (ds !== cleanup) 
	{	// positive match: DEFAULTSORT present
		categories = ds + categories;
		cleanup = cleanup.replace(re_ds, '$1$4');
	}
	re_chc = /^([\s\S]*)(\{\{\s*[Cc]heck +categories[^}]+\}\})(\s*%%%MTOCOMMENT\d+%%%)?(\s*?\n[\s\S]*?)$/;
	chc = cleanup.replace(re_chc, '$2$3\n');
	if (chc !== cleanup) 
	{	// positive match: checkcategories present
		categories = chc + categories;
		cleanup = cleanup.replace(re_chc, '$1$4');
	}
	re_cs_chc = /^([\s\S]*)(%%%MTOCSCOMMENT\d+%%%)(\s*?\n[\s\S]*?)$/;
	cs_chc = cleanup.replace(re_cs_chc, '$2\n');
	if (cs_chc !== cleanup) {
		// positive match: commonsense comment present
		categories = cs_chc + categories;
		cleanup = cleanup.replace(re_cs_chc, '$1$3');
	}
	cleanup = cleanup.replace(/\s*$/, '\n\n') // we want exactly one trailing newline character
					.replace(/\}\}\n\n\*/g, '}}\n*')   // there was an erroneous double nl
					.replace(/\}\}\n\n\}\}\n/g, '}}\n}}\n');  // there was an erroneous double nl
		
	// remove duplicate categories
	categories = iterative_replace(/\[\[Category:(.+?)(\s*\|[^\]]*)?\]\](.*?)\n((?:.+\n)*?)\[\[Category:\1(?:\s*\|[^\]]*)?\]\](.*?)\n/, '[[Category:$1$2]]$3$5\n$4', categories);
	categories = iterative_replace(/^\s*%%%MTOCOMMENT\d+%%%s*$/m, '', categories);
	cleanup += categories;


	// add {{int:license-header}} where no license header exists, and it unambiguously would be appropriate
	// first, search for end of {{Information}} template, if applicable
	infoend = cleanup.search(/\{\{\s*[Ii]nformation/);
	infosry = cleanup.search(/int:filedesc/);
	if (infosry === -1 && infoend >= 0) 
	// add also summary {{int:filedesc}}
	{	cleanup_pre  = cleanup.substr(0, infoend);
		cleanup_post = cleanup.substr(infoend);
		cleanup = cleanup_pre + '== {{int:filedesc}} ==\n' + cleanup_post;
		infoend = infoend + 23; 
	}

	for (template_level = 1; template_level > 0 && infoend > -1; undefined) {
		infoend += 2;
		opennext = cleanup.indexOf('{{', infoend);
		closenext = cleanup.indexOf('}}', infoend);

		if (closenext === -1) 
		{	infoend = -1;
			break;
		}

		if (opennext < closenext && opennext !== -1) 
		{	infoend = opennext;
			template_level++;
		} else 
		{	infoend = closenext;
			template_level--;
		}
	}
	if (infoend === -1) infoend = 0;
	else infoend += 2;

	cleanup_pre = cleanup.substr(0, infoend);
	cleanup_post = cleanup.substr(infoend);
	cleanup_post = cleanup_post.replace(/^((?:\s*?\{\{\s*(?:(?:should *be|(?:convert *)?to) *(?:svg|png)|svg|vectorize|artifacts|blurry|low quality[\w-., ]*|wrong +license|disputed[\w-., ]*|bad *(?:jpe?g|gif|svg)|crop|cleanup[\w-., ]*|ifc|(?:object +|globe +)?location[\w-., ]*)\s*(?:\|\s*[^}]+)?\}\})*)\s*(\{\{\s*(?:fop[\w-., ]*|freedom +of +panorama|cc(?:-zero|-by[\w-., ]*)?|gfdl[\w-., ]*|pd[\w-., ]*|trademark|(?:image|remove +)water +mark|self2?|Propio|[Сс]ебе|jac|multilicense[\w-., ]*|attribution[\w-., ]*|fal[\w-., ]*|GPL[\w-., ]*|wik(?:i[mp]edia|isource|iquote|iversity|tionary)-screenshot|wikiportrait|wikimedia +project +screenshot|flickrr?eview|license *review|ipernityreview|openphotoreview|panoramioreview|PD-?review|Picasa(?:review|web)|GFDL[\w-., ]*)\s*(?:\|\s*[^}]+)?\}\})/i, '$1\n== {{int:license-header}} ==\n$2');
	cleanup = cleanup_pre + cleanup_post;


	// remove {{ImageUpload|…}} (outside comments)
	cleanup = cleanup.replace(/\{\{\s*[Ii]mageUpload\s*(?:\|.+?)?\}\}(?:(\n)\n*)?/, '$1')
		// remove {{ImageUpload|…}} (inside comments)
		.replace(/<!--\s*\{\{\s*[Ii]mageUpload\s*(?:\|.+?)?\}\}\s*-->(?:(\n)\n*)?/, '$1')
		// unnecessary transfer message for User:Boteas (per request from Leyo)
		.replace(/(\|\s*[Ss]ource\s*=[\s\S]+?)\s*(?:<\s*[Bb][Rr]\s*\/?>\s*|\s+)[Tt]ransferr?ed +from +\[?[a-z-.:/ ]+\]? +to +[Cc]ommons +by +\[\[\s*[Uu]ser\s*:\s*[Bb]oteas\]\] .+/i, '$1')
		.replace(/(\|\s*[Ss]ource\s*=\s*[Tt]ransferr?ed +from +\[?[a-z-.:/ ]+\]? +to +[Cc]ommons) +by +\[\[\s*[Uu]ser\s*:\s*[Bb]oteas\]\](?: +using +\[[^\]]+\])?\.?\s*$/mi, '$1')
		// remove transfer message if it doesn't communicate anything, and other text isn't present
		.replace(/(\|\s*[Ss]ource\s*=[\s\S]{3,}?)\s*(?:<\s*[Bb][Rr]\s*\/?>\s*|\s+)(?:[Tt]ransferr?ed +from +\[?[a-z-.:/ ]+\]?(?: +to +[Cc]ommons)?(?: +using +\[[^\]]+\])?|\{\{\s*[Tt]ransferred +from\s*\|[^|]+?\}\})\.?\s*$/m, '$1')
		.replace(/(\|\s*[Ss]ource\s*=.+?)\s*(?:<\s*[Bb][Rr]\s*\/?>\s*)(\|\s*(?:[Dd]ate|[Aa]uthor|[Pp]ermission|[Oo]ther[_ ]versions)\s*=)/, '$1\n$2') // rm extra <br>
		// move down transfer message
		.replace(/(\|\s*[Ss]ource\s*=\s*)(\{\{\s*[Tt]ransferred[ _]+from\|.*?\}\})\.?\s*(?:<\s*[Bb][Rr]\s*\/?>)?\s*(\{\{\s*[Oo]riginal[ _]+text[\s\S]+?=+\s*\{\{\s*[Oo]riginal[ _]+upload[ _]+log\s*\}\}\s*=+\s*)/, '$1$3$2 ')
		.replace(/(\|\s*[Ss]ource\s*=\s*\S[\s\S]+?\s*)(?:\s*<\s*[Bb][Rr]\s*\/?>.?)?\s*\*?\s*\(\s*(\{\{\s*[Tt]ransferred[ _]+from\|.*?\}\})\s*\).?([\s\S]+?\{\{\s*[Oo]riginal[ _]+upload[ _]+log\s*\}\}\s*=+\s*)/, '$1$3$2 ')
		.replace(/(\|\s*[Ss]ource\s*=\s*\S[\s\S]+?\s*?)(?:\s*<\s*[Bb][Rr]\s*\/?>.?)?\s*\*?\s*(\{\{\s*[Tt]ransferred[ _]+from\|.*?\}\}).?([\s\S]+?\{\{\s*[Oo]riginal[ _]+upload[ _]+log\s*\}\}\s*=+\s*)/, '$1$3$2 ');

	// rebuild <nowiki>, <!-- --> tags
	cleanup = rebuild_comments_nowikis(cleanup, tokens, true);
	// spacing 0
	cleanup = iterative_replace(/((=+)\s*\{\{\s*int\s*:\s*license-header\s*\}\}\s*\2*\n(?:\s*?\{\{.+\}\}\n\s*?)*(?:\s*?\{\{.+\}\}\n\s*?))\s*\n+/, '$1', cleanup)

		// spacing 1
		.replace(/(dimensions\s*\|\s*comment)(\s*\n){2,}\*/i, '$1\n*') // inexplicably, this bothers me beyond measure

		// completely empty nowikis… no reason for these
		.replace(/\s*(?:''|<small>)\s*<nowiki>\s*<\/nowiki>\s*(?:''|<\/small>)\s*?\n/gm, '\n') // completely empty… just get rid of whole clause
		.replace(/\n<!-- Templates .+ do not appear to exist on commons. -->\s*\n/, '\n');
	if (hascat) cleanup = cleanup.replace(/<!--\s*remove\s*this\s*line\s*once\s*you\s*have\s*added\s*categories\s*-->\s*/i, '');

	// spacing 2 (need to take apart comments again)
	cnwt = parse_comments_nowikis(cleanup);
	cleanup = cnwt[0];
	tokens = cnwt[1];
	cleanup = cleanup.replace(/^\s+((=+)\s*\{\{\s*[Ii]nt\s*:\s*[Ff]iledesc\s*\}\}\s*\2\s*\n)/, '$1')
		.replace(/\n+(=+)(.+)\1\s*?\n+/g, '\n\n$1$2$1\n')
		.replace(/([^=])\s*(\[\[Category:.*?\]\]|\{\{\s*[Uu]ncat(?:egorized)?[\s\S]*?\s*\}\}|\{\{\s*DEFAULTSORT\s*:([^[\]{]*?)\}\}|\{\{\s*[Cc]heck +categories[\s\S]*?\s*\}\}|<!--\s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Ii][Ee][Ss]\s*[Bb][Yy]\s*(?:[Cc][Oo][Mm][Mm][Oo][Nn][Ss][Ee][Nn][Ss][Ee]|[Cc][Hh][Ee][Cc][Kk][Uu][Ss][Aa][Gg][Ee])\s*-->)/, '$1\n\n$2');
	cleanup = rebuild_comments_nowikis(cleanup, tokens, false);

	if (textbox.length) 
	{	textbox.val(cleanup);
		let sumTxt = sum.value.trim();
 	 //	if	(sumtext.slice(-igensum.length) === igen)							//  + '←←' + igen
 	 //	{	sumtext = sumtext.slice (0, -igensum.length) + summary + igensum;	//
 	 //		summary = '~';														//
	 //	}
		sum.value = `${sumTxt}+${summary}`;			//ed_wdh⏪⏪⏪⏪⏪⏪⏪⏪⏪⏪⏪⏪⏪⏪
//		if (orgText !== cleanup.trim()) 
//		{	if (!sumTxt) sum.value = summary;
//			else if (sumTxt.indexOf(summary) === -1) sum.value = `${sumTxt}+${summary}`;	//ed_wdh 
//		}
	}
	if (fileContent)
	{	fileContent.text = cleanup;
		mw.hook('gadget.cleanup.done').fire(fileContent);
	}
}

function mto_cleanup_ts()
{	$('<form>', 
	{	action: '//tools.wmflabs.org/magog/do_cleanup.php',
		method: 'post',
		style: 'display:none'
	}).append($('<input>', 
	{	name: 'image',
		value: pn
	})).append($('<textarea>', 
	{	name: 'text',
		text: getTextbox().val()
	})).append($('<input>', 
	{	name: 'summary',
		value: sum.value
	}))
		.appendTo('body')
		.submit();
}

function post_cleanup_ts() {
	let textbox = getTextbox(),
		cleanupTmp = textbox.val(),

		// store comments, nowikis as is to avoid changing them
		cnwt = parse_comments_nowikis(cleanupTmp),
		cleanup = cnwt[0],
		tokens = cnwt[1];

	// mark as bot move checked
	cleanup = bot_move_checked(cleanup);

	// restore comments
	cleanup = rebuild_comments_nowikis(cleanup, tokens, false);

	// add to textbox
	textbox.val(cleanup);

	// make a null edit to the edit summary, to turn off the MediaWiki flag which incorrectly warns that the summary is empty
	sum.value += ' ';
}

function fastCleanup() {
	let textbox = getTextbox(),
		cancel = 0,
		modal;

	function escape(text) {
		let escapeChars = {
			'&': '&amp;',
			'<': '&lt;',
			'>': '&gt;',
			'"': '&quot;',
			'\'': '&#39;'
		};
		return text.replace(/[&<>"']/g, s => escapeChars[s]);
	}

	function showMessage(backgroundColor, type, message) {
		modal
			.dialog('option', 'height', '178')
			.dialog('option', 'width', '486')
			.css('background-color', backgroundColor)
			.html(`${type}:<br>${message}`)

			// append close button
			.append('<br><br><input type="button" id="closeButton" value="OK">')
			.find('#closeButton')
			.click(() => {
				modal.dialog('close');
			});
	}

	function error(message) {
		showMessage('#faa', 'Error', message);
	}

	function warning(message) {
		showMessage('#aaf', 'Warning', message);
	}

	function ajaxError() {
		if (!cancel) error('Unable to communicate with Toolserver. If the error persists, please contact the bot owner.');
	}

	function ajaxSuccess(response) {
		if (!cancel) {
			if (response.error) {
				error('Unknown error. If it persists, please contact the bot owner.');
			} else {
				modal.on('dialogclose', () => {
					let updateText,
						update = 1;
					switch (response.changes) {
						case 'major':
							updateText = 'Text updated.';
							break;
						case 'minor':
							updateText = 'Text updated (minor changes only).';
							break;
						default:
							updateText = 'No updates.';
							update = 0;
					}
					mw.notify(updateText);
					sum.value = response.summary;
					if (update) textbox.val(response.text);
				});
				if (response.warnings && response.warnings.length > 0) {
					let warningsText = '';
					response.warnings.forEach(function (el) {
						warningsText += `<li>${escape(el)}</li>`;
					});
					warning(warningsText);
				} else {
					modal.dialog('close');
				}
			}
		}
	}

	modal = $('<div style="text-align:center">').dialog({
		autoOpen: 0,
		closeOnEscapeType: 0,
		dialogClass: 'noClose',
		height: 140,
		modal: 1
	}).off('dialogclose') // remove previous handlers
		.on('dialogclose', () => {
			cancel = 1;
		})
		.html('Loading…')
		.dialog('open');

	modal.find('.ui-dialog-titlebar-close').hide();
	$.ajax('//tools.wmflabs.org/magog/do_cleanup_json.php', {
		dataType: 'json',
		data: {
			image: pn,
			text: textbox.val(),
			summary: sum.value
		},
		error: ajaxError,
		global: true,
		type: 'POST',
		success: ajaxSuccess
	});
}

// stolen shamelessly and modified from the add {{information}} template text
function add_toolbox_button(text, callback, title) {
	cleanBox
		.find('ul')
		.first()
		.append(
			$('<li>').append($('<a>')
				.append(text)
				.attr({
					'className': 'external',
					'title': title,
					'href': '#'
				})
				.on('click', function () {
					callback();
					return false;
				}))
		);
}

function addFunction(functionId, callback, buttonDisplayName, forceImmediateCallback, title) {
	// don't add the same function twice
	if (linksShown[functionId]) return;
	linksShown[functionId] = true;

	if (mw.util.getParamValue('functionName') === functionId) {
		$(() => {
			callback(functionId);
		});
	}

	if (!/^(edit|submit)$/.test(mw.config.get('wgAction')) && !forceImmediateCallback) {
		callback = Array.isArray(callback) ? callback[1] : function () {
			location.href = `${mw.config.get('wgScript')}?title=${encodeURIComponent(pn)}&action=edit&functionName=${functionId}`;
		};
	} else if (Array.isArray(callback)) {
		callback = callback[0];
	}

	if (buttonDisplayName) {
		$(() => {
			add_toolbox_button(buttonDisplayName, callback, title);
		});
	}
}

mw.loader.using(['mediawiki.util'], () => {
	if (fileNamespace || pn === 'Special:Upload') {
		addFunction('mto_cleanup', mto_cleanup, 'cleanup JS', pn === 'Special:Upload', '(fast, maybe unsafe)');

		if (pn !== 'Special:Upload') {
			addFunction('cleanup TS', [fastCleanup, mto_cleanup_ts], 'cleanup TS', false, 'Toolserver version (slower but safe)');
			addFunction('post_cleanup_ts', post_cleanup_ts, null);
		}
		if (getTextbox().length) {
			addFunction('own', mto_wrapper, '{{Own}}');
			addFunction('ownbased', mto_wrapper, '{{Own based}}');
			addFunction('selfphoto', mto_wrapper, '{{Self-photographed}}');
			addFunction('ownoriginaluploader', mto_wrapper, '{{Own work by original uploader}}');
		}

		$('#p-tb').append(cleanBox);
	}

	mw.libs.fastCleanup = fastCleanup;
	// window.addCleanupFunction = addFunction;
	mw.hook('gadget.cleanup.loaded').fire();
	mw.loader.state('gadget.cleanup', 'ready');
});
mw.hook('gadget.cleanup.run').add(mto_cleanup);
}(jQuery, mediaWiki));
// EOF </nowiki>