МедияУики:Gadget-Advisor.js и Потребител:Ted Masters/Test 3.js: Разлика между страниците

(Разлики между страниците)
Изтрито е съдържание Добавено е съдържание
м Грешки в статичния код: Остарели HTML-тагове
 
Редакция без резюме
 
Ред 2:
// Виж МедияУики:Gadget-Advisor-core.js за основния скрипт
// виж http://en.wikipedia.org/wiki/User:Cameltrader/Advisor.js/Description
 
importScript('МедияУики:Gadget-Advisor-core.js');
 
var ct = ct || {};
 
ct.inBrackets = function (s, m, brackets) {
var let leftContext = s.substring(0, m.start);
var let rightContext = s.substring(m.end);
 
var let indexOfOpeningLeft = leftContext.lastIndexOf(brackets[0]);
var let indexOfClosingLeft = leftContext.lastIndexOf(brackets[1]);
var let indexOfOpeningRight = rightContext.indexOf(brackets[0]);
var let indexOfClosingRight = rightContext.indexOf(brackets[1]);
 
return (indexOfOpeningLeft !== -1 && (indexOfClosingLeft === -1 || indexOfOpeningLeft > indexOfClosingLeft)) ||
(indexOfClosingRight !== -1 && (indexOfOpeningRight === -1 || indexOfOpeningRight > indexOfClosingRight));
};
 
