diff --git a/ts/editable/Mathjax.svelte b/ts/editable/Mathjax.svelte
index cc56a1b2c..03687b73a 100644
--- a/ts/editable/Mathjax.svelte
+++ b/ts/editable/Mathjax.svelte
@@ -23,13 +23,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { randomUUID } from "../lib/uuid";
import { pageTheme } from "../sveltelib/theme";
- import { convertMathjax } from "./mathjax";
+ import { convertMathjax, unescapeSomeEntities } from "./mathjax";
export let mathjax: string;
export let block: boolean;
export let fontSize: number;
- $: [converted, title] = convertMathjax(mathjax, $pageTheme.isDark, fontSize);
+ $: [converted, title] = convertMathjax(
+ unescapeSomeEntities(mathjax),
+ $pageTheme.isDark,
+ fontSize,
+ );
$: empty = title === "MathJax";
$: encoded = encodeURIComponent(converted);
diff --git a/ts/editable/mathjax-element.ts b/ts/editable/mathjax-element.ts
index f2b24e335..1d7117cf5 100644
--- a/ts/editable/mathjax-element.ts
+++ b/ts/editable/mathjax-element.ts
@@ -15,14 +15,6 @@ const mathjaxTagPattern =
const mathjaxBlockDelimiterPattern = /\\\[(.*?)\\\]/gsu;
const mathjaxInlineDelimiterPattern = /\\\((.*?)\\\)/gsu;
-/**
- * If the user enters the Mathjax with delimiters, "<" and ">" will
- * be first translated to entities.
- */
-function translateEntitiesToMathjax(value: string) {
- return value.replace(/</g, "{\\lt}").replace(/>/g, "{\\gt}");
-}
-
export const Mathjax: DecoratedElementConstructor = class Mathjax
extends HTMLElement
implements DecoratedElement
@@ -45,12 +37,10 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
static toUndecorated(stored: string): string {
return stored
.replace(mathjaxBlockDelimiterPattern, (_match: string, text: string) => {
- const escaped = translateEntitiesToMathjax(text);
- return `<${Mathjax.tagName} block="true">${escaped}${Mathjax.tagName}>`;
+ return `<${Mathjax.tagName} block="true">${text}${Mathjax.tagName}>`;
})
.replace(mathjaxInlineDelimiterPattern, (_match: string, text: string) => {
- const escaped = translateEntitiesToMathjax(text);
- return `<${Mathjax.tagName}>${escaped}${Mathjax.tagName}>`;
+ return `<${Mathjax.tagName}>${text}${Mathjax.tagName}>`;
});
}
@@ -107,7 +97,7 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
return;
}
- this.dataset.mathjax = this.innerText;
+ this.dataset.mathjax = this.innerHTML;
this.innerHTML = "";
this.style.whiteSpace = "normal";
diff --git a/ts/editable/mathjax.ts b/ts/editable/mathjax.ts
index 6338b3f5d..825f9d7ed 100644
--- a/ts/editable/mathjax.ts
+++ b/ts/editable/mathjax.ts
@@ -65,3 +65,14 @@ export function convertMathjax(
return [svg.outerHTML, title];
}
+
+/**
+ * Escape characters which are technically legal in Mathjax, but confuse HTML.
+ */
+export function escapeSomeEntities(value: string): string {
+ return value.replace(//g, ">");
+}
+
+export function unescapeSomeEntities(value: string): string {
+ return value.replace(/</g, "<").replace(/>/g, ">");
+}
diff --git a/ts/editor/mathjax-overlay/MathjaxEditor.svelte b/ts/editor/mathjax-overlay/MathjaxEditor.svelte
index dfd74f6a4..c9ac38e41 100644
--- a/ts/editor/mathjax-overlay/MathjaxEditor.svelte
+++ b/ts/editor/mathjax-overlay/MathjaxEditor.svelte
@@ -87,13 +87,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
},
);
});
-
- /**
- * Escape characters which are technically legal in Mathjax, but confuse HTML.
- */
- export function escapeSomeEntities(value: string): string {
- return value.replace(//g, "{\\gt}");
- }
@@ -101,7 +94,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{code}
{configuration}
bind:api={codeMirror}
- on:change={({ detail }) => code.set(escapeSomeEntities(detail))}
+ on:change={({ detail: mathjaxText }) => code.set(mathjaxText)}
on:blur
/>
diff --git a/ts/editor/mathjax-overlay/MathjaxHandle.svelte b/ts/editor/mathjax-overlay/MathjaxHandle.svelte
index 6f13d384d..4b58ef1f3 100644
--- a/ts/editor/mathjax-overlay/MathjaxHandle.svelte
+++ b/ts/editor/mathjax-overlay/MathjaxHandle.svelte
@@ -8,6 +8,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { writable } from "svelte/store";
import WithDropdown from "../../components/WithDropdown.svelte";
+ import { escapeSomeEntities, unescapeSomeEntities } from "../../editable/mathjax";
import { Mathjax } from "../../editable/mathjax-element";
import { on } from "../../lib/events";
import { noop } from "../../lib/functional";
@@ -20,8 +21,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const { container, api } = context.get();
const { editable, preventResubscription } = api;
- const code = writable("");
-
let activeImage: HTMLImageElement | null = null;
let mathjaxElement: HTMLElement | null = null;
let allow = noop;
@@ -30,6 +29,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let selectAll = false;
let position: CodeMirrorLib.Position | undefined = undefined;
+ /**
+ * Will contain the Mathjax text with unescaped entities.
+ * This is the text displayed in the actual editor window.
+ */
+ const code = writable("");
+
function showHandle(image: HTMLImageElement, pos?: CodeMirrorLib.Position): void {
allow = preventResubscription();
position = pos;
@@ -39,9 +44,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
activeImage = image;
mathjaxElement = activeImage.closest(Mathjax.tagName)!;
- code.set(mathjaxElement.dataset.mathjax ?? "");
+ code.set(unescapeSomeEntities(mathjaxElement.dataset.mathjax ?? ""));
unsubscribe = code.subscribe((value: string) => {
- mathjaxElement!.dataset.mathjax = value;
+ mathjaxElement!.dataset.mathjax = escapeSomeEntities(value);
});
}
diff --git a/ts/editor/rich-text-input/transform.ts b/ts/editor/rich-text-input/transform.ts
index 185992834..33ce0afb8 100644
--- a/ts/editor/rich-text-input/transform.ts
+++ b/ts/editor/rich-text-input/transform.ts
@@ -23,11 +23,11 @@ function adjustInputFragment(fragment: DocumentFragment): void {
}
}
-export function storedToFragment(html: string): DocumentFragment {
+export function storedToFragment(storedHTML: string): DocumentFragment {
/* We need .createContextualFragment so that customElements are initialized */
const fragment = document
.createRange()
- .createContextualFragment(createDummyDoc(adjustInputHTML(html)));
+ .createContextualFragment(createDummyDoc(adjustInputHTML(storedHTML)));
adjustInputFragment(fragment);
return fragment;
@@ -56,5 +56,6 @@ export function fragmentToStored(fragment: DocumentFragment): string {
const clone = document.importNode(fragment, true);
adjustOutputFragment(clone);
- return adjustOutputHTML(fragmentToString(clone));
+ const storedHTML = adjustOutputHTML(fragmentToString(clone));
+ return storedHTML;
}
diff --git a/ts/lib/dom.ts b/ts/lib/dom.ts
index 1cf599050..60b79689a 100644
--- a/ts/lib/dom.ts
+++ b/ts/lib/dom.ts
@@ -110,6 +110,9 @@ export function nodeContainsInlineContent(node: Node): boolean {
return true;
}
+/**
+ * Consumes the input fragment.
+ */
export function fragmentToString(fragment: DocumentFragment): string {
const fragmentDiv = document.createElement("div");
fragmentDiv.appendChild(fragment);