metamorpov/src/replace-words.js

385 lines
12 KiB
JavaScript
Raw Normal View History

const verbsHelper = require("english-verbs-helper");
const verbsIrregular = require("english-verbs-irregular/dist/verbs.json");
const verbsGerunds = require("english-verbs-gerunds/dist/gerunds.json");
2025-01-26 23:42:29 -05:00
const verbsData = verbsHelper.mergeVerbsData(verbsIrregular, verbsGerunds);
/* Replaces verbs, point-of-view, name, pronouns, and "also" terms */
function replaceAll() {
browser.storage.local.get(null, (options) => {
const hostname = window.location.hostname;
const isEnabled = !(options.domains === undefined) && options.domains.includes(hostname);
if (!isEnabled) { return; }
2024-12-23 05:58:48 -05:00
const isNameSet = !(options.name === undefined || options.name == "");
const isPronounsSet = !(options.preset === undefined || options.preset == "");
const isPovSet = !(options.pov === undefined || options.pov == "");
const isPovLegal = isPovSet && (options.pov != "third" || (isNameSet && isPronounsSet));
2025-02-22 00:20:27 -05:00
const isAllSet = isNameSet && isPronounsSet && isPovLegal;
2025-01-24 17:36:22 -05:00
if (isPovLegal) {
replaceVerbs(options);
replacePov(options);
replacePlv(options);
}
if (isNameSet) {
replaceName(options);
}
if (isPronounsSet) {
replacePronouns(options);
}
const isAlsoSet = !options.also === undefined;
if (isAlsoSet) {
replaceAlso(options);
}
2025-02-21 23:56:45 -05:00
2025-02-22 00:20:27 -05:00
if (isAllSet) {
replaceExceptions(options);
replaceCuts();
}
});
2024-12-23 05:58:48 -05:00
}
/* Replaces all instances of "Y/n" with the user's name */
2024-12-23 05:58:48 -05:00
function replaceName(options) {
const searchTerm = /\by\/n\b|\(y\/n\)|\[y\/n\]/ig;
walk(document.body, searchTerm, options["name"], directMethod);
}
/* Replaces all verb keys */
function replaceVerbs(options) {
const searchTerm = /(v)r([nb])\/([\w\s]+)\/(?:((?:(?:simple|progressive|perfect|perfect[ _]progressive|participle(?![ _]future))[ _])*(?:past|present|future))\/)*/i;
walk(document.body, searchTerm, options, verbMethod);
2024-12-23 05:58:48 -05:00
}
/* Replaces all pronoun keys */
/* This could be made faster if I give premade regexp expressions for each, but I don't want to do that! */
2024-12-23 05:58:48 -05:00
function replacePronouns(options) {
let pronouns = getPronouns(options.preset, options.other);
replacePronounSet("Prn/s", "prn/s", pronouns["subjective"]);
replacePronounSet("Prn/o", "prn/o", pronouns["objective"]);
replacePronounSet("Prn/p", "prn/p", pronouns["possessive"]);
replacePronounSet("Prn/a", "prn/a", pronouns["adjective"]);
replacePronounSet("Prn/r", "prn/r", pronouns["reflexive"]);
replacePronounSet("Prn/H", "prn/H", pronouns["honorific-abbr"]);
replacePronounSet("Prn/h", "prn/h", pronouns["honorific"]);
2025-02-21 19:20:28 -05:00
replacePronounSet("Prn/N", "prn/N", pronouns["adult"]);
replacePronounSet("Prn/n", "prn/n", pronouns["youth"]);
replacePronounSet("Prn/F", "prn/f", pronouns["parent"]);
replacePronounSet("Prn/f", "prn/f", pronouns["child"]);
replacePronounSet("Prn/k", "prn/k", pronouns["sibling"]);
replacePronounSet("Prn/m", "prn/m", pronouns["married"]);
replacePronounSet("Prn/d", "prn/d", pronouns["dating"]);
2024-12-23 05:58:48 -05:00
}
/* Gets pronouns based on the provided key (or user input) */
2024-12-23 05:58:48 -05:00
function getPronouns(key, other) {
switch (key) {
case "she":
return {
"subjective": "she",
"objective": "her",
"possessive": "her",
"adjective": "hers",
"reflexive": "herself",
"honorific-abbr": "Ms.",
"honorific": "miss",
2025-02-21 19:20:28 -05:00
"adult": "woman",
"youth": "girl",
"parent": "mother",
"child": "daughter",
"sibling": "sister",
"married": "wife",
"dating": "girlfriend",
2024-12-23 05:58:48 -05:00
"plurality": "singular"
};
case "he":
return {
"subjective": "he",
"objective": "him",
"possessive": "his",
"adjective": "his",
"reflexive": "himself",
"honorific-abbr": "Mr.",
"honorific": "mister",
2025-02-21 19:20:28 -05:00
"adult": "man",
"youth": "boy",
"parent": "father",
"child": "son",
"sibling": "brother",
"married": "husband",
"dating": "boyfriend",
2024-12-23 05:58:48 -05:00
"plurality": "singular"
};
case "they":
return {
"subjective": "they",
"objective": "them",
"possessive": "their",
"adjective": "theirs",
"reflexive": "themself",
"honorific-abbr": "Mx.",
"honorific": "mix",
2025-02-21 19:20:28 -05:00
"adult": "person",
"youth": "kid",
"parent": "parent",
"child": "child",
"sibling": "sibling",
"married": "spouse",
"dating": "partner",
2024-12-23 05:58:48 -05:00
"plurality": "plural"
};
case "first":
return {
"subjective": "I",
"objective": "me",
"possessive": "my",
"adjective": "mine",
"reflexive": "myself"
};
case "second":
return {
"subjective": "you",
"objective": "you",
"possessive": "your",
"adjective": "yours",
"reflexive": "yourself"
};
case "first-plural":
return {
"subjective": "we",
"objective": "us",
"possessive": "our",
"adjective": "ours",
"reflexive": "ourselves"
};
case "second-plural":
return {
"subjective": "you",
"objective": "you",
"possessive": "your",
"adjective": "yours",
"reflexive": "yourselves"
};
case "third-plural":
return {
"subjective": "they",
"objective": "them",
"possessive": "their",
"adjective": "theirs",
"reflexive": "themselves"
};
case "other":
return other;
2024-12-23 05:58:48 -05:00
}
}
/* Replaces pronoun keys in capital and lowercase with their equivalent pronouns */
2024-12-23 05:58:48 -05:00
function replacePronounSet(keyCapital, key, pronoun) {
escapeAndReplace(keyCapital, capitalize(pronoun), true);
escapeAndReplace(key, pronoun, true);
}
/* Replaces all point-of-view keys */
2024-12-23 05:58:48 -05:00
function replacePov(options) {
let pronouns;
if (options.pov == "third") {
pronouns = getPronouns(options.preset, options.other);
replacePronounSet("Pov/S", "pov/S", options.name);
replacePronounSet("Pov/O", "pov/O", options.name);
replacePronounSet("Pov/P", "pov/P", options.name + "'s");
replacePronounSet("Pov/A", "pov/A", options.name + "'s");
replacePronounSet("Pov/R", "pov/R", options.name + "'s self");
} else {
pronouns = getPronouns(options.pov, null);
replacePronounSet("Pov/S", "pov/S", pronouns["subjective"]);
replacePronounSet("Pov/O", "pov/O", pronouns["objective"]);
replacePronounSet("Pov/P", "pov/P", pronouns["possessive"]);
replacePronounSet("Pov/A", "pov/A", pronouns["adjective"]);
replacePronounSet("Pov/R", "pov/R", pronouns["reflexive"]);
2024-12-23 05:58:48 -05:00
}
replacePronounSet("Pov/s", "pov/s", pronouns["subjective"]);
replacePronounSet("Pov/o", "pov/o", pronouns["objective"]);
replacePronounSet("Pov/p", "pov/p", pronouns["possessive"]);
replacePronounSet("Pov/a", "pov/a", pronouns["adjective"]);
replacePronounSet("Pov/r", "pov/r", pronouns["reflexive"]);
2019-10-20 07:41:12 -04:00
}
/* Replaces plural point-of-view keys */
2024-12-23 05:58:48 -05:00
function replacePlv(options) {
let pronouns = getPronouns(options.pov + "-plural", null);
replacePronounSet("Plv/s", "plv/s", pronouns["subjective"]);
replacePronounSet("Plv/o", "plv/o", pronouns["objective"]);
replacePronounSet("Plv/p", "plv/p", pronouns["possessive"]);
replacePronounSet("Plv/a", "plv/a", pronouns["adjective"]);
replacePronounSet("Plv/r", "plv/r", pronouns["reflexive"]);
}
/* Replaces additional terms specified by the user */
2024-12-23 05:58:48 -05:00
function replaceAlso(options) {
Object.entries(options.also).forEach(([key, value]) => {
escapeAndReplace(key, value, true);
});
2016-07-06 01:12:36 -04:00
}
2016-06-18 23:04:38 -04:00
2025-02-22 00:20:27 -05:00
/* Replaces exception statements by POV */
function replaceExceptions(options) {
const searchTerm = /exc\/([^\/]*)\/([^\/]*)\/ig/;
2025-02-22 00:20:27 -05:00
walk(document.body, searchTerm, options, exceptionMethod);
}
2025-02-21 23:56:45 -05:00
/* Replaces cut statements with cuts */
function replaceCuts() {
const searchTerm = /cut\/([^\/]+)\/(-?[\d]+)\/ig/;
2025-02-21 23:56:45 -05:00
walk(document.body, searchTerm, null, cutMethod);
}
/* Turns a term into regexp format and uses that to replace it */
function escapeAndReplace(searchTerm, replaceValue, caseSensitive) {
let searchTermEscaped = searchTerm.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
const flags = caseSensitive ? "g" : "ig"
if (searchTermEscaped[0].match(/[a-z]/i)) {
searchTermEscaped = `\\b${searchTermEscaped}`
2024-12-23 05:58:48 -05:00
}
if (searchTermEscaped[searchTermEscaped.length - 1].match((/[a-z]/i))) {
searchTermEscaped = `${searchTermEscaped}\\b`
2024-12-23 05:58:48 -05:00
}
const searchTermRegexp = new RegExp(searchTermEscaped, flags)
walk(document.body, searchTermRegexp, replaceValue, directMethod);
}
/* Traverses a node and its children to run the provided replacement method */
function walk(node, searchTerm, replaceValue, replaceMethod) {
if (node.contentEditable == "true" || node.type == "textarea" || node.type == "input") { return; }
let child, next;
switch (node.nodeType) {
case 1: /* ELEMENT_NODE */
case 9: /* DOCUMENT_NODE */
case 11: /* DOCUMENT_FRAGMENT_NODE */
child = node.firstChild;
while (child) {
next = child.nextSibling;
walk(child, searchTerm, replaceValue, replaceMethod);
child = next;
}
break;
case 3: /* TEXT_NODE */
replaceMethod(node, searchTerm, replaceValue);
2024-12-23 05:58:48 -05:00
}
}
/* Replaces all of the provided terms in a node */
function directMethod(node, searchTerm, replaceValue) {
node.nodeValue = node.nodeValue.replaceAll(searchTerm, replaceValue);
}
/* Replaces all verbs, not used for third-person plural */
function verbMethod(node, searchTerm, options) {
const match = node.nodeValue.match(searchTerm);
if (match == null) { return; }
const isCapital = /V/.test(match[1]);
const category = match[2];
const verb = match[3];
let tense = match[4];
if (tense == null) {
tense = "PAST";
} else {
tense = tense.toUpperCase().replaceAll(" ", "_");
}
let povIndex;
switch (category) {
case "B": /* B and b vary by POV, capitalized to indicate a named subject */
if (options.pov == "third") {
povIndex = getPovIndex("third-singular", null);
} else {
povIndex = getPovIndex(options.pov, options);
}
case "b":
povIndex = getPovIndex(options.pov, options);
case "N": /* N and n are always third-person */
povIndex = getPovIndex("third-singular", null);
case "n":
povIndex = getPovIndex("third", options);
}
let replaceValue = verbsHelper.getConjugation(verbsData, verb, tense, povIndex);
if (isCapital) {
replaceValue = capitalize(replaceValue);
}
node.nodeValue = node.nodeValue.replace(searchTerm, replaceValue);
verbMethod(node, searchTerm, options);
}
2025-02-21 23:56:45 -05:00
/* Allows for custom words to be cut off */
function cutMethod(node, searchTerm, unused) {
const match = node.nodeValue.match(searchTerm);
2025-02-21 23:56:45 -05:00
if (match == null) { return; }
const target = match[1];
const amount = match[2];
if (target.length <= Math.abs(amount)) {
node.nodeValue = node.nodeValue.replace(searchTerm, target);
cutMethod(node, searchTerm, unused);
return;
}
let replaceValue;
if (amount < 0) {
replaceValue = target.slice(0, amount);
} else {
replaceValue = target.slice(amount);
}
node.nodeValue = node.nodeValue.replace(searchTerm, replaceValue);
cutMethod(node, searchTerm, unused);
}
2025-02-22 00:20:27 -05:00
/* Allows for custom words to be cut off */
function exceptionMethod(node, searchTerm, options) {
const match = node.nodeValue.match(searchTerm);
2025-02-22 00:20:27 -05:00
if (match == null) { return; }
let replaceValue;
if (options.pov == "third") {
replaceValue = match[2];
} else {
replaceValue = match[1];
}
node.nodeValue = node.nodeValue.replace(searchTerm, replaceValue);
cutMethod(node, searchTerm, options);
}
/* Returns the input word, capitalized */
2024-12-23 05:58:48 -05:00
function capitalize(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
2015-02-21 22:00:28 -05:00
}
/* Determines the index for point-of-view to be used when conjugating verbs */
function getPovIndex(pov, options) {
switch (pov) {
case "first":
return 0;
case "second":
return 1;
case "third-singular":
return 2;
case "third":
if (getPronouns(options.preset, options.other).plurality == "singular") {
return 2;
} else { /* Plurality is plural, "they go" */
return 5;
}
return getThirdIndex(options);
}
}
replaceAll();