// originally from https://en.wikipedia.org/wiki/User:GregU/dashes.js
// checkPat1, checkPat2, checkTags, checkFileName default to true
ct.doNotFix = function (s, m, checkPat1, checkPat2, checkTags, checkFileName, checkWikiPreCode, checkQuotes) {
varlet pos = m.start;
var let pat = /\[\[[^|\]]*$|\{\{[^|}]*$|[:\/%][^\s|>]+$|<[^>]*$|#\w*expr:.*$/i;
if (checkPat1 !== false && s.substring(pos - 260, pos + 1).search(pat) >= 0)
return true; // it's a link, so don't change it
 
var let pat2 = /\{\{\s*(?:#[A-Za-z]+:|друг[ои] \s+значени[ея]|основна|main|към|от \s+пренасочване|категория|anchor|cite|citation|цитат2?|c?quote|is[sb]n|lang[i2]?|[es]fn|hrf|harv|пост\s+списък)(?:(?=([^{}]+))\1|\{(?:(?=([^{}]+))\2|\{(?:(?=([^{}]+))\3|\{(?:(?=([^{}]+))\4|\{(?:(?=([^{}]+))\5|\{(?:(?=([^{}]+))\6|\{(?=([^{}]*))\7\})*\})*\})*\})*\})*\})*$/i; // long and ugly regex because of limited JS regex engine
if (checkPat2 !== false && s.substringslice(pos - 2600, pos + 1).search(pat2) >= 0)
return true; // specific template/parser function (with up to six balanced curly brackets -- 3 levels of template/parser function nesting or 2 levels of parameter placeholder nesting)
return true; // likely templates with page-name
 
if (checkTags !== false) {
var let nextTagPos = s.slice(pos).search(/<\/?(chem|math|pre|code|tt|source|syntaxhighlight|timeline|graph|mapframe|maplink|poem|blockquote|q|i|ref)\b/i);
if (nextTagPos >= 0 && s.charAt(pos + nextTagPos + 1) === '/')
return true; // skip math/chem equations, source code, timelines, graphs, maps, ref content, content in citation tags
return true; // don't break a <math> equation, or source code
}
}
 
if (checkFileName !== false && s.slice(pos).search(/^[^|{}[\]<>\n]*\.([a-z]{3,4}\s*([|}\n]|\{\{!\}))/i) >= 0)
return true; // it's a file name parameter
 
if (checkWikiPreCode !== false && s.slice(0, pos + 1).search(/(?:^|\n) +.*$/) >= 0)
return true; // it's a text in wiki pre code (a line starting with a normal space)
 
if (checkQuotes !== false) {
let leftSlice = s.slice(0, pos + 1).replace(/'''/g, '');
let rightSlice = s.slice(pos + 1).replace(/'''/g, '');
if (leftSlice.match(/„(?:(?=([^„“”]+))\1|„[^„“”]*[“”])*$/) &&
rightSlice.match(/^(?:(?=([^„“”]+))\1|„[^„“”]*[“”])*[“”]/) ||
leftSlice.match(/“(?:(?=([^“”]+))\1|“[^“”]*”)*$/) &&
rightSlice.match(/^(?:(?=([^“”]+))\1|“[^“”]*”)*”/) ||
leftSlice.match(/«(?:(?=([^«»]+))\1|«[^«»]*»)*$/) &&
rightSlice.match(/^(?:(?=([^«»]+))\1|«[^«»]*»)*»/) ||
leftSlice.match(/"[^"]*$/) &&
rightSlice.match(/^[^"]*"/) &&
leftSlice.match(/"/g).length % 2 === 1 &&
rightSlice.match(/"/g).length % 2 === 1 ||
leftSlice.match(/''(?:(?!'').)*$/) &&
rightSlice.match(/^.*?''/) &&
leftSlice.match(/''/g).length % 2 === 1 &&
rightSlice.match(/''/g).length % 2 === 1
) return true; // it's a text in quotes or italicized text
}
};
 
if (mw.config.get('wgUserLanguage') === 'bg') {
ct.translation = {
 
'Changing text in wikEd is not yet supported.':
'Променянето на текст в wikEd още не се поддържа.',
Line 88 ⟶ 113:
};
}
 
 
ct.rules = ct.rules || [];
 
ct.rules.push(function (s) {
varlet re = /\[\[([{letter} ,\]?)([^\)|\-[\]]+)\|(['„\1(]*)([{letter}]+)?)\2([{letter}]*)(['“\).:,;]*)\]\]/g;
re = ct.fixRegExp(re);
varlet a = ct.getAllMatches(re, s);
let b = [];
for (varlet i = 0; i < a.length; i++) {
varlet m = a[i];
var ext1m[1] = m[21] === undefined ? '' :|| 'Б';
var ext2m[4] = m[24] === undefined ?|| '' : m[2];
if (m[1].toLowerCase() !== m[4].toLowerCase()) continue;
a[i] = {
m[3] = m[3] || '';
m[5] = m[5] || '';
m[6] = m[6] || '';
b.push({
start: m.start,
end: m.end,
replacement: m[3] + '[[' + m[14] + m[2] + ']]' + ext2m[5] + m[6],
name: 'А|' + (m[3] ? '..' : '') + 'А' + ext1(m[5] ? 'Б' : '') + (m[6] ? '..' : ''),
description: '„[['+ m[1] +'|'+ m[10] + ext2 + ']]“ може да се опрости до „' + m[3] + '[[' + m[14] + m[2] + ']]' + ext2m[5] + m[6] + '“.',
help: 'Синтаксисът на МедияУики позволява препратки от вида „<kbd>[[А|А' + ext1 + '(m[3]]</kbd>“ да? се пишат като „<kbd>[[А]]'...' + ext1 +: '</kbd>“.')
+ 'А' + (m[5] ? 'Б' : '') + (m[6] ? '...' : '') + ']]</kbd>“ да се пишат като „'
};
+ (m[3] ? '..' : '') + '[[А]]' + (m[5] ? 'Б' : '') + (m[6] ? '..' : '') + '“.'
});
}
return ab;
});
 
ct.rules.push(function (s) {
function doNotFixSpaces(str, index) {
var preTagRE = /<\/?pre\b/i;
let var sourceTagREnextTagPos = str.slice(index).search(/<\/?(source|syntaxhighlight|pre)\b/i);
let wikiPre = str.slice(0, index + 1).search(/(?:^|\n) +.*$/);
function doNotFixSpaces(s, index, re) {
return (nextTagPos > -1 && str.charAt(index + nextTagPos + 1) === '/' || wikiPre > -1);
var nextTagPos = s.slice(index).search(re);
}
if (nextTagPos >= 0 && s.charAt(index + nextTagPos + 1) == '/') return true;
return false;
}
 
var let start = -1, end = 0;
let replacement = s.replace(/[^\S\r\n]+$/gm, function (m, index, str) {
var replacement;
// Remove end-of-line spaces
var spacesRemoved1 = 0;
let prev2chars = str.slice(index - 2, index);
var spacesRemoved2 = 0;
// don't rm EOL-space in empty table cells (after |) and in empty template param vals (after =)
 
// but after headings, yes (after ==)
// Remove end-of-line spaces
if (prev2chars[1] === '=' && prev2chars !== '==' || prev2chars[1] === '|') return m;
replacement = s.replace(/ +$/gm, function (m, index, s) {
if (start === -1) start = index;
var prev2chars = s.slice(index - 2, index);
end = index;
// don't rm EOL-space in empty table cells (after |) and in empty template param vals (after =)
return '';
// but after headings, yes (after ==)
}).replace(/[^\s][^\S\r\n]{2,}(?=[^\s=]|==)/g, function (m, index, str) {
if (prev2chars[1] == '|' || ( prev2chars[1] == '=' && prev2chars != '==' )) return m;
// Remove double spaces
if ( doNotFixSpaces(s, index, preTagRE) ) return m;
if (doNotFixSpaces(str, index)) return m;
if (start == -1) start = index;
if (start === -1 || start > index + 1) endstart = index + m.length1;
if (end < index + 2) end = spacesRemoved1index += m.length2;
return m[0] + ' ';
return '';
});
let spacesRemoved = s.length - replacement.length;
 
if (spacesRemoved === 0) return [];
end = end - spacesRemoved1;
return [{
 
start: start,
// Remove double spaces
end: end + spacesRemoved,
replacement = replacement.replace(/([^\s])([ \u00a0]{2,})(?=[^ =]|==)/g, function (m, $1, $2, index, s) {
replacement: replacement.slice(start, end),
var repl;
name: (spacesRemoved === 1 ? 'интервал' : spacesRemoved + ' интервала'),
if ( doNotFixSpaces(s, index, sourceTagRE) || doNotFixSpaces(s, index, preTagRE) ) {
description: 'Изтрий двойните интервали и интервалите в края на редовете',
repl = m;
help: 'Двойните интервали и интервалите в края на редовете са ненужни.'
}
}];
else {
repl = $1 + ' ';
if (start == -1 || start > index + 1) start = index + 1;
if (index + m.length > end) end = index + m.length;
spacesRemoved2 += $2.length - 1;
}
return repl;
});
 
end = end - spacesRemoved2;
 
var spacesRemoved = spacesRemoved1 + spacesRemoved2; // == s.length - replacement.length;
 
if (spacesRemoved === 0) return [];
 
replacement = replacement.slice(start, end);
 
var a = [{
start: start,
end: end + spacesRemoved,
replacement: replacement,
name: (spacesRemoved == 1 ? 'интервал' : spacesRemoved + ' интервала'),
description: 'Изтрий двойните интервали и интервалите в края на редовете',
help: 'Двойните интервали и интервалите в края на редовете са ненужни.'
}];
 
return a;
});
 
ct.rules.push(function (s) {
// [^|] - пропусни ако вероятно е за означаване на празна клетка в таблица
varlet re = /[^|]([ \u00a0]+|&nbsp;)[-\u2014][ \u00a0]+/g;
varlet a = ct.getAllMatches(re, s);
varlet b = [];
for (varlet i = 0,; li =< a.length; i < l; i++) {
varlet m = a[i];
if ( ct.doNotFix(s, m) ) {continue;
continue;
}
b.push({
start: m.start + 1,
Line 202 ⟶ 203:
// не работи при липса на интервал преди цифра защото може да е негативно число
// също не работи преди " и" или " или" за случаи като "антропо- и зооморфна пластика"
varlet re = /[^|]([ \u00a0]+|&nbsp;)-[^\s\d-]|[^-| \u00a0\n]-(\n|[ \u00a0](?!и(?:ли)?[ |или \u00a0]))/g;
varlet a = ct.getAllMatches(re, s);
varlet b = [];
for (varlet i = 0,; li =< a.length; i < l; i++) {
varlet m = a[i];
if ( ct.doNotFix(s, m) ) continue;
b.push({
start: m.start + 1,
Line 221 ⟶ 222:
 
ct.rules.push(function (s) {
varlet re = /[^\d\wАdA-zА-я–-](\d+|\[\[\d+(?:\]\])?)(?:[-|\u2013\u2014]|--|\u2013)(\d+|(?:\[\[)?\d+\]\]|\?|\.{3}|…)[^\d\wАdA-zА-я–-]/g;
// U+2014 is mdash, U+2013 is ndash
relet a = ct.fixRegExpgetAllMatches(re, s);
let b = [];
var a = ct.getAllMatches(re, s);
for (let i = var0; bi =< []a.length; i++) {
let m = a[i];
for (var i = 0; i < a.length; i++) {
if (ct.doNotFix(s, m) || (m[1].length == 3 && m[2].length == 10)) continue; // don't change ISBN-13
var m = a[i];
 
if ( ct.doNotFix(s, m) || (m[1].length == 3 && m[2].length == 10) ) continue; // don't change ISBN-13
 
b.push({
start: m.start + 1,
Line 243 ⟶ 241:
 
ct.rules.push(function (s) {
varlet re = /^(==+)( *{2,})([^=\n]*(?:=[^= \n]+)( *)\1(={2,})$/gm;
varlet a = ct.getAllMatches(re, s);
varlet b = [];
varlet level = 0; // == Level 1 ==, === Level 2 ===, ==== Level 3 ====, etc.
varlet editform = document.getElementById('editform');
// If we are editing a section, we have to be tolerant to the first heading's level
varlet isSection = editform && editform.wpSection !== null && editform.wpSection.value !== '';
for (let i = 0; i < a.length; i++) {
editform['wpSection'] != null &&
let m = a[i];
editform['wpSection'].value != '';
for if (var!m[2] i|| = 0; i < am[2].length;trim() === i++'') {
var m = a[i];
if (m[2] != ' ' || m[4] != ' ') {
b.push({
start: m.start,
end: m.end,
replacement: m[1] + ' ' + m[3] + ' ' + m[1],
name: 'празно заглавие-стил',
description: 'ПоправиПремахни интервалитепразно заглавие',
help: 'Празните заглавия са ненужни.'
help: 'Стилът на заглавието трябва да е <kbd>==&nbsp;С интервали&nbsp;==</kbd>.'
});
} else {
if (!m[2].startsWith(' ') || !m[2].endsWith(' ')) {
var oldLevel = level;
b.push({
level = m[1].length - 1;
start: m.start,
if (level - oldLevel > 1 && (!isSection || oldLevel > 0) ) {
end: m.end,
var h = '======='.substring(0, oldLevel + 2);
replacement: m[1] + ' ' + m[2].trim() + ' ' + m[3],
b.push({
name: 'заглавие-стил',
start: m.start,
description: 'Поправи интервалите',
end: m.end,
help: 'Стилът на заглавието трябва да е <kbd>==&nbsp;С интервали&nbsp;==</kbd>.'
//replacement: h + m[2] + m[3] + m[2] + h,
});
name: 'заглавие-вложеност',
}
description: 'Поправи ръчно неправилната вложеност, провери ръчно и следващите подзаглавия',
let oldLevel = level;
help: 'Всяко заглавие трябва да е вложено точно едно ниво под по-общото заглавие.'
level = m[1].length - 1;
});
if (level - oldLevel > 1 && (!isSection || oldLevel > 0) ||
}
m[1].length > 6 ||
var frequentMistakes = [
m[3].length > 6 ||
// { code: 'външни вр.', wrong: /^[Вв]ъншни *[Вв]ръзки$/i, correct: 'Външни препратки' },
m[1].length !== m[3].length
// { code: 'see-also', wrong: /^see *al+so$/i, correct: 'See also' },
) {
// { code: 'ext-links', wrong: /^external links?$/i, correct: 'External links' },
b.push({
// { code: 'refs', wrong: /^ref+e?r+en(c|s)es?$/i, correct: 'References' }
start: m.start,
];
end: m.end,
for (var j = 0; j < frequentMistakes.length; j++) {
name: 'заглавие-вложеност',
var fm = frequentMistakes[j];
description: 'Поправи ръчно неправилната вложеност, провери ръчно и следващите подзаглавия',
if (fm.wrong.test(m[3]) && m[3] != fm.correct) {
help: 'Всяко заглавие трябва да е вложено точно едно ниво под по-общото заглавие.'
var r = m[1] + m[2] + fm.correct + m[2] + m[1];
if (r != m[0]}) {;
b.push({
start: m.start,
end: m.end,
replacement: r,
name: fm.code,
description: 'Поправи на „' + fm.correct + "“.",
help: 'Правилното изписване е „<kbd>' + fm.correct + "</kbd>“."
});
}
}
// let frequentMistakes = [
// { code: 'външни вр.', wrong: /^[Вв]ъншни *[Вв]ръзки$/i, correct: 'Външни препратки' },
// { code: 'see-also', wrong: /^see *al+so$/i, correct: 'See also' },
// { code: 'ext-links', wrong: /^external links?$/i, correct: 'External links' },
// { code: 'refs', wrong: /^ref+e?r+en(c|s)es?$/i, correct: 'References' }
// ];
// for (let j = 0; j < frequentMistakes.length; j++) {
// let fm = frequentMistakes[j];
// if (fm.wrong.test(m[3]) && m[3] != fm.correct) {
// let r = m[1] + m[2] + fm.correct + m[2] + m[1];
// if (r != m[0]) {
// b.push({
// start: m.start,
// end: m.end,
// replacement: r,
// name: fm.code,
// description: 'Поправи на „' + fm.correct + "“.",
// help: 'Правилното изписване е „<kbd>' + fm.correct + "</kbd>“."
// });
// }
// }
// }
}
}
Line 305 ⟶ 314:
ct.rules.push(function (s) {
// ISBN: ten or thirteen digits, each digit optionally followed by a hyphen, the last digit can be 'X' or 'x'
varlet a = ct.getAllMatches(/ISBN *=? *(([0-9Xx]-?)+)/gi, s);
varlet b = [];
for (varlet i = 0; i < a.length; i++) {
varlet m = a[i];
varlet sstr = m[1].replace(/[^0-9Xx]+/g, '').toUpperCase(); // remove all non-digits
if (sstr.length !== 10 && sstr.length !== 13) {
b.push({
start: m.start,
Line 317 ⟶ 326:
description: 'Трябва да е дълъг 10 или 13 цифри',
help: 'ISBN номерата трябва да са дълги 10 или 13 цифри. '
+ 'Този се състои от ' + sstr.length + ' цифри:<br><kbd>' + m[1] + '</kbd>'
});
continue;
}
varlet isNew = (sstr.length === 13); // old (10 digits) or new (13 digits)
varlet xIndex = sstr.indexOf('X');
if (xIndex !== -1 && (xIndex !== 9 || isNew)) {
b.push({
Line 334 ⟶ 343:
continue;
}
varlet computedChecksum = 0;
varlet modulus = isNew ? 10 : 11;
for (varlet j = sstr.length - 2; j >= 0; j--) {
varlet digit = sstr.charCodeAt(j) - 48; // 48 is the ASCII code of '0'
varlet quotient = isNew
? ((j & 1) ? 3 : 1) // the new way: 1 for even, 3 for odd
: 10 - j; // the old way: 10, 9, 8, etc
computedChecksum = (computedChecksum + (quotient * digit)) % modulus;
}
computedChecksum = (modulus - computedChecksum) % modulus;
varlet c = sstr.charCodeAt(sstr.length - 1) - 48;
varlet actualChecksum = (c < 0 || 9 < c) ? 10 : c;
if (computedChecksum === actualChecksum) {continue;
continue;
}
b.push({
start: m.start,
Line 361 ⟶ 368:
 
ct.rules.push(function (s) {
varlet re = / й[^А-яяA-z\wd]/g;
relet a = ct.fixRegExpgetAllMatches(re, s);
let b = [];
var a = ct.getAllMatches(re, s);
for (varlet i = 0; i < a.length; i++) {
varlet m = a[i];
if ( ct.doNotFix(s, m) ) continue;
a[i] = b.push({
start: m.start + 1,
end: m.end - 1,
Line 375 ⟶ 382:
help: 'Когато се ползва като местоимение, „й“ трябва да се изписва '
+ 'като „ѝ“ с ударение.'
});
}
return ab;
});
 
ct.rules.push(function (s) {
// год.единица, предшестванопредшествана от цифри, евентуално оградени с [[ ив ]]препратка
varlet re = /(\[\[[0-9яA-z\d](\d+(?:\]\]|[0-9]+)?)((?:[ \u00a0]+|&nbsp;)+)?(г(?:од)?\u0433.|лв\u043e.|щ\u0434\.|(?:[мк]?г|[мск]?м|[mk]?g|[mck]?m)(?![А-яA-z\d]))/g;
let autofix = ['г.', 'лв.', 'щ.д.'];
var a = ct.getAllMatches(re, s);
let a = ct.getAllMatches(re, s);
for (var i = 0; i < a.length; i++) {
var let mb = a[i];
for (let i = 0; i < a.length; i++) {
if ( ct.doNotFix(s, m) ) continue;
let m = a[i] = {;
if (ct.doNotFix(s, m)) continue;
start: m.start,
let number = m[1];
end: m.end,
replacement:let spacing = m[12] +|| '\u00a0г.',;
name:let unit = m[3] === 'год.→г' ? 'г.', : m[3];
let autofixThis = $.inArray(unit, autofix) > -1;
description: 'год.→г.',
if (m[3] === 'год.' ||
help: 'Приетото съкращение за година е „г.“, а не „год.“'
(autofixThis && spacing !== ' ' && spacing !== '\u00a0' && spacing !== '&nbsp;') ||
};
(!autofixThis && spacing === '')
}
) {
return a;
});
 
ct.rules.push(function (s) {
var re = /(\[\[[0-9]+\]\]|[0-9]+)( +|&nbsp;)?(г\.|лв\.|щ\.д\.|(мг|кг|мм|см|км|mg|kg|mm|cm|km|m|м|g|г)(?![\w\dА-я]))/g;
var autofix = ['г.', 'лв.', 'щ.д.'];
var a = ct.getAllMatches(re, s);
var b = [];
for (var i = 0; i < a.length; i++) {
var m = a[i];
if ( ct.doNotFix(s, m) ) continue;
var number = m[1]; // може да е оградено с [[ и ]]
var spacing = m[2] || '';
var unit = m[3];
var autofixThis = $.inArray(unit, autofix) > -1;
if ( ( autofixThis && spacing !== ' ' ) || ( !autofixThis && spacing == '' ) ) {
b.push({
start: m.start + 1,
end: m.end,
replacement: ( autofixThis ? number + '\u00a0' + unit : undefined ),
name: 'число+' + unit,
description: 'Добави интервал между числото и единицата ' + unit,
help: 'Между числочислото и единицата <i>' + unit + '</i> трябва да се оставя един интервал, '
+ 'за предпочитане непренасящият се <kbd>&amp;nbsp;</kbd> '
+ '(non-breaking space, <kbd>U+00A0</kbd>).'
Line 428 ⟶ 420:
 
ct.rules.push(function (s) {
var let re = /(([А-я{letter}\d])[\]\)“']+*)( , ?:[^\S\r\n]+,[^\S\r\n]*|,)(?=[А-я\[\(„']*([{letter}\d]))/g;
re = ct.fixRegExp(re);
var let a = ct.getAllMatches(re, s);
var let b = [];
for (varlet i = 0; i < a.length; i++) {
var let m = a[i];
let m1 = { 'start': m.start + m[1].length, 'end': m.end - m[3].length };
if (ct.inBrackets(s, m, ['[', ']']) || ct.inBrackets(s, m, ['{', '}'])) {
if (ct.doNotFix(s, m, false, false, true, true, true, false) ||
continue;
ct.inBrackets(s, m1, ['[', ']']) ||
}
ct.inBrackets(s, m1, ['{', '}']) ||
b.push({
!isNaN(m[2]) && !isNaN(m[4]) // m[2] и m[4] са цифри; вероятност за десетично число
start: m.start + m[1].length,
) continue;
end: m.end,
b.push({
replacement: m[2].trim() + ' ',
start: m1.start,
name: 'запетая',
end: m1.end,
description: 'Премахни интервала преди запетаята и/или добави такъв след нея',
replacement: ', ',
help: 'Интервалът трябва да е след запетаята и не преди нея.'
name: 'запетая',
});
description: 'Премахни интервала преди запетаята и/или добави такъв след нея',
}
help: 'Интервалът трябва да е след запетаята и не преди нея.'
return b;
});
}
return b;
});
 
ct.rules.push(function (s) {
var let re = /( ([а-я]{2,letter}\d])[\]\)“']*)( ?:[^\S\r\n]+\. ?[^\S\r\n]*|\.)(?=[А-Я\[\(„']*([а-я{letter}\d]+))/g;
re = ct.fixRegExp(re);
var let a = ct.getAllMatches(re, s);
var let b = [];
for (varlet i = 0; i < a.length; i++) {
var let m = a[i];
let m1 = { 'start': m.start + m[1].length, 'end': m.end - m[3].length };
if (ct.inBrackets(s, m, ['[', ']']) || ct.inBrackets(s, m, ['{', '}'])) {
if (ct.doNotFix(s, m, false, false, true, true, true, false) ||
continue;
ct.inBrackets(s, m1, ['[', ']']) ||
}
ct.inBrackets(s, m1, ['{', '}']) ||
b.push({
!isNaN(m[2]) && !isNaN(m[4]) || // m[2] и m[4] са цифри; вероятност за десетично число след копиране
start: m.start + m[1].length,
m[4] !== m[4].toUpperCase() // m[4] не е главна буква
end: m.end,
) continue;
replacement: m[2].trim() + ' ',
b.push({
name: 'точка',
start: m1.start,
description: 'Премахни интервала преди точката в края на изречението и/или добави такъв след нея',
end: m1.end,
help: 'Интервалът трябва да е след точката и не преди нея.'
replacement: '. ',
});
name: 'точка',
}
description: 'Премахни интервала преди точката в края на изречението и/или добави такъв след нея',
return b;
help: 'Интервалът трябва да е след точката и не преди нея.'
});
}
return b;
});
 
ct.rules.push(function (s) {
var let re = /((=\n{2,}.)|[^=\n]\n=|.\n{3,}.|\.\n[А-я])|^\n+/g;
let rea = ct.fixRegExpgetAllMatches(re, s);
for (let i = var0; ai =< cta.getAllMatches(re,length; si++); {
let m = a[i];
for (var i = 0; i < a.length; i++) {
let lines = m[2] ? '\n' : '\n\n';
var m = a[i];
a[i] = {
var lines = (m[2] === undefined) ? '\n\n' : '\n';
start: m.start,
a[i] = {
end: m.end,
start: m.start,
replacement: m[1] ? m[1][0] + lines + m[1][m.end - m.start - 1] : '',
end: m.end,
name: 'нов ред',
replacement: m[1][0] + lines + m[1][m.end - m.start - 1],
description: 'Премахни излишните празни редове или добави нов ред между отделните абзаци',
name: 'нов ред',
help: 'Между отделните абзаци трябва да има един празен ред. Повече от един празен ред е излишен.'
description: 'Премахни излишните празни редове или добави нов ред между отделните абзаци',
};
help: 'Между отделните абзаци трябва да има един празен ред. Повече от един празен ред е излишен.'
}
};
return a;
}
return a;
});
 
ct.rules.push(function (s) {
function replace(latin, cyrillic, str, obj) {
var re = /(([А-я] e [А-я])|[a-z][А-я]|[А-я][a-z])/g;
if (str.indexOf(latin) > -1) {
re = ct.fixRegExp(re);
obj.replacement = str.replace(latin, cyrillic);
var a = ct.getAllMatches(re, s);
obj.description = 'Замени латинско "' + latin + '" с кирилско.';
for (var i = 0; i < a.length; i++) {
}
var m = a[i];
}
a[i] = {
 
start: m.start,
let re = /[А-я] [ec] [А-я]|[a-z][А-я]|[А-я][a-z]/g;
end: m.end,
let a = ct.getAllMatches(re, s);
replacement: null,
for (let i = 0; i < a.length; i++) {
name: '6lokavica',
let m = a[i];
description: 'Неизвестна замяна. Проверете текста.',
a[i] = {
help: 'Една дума трябва да бъде написана или само на кирилица или само на латиница.'
start: m.start,
};
end: m.end,
replacement: null,
function replace(latin, cyrillic) {
name: '6lokavica',
a[i].replacement = m[1].replace(latin, cyrillic);
description: 'Неизвестна замяна. Проверете текста.',
a[i].description = 'Замени латинско "' + latin + '" с кирилско.';
help: 'Една дума трябва да бъде написана или само на кирилица или само на латиница.'
}
};
if (m[2] !== undefined) replace('ea', 'еа', m[0], a[i]);
replace('e', 'е', m[0], a[i]);
else {
replace('o', 'о', m[0], a[i]);
if (m[1].indexOf('a') > -1) replace('a', 'а');
replace('x', 'х', m[0], a[i]);
else if (m[1].indexOf('e') > -1) replace('e', 'е');
replace('p', 'р', m[0], a[i]);
else if (m[1].indexOf('o') > -1) replace('o', 'о');
replace('c', 'с', m[0], a[i]);
else if (m[1].indexOf('x') > -1) replace('x', 'х');
}
else if (m[1].indexOf('p') > -1) replace('p', 'р');
return a;
else if (m[1].indexOf('c') > -1) replace('c', 'с');
}
}
return a;
});
 
ct.rules.push(function (s) {
// отварящи кавички ако са в нач. на реда или в списъчен елемент, след '', интервали (непредхождани от единично =), ==, *, #, >, }, (|, [, «(
let re = /((?:^|\n)[*#:;]*|''|[^=\s][^\S\n]+|==[^\S\n]*|[>}|\(])(?:"(?![\s.,;])((?:[^"\[\]\s]|\s(?!")|\[\[[^\[\]]+\]\]|\[[^\[\]]+\])+)"|[„“](?![\s.,;])((?:[^„“”\[\]\s]|\s(?![“”])|\[\[[^\[\]]+\]\]|\[[^\[\]]+\])+)”|«(?!\s)((?:[^«»\[\]\s]|\s(?!»)|\[\[[^\[\]]+\]\]|\[[^\[\]]+\])+)»)/g;
// за "" (но не и за “”) също работи когато има "„“ кавички в адреса на връзка вътре в кавички
let a = ct.getAllMatches(re, s);
var re = /(\n|''|[^= ] +|== *|[*#>}|(\[«])(?:"(?![\s.,;])((?:[^"„“\[]|\[\[[^|\]"„“]+\]|\[\[[^\]|]+\|)*[^"„“\s(\[«])"|[“„](?![\s.,;])([^"„“]*[^"„“\s(\[«])”|[«](?![\s.,;])([^"„“«»]*[^"„“\s(\[«»])»)/g;
let b = [];
var a = ct.getAllMatches(re, s);
for (let i = 0; i < a.length; i++) {
var b = [];
let m = a[i];
for (var i = 0, l = a.length; i < l; i++) {
varlet mstr = am[2] || m[3] || m[i4];
if ( ct.doNotFix(s, m), true, true, true, true, true, false) {||
str.search(/[а-ъюяѝ]|ь(?=о)/i) === -1 || // не съдържа нито една кирилска буква, ползвана в българския
continue;
str.search(/(?![а-ъюяѝ]|ь(?=о))[\u0400-\u04ff]/i) > -1 // или пък съдържа и други кирилски букви, които не се ползват в българския
}
) continue;
b.push({
start: m.start + m[1].length,
end: m.end,
replacement: '„' + (m[2] || m[3] || m[4])str + '“',
name: 'кавички',
description: 'Заместване на "прави", “други горни”, „смесени” или «френски» с „български“ кавички.',
Line 546 ⟶ 542:
});
 
// Премахването на празни параметри от шаблоните като практика не се ползва с
/*
// консенсусна подкрепа сред редакторите, затова на този етап е изключено.
Премахването на празни параметри от шаблоните като практика не се ползва с
консенсусна подкрепа сред редакторите, затова на този етап е изключено.
*/
/*
ct.rules.push(function (s) {
var let re = /(^|[^\n ] *)(\| *[\wА-я-]+ *= *(?=[\|\}]))+/g;
var let a = ct.getAllMatches(re, s);
if (a.length === 0) return [];
var let n = a.length;
var let start = a[0].start + a[0][1].length;
var let end = a[n - 1].end + 1;
let replacement = s.slice(start, end).replace(re, '$1');
 
let b = [{
var replacement = s.slice(start, end).replace(re, '$1');
start: start,
 
end: end - 1,
var b = [{
replacement: replacement.slice(0, -1),
start: start,
name: (n == 1 ? 'параметър' : n + '+ параметъра'),
end: end - 1,
description: 'Премахва неизползваните параметри от шаблоните',
replacement: replacement.slice(0, -1),
help: 'Неизползваните параметри са излишни.'
name: (n == 1 ? 'параметър' : n + '+ параметъра'),
}];
description: 'Премахва неизползваните параметри от шаблоните',
return b;
help: 'Неизползваните параметри са излишни.'
}];
return b;
});
*/
 
ct.rules.push(function (s) {
var let skipNext = 0;
var let decoder = function (match, charCode, index, s) {
if (skipNext > 0) {
skipNext--;
return '';
}
}
let decimal = parseInt(charCode, 16);
let bin = Number(decimal).toString(2);
if (decimal < 128) return match; // ASCII, don't decode
let nOfBytes = bin.match(/^1+/)[0].length;
skipNext = nOfBytes - 1;
let urlEncoded = match + s.slice(index + 3, index + 3 * nOfBytes);
let char = decodeURI(urlEncoded);
return (char.length == 1 ? char : urlEncoded);
};
 
let re = /(https?:\/\/[^\/\s]+\/)([^\s|}<>\]]*)/g;
var decimal = parseInt(charCode, 16);
let a = ct.getAllMatches(re, s);
var bin = Number(decimal).toString(2);
let b = [];
if ( decimal < 128 ) return match; // ASCII, don't decode
let decoded;
 
for (let i = 0; i < a.length; i++) {
var nOfBytes = bin.match(/^1+/)[0].length;
let m = a[i];
skipNext = nOfBytes - 1;
try {
 
decoded = m[2].replace(/%([A-F\d]{2})/gi, decoder);
var urlEncoded = match + s.slice(index + 3, index + 3 * nOfBytes);
if (m[2] === decoded) continue;
b.push({
start: m.start,
end: m.end,
replacement: m[1] + decoded,
name: 'URL',
description: 'Декодира кодирани URL адреси',
help: 'URL адресите се четат по-лесно когато са декодирани.'
});
} catch (e) {
// не е кодиран Unicode текст
}
}
return b;
});
 
ct.rules.push(function (s) {
var char = decodeURI(urlEncoded);
let re = /\([^\S\r\n]+|[^\S\r\n]+\)/g;
return (char.length == 1 ? char : urlEncoded);
let a = ct.getAllMatches(re, s);
}
for (let i = 0; i < a.length; i++) {
 
let m = a[i];
var re = /(https?:\/\/[^\/ ]+\/)(((?![ \n\|\]\}><]).)*)/g;
if (ct.doNotFix(s, m)) continue;
var a = ct.getAllMatches(re, s);
a[i] = {
var b = [];
start: m.start,
var decoded;
end: m.end,
for (var i = 0; i < a.length; i++) {
replacement: m[0].trim(),
var m = a[i];
name: 'скоба',
try {
description: 'Премахни интервала след отварящата и/или преди затварящата скоба',
decoded = m[2].replace(/%([0-9A-Fa-f]{2})/g, decoder);
help: 'Интервалите са ненужни след отваряща и преди затваряща скоба.'
if (m[2] === decoded) continue;
};
b.push({
}
start: m.start,
return a;
end: m.end,
replacement: m[1] + decoded,
name: 'URL',
description: 'Декодира кодирани URL адреси',
help: 'URL адресите се четат по-лесно когато са декодирани.'
});
}
catch (e) {
// не е кодиран Unicode текст
}
}
return b;
});
 
ct.rules.push(function (s) {
let re = /((?:[^А-яA-z\d](?:[Оо]т|[Дд]о|[Пп]ре(?:з|ди)|[Сс]лед|[Мм]е(?:жду|сец)|[Ии](?:ли)?|[Вв])|[\d,])[ \u00a0])(Януари|Февруари|Март|Април|Май|Юни|Юли|Август|Септември|Октомври|Ноември|Декември)[^А-яA-z\d]/g;
var re = /([А-я“"]+)(\(|\)| ?\( | \) ?)(?=[А-я\d]+)/g;
let rea = ct.fixRegExpgetAllMatches(re, s);
let b = [];
var a = ct.getAllMatches(re, s);
for (varlet i = 0; i < a.length; i++) {
var let m = a[i];
if ( ct.doNotFix(s, m) ) continue;
b.push({
a[i] = {
start: m.start + m[1].length,
end: m.end - 1,
replacement: m[2].toLowerCase(),
// replacement: m[2].indexOf('(') != -1 ? ' (' : ') ', // чупи параметри в Шаблон:ТВ продукция
name: 'скобамесец',
description: m[2] + ' → ' + m[2].toLowerCase(),
description: 'Добави/премахни интервала преди/след отварящата/затварящата скоба',
help: 'В българския език имената на месеците се пишат с малка буква.'
help: 'Преди отваряща и след затваряща скоба трябва да има интервал. Интервалите са ненужни след отваряща и преди затваряща скоба.'
});
};
}
}
return ab;
});
 
ct.rules.push(function (s) {
let re = /№(\d+)/g;
var re = /[0-9] (Януари|Февруари|Март|Април|Май|Юни|Юли|Август|Септември|Октомври|Ноември|Декември)[^А-я]/g;
var let a = ct.getAllMatches(re, s);
var m, replacement, let b = [];
for (varlet i = 0; i < a.length; i++) {
let m = a[i];
if ( ct.doNotFix(s, m) ) continue;
b.push({
replacement = m[1][0].toLowerCase() + m[1].slice(1);
start: m.start,
b.push({
end: m.end,
start: m.start + 2,
replacement: '№\u00a0' + m[1],
end: m.end - 1,
name: '№+число',
replacement: replacement,
description: 'Добави несекаем интервал между № и последващите числа',
name: 'месец',
help: 'Между символа за номер и последващите числа трябва да има несекаем интервал.'
description: m[1] + ' -> ' + replacement,
});
help: 'В българския език имената на месеците се пишат с малка буква.'
}
});
return b;
}
return b;
});
 
ct.rules.push(function (s) {
window.ct = ct;
function skipReferenceTags(str, index) {
let sliced = str.slice(0, index);
let tag1Pos = sliced.search(/<references\b(?:(?=([^/>]+))\1|\/(?!>))*>(?:(?=([^<]+))\2|<(?!\/references\b))*$/i);
let tag2Pos = sliced.search(/\{\{\s*[Rr]eflist\b(?:(?=([^{}]+))\1|\{(?:(?=([^{}]+))\2|\{(?:(?=([^{}]+))\3|\{(?:(?=([^{}]+))\4|\{(?:(?=([^{}]+))\5|\{(?:(?=([^{}]+))\6|\{(?=([^{}]*))\7\})*\})*\})*\})*\})*\})*$/);
return (tag1Pos > -1 || tag2Pos > -1);
}
 
let start = -1, end = 0;
if ($.inArray(mw.config.get('wgCanonicalNamespace'), ['MediaWiki', 'Template', 'Module']) === -1) {
let noChange = true;
mw.loader.using( 'ext.gadget.Advisor', function () {
let replacement = s.replace(/(<\/[Rr][Ee][Ff]\s*>|<[Rr][Ee][Ff]\b[^>]*\/>|\{\{\s*(?:[SsEe]fn|[Hh]rf|[Rr]p)\b[^{}]*\}\})[\s.:,;]+(?=<[Rr][Ee][Ff]\b|\{\{\s*(?:[SsEe]fn|[Hh]rf|[Rr]p)\b)/g, function (m, g1, index, str) {
mw.loader.load('//bg.wikipedia.org/w/index.php?title=МедияУики:Gadget-Advisor-core.js&action=raw&ctype=text/javascript');
// Remove punctuation chars and spaces between refs
if (skipReferenceTags(str, index)) return m;
if (start === -1) start = index;
end = index + g1.length;
noChange = false;
return g1;
}).replace(/([^\s.:,;=]\s+|\s*[.,]\s+|[^\S\r\n]*[:;][^\S\r\n]+)(?:<[Rr][Ee][Ff]\b|\{\{\s*(?:[SsEe]fn|[Hh]rf|[Rr]p)\b)|(?:<\/[Rr][Ee][Ff]\s*>|<[Rr][Ee][Ff]\b[^>]*\/>|\{\{\s*(?:[SsEe]fn|[Hh]rf|[Rr]p)\b[^{}]*\}\})(?:\s+(?=[.,])|[^\S\r\n]+(?=[:;]))/g, function (m, g1, index, str) {
// Remove spaces before ref/after ref and between punctuation chars
if (skipReferenceTags(str, index)) return m;
if (g1) m = g1.trim() + m.slice(g1.length);
m = m.trim();
if (start === -1 || start > index) start = index;
if (end < index + m.length) end = index + m.length;
noChange = false;
return m;
}).replace(/([.:,;])((?:<[Rr][Ee][Ff]\b(?:(?=([^>/]+))\3|\/(?!>))*(?:\/|>(?:(?=([^<]+))\4|<(?!\/?[Rr][Ee][Ff]\b))*<\/[Rr][Ee][Ff]\s*)>|\{\{(?:[SsEe]fn|[Hh]rf|[Rr]p)\b[^{}]*\}\})+)\1/g, function (m, g1, g2, g3, g4, index, str) {
// Remove last punctuation char if equal at start/end position for a group of refs
if (skipReferenceTags(str, index)) return m;
if (start === -1 || start > index) start = index;
if (end < index + (g1 + g2).length) end = index + (g1 + g2).length;
noChange = false;
return g1 + g2;
}).replace(ct.fixRegExp(/(?:<\/[Rr][Ee][Ff]\s*>|<[Rr][Ee][Ff]\b[^>]*\/>|\{\{(?:[SsEe]fn|[Hh]rf|[Rr]p)\b[^{}]*\}\})[.:,;]?(?=[^{letter}\d\s<>{}.:,;]*[{letter}\d])/g), function (m, index, str) {
// Add space if closing ref is followed by a word, only if the word is not positioned after opening/closing angular/curly bracket
if (skipReferenceTags(str, index)) return m;
if (start === -1 || start > index) start = index;
if (end < index + m.length + 1) end = index + m.length + 1;
noChange = false;
return m + ' ';
});
if (noChange) return [];
}
let deltaLength = s.length - replacement.length;
return [{
start: start,
end: end + deltaLength,
replacement: replacement.slice(start, end),
name: 'пунктуация+ref',
description: 'Интервалите и/или част от пунктуационните знаци преди, между и след ref-овете са ненужни',
help: 'Изтрий ненужните пунктуационни знаци и интервали преди, между и след ref-овете.'
}];
});
 
ct.rules.push(function (s) {
let re = ct.fixRegExp(/(^|\s+)([Вв](?:ъв)?|[Сс](?:ъс)?)(\s+(?:[^{letter}\d\s\[\]{}<>-]*\[\[?[^\[\]]+\]|(?:[^\s\[\]{}<>]+\s+){1,2}))/g);
let a = ct.getAllMatches(re, s);
let b = [];
for (let i = 0; i < a.length; i++) {
let m = a[i];
if (ct.doNotFix(s, m)) continue;
let tooltip;
let preposition = m[2];
let text = m[3].replace(/\[(?:(?:https?:|ftps?:)?\/\/\S+|\[[^|\[\]]+\|)/gi, '')
.replace(ct.fixRegExp(/[^{letter}\d\s-]+/g), '')
.replace(/^\s+/, '');
if (preposition.toLowerCase() === 'в' && text.match(/^(?:[ВвФф][А-Яа-я]|2-р|II(?![А-яA-z\d])|и(?:ли)?\s+)/)) {
preposition += 'ъв';
tooltip = '„в“ трябва да стане „във“, тъй като следващата дума започва с „в“ или „ф“, или попада под логическо ударение.';
}
else if (preposition.toLowerCase() === 'във' && !text.match(/^(?:[ВвФфVvFfWw]|2-р|II(?![А-яA-z\d])|и(?:ли)?\s+)/)) {
preposition = preposition[0];
tooltip = '„във“ трябва да стане „в“, тъй като следващата дума не започва с „в“ или „ф“, или не попада под логическо ударение.';
}
else if (preposition.toLowerCase() === 'с' && text.match(/^(?:[СсЗз][А-Яа-я]|(?:1?7(?:\d|\s*\d{3})?|[17]\d\d)(?!\d)|(?:X?VII|LXX[IVX]*|(?:DC)?C[IVXL]*)(?![А-яA-z\d])|и(?:ли)?\s+без\s+)/)) {
preposition += 'ъс';
tooltip = '„с“ трябва да стане „със“, тъй като следващата дума започва със „з“ или „с“, или попада под логическо ударение.';
}
else if (preposition.toLowerCase() === 'със' && !text.match(/^(?:[СсЗзSsZzCc]|(?:1?7(?:\d|\s*\d{3})?|[17]\d\d)(?!\d)|(?:X?VII|LXX[IVX]*|DCC[IVXL]*)(?![А-яA-z\d])|и(?:ли)?\s+без\s+)/)) {
preposition = preposition[0];
tooltip = '„със“ трябва да стане „с“, тъй като следващата дума не започва със „з“ или „с“, или не попада под логическо ударение.';
}
if (tooltip) {
b.push({
start: m.start + m[1].length,
end: m.end - m[3].length,
replacement: preposition,
name: m[2].toLowerCase() + '→' + preposition.toLowerCase(),
description: 'Поправи „' + m[2] + '“ на „' + preposition + '“',
help: tooltip
});
}
}
return b;
});