MediaWiki:Gadget-wikifier.js

Материал из свободной русской энциклопедии «Традиция»
Перейти к: навигация, поиск

Замечание: Чтобы после сохранения вступили в силу изменения стилей, перезагрузите файл //traditio.wiki/w/load.php?debug=false&lang=ru&modules=site&only=styles&skin=vector&*, если используете скин Vector, или //traditio.wiki/w/load.php?debug=false&lang=ru&modules=site&only=styles&skin=common&*, если используете скин Common.

Чтобы вступили в силу изменения скриптов, перезагрузите файл //traditio.wiki/w/load.php?debug=false&lang=ru&modules=site&only=scripts&skin=vector&*, если используете скин Vector, или //traditio.wiki/w/load.php?debug=false&lang=ru&modules=site&only=scripts&skin=common&*, если используете скин Common.

Гаджеты и импортируемые скрипты загружаются отдельными файлами.

/*
 * Викификатор. Подключается к панели инструментов «Традиции».
 *
 * '''ВНИМАНИЕ! Внося изменения в код, не забывайте обновлять справку на странице [[Традиция:Викификатор]]'''
 */
var wmVersion = '2017-06-19'
var wmCantWork = 'Викификатор не может работать в вашем браузере\n\nWikifier cannot work in your browser'
var wmTalkPage = 'Викификатор не обрабатывает страницы обсуждения целиком.\n\nВыделите своё сообщение — обработано будет только оно'

