MediaWiki:Gadget-Extra-Editbuttons-persianwikitools.js

From WikiPasokh

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

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
// <nowiki> // DO NOT REMOVE THIS LINE EVER
/**
 * Wikipedia specific Persian text style improvement tools
 * Tests: [[مدیاویکی:Gadget-Extra-Editbuttons-tests.js]] [[وپ:تست]]
 * See also: [[مدیاویکی:Gadget-Extra-Editbuttons-persiantools.js]]
 */
/*global persianTools, persianToolsDictionary, autoEd*/
var persianWikiTools = (function () {
	'use strict';
	var persianMonths = ["فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"];
	//http://www.entitycode.com/
	var htmlEntityCodes = {
		"&iexcl;": "¡", "&cent;": "¢", "&pound;": "£", "&curren;": "¤",
		"&yen;": "¥", "&brvbar;": "¦", "&sect;": "§", "&copy;": "©",
		"&middot;": "·", "&times;": "×", "&rdquo;": "”", "&dagger;": "†",
		"&Dagger;": "‡", "&euro;": "€", "&laquo;": "«", "&reg;": "®",
		"&deg;": "°", "&plusmn;": "±", "&sup2;": "²", "&sup3;": "³",
		"&para;": "¶", "&sup1;": "¹", "&raquo;": "»", "&frac14;": "¼",
		"&frac12;": "½", "&frac34;": "¾", "&iquest;": "¿", "&divide;": "÷",
		"&ndash;": "–", "&mdash;": "—", "&lsquo;": "‘", "&rsquo;": "’",
		"&ldquo;": "“", "&trade;": "™", "&bull;": "•", "&hellip;": "…",
		"&permil;": "‰", "&lsaquo;": "‹", "&rsaquo;": "›", "&larr;": "←",
		"&uarr;": "↑", "&rarr;": "→", "&darr;": "↓", "&harr;": "↔",
		"&crarr;": "↵", "&minus;": "−", "&radic;": "√", "&infin;": "∞",
		"&loz;": "◊", "&spades;": "♠", "&rfloor;": "⌋", "&nbsp;": " ",
		"&ne;": "≠", "&ap;": "≈", "&approx;": "≈", "&asymp;": "≈",
		"&rceil;": "⌉", "&lfloor;": "⌊", "&diams;": "♦", "&auml;": "ä",
		"&ouml;": "ö", "&uuml;": "ü", "&szlig;": "ß", "&aring;": "å",
		"&oline; ": "‾ ", "&aelig;": "æ", "&ccedil;": "ç", "&ntilde;": "ñ",
		"&acirc;": "â", "&aacute;": "á", "&agrave;": "à", "&#36;": "$",
		"&clubs;": "♣", "&hearts;": "♥", "&Prime;": "″", "&prime;": "′",
		"&lceil;": "⌈", "&mldr;": "…", "&bullet;": "•", "&grave;": "`",
		"&pm;": "±", "&acute;": "´", "&centerdot;": "·", "&half;": "½",
		"&Auml;": "Ä", "&Ouml;": "Ö", "&Uuml;": "Ü", "&rsquor;": "’",
		"&lsquor;": "‚", "&sbquo;": "‚", "&rdquor;": "”", "&bdquo;": "„",
		"&ldquor;": "„", "&ddagger;": "‡", "&div;": "÷", "&leq;": "≤",
		"&geq;": "≥", "&le;": "≤", "&ge;": "≥"
	}; // &quot; , &amp; &lt; &gt; &#124;
	var patterns = {
		arabicDigitsEnglishContext: /[a-z]([\|a-z %"'\._:\;,\-\\\/\(\)\#\^\+\d><–\[\]&?{}](?!\|\|))*\d|(\d|[a-z])[a-z %"'\._:\;\|,\-\\\/\(\)\#\^\+\d><–\[\]&?{}]*[a-z]\d*/gi,
		arabicTagEnclosed: /\{\{(?:عربی|شروع عربی|آغاز عربی)\}\}([\s\S]*?)\{\{(?:پایان عربی)\}\}/g,
		LtRTagEnclosed: /\{\{(?:چپ چین|چپ‌چین)\}\}([\s\S]*?)\{\{(?:پایان چپ‌چین|پایان چپ چین|پایان)\}\}/g,
		argumentsBlacklist: /(?:accessdate|namespace|legend1start|image|تصویر|doi|style|شابک|عرض|bibcode|isbn|issn|pmid|arxiv|upright|upleft|padding|spacing|border|filename|نام پرونده|ایستاده)\s*\=\s*[^\|\}\]]*/gi,
		color: /#(?:[abcdef0-9]{8}|[abcdef0-9]{6}|[abcdef0-9]{3})/gi,
		//colorAsParameter: /\=\s*(?:[abcdef0-9]{8}|[abcdef0-9]{6}|[abcdef0-9]{3})(?:[\s\|\}]|$)/gi,
		// space, ", \t, \n, {, |, }, ... they will interfere with wiki markup
		decodeUriBlacklist: /(?:%20|%27|%5C|%5E|%60|%23|%25|%3C|%3E|%5B|%5D|%22|%09|%0A|%7B|%7C|%7D|%C2|%A0)/gi,
		diffLink: /\[\[(?:ویژه|Special):(?:تفاوت|Diff)\/[^\|\]]*/gi,
		englishDate: /\d{1,2},? [a-z]{3,} \d{2,4}/gi, // 3, May 2013
		fileNames: /(?:پرونده|File|تصویر|Image)\:.*?(?=\||\]|\n|$)|=.*\.(?:flac|gif|jpeg|jpg|mid|mp3|mpeg|mpg|ogg|pdf|png|svg|wav|webm)$/mgi, // don't capture | after
		fileParameter: /\|\s*(image|تصویر)\s*\=\s*.*/g,
		ipSign: /\[\[ویژه:مشارکت\u200cها.*?\]\]/g,
		isbn: /(?:ISBN|ISSN|PMID) [\d\-]*/gi,
		galleryTag: /<gallery.*?>[\s\S]*?<\/gallery>/g,
		htmlAttributes: /(?:style|perrow|colspan|color|rowspan|cellpadding|cellspacing|height|width|size|border|thumbtime|name|perrow|upright|upleft)\s*[\=\:]\s*(?:['\"].*?['\"]|[\da-z]+)/gi,
		htmlEntity: /&#\d+;/,
		imagePixelSize: /[\|=] *[x\d]+?(px|پیکسل)[\]\|\s]/g, // means it will capture |10px| and |10x10px|
		insideQuote: /[^ا]".*?"/g,
		wikilinkTargets: /\[[^\[|\]]+/g,
		nowikiTag: /<nowiki>.+?<\/nowiki>/g,
		preTag: /<pre.*?>.*?<\/pre>/g,
		insideHtmlComment: /<\!\-\-[\s\S]*?\-\->/g,
		linksOnEnglishContext: /[a-z][\:\,\. ]*\[\[[\da-z\-\, ]*/gi,
		mapFrameTag: /<mapframe.*?>[\s\S]*?<\/mapframe>/g,
		mathTag: /<math.*?>[\s\S]*?<\/math>/g,
		otherLanguagesInline: /\{\{(?:به .+?|پم|به انگلیسی|انگلیسی|عربی|متن عربی|عبارت عربی|حدیث|به عربی|به اردو|اردو|lang\-[au]r|پینگ|ping)[\s\S]*?\}\}/g,
		parameter: /\{\{\{\d+/gi,
		parenthesesAfterDigits: /\w\s?\([\w\s\.\-]*?\)/g,
		parenthesesHa: /\)ها/g,
		ref: /(?:<ref[^\/]*?>[\s\S]*?<\/ref>|<ref[^\/]*?\/>)/g, // inside <ref></ref> and <ref/>
		refname: /\<ref name\=.*?\>/g,
		citation: /\{\{\s*(?:[Cc]it|یادکرد).*?[_\s]*(?:\{\{.*?\}\}|[^\}])*\}\}/g,
		signatures: /\[\[(?:کاربر|User|بحث[ _]کاربر|User[_ ]talk)\:.*?\]\]/gi,
		sourceTag: /(<source.*?>[\s\S]*?<\/source>|<syntaxhighlight.*?>[\s\S]*?<\/syntaxhighlight>|<code.*?>[\s\S]*?<\/code>|<timeline.*?>[\s\S]*?<\/timeline>)/g,
		tagNames: /<\/?[a-zA-Z\d]*/g,
		graphTemplate: /(?:\<div[^\>]+>)?\{\{[ \_]*(?:الگو|Template)?(?:[Gg]raph\:?[^\|\}]+).*?[_\s\n]*(?:\{\{.*?\}\}|[^\}])*\}\}(?:\<\/div\>)?/g,
		templateEnglishName: /(الگو|Template):[a-z][a-z\d\-\+_]+/gi,
		templateWithEnglishName: /\{\{[ \_]*(?:الگو|Template)?[\x00-\x7a~]*\|.*?\}\}/gi,
		templateParameterName: /\|\s*(?=[a-z_]*\d)[a-z_\d]*\s*\=/gi,
		globalExceptionTag: /(<nowiki>.+?<\/nowiki>|<!--[\s]*ابر[\s]*-->.+?<!--[\s]*\/[\s]*ابر[\s]*-->|\{\{استثنای ابرابزار\|[\s\S]*?\}\})/gi,
		translatedUrl: /.(کام|نت|آی‌آر)/g,
		boxVar: /([a-zA-Z][۱۲۳۴۵۶۷۸۹۰]+) *\=/g,
		url: /\/\/.*?(?=[\s\n\|\}\]<]|$)/gi,	 // بدون https?: هم ممکن است
		url2: /\/\/.*?(?:[\s\n\|\}\]<]|$)/gi,	 //برای مواردی که نیاز باشد ادامهٔ نشانی وب را دست نزند
		urlArchive: /\/\/web\.archive\.org[^\|\}\n\<\>]+/gi,
		mediawikiFunctions: /\{\{\#(?:\{\{.*?\}\}|[^\}])*\}\}/gi,
		articleTitleParts: new RegExp('\\s' + escapeRegExp(mw.config.get('wgTitle')).split(' ').join('\\s|\\s') + '\\s', 'g'),
		catgories: /\[\[(?:رده|[Cc]ategory)\:[^\]]+\]\]/gi,
		rfd: /\{\{نظرخواهی برای حذف.*\}\}/g
	};
	
	function escapeRegExp(string) {
		return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
	}

	function escapeRE(s) {
		return s.replace(/([$()*+\-.?[\\\]^{|}])/g, '\\$1');
	}

	function descendingFromComparetor(x, y) {
		return x.from - y.from;
	}
	function removeCheckDict(text) {
		// رفع مشکل [[Special:Permalink/25174868#متن هایلایت‌شده]]
		var otext = ''
		while (otext != text) {
			otext = text
			text = text.replace(/\<span[^>]+CheckDictation\-marked[^>]+\>([^<>]+)\<\/span\>/g, '$1')
		}
		return text
	}
	function replaceExcept(text, callback, excepts) {
		var match, result = [], i, ranges, minRange, to, min, max;
		while (text !== '') {
			ranges = [];

			for (i in excepts) {
				if (excepts.hasOwnProperty(i)) {
					// a global regex should be reset before calls
					excepts[i].lastIndex = 0;
					match = excepts[i].exec(text);
					if (match !== null) {
						ranges.push({
							from: match.index,
							to: match.index + match[0].length
						});
					}
				}
			}

			// so nothing is matched
			if (ranges.length === 0) {
				result.push(callback(text));
				break;
			}

			minRange = ranges.sort(descendingFromComparetor)[0];
			min = minRange.from;

			to = [];
			for (i in ranges) {
				if (ranges.hasOwnProperty(i)) {
					if (ranges[i].from <= minRange.to) {
						to.push(ranges[i].to);
					}
				}
			}
			max = Math.max.apply(null, to);

			result.push(callback(text.substr(0, min)));
			result.push(text.substr(min, max - min));
			// console.log('Excepted: "' + text.substr(min, max - min) + '"');
			text = text.substr(max);
		}
		return result.join('');
	}

	function wikiConvertToPersianCharacters(text) {
		return replaceExcept(
			text,
			persianTools.toStandardPersianCharacters,
			[patterns.globalExceptionTag, patterns.otherLanguagesInline, patterns.arabicTagEnclosed, patterns.fileNames, patterns.signatures, patterns.url, patterns.fileParameter]
		);
	}

	if (!String.prototype.trim) { // if is not available currently
		String.prototype.trim = function () {
			return this.replace(/^\s+|\s+$/g, '');
		};
	}

	function autoFormatCleanReferences(str) {
		// تمیزکاری autoFormater.js > cleanReferences
		str = str.replace(
			/<\s*references\s*(\s\b[^<>]*?)?\s*(?:\/|>\s*<\s*\/\s*references)\s*>/gi,
			'<references$1/>'
		);
		if (mw.config.get('wgNamespaceNumber') === 0) {
			//ویکی‌پدیای فارسی منبع برای ارجاع نیست  
			str = str.replace(/\<ref[^>]*\>\[?(?:https?:)?\/\/fa.(?:m\.)?wikipedia.org\/[^\<\n\}\]\[]+\]?\<\/ref\>/gi, '')
		};
		str = str.replace(/<\s*references\s*(\s\b[^<\/>]*?)?\s*>/gi, '<references$1>');
		str = str.replace(/<\s*\/\s*references\s*>/gi, '<\/references>');
		var re = /(<references[^<\/>]*)>/g, m;
		while (m = re.exec(str)) {
			if (str.indexOf('<\/references>', m.index) < 0) {
				str = str.slice(0, m.index) + m[1] + '/>' + str.slice(m.index + m[0].length);
			}
		}
		str = str.replace(/< *ref\s*(\s\b[^<>]*?)\s*(?:\/+|>\s*<\s*\/+\s*ref) *>/gi, '<ref$1/>');

		/* remove line breaks with assays only the top of the article */
		var i = str.indexOf('<references'),
			slice;
		if (i > 0) {
			slice = str.slice(i);
			slice = slice.replace(/< *ref\s*(\s\b[^<\/>]*?)?\s*>[\t ]*/gi, '<ref$1>');
			slice = slice.replace(/(?:(\n[\t ]*)|[\t ]*)<\s*\/+\s*ref\s*>/gi, '$1<\/ref>');
			str = str.slice(0, i);
		}
		str = str.replace(/< *ref\s*(\s\b[^<\/>]*?)?\s*>\s*/gi, '<ref$1>');
		str = str.replace(/\s*<\s*\/+\s*ref\s*>/gi, '<\/ref>');
		if (slice) {
			str += slice;
		}

		/* Space between the end of block and remove <ref> or two <ref> */
		str = str.replace(/([!,.;?]|<ref\b[^<>]*(?:\/|>[^<>]*<\/ref)>) +(?=<ref[ >])/gi, '$1');
		/* Two identical punctuation before and cut after a <ref> on one */
		str = str.replace(/([!,.:;?])(<ref\b[^<>]*(?:\/|>[^<>]*<\/ref)>)\1/gi, '$1$2');
		/* ref inside small */
		return str.replace(/\<small\> *(\<ref[^\<]+\<\/ref\>)<\/small\>/gi, '$1');
	}

	function autoFormatCleanTags(str) {
		str = str.replace(/(<\/?s)trike\b/gi, '$1');
		str = str.replace(
			/\<u\>([^\<]+)\<\/u\>/gi,
			"''$1''"
		);
		str = str.replace(
			/\<center\>/gi,
			"{{وسط‌چین}}"
		);
		str = str.replace(
			/\<\/center\>/gi,
			"{{پایان}}"
		);
		str = str.replace(
			/<sub\s*(>[^<>]*<)\s*(?:su[bp]\s*[.\/\\]+|[.\/\\]+\s*su[bp])\s*>/gi,
			'<sub$1/sub>'
		);
		str = str.replace(
			/<sup\s*(>[^<>]*<)\s*(?:su[bp]\s*[.\/\\]+|[.\/\\]+\s*su[bp])\s*>/gi,
			'<sup$1/sup>'
		);

		/* Drop default font attributes */
		str = str.replace(
			/(<font\b[^<>]*?)\s+fa\w+(?:[\s"',=]*(?:Arial|Helvetica(?:\W?N\w*)?|sans\W?serif)\b)+[\s"';]*(?=\s\w+\s*=|>)/gi,
			'$1'
		);
		str = str.replace(
			/(<font\b[^<>]*?)\s+size[\s"',=]*(?:-1\b|2\b|100\b[ ,.]*\d*%|1(?:\.0*)?em\b)["';]*/gi,
			'$1'
		);
		/* Remove inline elements with no attributes */
		while (/<(font|span)\s*>\s*(?:<(?!\1)|[^<])*?\s*<\/\1[^<>]*>/i.test(str)) {
			str = str.replace(/<(font|span)\s*>[ \n]*((?:<(?!\1)|[^<])*?)[ \n]*<\/\1[^<>]*>/gi, '$2');
		}
		str = str.replace(
			/<font\s+color[\s"',=]*(#[\dA-F]{3,6}|[a-z]{3,20})[\s"';]*>((?:<(?!font)|[^<])*?)<\/font[^<>]*>/gi,
			'<span style="color:$1;">$2<\/span>'
		);
		str = str.replace(
			/<font\s+size[\s"',=]*(?:-[2-9]|[01])[\s"';]*>((?:<(?!font)|[^<])*?)<\/font[^<>]*>/gi,
			'<small>$1<\/small>'
		);
		str = str.replace(
			/<font\s+size[\s"',=]*(?:[+-]0|3)[\s"';]*>((?:<(?!font)|[^<])*?)<\/font[^<>]*>/gi,
			'<span style="font-size:larger;">$1<\/span>'
		);
		/* Merge nested inline tags */
		str = str.replace(
			/<(abbr|cite|mark|q|s|small|u)\s*><(font|span)\s+style\s*=\s*["']?([^\n"<>]*?);?["']?\s*>([^<>]*)<\/\2\s*>\s*(?=<\/\1\s*>)/gi,
			'<$1 style="$3;">$4'
		);
		str = str.replace(
			/(<span\b[^<>]*?)\s+style\s*=\s*["']?([^\n"<>]*?);?["']?\s*><span\s+style\s*=\s*["']?([^\n"<>]*?);?["']?\s*>([^<>]*)<\/span\s*>\s*(?=<\/span\s*>)/gi,
			'$1 style="$2; $3;">$4'
		);

		/* Verschiedenste Formen von HTML-Zeilenumbrüchen durch einheitliche ersetzen */
		str = str.replace(/<(?:[\s\/\\]*br\b)+\s*(\s\w[^<>]*?)?[\s.\/\\]*>/gi, '<br$1/>');
		/* Unnötige HTML-Zeilenumbrüche entfernen, wenn sowieso ein Absatz folgt */
		str = str.replace(/ *(?:{{سخ}}|<br \/>)(?=[\r\n][\n#*:;\|}\]])/gi, '');
		str = str.replace(
			/<(ref|small|su[bp])\b\s*(\s\w[^<>]*?)?\s*><small\s*>([^<>]*)<\/small\s*><\/\1\s*>/gi,
			'<$1$2>$3<\/$1>'
		);
		str = str.replace(
			/<small\s*><(ref|small|su[bp])\b\s*(\s\w[^<>]*?)?\s*?( ?\/|>[^<>]*<\/\1)\s*><\/small\s*>/gi,
			'<$1$2$3>'
		);
		/* Drop old navigation bar wrapper, see [[Template:NaviBlock]] */
		return str.replace(
			/<div\s+class[^<>\w]*BoxenVerschmelzen[^<>\w]*>\s*(\{\{[^#:<>{}]*\}\})\s*<\/div>/gi,
			'$1'
		);
	}

	function autoFormatCleanDuplicateLinks(str) {
		/* Exclude files and infoboxes from the start of the article */
		var m = /^(?:\s*\[\[\w+:(?:\[\[[^\n\]]*\]\]|[^\n\]])*\]\])*(?:\s*\{\{(?:\{\{[^}]*\}\}|[^}])*\}\})+/.exec(str),
			start = m ? m[0].length : 0,
			found = [],
			a = [];
		/* Unlink years that are linked more than one time */
		var re = /\[\[ *([۱۲][۱۲۳۴۵۶۷۸۹۰]{3}|[۱۲][۱۲۳۴۵۶۷۸۹۰]{3} \((میلادی|قمری)\)) *\]\]/g;
		/* In each case the first discovery of a year noted entlinken thereafter */
		while (m = re.exec(str)) {
			if (m.index >= start) {
				found[m[1]] ? a.push(m) : found[m[1]] = true;
			}
		}
		var r = '',
			p = 0;
		for (var i = 0; i < a.length; i++) {
			r += str.slice(p, a[i].index) + a[i][1];
			p = a[i].index + a[i][0].length;
		}
		return p ? r + str.slice(p) : str;
	}

	function autoFormatCleanDates(str) {
		var months = ["ژانویه", "فوریه", "مارس", "آوریل", "مه", "ژوئن", "ژوئیه", "اوت", "سپتامبر", "اکتبر", "نوامبر", "دسامبر", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"].concat(persianMonths);

		/* Add missing space between day and month */
		str = str.replace(new RegExp('([\\s!\'(>|„](?:3[01]|[12]\\d|0?[1-9])\\.?)(?=(?:' +
			months.join('|') + ')\\b)', 'g'), '$1 ');
		/* No non-breaking space between month and year */
		str = str.replace(new RegExp('(\\b(?:3[01]|[12]\\d|0?[1-9])\\.?(?:[\\s\\xA0]|&nbsp;)+(?:' +
			months.join('|') + '))(?:\xA0|&nbsp;)(?=[12]\\d{3}\\b)', 'g'), '$1 ');
		/* Missverständliches deutsches Datumsformat durch Langform ersetzen */
		var separator = ' ';
		str = str.replace(
			/([\s'(>„])(3[01]|[12]\d|0?[1-9])\. *(1[012]|0?[1-9])\. *(?=[12]\d{3}[!,.:;?]?[\s')<\]“])/g,
			function ($0, $1, $2, $3) {
				return $1 + ($2 | 0) + separator + months[$3 | 0] + ' ';
			}
		);
		// عدد فارسی
		str = str.replace(new RegExp('([\\s!\'(>|„](?:۳[۰۱]|[۱۲][۱۲۳۴۵۶۷۸۹]|۰?[۱۲۳۴۵۶۷۸۹])\\.?)(?=(?:' +
			months.join('|') + ')[\\s\')<\\]»}|])', 'g'), '$1 ');
		/* No non-breaking space between month and year */
		str = str.replace(new RegExp('([\\s\'(>«](?:۳[۰۱]|[۱۲][۱۲۳۴۵۶۷۸۹]|۰?[۱۲۳۴۵۶۷۸۹])\\.?(?:[\\s\\xA0]|&nbsp;)+(?:' +
			months.join('|') + '))(?:\xA0|&nbsp;)(?=[۱۲][۱۲۳۴۵۶۷۸۹]{3}[\\s\')<\\]»}|])', 'g'), '$1 ');
		/* Missverständliches deutsches Datumsformat durch Langform ersetzen */
		var separator = ' ';
		str = str.replace(
			/([\s'(>«])(۳[۰۱]|[۱۲][۱۲۳۴۵۶۷۸۹]|۰?[۱۲۳۴۵۶۷۸۹])\. *(۱[۰۱۲]|۰?[۱۲۳۴۵۶۷۸۹])\. *(?=[۱۲][۱۲۳۴۵۶۷۸۹]{3}[!,.:;?]?[\s')<\]»}|])/g,
			function ($0, $1, $2, $3) {
				return $1 + ($2 | 0) + separator + months[$3 | 0] + ' ';
			}
		);
		return str
	}

	function quotationReplace(line) {
		[
			['“', '”'], ['”', '“'], ['‘', '’'], ['’', '‘'], ['‚', '’'], ['„', '”'], ['\\<\\<', '\\>\\>'], ['\\(\\(', '\\)\\)']
		].forEach(function(qs) {
			line = line.replace(new RegExp(
				'(^|[' + persianTools.persianCharacters + '\\:>،»؛\\s\\n\\}\\]\\.]+)'
				+ qs[0] + '((?:\\[\\[|).*?[' + persianTools.persianCharacters + '\\n]+?(?:\\]\\]|\\.|\\<|\\:|\\{|\\[|…|))' + qs[1]
				+ '([' + persianTools.persianCharacters + '،«؛\\s\\n\\.\\[\\{]|$)', 'g'
			), '$1«$2»$3');
		});
		return line;
	}

	function quotation(text) {
		// این تابع زمانی گیومه را به فارسی تیدیل می‌کند که در پاراگراف مورد نظر تعداد گیومهٔ لاتین زوج باشد.
		var lines = text.split(/\n\n/);
		var result = [];
		for (var i = 0; i < lines.length; ++i) {
			var line = lines[i];
			if ((line.match(/"/g) || []).length % 2 === 0) { // count of quote marks
				// تبدیل گیومهٔ لاتین به فارسی
				// این دستور در ابتدا باشد تا فاصله‌های قبل و بعد گیومه هم اصلاح شود
				line = line.replace(
					new RegExp('(^|[' + persianTools.persianCharacters + '\\:>،»؛\\s\\n\\}\\]\\.\\)]+)"((?:\\[\\[|).*?[' + persianTools.persianCharacters + '؛\\n،]+?(?:\\]\\]|\\.|\\<|\\:|…|))"([' + persianTools.persianCharacters + '،«؛\\s\\n\\.\\[\\{\\(]|$)', 'g'),
					'$1«$2»$3'
				);
				// if some of quote marks are remained from conversion, something might wrong, revert
				var testline = line.replace(/(?:<ref[^\/]*?>[\s\S]*?<\/ref>|<ref[^\/]*?\/>)/g, '')

				if (testline.match(/"/g)) {
					line = lines[i];
				}
			}
			// رفع مشکل استفاده از ـً به جای گیومه لاتین در متن فارسی
			line = line.replace(new RegExp('ا\\"([ ]*[' + persianTools.persianCharacters + '])', 'g'), 'اً$1')
			line = line.replace(new RegExp('ا\\”([ ]*[' + persianTools.persianCharacters + '])', 'g'), 'اً$1')
			line = quotationReplace(line);
			result.push(line);
		}
		return result.join('\n\n');
	}
	/**
	 * افزودن ستون به الگوی پانویس
	 * @param {string} text محتوا
	*/
	function addColumnToRefTemplate(text) {
		var refTemplate = /\{\{پانویس([^\}\{]+)?\}\}/i.exec(text), needChange = false;
		if (refTemplate) {
			if ((text.match(/<ref/gi) || []).length >= 6) {
				if (refTemplate[1] !== undefined) {
					var refParams = refTemplate[1].split('|');
					for (var i = refParams.length - 1; i >= 0; i--) {
						// اگر از پیش ستون یا پارامتر عرض تعریف شده‌باشد تغییری ایجاد نمی‌شود.
						if (refParams[i].length == 1 || refParams[i].indexOf('عرض') > -1) {
							needChange = true;
							break;
						}
					}
				}
				if (refTemplate[1] === undefined || !needChange) {
					return text.replace(refTemplate[0], refTemplate[0].replace('}}', '|۲}}'));
				}
			}
		} else {
			if ((mw.config.get('wgNamespaceNumber') === 0 && mw.config.get("wgEditMessage") === 'editing') || mw.config.get('wgPageName') === "ویکی‌پدیا:ویکی‌پروژه_ابزارها/آزمایش_واحد") {
				if ((text.match(/<ref/gi) || []).length > 0 && /\{\{پانویس([^\}\{]+)?\}\}/i.exec(text) != null) {
					var text2 = text.replace('== منابع ==', '== منابع ==\n{{پانویس}}')
					if (text2 == text) {
						text2 = text.replace('== پانویس ==', '== پانویس ==\n{{پانویس}}')
					}
					if (text2 == text) {
						text2 = text.replace('[[رده:', '== منابع ==\n{{پانویس}}\n\n[[رده:')
					}
					if (text2 == text) {
						text2 = text + '\n== منابع ==\n{{پانویس}}'
					}
					text = text2
				}
			}
		}
		return text;
	}

	/**
	 * اصلاح پیوندها
	 * @param  {string} text محتوا
	 * @return {string}
	 */
	function fixBadLinks(text) {
		// حذف متن جایگزین پیوند اگر با نشانی پیوند برابر باشد؛ مانند [[سلام|سلام]]
		text = text.replace(/\[{2}([^\|]+)\|\1\]{2}/gi, '[[$1]]');

		// حذف پیوند سال و روز ماه
		text = text.replace(/\[{2}([۰-۹]+|[۰-۹]+ [\)\(\u0621-\u0655\u067E\u0686\u0698\u06AF\u06A9\u0643\u06AA\uFED9\uFEDA\u06CC\uFEF1\uFEF2]+)(?:\|([^\]\|\[]+))?\]{2}/g, function (match, p1, p2) {
			// اگر فقط سال پیوند شده‌باشد یا به شکل [[سال|همان سال]] باشد فقط سال را می‌گرداند.
			var calendar_type = / \((میلادی|قمری|خورشیدی|شمسی|پیش از میلاد|قبل از میلاد)\)/g;
			if (p1 !== p2 && p1.replace(calendar_type, '') === p2) {
				return p2;
			}//[[Special:Permalink/19908981#حذف پیوند تاریخ‌ها]]
			if (p2 === undefined || p1 === p2) {
				// اگر پیوند به روز و ماه بود، برای جلوگیری از اشتباه و تداخل، بررسی می‌شوند که حتما یکی از ماه‌ها داخل رشته باشد.
				if (p1.indexOf(" ") > -1) {
					var
						months = [
							"محرم", "صفر", "ربیع‌الاول", "ربیع‌الثانی", "جمادی‌الاول", "جمادی‌الثانی", "رجب", "شعبان", "رمضان", "شوال", "ذیقعده", "ذیحجه"
						].concat(persianMonths),
						i;
					for (i = months.length - 1; i >= 0; i--) {
						if (p1.indexOf(months[i]) > -1) {
							return p1.replace(calendar_type, '');
						}
					}
					return "[[" + p1 + "]]";
				}
				return p1;
			}
			// اگر متن جایگزین پیوند مخالف پیوند سال بود، متن جایگزین را برمی‌گرداند.
			return p2;
		});

		return text;
	}

	function sortCategories(content, pageName) {
		return content.replace(/((?:\n\[\[رده:.+\]\])+)/, function (categories) {
			var sortedCategories = persianTools.persianSort(categories.trim().split('\n'));
			return '\n' + sortedCategories.map(function (line, i) {
				// Put a "| " on category with the same name as the page
				if (line.indexOf('[[رده:' + pageName + ']]') === 0)
					line = '[[رده:' + pageName + '| ]]';

				// If it contains "| " put it at top
				if (line.indexOf('| ]]') !== -1) i = -1;

				return { index: i, content: line };
			}).sort(function (x, y) { // sort based on their initial line index
				return x.index - y.index;
			}).map(function (x) { return x.content; }).join('\n');
		});
	}

	function wikiPunctuation(text) {
		text = autoFormatCleanReferences(text)
		text = autoFormatCleanTags(text)
		text = autoFormatCleanDuplicateLinks(text)
		text = replaceExcept(
			text,
			function (text) {
				return quotation(text);
			},
			[patterns.ref]
		);
		if (mw.config.get('wgNamespaceNumber') === 0) {
			var old_text = text.replace(/\=\=/g, '')
			if (old_text == text) {//در صورتی که در مقاله بخش‌بندی وجود نداشته باشد
				text = text.replace(/(\n\{\{سخ\}\}|\n\n)(\s|_|)\'\'\'(.*?)\'\'\'(\s|_|)(\{\{سخ\}\}|\n)([\n\#\*])/g, '\n\n== $3 ==\n$6')
			}
		}
		text = replaceExcept(
			text,
			function (text) {
				text = text.replace(
					new RegExp('([' + persianTools.persianCharactersNoVowels + '])ـ+([' + persianTools.persianCharactersNoVowels + '])', 'g'),
					'$1$2'
				);
				return text.replace(new RegExp('([' + persianTools.persianCharacters + '])(\\]\\]|), (\\[\\[|)?(?=[' + persianTools.persianCharacters + "])", 'g'), '$1$2، $3');
			},
			[patterns.globalExceptionTag, patterns.fileNames, patterns.url, patterns.galleryTag, patterns.sourceTag, patterns.translatedUrl, patterns.parenthesesHa]
		);
		text = replaceExcept(
			text,
			function (text) {
				return text.replace(/(<\/ref>)\s+(<ref)/g, '$1$2').replace(/([^=])\s+<ref(?!erences)/g, '$1<ref');
			},
			[/\{\{(پانویس|پانویس‌ها|پی‌نوشت)\s*\|[\s\S]*/]
		);
		return replaceExcept(
			text,
			function (text) {
				return persianTools.punctuation(text)
					.replace(/^([*#]+;?)([^*#;\:\s])/mg, '$1 $2') // Adds a space after the # or * for lists
					.replace(/^([*#]+;?) {2,}(.)/mg, '$1 $2'); // Trim more that one space after the # or * for lists
			},
			[patterns.globalExceptionTag, patterns.mapFrameTag, patterns.mathTag, patterns.fileNames, patterns.url, patterns.wikilinkTargets,
			patterns.galleryTag, patterns.sourceTag, patterns.translatedUrl, patterns.parenthesesHa, patterns.insideHtmlComment]
		)
			.replace(/\u00A0/g, ' ') // convert implicit nbsp to space, probably is being added by some bug on ContentTranslation
			.replace(/٬ /g, '، ')
			.replace(new RegExp('([' + persianTools.persianCharacters + '\]])٬', 'g'), '$1،')
			.replace(new RegExp('([' + persianTools.persianCharacters + '])(\]\]|»|)[ ]*[,]', 'g'), '$1$2، ')
			.replace(/\[\[([^\|\]]+)\|(\'{2,3})\1'{2,3}\]\]/g, '$2[[$1]]$2') // for [[foo|'''foo''']] > '''[[foo]]'''
			.replace(new RegExp('\\[\\[(' + escapeRE(mw.config.get('wgPageName')) + ')\\]\\]', 'g'), '$1') // پیوندزدایی از خود صفحه
			//برگرفته از https://checkwiki.toolforge.org/cgi-bin/checkwikin.cgi?project=fawiki&view=project
			.replace(/\[\[([^\|\]]+)\|\1([\u200c ]*(های?))\]\]/g, '[[$1]]$2')//[[Special:Diff/17515365/17865938]]
			.replace(/([\S]*)[\s\u200c]*-[\s\u200c]*(تاکنون)(?![ ]+)/g, '$1-اکنون')//[[Special:Diff/17515365/17865938]]
			//مشکل در نشانی اینترنتی
			.replace(/(\<ref.*?\>) *(\[|)\www(6|3|)\./gi, '$1$2http://www$3.')
			.replace(/\[\[ *(https?\:\/\/.*?) *\]\]/g, '[$1]')
			.replace(/\[\[ *(\/\/.*?) *\]\]/g, '[$1]')
			.replace(/(https?:\/?\/?){2,}/g, '$1')
			// تمیزکاری autoFormatter.js > cleanExternalLinks
			.replace(/\b(?:http(s?)(?::+\/*|\/\/+:*)\b)+/gi, 'http$1://')
			// repair links with vertical stroke
			.replace(/(\[https?:\/\/[^\s[\]|]*?) *\| *(?=[^\s=[\]|]+\])/gi, '$1 ')
			// supplement slashes at the end easier Domains
			.replace(/(\[https?:\/\/\w[\w.-]*\w\.\w+) +/gi, '$1/ ')
			// Domains lowercase, whether labeled or not
			.replace(/\bhttps?:\/\/\b[0-9a-z.-]*[A-Z][\w.-]*/g, function ($0) {
				return $0.toLowerCase();
			})
			// پیوند به بیرون‌هایی که در میان پیوند نویسهٔ رفتن به خط بعد، وجود داشته باشد
			.replace(/\[(?:https?\:|)\/\/[^\]\[]+\]/g, function (x) {
				x = x.replace(/[\n\r]/g, '');
				return x
			})
			// فاصله اول زیربخش
			.replace(/پیوند\n\=/, 'پیوند=')// [[Special:Diff/20238012/20244702]]
			.replace(/^ +(\=+[^\=]+\=+)/mg, '$1')
			.replace(/\[\[\|/g, '[[')
			.replace(/(\< *\/ *br *\>|\< *br *\\ *\>|\< *br *\. *\>)/g, '<br/>')
			.replace(/(\<br *\/\>|\{\{سخ\}\})([\r\n])(\*|\#|\=\=)/g, '$2$3')
			.replace(/(\<br *\/\>|\{\{سخ\}\}) *\]\]/g, ']]')
			.replace(/\[\[ *(\<br *\/\>|\{\{سخ\}\})/g, '[[')
			.replace(/(\< *span *\/ *\>|\< *\/ *span *\/ *\>)/gi, '</span>')
			.replace(/(\< *center *\/ *\>|\< *\/ *center *\/ *\>)/gi, '</center>')
			.replace(/(\< *b *\/ *\>|\< *\/ *b *\/ *\>)/gi, '</b>')
			.replace(/(\< *div *\/ *\>|\< *\/ *div *\/ *\>)/gi, '</div>')
			.replace(/(\< *p *\/ *\>|\< *\/ *p *\/ *\>)/gi, '</p>')
			.replace(/(\< *td *\/ *\>|\< *\/ *td *\/ *\>)/gi, '</td>')
			.replace(/(\< *small *\/ *\>|\< *\/ *small *\/ *\>)/gi, '</small>')
			.replace(/\[\[([^\]]+)\{\{\!\}\}([^\]]+)\]\]/g, '[[$1|$2]]')//وجود {{!}} درون پیوند
			.replace(/\[{2}([^\|]+)\|\1\]{2}/gi, '[[$1]]')//زمانی که بخش هدف و نمایه پیوند یکی باشند
			.replace(/\[\[(.+)\|('+)(.+\b)('+)\]\]/gi, '$2[[$1|$3]]$4')// انتقال ''' به بیرون پیوند
			// تمیزکاری autoFormatter.js > CleanGalleries
			.replace(/<gallery\b([^<>]*)>([^<>]+)<\/gallery\b[^<>]*>/gi,
				function ($0, $1, $2) {
					return '<gallery' + $1 + '>' + $2
						.replace(/^(\s*)\[+([^[\]]*)\]\]?\s*$/gm, '$1$2')
						.replace(/^(\s*)\[+/gm, '$1') + '<\/gallery>';
				}
			)
			// تمیزکاری پرونده‌ها
			// زمانی که برچسب کوچک در توضیحات تصویر می‌آید برای دستگاه‌های با مانیتور کوچک خواندن متن مشکل می‌شود
			//https://checkwiki.toolforge.org/cgi-bin/checkwiki.cgi?project=fawiki&view=only&id=77
			.replace(/\[\[(پرونده|[Ff]ile)\:((?:\[\[.*?\]\]|[^\]])*)\]\]/g, function ($0, $1, $2) {
				$2 = $2.replace(/\<\/?(big|center|small)>/g, '');
				return '[[' + $1 + ':' + $2 + ']]'
			})
			.replace(/\[\[ *تصو[يی]ر\:/gi, '[[پرونده:')
			// تمیزکاری الگو autoFormater.js > cleanTemplates
			.replace(/\{\{\s*:?\s*(?:الگو|Template)\s*:\s*/gi, '{\{')
			// حذف خط زیر از عنوان الگو
			.replace(/(?:^|[^{])\{\{[ 0-9a-z\xC0-\u024F-]*_[ \w\xC0-\u024F-]*/gi,
				function ($0) {
					return $0.replace(/_+$/, '').replace(/[ _]+/g, ' ').replace(/\{ +/, '{');
				}
			)
			//سایر موارد
			.replace(/\[\[(رده|الگو|ویکی\u200cپدیا)\: +/g, '[[$1:')
			.replace(/\{\{\*\}\}/g, '{{•}}')
			.replace(/[\n\s]*\{\{[•·ن](w?)\}\}\s*/g, '{{•$1}} ')
			.replace(/\=\{\{[•·ن](w?)\}\}\s*/g, '=\n{{•$1}} ')//رفع باگ [[Special:Diff/14799178/16387261]] در خط بالا 
			.replace(/ *(<\/? ?br ?\/?>|\{\{بر\}\}) */g, '{{سخ}}')
			.replace(/\{\{سخ\}\}\n\n/g, '\n\n')
			.replace(/\n\n\{\{سخ\}\}/g, '\n\n')
			.replace(/\{\{سخ\}\}\]/g, ']')
			.replace(/\[\{\{سخ\}\}/g, '[')
			.replace(/\n\n(\*|\#)/g, '\n$1')
			.replace(/\n(\#|\*)( |)\n/g, '\n$1')
			.replace(/\n(\*|\#)( |)(\={2,})/g, '\n$3')
			.replace(/(\n?)\s+?<\/ref>/g, '$1</ref>')
			.replace(/\{ *\|/g, '{|')
			.replace(/\| *\}/g, '|}')
			.replace(/\{\| *\{\|/g, '{|')
			.replace(/\|\} *\|\}/g, '|}')
			.replace(/^=.*&nbsp;.*=$/gim, function ($0) {
				return $0.replace(/(?:&nbsp;|\s)+/gi, ' ');
			})
			.replace(/([^=])\n+(\=.*?\=\n+)/g, '$1\n\n$2')
			.replace(/^(=+([^=].*?)=+)[\t\s]{1,}\n/g, '$1\n')
			.replace(/^(\={2,}) +[\:,;>&\^#@•→←↔↑↓—–…~٫،؛ٔ]/mg, '$1') // Cleanup headers
			.replace(/[\:,;<&\^#@•→←↔↑↓—–…~٫،؛ٔ] +(\={2,})$/mg, '$1')
			.replace(/^(\={2,}\s*)(«)([^\n«»]*?)(»)(\s*\={2,})/mg, '$1 $3 $5')
			.replace(/^(\={2,}) *'+(.*?)'+ *(\={2,})/mg, '$1 $2 $3')
			.replace(/^[•●⚫⬤]/mg, '*') // Wikify bullets in start of lines
			.replace(/^#\s*(REDIRECT|تغییر[ _]?مسیر)/gi, '#تغییرمسیر')
			.replace(/^#تغییرمسیر(?=\S)/g, '#تغییرمسیر ') // Adds a space after #REDIRECT
			.replace(/(\={2,}) *([^\n\r]*?) *(\={2,})/g, '$1 $2 $3') // Format headings level 2 and above
			// زیربخش نیازی به برچسب بزرگ و کوچک ندارد
			.replace(/(\=+) \<(?:small|big)\>([^\=\n\r]+)\<\/(?:small|big)\> (\=+)/g, '$1 $2 $3')
			//حذف فاصلهٔ اضافی درون {{}}
			.replace(/\{\{(\s*)(.*?)(\s*)\}\}/g, '{{$2}}')
			// تبدیل به نویسه / یکی کردن فاصله های مجازی پشت سرهم
			.replace(/(\{\{فم\}\}|\&zwnj\;|\u200c+)/g, '\u200c')
			// Full stop and comma should be before citation. See en:WP:REFPUNC
			.replace(/ *((?:<ref[^\/]*?>.*?<\/ref>)+)([\.،,:])?/g, '$2$1')
			.replace(/([^.])([\.،,:]){2}((?:<ref[^\/]*?>.*?<\/ref>)+)/g, '$1$2$3')
			.replace(/ *((?:<ref[^\/]*?\/>)+)([\.،,:])/g, '$2$1')
			.replace(/([^.])([\.،,:]){2}(((?:<ref[^\/]*?\/>)+)+)/g, '$1$2$3')
			/* هر رده در یک خط */
			.replace(/([^\s>-]) *(\[\[رده:[^\n[\]]*\]\])/gi, '$1\n$2')
			.replace(/(\[\[رده:[^\n[\]]*\]\]) *(?![\s<-]|$)/gi, '$1\n')
			.replace(/(\[\[رده:[^\n[\]]*\]\]\n) *(?!\[\[رده:|[\s<-]|$)/gi, '$1\n')
			//ترتیب‌پیش‌فرض
			.replace(/\{\{(?:DEFAULTSORT|[Dd]efaultsort|ترتیب|ترتیب[‌ ]پیش[‌ ]?فرض) *[|:] *(?=.*?}})/g, '{{ترتیب‌پیش‌فرض:')
			.replace(/\{\{(ترتیب‌پیش‌فرض|DEFAULTSORT)\:[-\w,\s\(\)]+\}\}\n?/g, '')
			.replace(/(\{\{(?:ترتیب‌پیش‌فرض|DEFAULT\w*SORT\w*):[^\n{}]*\}\})\s*(?=\[\[رده:)/gi, '$1\n')
			.replace(/(\{\{ترتیب‌پیش‌فرض\:)\s/g, '$1')
			.replace(/(==\n)\n+(?=<references[^\n<>]*\/>\n\n)/gi, '$1')
			//نچسبیدن و+فاصله به براکت که محصول اشتباه در تایپ کردن است
			.replace(/\]\]و /g, ']] و ')
			.replace(/(\s|^)\'\'\'(\s|)(.*?)(\s|)\'\'\'(\s)/g, "$1'''$3'''$5") // حذف فاصلهٔ اضافی درون ویکی کد
			.replace(/'''\{\{به /g, "''' {{به ")
			.replace(/\*(\s+|\n)?\{\{پانویس/g, "{{پانویس")
			.replace(/((?:^|\n\s)\=+\s+\=+(?:\s+|)\n)/g, "\n\n")
			.replace(/\n{3,}/g, '\n\n')
			// فاصله‌های اضافی را از داخل پیوند به بیرون منتقل کند تا اگر اضافه بودند در کدهای دیگر حذف شوند
			// غیرفعال‌سازی: عملکرد مشابه پایین هم وجود دارد
			// .replace(/\[\[(\s*)(.*?)(\s*)\]\]/g, '$1[[$2]]$3')
			.replace(/(\[{1,2})([^\[\]\n]*[^\[\]\n\|]) +(\]{1,2})/g, "$1$2$3")
			.replace(/(\[{1,2}) +([^\[\]\n]+)(\]{1,2})/g, "$1$2$3")
			//تبدیل بندهای ترتیبی به قالب‌بندی ویکی [[Special:Permalink/19923598#تبدیل بندهای ترتیبی به قالب‌بندی ویکی]]
			.replace(/(?:(?:^(?:\* ?)?[۱۲۳۴۵۶۷۸۹۰0-9]{1,2} ?[\.\-\)]) ?[^۱۲۳۴۵۶۷۸۹۰0-9\n]+\n+){2,}/gm, function (x) {
				var x2 = x.replace(/((?:\* ?)?[۱۲۳۴۵۶۷۸۹۰0-9]{1,2} ?[\.\-\)]) ?([^۱۲۳۴۵۶۷۸۹۰0-9\n]+)\n+/gm, '# $2\n')
				if (x2.replace(/[۱۲۳۴۵۶۷۸۹۰0-9]/g, '') === x2) {
					return x2;
				}
				return x;
			})
			.trim();
	}

	function wikiSubsection(text) {
		return text.replace(/\<(?:\s*)references?(?:\s*\/|\s*\/\s*)\>/g, '{{پانویس}}')
			.replace(/\{\{(?:[Rr]eflist|[Rr]eferences?|پانویس[‌ ]?ها)(?=\||\})/g, '{{پانویس')
			.replace(/\{\{راست(| |‌)چین\}\}\s*\{\{پانویس(.*?)\}\}\s*\{\{(پایان راست(| |‌)چین|پایان)\}\}/g, '{{پانویس$2}}')
			.replace(/\{\{چپ(| |‌)چین\}\}\s*\{\{پانویس(.*?)\}\}\s*\{\{(پایان چپ(| |‌)چین|پایان)\}\}/g, '{{پانویس$2|چپ‌چین=بله}}')
			.replace(/\<small\>\s*\{\{پانویس(.*?)\}\}\s*\<\/small\>/g, '{{پانویس$1|اندازه=کوچک}}')
			.replace(/(({\{پانویس.*?\}\})(\n|)){1,}/g, '$1')
			.replace(/\=\s*لیست\s*\=/g, '= فهرست =')
			.replace(/\=\s*(?:[gG]allery|نگارستان|گالری (تصویر|عکس|))\s*\=/g, '= نگارخانه =')
			.replace(/\=\s*(?:بیوگرافی|زندگینامه)\s*\=/g, '= زندگی‌نامه =')
			.replace(/\=\s*(?:[eE]xternal links|لینک‌?های بیرونی|پیوندهای خارجی|لینک‌?های خارجی|پیوندهای بیرونی)\s*\=/g, '= پیوند به بیرون =')
			.replace(/\=\s*(?:[nN]otes|[fF]ootnotes?|پاورقی|پاورقی‌ها|پانوشت|پانویس‌ها)\s*\=/g, '= پانویس =')
			.replace(/\=\s*(?:[Ss]ee [Aa]lso|همچنین ببینی[مد]|بیشتر ببینی[مد]|همچنین نگاه کنید|بیشتر بدانی[مد]|مراجعات مرتبط|جستار وابسته|مطلب مرتبط|مطالب مرتبط|جستارهای مشابه|جستارهای دیگر)\s*\=/g, '= جستارهای وابسته =')
			.replace(/\=\s*(?:منبع|منبع[‌ ]?ها|رفرنس|رفرنس[‌ ]?ها|ارجاع[‌ ]?ها|ارجاع|مرجع[‌ ]?ها|رفرنس|برگرفته از|مراجع|منابع و یادداشت[‌ ]?ها|منبع|مرجع|م[آا]خذ|منابع و م[آا]خذ|منابع و پانویس‌ها|فهرست مراجع|لیست مراجع|فهرست ارجاع[‌ ]?ها|فهرست ارجاع|[rR]eferences)\s*\=/g, '= منابع =')
			//.replace(/\=\s*(?:مطالعه بیشتر|بیشتر بخوانی[مد])\s*\=/g, '= برای مطالعهٔ بیشتر =')
			.replace(/^\={3,}\s*(جستارهای وابسته|پانویس|منابع)\s*\={3,}$/g, '== $1 ==');
	}
	function wikiUrlMinifier(text) {
		return text
			.replace(patterns.url, function (x) {
				return replaceExcept(
					x,
					function (x) {
						try {
							x = decodeURI(x);
						} catch (e) {
							try {
								x = decodeURIComponent(unescape(x));
							} catch (e) { mw.notify(e); }
						}
						return x;
					},
					[patterns.globalExceptionTag, patterns.decodeUriBlacklist, patterns.refname, patterns.urlArchive]
				);
			})

			// Strip the http(s) prefix
			.replace(/\[(https?\:)(?=\/\/(?:[\w\-]+)\.(?:m\.)?(wiki(pedia|media|data|source|news|oyage|quote)|wiktionary)\.org\/[^\s\]]*)/g, '[')
			.replace(/[\[\=](?:https?\:|)\/\/[\w\-]{2,}\.(?:m\.)?wikipedia\.org\/w(?:iki)?\/([^\n?]*?)[\]\|]/g, function (x) {
				x = x.replace(/\[(?:https?\:|)\/\/([\w\-]{2,})\.(?:m\.)?wikipedia\.org\/w(?:iki)?\/(.*?) (.*?)\]/g, '[[:$1:$2|$3]]')
				x = x.replace(/\[(?:https?\:|)\/\/([\w\-]{2,})\.(?:m\.)?wikipedia\.org\/w(?:iki)?\/(.*?)\]/g, '[[:$1:$2]]')
				//x = x.replace(/\=(?:https?\:|)\/\/([\w\-]{2,})\.(?:m\.)?wikipedia\.org\/w(?:iki)?\/(.*?)\|/g,'=[[:$1:$2]]|') // در الگو یادکرد مشکل ایجاد می‌کند
				x = x.replace(/\[\[\:fa\:/g, '[[').replace(/\%20/g, ' ').replace(/٪۲۰/g, ' ').replace(/\[\[[a-zA-Z\'0-9 ]+\|/g, '[[')
				x = x.replace(/\[\[.*?\]\]/g, function (y) {
					y = y.replace(/\_/g, ' ')
					return y
				})
				return x
			}).replace(/\[{2}([^\|]+)\|\1\]{2}/gi, '[[$1]]');
	}
	function SubSectionLeveling(text) {
		// تنظیم سطح زیربخش‌ها
		text = text.replace(/^(\={2,}) *(.*?) *(\={2,})/mg, '$1 $2 $3')
		if ((mw.config.get('wgNamespaceNumber') === 0 && mw.config.get("wgEditMessage") === 'editing') || mw.config.get('wgPageName') === "ویکی‌پدیا:ویکی‌پروژه_ابزارها/آزمایش_واحد") {
			//مقاله‌ای که فقط زیربخش سطح ۱ دارد
			if (text.replace(/\=\=/g, '') === text) {
				var text2 = text.replace(new RegExp('^\=([^\=\r\n]+)\=$', 'gm'), "== $1 ==")
				if (text !== text2) {
					text = text2.replace(/\n\=  /g, '\n= ').replace(/  \=\n/g, ' =\n')
				};
			};
			//مقاله‌ای که فقط زیربخش سطح ۳ یا ۴ دارد
			var text_test = text.replace(/^\=+ (منابع|جستارهای وابسته|پیوند به بیرون|پانویس|نگارخانه) \=+\n/gm, '')
			if (text_test.replace(/^\=\= /gm, '') === text_test) {
				if (text_test.replace(/^\=\=\= /gm, '') !== text_test) {
					//سطح ۳
					text = text.replace(new RegExp('^\=\=\=([^\=\r\n]+)\=\=\=$', 'gm'), "== $1 ==")
					text = text.replace(new RegExp('^\=\=\=\=([^\=\r\n]+)\=\=\=\=$', 'gm'), "=== $1 ===")
				} else if (text_test.replace(/^\=\=\=\= /gm, '') !== text_test) {
					//سطح ۴
					text = text.replace(new RegExp('^\=\=\=\=([^\=\r\n]+)\=\=\=\=$', 'gm'), "== $1 ==")
				} else {
					text = text.replace(/===/g, '==')
				};
			};
			text = text.replace(/==  /g, '== ').replace(/  ==/g, ' ==')
		};
		return text;
	};
	function decimalPointToPersian(text) {
		//Decimal point [[Special:Permalink/25438370#جمع‌بندی_بدون_رسیدگی]]
		return text.replace(new RegExp('([۰۱۲۳۴۵۶۷۸۹])\\.(?=[۰۱۲۳۴۵۶۷۸۹])', 'g'), '$1٫')
	};
	function wikiTextDigitsToPersian(text) {
		text = replaceExcept(
			text,
			toEnglishDigits,
			[patterns.argumentsBlacklist, patterns.fileNames, patterns.fileParameter]);
		text = replaceExcept(
			text,
			persianTools.toPersianDigits,
			[patterns.globalExceptionTag, patterns.url, patterns.argumentsBlacklist, patterns.mapFrameTag, patterns.mathTag, patterns.imagePixelSize, patterns.fileNames, patterns.ref,
			patterns.sourceTag, patterns.arabicDigitsEnglishContext, patterns.signatures, patterns.htmlEntity, patterns.diffLink,
			patterns.htmlAttributes, patterns.fileParameter, patterns.templateParameterName, patterns.ipSign,
			patterns.parenthesesAfterDigits, patterns.otherLanguagesInline, patterns.preTag, patterns.isbn, patterns.englishDate,
			patterns.parameter, patterns.color, patterns.templateEnglishName, patterns.linksOnEnglishContext, patterns.citation, patterns.refname,
			patterns.LtRTagEnclosed, patterns.boxVar, patterns.mediawikiFunctions, patterns.graphTemplate]
		);
		text = replaceExcept(
			text,
			decimalPointToPersian,
			[patterns.catgories, patterns.graphTemplate]
		);
		return text
			//  thousands' separator
			.replace(/([۱۲۳۴۵۶۷۸۹۰]),([۱۲۳۴۵۶۷۸۹۰])/g, '$1٬$2')
			.replace(/([۱۲۳۴۵۶۷۸۹۰])( |)\u0652/g, '$1°')//تبدیل نویسه سکون+عدد فارسی به نویسه درجه و عدد فارسی
			.replace(/\u0652( |)([۱۲۳۴۵۶۷۸۹۰])/g, '°$2')
			//فاصله بین نویسه درجه و حروف الفبای فارسی به جز عدد فارسی
			.replace(/([\u0621-\u064A\u0653-\u0655\u067E\u0686\u0698\u06AF\u06A9\u0643\u06AA\uFED9\uFEDA\u06CC\uFEF1\uFEF2])°/g, '$1 °');
	}

	function dictationReplace(x, y, extensions, text) {
		return text.replace(
			new RegExp(
				'(^|[^' + persianTools.persianCharacters + '])(\\s|\u200c|_|)(' + x + ')(\\s|_)(' + y + ')(\\s|\u200c|_|)(' +
				extensions + ')($|[^' + persianTools.persianCharacters + '])',
				'g'
			),
			'$1$2$3\u200c$5$6$7$8'
		);
	}

	// it has dependency to MediaWiki:Gadget-Extra-Editbuttons-Dictionary.js
	function dictation(text) {
		var i,
			dictionary = persianToolsDictionary,
			NASB = '\u064b', // ًـ
			ZAMM = '\u064c'; // ُـ
		for (i in dictionary.complexes) {
			if (dictionary.complexes.hasOwnProperty(i)) {
				text = dictationReplace(
					i,
					dictionary.complexes[i],
					'ی|یی|ها|های|هایی|هایم|هایت|هایش|هایمان|هایتان|هایشان|',
					text
				);
			}
		}
		// for last name
		text = dictationReplace(
			dictionary.personNames,
			'ی|یی|زاده|نیا|گان|فر|نژاد|یان|ی\u200cها|یها',
			'ی|',
			text
		);
		// for 'آباد's
		text = dictationReplace(
			dictionary.personNames + '|' + dictionary.addToAbad,
			'آباد',
			'زاده|نیا|پور|گان|فر|نژاد|ی|یان|ها|های|یی|هایی|ی\u200cها|یها|',
			text
		);
		// for first names
		for (i in dictionary.firstNameComplex) {
			if (dictionary.firstNameComplex.hasOwnProperty(i)) {
				text = text.replace(
					new RegExp(
						'(^|[^' + persianTools.persianCharacters + ']|\\s|_)(' + i + ')(\\s|_)(' +
						dictionary.firstNameComplex[i] + ')(\\s|_)($|[^' + persianTools.persianCharacters + ']|[^' +
						persianTools.persianCharacters + '])',
						'g'
					),
					'$1$2\u200c$4$5$6'
				);
			}
		}
		// for colors
		text = dictationReplace(
			dictionary.colorsNames,
			'فام|گون',
			'زاده|نیا|پور|گان|فر|نژاد|ی|یی|ها|های|هایی|ی\u200cها|یها|هایم|هایت|هایش|هایمان|هایتان|هایشان|',
			text
		);
		// for numbers
		text = dictationReplace(
			dictionary.persianNumbers,
			'گانه|ماهه',
			'زاده|نیا|پور|گان|فر|نژاد|ی|یی|ها|های|هایی|هایم|هایت|هایش|هایمان|هایتان|هایشان|',
			text
		);
		// wrong dictation
		for (i in dictionary.forReplace) {
			if (dictionary.forReplace.hasOwnProperty(i)) {
				text = text.replace(
					new RegExp(
						'(^|[^' + persianTools.persianCharacters + '])(\\s|\\(|\u200c|_|)(' + i + ')(\\s|\\)|\u200c|_|)($|[^' +
						persianTools.persianCharacters + '])',
						'g'
					),
					'$1$2' + dictionary.forReplace[i] + '$4$5'
				);
			}
		}

		var preword = "(^|\\s|_|«|»|\\[|\\(|\\<|\\>|\\')(";
		// کلماتی که آ دارند
		text = text.replace(
			new RegExp(preword + dictionary.wordsWithA + ")(ی|ئی|یی|ٔ|)(?= |«|»|\\.|،|_|\\]|\\s|\\:|\\)|\\<|\\>|؟|\\'|\\!|$)", 'g'),
			function (x) { return x.replace(/ا/i, 'آ'); } // 'i' is just to trick bidi algorithm on code view
		);
		// بن مضارع که آ دارند
		text = text.replace(
			new RegExp("(^|\u200c|\\s|_|«|»|\\[|\\(|\\<|\\>|\\')(" + dictionary.PresentVerbsWithA + ")(م|ی|د|یم|ید|ند)(?= |«|»|\\.|،|_|\\s|\\]|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'),
			function (x) { return x.replace(/ا/i, 'آ'); } // 'i' is just to trick bidi algorithm on code view
		);

		// بن ماضی که آ دارند
		text = text.replace(
			new RegExp("(^|\u200c|\\s|_|«|»|\\[|\\(|\\<|\\>|\\')(" + dictionary.PastVerbsWithA + ")(م|ی|یم|ید|ند|ه|)(?= |«|»|\\.|،|_|\\s|\\]|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'),
			function (x) { return x.replace(/ا/i, 'آ'); } // 'i' is just to trick bidi algorithm on code view
		);

		// همزه ضم
		text = text.replace(
			new RegExp(preword + dictionary.HamzehZam + ")(‌ها|ها|ین|ان|ی|ئی|یی|ٔ|)(?= |«|»|\\.|،|_|\\s|\\]|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'),
			function (x) { return x.replace(/وء/, 'ؤ').replace(/و/i, 'ؤ'); } // 'i' is just to trick bidi algorithm on code view
		);
		//همزه نصب
		text = text.replace(
			new RegExp(preword + dictionary.HamzehNasb + ")(ی|ئی|یی|ٔ|)(?= |«|»|\\.|،|_|\\s|\\]|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'),
			function (x) { return x.replace(/ا/i, 'أ'); } // 'i' is just to trick bidi algorithm on code view
		);

		//همزه وسط کلمه
		for (i in dictionary.HamzehAtInside) {
			text = text.replace(new RegExp(
				preword + i + ')(| )(' + dictionary.HamzehAtInside[i] + ")(?= |«|»|\\.|،|_|\\s|\\]|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)",
				'g'
			), '$1$2ء$4');
		}

		// در مورد افزودن یا حذف همزهٔ پایانی اجماعی وجود ندارد.
		/* text = text.replace(new RegExp(preword + dictionary.HamzehAtEnd + ")(?= |«|»|\\.|،|_|\\s|\\]|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'),'$1$2ء'); */

		//الف مقصوره
		text = text.replace(
			new RegExp(preword + dictionary.AlefMaghsooreh + ")(?= |«|»|\\.|،|_|\\s|\\]|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'),
			function (x) { return x.replace(/ا/i, 'ی'); } // 'i' is just to trick bidi algorithm on code view
		);

		// صفت+تر
		text = text.replace(new RegExp("(^|\\s|_|«|»|\\]|\\[|\\(|\\<|\\>|\\')(" + dictionary.adjective + ")( |_)تر(?= |«|»|\\.|\\[|\\]|،|_|\\s|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'), '$1$2\u200cتر');

		// اسامی رنگ‌ها (به‌عنوان صفت)+تر
		text = text.replace(new RegExp("(^|\\s|_|«|»|\\]|\\[|\\(|\\<|\\>|\\')(" + dictionary.colorsNames + ")( |_)تر(?= |«|»|\\.|\\[|\\]|،|_|\\s|\\:|\\)|\\<|\\>|؟|\\!|\\'|$)", 'g'), '$1$2\u200cتر');

		text = text.replace(/به دست\u200cآورد/g, 'به دست آورد'); // Solving a bug!
		text = persianTools.normalizeZwnj(text);
		return text.replace(new RegExp("(^|[؛\\s\\n\\.،«»\'\\<\\>؟])(" + dictionary.needsNasb + ')[' + NASB + ZAMM + ']?([؛؟\\s\\n\\.،«»\'"\\<\\>]|$)', 'g'), function (match) {
			return match
				.replace(new RegExp('ا([\\s\\n\\.،«»؟؛"\'\\>\\<' + ZAMM + '])', 'i'), 'ا' + NASB + '$1')
				.replace(new RegExp(NASB + '["' + NASB + ZAMM + ']'), NASB);
		});
	}

	function wikiDictation(text) {
		return replaceExcept(
			text,
			dictation,
			[patterns.globalExceptionTag, patterns.fileNames, patterns.signatures, patterns.url, patterns.galleryTag, patterns.insideQuote, patterns.argumentsBlacklist, patterns.articleTitleParts]
		);
	}
	function wikiApplyOrthography(text) {
		text = text //en:Wikipedia:HTML5
			//big
			.replace(/((?:\<big\>){5})([^<]+)((?:\<\/big\>){5})/g, '<span style="font-size: 56px;">$2</span>')
			.replace(/((?:\<big\>){4})([^<]+)((?:\<\/big\>){4})/g, '<span style="font-size: 38px;">$2</span>')
			.replace(/((?:\<big\>){3})([^<]+)((?:\<\/big\>){3})/g, '{{خیلی بزرگ|$2}}')
			.replace(/((?:\<big\>){2})([^<]+)((?:\<\/big\>){2})/g, '{{بزرگ|$2}}')
			.replace(/((?:\<big\>){1})([^<]+|[\s\S]+)((?:\<\/big\>){1})/g, '{{درشت|$2}}')
			//center
			.replace(/<center><gallery>([\S\s]+?)\<\/gallery><\/center>/g, '<gallery class="center">$1</gallery>')
			//.replace(/<center>([\S\s]+?)<\/center>/g,'{{وسط|$1}}')
			//empty tag
			.replace(/<span style="font-size: [^>]+"><\/span>/g, '')
			.replace(/{{(?:درشت|خیلی بزرگ|بزرگ|وسط)\|}}/g, '')

		//حذف برچسب‌های خالی نرم‌افزار مدیاویکی
		var tags = ['math', 'div', 'grammarly\\-btn', 'code', 'nowiki', 'pre', 'syntaxhighlight', 'source', 's', 'noinclude', 'includeonly', 'big', 'small', 'gallery'];// has bug for sub , sup [[Special:Diff/19450140/19490903]]
		text = replaceExcept(
			text,
			function (text) {
				for (var i = 0; i < tags.length; ++i) {
					for (var b = 0; b < 5; ++b) {//خیلی از برچسب‌های خالی تو در تو هستند مانند [[Special:PermaLink/19223877]]
						text = text.replace(new RegExp('\<' + tags[i] + '[^\>]*\>(\\n|\\s|\u200c)*?\<\\/' + tags[i] + '\>', 'g'), '');
					}
					// remove the tags if they occurred multiple times consequently
					if (mw.config.get('wgNamespaceNumber') === 0 || mw.config.get('wgNamespaceNumber') === 4) {
						text = text.replace(new RegExp('(\<' + tags[i] + '\>){2,}', 'g'), '$1')
							.replace(new RegExp('(\<\\/' + tags[i] + '\>){2,}', 'g'), '$1');
					}
				}
				if (mw.config.get('wgNamespaceNumber') === 0) {
					text = text.replace(/\<ref\>[\s\n]*\<\/ref\>/g, '').replace(/\<ref\>[\s\n]*\<ref\>/g, '<ref>').replace(/\<\/ref\>[\s\n]*\<\/ref\>/g, '</ref>').replace(/\<ref\/\>/g, '</ref>');
				};
				return text
			},
			[patterns.insideHtmlComment]
		);
		return replaceExcept(
			text,
			persianTools.applyOrthography,
			[patterns.globalExceptionTag, patterns.fileNames, patterns.signatures, patterns.url, patterns.galleryTag, patterns.wikilinkTargets, patterns.rfd]
		).replace(patterns.galleryTag, function (gallery) {
			// apply `applyOrthography` on gallery descriptions separately
			return gallery.replace(/^([^\|]*?\|)(.*)$/mg, function (x, y, z) {
				return y + persianTools.applyOrthography(z);
			});
		});
	}

	// probably should be exactly same above but for applyZwnj
	function wikiApplyZwnj(text) {
		return replaceExcept(
			text,
			persianTools.applyZwnj,
			[patterns.globalExceptionTag, patterns.fileNames, patterns.signatures, patterns.url, patterns.galleryTag]
		).replace(patterns.galleryTag, function (gallery) {
			// apply `applyOrthography` on gallery descriptions separatly
			return gallery.replace(/^([^\|]*?\|)(.*)$/mg, function (x, y, z) {
				return y + persianTools.applyZwnj(z);
			});
		});
	}

	function replaceEnMonth(text) {
		var enMonth = {
			'آگست': 'اوت',
			'آگوست': 'اوت',
			'جولای': 'ژوئیه',
			'مارچ': 'مارس',
			'آپریل': 'آوریل',
			'فوریوری': 'فوریه',
			'january': 'ژانویه',
			'jan': 'ژانویه',
			'february': 'فوریه',
			'feb': 'فوریه',
			'march': 'مارس',
			'mar': 'مارس',
			'april': 'آوریل',
			'apr': 'آوریل',
			'may': 'مه',
			'june': 'ژوئن',
			'jun': 'ژوئن',
			'july': 'ژوئیه',
			'august': 'اوت',
			'aug': 'اوت',
			'september': 'سپتامبر',
			'sept': 'سپتامبر',
			'sep': 'سپتامبر',
			'october': 'اکتبر',
			'oct': 'اکتبر',
			'november': 'نوامبر',
			'nov': 'نوامبر',
			'december': 'دسامبر',
			'dec': 'دسامبر'
		};
		for (var i in enMonth) {
			var text_new = text.replace(new RegExp(i, 'ig'), enMonth[i])
			if (text_new != text) {
				return text_new
			}
		}
		return text
	};
	function autoFormatCleanDatesException(text) {
		return replaceExcept(
			text,
			autoFormatCleanDates,
			[patterns.globalExceptionTag, patterns.fileNames, patterns.galleryTag, patterns.mapFrameTag, patterns.mathTag, patterns.sourceTag, patterns.templateWithEnglishName, patterns.citation, patterns.argumentsBlacklist, patterns.ref]
		)
	}
	function wikitranslateEnMonth(text) {
		text = autoFormatCleanDatesException(text)
		return replaceExcept(
			text,
			function translateEnMonth(text) {
				var enMonthRegex = '(آگست|آگوست|جولای|مارچ|آپریل|january|jan|february|feb|march|mar|april|apr|may|jun|june|july|august|aug|sep|sept|september|oct|october|nov|november|december|dec)';
				return text.replace(new RegExp('([^a-zA-Z])(\^|\\||\\s|\\=|\\n|\\(|«|\\:)' + enMonthRegex + ' (\\d{1,2}|[۱۲۳۴۵۶۷۸۹۰]{1,2})\\, (\\d{3,4}|[۱۲۳۴۵۶۷۸۹۰]{3,4})(\\||\\s|\\n|\$|\\)|\\}|»)([\^a\-zA\-Z])', 'ig'),
					function (x) {
						x = x.replace(new RegExp('([\^a\-zA\-Z])(\^|\\||\\s|\\=|\\n|\\(|«|\\:)' + enMonthRegex + ' (\\d{1,2}|[۱۲۳۴۵۶۷۸۹۰]{1,2})\\, (\\d{3,4}|[۱۲۳۴۵۶۷۸۹۰]{3,4})(\\||\\s|\\n|\$|\\)|\\}||»)([^a-zA-Z])', 'ig'),
							'$1$2$4 $3 $5$6$7')
						x = persianTools.toPersianDigits(x);
						x = replaceEnMonth(x);
						return x;
					})
					.replace(new RegExp('([\^a\-zA\-Z])(\^|\\||\\s|\\=|\\n|\\(|«)((\\d{1,2}|[۱۲۳۴۵۶۷۸۹۰]{1,2}) |)' + enMonthRegex + ' (\\d{3,4}|[۱۲۳۴۵۶۷۸۹۰]{3,4})(\\||\\s|\\n|$|\\)|\\}|»|\\:)([\^a\-zA\-Z])', 'ig'),
						function (x) {
							x = persianTools.toPersianDigits(x);
							x = replaceEnMonth(x);
							return x;
						}
					);
			}, [patterns.globalExceptionTag, patterns.fileNames, patterns.ref, patterns.fileParameter, patterns.galleryTag, patterns.mapFrameTag, patterns.mathTag, patterns.sourceTag, patterns.templateWithEnglishName, patterns.citation, patterns.argumentsBlacklist]
		)
	}

	var arabicDigits = '0123456789', persianDigits = '۰۱۲۳۴۵۶۷۸۹', arabicIndicDigits = '٠١٢٣٤٥٦٧٨٩';
	function robustToEnglishDigits(text) {
		var i = 0;
		for (i = 0; i <= 9; i = i + 1) {
			text = text.replace(new RegExp('[' + persianDigits[i] + arabicIndicDigits[i] + ']', 'g'), arabicDigits[i]);
		}
		return text;
	}

	function toEnglishDigits(text) {
		text = text.replace(/[a-zA-Z]([\_\s\:\.\,\;\]\[\"\'\)\(\}\{\/\\ ]+|)([۱۲۳۴۵۶۷۸۹۰٪\.٫\-\—\–°÷×\+\,\s\_\:،»«؛]+)([\_\s\:\.\,\;\]\[\"\'\)\(\}\{\/\\\<\> ]+|)([a-zA-Z\>]|$)/g, function (x) {
			var i = 0;
			for (i = 0; i <= 9; i = i + 1) {
				x = x.replace(new RegExp('[' + persianDigits[i] + arabicIndicDigits[i] + ']', 'g'), arabicDigits[i]);
			}
			return x.replace(/،/g, ',').replace(/»/g, '"').replace(/«/g, '"').replace(/؛/g, ';');
		});
		// bug [[Special:Diff/17760890/17760898]]
		text = text.replace(/([a-zA-Z][۱۲۳۴۵۶۷۸۹۰]+) *\=/g, function (x) {
			var i = 0;
			for (i = 0; i <= 9; i = i + 1) {
				x = x.replace(new RegExp('[' + persianDigits[i] + arabicIndicDigits[i] + ']', 'g'), arabicDigits[i]);
			}
			return x;
		});
		// ISBN, ISSN and PMID's numbers should in english
		text = text.replace(/\b(ISBN|ISSN|PMID|PubMed) *:? *([۱۲۳۴۵۶۷۸۹۰0-9–—−ـ_\-]+)([^۱۲۳۴۵۶۷۸۹۰0-9–—−ـ_\-]|$)/gi, function (x) {
			x = x.replace(/[–—−ـ_\-]+/g, '-')
			var i = 0;
			for (i = 0; i <= 9; i = i + 1) {
				x = x.replace(new RegExp('[' + persianDigits[i] + arabicIndicDigits[i] + ']', 'g'), arabicDigits[i]);
			}
			x = x.replace(/\b(ISBN|ISSN|PMID|PubMed) *:? *([۱۲۳۴۵۶۷۸۹۰\-0-9]+)([^۱۲۳۴۵۶۷۸۹۰\-0-9]|$)/gi, '$1 $2$3')
			x = x.replace('PubMed', 'PMID')
			return x;
		});
		text = text.replace(/ISBN \-note/g, 'ISBN-note');
		// تبدیل عددهای فارسی در عدد ترتیبی انگلیسی
		text = text.replace(/(?:^|["\'\s_«\(\[\{])([۱۲۳۴۵۶۷۸۹۰]+)(st|nd|rd|th)[\s_\.,»"\'\)\]\}]/g, function (x) {
			var i = 0;
			for (i = 0; i <= 9; i = i + 1) {
				x = x.replace(new RegExp('[' + persianDigits[i] + arabicIndicDigits[i] + ']', 'g'), arabicDigits[i]);
			}
			return x;
		});
		return text
			.replace(new RegExp('([' + arabicDigits + ']) ?٪', 'g'), '$1%')
			.replace(new RegExp('([' + arabicDigits + '])٫(?=[' + arabicDigits + '])', 'g'), '$1.') // English decimal separator
	}
	function cleanTemplateBracesFromArticle(text, alwaysRun) { // like [[Special:Diff/18828723]]
		if (mw.config.get('wgNamespaceNumber') !== 0 && !alwaysRun) {
			return text;
		}
		var i = 0
		for (i = 0; i <= 5; i = i + 1) {
			text = text.replace(/\{\{\{[^\|\}]+\|([^\}]+)\}\}\}/g, '$1')
				.replace(/\{\{\{[^\|\}]+\| *\}\}\}/g, '')
		}

		return text.replace(/\{\{\#(?!invoke)/ig, '{{جا:#')
			.replace(/\{\{ *(PAGENAME|FULLPAGENAMEE|SITENAME|NAMESPACE) *\}\}/g, '{{جا:$1}}')
			.replace('{{ترتیب‌پیش‌فرض:}}', '');
	}
	function decodeHTMLSymbolEntitiesCodes(text) {
		//&copy; > ©
		return replaceExcept(
			text,
			function decodeEntitiesCodes(text) {
				if(!text) return '';
				for (var i in htmlEntityCodes) {
					var entityInput = i;
					var entityoutput = htmlEntityCodes[i];
					text = text.replace(new RegExp(entityInput, 'g'), entityoutput);
				}
				return text;
			}, [patterns.globalExceptionTag, patterns.fileNames, patterns.ref, patterns.fileParameter,
			patterns.galleryTag, patterns.mapFrameTag, patterns.mathTag, patterns.sourceTag, patterns.templateWithEnglishName,
			patterns.citation, patterns.url]
		)
	}
	function removeWikiSigne(text) {
		return replaceExcept(
			text,
			function (text) {
				text = text.replace(/\[\[([^\|\]]+)\|([^\]]+)\]\]/g, '$2');
				text = text.replace(/\[\[([^\|\]]+)\]\]/g, '$1');
				return text;
			}, [patterns.globalExceptionTag, patterns.fileNames, patterns.fileParameter,
			patterns.mapFrameTag, patterns.mathTag, patterns.sourceTag, patterns.templateWithEnglishName, patterns.nowikiTag, patterns.preTag,
			patterns.insideHtmlComment]
		)
	}
	function autoEdHTMLtoWikitextWikiTools(text) {
		return replaceExcept(
			text,
			autoEd.autoEdHTMLtoWikitext,
			[patterns.fileParameter, patterns.mapFrameTag, patterns.mathTag, patterns.sourceTag]
		)
	}
	function superTool(text) {
		text = decodeHTMLSymbolEntitiesCodes(text);
		text = cleanTemplateBracesFromArticle(text);
		text = persianWikiTools.wikiConvertToPersianCharacters(text);
		text = persianWikiTools.wikiApplyZwnj(text);
		text = persianWikiTools.wikiApplyOrthography(text);
		text = persianWikiTools.wikitranslateEnMonth(text);
		if (mw.config.get('wgNamespaceNumber') !== 10) {
			text = persianWikiTools.wikiTextDigitsToPersian(text);
		}
		text = persianWikiTools.wikiUrlMinifier(text);
		text = persianWikiTools.wikiDictation(text);
		text = persianWikiTools.wikiPunctuation(text);
		text = persianWikiTools.wikiSubsection(text);
		text = persianWikiTools.SubSectionLeveling(text)
		//ابزارهای بیشتر برگرفته از ویکی‌پدیای انگلیسی [[Mediawiki:Gadget-Extra-Editbuttons-autoed.js]]
		text = autoEd.autoEdISBN(text);
		text = autoEd.autoEdWhitespace(text);
		text = autoEdHTMLtoWikitextWikiTools(text);
		text = autoEd.autoEdHeadlines(text);
		text = autoEd.autoEdTablestoWikitext(text);
		text = autoEd.autoEdExtraBreaks(text);
		text = persianWikiTools.addColumnToRefTemplate(text);
		text = persianWikiTools.removeCheckDict(text);
		text = persianWikiTools.sortCategories(text, mw.config.get('wgPageName').replace(/_/g, ' '));
		return text;
	}

	function superToolMove(text) {
		text = ' ' + text + ' '; // بعضی از کدها اگر فاصله در انتها یا اول نباشد عمل نمی‌کنند مانند افزودن تنوین به کلمه بعدا
		text = persianWikiTools.wikiConvertToPersianCharacters(text);
		text = persianTools.applyZwnj(text);
		text = persianWikiTools.wikiApplyOrthography(text);
		text = persianWikiTools.wikiTextDigitsToPersian(text);
		text = persianWikiTools.wikiUrlMinifier(text);
		text = persianWikiTools.wikiDictation(text);
		text = persianWikiTools.wikiPunctuation(text);
		return text.trim();
	}

	// Puts the selceted text (trims spaces from start and end) in bdi tags
	// does not put the whole content in tags if no text is selected
	function putSelectionInBDI(text) {
	    // if no text is selected, don't put the whole content inside the tags
	    // can't return nothing because applyOnSelection() expects some text
	    // and throws an error while no text is provided
	    if (text == $('#wpTextbox1').val()) {
	        return text;
	    } else {
	        // regExp to trim space from begining and end of the selected text,
	        // and save them to be placed outside of tags
	        var regExp = /(^\s*)|(\s*$)/gm;
	        var startEndSpaces = text.match(regExp);
	        // place selected spaces outside of tags
	        // and remove them from the text inside
	        var outText = startEndSpaces[0] + '<bdi>' + text.replace(regExp, '') + '</bdi>' +  startEndSpaces[1]; 
	        return outText;
	    }
	}

	return {
		removeWikiSigne: removeWikiSigne,
		superTool: superTool,
		superToolMove: superToolMove,
		dictation: dictation,
		wikiApplyOrthography: wikiApplyOrthography,
		wikiApplyZwnj: wikiApplyZwnj,
		wikiConvertToPersianCharacters: wikiConvertToPersianCharacters,
		wikiDictation: wikiDictation,
		wikiPunctuation: wikiPunctuation,
		wikiSubsection: wikiSubsection,
		SubSectionLeveling: SubSectionLeveling,
		wikiUrlMinifier: wikiUrlMinifier,
		wikiTextDigitsToPersian: wikiTextDigitsToPersian,
		wikitranslateEnMonth: wikitranslateEnMonth,
		addColumnToRefTemplate: addColumnToRefTemplate,
		fixBadLinks: fixBadLinks,
		robustToEnglishDigits: robustToEnglishDigits,
		toEnglishDigits: toEnglishDigits,
		cleanTemplateBracesFromArticle: cleanTemplateBracesFromArticle,
		removeCheckDict: removeCheckDict,
		sortCategories: sortCategories,
		putSelectionInBDI : putSelectionInBDI
	};
}());
if (typeof window !== "undefined") {
	window.persianWikiTools = persianWikiTools;
}
// </nowiki>