// SPDX-License-Identifier: GPL-2.0-only "use strict"; (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": "⩾", "\\<": "⟨", "\\>": "⟩", "\\alpha": "α", "\\a": "α", "\\beta": "β", "\\b": "β", "\\gamma": "γ", "\\g": "γ", "\\delta": "δ", "\\d": "δ", "\\epsilon": "ε", "\\e": "ε", "\\zeta": "ζ", "\\z": "ζ", "\\eta": "η", "\\theta": "θ", "\\th": "θ", "\\iota": "ι", "\\i": "ι", "\\kappa": "κ", "\\k": "κ", "\\lambda": "λ", "\\l": "λ", "\\mu": "μ", "\\m": "μ", "\\nu": "ν", "\\n": "ν", "\\ks": "ξ", "\\omicron": "ο", "\\pi": "π", "\\p": "π", "\\rho": "ρ", "\\r": "ρ", "\\sigma": "σ", "\\s": "σ", "\\tau": "τ", "\\t": "τ", "\\upsilon": "υ", "\\u": "υ", "\\varphi": "φ", "\\ph": "φ", "\\chi": "χ", "\\psi": "ψ", "\\ps": "ψ", "\\omega": "ω", "\\aw": "ω", "\\partial": "∂", "\\dp": "∂", "\\infty": "∞", "\\inf": "∞", "\\del": "∇", "\\int": "∫", }[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/); if (offset === -1) return { end: input.value.length, success: false }; nameEnd = nameStart + 2 + 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); if (input.value[nameEnd] === ";") 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, "\\<": 0, "\\>": 0, "\\alpha": 0, "\\a": 0, "\\beta": 0, "\\b": 0, "\\gamma": 0, "\\g": 0, "\\delta": 0, "\\d": 0, "\\epsilon": 0, "\\e": 0, "\\zeta": 0, "\\z": 0, "\\eta": 0, "\\e": 0, "\\theta": 0, "\\th": 0, "\\iota": 0, "\\i": 0, "\\kappa": 0, "\\k": 0, "\\lambda": 0, "\\l": 0, "\\mu": 0, "\\m": 0, "\\nu": 0, "\\n": 0, "\\ks": 0, "\\omicron": 0, "\\pi": 0, "\\p": 0, "\\rho": 0, "\\r": 0, "\\sigma": 0, "\\s": 0, "\\tau": 0, "\\t": 0, "\\upsilon": 0, "\\u": 0, "\\varphi": 0, "\\ph": 0, "\\chi": 0, "\\psi": 0, "\\ps": 0, "\\omega": 0, "\\aw": 0, "\\partial": 0, "\\dp": 0, "\\infty": 0, "\\inf": 0, "\\del": 0, "\\int": 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; } function eventHandler(event) { if (event.target.selectionStart != null) { const { selectionStart, selectionEnd } = event.target; const input = { value: event.target.value, caret: selectionStart, }; subCmds(input); event.target.value = input.value; event.target.selectionStart = input.caret; // Sloppily ignore spurious `keyup` event.target.selectionEnd = selectionEnd - selectionStart + 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); } } const adobeConnectIframe = document.getElementById("html-meeting-frame"); if (adobeConnectIframe) { // Adobe Connect workaround console.debug("Tex Type: Listening for `textarea` elements."); const textAreas = adobeConnectIframe.contentDocument.getElementsByTagName("textarea"); const oldTextAreas = new Set(textAreas); function handleNewTextAreas() { console.debug("Tex Type: Seeking new `textarea`s."); for (const textArea of textAreas) if (!oldTextAreas.has(textArea)) { console.debug("Tex Type: Found new `textarea`."); textArea.addEventListener("keyup", eventHandler, true); oldTextAreas.add(textArea); } } const cb = new MutationObserver(handleNewTextAreas); cb.observe(adobeConnectIframe.contentDocument, { childList: true, subtree: true, }); handleNewTextAreas(); } else window.addEventListener("input", eventHandler, true); } })();