// Функция викифицирует переданный текст и возвращает викифицированный:
function wikifyText (s) {
    var txt='', hidden = [];

    var u = '\u00A0'; // -- unbreakable space.
    if (mw.config.get ('wgNamespaceNumber') % 2 === 1 || mw.config.get ('wgNamespaceNumber') === 4) {
    	// -- это обсуждение.
        u = ' ';
        // Несколько дат, вероятно, чужие подписи::
        var sigs = s.match (/\d\d:\d\d, \d\d? \S{3,8} 20\d\d \(UTC\)/g);
            if (sigs && sigs.length > 1) {
                alert(wmTalkPage);
                return s;
             }
    }

    // Скрытие преформатированных тегов и прочих тегов, не подлежащих викификации:
    s = hideTag (s, ['nowiki', 'pre', 'source', 'syntaxhighlight', 'code', 'tt', 'svgcode', 'graphviz', 'mscgen', 'score', 'math', 'gallery']);
    
    s = runReplacements (s, [[/(IS[BS]N):?\s*([\s\d‒−—-]{8,17}[X\d])/ig, '{{$1|$2}}']]); // -- ISBN/ISSN теперь в шаблон.
    
    s = hide (s, /{\{[\s\S]+?}}/g);	// — шаблоны.
    s = hide (s, /^ .*/mg);			// — преформатированный текст.
    
	s = runReplacements (s, [
		[/<\s*a\s+href\s*=\s*(["'])\s*https?:\/\/traditio(?:\.ru|-ru\.org|\.wiki)(?:\/wiki)?\/(\S+?)\s*\1\s*>(.+?)<\s*\/a\s*>/gi, function (_, __, p2, p3) {
        	return '[[' + decodeURIComponent (p2) + '|' + p3 + ']]';
        }]																		// -- внутренние <a> → [[]]
	  , [/<\s*a\s+href\s*=\s*(["'])(\S+?)\1\s*>(.+?)<\s*\/a\s*>/gi, '[$2 $3]']	// -- <a> → []
	]);
	     
    s = hide (s, /(https?|ftp|news|nntp|telnet|irc|gopher):\/\/[^\s\[\]<>"]+ ?/gi);	// — гиперссылки.
    s = hide (s, /^#(redirect|перенапр(авление)?)/i);								// — перенаправления.

    s = s.replace (/ +(\n|\r)/g, '$1'); // -- пробелы в конце строки.
    s = '\n' + s + '\n';

	s = runReplacements (s, [
        // Русификация пространств имён:
        [/(\[\[:?)(category|категория):( *)/ig, '$1Категория:']
      , [/(\[\[:?)(image|изображение|file):( *)/ig, '$1Файл:']
        // Оформление дат:
      , [/(\(|\s)(\[\[[12]?\d{3}\]\])[\u00A0 ]?(-{1,3}|–|—) ?(\[\[[12]?\d{3}\]\])(\W)/g, '$1$2—$4$5']
      , [/(\[\[[12]?\d{3}\]\]) ?(гг?\.)/g, '$1' + u + '$2']
      , [/(\(|\s)(\[\[[IVX]{1,5}\]\])[\u00A0 ]?(-{1,3}|–|—) ?(\[\[[IVX]{1,5}\]\])(\W)/g, '$1$2—$4$5']
      , [/(\[\[[IVX]{1,5}\]\]) ?(вв?\.)/g, '$1' + u + '$2']
      , [/\[\[(\d+)\]\][\u00A0 ]год/g, '[[$1' + u + 'год]]']
      , [/\[\[((\d+)(?: (?:год )?в [\wa-яёА-ЯЁ ]+\|\2)?)\]\][\u00A0 ](год[а-яё]*)/g, '[[$1' + u + '$3]]']
      , [/\[\[([XVI]+)\]\][\u00A0 ]век/g, '[[$1' + u + 'век]]']
      , [/\[\[(([XVI]+) век\|\2)\]\][\u00A0 ]век/g, '[[$2' + u + 'век]]']
      // Удаление недопустимых символов из викиссылок:
      , [/(\[\[[^|\[\]]*)[\u00AD\u200E\u200F]+([^\[\]]*\]\])/g, '$1$2'] // -- Soft Hyphen & DirMark.
      , [/\[\[ *([a-zA-Zа-яёА-ЯЁ\u00A0-\u00FF %!\"$&'()*,\-.\/0-9:;=?\\@\^_`’~]+) *\| *(\1)([a-zа-яё]*) *\]\]/g, '[[$2]]$3'] // -- ".
      , [/\[\[ *([a-zA-Zа-яёА-ЯЁ\u00A0-\u00FF %!\"$&'()*,\-.\/0-9:;=?\\@\^_`’~]+) *\| *([^|[\]]+) *\]\]([a-zа-яё]+)/g, '[[$1|$2$3]]'] // -- ".
     ]);
     s = hide (s, /\[\[[^\]|]+/g); // -- скрытие викиссылок.

    // Все теги в <> надо обработать здесь:
	s = runReplacements (s, [
        // HTML -> викитекст:
		[/<<(\S.+\S)>>/g, '"$1"']			// -- угловые кавычки в обычные. Или лучше в «»?
      , [/(sup>|sub>|\s)-(\d)/g, '$1−$2']	// -- minus в индексах.
      , [/(<sup>2<\/sup>|&sup2;)/gi, '²']	// -- символы квадрата
      , [/(<sup>3<\/sup>|&sup3;)/gi, '³']	//    и куба.
      , [/<(b|strong)>(.+?)<\/(b|strong)>/gi,"'''$2'''"]	// -- вики-полужирный.
      , [/<(i|em)>(.+?)<\/(i|em)>/gi,"''$2''"]				// -- вики-курсив.
      , [/^<hr ?\/?>/gim, '----']							// -- вики-гор. разделитель.
      , [/<\/?(hr|br)( [^\/>]+?)? ?\/?>/gi, '<$1$2 />']		// оформление hr/br по стандарту XML.
      , [/(\n==\s*Примечания\s*==\n)<references *\/>/,'$1{{примечания}}']

        // .,… до сносок -- сдвиг сносок:
      , [/\s*,\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi , '{{,}}$1']   	// -- запятая до;
      , [/\s*(…)\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi , '{{,|$1}}$2']	// -- др. знак до;
      , [/\s*\.\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi, '{{тчк}}$1']	// -- точка до;

        // .,… после сносок -- перенос до и сдвиг сносок:
      , [/\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)\s*(?:,|\{\{(?:,|зпт)\}\})/gi , '{{,}}$1']		// -- запятая после;
      , [/\s*((?:<ref[^<\/>]*?(?:>[^<]+?<\/ref|\/)>)+)\s*([…])/gi , '{{,|$2}}$1']					// -- др. знак после;
      , [/\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)\s*(?:\.|\{\{(?:,\|\.|тчк)\}\})/gi, '{{тчк}}$1']	// -- точка после;

        // !? после сносок -- перенос до:
      , [/\s*((?:<ref[^<\/>]*?(?:>[^<]+?<\/ref|\/)>)+)([!?;:“])/gi , '$2$1']   // -- !? после;

        // !?;:“» до сносок -- удаление лишних пробелов:
      , [/\s*([!?;:“»])\s+((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi , '$1$2']   // -- др. знак до;

    ]);
    s = hide(s, /<[a-z][^>]*?>/gi);	// -- hide all HTML tags.

    s = hide(s, /^({\||\|-).*/mg);	// -- таблицы и ряды.
    s = hide(s, /(^\||^!|!!|\|\|) *[a-z]+=[^|]+\|(?!\|)/mgi);	// -- стили ячеек.
    s = hide(s, /\| +/g);			// -- форматированные ячейки.

    s = s.replace (/[ \t]+/g, ' ');	// -- двойные пробелы.

    s = runReplacements (s, [
        // Заголовки:
        [/^(=+)[ \t\f\v]*(.*?)[ \t\f\v]*=+$/gm, '$1 $2 $1']			// -- окружить пробелами.
      , [/([^\r\n])(\r?\n==.*==\r?\n)/g, '$1\n$2']					// -- пустую строку впереди.
      , [/^== см(\.?|отрите) ?так\s*же ==$/gmi, '== См. также ==']	// -- «См. также».
      , [/^== сноски ==$/gmi, '== Примечания ==']
      , [/^== (.*[^.])[.:] ==$/gm, '== $1 ==']						// -- точка или двоеточие в конце.

      , [/«|»|“|”|„/g, '"']											// -- временное скрытие нормальных кавычек. Нужно ли?

        // Тире и дефисы:
      , [/–/g, '-']                    	// -- &ndash; → -
      , [/&(#151|[nm]dash);/g, '—']     // -- мнемоника тире → —
      , [/(&nbsp;|\s)-{1,3} /g, '$1— '] // -- отбитые - → —
      , [/(\d)-{1,2}(\d)/g, '$1‒$2']    // -- -/-- между цифрами → ‒ (&#2012;)
      , [/([IVXLCDM]+)-{1,2}([IVXLCDM]+)/g, '$1‒$2']    // -- -/-- между римскими цифрами → ‒ (&#2012;)
      , [/(\s)-(\d)/g, '$1−$2']         // -- отбитый - перед цифрой → −

        // Мнемоники HTML -> символы:
      , [/&#x([0-9a-f]{1,4});/gi, function (n, a) {return String.fromCharCode (eval ('0x' + a.substr (-4)))}]  //&#x301;
      , [/&copy;/gi,'©']
      , [/&reg;/gi,'®']
      , [/&sect;/gi,'§']
      , [/&euro;/gi,'€']
      , [/&yen;/gi,'¥']
      , [/&pound;/gi,'£']
      , [/&deg;/g,'°']
      , [/\(tm\)|\(тм\)|&trade;/gi,'™']
      
      // Символы, которых нет на клавиатуре:
      , [/\.\.\.|&hellip;/g,'…']
      , [/\+-|&plusmn;/g,'±']
      , [/~=/g,'≈']
      , [/\^2(\D)/g,'²$1']
      , [/\^3(\D)/g,'³$1']
      , [/\([cс]\)/gi,'©']
      
      // Любые кавычки → в простые, которые потом будут перерасставлены:
      , [/&((la|ra|bd|ld)quo|quot);/g,'"']
      
      , [/([\wа-яА-ЯёЁ])'([\wа-яА-ЯёЁ])/g,'$1’$2'] //' → типографский апостроф.
      
      , [/№№/g,'№'] // знаки номера.

        // Годы и века
      , [/(\(|\s)([12]?\d{3})[\u00A0 ]?(-{1,3}|—) ?([12]?\d{3})(?![\w-°])/g, '$1$2—$4']
      , [/([12]?\d{3}) ?(гг?\.)/g, '$1' + u + '$2']
      , [/(\(|\s)([IVX]{1,5})[\u00A0 ]?(-{1,3}|—) ?([IVX]{1,5})(?![\w-°])/g, '$1$2—$4']
      , [/([IVX]{1,5}) ?(вв?\.)/g, '$1' + u + '$2']

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

        // Пробелы:
      , [/^([#*:]+)[ \t\f\v]*([^ \t\f\v*#:;])/gm, '$1 $2'] // -- в списках.
      , [/(\S) (-{1,3}|—) (\S)/g, '$1' + u + '— $3'] //  тире.
      , [/([А-Я]\.) ?([А-Я]\.) ?([А-Я][а-я])/g, '$1' + u + '$2' + u + '$3'] // -- инициалы.
      , [/([А-Я]\.)([А-Я]\.)/g, '$1 $2'] //  инициалы.
      , [/([а-я]\.)([А-ЯA-Z])/g, '$1 $2'] // -- после точки.
      , [/([)"а-яa-z\]])\s*,([\[("а-яa-z])/g, '$1, $2'] // -- после точки.
      , [/([)"а-яa-z\]])\s([,;])\s([\[("а-яa-z])/g, '$1$2 $3'] // -- после запятой.
      , [/([^%\/\w]\d+?(?:[.,]\d+?)?) ?([%‰])(?!-[А-Яа-яЁё])/g, '$1' + u + '$2'] // -- проценты.
      , [/(\d) ([%‰])(?=-[А-Яа-яЁё])/g, '$1$2'] // -- 5%-й
      , [/([№§])(\s*)(\d)/g, '$1' + u + '$3'] // -- номер и параграф.
      , [/\( +/g, '(']
      , [/ +\)/g, ')'] // -- убрать пробелы у внутренних сторон скобок.

        // Температура:
      , [/([\s\d=≈≠≤≥<>("'|])([+±−-]?\d+?(?:[.,]\d+?)?)(([ °^*]| [°^*])[CС])(?=[\s"').,;!?|])/gm, '$1$2' + u + '°C']
      , [/([\s\d=≈≠≤≥<>("'|])([+±−-]?\d+?(?:[.,]\d+?)?)(([ °^*]| [°^*])F)(?=[\s"').,;|!?])/gm, '$1$2' + u + '°F']

        // Десятичная точка → запятая:
      , [/(\s\d+)\.(\d+[\u00A0 ]*[%‰°])/gi, '$1,$2']

        // Союзы, предлоги, частицы, единицы измерения:
      , [/(^|\s|\()(а|в|во|да|для|до|за|и|из-за|из-под|или|к|ко|на|не|ни|но|о|об|обо|перед|при|с|со|у)\s+/gi, '$1$2' + u] // -- проклитики
      , [/\s+(б|бы|же|ли|ль)(?=$|\s|[,;:.!?)])/gi, u + '$1'] // -- энклитики.
    ]);
    
    // Интерфейс для дополнений к викификатору:
    s = runReplacements (s, window.wfPlugins);

    // Подумать о простых <q></q>:
    // Восстановление кавычек: "" → «»:
    for (var i=1; i<=2; i++) {
        s = s.replace (/([\s\u00A0·\x02!|#'"\/(;+-])"([^"]*)([^\s"(|])"([^a-zа-яё])/ig, '$1«$2$3»$4'); //"
    }
    while (/«[^»]*«/.test(s)) {
        s = s.replace (/«([^»]*)«([^»]*)»/g, '«$1„$2“');
    }

    s = s.substr (1, s.length - 2); // -- удаление начального и конечного пробелов.
    if ('0'.replace('0', '$$') === '$') { // -- $ в регэксах, как всегда, IE особенный.
        for (i = 0; i < hidden.length; i++) {
            hidden [i] = hidden [i].replace (/\$/g, '$$$$');
        }
    }
    // Раскрытие скрытого в hide ():
    while (hidden.length > 0) {
        s = s.replace ('\x01' + hidden.length + '\x02', hidden.pop ());
    }
    
    return s;
    
    // Вспомогательные функции:
    // Выполнение массива замен:
    function runReplacements (s, pairs, log) {
        if (pairs && pairs.length) {
            $.each (pairs, function () {
            	if (log) console.log (this [0] + ': Before: ' + s);
            	s = s.replace (this [0], this [1]);
            	if (log) console.log ('After: ' + s + '\n')
            });
        }
        return s;
    }
    // Скрытие фрагментов путём окружения \x01 и \x02:
    function hide (txt, re) {
        return txt.replace (re, function (s) {return '\x01' + hidden.push (s) + '\x02'});
    }
    function hideTag (txt, tag) {
    	if (tag.join) {
    		tag = tag.join ('|');
    	}
        return hide (txt, RegExp ('<(' + tag + ')( [^>]+)?>[\\s\\S]+?<\\/\\1>', 'gi'));
    }
}	// -- function wikifyText (s)

// Подключение викификатора к панели инструментов «Традиции»:
if (!mw.edit_gadget_extensions) {
	mw.edit_gadget_extensions = [];
}
mw.edit_gadget_extensions.push (function () {
    mw.tools_above [0].splice (1, 0
	  , {w: wikifyText, b: '<img src="/images/1/1f/Etool_wikify.png" width="20" height="16" alt="W"/>', t: 'Викификация', all: true}
      , {url: mw.util.getUrl ('Справка:Викификатор'), b: '<img src="/images/d/d5/Etool_help.png" height="16" width="16" alt="?">', t: '(справка о викификаторе)'}
    );
})