aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--demo.html5
-rw-r--r--lib/extension.js26
-rw-r--r--manifest.json10
-rw-r--r--options.html70
-rw-r--r--options.js12
-rw-r--r--page.js348
-rw-r--r--popup.html12
-rw-r--r--popup.js6
9 files changed, 323 insertions, 172 deletions
diff --git a/README.md b/README.md
index e586622..8a4e579 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,12 @@ handlers. It works on the following, to a certain extent:
- Pronto
- Skype, but only halfway
+## Configuration
+
+Click on the extension icon in the toolbar or in its extensions menu to open the
+options page. This page can also be accessed from <about:addons>,
+<chrome://extensions>, etc.
+
## Alternative
Font ligatures could be used instead, but then all readers would have to install
diff --git a/demo.html b/demo.html
index 4bd8899..f4629a6 100644
--- a/demo.html
+++ b/demo.html
@@ -24,6 +24,11 @@
</head>
<body>
<textarea autofocus></textarea>
+ <script>
+ async function subEnabled() {
+ return true;
+ }
+ </script>
<script src="init-unicode-maps.js"></script>
<script src="unicode-maps-to-bold.js"></script>
<script src="unicode-maps-to-subscript.js"></script>
diff --git a/lib/extension.js b/lib/extension.js
new file mode 100644
index 0000000..e74c877
--- /dev/null
+++ b/lib/extension.js
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+"use strict";
+
+const defaultSettings = {
+ enableRegExp: ".*",
+};
+
+window.browser = window.browser ?? window.chrome;
+
+async function setSetting(setting, val) {
+ await browser.storage.sync.set({ [setting]: val });
+}
+
+async function getSetting(setting) {
+ return (
+ (await browser.storage.sync.get(setting))[setting] ??
+ defaultSettings[setting]
+ );
+}
+
+async function subEnabled() {
+ return new RegExp(
+ "^" + (await getSetting("enableRegExp")).replace(/\s+/g, "") + "$"
+ ).test(window.location.host);
+}
diff --git a/manifest.json b/manifest.json
index 4dbdacb..3bf0b83 100644
--- a/manifest.json
+++ b/manifest.json
@@ -3,9 +3,11 @@
"name": "TeX Type",
"version": "0.1.0",
"description": "Converts commands (some TeX) in textboxes to unicode as one types.",
+ "permissions": ["storage"],
"content_scripts": [{
"matches": ["*://*/*"],
"js": [
+ "lib/extension.js",
"init-unicode-maps.js",
"unicode-maps-to-bold.js",
"unicode-maps-to-subscript.js",
@@ -13,6 +15,14 @@
"page.js"
]
}],
+ "action": {
+ "default_popup": "popup.html",
+ "default_title": "TeX Type Settings"
+ },
+ "options_ui": {
+ "page": "options.html",
+ "open_in_tab": true
+ },
"browser_specific_settings": {
"gecko": {
"id": "tex-type@pml4t.net"
diff --git a/options.html b/options.html
new file mode 100644
index 0000000..8fb6b5f
--- /dev/null
+++ b/options.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<!-- SPDX-License-Identifier: GPL-2.0-only -->
+
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <style>
+ html,
+ body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ }
+
+ body {
+ background-color: black;
+ box-sizing: border-box;
+ color: white;
+ display: flex;
+ flex-direction: column;
+ font-family: sans-serif;
+ gap: 8px;
+ padding: 8px;
+ }
+
+ textarea {
+ background-color: inherit;
+ border: 2px solid gray;
+ color: inherit;
+ padding: 8px;
+ }
+
+ textarea:focus {
+ border: 2px solid white;
+ outline: none;
+ }
+
+ #enable-reg-exp {
+ flex: 1;
+ resize: none;
+ }
+
+ button {
+ border: 2px solid transparent;
+ color: inherit;
+ outline: none;
+ padding: 4px;
+ }
+
+ button:focus {
+ border: 2px solid white;
+ outline: none;
+ }
+
+ button.inviting {
+ background-color: green;
+ }
+ </style>
+ </head>
+ <body>
+ <label for="enable-reg-exp">
+ Regular expression matching domain names of sites on which to enable
+ substitution. Whitespace is ignored.
+ </label>
+ <textarea id="enable-reg-exp"></textarea>
+ <button id="enable-reg-exp-save" class="inviting">Save</button>
+ <script src="lib/extension.js"></script>
+ <script src="options.js"></script>
+ </body>
+</html>
diff --git a/options.js b/options.js
new file mode 100644
index 0000000..3262181
--- /dev/null
+++ b/options.js
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+"use strict";
+
+(async () => {
+ const input = document.getElementById("enable-reg-exp");
+ input.value = await getSetting("enableRegExp");
+ const saveButton = document.getElementById("enable-reg-exp-save");
+ saveButton.addEventListener("click", () =>
+ setSetting("enableRegExp", input.value)
+ );
+})();
diff --git a/page.js b/page.js
index cfc31c1..a55e5e0 100644
--- a/page.js
+++ b/page.js
@@ -2,185 +2,189 @@
"use strict";
-(() => {
- console.log("TeX Type started.");
-
- function cmdToUnicode(name, args) {
- if (name === "^")
- return Array.from(args[0]).reduce(
- (acc, c) => acc + (window.unicodeMaps.toSuperscript[c] ?? "^" + c),
- ""
- );
- if (name === "_")
- return Array.from(args[0]).reduce(
- (acc, c) => acc + (window.unicodeMaps.toSubscript[c] ?? "_" + c),
- ""
- );
-
- let sym = {
- "\\x": "×",
- "\\times": "×",
- "\\.": "⋅",
- "\\cdot": "⋅",
- "\\pm": "±",
- "\\mp": "∓",
- "\\in": "∈",
- "\\/in": "∉",
- "\\\\": "\u200b\\",
- "\\{": "\u200b{",
- "\\}": "\u200b}",
- "\\^": "\u200b^",
- "\\_": "\u200b_",
- "<===": "⇐",
- "==>": "⇒",
- "<=>": "⇔",
- "<==": "⟸",
- "=>": "⟹",
- "\\implies": "⟹",
- "<>": "⟺",
- "\\iff": "⟺",
- "<=": "⩽",
- "\\le": "⩽",
- ">=": "⩾",
- "\\ge": "⩾",
- }[name];
- if (sym) return sym;
-
- if (["\\bf", "\\mathbf"].includes(name))
- return Array.from(args[0]).reduce(
- (acc, chr) => acc + (window.unicodeMaps.toBold[chr] ?? chr),
- ""
- );
-
- console.error("unreachable");
- }
+(async () => {
+ if (await subEnabled()) {
+ console.log("TeX Type started.");
+
+ function cmdToUnicode(name, args) {
+ if (name === "^")
+ return Array.from(args[0]).reduce(
+ (acc, c) => acc + (window.unicodeMaps.toSuperscript[c] ?? "^" + c),
+ ""
+ );
+ if (name === "_")
+ return Array.from(args[0]).reduce(
+ (acc, c) => acc + (window.unicodeMaps.toSubscript[c] ?? "_" + c),
+ ""
+ );
+
+ let sym = {
+ "\\x": "×",
+ "\\times": "×",
+ "\\.": "⋅",
+ "\\cdot": "⋅",
+ "\\pm": "±",
+ "\\mp": "∓",
+ "\\in": "∈",
+ "\\/in": "∉",
+ "\\\\": "\u200b\\",
+ "\\{": "\u200b{",
+ "\\}": "\u200b}",
+ "\\^": "\u200b^",
+ "\\_": "\u200b_",
+ "<===": "⇐",
+ "==>": "⇒",
+ "<=>": "⇔",
+ "<==": "⟸",
+ "=>": "⟹",
+ "\\implies": "⟹",
+ "<>": "⟺",
+ "\\iff": "⟺",
+ "<=": "⩽",
+ "\\le": "⩽",
+ ">=": "⩾",
+ "\\ge": "⩾",
+ }[name];
+ if (sym) return sym;
+
+ if (["\\bf", "\\mathbf"].includes(name))
+ return Array.from(args[0]).reduce(
+ (acc, chr) => acc + (window.unicodeMaps.toBold[chr] ?? chr),
+ ""
+ );
+
+ console.error("unreachable");
+ }
- function getArgs(input, i, count) {
- const args = [];
- for (let argI = 0; argI < count; argI++) {
- while (input.value[i] === " ") i++;
-
- if (!input.value[i]) break;
-
- if (input.value[i] === "\\") {
- const start = i;
- i = handleCmd(input, i);
- if (failed) break;
- args.push(input.value.slice(start, i));
- } else if (input.value[i] === "{") {
- const argStart = ++i;
- i = subCmds(input, i);
- if (i === input.value.length) break;
- const arg = input.value.slice(argStart, i++);
- args.push(arg);
- } else {
- args.push(input.value[i++]);
+ function getArgs(input, i, count) {
+ const args = [];
+ for (let argI = 0; argI < count; argI++) {
+ while (input.value[i] === " ") i++;
+
+ if (!input.value[i]) break;
+
+ if (input.value[i] === "\\") {
+ const start = i;
+ i = handleCmd(input, i);
+ if (failed) break;
+ args.push(input.value.slice(start, i));
+ } else if (input.value[i] === "{") {
+ const argStart = ++i;
+ i = subCmds(input, i);
+ if (i === input.value.length) break;
+ const arg = input.value.slice(argStart, i++);
+ args.push(arg);
+ } else {
+ args.push(input.value[i++]);
+ }
}
+ return { args, argsEnd: i };
}
- return { args, argsEnd: i };
- }
- function trySubCmd(input, nameStart) {
- let nameEnd;
- if (input.value[nameStart] === "\\") {
- const offset = input.value.slice(nameStart + 2).search(/\W/);
- nameEnd = nameStart + 2 + (offset < 0 ? 0 : offset);
- } else if ("^_".includes(input.value[nameStart])) nameEnd = nameStart + 1;
- else if (
- "<>=".includes(input.value[nameStart]) &&
- ">=".includes(input.value[nameStart + 1])
- )
- if ("<>=".includes(input.value[nameStart + 2]))
- if ("<>=".includes(input.value[nameStart + 3])) nameEnd = nameStart + 4;
- else if (/\s/.test(input.value[nameStart + 3])) nameEnd = nameStart + 3;
+ function trySubCmd(input, nameStart) {
+ let nameEnd;
+ if (input.value[nameStart] === "\\") {
+ const offset = input.value.slice(nameStart + 2).search(/\W/);
+ nameEnd = nameStart + 2 + (offset < 0 ? 0 : offset);
+ } else if ("^_".includes(input.value[nameStart])) nameEnd = nameStart + 1;
+ else if (
+ "<>=".includes(input.value[nameStart]) &&
+ ">=".includes(input.value[nameStart + 1])
+ )
+ if ("<>=".includes(input.value[nameStart + 2]))
+ if ("<>=".includes(input.value[nameStart + 3]))
+ nameEnd = nameStart + 4;
+ else if (/\s/.test(input.value[nameStart + 3]))
+ nameEnd = nameStart + 3;
+ else return { end: nameStart + 1, success: false };
+ else if (/\s/.test(input.value[nameStart + 2])) nameEnd = nameStart + 2;
else return { end: nameStart + 1, success: false };
- else if (/\s/.test(input.value[nameStart + 2])) nameEnd = nameStart + 2;
+ else if (input.value[nameStart] === "\u200b")
+ return { end: nameStart + 2, success: true };
else return { end: nameStart + 1, success: false };
- else if (input.value[nameStart] === "\u200b")
- return { end: nameStart + 2, success: true };
- else return { end: nameStart + 1, success: false };
- const name = input.value.slice(nameStart, nameEnd);
-
- const argsCount = {
- "\\x": 0,
- "\\times": 0,
- "\\.": 0,
- "\\cdot": 0,
- "\\pm": 0,
- "\\mp": 0,
- "\\in": 0,
- "\\/in": 0,
- "\\\\": 0,
- "\\{": 0,
- "\\}": 0,
- "\\^": 0,
- "\\_": 0,
- "<==": 0,
- "=>": 0,
- "<=>": 0,
- "<===": 0,
- "==>": 0,
- "\\implies": 0,
- "<>": 0,
- "\\iff": 0,
- "<=": 0,
- "\\le": 0,
- ">=": 0,
- "\\ge": 0,
- "\\bf": 1,
- "\\mathbf": 1,
- "^": 1,
- _: 1,
- }[name];
- if (argsCount == null) return { end: nameEnd, success: false };
-
- const argsRet = getArgs(input, nameEnd, argsCount);
- const args = argsRet.args;
- const argsEnd = argsRet.argsEnd;
- if (args.length < argsCount) return { end: argsEnd, success: false };
-
- const unicode = cmdToUnicode(name, args);
- input.value =
- input.value.slice(0, nameStart) + unicode + input.value.slice(argsEnd);
-
- const caret = input.caret;
- if (caret > nameStart)
- input.caret = caret + nameStart - argsEnd + unicode.length;
-
- return { end: nameStart + unicode.length, success: true };
- }
-
- function subCmds(input, i) {
- const starts = [];
- const ends = [];
- if (i == null) i = 0;
- while (i < input.value.length) {
- if (input.value[i] === "}") return i;
- else i = trySubCmd(input, i).end;
+ const name = input.value.slice(nameStart, nameEnd);
+
+ const argsCount = {
+ "\\x": 0,
+ "\\times": 0,
+ "\\.": 0,
+ "\\cdot": 0,
+ "\\pm": 0,
+ "\\mp": 0,
+ "\\in": 0,
+ "\\/in": 0,
+ "\\\\": 0,
+ "\\{": 0,
+ "\\}": 0,
+ "\\^": 0,
+ "\\_": 0,
+ "<==": 0,
+ "=>": 0,
+ "<=>": 0,
+ "<===": 0,
+ "==>": 0,
+ "\\implies": 0,
+ "<>": 0,
+ "\\iff": 0,
+ "<=": 0,
+ "\\le": 0,
+ ">=": 0,
+ "\\ge": 0,
+ "\\bf": 1,
+ "\\mathbf": 1,
+ "^": 1,
+ _: 1,
+ }[name];
+ if (argsCount == null) return { end: nameEnd, success: false };
+
+ const argsRet = getArgs(input, nameEnd, argsCount);
+ const args = argsRet.args;
+ const argsEnd = argsRet.argsEnd;
+ if (args.length < argsCount) return { end: argsEnd, success: false };
+
+ const unicode = cmdToUnicode(name, args);
+ input.value =
+ input.value.slice(0, nameStart) + unicode + input.value.slice(argsEnd);
+
+ const caret = input.caret;
+ if (caret > nameStart)
+ input.caret = caret + nameStart - argsEnd + unicode.length;
+
+ return { end: nameStart + unicode.length, success: true };
}
- return input.value.length;
- }
- window.addEventListener("input", (event) => {
- if (event.target.selectionStart != null) {
- const input = {
- value: event.target.value,
- caret: event.target.selectionStart,
- };
- subCmds(input);
- event.target.value = input.value;
- event.target.selectionStart = event.target.selectionEnd = input.caret;
- } else if (event.target.contentEditable === "true") {
- const textNode = window.getSelection().anchorNode;
- const input = {
- value: textNode.textContent,
- caret: window.getSelection().getRangeAt(0).startOffset,
- };
- subCmds(input);
- textNode.textContent = input.value;
- window
- .getSelection()
- .setBaseAndExtent(textNode, input.caret, textNode, input.caret);
+ function subCmds(input, i) {
+ const starts = [];
+ const ends = [];
+ if (i == null) i = 0;
+ while (i < input.value.length) {
+ if (input.value[i] === "}") return i;
+ else i = trySubCmd(input, i).end;
+ }
+ return input.value.length;
}
- });
+
+ window.addEventListener("input", (event) => {
+ if (event.target.selectionStart != null) {
+ const input = {
+ value: event.target.value,
+ caret: event.target.selectionStart,
+ };
+ subCmds(input);
+ event.target.value = input.value;
+ event.target.selectionStart = event.target.selectionEnd = input.caret;
+ } else if (event.target.contentEditable === "true") {
+ const textNode = window.getSelection().anchorNode;
+ const input = {
+ value: textNode.textContent,
+ caret: window.getSelection().getRangeAt(0).startOffset,
+ };
+ subCmds(input);
+ textNode.textContent = input.value;
+ window
+ .getSelection()
+ .setBaseAndExtent(textNode, input.caret, textNode, input.caret);
+ }
+ });
+ }
})();
diff --git a/popup.html b/popup.html
new file mode 100644
index 0000000..044ad4f
--- /dev/null
+++ b/popup.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- SPDX-License-Identifier: GPL-2.0-only -->
+
+<html>
+ <head>
+ <meta charset="utf-8" />
+ </head>
+ <body>
+ <script src="lib/extension.js"></script>
+ <script src="popup.js"></script>
+ </body>
+</html>
diff --git a/popup.js b/popup.js
new file mode 100644
index 0000000..c07a031
--- /dev/null
+++ b/popup.js
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+"use strict";
+
+browser.runtime.openOptionsPage();
+window.close();