MediaWiki:Gadget-wikifier.js
Перейти к навигации
Перейти к поиску
Замечание: Чтобы после сохранения вступили в силу изменения стилей, перезагрузите файл http://in.wiki/w/load.php?debug=false&lang=ru&modules=site&only=styles&skin=vector&*, если используете скин Vector, или http://in.wiki/w/load.php?debug=false&lang=ru&modules=site&only=styles&skin=common&*, если используете скин Common.
Чтобы вступили в силу изменения скриптов, перезагрузите файл http://in.wiki/w/load.php?debug=false&lang=ru&modules=site&only=scripts&skin=vector&*, если используете скин Vector, или http://in.wiki/w/load.php?debug=false&lang=ru&modules=site&only=scripts&skin=common&*, если используете скин Common.
Гаджеты и·импортируемые скрипты загружаются отдельными файлами.
/*
* Викификатор. Работает в браузере (когда подключат) и на сервере.
*
* '''ВНИМАНИЕ! Внося изменения в код, не забывайте обновлять справку на странице [[Проект:Викификатор]]'''
*/
var wmVersion = '2023-09-08';
var wmCantWork = 'Викификатор не может работать в вашем браузере\n\nWikifier cannot work in your browser';
var wmTalkPage = 'Викификатор не обрабатывает страницы обсуждения целиком.\n\nВыделите своё сообщение — обработано будет только оно';
var isDiscussion = mw && (mw.config.get ('wgNamespaceNumber') % 2 === 1 || mw.config.get ('wgNamespaceNumber') === 4);
// Функция викифицирует переданный текст и возвращает викифицированный:
function wikifyText (s) {
if (isDiscussion) {
// -- это обсуждение.
// Несколько дат, вероятно, чужие подписи::
var sigs = s.match (/\d\d:\d\d, \d\d? \S{3,8} 20\d\d \(UTC\)/g);
if (sigs && sigs.length > 1) {
window.alert (wmTalkPage);
return s;
}
}
// Собственно, викификация:
/*
Следует ограничиться использованием функций hide(), hideTags(), restore(), r(), collect_link(), resolve_links() и internalise_link().
Они будут также распознаны и выполнены в том же порядке серверным викификатором
Module:Wikifier.
Вызовы функций re(), collect_link() и internalise_links() будут выполнены только серверным викификатором,
а в JavaScript они не будут иметь эффекта.
*/
var hidden = [];
var external_links = new Set ();
var resolved = {};
// Скрытие преформатированных тегов и прочих тегов, не подлежащих викификации:
s = hideTags (s, 'nowiki', 'templatedata',
'pre', 'source', 'syntaxhighlight', 'code', 'kbd', 'tt', 'gallery',
'graph', 'svgcode', 'graphviz', 'mscgen', 'score', 'math', 'hiero', 'timeline',
'chem', 'mapframe', 'maplink'
);
s = r (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 = r (s, /<\s*a\s+href\s*=\s*(["'])\s*https?:\/\/in\.wiki?\/(\S+?)\s*\1\s*>(.+?)<\s*\/a\s*>/gi, wikifyInternalLinks);
// -- внутренние <a> → [[]);
s = r (s, /<\s*a\s+href\s*=\s*(["'])(\S+?)\1\s*>(.+?)<\s*\/a\s*>/gi, '[$2 $3]'); // — <a> → [].
s = r (s, /\[\s*(https?:\/\/[^\]\s]+)\s*([^\]]*)\]/gi, collect_link); // — сбор внешних ссылок для последующей замены внутренними.
s = resolve_links (s); // — массовая интернализация ссылок. Выполняется только в Lua.
s = r (s, /\[\s*(https?:\/\/[^\]\s]+)\s*([^\]]*)\]/gi, internalise_link); // — замена внешних ссылок на загруженные документы.
s = hide (s, /(?:https?|ftp|news|nntp|telnet|irc|gopher|magnet):\/\/[^\s\[\]<>"]+ ?/gi); // — гиперссылки.
s = hide (s, /^#(?:redirect|перенапр(авление)?)/i); // — перенаправления.
s = r (s, / +(\n|\r)/g, '$1'); // -- пробелы в конце строки.
// Окружить текст переводами строки, чтобы гарантировать правильную работу
// регулярных выражений в первой и последней строке:
// s = r (s, /^/, '\n');
// s = r (s, /$/, '\n');
// Русификация пространств имён:
s = r (s, /(\[\[:?)(category|категория):( *)/ig, '$1Категория:');
s = r (s, /(\[\[:?)(image|изображение|file):( *)/ig, '$1Файл:');
// Оформление дат:
s = r (s, /(\(|\s)(\[\[[12]?\d{3}\]\])[\u00A0 ]?(-{1,3}|–|—) ?(\[\[[12]?\d{3}\]\])(\W)/g, '$1$2—$4$5');
s = r (s, /(\[\[[12]?\d{3}\]\]) ?(гг?\.)/g, '$1\xa0$2');
s = r (s, /(\(|\s)(\[\[[IVX]{1,5}\]\])[\u00A0 ]?(-{1,3}|–|—) ?(\[\[[IVX]{1,5}\]\])(\W)/g, '$1$2—$4$5');
s = r (s, /(\[\[[IVX]{1,5}\]\]) ?(вв?\.)/g, '$1\xa0$2');
s = r (s, /\[\[(\d+)\]\][\u00A0 ]год/g, '[[$1\xa0год]]');
s = r (s, /\[\[((\d+)(?: (?:год )?в [\wa-яёА-ЯЁ ]+\|\2)?)\]\][\u00A0 ](год[а-яё]*)/g, '[[$1\xa0$3]]');
s = r (s, /\[\[([XVI]+)\]\][\u00A0 ]век/g, '[[$1\xa0век]]');
s = r (s, /\[\[(([XVI]+) век\|\2)\]\][\u00A0 ]век/g, '[[$2\xa0век]]');
// Удаление недопустимых символов из викиссылок:
s = r (s, /(\[\[[^|\[\]]*)[\u00AD\u200E\u200F]+([^\[\]]*\]\])/g, '$1$2'); // -- Soft Hyphen & DirMark.
s = r (s, /\[\[ *([a-zA-Zа-яёА-ЯЁ\u00A0-\u00FF %!\"$&'()*,\-.\/0-9:;=?\\@\^_`’~]+) *\| *(\1)([a-zа-яё]*) *\]\]/g, '[[$2]]$3'); // -- ".
s = r (s, /\[\[ *([a-zA-Zа-яёА-ЯЁ\u00A0-\u00FF %!\"$&'()*,\-.\/0-9:;=?\\@\^_`’~]+) *\| *([^|[\]]+) *\]\]([a-zа-яё]+)/g, '[[$1|$2$3]]'); // -- ".
s = hide (s, /\[\[[^\]|]+/g); // -- скрытие викиссылок.
// Все теги в <> надо обработать здесь:
// HTML -> викитекст:
s = r (s, /<<(\S.+\S)>>/g, '"$1"'); // -- угловые псевдокавычки в обычные. Или лучше в «»?
s = r (s, /(sup>|sub>|\s)-(\d)/g, '$1−$2'); // -- minus в индексах.
s = r (s, /(<sup>2<\/sup>|²)/gi, '²'); // -- символы квадрата
s = r (s, /(<sup>3<\/sup>|³)/gi, '³'); // и куба.
s = r (s, /<(b|strong)>([\s\S]+?)<\/(b|strong)>/gi,"'''$2'''"); // -- вики-полужирный.
s = r (s, /<(i|em)>([\s\S]+?)<\/(i|em)>/gi,"''$2''"); // -- вики-курсив.
s = r (s, /^<hr ?\/?>/gim, '----'); // -- вики-гор. разделитель.
s = r (s, /<\/?(hr|br)( [^\/>]+?)? ?\/?>/gi, '<$1$2 />'); // оформление hr/br по стандарту XML.
s = r (s, /\n*<p(?:\s+align\s*=\s*(["']?)(?:left|justify)\1\s*)?>([\s\S]*?)(<\/p>|(?=\n*<(p|h|pre)))/gi, '\n\n$2');
// -- remove <p>.
// Заголовки:
s = r (s, /\n*<h1(?:[^>]*)>(.+?)<\/h1>\n*/gi, '\n\n= $1 =\n');
s = r (s, /\n*<h2(?:[^>]*)>(.+?)<\/h2>\n*/gi, '\n\n== $1 ==\n');
s = r (s, /\n*<h3(?:[^>]*)>(.+?)<\/h3>\n*/gi, '\n\n=== $1 ===\n');
s = r (s, /\n*<h4(?:[^>]*)>(.+?)<\/h4>\n*/gi, '\n\n==== $1 ====\n');
s = r (s, /\n*<h5(?:[^>]*)>(.+?)<\/h5>\n*/gi, '\n\n===== $1 =====\n');
s = r (s, /\n*<h6(?:[^>]*)>(.+?)<\/h6>\n*/gi, '\n\n====== $1 ======\n');
s = r (s, /(\n==\s*Примечания\s*==\n)<references *\/>/,'$1{{примечания}}');
// Создание сносок:
s = r (s, /(?!\n)([^\[])\[(\d+)\](?!\s*с\.)/g, '$1<ref name="ref$2" />'); // -- сноска вида [1].
s = r (s, /\n\[(\d+)\]\s*([\s\S]+?)(?=\n\[\d+\]|$)/g, '\n<ref name="ref$1">$2</ref>');// -- текст сноски вида [1].
// .,… до сносок -- сдвиг сносок:
s = r (s, /\s*,\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi , '{{,}}$1'); // -- запятая до;
s = r (s, /\s*(…)\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi , '{{,|$1}}$2'); // -- др. знак до;
s = r (s, /\s*\.\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi, '{{тчк}}$1'); // -- точка до;
// .,… после сносок -- перенос до и сдвиг сносок:
s = r (s, /\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)\s*(?:,|\{\{(?:,|зпт)\}\})/gi , '{{,}}$1'); // -- запятая после;
s = r (s, /\s*((?:<ref[^<\/>]*?(?:>[^<]+?<\/ref|\/)>)+)\s*([…])/gi , '{{,|$2}}$1'); // -- др. знак после;
s = r (s, /\s*((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)\s*(?:\.|\{\{(?:,\|\.|тчк)\}\})/gi, '{{тчк}}$1'); // -- точка после;
// !? после сносок -- перенос до:
s = r (s, /\s*((?:<ref[^<\/>]*?(?:>[^<]+?<\/ref|\/)>)+)([!?;:“])/gi , '$2$1'); // -- !? после;
// !?;:“» до сносок -- удаление лишних пробелов:
s = r (s, /\s*([!?;:“»])\s+((<ref[^<\/>]*?(>[^<]+?<\/ref|\/)>)+)/gi , '$1$2'); // -- др. знак до;
// Удаление лишних пробелов и переводов строки внутри сносок:
s = r (s, /(<ref[^\/>]*>)\s+/gi, '$1');
s = r (s, /\s+<\/ref>/gi, '</ref>');
// Создание раздела сносок:
s = r (s, /((?:<ref\sname=".+?">[\s\S]+?<\/ref>)+)\s*$/, '<references>$1</references>\n'); // -- раздел сносок.
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 = r (s, /[ \t]+/g, ' ');
// Заголовки:
s = r (s, /^(=+)[ \t\f\v]*(.*?)[ \t\f\v]*=+$/gm, '$1 $2 $1'); // -- окружить пробелами.
s = r (s, /([^\r\n])(\r?\n==.*==\r?\n)/g, '$1\n$2'); // -- пустую строку впереди.
s = r (s, /^== см(\.?|отрите) ?так\s*же ==$/gmi, '== См. также =='); // -- «См. также».
s = r (s, /^== сноски ==$/gmi, '== Примечания ==');
s = r (s, /^== L ==$/gmi, '== Ссылки ==');
s = r (s, /^== (.*[^.])[.:] ==$/gm, '== $1 =='); // -- точка или двоеточие в конце.
// Любые кавычки → в простые, которые потом будут перерасставлены:
s = r (s, /&((la|ra|bd|ld)quo|quot);/g,'"');
s = r (s, /«|»|“|”|„/g, '"'); // -- временное скрытие нормальных кавычек. Нужно ли?
// Программный код как в github:
// ``` … ``` → <pre>…</pre>:
s = r (s, /\n```((.|\n)+?)\n```/g, '\n<pre>$1</pre>\n');
s = r (s, /`(.+?)`/g, '<code>$1</code>'); // `…` → <code>…</code>
// Тире и дефисы:
s = r (s, /–/g, '-'); // -- – → -
s = r (s, /&(#151|[nm]dash);/g, '—'); // -- мнемоника тире → —
s = r (s, /( |\s)-{1,3} /g, '$1— '); // -- отбитые - → —
s = r (s, /(\d)-{1,2}(\d)/g, '$1‒$2'); // -- -/-- между цифрами → ‒ (ߜ)
s = r (s, /([IVXLCDM]+)-{1,2}([IVXLCDM]+)/g, '$1‒$2'); // -- -/-- между римскими цифрами → ‒ (ߜ)
s = r (s, /(\s)-(\d)/g, '$1−$2'); // -- отбитый - перед цифрой → −
// Мнемоники HTML -> символы:
s = r (s, /&#x([0-9a-f]{1,4});/gi, char); //́
s = r (s, /©/gi,'©');
s = r (s, /®/gi,'®');
s = r (s, /§/gi,'§');
s = r (s, /€/gi,'€');
s = r (s, /¥/gi,'¥');
s = r (s, /£/gi,'£');
s = r (s, /°/g,'°');
s = r (s, /\(tm\)|\(тм\)|™/gi,'™');
// Символы, которых нет на клавиатуре:
s = r (s, /\.\.\.|…/g,'…');
s = r (s, /\+-|±/g,'±');
s = r (s, /~=/g,'≈');
s = r (s, /\^2(\D)/g,'²$1');
s = r (s, /\^3(\D)/g,'³$1');
s = r (s, /\([cс]\)/gi,'©');
s = r (s, /([\wа-яА-ЯёЁ])'([\wа-яА-ЯёЁ])/g,'$1’$2'); //' → типографский апостроф.
s = r (s, /№№/g,'№'); // знаки номера.
// Годы и века
s = r (s, /(\(|\s)([12]?\d{3})[\u00A0 ]?(-{1,3}|—) ?([12]?\d{3})(?![\w-°])/g, '$1$2—$4');
s = r (s, /([12]?\d{3}) ?(гг?\.)/g, '$1\xa0$2');
s = r (s, /(\(|\s)([IVX]{1,5})[\u00A0 ]?(-{1,3}|—) ?([IVX]{1,5})(?![\w-°])/g, '$1$2—$4');
s = r (s, /([IVX]{1,5}) ?(вв?\.)/g, '$1\xa0$2');
// Сокращения:
s = r (s, /(Т|т)\.\s?е\./g, '$1о есть');
s = r (s, /(Т|т)\.\s?к\./g, '$1ак как');
s = r (s, /(В|в)\sт\. ?ч\./g, '$1 том числе');
s = r (s, /и\sт\.\s?д\./g, 'и\xa0т\.\xa0д\.');
s = r (s, /и\sт\.\s?п\./g, 'и\xa0т\.\xa0п\.');
s = r (s, /(Т|т)\.\s?н\./g, '$1\.\xa0н\.');
s = r (s, /н\.\s?э\./g, 'н\.\xa0э\.');
s = r (s, /(Д|д)(о|\.)\sн\.\s?э\./g, '$1о\xa0н\.\xa0э\.');
s = r (s, /(\d)[\u00A0 ]?(тыс\.|млн|млрд|трлн|(?:м|с|д|к)?м|[км]г)\.?(?=[,;.]| "?[а-яё-])/g, '$1\xa0$2');
s = r (s, /(\d)[\u00A0 ](тыс)([^\.А-Яа-яЁё])/g, '$1\xa0$2.$3');
s = r (s, /(\s)кв\.\s*(дм|см|мм|мкм|нм|км|м)(\s)/g, '$1\xA0$2²$3'); // -- квадратные единицы измерения.
s = r (s, /(\s)куб\.\s*(дм|см|мм|мкм|нм|км|м)(\s)/g, '$1\xA0$2³$3'); // -- кубические единицы измерения.
// Пробелы:
s = r (s, /^([#*:]+)[ \t\f\v]*([^ \t\f\v*#:;])/gm, '$1 $2'); // -- в списках.
s = r (s, /(\S) (-{1,3}|—) (\S)/g, '$1\xa0— $3'); // тире.
s = r (s, /([А-Я]\.) ?([А-Я]\.) ?([А-Я][а-я])/g, '$1\xa0$2\xa0$3'); // -- инициалы.
s = r (s, /([А-Я]\.)([А-Я]\.)/g, '$1 $2'); // инициалы.
s = r (s, /([а-я]\.)([А-ЯA-Z])/g, '$1 $2'); // -- после точки.
s = r (s, /([)"а-яa-z\]])\s*,([\[("а-яa-z])/g, '$1, $2'); // -- после точки.
s = r (s, /([)"а-яa-z\]])\s([,;])\s([\[("а-яa-z])/g, '$1$2 $3'); // -- после запятой.
s = r (s, /([^%\/\w]\d+?(?:[.,]\d+?)?) ?([%‰])(?!-[А-Яа-яЁё])/g, '$1\xa0$2'); // -- проценты.
s = r (s, /(\d) ([%‰])(?=-[А-Яа-яЁё])/g, '$1$2'); // -- 5%-й
s = r (s, /([№§])(\s*)(\d)/g, '$1\xa0$3'); // -- номер и параграф.
s = r (s, /\( +/g, '(');
s = r (s, / +\)/g, ')'); // -- убрать пробелы у внутренних сторон скобок.
// Температура:
s = r (s, /([\s\d=≈≠≤≥<>("'|])([+±−-]?\d+?(?:[.,]\d+?)?)(([ °^*]| [°^*])[CС])(?=[\s"').,;!?|])/gm, '$1$2\xa0°C');
s = r (s, /([\s\d=≈≠≤≥<>("'|])([+±−-]?\d+?(?:[.,]\d+?)?)(([ °^*]| [°^*])F)(?=[\s"').,;|!?])/gm, '$1$2\xa0°F');
// Десятичная точка → запятая:
s = r (s, /(\s\d+)\.(\d+[\u00A0 ]*[%‰°])/gi, '$1,$2');
// Союзы, предлоги, частицы:
s = r (s, /(^|\s|\()(а|в|во|да|для|до|за|и|ибо|из|из-за|из-под|или|к|ко|на|над|не|ни|но|о|об|обо|от|перед|по|под|при|с|со|у)\s+/gi, '$1$2\xA0'); // -- проклитики
s = r (s, /\s+(б|бы|ж|же|ли|ль)(?=$|\s|[,;:.!?)])/gi, '\xA0$1'); // -- энклитики.
// Интерфейс для дополнений к викификатору:
var pairs = window.wfPlugins;
if (pairs && pairs.length) {
pairs.forEach (function (pair) {
s = r (s, pair [0], pair [1]);
});
}
// Восстановление кавычек: "" → «»:
// s = restoreQuotes (s);
s = r (s, /([\s\u00A0·\x02!|#'"\/(;+-])"([^"]*)([^\s"(|])"([^a-zа-яё])/ig, '$1<q>$2$3</q>$4'); // -- кавычки, внутри которых нет кавычек.
s = r (s, /([\s\u00A0·\x02!|#'"\/(;+-])"([^"]*)([^\s"(|])"([^a-zа-яё])/ig, '$1<q>$2$3</q>$4'); // -- и ещё раз.
// Удаление начального и конечного пробелов:
s = r (s, /^\n/, '');
s = r (s, /\n$/, '');
// Восстановление скрытого:
s = restore (s);
return s;
// Вспомогательные функции:
// Одна замена:
function r (string, pattern, replacement) {
console.log ('Replacing ' + pattern + ' with ' + replacement);
return string.replace (pattern, replacement);
}
// Скрытие фрагментов путём окружения \x01 и \x02:
function hide (txt, re) {
console.log ('Hiding ' + re);
return txt.replace (re, function (s) {return '\x01' + hidden.push (s) + '\x02'});
}
// Скрытие тегов:
function hideTags (/* variadic; no ES6 spread syntax txt, hidden, ... */) {
var args = Array.prototype.slice.call (arguments);
var txt = args.shift ()
var tags = args.join ('|');
return hide (txt, RegExp ('<(' + tags + ')( [^>]+)?>[\\s\\S]+?<\\/\\1>', 'gi'));
} // -- function hideTags (txt, tags)
// Восстановление скрытого:
function restore (/* String */ s) {
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 restore (/* String */ s)
// Эти функции ничего не делают в браузере, но реализованы в серверном викификаторе:
function re (/* String */ s, /* String */ re) {
return s;
}
function collect_link (_, url, alias) {
console.log ('_ = ' + _ + ', url = ' + url + ', alias = ' + alias);
external_links.add (url);
return '[' + url + ' ' + alias + ']';
}
function resolve_links (/* String */ s) {
var text = s;
if ( external_links.size > 0 ) {
var property = 'URL источника';
var list = Array.from (external_links).join ('||');
var api = new mw.Api ();
api.get ({
action: 'askargs',
conditions: property + '::' + list,
printouts: property,
format: 'json',
parameters: 'limit=' + external_links.size,
api_version: 3
}).done (function (result) {
if ( !result.query || !result.query.results ) return false;
result.query.results.forEach (function (row) {
Object.keys (row).forEach (function (page) {
row [page].printouts [property].forEach (function (url) {
resolved [url] = page;
});
})
});
Object.keys (resolved).forEach (function (url) {
if ( resolved [url] ) {
text = r (text, /\[\s*(https?:\/\/[^\]\s]+)\s*([^\]]*)\]/gi, '[[' + resolved [url] + '|\\2]]');
}
});
}).fail (function (code, result) {
return false;
});
}
return text;
}
function internalise_link (_, url, alias) {
return '[' + url + ' ' + alias + ']';
}
// Превращение ссылок HTML на «Традицию» в викиссылки:
function wikifyInternalLinks (_, __, page, alias) {
return '[[' + decodeURIComponent (page) + '|' + alias + ']]';
}
// Создание символа из кода:
function char (_, a) {
return String.fromCharCode (eval ('0x' + a.substr (-4)))
}
} // -- function wikifyText (s)
// Подключение викификатора к панели инструментов «Традиции»:
if (mw) {
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="/files/1/1f/Etool_wikify.png" width="20" height="16" alt="W"/>', t: 'Викификация', all: true}
, {url: mw.util.getUrl ('Справка:Викификатор'), b: '<img src="/files/d/d5/Etool_help.png" height="16" width="16" alt="?">', t: '(справка о викификаторе)'}
);
});
}