diff --git a/ts/editable/decorated.ts b/ts/editable/decorated.ts index 760e75208..8df02479e 100644 --- a/ts/editable/decorated.ts +++ b/ts/editable/decorated.ts @@ -38,13 +38,37 @@ export interface DecoratedElementConstructor toUndecorated(stored: string): string; } -export class CustomElementArray< - T extends CustomElementConstructor & WithTagName, -> extends Array { - push(...elements: T[]): number { +export class CustomElementArray extends Array { + push(...elements: DecoratedElementConstructor[]): number { for (const element of elements) { customElements.define(element.tagName, element); } return super.push(...elements); } + + /** + * Transforms any decorated elements in input HTML from undecorated to stored state. + */ + toStored(html: string): string { + let result = html; + + for (const element of this) { + result = element.toStored(result); + } + + return result; + } + + /** + * Transforms any decorated elements in input HTML from stored to undecorated state. + */ + toUndecorated(html: string): string { + let result = html; + + for (const element of this) { + result = element.toUndecorated(result); + } + + return result; + } } diff --git a/ts/editable/mathjax-element.ts b/ts/editable/mathjax-element.ts index 73610c34f..1cfbcfdba 100644 --- a/ts/editable/mathjax-element.ts +++ b/ts/editable/mathjax-element.ts @@ -15,6 +15,14 @@ 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 @@ -22,7 +30,7 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax static tagName = "anki-mathjax"; static toStored(undecorated: string): string { - return undecorated.replace( + const stored = undecorated.replace( mathjaxTagPattern, (_match: string, block: string | undefined, text: string) => { return typeof block === "string" && block !== "false" @@ -30,20 +38,20 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax : `\\(${text}\\)`; }, ); + + return stored; } static toUndecorated(stored: string): string { return stored - .replace( - mathjaxBlockDelimiterPattern, - (_match: string, text: string) => - `<${Mathjax.tagName} block="true">${text}`, - ) - .replace( - mathjaxInlineDelimiterPattern, - (_match: string, text: string) => - `<${Mathjax.tagName}>${text}`, - ); + .replace(mathjaxBlockDelimiterPattern, (_match: string, text: string) => { + const escaped = translateEntitiesToMathjax(text); + return `<${Mathjax.tagName} block="true">${escaped}`; + }) + .replace(mathjaxInlineDelimiterPattern, (_match: string, text: string) => { + const escaped = translateEntitiesToMathjax(text); + return `<${Mathjax.tagName}>${escaped}`; + }); } block = false; @@ -76,6 +84,9 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax break; case "data-mathjax": + if (!newValue) { + return; + } this.component?.$set({ mathjax: newValue }); break; } diff --git a/ts/editor/CodeMirror.svelte b/ts/editor/CodeMirror.svelte index 3b2ab8136..755365544 100644 --- a/ts/editor/CodeMirror.svelte +++ b/ts/editor/CodeMirror.svelte @@ -3,92 +3,84 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -->
-