From 5129aca66f92bc9587b2f0beab19632f2186e6a5 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Wed, 4 Aug 2021 01:55:39 +0200 Subject: [PATCH] Implement MathjaxBlock --- ts/editable/BUILD.bazel | 3 +- ts/editable/MathjaxBlock.svelte | 13 +++++++ ts/editable/mathjax-components.ts | 50 +++++++++++++++++++++++++++ ts/editable/mathjax.ts | 38 +++++++++++++++++++++ ts/editor/BUILD.bazel | 1 + ts/editor/index.ts | 5 +-- ts/licenses.json | 57 +++++++++++++++++++++++++++++++ ts/package.json | 1 + ts/yarn.lock | 49 ++++++++++++++++++++++++++ 9 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 ts/editable/MathjaxBlock.svelte create mode 100644 ts/editable/mathjax-components.ts create mode 100644 ts/editable/mathjax.ts diff --git a/ts/editable/BUILD.bazel b/ts/editable/BUILD.bazel index fa22483a0..e2dc0b775 100644 --- a/ts/editable/BUILD.bazel +++ b/ts/editable/BUILD.bazel @@ -49,6 +49,7 @@ ts_library( "//ts/components", "//ts:image_module_support", "@npm//svelte", + "@npm//mathjax-full", ] + svelte_names, ) @@ -63,7 +64,7 @@ esbuild( output_css = "editable-build.css", visibility = ["//visibility:public"], deps = [ - "editable_ts", + "editable", "svelte_components", "//ts/components", "//ts/components:svelte_components", diff --git a/ts/editable/MathjaxBlock.svelte b/ts/editable/MathjaxBlock.svelte new file mode 100644 index 000000000..ba78b659a --- /dev/null +++ b/ts/editable/MathjaxBlock.svelte @@ -0,0 +1,13 @@ + + + +{@html converted} diff --git a/ts/editable/mathjax-components.ts b/ts/editable/mathjax-components.ts new file mode 100644 index 000000000..9b905b376 --- /dev/null +++ b/ts/editable/mathjax-components.ts @@ -0,0 +1,50 @@ +import MathjaxBlock_svelte from "./MathjaxBlock.svelte"; + +class MathjaxBlock extends HTMLElement { + connectedCallback() { + this.contentEditable = "false"; + + const mathjax = (this.dataset.mathjax = this.innerText); + this.innerHTML = ""; + + new MathjaxBlock_svelte({ + target: this, + props: { mathjax }, + }); + } +} + +// customElements.define("anki-mathjax-inline", MathjaxInline); +customElements.define("anki-mathjax-block", MathjaxBlock); + +const mathjaxInlineTagPattern = + /(.*?)<\/anki-mathjax-inline>/gsu; +const mathjaxBlockTagPattern = /(.*?)<\/anki-mathjax-block>/gsu; + +export function toMathjaxDelimiters(html: string): string { + return html + .replace( + mathjaxInlineTagPattern, + (_match: string, text: string) => `\(${text}\)` + ) + .replace( + mathjaxBlockTagPattern, + (_match: string, text: string) => `\[${text}\]` + ); +} + +const mathjaxInlineDelimiterPattern = /\\\((.*?)\\\)/gsu; +const mathjaxBlockDelimiterPattern = /\\\[(.*?)\\\]/gsu; + +export function toMathjaxTags(html: string): string { + return html + .replace( + mathjaxInlineDelimiterPattern, + (_match: string, text: string) => + `${text}` + ) + .replace( + mathjaxBlockDelimiterPattern, + (_match: string, text: string) => `${text}` + ); +} diff --git a/ts/editable/mathjax.ts b/ts/editable/mathjax.ts new file mode 100644 index 000000000..556e64dcf --- /dev/null +++ b/ts/editable/mathjax.ts @@ -0,0 +1,38 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +import { mathjax } from "mathjax-full/js/mathjax"; +import { TeX } from "mathjax-full/js/input/tex"; +import { CHTML } from "mathjax-full/js/output/chtml"; +import { HTMLAdaptor } from "mathjax-full/js/adaptors/HTMLAdaptor"; +import { RegisterHTMLHandler } from "mathjax-full/js/handlers/html"; + +import { AllPackages } from "mathjax-full/js/input/tex/AllPackages.js"; +import "mathjax-full/js/util/entities/all"; + +// @ts-expect-error Minor interface mismatch: document.documentElement.nodeValue might be null +const adaptor = new HTMLAdaptor(window); +RegisterHTMLHandler(adaptor); + +const texOptions = { + displayMath: [["\\[", "\\]"]], + processRefs: false, + processEnvironments: false, + processEscapes: false, + packages: AllPackages, +}; + +export function convertMathjax(input: string): string { + const tex = new TeX(texOptions); + const chtml = new CHTML({ + fontURL: "/_anki/js/vendor/mathjax/output/chtml/fonts/woff-v2", + }); + + const html = mathjax.document(input, { InputJax: tex, OutputJax: chtml }); + html.render(); + + return ( + adaptor.innerHTML(adaptor.head(html.document)) + + adaptor.innerHTML(adaptor.body(html.document)) + ); +} diff --git a/ts/editor/BUILD.bazel b/ts/editor/BUILD.bazel index e2ff25dae..a358c6358 100644 --- a/ts/editor/BUILD.bazel +++ b/ts/editor/BUILD.bazel @@ -148,6 +148,7 @@ esbuild( "//ts/editable", "//ts/components", "//ts/components:svelte_components", + "//ts/editable:svelte_components", "@npm//protobufjs", ], ) diff --git a/ts/editor/index.ts b/ts/editor/index.ts index 0d55e318b..cc4b32317 100644 --- a/ts/editor/index.ts +++ b/ts/editor/index.ts @@ -22,13 +22,14 @@ import { saveField } from "./change-timer"; import "./fields.css"; -import "editable/editable"; -import "editable/editable-container"; import "./label-container"; import "./codable"; import "./editor-field"; import type { EditorField } from "./editor-field"; import { EditingArea } from "./editing-area"; +import "editable/editable-container"; +import "editable/editable"; +import "editable/mathjax-components"; import { initToolbar, fieldFocused } from "./toolbar"; import { initTagEditor } from "./tag-editor"; diff --git a/ts/licenses.json b/ts/licenses.json index 2f177c860..af0913912 100644 --- a/ts/licenses.json +++ b/ts/licenses.json @@ -164,6 +164,14 @@ "path": "node_modules/commander", "licenseFile": "node_modules/commander/LICENSE" }, + "commander@8.1.0": { + "licenses": "MIT", + "repository": "https://github.com/tj/commander.js", + "publisher": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "path": "node_modules/speech-rule-engine/node_modules/commander", + "licenseFile": "node_modules/speech-rule-engine/node_modules/commander/LICENSE" + }, "css-browser-selector@0.6.5": { "licenses": "CC-BY-SA-2.5", "repository": "https://github.com/verbatim/css_browser_selector", @@ -426,6 +434,14 @@ "path": "node_modules/delaunator", "licenseFile": "node_modules/delaunator/LICENSE" }, + "esm@3.2.25": { + "licenses": "MIT", + "repository": "https://github.com/standard-things/esm", + "publisher": "John-David Dalton", + "email": "john.david.dalton@gmail.com", + "path": "node_modules/esm", + "licenseFile": "node_modules/esm/LICENSE" + }, "iconv-lite@0.6.3": { "licenses": "MIT", "repository": "https://github.com/ashtuchkin/iconv-lite", @@ -489,12 +505,31 @@ "path": "node_modules/marked", "licenseFile": "node_modules/marked/LICENSE.md" }, + "mathjax-full@3.2.0": { + "licenses": "Apache-2.0", + "repository": "https://github.com/mathjax/Mathjax-src", + "path": "node_modules/mathjax-full", + "licenseFile": "node_modules/mathjax-full/LICENSE" + }, "mathjax@3.1.4": { "licenses": "Apache-2.0", "repository": "https://github.com/mathjax/MathJax", "path": "node_modules/mathjax", "licenseFile": "node_modules/mathjax/LICENSE" }, + "mhchemparser@4.1.1": { + "licenses": "Apache-2.0", + "repository": "https://github.com/mhchem/mhchemParser", + "publisher": "Martin Hensel", + "path": "node_modules/mhchemparser", + "licenseFile": "node_modules/mhchemparser/LICENSE.txt" + }, + "mj-context-menu@0.6.1": { + "licenses": "Apache-2.0", + "repository": "https://github.com/zorkow/context-menu", + "path": "node_modules/mj-context-menu", + "licenseFile": "node_modules/mj-context-menu/README.md" + }, "protobufjs@6.11.2": { "licenses": "BSD-3-Clause", "repository": "https://github.com/protobufjs/protobuf.js", @@ -526,6 +561,28 @@ "url": "https://github.com/ChALkeR", "path": "node_modules/safer-buffer", "licenseFile": "node_modules/safer-buffer/LICENSE" + }, + "speech-rule-engine@3.3.3": { + "licenses": "Apache-2.0", + "repository": "https://github.com/zorkow/speech-rule-engine", + "path": "node_modules/speech-rule-engine", + "licenseFile": "node_modules/speech-rule-engine/LICENSE" + }, + "wicked-good-xpath@1.3.0": { + "licenses": "MIT", + "repository": "https://github.com/google/wicked-good-xpath", + "publisher": "Google Inc.", + "path": "node_modules/wicked-good-xpath", + "licenseFile": "node_modules/wicked-good-xpath/LICENSE" + }, + "xmldom-sre@0.1.31": { + "licenses": "MIT*", + "repository": "https://github.com/zorkow/xmldom", + "publisher": "jindw", + "email": "jindw@xidea.org", + "url": "http://www.xidea.org", + "path": "node_modules/xmldom-sre", + "licenseFile": "node_modules/xmldom-sre/LICENSE" } } diff --git a/ts/package.json b/ts/package.json index 1b68e2934..24722dcfc 100644 --- a/ts/package.json +++ b/ts/package.json @@ -72,6 +72,7 @@ "lodash-es": "^4.17.21", "marked": "=2.0.5", "mathjax": "^3.1.2", + "mathjax-full": "^3.2.0", "protobufjs": "^6.10.2" }, "resolutions": { diff --git a/ts/yarn.lock b/ts/yarn.lock index d2dd309de..a403e4680 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -1596,6 +1596,11 @@ commander@7: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@>=7.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.1.0.tgz#db36e3e66edf24ff591d639862c6ab2c52664362" + integrity sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2154,6 +2159,11 @@ eslint@^7.24.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +esm@^3.2.25: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== + espree@^7.3.0, espree@^7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" @@ -3356,6 +3366,16 @@ marked@=2.0.5, marked@^2.0.3: resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.5.tgz#2d15c759b9497b0e7b5b57f4c2edabe1002ef9e7" integrity sha512-yfCEUXmKhBPLOzEC7c+tc4XZdIeTdGoRCZakFMkCxodr7wDXqoapIME4wjcpBPJLNyUnKJ3e8rb8wlAgnLnaDw== +mathjax-full@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mathjax-full/-/mathjax-full-3.2.0.tgz#e53269842a943d4df10502937518991268996c5c" + integrity sha512-D2EBNvUG+mJyhn+M1C858k0f2Fc4KxXvbEX2WCMXroV10212JwfYqaBJ336ECBSz5X9L5LRoamxb7AJtg3KaJA== + dependencies: + esm "^3.2.25" + mhchemparser "^4.1.0" + mj-context-menu "^0.6.1" + speech-rule-engine "^3.3.3" + mathjax@^3.1.2: version "3.1.4" resolved "https://registry.yarnpkg.com/mathjax/-/mathjax-3.1.4.tgz#4e8932d12845c0abae8b7f1976ea98cb505e8420" @@ -3376,6 +3396,11 @@ merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +mhchemparser@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/mhchemparser/-/mhchemparser-4.1.1.tgz#a2142fdab37a02ec8d1b48a445059287790becd5" + integrity sha512-R75CUN6O6e1t8bgailrF1qPq+HhVeFTM3XQ0uzI+mXTybmphy3b6h4NbLOYhemViQ3lUs+6CKRkC3Ws1TlYREA== + micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" @@ -3418,6 +3443,11 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +mj-context-menu@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/mj-context-menu/-/mj-context-menu-0.6.1.tgz#a043c5282bf7e1cf3821de07b13525ca6f85aa69" + integrity sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA== + mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -4094,6 +4124,15 @@ spdx-satisfies@^5.0.0: spdx-expression-parse "^3.0.0" spdx-ranges "^2.0.0" +speech-rule-engine@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/speech-rule-engine/-/speech-rule-engine-3.3.3.tgz#781ed03cbcf3279f94d1d80241025ea954c6d571" + integrity sha512-0exWw+0XauLjat+f/aFeo5T8SiDsO1JtwpY3qgJE4cWt+yL/Stl0WP4VNDWdh7lzGkubUD9lWP4J1ASnORXfyQ== + dependencies: + commander ">=7.0.0" + wicked-good-xpath "^1.3.0" + xmldom-sre "^0.1.31" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -4505,6 +4544,11 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wicked-good-xpath@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz#81b0e95e8650e49c94b22298fff8686b5553cf6c" + integrity sha1-gbDpXoZQ5JyUsiKY//hoa1VTz2w= + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -4554,6 +4598,11 @@ xmlcreate@^2.0.3: resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.3.tgz#df9ecd518fd3890ab3548e1b811d040614993497" integrity sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ== +xmldom-sre@^0.1.31: + version "0.1.31" + resolved "https://registry.yarnpkg.com/xmldom-sre/-/xmldom-sre-0.1.31.tgz#10860d5bab2c603144597d04bf2c4980e98067f4" + integrity sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"