User:Kiril kovachev/common.js
Appearance
Note: You may have to bypass your browser’s cache to see the changes. In addition, after saving a sitewide CSS file such as MediaWiki:Common.css, it will take 5-10 minutes before the changes take effect, even if you clear your cache.
- Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
- Konqueror and Chrome: click Reload or press F5;
- Opera: clear the cache in Tools → Preferences;
- Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.
- This user script lacks a documentation subpage. You may create it.
- Useful links: root page • root page’s subpages • links • redirects • your own
This JavaScript is executed for Kiril kovachev on every page load.
// <nowiki>
/* TODO:
- Rework auto-insertion templates to work only on an existing Bulgarian section
- Make reference template insert any missing references, even if a reference section already exists
- Auto-add references / format page?
- Add auto-detection of common {{affix}}-based etymologies
- Fix shortcuts for derived/related terms
- Override the bold shortcut from the editor
- Fix the relational adjective auto-detection (broken)
- Fix the BER fetching (broken?)
- Make some kind of system for auto-creating adjective forms on click (via KovachevBot)
- Make more motions / shortcuts that take me to defined places
-- The nearest definition line
- Copy over related terms from another article using a command
- Allocate more shortcuts and unbind my browser ones
- Tangential: finish and use bg-pr
- Add EditorReplaceAll to permit multiple regex subs
*/
// Constants pertaining to references
const CURRENT_BER_PAGE = 7;
const CURRENT_BER_VOLUME = 1;
const CURRENT_BTR_PAGE = 23;
const GRAVE = String.fromCodePoint(0x300);
const ACUTE = String.fromCodePoint(0x301);
// ------------------PURE FUNCTIONS TO GENERATE PAGE DATA BEGIN HERE--------------------------------
// {{der}}, {{inh}}, {{bor}}, {{der+}}, {{inh+}}, and {{bor+}} templates
const der = (s, w) => derivationTemplateGeneric("der", s, w);
const inh = (s, w) => derivationTemplateGeneric("inh", s, w);
const bor = (s, w) => derivationTemplateGeneric("bor", s, w);
const derP = (s, w) => derivationTemplateGeneric("der+", s, w);
const inhP = (s, w) => derivationTemplateGeneric("inh+", s, w);
const borP = (s, w) => derivationTemplateGeneric("bor+", s, w);
function referencesTemplate(berPageName) {
return "\n===References===\n* {{R:bg:RBE}}\n* {{R:bg:RBE2}}\n* {{R:bg:BER|" + berPageName + "|" + CURRENT_BER_PAGE + "|" + CURRENT_BER_VOLUME + "}}\n* {{R:bg:BTR|page=" + CURRENT_BTR_PAGE + "}}\n";
}
function bulgarianNounTemplate(stressed, gender) {
return (
`==Bulgarian==
===Etymology===
From
${pronunciationTemplate(stressed)}
===Noun===
{{bg-noun|${stressed}|${gender}}}
# definitions
====Declension====
{{bg-ndecl|${stressed}<>}}
`);
}
function bulgarianVerbTemplate(stressed, aspect) {
return (
`==Bulgarian==
===Etymology===
From
${pronunciationTemplate(stressed)}
===Verb===
{{bg-verb|${stressed}|${aspect}}}
# definitions
====Conjugation====
{{bg-conj|${stressed}<>}}
`);
}
function bulgarianAdjectiveTemplate(stressed) {
return (
`==Bulgarian==
===Etymology===
From
${pronunciationTemplate(stressed)}
===Adjective===
{{bg-adj|${stressed}}}
# definitions
====Declension====
{{bg-adecl|${stressed}<>}}
`);
}
function bulgarianAdverbTemplate(stressed) {
return (
`==Bulgarian==
===Etymology===
From
${pronunciationTemplate(stressed)}
===Adverb===
{{bg-adv|${stressed}}}
# definitions
`);
}
function pronunciationTemplate(stressed) {
return (`===Pronunciation===
* {{bg-IPA|${stressed}}}
* {{bg-hyph}}`);
}
// Depending on what source languages we've seen so far,
// predict what language should be suggested next.
/* Based on this order:
Russian
French
Latin
Ancient Greek
*/
function predictNextEtymologyLanguage(metSoFar) {
if (!metSoFar.includes("ru")) return "ru";
if (!metSoFar.includes("fr")) return "fr";
if (!metSoFar.includes("la")) return "la";
if (!metSoFar.includes("grc")) return "grc";
}
function getExistingEtymologyLanguages(page) {
return [...page.matchAll(/{{(?:der|inh|bor)\+?\|bg\|(\w{2,3}).*?}}/g)].map((m)=>m[1]);
}
function predictRelationalDeclension(relationalAdj) {
if (relationalAdj.endsWith("ен")) {
return "!*";
} else if (relationalAdj.endsWith("ов")) {
return "!";
}
return "";
}
// Return format:
// array of objects, each of which has a property
// "lemma", which is a Bulgarian word/morpheme,
// "t" (gloss), i.e. its meaning,
// and "pos", part-of-speech or morphological information
function predictRelationalForm(relationalAdj) {
if (relationalAdj.endsWith("и́чен")) {
const ichenPos = relationalAdj.indexOf("и́чен");
return [
{
lemma: relationalAdj.slice(0, ichenPos),
t: undefined, // May perform scraping of Wiktionary for this in future
pos: undefined
},
{
lemma: "-и́чен",
t: undefined,
pos: "adjectival suffix"
}
];
} else if (relationalAdj.endsWith("и́чески")) {
const icheskiPos = relationalAdj.indexOf("и́чески");
return [
{
lemma: relationalAdj.slice(0, icheskiPos),
t: undefined, // May perform scraping of Wiktionary for this in future
pos: undefined
},
{
lemma: "-и́чески",
t: undefined,
pos: "adjectival suffix"
}
];
} else if (relationalAdj.endsWith("ен")) {
const enPos = relationalAdj.indexOf("ен");
return [
{
lemma: relationalAdj.slice(0, enPos),
t: undefined, // May perform scraping of Wiktionary for this in future
pos: undefined
},
{
lemma: "-ен",
t: undefined,
pos: "adjectival suffix"
}
];
} else if (relationalAdj.endsWith("ски")) {
const skiPos = relationalAdj.indexOf("ски");
return [
{
lemma: relationalAdj.slice(0, skiPos),
t: undefined, // May perform scraping of Wiktionary for this in future
pos: undefined
},
{
lemma: "-ски",
t: undefined,
pos: "adjectival suffix"
}
];
}
return null;
}
const masculineSuffixes = [
"ар", "ач", "тел", "ин", "ик",
"ец", "еж",
];
const feminineSuffixes = [
"ица", "ка", "ня", "ба",
"ина", "ост", "ота"
];
const neuterSuffixes = [
"ище", "ство", "ние",
"не", "ие", "че", "ле",
"це"
];
function detectGender(title) {
// Use some very basic logic to generally assume masculine, barring known exceptions
if (endsInAnyOf(title, masculineSuffixes)) {
return "m";
} else if (endsInAnyOf(title, feminineSuffixes)) {
return "f";
} else if (endsInAnyOf(title, neuterSuffixes)) {
return "n";
} else {
return detectGenderByEndingPhonemes(title);
}
}
function detectGenderByEndingPhonemes(title) {
if (endsInAnyOf(title, ["а", "я", "ст", "щ"])) {
return "f";
} else if (endsInAnyOf(title, ["о", "е", "и", "у", "ю"])) {
return "n";
} else {
return "m";
}
}
function endsInAnyOf(string, suffixesToTest) {
for (let suffix of suffixesToTest) {
if (string.endsWith(suffix)) {
return true;
}
}
return false;
}
function startsWithAnyOf(string, prefixesToTest) {
for (let prefix of prefixesToTest) {
if (string.startsWith(prefix)) {
return true;
}
}
return false;
}
function linkTemplate(term) {
return `{{l|bg|${term}}}`;
}
function alterTemplate(term) {
return `{{alter|bg|${term}}}`;
}
function generateAffixFromForm(form) {
let out = [];
out.push("affix");
out.push("bg");
for (let i = 0; i < form.length; i++) {
out.push(form[i].lemma);
}
for (let i = 0; i < form.length; i++) {
if (form[i].t) {
out.push(`t${i+1}=${form[i].t}`);
}
if (form[i].pos) {
out.push(`pos${i+1}=${form[i].pos}`);
}
}
return "{{" + out.join("|") + "}}";
}
function getSection(source, level, sectionName) {
const sectionPattern = new RegExp("^" + "=".repeat(level) + sectionName + "=".repeat(level) + "$", "gm");
const insubordinatePattern = new RegExp(`^={2,${level}}.+={2,${level}}$`, "gm");
const sectionStart = sectionPattern.exec(source).index;
insubordinatePattern.lastIndex = sectionStart + 1;
const m = insubordinatePattern.exec(source);
const sectionEnd = m && m.index || -1;
return source.slice(sectionStart, sectionEnd);
}
function calculateAdditions(stateBefore, stateAfter) {
function diffHas(pattern) {
return stateBefore.search(pattern) === -1 && stateAfter.search(pattern) !== -1;
}
const additions = [];
for (const [pattern, name] of [
["===Etymology===", "etymology"],
["===Pronunciation===", "pronunciation"],
["===References===", "references"],
["===Alternative forms===", "alternative forms"],
["====Related terms====", "related terms"],
["====Derived terms====", "derived terms"],
["{{syn|bg", "synonyms"],
["{{ant|bg", "antonyms"],
["{{cot|bg", "coordinate terms"],
["{{audio|bg|", "audio"],
]) {
if (diffHas(pattern)) {
additions.push(name);
}
}
return additions;
}
function detectPartsOfSpeechAdded(state) {
const added = [];
for (const pos of ["Noun", "Adjective", "Verb", "Adverb"]) {
if (state.includes(`===${pos}===`)) {
added.push(post.toLowerCase());
}
}
return added;
}
function calculateEditSummary(stateBefore, stateAfter) {
if (IsNewEntry()) {
const added = detectPartsOfSpeechAdded(stateAfter);
if (added.length > 0) {
return `Created Bulgarian ${added.join(" + ")}`;
} else {
return null;
}
} else {
const added = calculateAdditions(stateBefore, stateAfter);
if (added.length > 0) {
return `Added ${added.join(", ")}`;
} else {
return null;
}
}
}
// ------------------- EDITOR-ENABLED FUNCTIONS BEGIN HERE ------------------------------------------------------------
function GetLemma(editor) {
const IPATemplate = /\{\{bg-IPA\|(.+?)\}\}/;
const pageNameMatch = IPATemplate.exec(editor.get());
if (pageNameMatch) {
// If e.g. IPA template has the stressed lemma inside, retrieve it
return pageNameMatch[1];
} else {
// Just return the unstressed name for now
return GetPageName(editor);
}
}
function GetPageName(editor) {
return document.getElementById("firstHeadingTitle").innerText;
}
function GetBulgarian(editor) {
const BulgarianSection = /==Bulgarian==\n[\s\S]+?(?=(==)[^=]+\1(?=\n))|==Bulgarian==\n[\s\S]+$/;
return (BulgarianSection.exec(editor.get()) || [null])[0];
}
function GetTextArea() {
return document.getElementById("wpTextbox1");
}
function IsNewEntry() {
return window.location.href.includes("redlink=1");
}
function FindBgSectionNum(editor) {
const sectionPattern = /(=)(.+)\1/;
const sections = [...editor.get().matchAll(/(=+)([^=]+)\1(?=\n)/g).map(m => m[2])];
return sections.indexOf("Bulgarian");
}
function EditBgOnly(editor) {
if (!IsNewEntry() && !window.location.href.includes("section=")) {
const bgSectionNum = FindBgSectionNum(editor);
if (bgSectionNum === -1)
return;
window.location.href = `https://en.wiktionary.org/w/index.php?title=${GetPageName()}&action=edit§ion=${bgSectionNum+1}`;
}
}
function Find() {
const pattern = new RegExp(prompt("Search: "));
HighlightText(pattern);
}
// Given a string, if the string exists in the editor, it will be highlighted.
// Return the regex match or undefined.
function HighlightText(regex) {
const textArea = GetTextArea();
const match = regex.exec(textArea.value);
if (!match) return;
const selectionStart = match.index;
const text = match[0];
textArea.focus();
textArea.setSelectionRange(selectionStart, selectionStart + text.length);
return match;
}
// Assuming text is select, replace that selection with the input.
// If the cursor is just floating with no selection, then it will input the text wherever
// the cursor is.
function ReplaceText(text) {
GetTextArea().focus();
document.execCommand('insertText', false, text); // FIXME: DEPRECATED!
// But, there is no replacement for execCommand at this stage (Dec 2024).
}
// Given a regex pattern or string, select the text and replace it with the replacement.
function EditorReplace(search, replacement, removeLookaround=false) {
let regex = search instanceof RegExp ? search : new RegExp(search);
const match = HighlightText(regex);
// Remove RegExp lookaround, which is necessary because we can't "select" the entire
// text which triggered the match in HighlightText. The alternative is to just
// perform the replacement on the entire editor, but that seems less wise to me.
// Well, this is also rather hacky...
if (removeLookaround) {
const regexStr = regex.toString().slice(1,-1);
const regexStrFixed = regexStr.replace(/\(\?<?[=!].+?\)/, "");
regex = new RegExp(regexStrFixed);
}
const out = match[0].replace(regex, replacement); // Expand the regex patterns
ReplaceText(out);
}
function EditorSet(text) {
HighlightText(/[\s\S]*/);
ReplaceText(text);
}
function EditorAppend(text) {
const textarea = GetTextArea();
const current = textarea.value;
textarea.setSelectionRange(current.length, current.length);
ReplaceText(text);
}
// Given a string, move the cursor after that string in the editor
function MoveCursorAfter(regex) {
const textArea = GetTextArea();
const match = regex.exec(textArea.value);
if (!match) return;
const text = match[0];
const selectionStart = match.index + text.length;
textArea.focus();
textArea.setSelectionRange(selectionStart, selectionStart);
}
function ReleaseSelection() {
const textArea = GetTextArea();
const start = textArea.selectionStart;
textArea.setSelectionRange(start, start);
}
// Same as above, but move the cursor to the end of the current selection instead of the start
function ReleaseSelectionAfter() {
const textArea = GetTextArea();
const end = textArea.selectionEnd;
textArea.setSelectionRange(end, end);
}
function PortOverMeaning(editor, wiktPage) {
getWiktionary(wiktPage).then((resp) => resp.json().then((json)=> {
console.log(json);
const src = json.parse.wikitext;
const bulgarianDefsSrc = /==Bulgarian==[\s\S]+?#[\s\S]+?\n\n/.exec(src)[0];
const bulgarianDefs = [...bulgarianDefsSrc.matchAll(/# .+/g)].map((m) => m[0]);
if (bulgarianDefs.length == 1) {
EditorReplace("# {{lb|bg|relational}} {{rfdef|bg}}", "# {{lb|bg|relational}} " + bulgarianDefs[0].slice(2));
} else {
EditorReplace(/({{bg-adj.+?}})\n\n# {{lb\|bg\|relational}} {{rfdef\|bg}}/, "$1 {{tlb|bg|relational}}\n\n" + bulgarianDefs.join("\n"));
}
}));
}
function InsertSortedL2(editor, content) {
const page = editor.get();
const allL2s = [...page.matchAll(/^==(.+)==$/gm)];
if (allL2s.length == 0) {
EditorAppend(content);
return;
}
let i = 0;
while (i < allL2s.length && allL2s[i][1] < "Bulgarian") {
i++;
}
if (i == allL2s.length) {
EditorAppend("\n");
EditorAppend(content);
} else {
let index = page.indexOf(allL2s[i][0]);
const inserted = page.slice(0, index) + content + "\n" + page.slice(index);
EditorSet(inserted);
}
}
// ---------------------INTERACTIVE FUNCTIONS BEGIN HERE----------------------
// Returns an array of responses
function PromptRepeatedly(promptStr) {
let out = [];
let answer;
do {
answer = prompt(promptStr);
out.push(answer);
} while (answer);
return out.slice(0, -1);
}
// Give in an array of prompts, an array of corresponding defaults for those prompts,
// and get back an array of all the answers to each prompt in sequence
function PromptMany(prompts, defaults) {
let out = [];
for (let i = 0; i < prompts.length; i++) {
const response = prompt(prompts[i], defaults[i]);
if (response) {
out.push(response);
} else {
throw new Error("Prompt quit early");
}
}
return out;
}
function CreateTitle(level, title) {
return `${'='.repeat(level)}${title}${'='.repeat(level)}`;
}
// Gets a sequence of user input words, and makes a list of
// {{l}} (link templates) to them, e.g.
// * {{l|bg|едно}}
// * {{l|bg|две}}
// * {{l|bg|три}}
function CreateLinkSection(title, promptText, template=linkTemplate, level=4) {
const terms = PromptRepeatedly(promptText);
const sectionTitle = CreateTitle(level, title);
return `${sectionTitle}
${ terms.map(
(term) => "* " + template(term)
).join("\n")}`;
}
// ----------------------- SCRAPING STUFF BEGIN HERE ---------------------
// Synchronous web request might be better IMO as the data is needed
// immediately for the prompt, e.g. when getting the stressed lemma from
// Chitanka.
function getRequestJSON(url) {
return JSON.stringify({
"kovachev_url": url,
"kovachev_password": localStorage.getItem("password") || ""
});
}
const CORS_URL = "https://cors.kovachev.xyz/";
const CONTENT_TYPE = "application/json;charset=UTF-8";
function fetchSynchronous(url) {
const request = new XMLHttpRequest();
request.open("POST", CORS_URL, false); // `false` makes the request synchronous
request.setRequestHeader("Content-Type", CONTENT_TYPE);
request.send(getRequestJSON(url));
if (request.status === 200) {
return request.responseText;
} else {
return null;
}
}
function fetchProxy(url) {
return fetch(CORS_URL, {
method: "post",
headers: {
"Content-Type": CONTENT_TYPE
},
body: getRequestJSON(url)
});
}
// Return null if it isn't found
function getRBE(word) {
return fetchSynchronous(`https://rbe.chitanka.info/?q=${word}`);
}
function getChitanka(word) {
return fetchSynchronous(`https://rechnik.chitanka.info/w/${word}`);
}
function getWiktionary(word) {
return fetch(`https://en.wiktionary.org/w/api.php?action=parse&formatversion=2&page=${word}&prop=wikitext&format=json`);
}
function getRBEAsync(word) {
return fetchProxy(`https://rbe.chitanka.info/?q=${word}`);
}
function getChitankaAsync(word) {
return fetchProxy(`https://rechnik.chitanka.info/w/${word}`);
}
// Given a word, get the version with stress mark according to Chitanka
function getChitankaStress(word) {
const chitankaText = getChitanka(word);
if (!chitankaText) return;
const m = /<span id="\w+-stressed_.+">\s*(.+)\s*<\/span>/.exec(chitankaText.replace("̀", ACUTE));
return (m && m[1]) || undefined;
}
// ------------------TEMPLATESCRIPT SCRIPT FUNCTIONS BEGIN HERE -----------------------
function References(editor) {
if (editor.get().includes("===References===")) {
return;
}
const pageName = GetLemma(editor);
const berPageName = pageName.replace(ACUTE, GRAVE);
const outputText = referencesTemplate(berPageName);
// Source: https://github.com/doozan/wikibot/blob/46b8275a156cce8aef061018dde70d16c6b07e94/fix_es_forms.py#L1127
// by JeffDoozan
// const catTemplates = [ "c", "C", "cat", "top", "topic", "topics", "categorize", "catlangname", "catlangcode", "cln", "DEFAULTSORT" ];
// const reTemplates = "\\{\\{\s*(" + catTemplates.join("|") + ")\\s*[|}][^{}]*\\}*";
// const reCategories = "\\[\\[\\s*Category\\s*:[^\\]]*\\]\\]";
// const categoryPattern = new RegExp(`(?s)(.*?\\n)((\\s*(${reTemplates}|${reCategories})\\s*)*)$`);
const categoryPattern = /\n((?:\s*(?:\{\{\s*(?:c|C|cat|top|topic|topics|categorize|catlangname|catlangcode|cln|DEFAULTSORT)\s*[|}][^{}]*\}*|\[\[\s*Category\s*:[^\]]*\]\])\s*)*)$/;
if (editor.get().includes("===Anagrams===")) {
const AnagramsSection = RegExp("\n(===Anagrams===\n.+\n)", 'g');
EditorReplace(AnagramsSection, `${outputText}\n$1`);
} else if (categoryPattern.exec(editor.get()) /*includes category syntax*/) {
EditorReplace(categoryPattern, `\n${outputText}$1`);
}
else {
EditorAppend(outputText);
}
// editor.setEditSummary("Added references");
}
function BulgarianNoun(editor) {
const title = GetPageName(editor);
const [stressed, gender] = PromptMany(
["Please enter the lemma: ",
"Please enter the gender: "
],
[getChitankaStress(title) || title,
detectGender(title)
]
);
InsertSortedL2(editor, bulgarianNounTemplate(stressed, gender));
References(editor);
HighlightText(/definitions/);
}
function BulgarianVerb(editor) {
const title = GetPageName(editor);
const [stressed, aspect] = PromptMany(
["Please enter the lemma: ",
"Please enter the aspect: "
],
[getChitankaStress(title) || title,
"impf"
]
);
InsertSortedL2(editor, bulgarianVerbTemplate(stressed, aspect));
References(editor);
HighlightText(/definitions/);
}
function BulgarianAdjective(editor) {
const title = GetPageName(editor);
const stressed = PromptMany(["Please enter the lemma: "], [getChitankaStress(title) || title]);
InsertSortedL2(editor, bulgarianAdjectiveTemplate(stressed));
References(editor);
HighlightText(/definitions/);
}
function BulgarianAdverb(editor) {
const title = GetPageName(editor);
const stressed = PromptMany(["Please enter the lemma: "], [getChitankaStress(title) || title]);
InsertSortedL2(editor, bulgarianAdverbTemplate(stressed));
References(editor);
HighlightText(/definitions/);
}
// Note: not used right now.
// Use when specifically editing a Spanish entry, currently works only if it has an L3 Etymology header. I'll figure out the JavaScript for this later.
function EsPr(editor) {
if (editor.get().includes("Pronunciation")) { return; }
const EtymologySection = RegExp("(===Etymology===\n.+)\n\n", 'g');
EditorReplace(
EtymologySection,
"$1\n\n===Pronunciation===\n{{es-pr}}\n\n"
);
// .setEditSummary("Add pronunciation");
}
function BgEtymology(editor) {
if (!editor.get().includes("Etymology")) {
const BulgarianSection = /(==Bulgarian==\n(?:\{\{(?:wikipedia|wp)\|.+?\}\}\n)?)/;
EditorReplace(
BulgarianSection,
"$1\n===Etymology===\nFrom \n"
);
// .setEditSummary("Added etymology");
}
// Move to edit the default etymology stub
MoveCursorAfter(/(?<====Etymology===\n)[^\n]*(?=\s*===)/);
}
// Derivation function takes a source language code and a word and produces
// an etymology template like {{der|bg|grc|...}}
function DerivationGeneric(editor, derivationFunction, plus=false) {
const predictedLanguage = predictNextEtymologyLanguage(getExistingEtymologyLanguages(editor.get()));
const [sourceLanguage, sourceWord] = PromptMany(
["Enter source language: ",
"Enter etymon: "
],
[predictedLanguage,
(predictedLanguage === "ru") ? GetLemma(editor) : undefined
]
);
const derivationOutput = derivationFunction(sourceLanguage, sourceWord);
const BareEtymology = /(?<====Etymology===\n)From /;
if (plus && editor.contains(BareEtymology)) {
console.log("Bare etymology found");
EditorReplace(BareEtymology, derivationOutput, removeLookaround=true);
} else {
ReplaceText(derivationOutput);
}
}
function derivationTemplateGeneric(templateName, sourceLanguage, sourceWord) {
return `{{${templateName}|bg|${sourceLanguage}|${sourceWord}}}`;
}
const Derived = (editor) => DerivationGeneric(editor, der);
const Borrowed = (editor) => DerivationGeneric(editor, bor);
const Inherited = (editor) => DerivationGeneric(editor, inh);
const DerivedPlus = (editor) => DerivationGeneric(editor, derP, true);
const BorrowedPlus = (editor) => DerivationGeneric(editor, borP, true);
const InheritedPlus = (editor) => DerivationGeneric(editor, inhP, true);
function predictAffixElements(lemma) {
return null;
}
function autoGlossCommonAffixes(affixes) {
// No-op (WIP)
return affixes;
}
function AddAffix(editor) {
const predictedAffixElements = predictAffixElements(GetLemma(editor));
let affixElements;
if (predictedAffixElements === null) {
// Get affix elements from the user directly
const userAffixElements = PromptRepeatedly("Enter prompt elements: ");
const autoGlossed = userAffixElements.map(autoGlossCommonAffixes);
affixElements = autoGlossed;
} else {
affixElements = predictedAffixElements;
}
const affixTemplateStr = `{{affix|bg|${affixElements.join('|')}}}`;
ReplaceText(affixTemplateStr);
}
function AddDerivedTerms(editor) {
if (!editor.get().includes("Derived terms")) {
const derivedTermsSection = CreateLinkSection("Derived terms", "Please enter derived term: ");
const Declension = RegExp("(====Declension====\n.+}}\n)", 'g');
EditorReplace(
Declension,
"$1\n" + derivedTermsSection + "\n"
);
// .setEditSummary("Added derived terms");
}
}
function AddRelatedTerms(editor) {
if (!editor.get().includes("Related terms")) {
const relatedTermsSection = CreateLinkSection("Related terms", "Please enter related term: ");
const References = RegExp("(===References===)", 'g');
EditorReplace(
References,
relatedTermsSection + "\n\n$1"
);
// .setEditSummary("Added derived terms");
}
}
function AddAlternativeForms(editor) {
if (!editor.get().includes("Alternative forms")) {
const alternativeFormsSection = CreateLinkSection("Alternative forms", "Please enter alternative form: ", template=alterTemplate, level=3);
const Etymology = RegExp("(===Etymology===\n.+\n)", 'g');
EditorReplace(
Etymology,
alternativeFormsSection + "\n\n$1"
);
// .setEditSummary("Added derived terms");
}
}
// When creating an accelerated entry for a relational adjective,
// this template will handle filling in as much as possible for the
// usual forms.
function AutocompleteRelationalAdj(editor) {
EditorReplace(/ *<!--.*-->/g, ""); // Remove comments
const lemma = GetLemma(editor);
const declSpec = predictRelationalDeclension(lemma);
EditorReplace(/<.*>/, `<${declSpec}>`); // Predict declension spec
const form = predictRelationalForm(lemma);
if (form) {
EditorReplace("{{rfe|bg}}", `From ${generateAffixFromForm(form)}.`);
PortOverMeaning(editor, form[0].lemma.replace(ACUTE, "")); // Copy the definitions over from the original Wiktionary page
}
}
function PurgeReferences(editor) {
const title = GetPageName(editor);
if (startsWithAnyOf(title, ["а", "б", "в", "г", "д", "е", "н", "о", "п"])) {
getRBEAsync(title).then((resp) => {
if (!resp.ok) {
EditorReplace("* {{R:bg:RBE}}\n", "");
}
});
}
getChitankaAsync(title).then((resp) => {
if (!resp.ok) {
EditorReplace("* {{R:bg:RBE2}}\n", "");
}
});
}
// -------------- TEMPLATESCRIPT TEMPLATE OBJECTS BEGIN HERE ----------------------
const EsPrTemplate = {
name: "Add es-pr",
isMinorEdit: false,
enabled: false,
category: "One-click edits",
script: EsPr
};
const BgReferencesTemplate = {
name: "Add references",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: References,
};
const BgEtymologyTemplate = {
name: "Add etymology",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: BgEtymology
};
const BulgarianNounTemplate = {
name: "Bulgarian noun",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
editSummary: "Create Bulgarian noun",
script: BulgarianNoun
};
const BulgarianVerbTemplate = {
name: "Bulgarian verb",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
editSummary: "Create Bulgarian verb",
script: BulgarianVerb
};
const BulgarianAdjectiveTemplate = {
name: "Bulgarian adjective",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
editSummary: "Create Bulgarian adjective",
script: BulgarianAdjective
};
const BulgarianAdverbTemplate = {
name: "Bulgarian adverb",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
editSummary: "Create Bulgarian adverb",
script: BulgarianAdverb
};
const DerTemplate = {
name: "Add {{der}} template",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: Derived,
};
const BorTemplate = {
name: "Add {{bor}} template",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: Borrowed,
};
const InhTemplate = {
name: "Add {{inh}} template",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: Inherited,
};
const DerPlusTemplate = {
name: "Add {{der+}} template",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: DerivedPlus,
};
const BorPlusTemplate = {
name: "Add {{bor+}} template",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: BorrowedPlus,
};
const InhPlusTemplate = {
name: "Add {{inh+}} template",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: InheritedPlus,
};
const AddAffixTemplate = {
name: "Add {{affix}} template",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: AddAffix,
};
const DerivedTermsTemplate = {
name: "Add derived terms",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: AddDerivedTerms,
};
const RelatedTermsTemplate = {
name: "Add related terms",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: AddRelatedTerms,
};
const AlternativeFormsTemplate = {
name: "Add alternative forms",
isMinorEdit: false,
enabled: true,
category: "Fill-in edits",
script: AddAlternativeForms,
};
const AutocompleteRelationalAdjTemplate = {
name: "Autodetect relational adjective",
isMinorEdit: false,
enabled: true,
category: "One-click edits",
script: AutocompleteRelationalAdj,
editSummary: "Create relational adjective"
};
const PurgeReferencesTemplate = {
name: "Purge dead references",
isMinorEdit: false,
enabled: true,
category: "One-click edits",
script: PurgeReferences
};
// -----------------------MAIN DRIVER CODE BEGINS HERE-------------------------------------------
// List of template objects to add to the menu
const TEMPLATES = [
EsPrTemplate,
BulgarianNounTemplate,
BulgarianVerbTemplate,
BulgarianAdjectiveTemplate,
BulgarianAdverbTemplate,
BgReferencesTemplate,
BgEtymologyTemplate,
AddAffixTemplate,
DerTemplate,
DerPlusTemplate,
BorTemplate,
BorPlusTemplate,
InhTemplate,
InhPlusTemplate,
DerivedTermsTemplate,
RelatedTermsTemplate,
AlternativeFormsTemplate,
AutocompleteRelationalAdjTemplate,
PurgeReferencesTemplate
];
function applyTemplate(template) {
template(pathoschild.TemplateScript.Context);
}
let editorStateBefore;
mw.config.set('userjs-templatescript', { regexEditor: false });
$.ajax("//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js",
{
dataType: "script",
cache: true
})
.then(function() {
// ----------------TEMPLATESCRIPT HACKS AND POLYFILLS INTERCEDE HERE-----------------------------
// (These should go here to ensure that the rest of the templates and so on
// definitely have access to the extra functions I'm defining)
const editor = pathoschild.TemplateScript.Context;
editorStateBefore = editor.get();
// ------------------------- DRIVER CODE RESUMES -------------------------------
pathoschild.TemplateScript.add(TEMPLATES);
function customEditorKeybinds(event) {
console.log(event);
if (event.ctrlKey) {
if (event.key == "F1") {
applyTemplate(BulgarianNoun);
}
if (event.key == "F2") {
applyTemplate(BulgarianVerb);
}
if (event.key == "F3") {
applyTemplate(BulgarianAdjective);
}
if (event.key == "e") {
applyTemplate(BgEtymology);
event.preventDefault();
return false;
}
if (event.key == ",") {
// Edit the declension spec
HighlightText(/(?<=<)[^<>\s]*(?=>)/);
event.preventDefault();
return false;
}
if (event.key == "r") {
applyTemplate(References);
event.preventDefault();
return false;
}
if (event.key == "A") {
applyTemplate(AddAffix);
}
if (event.key == "d") {
applyTemplate(Derived);
}
if (event.key == "i") {
applyTemplate(Inherited);
}
if (event.key == "b") {
applyTemplate(Borrowed);
event.preventDefault();
return false;
}
if (event.key == "D") {
applyTemplate(DerivedPlus);
}
if (event.key == "I") {
applyTemplate(InheritedPlus);
}
if (event.key == "B") {
applyTemplate(BorrowedPlus);
}
if (event.key == "m") {
applyTemplate(AddDerivedTerms);
event.preventDefault();
}
if (event.key == "M") {
applyTemplate(AddRelatedTerms);
event.preventDefault();
}
if (event.key == ".") {
applyTemplate(AutocompleteRelationalAdj);
}
if (event.key == "'") {
applyTemplate(AddAlternativeForms);
}
if (event.key == "Escape") {
ReleaseSelectionAfter();
return;
}
if (event.key == ";") {
applyTemplate(EditBgOnly);
}
if (event.key == "f") {
Find();
event.preventDefault();
return false;
}
}
if (event.key == "Escape") {
ReleaseSelection();
}
}
document.addEventListener("keydown", customEditorKeybinds, false);
document.getElementById("wpSave").addEventListener("mouseover", function(){
const editorStateAfter = editor.get();
const editMessage = calculateEditSummary(editorStateBefore, editorStateAfter);
if (editMessage !== null) {
editor.setEditSummary(editMessage);
}
});
});
// </nowiki>