aboutsummaryrefslogtreecommitdiff
path: root/page.js
diff options
context:
space:
mode:
Diffstat (limited to 'page.js')
-rw-r--r--page.js186
1 files changed, 186 insertions, 0 deletions
diff --git a/page.js b/page.js
new file mode 100644
index 0000000..cfc31c1
--- /dev/null
+++ b/page.js
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+"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");
+ }
+
+ 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 };
+ }
+
+ 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 (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;
+ }
+ 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);
+ }
+ });
+})();