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 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?
*/
// Constants pertaining to references
const CURRENT_BER_PAGE = 7;
const CURRENT_BER_VOLUME = 1;
const CURRENT_BTR_PAGE = 22;
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 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 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);
}
// ------------------- 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 GetTextArea() {
return document.getElementById("wpTextbox1");
}
// Given a string, if the string exists in the editor, it will be highlighted.
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);
}
// 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) {
editor.replace("# {{lb|bg|relational}} {{rfdef|bg}}", "# {{lb|bg|relational}} " + bulgarianDefs[0].slice(2));
} else {
editor.replace(/({{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) {
editor.append(content);
return;
}
let i = 0;
while (i < allL2s.length && allL2s[i][1] < "Bulgarian") {
i++;
}
if (i == allL2s.length) {
editor.append("\n");
editor.append(content);
} else {
let index = page.indexOf(allL2s[i][0]);
const inserted = page.slice(0, index) + content + "\n" + page.slice(index);
editor.set(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;
}
// 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) {
const terms = PromptRepeatedly(promptText);
return `====${title}====
${ terms.map(
(term) => "* " + linkTemplate(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 fetchSynchronous(url) {
const request = new XMLHttpRequest();
request.open("GET", "https://corsproxy.io/?" + url, false); // `false` makes the request synchronous
request.send(null);
if (request.status === 200) {
return request.responseText;
} else {
return null;
}
}
function fetchProxy(url) {
return fetch(`https://corsproxy.io/?${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);
editor.append(referencesTemplate(berPageName));
// 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/);
}
// 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');
editor
.replace(
EtymologySection,
"$1\n\n===Pronunciation===\n{{es-pr}}\n\n"
);
// .setEditSummary("Add pronunciation");
}
function BgEtymology(editor) {
if (!editor.get().includes("Etymology")) {
const BulgarianSection = RegExp("(==Bulgarian==\n)", 'g');
editor
.replace(
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===\nFrom \n";
if (plus && editor.contains(BareEtymology)) {
console.log("Bare etymology found");
editor.replace(BareEtymology, "===Etymology===\n" + derivationOutput + "\n");
} else {
editor.insertAtCursor(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 AddDerivedTerms(editor) {
if (!editor.get().includes("Derived terms")) {
const derivedTermsSection = CreateLinkSection("Derived terms", "Please enter derived term: ");
const Declension = RegExp("(====Declension====\n.+}}\n)", 'g');
editor
.replace(
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');
editor
.replace(
References,
relatedTermsSection + "\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) {
editor.replace(/ *<!--.*-->/g, ""); // Remove comments
const lemma = GetLemma(editor);
const declSpec = predictRelationalDeclension(lemma);
editor.replace(/<.*>/, `<${declSpec}>`); // Predict declension spec
const form = predictRelationalForm(lemma);
if (form) {
editor.replace("{{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) {
editor.replace("* {{R:bg:RBE}}\n", "");
}
});
}
getChitankaAsync(title).then((resp) => {
if (!resp.ok) {
editor.replace("* {{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 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 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 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,
BgReferencesTemplate,
BgEtymologyTemplate,
DerTemplate,
DerPlusTemplate,
BorTemplate,
BorPlusTemplate,
InhTemplate,
InhPlusTemplate,
DerivedTermsTemplate,
RelatedTermsTemplate,
AutocompleteRelationalAdjTemplate,
PurgeReferencesTemplate
];
function applyTemplate(template) {
template(pathoschild.TemplateScript.Context);
}
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 definding)
pathoschild.TemplateScript.Context.insertAtCursor = function(text) {
this.replaceSelection(text);
// Advance the cursor to after the inserted text
const textArea = GetTextArea();
const start = textArea.selectionStart;
const end = start + text.length;
textArea.setSelectionRange(end, end);
};
// ------------------------- 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 == "d") {
applyTemplate(Derived);
}
if (event.key == "i") {
applyTemplate(Inherited);
}
if (event.key == "b") {
applyTemplate(Borrowed);
}
if (event.key == "D") {
applyTemplate(DerivedPlus);
}
if (event.key == "I") {
applyTemplate(InheritedPlus);
}
if (event.key == "B") {
applyTemplate(BorrowedPlus);
}
if (event.key == "y") {
applyTemplate(AddDerivedTerms);
event.preventDefault();
}
if (event.key == "Y") {
applyTemplate(AddRelatedTerms);
event.preventDefault();
}
if (event.key == ".") {
applyTemplate(AutocompleteRelationalAdj);
}
if (event.key == "Escape") {
ReleaseSelectionAfter();
return;
}
}
if (event.key == "Escape") {
ReleaseSelection();
}
}
document.addEventListener("keydown", customEditorKeybinds, false);
});
// </nowiki>