Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
  • Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
  • Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
  • Opera: Нажмите Ctrl+F5.
//<!--
( function () {
var wmVersion = '2021-03-13';
var wmCantWork = 'Викификатор не может работать в вашем браузере\n\nWikificator can not work in your browser';
var wmFullText = 'Викификатор обработает ВЕСЬ текст на этой странице. Продолжить?';
var wmTalkPage = 'Викификатор не обрабатывает страницы обсуждения целиком.\n\nВыделите ваше сообщение — обработано будет только оно';

function Wikify(inputOrText) {
	var text, isInput, $input, caretPosition, textScroll,
		txt = '',
		hidden = [],
		winScroll = document.documentElement.scrollTop,
		$CodeMirrorVscrollbar = $( '.CodeMirror-vscrollbar' );

	// Check what's in the first parameter
	if ( typeof inputOrText === 'string' ) {
		text = inputOrText;
	} else {
		isInput = ( inputOrText &&
			( ( inputOrText.nodeType && inputOrText.value !== undefined ) || // node
				( inputOrText.prop && inputOrText.prop( 'nodeType' ) ) // jQuery object
			)
		);
		$input = $( isInput ? inputOrText : '#wpTextbox1' );
	}

	// Functions:

	function r(regExp, replaceTo) {
		txt = txt.replace(regExp, replaceTo);
	}

	function hide(regExp) {
		r(regExp, function (value) {
			return '\x01' + hidden.push(value) + '\x02';
		});
	}

	function hideTag(tag) {
		hide(new RegExp('<' + tag + '( [^>]+)?>[\\s\\S]+?<\\/' + tag + '>', 'gi'));
	}

	function hideTags(tags) {
		for (var i = 0; i < tags.length; i++) {
			hideTag(tags[i]);
		}
	}
	
	function hideTemplate(template) {
		hide(new RegExp('\{\{' + template + '[^-A-Za-zА-Яа-я0-9][\\s\\S]*?\}\}', 'g'));
	}
	
	function hideTemplates(templates) {
		for (var i = 0; i < templates.length; i++) {
			hideTemplate(templates[i]);
		}
	}

	function findall(regExp, func) {
		var matches = txt.match(regExp);
		if (matches) {
			for (var i = 0; i < matches.length; i++) {
				func(matches[i]);
			}
		}
	}

	function endsWith(str, suffixes) {
		for (var i = 0; i < suffixes.length; i++) {
			var suffix = suffixes[i];
			if (str.indexOf(suffix, str.length - suffix.length) !== -1) {
				return true;
			}
		}
		return false;
	}

	function processText() {
		var pattern, matches; // служебные переменные
		var nbsp = '\u00A0'; // non-breaking space
		var spacenum = mw.config.get('wgNamespaceNumber');
		if (spacenum % 2 || spacenum == 4) { // is talk page
			nbsp = ' ';
			var sigs = txt.match(/\d\d:\d\d, \d\d? \S{3,8} 20\d\d \(UTC\)/g);
			if (sigs && sigs.length > 1) {
				alert(wmTalkPage);
				return;
			}
		}

		hideTags(['nowiki', 'pre', 'source', 'code', 'tt', 'math', 'gallery']);

		//hide(/{\{[\s\S]+?}}/g); // templates
		//hide(/\{\{илл[\s\S]+?\}\}/g); // Скрываем шаблон "илл", чтобы не заменялись тире в именах файлов
		//hide(/\{\{Слайдер[\s\S]+?\}\}/g); // Скрываем шаблон "Слайдер", чтобы не заменялись тире в именах файлов
		//hide(/\{\{сэ[\s\S]+?\}\}/g); // Скрываем шаблон "сэ", чтобы не заменялись ' на ’ во французских словах
		hideTemplates(['илл', 'Слайдер', 'сэ', 'Сэ', 'от', 't', 'trad', 'с']);

		hide(/^ .*/mg);
		hide(/(https?|ftp|news|nntp|telnet|irc|gopher):\/\/[^\s\[\]<>"]+ ?/gi);
		hide(/^#(redirect|перенапр(авление)?)/i);

		r(/ +(\n|\r)/g, '$1'); // spaces at EOL
		txt = '\n' + txt + '\n';

		// Links
		r(/(\[\[:?)(category|категория):( *)/ig, '$1Категория:');
		r(/(\[\[:?)(image|изображение|file):( *)/ig, '$1Файл:');
		// Linked years, centuries and ranges
		r(/(\(|\s)(\[\[[12]?\d{3}\]\])[\u00A0 ]?(-{1,3}|—) ?(\[\[[12]?\d{3}\]\])(\W)/g, '$1$2–$4$5'); // короткое тире
		r(/(\[\[[12]?\d{3}\]\]) ?(гг?\.)/g, '$1' + nbsp + '$2');
		r(/(\(|\s)(\[\[[IVX]{1,5}\]\])[\u00A0 ]?(-{1,3}|—) ?(\[\[[IVX]{1,5}\]\])(\W)/g, '$1$2–$4$5'); // короткое тире
		r(/(\[\[[IVX]{1,5}\]\]) ?(вв?\.)/g, '$1' + nbsp + '$2');
		r(/\[\[(\d+)\]\][\u00A0 ]год/g, '[[$1' + nbsp + 'год]]');
		r(/\[\[((\d+)(?: (?:год )?в [\wa-яёА-ЯЁ ]+\|\2)?)\]\][\u00A0 ](год[а-яё]*)/g, '[[$1' + nbsp + '$3]]');
		r(/\[\[([XVI]+)\]\][\u00A0 ]век/g, '[[$1' + nbsp + 'век]]');
		r(/\[\[(([XVI]+) век\|\2)\]\][\u00A0 ]век/g, '[[$2' + nbsp + 'век]]');
		// Nice links
		r(/(\[\[[^|\[\]]*)[\u00AD\u200E\u200F]+([^\[\]]*\]\])/g, '$1$2'); // Soft Hyphen & DirMark
		r(/\[\[ *([a-zA-Zа-яёА-ЯЁ\u00A0-\u00FF %!\"$&'()*,\-.\/0-9:;=?\\@\^_`’~]+) *\| *(\1)([a-zа-яё]*) *\]\]/g, '[[$2]]$3'); // "
		r(/\[\[ *([a-zA-Zа-яёА-ЯЁ\u00A0-\u00FF %!\"$&'()*,\-.\/0-9:;=?\\@\^_`’~]+) *\| *([^|[\]]+) *\]\]([a-zа-яё]+)/g, '[[$1|$2$3]]'); // "
		
		// удаление ссылок в шаблоне {{родств-блок}}
		findall(/{{родств-блок[^{}|]*[^{}]+}}/gm, function (value) {
			r(value, value.replace(/\[\[([^\[\]\|]*?)\]\]/g, '$1'));
		});
		
		hide(/\[\[[^\]|]+/g); // only link part

		// Tags
		r(/<<(\S.+\S)>>/g, '"$1"'); //<< >>
		r(/(sup>|sub>|\s)-(\d)/g, '$1−$2'); //minus
		r(/(<sup>2<\/sup>|&sup2;)/gi, '²');
		r(/(<sup>3<\/sup>|&sup3;)/gi, '³');
		r(/<(b|strong)>(.*)<\/(b|strong)>/gi, "'''$2'''");
		r(/<(i|em)>(.*?)<\/(i|em)>/gi, "''$2''");
		r(/^<hr ?\/?>/gim, '----');
		r(/<\/?(hr|br)( [^\/>]+?)? ?\/?>/gi, '<$1$2 />');
		r(/(\n== *Примечания *==\n)<references *\/>/, '$1{{примечания}}');
		hide(/<[a-z][^>]*?>/gi);

		// leading spaces in args of {{помета}}:
		r(/{{помета[|]([^}]+) +}}/g, '{{помета|$1}}');
		r(/{{помета[|] +([^}]+)}}/g, '{{помета|$1}}');
		
		// Tables
		hide(/^({\||\|-).*/mg); // table/row def
		hide(/(^\||^!|!!|\|\|) *[a-z]+=[^|]+\|(?!\|)/mgi); // cell style
		hide(/\| +/g); // formatted cell

		// Spaces
		r(/[ \t]+/g, ' '); // double spaces
		r(/\n\n\n+/g, '\n\n'); // remove several empty lines

		// Headings
		r(/^(=+)[ \t\f\v]*(.*?)[ \t\f\v]*=+$/gm, '$1 $2 $1'); // add spaces inside
		r(/([^\r\n])(\r?\n==.*==\r?\n)/g, '$1\n$2'); // add empty line before
		r(/^== см(\.?|отрите) ?также ==$/gmi, '== См. также ==');
		//r(/^== сноски ==$/gmi, '== Примечания ==');
		r(/^== (.+)[.:] ==$/gm, '== $1 ==');

		r(/«|»|“|”|„/g, '"'); // temp

		// Hyphens and en dashes to pretty dashes
		//r(/–/g, '-'); // &ndash; ->  hyphen
		r(/&(#151|mdash);/g, '—'); // -> &mdash;
		r(/(&nbsp;|\s)[-—]{1,3} /g, '$1— '); // hyphen -> &mdash;
		r(/(\d)--(\d)/g, '$1–$2'); // -> &ndash;
		r(/^# ?-$/mg, '# —'); // это нужно в списках синонимов/антонимов

		// Entities etc. -> Unicode chars
		r(/&#x([0-9a-f]{1,4});/gi, function (n, a) {
			return String.fromCharCode(eval('0x' + a.substr(-4)));
		}); // &#x301;
		r(/&copy;/gi, '©');
		r(/&reg;/gi, '®');
		r(/&sect;/gi, '§');
		r(/&euro;/gi, '€');
		r(/&yen;/gi, '¥');
		r(/&pound;/gi, '£');
		r(/&deg;/g, '°');
		r(/\(tm\)|\(тм\)|&trade;/gi, '™');
		r(/\.\.\.|&hellip;/g, '…');
		r(/\+-|&plusmn;/g, '±');
		r(/~=/g, '≈');
		r(/\^2(\D)/g, '²$1');
		r(/\^3(\D)/g, '³$1');
		r(/&((la|ra|bd|ld)quo|quot);/g, '"');
		r(/([\wа-яА-ЯёЁ])'([\wа-яА-ЯёЁ])/g, '$1’$2'); //'
		r(/№№/g, '№');

		// Year and century ranges
		r(/(\(|\b)([12]?\d{3})[\u00A0 ]?(-{1,3}|—) ?([12]?\d{3})(?![\w-°])/g, '$1$2–$4'); // короткое тире
		r(/([12]?\d{3}) ?(гг?\.)/g, '$1' + nbsp + '$2');
		r(/(\(|\s)([IVX]{1,5})[\u00A0 ]?(-{1,3}|—) ?([IVX]{1,5})(?![\w-°])/g, '$1$2–$4'); // короткое тире
		r(/([IVX]{1,5}) ?(вв?\.)/g, '$1' + nbsp + '$2');

		// Reductions
		//r(/(Т|т)\.\s?е\./g, '$1о есть');
		//r(/(Т|т)\.\s?к\./g, '$1ак как');
		//r(/(В|в)\sт\. ?ч\./g, '$1 том числе');
		r(/и\sт\.\s?д\./g, 'и' + nbsp + 'т.' + nbsp + 'д.');
		r(/и\sт\.\s?п\./g, 'и' + nbsp + 'т.' + nbsp + 'п.');
		r(/(Т|т)\.\s?н\./g, '$1.' + nbsp + 'н.');
		r(/н\.\s?э\./g, 'н.' + nbsp + 'э.');
		r(/(Д|д)(о|\.)\sн\.\s?э\./g, '$1о' + nbsp + 'н.' + nbsp + 'э.');
		r(/(\d)[\u00A0 ]?(млн|млрд|трлн|(?:м|с|д|к)?м|[км]г)\.?(?=[,;.]| "?[а-яё-])/g, '$1' + nbsp + '$2');
		r(/(\d)[\u00A0 ](тыс)([^\.А-Яа-яЁё])/g, '$1' + nbsp + '$2.$3');
		r(/ISBN:\s?(?=[\d\-]{8,17})/, 'ISBN ');

		// Insert/delete spaces
		r(/&#32;/g, ' '); // html-code of space
		r(/^([#*:]+)[ \t\f\v]*([^ \t\f\v*#:;])/gm, '$1 $2'); // space after #*:
		r(/(\S) (-{1,3}|—) (\S)/g, '$1' + nbsp + '— $3');
		r(/([А-Я]\.) ?([А-Я]\.) ?([А-Я][а-я])/g, '$1' + nbsp + '$2' + nbsp + '$3');
		r(/([А-Я]\.)([А-Я]\.)/g, '$1 $2');
		r(/([а-я]\.)([А-ЯA-Z])/g, '$1 $2'); // word. word
		r(/([)"а-яa-z\]])\s*,([\[("а-яa-z])/g, '$1, $2'); // word, word
		r(/([)"а-яa-z\]])\s([,;])\s([\[("а-яa-z])/g, '$1$2 $3');
		r(/([^%\/\w]\d+?(?:[.,]\d+?)?) ?([%‰])(?!-[А-Яа-яЁё])/g, '$1' + nbsp + '$2'); // 5 %
		r(/(\d) ([%‰])(?=-[А-Яа-яЁё])/g, '$1$2'); // 5%-й
		r(/([№§])(\s*)(\d)/g, '$1' + nbsp + '$3');
		r(/\( +/g, '(');
		r(/ +\)/g, ')'); // inside ()

		// Temperature
		r(/([\s\d=≈≠≤≥<>("'|])([+±−-]?\d+?(?:[.,]\d+?)?)(([ °^*]| [°^*])[CС])(?=[\s"').,;!?|])/gm, '$1$2' + nbsp + '°C'); //'
		r(/([\s\d=≈≠≤≥<>("'|])([+±−-]?\d+?(?:[.,]\d+?)?)(([ °^*]| [°^*])F)(?=[\s"').,;|!?])/gm, '$1$2' + nbsp + '°F'); //'

		// Dot -> comma in numbers
		r(/(\s\d+)\.(\d+[\u00A0 ]*[%‰°])/gi, '$1,$2');

		// "" -> «»
		for (var i = 1; i <= 2; i++) {
			r(/([\s\x02!|#'"\/(;+-])"([^"]*)([^\s"(|])"([^a-zа-яё])/ig, '$1«$2$3»$4'); //"
		}
		while (/«[^»]*«/.test(txt)) {
			r(/«([^»]*)«([^»]*)»/g, '«$1„$2“');
		}

		// Специфичные для Викисловаря вещи
		r(/([а-я\]]{2})\.\s*{{пример/gi, '$1 {{пример'); // Убрать точки в конце толкований
		r(/\{\{categ(.*?)lang=([^\}]*)\}\}/g, '{{Категория|язык=$2$1}}'); // Замена синонима шаблона

		// Заполнение слогов

		matches = txt.match(/(\|слоги=([^{|}]+))(}|\||$})/m); // Если разбиение приведено без шаблона, обернуть его в шаблон
		if (matches) {
			r(matches[1], '|слоги={{по-слогам|' + matches[2].replace("\r", "").replace("\n", "").replace("-", "|").replace("·", "|.|") + '}}\n' );
		}
		var title = mw.config.get('wgTitle');
		r(/\|слоги=$/m, '|слоги={{по-слогам|' + title + '}}');
		r(/{{adv ru\|([а-я]+)}}/, '{{adv ru|{{по-слогам|$1}}}}');
		var words = title.split(/[ .,;:!?—]+/)
		r(/\|слово([1-9])={{по-слогам\|}}\r?\n\|лемма[1-9]=\r?\n(\|знак[1-9]=\r?\n)*/gm, function (str, num) {
			var w = words[parseInt(num)-1]
			var nextch = title.match(new RegExp(w + '([ .,;:!?—]+)') )
			var z = (nextch && nextch[1] != ' ' ) ? '|знак' + num + '=' + nextch[1] + '\r\n' : ''
			return (w) ? '|слово' + num + '={{по-слогам|' + w + '}}\r\n|лемма' + num + '=' + w + '\r\n' + z : ''
		})
		r(/\{\{по-слогам\|\}\}/g, '{{по слогам|' + title + '}}'); // Заполнить пустой шаблон-болванку

		// Удаление устаревших параметров
		findall(/{{(гл|сущ) ru((.|\s)*?)==/gm, function (value) {
			r(value, value.replace("|show-text=1\r\n", '').replace("|show-text=1\n", '').replace("|show-text=1", ''));
		});

		// Добавление значения для параметра "основа="
		r(/\|основа=$/m, '|основа=' + title);

		// Удаление "-а", "-я", "-ь", "-о" и "-й" из основы для изменяемых существительных
		findall(/{{сущ ru [fmn] (ina|a) [1-9][^{}|]*\|основа=([^{}|\n\r]+)/gm, function (value) {
			if (value.match(/f (ina|a) 6\*/) || value.match(/m (ina|a) 6/)) {
				return;
			}
			var word = value.match(/\|основа=([^{}|\n\r]+)/)[1];
			if (endsWith(word, ["ь", "а", "я", "о", "й"])) {
				r(value, value.replace("|основа=" + word, "|основа=" + word.slice(0, -1)));
			}
		});

		// Удаление "-ий", "-ый" и "-ой" в основе для прилагательных
		findall(/{{прил ru[^{}|]*\|основа=([^{}|\n\r]+)/gm, function (value) {
			var word = value.match(/\|основа=([^{}|\n\r]+)/)[1];
			if (endsWith(word, ["ый", "ий", "ой"])) {
				r(value, value.replace("|основа=" + word, "|основа=" + word.slice(0, -2)));
			}
		});

		// Удаление лишнего параметра "основа1="
		pattern =
			"{{" +
				// подходящий заголовок шаблона:
				"(" +
					"сущ ru f ina (0|1a|2a|3a|4a|5a|6a|7a|8a)|" +
					"сущ ru f a (0|1a|2a|3a|4a|5a|7a|8a)|" +
					"сущ ru n ina (0|1a|4a|7a)|" +
					"сущ ru m a (0|1a|2a|3a|4a|6a)|" +
					"сущ ru m ina (0|1a|2a|3a|4a|6a)" +
				")" +
				// оставшаясь часть шаблона:
				"([^{}]*({{([^{}]*({{[^{}]+}})?[^{}]*)+}})?[^{}]*)+" +
			"}}";
		findall(new RegExp(pattern, 'gm'), function (value) {
			r(value, value.replace(/\|основа1=[^|{}]*/g, ''));
		});
		// Возвращение всех ранее скрытых элементов (через hide и hideTag)
		txt = txt.substr(1, txt.length - 2);
		if ('0'.replace('0', '$$') == '$') { ////$ in replacing string is special, except in IE
			for (i = 0; i < hidden.length; i++) {
				hidden[i] = hidden[i].replace(/\$/g, '$$$$');
			}
		}
		while (hidden.length > 0) {
			r('\x01' + hidden.length + '\x02', hidden.pop());
		}
	}
	
	function processAllText() {
		txt = $input ? $input.textSelection( 'getContents' ) : text;
		processText();

		if ( $input ) {
			r( /^[\n\r]+/, '' );
			
			// 2017 wikitext editor adds an empty line to the end with every text replacement
			// Remove the following block when [[phab:T198010]] is fixed.
			if ( window.ve && ve.init && ve.init.target && ve.init.target.active ) {
				r( /[\n\r]+$/, '' );
			}
			
			$input.textSelection( 'setContents', txt );
			if ( caretPosition ) {
				$input.textSelection( 'setSelection', {
					start: caretPosition[0] > txt.length ? txt.length : caretPosition[0]
				} );
			}
		} else {
			text = txt;
		}
		if ( window.auto_comment &&
			window.insertSummary &&
			!document.editform.wpSection.value
		) {
			window.insertSummary( 'викификатор' );
		}
	}

	// MAIN CODE

	if ( $input ) {
		$input.focus();
		
		caretPosition = $input.textSelection( 'getCaretPosition', { startAndEnd: true } );
		if ( caretPosition ) {
			textScroll = ( $CodeMirrorVscrollbar.length ? $CodeMirrorVscrollbar : $input )
				.scrollTop();
			if ( caretPosition[0] === caretPosition[1] ) {
				processAllText();
			} else {
				txt = $input.textSelection( 'getSelection' );
				processText();
				// replaceSelection doesn't work with MediaWiki 1.30 in case this gadget is loaded
				// from other wiki
				$input.textSelection( 'encapsulateSelection', {
					replace: true,
					peri: txt
				} );
				// In CodeMirror, the selection isn't preserved, so we do it explicitly
				$input.textSelection( 'setSelection', {
					start: caretPosition[0],
					end: caretPosition[0] + txt.length
				} );
			}
			( $CodeMirrorVscrollbar.length ? $CodeMirrorVscrollbar : $input )
				.scrollTop( textScroll );
		// If something went wrong
		} else if ( confirm( wmFullText ) ) {
			processAllText();
		}
	} else {
		processAllText();
		return text;
	}

	// scroll back, for 2017 wikitext editor, IE, Opera
	document.documentElement.scrollTop = winScroll;

}

// Toolbar buttons

var addOldToolbarButton = function () {
	var $toolbar = $( '#wikiEditor-ui-toolbar' );
	if ( !$toolbar.length ) {
		$toolbar = $( '#toolbar' );
	}
	$( '<div>' )
		.addClass( 'mw-toolbar-editbutton' )
		.attr( 'alt', 'Викификатор' )
		.attr( 'title', 'Викификатор' )
		.css( {
			'width': '69px',
			'backgroundImage': 'url(//upload.wikimedia.org/wikipedia/commons/3/38/Button_wikify.png)'
		} )
		.appendTo( $toolbar )
		.on( 'click', Wikify );
};

var addNewToolbarButton = function () {
	$( '#wpTextbox1' ).wikiEditor( 'addToToolbar', {
		'section': 'main',
		'group': 'extra',
		'tools': {
			'wikif': {
				label: 'Викификатор',
				type: 'button',
				icon: '//upload.wikimedia.org/wikipedia/commons/0/06/Wikify-toolbutton.png',
				action: {
					type: 'callback',
					execute: Wikify
				}
			}
		}
	} );

	mw.hook( 'wikieditor.toolbar.wikificator' ).fire();
	
};

if ( $.inArray( mw.config.get( 'wgAction' ), [ 'edit', 'submit' ] ) !== -1 && mw.config.get('wgPageContentModel') === 'wikitext' ) {
	mw.loader.using( [ 'user.options', 'jquery.textSelection' ], function () {
		if ( mw.user.options.get( 'usebetatoolbar' ) === 1 ) {
			$.when(
				mw.loader.using( 'ext.wikiEditor' ),
				$.ready
			).then( function () {
				mw.hook( 'wikieditor.toolbar.extragroup' ).add( addNewToolbarButton );
			} );
		} else {
			mw.loader.using( 'mediawiki.toolbar', function () {
				$( addOldToolbarButton );
			});
		}
	} );
}

}() );
//-->