diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3d4e9f906..d820ce859 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,7 +5,7 @@ on: types: [published] jobs: - setup: + publish: runs-on: ubuntu-latest name: Build and publish MathJax permissions: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a0da0f71..8c8d6a72e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ on: - develop jobs: - setup: + testing: runs-on: ubuntu-latest name: Compile and test MathJax steps: @@ -22,6 +22,12 @@ jobs: version: 10 run_install: false + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: 'pnpm' + - name: Install packages run: | pnpm -r i diff --git a/.gitignore b/.gitignore index b16c8614b..31f8b3162 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ coverage /bundle /bundle-cjs /testsuite/js +/testsuite/lib/localstorage /lab/sre.js diff --git a/README.md b/README.md index 9db824357..286e6797c 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,35 @@ npm run --silent build-all ``` in order to compile the JavaScript files from the TypeScript source, -and build the component files from the JavaScript files. +and build the component files from the JavaScript files. Windows +users will need to use the command + +``` bash +npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe" +``` + +first in order to tell `pnpm` to use the `bash` shell for scripts that +it runs, as that is required by the build scripts that MathJax defines +in the `package.json` file. You may also need to use + +``` bash +Set-ExecutionPolicy Unrestricted +``` + +to allow the scripts to run, if you receive errors about not being +able to run the scripts. + +The build process requires MathJax to set up a symbolic link, and in +Windows, that requires permission, so you may receive an error message +to that effect. If so, you may need to run + +``` bash +pnpm link:src +``` + +from a shell with administrator privileges. Once that is done, you +can run the build process from a non-administrator shell. + ## Code Contributions diff --git a/components/bin/build b/components/bin/build index 68360eac4..75ebd66e4 100755 --- a/components/bin/build +++ b/components/bin/build @@ -61,6 +61,9 @@ process.chdir(path.dirname(json)); const mjPath = path.relative(process.cwd(), path.resolve(__dirname, '..', '..', target)); const mjGlobal = path.join('..', mjPath, 'components', 'global.js'); +/** + * Determine the module type + */ function getType() { const component = config.component || 'part'; if (component.match(/\/(svg|chtml|common)\/fonts\//)) return RegExp.$1 + '-font'; @@ -69,6 +72,13 @@ function getType() { return component; } +/** + * Convert Windows paths to unix paths + */ +const normalize = process.platform === 'win32' + ? (file) => file.replace(/\\/g, '/') + : (file) => file; + /** * Extract the configuration values */ @@ -100,7 +110,7 @@ let PACKAGE = []; */ function processList(base, dir, list, top = true) { for (const item of list) { - const file = path.join(dir, item); + const file = normalize(path.join(dir, item)); if (!EXCLUDE.has(file)) { const stat = fs.statSync(path.resolve(base, file)); if (stat.isDirectory()) { @@ -183,9 +193,9 @@ function processParts(parts) { function processLines(file, objects) { if (objects.length === 0) return []; const base = path.dirname(file).replace(/^\.$/, ''); - const dir = (PREFIX ? path.join(PREFIX, base) : base); + const dir = (PREFIX ? normalize(path.join(PREFIX, base)) : base); const dots = dir.replace(/[^\/]+/g, '..') || '.'; - const relative = path.join(dots, '..', JS, dir, path.basename(file)).replace(/\.ts$/, '.js'); + const relative = normalize(path.join(dots, '..', JS, dir, path.basename(file))).replace(/\.ts$/, '.js'); const name = path.parse(file).name; const lines = (target === 'mjs' ? [] : [ '"use strict";', @@ -254,7 +264,7 @@ function getExtraDirectories() { let prefix = ''; let indent = INDENT; let postfix = ''; - for (let name of PREFIX.split(/\//)) { + for (let name of PREFIX.split('/')) { if (name.match(/[^a-zA-Z0-9]/)) { name = `"${name}"`; } @@ -271,19 +281,19 @@ function getExtraDirectories() { function processGlobal() { console.info(' ' + COMPONENT + '.ts'); const lines = (target === 'cjs' ? [ - `const {combineWithMathJax} = require('${GLOBAL}')`, - `const {VERSION} = require('${VERSION}');`, + `const {combineWithMathJax} = require('${normalize(GLOBAL)}')`, + `const {VERSION} = require('${normalize(VERSION)}');`, '', ] : [ - `import {combineWithMathJax} from '${GLOBAL}';`, - `import {VERSION} from '${VERSION}';`, + `import {combineWithMathJax} from '${normalize(GLOBAL)}';`, + `import {VERSION} from '${normalize(VERSION)}';`, '', ]); const [prefix, indent, postfix] = getExtraDirectories(); const packages = []; PACKAGE = PACKAGE.sort(sortDir); while (PACKAGE.length) { - const dir = path.dirname(PACKAGE[0]).split(path.sep)[0]; + const dir = path.dirname(PACKAGE[0]).split('/')[0]; packages.push(processPackage(lines, indent, dir)); } const name = (ID.match(/[^a-zA-Z0-9_]/) ? `"${ID}"` : ID); @@ -337,7 +347,7 @@ function processPackage(lines, space, dir) { if (path.dirname(PACKAGE[0]) === dir) { const file = PACKAGE.shift(); const name = path.basename(file); - let relativefile = path.join('..', JS, dir, name).replace(/\.ts$/, '.js') + const relativefile = normalize(path.join('..', JS, dir, name).replace(/\.ts$/, '.js')); const component = 'module' + (++importCount); lines.push( target === 'cjs' ? diff --git a/components/bin/makeAll b/components/bin/makeAll index b661d3696..8eafa678c 100755 --- a/components/bin/makeAll +++ b/components/bin/makeAll @@ -124,6 +124,13 @@ function fileRegExp(name) { return new RegExp(name.replace(/([\\.{}[\]()?*^$])/g, '\\$1'), 'g'); } +/** + * Options for the execSync() function + */ +const execOptions = process.platform === 'win32' + ? { shell: `${process.env.ProgramFiles}\\Git\\bin\\bash.exe` } + : {}; + /** * Get the current working directory */ @@ -196,7 +203,7 @@ function processSubdirs(dir, action, config) { * Run a command on a given directory */ function run(cmd, dir) { - return execSync(cmd + ` '${path.relative('.', dir).replace(/'/g, '\\\'')}'`); + return execSync(cmd + ` '${path.relative('.', dir).replace(/'/g, '\\\'')}'`, execOptions); } /** diff --git a/components/bin/pack b/components/bin/pack index ebcf8a214..4a91ab5de 100755 --- a/components/bin/pack +++ b/components/bin/pack @@ -25,7 +25,7 @@ const fs = require('fs'); const path = require('path'); -const {spawn, execSync} = require('child_process'); +const {spawn} = require('child_process'); /** * The module type to use ('cjs' or 'mjs') @@ -64,14 +64,7 @@ const rootRE = fileRegExp(path.dirname(jsPath)); const nodeRE = /^.*\/node_modules/; const fontRE = new RegExp('^.*\\/(mathjax-[^\/-]*)(?:-font)?\/(build|[cm]js)'); -/** - * Find the directory where npx runs (so we know where "npx webpack" will run) - * (We use npx rather than pnpm here as it seems that pnpm doesn't - * find the executable from a node_modules directory higher than the - * first package.json, and extensions and fonts can have their own - * package.json.) - */ -const packDir = String(execSync('npx node -e "console.log(process.cwd())"')); +const packDir = process.cwd(); /** * @param {string} dir The directory to pack @@ -80,11 +73,20 @@ const packDir = String(execSync('npx node -e "console.log(process.cwd())"')); async function readJSON(dir) { return new Promise((ok, fail) => { const buffer = []; - const child = spawn('npx', [ - 'webpack', '--env', `dir=${path.relative(packDir, path.resolve(dir))}`, - '--env', `bundle=${bundle}`, '--json', - '-c', path.relative(packDir, path.join(compPath, 'webpack.config.' + target)) - ]); + const child = spawn( + 'npx', + [ + 'webpack', + '--env', `dir=${path.relative(packDir, path.resolve(dir))}`, + '--env', `bundle=${bundle}`, + '--json', + '-c', path.relative(packDir, path.join(compPath, 'webpack.config.' + target)) + ], + { + cwd: packDir, + shell: true, + } + ); child.stdout.on('data', (data) => buffer.push(String(data))); child.stderr.on('data', (data) => console.error(String(data))); child.on('close', (code) => { diff --git a/components/mjs/a11y/speech/speech.js b/components/mjs/a11y/speech/speech.js index a4c474b4d..4f6aae494 100644 --- a/components/mjs/a11y/speech/speech.js +++ b/components/mjs/a11y/speech/speech.js @@ -2,20 +2,20 @@ import './lib/speech.js'; import {combineDefaults} from '#js/components/global.js'; import {Package} from '#js/components/package.js'; -import {hasWindow} from '#js/util/context.js'; +import {context} from '#js/util/context.js'; import {SpeechHandler} from '#js/a11y/speech.js'; if (MathJax.loader) { let path = Package.resolvePath('[sre]', false); let maps = Package.resolvePath('[mathmaps]', false); - if (hasWindow) { + if (context.window) { path = new URL(path, location).href; maps = new URL(maps, location).href; } else { const REQUIRE = typeof require !== 'undefined' ? require : MathJax.config.loader.require; if (REQUIRE?.resolve) { - path = REQUIRE.resolve(`${path}/require.mjs`).replace(/\/[^\/]*$/, ''); - maps = REQUIRE.resolve(`${maps}/base.json`).replace(/\/[^\/]*$/, ''); + path = context.path(REQUIRE.resolve(`${path}/require.mjs`)).replace(/\/[^\/]*$/, ''); + maps = context.path(REQUIRE.resolve(`${maps}/base.json`)).replace(/\/[^\/]*$/, ''); } else { path = maps = ''; } diff --git a/components/mjs/a11y/util.js b/components/mjs/a11y/util.js index 6e2096ba7..dc1efd5df 100644 --- a/components/mjs/a11y/util.js +++ b/components/mjs/a11y/util.js @@ -9,5 +9,6 @@ Loader.preLoaded( 'a11y/sre', 'a11y/semantic-enrich', 'a11y/speech', - 'a11y/explorer' + 'a11y/explorer', + 'input/mml', ); diff --git a/components/mjs/core/config.json b/components/mjs/core/config.json index 7f032dbc4..87e95dc00 100644 --- a/components/mjs/core/config.json +++ b/components/mjs/core/config.json @@ -4,6 +4,8 @@ "targets": [ "mathjax.ts", "core", "util", "handlers", + "ui/dialog/DraggableDialog.ts", + "ui/dialog/InfoDialog.ts", "adaptors/HTMLAdaptor.ts", "adaptors/browserAdaptor.ts", "components/global.ts" diff --git a/components/mjs/input/tex/extension.js b/components/mjs/input/tex/extension.js index 50e7f67a5..84c7a4ef3 100644 --- a/components/mjs/input/tex/extension.js +++ b/components/mjs/input/tex/extension.js @@ -1,13 +1,13 @@ import {combineDefaults} from '#js/components/global.js'; -import {hasWindow} from '#js/util/context.js'; -export function fontExtension(id, name, pkg = `@mathjax/${name}`) { +export function fontExtension(id, name, pkg = `[fonts]/${name}`) { if (MathJax.loader) { - const FONTPATH = hasWindow ? `https://cdn.jsdelivr.net/npm/${pkg}` : pkg; const path = name.replace(/-font-extension$/, '-extension'); const jax = (MathJax.config?.startup?.output || 'chtml'); - combineDefaults(MathJax.config.loader, 'paths', {[path]: FONTPATH}); - combineDefaults(MathJax.config.loader, 'dependencies', {[`[${path}]/${jax}`]: [`output/${jax}`]}); + combineDefaults(MathJax.config.loader, 'paths', {[path]: pkg}); + if (!MathJax._.output?.[jax]) { + combineDefaults(MathJax.config.loader, 'dependencies', {[`[${path}]/${jax}`]: [`output/${jax}`]}); + } MathJax.loader.addPackageData(id, { extraLoads: [`[${path}]/${jax}`], rendererExtensions: [path] diff --git a/components/mjs/node-main/config.json b/components/mjs/node-main/config.json index 28cd82bb6..da639e122 100644 --- a/components/mjs/node-main/config.json +++ b/components/mjs/node-main/config.json @@ -5,7 +5,7 @@ "copy": [ "node-main.mjs", "node-main.cjs", - "node-main-setup.mjs" + "node-main-setup.cjs" ] }, "webpack": { diff --git a/components/mjs/node-main/node-main-setup.cjs b/components/mjs/node-main/node-main-setup.cjs new file mode 100644 index 000000000..293652c51 --- /dev/null +++ b/components/mjs/node-main/node-main-setup.cjs @@ -0,0 +1,5 @@ +global.require = require; +const path = require("path"); + +if (!global.MathJax) global.MathJax = {}; +global.MathJax.__dirname = __dirname; diff --git a/components/mjs/node-main/node-main-setup.mjs b/components/mjs/node-main/node-main-setup.mjs deleted file mode 100644 index f143f4233..000000000 --- a/components/mjs/node-main/node-main-setup.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import {createRequire} from 'module'; -global.require = createRequire(import.meta.url); - -const path = require("path"); - -if (!global.MathJax) global.MathJax = {}; -global.MathJax.__dirname = path.dirname(new URL(import.meta.url).pathname); diff --git a/components/mjs/node-main/node-main.js b/components/mjs/node-main/node-main.js index 13b635173..e09f18270 100644 --- a/components/mjs/node-main/node-main.js +++ b/components/mjs/node-main/node-main.js @@ -23,6 +23,7 @@ import '../startup/init.js'; import {Loader, CONFIG} from '#js/components/loader.js'; import {Package} from '#js/components/package.js'; import {combineDefaults, combineConfig} from '#js/components/global.js'; +import {context} from '#js/util/context.js'; import '../core/core.js'; import '../adaptors/liteDOM/liteDOM.js'; import {source} from '../source.js'; @@ -30,7 +31,7 @@ import {source} from '../source.js'; const MathJax = global.MathJax; const path = eval('require("path")'); // get path from node, not webpack -const dir = MathJax.config.__dirname; // set up by node-main.mjs or node-main.cjs +const dir = context.path(MathJax.config.__dirname); // set up by node-main.mjs or node-main.cjs /* * Set up the initial configuration diff --git a/components/mjs/node-main/node-main.mjs b/components/mjs/node-main/node-main.mjs index a6eb7be34..0276610be 100644 --- a/components/mjs/node-main/node-main.mjs +++ b/components/mjs/node-main/node-main.mjs @@ -1,4 +1,4 @@ -import './node-main-setup.mjs'; +import './node-main-setup.cjs'; import {MathJax} from './node-main.js'; export default MathJax; export const init = MathJax.init; diff --git a/components/mjs/output/util.js b/components/mjs/output/util.js index 31d964747..9b462a0ec 100644 --- a/components/mjs/output/util.js +++ b/components/mjs/output/util.js @@ -2,9 +2,25 @@ import {combineDefaults, combineWithMathJax} from '#js/components/global.js'; import {Package} from '#js/components/package.js'; import {hasWindow} from '#js/util/context.js'; -export const FONTPATH = hasWindow ? - 'https://cdn.jsdelivr.net/npm/@mathjax/%%FONT%%-font': - '@mathjax/%%FONT%%-font'; +export function configFont(font, jax, config, extension = '') { + const path = (config.fontPath || `[fonts]/%%FONT%%-font${extension}`); + const name = (font.match(/^[a-z]+:/) ? (font.match(/[^/:\\]*$/) || [jax])[0] : font); + combineDefaults(MathJax.config.loader, 'paths', { + [name+extension]: (name === font ? path.replace(/%%FONT%%/g, font) : font) + }); + return `[${name}${extension}]`; +} + +export function configExtensions(jax, config) { + const extensions = []; + for (const name of (config.fontExtensions || [])) { + const font = configFont(name, jax, config, '-extension'); + const module = `${font}/${jax}` + extensions.push(module); + } + return extensions; +} + export const OutputUtil = { config(jax, jaxClass, defaultFont, fontClass) { @@ -20,15 +36,15 @@ export const OutputUtil = { } if (font.charAt(0) !== '[') { - const path = (config.fontPath || FONTPATH); - const name = (font.match(/^[a-z]+:/) ? (font.match(/[^/:\\]*$/) || [jax])[0] : font); - combineDefaults(MathJax.config.loader, 'paths', { - [name]: (name === font ? path.replace(/%%FONT%%/g, font) : font) - }); - font = `[${name}]`; + font = configFont(font, jax, config); } const name = font.substring(1, font.length - 1); + const extensions = configExtensions(jax, config); + if (extensions.length) { + MathJax.loader.addPackageData(`${font}/${jax}`, {extraLoads: extensions}); + } + if (name !== defaultFont || !fontClass) { MathJax.loader.addPackageData(`output/${jax}`, {extraLoads: [`${font}/${jax}`]}); diff --git a/components/mjs/source-lab.js b/components/mjs/source-lab.js index 708290300..d7455ab4a 100644 --- a/components/mjs/source-lab.js +++ b/components/mjs/source-lab.js @@ -15,4 +15,4 @@ * limitations under the License. */ -export const src = String(new URL('.', import.meta.url)).replace(/\/$/, ''); +export const dirname = String(new URL('.', import.meta.url)).replace(/\/$/, ''); diff --git a/components/mjs/source.cjs b/components/mjs/source.cjs index db7fddf94..bd1f9dd24 100644 --- a/components/mjs/source.cjs +++ b/components/mjs/source.cjs @@ -15,4 +15,4 @@ * limitations under the License. */ -module.exports.src = __dirname; +module.exports.dirname = __dirname; diff --git a/components/mjs/source.d.cts b/components/mjs/source.d.cts index 93e9f7a7c..363367412 100644 --- a/components/mjs/source.d.cts +++ b/components/mjs/source.d.cts @@ -1 +1 @@ -export declare const src: string; +export declare const dirname: string; diff --git a/components/mjs/source.js b/components/mjs/source.js index e632cbf88..ab4e056da 100644 --- a/components/mjs/source.js +++ b/components/mjs/source.js @@ -15,7 +15,9 @@ * limitations under the License. */ -import {src} from '#source/source.cjs'; +import {dirname} from '#source/source.cjs'; +import {context} from '#js/util/context.js'; +const src = context.path(dirname); export const source = { 'core': `${src}/core/core.js`, diff --git a/components/mjs/startup/hasown.js b/components/mjs/startup/hasown.js new file mode 100644 index 000000000..b886b536b --- /dev/null +++ b/components/mjs/startup/hasown.js @@ -0,0 +1,10 @@ +import {MathJax} from '#js/components/global.js'; + +if (!Object.hasOwn && MathJax.config.startup.polyfillHasOwn) { + Object.hasOwn = function (el, prop) { + if (typeof el === 'undefined' || el === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + return Object.prototype.hasOwnProperty.call(Object(el), prop); + } +} diff --git a/components/mjs/startup/init.js b/components/mjs/startup/init.js index f34d29799..558f1af4c 100644 --- a/components/mjs/startup/init.js +++ b/components/mjs/startup/init.js @@ -1,3 +1,4 @@ +import './hasown.js'; // Can be removed with ES2024 implementation of Object.hasown import './lib/startup.js'; import {combineDefaults} from '#js/components/global.js'; diff --git a/eslint.config.mjs b/eslint.config.mjs index 9f3f93010..4ad499b35 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -28,7 +28,9 @@ export default tseslint.config({ "@typescript-eslint/no-empty-object-type": ["error", {"allowInterfaces": "with-single-extends"}], "@typescript-eslint/no-unused-expressions": ["error", { "allowTernary": true }], "prefer-const": ["error", {"destructuring": "all"}], - "jsdoc/tag-lines": ["warn", "always", {"count": 0, "startLines": 1}] + "jsdoc/tag-lines": ["warn", "always", {"count": 0, "startLines": 1}], + "jsdoc/reject-any-type": "off", + "jsdoc/reject-function-type": "off" } }); diff --git a/package.json b/package.json index ef2dece39..c80f1d28d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mathjax/src", - "version": "4.0.0", + "version": "4.1.0", "description": "Beautiful and accessible math in all browsers. MathJax is an open-source JavaScript display engine for LaTeX, MathML, and AsciiMath notation that works in all browsers and in server-side node applications. This package includes the source code as well as the packaged components.", "keywords": [ "MathJax", @@ -86,9 +86,9 @@ "copy:mml3": "copy() { pnpm -s log:single 'Copying legacy code MathML3'; pnpm copyfiles -u 1 ts/input/mathml/mml3/mml3.sef.json $1; }; copy", "copy:pkg": "copy() { pnpm -s log:single \"Copying package.json to $1\"; pnpm copyfiles -u 2 components/bin/package.json $1; }; copy", "=============================================================================== log": "", - "log:comp": "log() { echo \\\\033[32m$1\\\\033[0m; }; log", - "log:header": "log() { echo '============='; echo $1; echo '============='; }; log", - "log:single": "log() { echo \\\\033[34m--$1\\\\033[0m; }; log", + "log:comp": "log() { echo \u001b[32m$1\u001b[0m; }; log", + "log:header": "log() { echo '\u001b[1m============='; echo $1; echo '=============\u001b[0m'; }; log", + "log:single": "log() { echo \u001b[94m--$1\u001b[0m; }; log", "=============================================================================== cjs": "", "cjs:build": "pnpm -s log:header 'Building cjs'; pnpm -s cjs:src:build && pnpm -s cjs:components:build", "cjs:bundle:clean": "pnpm clean:dir bundle-cjs", @@ -100,7 +100,7 @@ "cjs:components:compile": "pnpm -s log:single 'Compiling component files'; pnpm tsc --project tsconfig/components.json", "cjs:components:copy": "pnpm copyfiles -u 2 -e 'components/mjs/**/*.js' 'components/mjs/**/*' components/cjs", "cjs:components:finalize": "pnpm -s log:comp 'Finalize cjs components'; pnpm -s cjs:components:copy && pnpm -s copy:pkg components/cjs && pnpm -s clean:lib cjs", - "cjs:components:make": "make() { pnpm -s log:single 'Making cjs components'; components/bin/makeAll --cjs --terse --bundle-cjs $1 components/cjs; }; make", + "cjs:components:make": "make() { pnpm -s log:single 'Making cjs components'; node components/bin/makeAll --cjs --terse --bundle-cjs $1 components/cjs; }; make", "cjs:components:src:build": "pnpm -s log:comp 'Building cjs components sources'; pnpm cjs:components:clean && pnpm cjs:components:compile && pnpm cjs:components:finalize", "cjs:src:build": "pnpm -s log:comp 'Building cjs sources'; pnpm -s link:src && pnpm clean:dir cjs && pnpm -s cjs:compile && pnpm -s copy:assets cjs && pnpm -s copy:pkg cjs", "cjs:copy:components": "pnpm -s log:single 'Moving cjs files from components' && pnpm copyfiles -u 2 'components/mjs/**/*.cjs' 'components/mjs/**/*.d.cts' components/cjs", @@ -110,7 +110,7 @@ "mjs:bundle:finalize": "pnpm -s log:single 'Finalize mjs bundle'; echo '{\n \"type\": \"commonjs\"\n}' > bundle/package.json;", "mjs:compile": "pnpm -s log:single 'Compiling mjs typescript files'; pnpm tsc --project tsconfig/mjs.json && pnpm tsc --project tsconfig/worker.json", "mjs:components:build": "pnpm -s log:comp 'Compiling mjs component files'; pnpm clean:lib mjs && pnpm clean:dir bundle && pnpm mjs:components:make && pnpm mjs:bundle:finalize", - "mjs:components:make": "pnpm -s log:single 'Making mjs components'; components/bin/makeAll --mjs --terse components/mjs", + "mjs:components:make": "pnpm -s log:single 'Making mjs components'; node components/bin/makeAll --mjs --terse components/mjs", "mjs:src:build": "pnpm -s log:comp 'Building mjs sources'; pnpm -s link:src && pnpm -s clean:dir mjs && pnpm -s mjs:compile && pnpm -s copy:assets mjs", "=============================================================================== mml3": "", "mml3:make:xslt": "pnpm xslt3 -t -xsl:/tmp/mml3.xsl -export:ts/input/mathml/mml3/mml3.sef.json -nogo", @@ -132,32 +132,32 @@ "build-mjs": "pnpm -s mjs:build", "make-cjs-components": "pnpm -s cjs:components:make && pnpm -s cjs:bundle:finalize", "make-mjs-components": "pnpm -s mjs:components:make", - "make-one": "make() { components/bin/makeAll --no-subdirs $3 $4 --${2:-mjs} components/${2-:mjs}/$1; }; make", + "make-one": "make() { node components/bin/makeAll --no-subdirs $3 $4 --${2:-mjs} components/${2-:mjs}/$1; }; make", "make-components": "pnpm -s make-mjs-components", "compile": "pnpm -s compile-mjs", "build": "pnpm -s build-mjs", "build-all": "pnpm -s build-mjs ; echo ; pnpm -s build-cjs" }, "devDependencies": { - "@eslint/js": "^9.28.0", - "@xmldom/xmldom": "^0.8.10", + "@eslint/js": "^9.39.2", + "@xmldom/xmldom": "^0.8.11", "copyfiles": "^2.4.1", - "diff": "^5.2.0", - "eslint": "^9.28.0", - "eslint-formatter-unix": "^8.40.0", - "eslint-plugin-jsdoc": "^48.11.0", - "eslint-plugin-prettier": "^5.4.1", + "diff": "^8.0.2", + "eslint": "^9.39.2", + "eslint-formatter-unix": "^9.0.1", + "eslint-plugin-jsdoc": "^61.5.0", + "eslint-plugin-prettier": "^5.5.4", "husky": "^9.1.7", - "lint-staged": "^15.5.2", - "prettier": "^3.5.3", - "rimraf": "^5.0.10", - "terser-webpack-plugin": "^5.3.14", - "typedoc": "^0.28.5", - "typescript": "^5.8.3", - "typescript-eslint": "^8.33.1", + "lint-staged": "^16.2.7", + "prettier": "^3.7.4", + "rimraf": "^6.1.2", + "terser-webpack-plugin": "^5.3.16", + "typedoc": "^0.28.15", + "typescript": "^5.9.3", + "typescript-eslint": "^8.50.0", "typescript-tools": "^0.3.1", - "webpack": "^5.99.9", - "webpack-cli": "^5.1.4", + "webpack": "^5.103.0", + "webpack-cli": "^6.0.1", "wicked-good-xpath": "^1.3.0", "xslt3": "^2.7.0" }, @@ -168,9 +168,9 @@ ] }, "dependencies": { - "@mathjax/mathjax-newcm-font": "4.0.0", + "@mathjax/mathjax-newcm-font": "4.1.0", "mhchemparser": "^4.2.1", - "mj-context-menu": "^0.9.1", - "speech-rule-engine": "5.0.0-beta.1" + "mj-context-menu": "^1.0.0", + "speech-rule-engine": "5.0.0-beta.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7dfbced8e..22da6d912 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,75 +9,75 @@ importers: .: dependencies: '@mathjax/mathjax-newcm-font': - specifier: 4.0.0 - version: 4.0.0 + specifier: 4.1.0 + version: 4.1.0 mhchemparser: specifier: ^4.2.1 version: 4.2.1 mj-context-menu: - specifier: ^0.9.1 - version: 0.9.1 + specifier: ^1.0.0 + version: 1.0.0 speech-rule-engine: - specifier: 5.0.0-beta.1 - version: 5.0.0-beta.1 + specifier: 5.0.0-beta.3 + version: 5.0.0-beta.3 devDependencies: '@eslint/js': - specifier: ^9.28.0 - version: 9.28.0 + specifier: ^9.39.2 + version: 9.39.2 '@xmldom/xmldom': - specifier: ^0.8.10 - version: 0.8.10 + specifier: ^0.8.11 + version: 0.8.11 copyfiles: specifier: ^2.4.1 version: 2.4.1 diff: - specifier: ^5.2.0 - version: 5.2.0 + specifier: ^8.0.2 + version: 8.0.2 eslint: - specifier: ^9.28.0 - version: 9.28.0 + specifier: ^9.39.2 + version: 9.39.2 eslint-formatter-unix: - specifier: ^8.40.0 - version: 8.40.0 + specifier: ^9.0.1 + version: 9.0.1 eslint-plugin-jsdoc: - specifier: ^48.11.0 - version: 48.11.0(eslint@9.28.0) + specifier: ^61.5.0 + version: 61.5.0(eslint@9.39.2) eslint-plugin-prettier: - specifier: ^5.4.1 - version: 5.4.1(@types/eslint@9.6.1)(eslint@9.28.0)(prettier@3.5.3) + specifier: ^5.5.4 + version: 5.5.4(@types/eslint@9.6.1)(eslint@9.39.2)(prettier@3.7.4) husky: specifier: ^9.1.7 version: 9.1.7 lint-staged: - specifier: ^15.5.2 - version: 15.5.2 + specifier: ^16.2.7 + version: 16.2.7 prettier: - specifier: ^3.5.3 - version: 3.5.3 + specifier: ^3.7.4 + version: 3.7.4 rimraf: - specifier: ^5.0.10 - version: 5.0.10 + specifier: ^6.1.2 + version: 6.1.2 terser-webpack-plugin: - specifier: ^5.3.14 - version: 5.3.14(webpack@5.99.9) + specifier: ^5.3.16 + version: 5.3.16(webpack@5.103.0) typedoc: - specifier: ^0.28.5 - version: 0.28.5(typescript@5.8.3) + specifier: ^0.28.15 + version: 0.28.15(typescript@5.9.3) typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.33.1 - version: 8.33.1(eslint@9.28.0)(typescript@5.8.3) + specifier: ^8.50.0 + version: 8.50.0(eslint@9.39.2)(typescript@5.9.3) typescript-tools: specifier: ^0.3.1 version: 0.3.1 webpack: - specifier: ^5.99.9 - version: 5.99.9(webpack-cli@5.1.4) + specifier: ^5.103.0 + version: 5.103.0(webpack-cli@6.0.1) webpack-cli: - specifier: ^5.1.4 - version: 5.1.4(webpack@5.99.9) + specifier: ^6.0.1 + version: 6.0.1(webpack@5.103.0) wicked-good-xpath: specifier: ^1.3.0 version: 1.3.0 @@ -87,16 +87,20 @@ importers: packages: - '@discoveryjs/json-ext@0.5.7': - resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} - engines: {node: '>=10.0.0'} + '@discoveryjs/json-ext@0.6.3': + resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} + engines: {node: '>=14.17.0'} - '@es-joy/jsdoccomment@0.46.0': - resolution: {integrity: sha512-C3Axuq1xd/9VqFZpW4YAzOx5O9q/LP46uIQy/iNDpHG3fmPa6TBtvfglMCs3RBiBxAIi0Go97r8+jvTt55XMyQ==} - engines: {node: '>=16'} + '@es-joy/jsdoccomment@0.76.0': + resolution: {integrity: sha512-g+RihtzFgGTx2WYCuTHbdOXJeAlGnROws0TeALx9ow/ZmOROOZkVg5wp/B44n0WJgI4SQFP1eWM2iRPlU2Y14w==} + engines: {node: '>=20.11.0'} + + '@es-joy/resolve.exports@1.2.0': + resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} + engines: {node: '>=10'} - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -105,36 +109,36 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.20.0': - resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.2.2': - resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.14.0': - resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.28.0': - resolution: {integrity: sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.1': - resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@gerrit0/mini-shiki@3.7.0': - resolution: {integrity: sha512-7iY9wg4FWXmeoFJpUL2u+tsmh0d0jcEJHAIzVxl3TG4KL493JNnisdLAILZ77zcD+z3J0keEXZ+lFzUgzQzPDg==} + '@gerrit0/mini-shiki@3.20.0': + resolution: {integrity: sha512-Wa57i+bMpK6PGJZ1f2myxo3iO+K/kZikcyvH8NIqNNZhQUbDav7V9LQmWOXhf946mz5c1NZ19WMsGYiDKTryzQ==} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -156,9 +160,13 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} @@ -181,48 +189,32 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@mathjax/mathjax-newcm-font@4.0.0': - resolution: {integrity: sha512-kpsJgIF4FpWiwIkFgOPmWwy5GXfL25spmJJNg27HQxPddmEL8Blx0jn2BuU/nlwjM/9SnYpEfDrWiAMgLPlB8Q==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@pkgr/core@0.1.2': - resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@mathjax/mathjax-newcm-font@4.1.0': + resolution: {integrity: sha512-n10AwYubUa2hyOzxSRzkwRrgCVns083zkentryXICMPKaWT/watfvK2sUk5D9Bow9mpDfoqb5EWApuUvqnlzaw==} '@pkgr/core@0.2.7': resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@shikijs/engine-oniguruma@3.7.0': - resolution: {integrity: sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==} + '@shikijs/engine-oniguruma@3.20.0': + resolution: {integrity: sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ==} - '@shikijs/langs@3.7.0': - resolution: {integrity: sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==} + '@shikijs/langs@3.20.0': + resolution: {integrity: sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA==} - '@shikijs/themes@3.7.0': - resolution: {integrity: sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==} + '@shikijs/themes@3.20.0': + resolution: {integrity: sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ==} - '@shikijs/types@3.7.0': - resolution: {integrity: sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==} + '@shikijs/types@3.20.0': + resolution: {integrity: sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sindresorhus/base62@1.0.0': + resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} + engines: {node: '>=18'} + '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -244,63 +236,63 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@typescript-eslint/eslint-plugin@8.33.1': - resolution: {integrity: sha512-TDCXj+YxLgtvxvFlAvpoRv9MAncDLBV2oT9Bd7YBGC/b/sEURoOYuIwLI99rjWOfY3QtDzO+mk0n4AmdFExW8A==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.33.1 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.33.1': - resolution: {integrity: sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA==} + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.33.1': - resolution: {integrity: sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw==} + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.33.1': - resolution: {integrity: sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA==} + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.33.1': - resolution: {integrity: sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g==} + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.33.1': - resolution: {integrity: sha512-1cG37d9xOkhlykom55WVwG2QRNC7YXlxMaMzqw2uPeJixBFfKWZgaP/hjAObqMN/u3fr5BrTwTnc31/L9jQ2ww==} + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.33.1': - resolution: {integrity: sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==} + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.33.1': - resolution: {integrity: sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA==} + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.33.1': - resolution: {integrity: sha512-52HaBiEQUaRYqAXpfzWSR2U3gxk92Kw006+xZpElaPMg3C4PgM+A5LqwoQI1f9E5aZ/qlxAZxzm42WX+vn92SQ==} + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.33.1': - resolution: {integrity: sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ==} + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@webassemblyjs/ast@1.14.1': @@ -348,33 +340,33 @@ packages: '@webassemblyjs/wast-printer@1.14.1': resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} - '@webpack-cli/configtest@2.1.1': - resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} - engines: {node: '>=14.15.0'} + '@webpack-cli/configtest@3.0.1': + resolution: {integrity: sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==} + engines: {node: '>=18.12.0'} peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x + webpack: ^5.82.0 + webpack-cli: 6.x.x - '@webpack-cli/info@2.0.2': - resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} - engines: {node: '>=14.15.0'} + '@webpack-cli/info@3.0.1': + resolution: {integrity: sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==} + engines: {node: '>=18.12.0'} peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x + webpack: ^5.82.0 + webpack-cli: 6.x.x - '@webpack-cli/serve@2.0.5': - resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} - engines: {node: '>=14.15.0'} + '@webpack-cli/serve@3.0.1': + resolution: {integrity: sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==} + engines: {node: '>=18.12.0'} peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x + webpack: ^5.82.0 + webpack-cli: 6.x.x webpack-dev-server: '*' peerDependenciesMeta: webpack-dev-server: optional: true - '@xmldom/xmldom@0.8.10': - resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + '@xmldom/xmldom@0.8.11': + resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} engines: {node: '>=10.0.0'} '@xmldom/xmldom@0.9.8': @@ -387,13 +379,19 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true @@ -452,6 +450,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.9.7: + resolution: {integrity: sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==} + hasBin: true + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -462,8 +464,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.0: - resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -478,17 +480,13 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001721: - resolution: {integrity: sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==} + caniuse-lite@1.0.30001760: + resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} @@ -497,9 +495,9 @@ packages: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} - cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} + cli-truncate@5.1.1: + resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==} + engines: {node: '>=20'} cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -522,16 +520,12 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - commander@13.1.0: - resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - commander@14.0.0: - resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} commander@2.20.3: @@ -564,6 +558,15 @@ packages: supports-color: optional: true + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -571,19 +574,16 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - electron-to-chromium@1.5.165: - resolution: {integrity: sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -591,9 +591,6 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} @@ -638,18 +635,18 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-formatter-unix@8.40.0: - resolution: {integrity: sha512-gfsmFZ/cb1MobrMfYl2IPFLZEz2tWQVO/tnmziNQdhWJMN85GfZD64dcPsEgaEoeVKgAtK6W9LWLlOxhJWZvDw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-formatter-unix@9.0.1: + resolution: {integrity: sha512-6trzj/OL0Q2B5mw3dqryAmQWzo5vVfL9YkaJdw3laouSgbs83TsSz9GFN+1/7lMUlUkBY+8mVEWelkAQoKnlcA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-plugin-jsdoc@48.11.0: - resolution: {integrity: sha512-d12JHJDPNo7IFwTOAItCeJY1hcqoIxE0lHA8infQByLilQ9xkqrRa6laWCnsuCrf+8rUnvxXY1XuTbibRBNylA==} - engines: {node: '>=18'} + eslint-plugin-jsdoc@61.5.0: + resolution: {integrity: sha512-PR81eOGq4S7diVnV9xzFSBE4CDENRQGP0Lckkek8AdHtbj+6Bm0cItwlFnxsLFriJHspiE3mpu8U20eODyToIg==} + engines: {node: '>=20.11.0'} peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - eslint-plugin-prettier@5.4.1: - resolution: {integrity: sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==} + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -666,20 +663,20 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@8.3.0: - resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.28.0: - resolution: {integrity: sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -688,8 +685,8 @@ packages: jiti: optional: true - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esquery@1.6.0: @@ -719,20 +716,12 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -746,8 +735,14 @@ packages: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} @@ -785,10 +780,6 @@ packages: debug: optional: true - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - form-data@4.0.3: resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} @@ -815,14 +806,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -830,9 +813,9 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true + glob@13.0.0: + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} + engines: {node: 20 || >=22} glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -849,9 +832,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -868,9 +848,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} @@ -921,10 +900,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - is-fullwidth-code-point@5.0.0: resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} engines: {node: '>=18'} @@ -941,10 +916,6 @@ packages: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} engines: {node: '>=0.10.0'} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - isarray@0.0.1: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} @@ -958,9 +929,6 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -969,9 +937,9 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsdoc-type-pratt-parser@4.0.0: - resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} - engines: {node: '>=12.0.0'} + jsdoc-type-pratt-parser@6.10.0: + resolution: {integrity: sha512-+LexoTRyYui5iOhJGn13N9ZazL23nAHGkXsa1p/C8yeq79WRfLBag6ZZ0FQG2aRoc9yfo59JT9EYCQonOkHKkQ==} + engines: {node: '>=20.0.0'} json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -999,24 +967,20 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} - linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - lint-staged@15.5.2: - resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==} - engines: {node: '>=18.12.0'} + lint-staged@16.2.7: + resolution: {integrity: sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==} + engines: {node: '>=20.17'} hasBin: true - listr2@8.3.3: - resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} - engines: {node: '>=18.0.0'} + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} - loader-runner@4.3.0: - resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} locate-path@5.0.0: @@ -1034,8 +998,9 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -1054,10 +1019,6 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - mhchemparser@4.2.1: resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} @@ -1073,14 +1034,14 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1092,8 +1053,8 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - mj-context-menu@0.9.1: - resolution: {integrity: sha512-ECPcVXZFRfeYOxb1MWGzctAtnQcZ6nRucE3orfkKX7t/KE2mlXO2K/bq4BcCGOuhdz3Wg2BZDy2S8ECK73/iIw==} + mj-context-menu@1.0.0: + resolution: {integrity: sha512-OSgBFQRCVhrZwRa9lhqnKcJU92dd/YXgBjup0uyeuj9bOaVsf4myOyrjU4PfhYkdDk6AqVUTov7v5uFMvIbduA==} mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} @@ -1103,29 +1064,28 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nano-spawn@2.0.0: + resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==} + engines: {node: '>=20.17'} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} noms@0.0.0: resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + object-deep-merge@2.0.0: + resolution: {integrity: sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==} once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} @@ -1161,9 +1121,11 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-imports@2.2.1: - resolution: {integrity: sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==} - engines: {node: '>= 18'} + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -1177,16 +1139,12 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1195,6 +1153,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} engines: {node: '>=0.10'} @@ -1212,8 +1174,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -1231,9 +1193,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -1255,6 +1214,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} + resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -1276,20 +1239,14 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@5.0.10: - resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + rimraf@6.1.2: + resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} + engines: {node: 20 || >=22} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -1303,8 +1260,12 @@ packages: resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} engines: {node: '>= 10.13.0'} - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true @@ -1327,13 +1288,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - slashes@3.0.12: - resolution: {integrity: sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==} - - slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} - slice-ansi@7.1.0: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} @@ -1354,8 +1308,8 @@ packages: spdx-license-ids@3.0.21: resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} - speech-rule-engine@5.0.0-beta.1: - resolution: {integrity: sha512-arqcJpXEYRG9mQMxRCNd2xFERGvIvwvuhcnoXDw/SyeYNyJ5I9SUU5ft+BPw0M1rPpwl3Q+6ZeeYAcwGXTa6oQ==} + speech-rule-engine@5.0.0-beta.3: + resolution: {integrity: sha512-wSrjCo8h4SJmGtjicD0OrTuCNH/wEaMsV+ktA1D9C2IPnensiP4pfPv+DnEZDMbauC5SCSE1prP5KA0z/W8bLg==} hasBin: true string-argv@0.3.2: @@ -1366,14 +1320,14 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.1.0: + resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} + engines: {node: '>=20'} + string_decoder@0.10.31: resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} @@ -1388,10 +1342,6 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1412,16 +1362,12 @@ packages: resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} engines: {node: ^14.18.0 || >=16.0.0} - synckit@0.9.3: - resolution: {integrity: sha512-JJoOEKTfL1urb1mDoEblhD9NhEbWmq9jHEMEnxoC4ujUaZ4itA8vKgwkFAyNClgxplLi9tsUKX+EduK0p/l7sg==} - engines: {node: ^14.18.0 || >=16.0.0} - - tapable@2.2.2: - resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - terser-webpack-plugin@5.3.14: - resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + terser-webpack-plugin@5.3.16: + resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -1444,43 +1390,48 @@ packages: through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-valid-identifier@1.0.0: + resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} + engines: {node: '>=20'} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typedoc@0.28.5: - resolution: {integrity: sha512-5PzUddaA9FbaarUzIsEc4wNXCiO4Ot3bJNeMF2qKpYlTmM9TTaSHQ7162w756ERCkXER/+o2purRG6YOAv6EMA==} + typedoc@0.28.15: + resolution: {integrity: sha512-mw2/2vTL7MlT+BVo43lOsufkkd2CJO4zeOSuWQQsiXoV2VuEn7f6IZp2jsUDPmBMABpgR0R5jlcJ2OGEFYmkyg==} engines: {node: '>= 18', pnpm: '>= 10'} hasBin: true peerDependencies: - typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x - typescript-eslint@8.33.1: - resolution: {integrity: sha512-AgRnV4sKkWOiZ0Kjbnf5ytTJXMUZQ0qhSVdQtDNYLPLnjsATEYhaO94GlRQwi4t4gO8FfjM6NnikHeKjUm8D7A==} + typescript-eslint@8.50.0: + resolution: {integrity: sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' typescript-tools@0.3.1: resolution: {integrity: sha512-rFRO0bQ5fOu0r6oESjJkgtLE1yCSi7uBz4X2EvawjM9EwH127gBR2h0EM2DK/EcN3FQEJAn14GLBnoyi9nXNig==} hasBin: true - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true @@ -1494,8 +1445,8 @@ packages: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -1510,33 +1461,30 @@ packages: resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} - webpack-cli@5.1.4: - resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} - engines: {node: '>=14.15.0'} + webpack-cli@6.0.1: + resolution: {integrity: sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==} + engines: {node: '>=18.12.0'} hasBin: true peerDependencies: - '@webpack-cli/generators': '*' - webpack: 5.x.x + webpack: ^5.82.0 webpack-bundle-analyzer: '*' webpack-dev-server: '*' peerDependenciesMeta: - '@webpack-cli/generators': - optional: true webpack-bundle-analyzer: optional: true webpack-dev-server: optional: true - webpack-merge@5.10.0: - resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} - engines: {node: '>=10.0.0'} + webpack-merge@6.0.1: + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + engines: {node: '>=18.0.0'} - webpack-sources@3.3.2: - resolution: {integrity: sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==} + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack@5.99.9: - resolution: {integrity: sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==} + webpack@5.103.0: + resolution: {integrity: sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -1564,10 +1512,6 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} @@ -1587,8 +1531,8 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yaml@2.8.0: - resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true @@ -1606,32 +1550,38 @@ packages: snapshots: - '@discoveryjs/json-ext@0.5.7': {} + '@discoveryjs/json-ext@0.6.3': {} - '@es-joy/jsdoccomment@0.46.0': + '@es-joy/jsdoccomment@0.76.0': dependencies: + '@types/estree': 1.0.8 + '@typescript-eslint/types': 8.50.0 comment-parser: 1.4.1 esquery: 1.6.0 - jsdoc-type-pratt-parser: 4.0.0 + jsdoc-type-pratt-parser: 6.10.0 + + '@es-joy/resolve.exports@1.2.0': {} - '@eslint-community/eslint-utils@4.7.0(eslint@9.28.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': dependencies: - eslint: 9.28.0 + eslint: 9.39.2 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.20.0': + '@eslint/config-array@0.21.1': dependencies: - '@eslint/object-schema': 2.1.6 + '@eslint/object-schema': 2.1.7 debug: 4.4.1 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.2.2': {} + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 - '@eslint/core@0.14.0': + '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 @@ -1639,7 +1589,7 @@ snapshots: dependencies: ajv: 6.12.6 debug: 4.4.1 - espree: 10.3.0 + espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 @@ -1649,21 +1599,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.28.0': {} + '@eslint/js@9.39.2': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.3.1': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.14.0 + '@eslint/core': 0.17.0 levn: 0.4.1 - '@gerrit0/mini-shiki@3.7.0': + '@gerrit0/mini-shiki@3.20.0': dependencies: - '@shikijs/engine-oniguruma': 3.7.0 - '@shikijs/langs': 3.7.0 - '@shikijs/themes': 3.7.0 - '@shikijs/types': 3.7.0 + '@shikijs/engine-oniguruma': 3.20.0 + '@shikijs/langs': 3.20.0 + '@shikijs/themes': 3.20.0 + '@shikijs/types': 3.20.0 '@shikijs/vscode-textmate': 10.0.2 '@humanfs/core@0.19.1': {} @@ -1679,14 +1629,11 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@isaacs/cliui@8.0.2': + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/balanced-match': 4.0.1 '@jridgewell/gen-mapping@0.3.8': dependencies: @@ -1710,47 +1657,32 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@mathjax/mathjax-newcm-font@4.0.0': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@pkgr/core@0.1.2': {} + '@mathjax/mathjax-newcm-font@4.1.0': {} '@pkgr/core@0.2.7': {} - '@shikijs/engine-oniguruma@3.7.0': + '@shikijs/engine-oniguruma@3.20.0': dependencies: - '@shikijs/types': 3.7.0 + '@shikijs/types': 3.20.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.7.0': + '@shikijs/langs@3.20.0': dependencies: - '@shikijs/types': 3.7.0 + '@shikijs/types': 3.20.0 - '@shikijs/themes@3.7.0': + '@shikijs/themes@3.20.0': dependencies: - '@shikijs/types': 3.7.0 + '@shikijs/types': 3.20.0 - '@shikijs/types@3.7.0': + '@shikijs/types@3.20.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 '@shikijs/vscode-textmate@10.0.2': {} + '@sindresorhus/base62@1.0.0': {} + '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 @@ -1775,97 +1707,96 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.33.1(@typescript-eslint/parser@8.33.1(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.33.1(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.33.1 - '@typescript-eslint/type-utils': 8.33.1(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.33.1(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.33.1 - eslint: 9.28.0 - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.33.1(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.33.1 - '@typescript-eslint/types': 8.33.1 - '@typescript-eslint/typescript-estree': 8.33.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.33.1 - debug: 4.4.1 - eslint: 9.28.0 - typescript: 5.8.3 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3 + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.33.1(typescript@5.8.3)': + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.33.1(typescript@5.8.3) - '@typescript-eslint/types': 8.33.1 - debug: 4.4.1 - typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + debug: 4.4.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.33.1': + '@typescript-eslint/scope-manager@8.50.0': dependencies: - '@typescript-eslint/types': 8.33.1 - '@typescript-eslint/visitor-keys': 8.33.1 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 - '@typescript-eslint/tsconfig-utils@8.33.1(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': dependencies: - typescript: 5.8.3 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.33.1(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.33.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.33.1(eslint@9.28.0)(typescript@5.8.3) - debug: 4.4.1 - eslint: 9.28.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.33.1': {} + '@typescript-eslint/types@8.50.0': {} - '@typescript-eslint/typescript-estree@8.33.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.33.1(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.33.1(typescript@5.8.3) - '@typescript-eslint/types': 8.33.1 - '@typescript-eslint/visitor-keys': 8.33.1 - debug: 4.4.1 - fast-glob: 3.3.3 - is-glob: 4.0.3 + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3 minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.33.1(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) - '@typescript-eslint/scope-manager': 8.33.1 - '@typescript-eslint/types': 8.33.1 - '@typescript-eslint/typescript-estree': 8.33.1(typescript@5.8.3) - eslint: 9.28.0 - typescript: 5.8.3 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.33.1': + '@typescript-eslint/visitor-keys@8.50.0': dependencies: - '@typescript-eslint/types': 8.33.1 - eslint-visitor-keys: 4.2.0 + '@typescript-eslint/types': 8.50.0 + eslint-visitor-keys: 4.2.1 '@webassemblyjs/ast@1.14.1': dependencies: @@ -1943,22 +1874,22 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.99.9)': + '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.103.0)': dependencies: - webpack: 5.99.9(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.99.9) + webpack: 5.103.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.103.0) - '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.99.9)': + '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.103.0)': dependencies: - webpack: 5.99.9(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.99.9) + webpack: 5.103.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.103.0) - '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.99.9)': + '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.103.0)': dependencies: - webpack: 5.99.9(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.99.9) + webpack: 5.103.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.103.0) - '@xmldom/xmldom@0.8.10': {} + '@xmldom/xmldom@0.8.11': {} '@xmldom/xmldom@0.9.8': {} @@ -1966,11 +1897,15 @@ snapshots: '@xtuc/long@4.2.2': {} - acorn-jsx@5.3.2(acorn@8.14.1): + acorn-import-phases@1.0.4(acorn@8.15.0): dependencies: - acorn: 8.14.1 + acorn: 8.15.0 - acorn@8.14.1: {} + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: @@ -2025,6 +1960,8 @@ snapshots: balanced-match@1.0.2: {} + baseline-browser-mapping@2.9.7: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -2038,12 +1975,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.0: + browserslist@4.28.1: dependencies: - caniuse-lite: 1.0.30001721 - electron-to-chromium: 1.5.165 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.0) + baseline-browser-mapping: 2.9.7 + caniuse-lite: 1.0.30001760 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.2(browserslist@4.28.1) buffer-from@1.1.2: {} @@ -2054,25 +1992,23 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001721: {} + caniuse-lite@1.0.30001760: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.4.1: {} - chrome-trace-event@1.0.4: {} cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 - cli-truncate@4.0.0: + cli-truncate@5.1.1: dependencies: - slice-ansi: 5.0.0 - string-width: 7.2.0 + slice-ansi: 7.1.0 + string-width: 8.1.0 cliui@7.0.4: dependencies: @@ -2098,11 +2034,9 @@ snapshots: dependencies: delayed-stream: 1.0.0 - commander@10.0.1: {} + commander@12.1.0: {} - commander@13.1.0: {} - - commander@14.0.0: {} + commander@14.0.2: {} commander@2.20.3: {} @@ -2132,11 +2066,15 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.3: + dependencies: + ms: 2.1.3 + deep-is@0.1.4: {} delayed-stream@1.0.0: {} - diff@5.2.0: {} + diff@8.0.2: {} dunder-proto@1.0.1: dependencies: @@ -2144,20 +2082,16 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - eastasianwidth@0.2.0: {} - - electron-to-chromium@1.5.165: {} + electron-to-chromium@1.5.267: {} emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} - emoji-regex@9.2.2: {} - enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.2 + tapable: 2.3.0 entities@4.5.0: {} @@ -2186,29 +2120,32 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-formatter-unix@8.40.0: {} + eslint-formatter-unix@9.0.1: {} - eslint-plugin-jsdoc@48.11.0(eslint@9.28.0): + eslint-plugin-jsdoc@61.5.0(eslint@9.39.2): dependencies: - '@es-joy/jsdoccomment': 0.46.0 + '@es-joy/jsdoccomment': 0.76.0 + '@es-joy/resolve.exports': 1.2.0 are-docs-informative: 0.0.2 comment-parser: 1.4.1 - debug: 4.4.1 + debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint: 9.28.0 - espree: 10.3.0 + eslint: 9.39.2 + espree: 10.4.0 esquery: 1.6.0 - parse-imports: 2.2.1 - semver: 7.7.2 + html-entities: 2.6.0 + object-deep-merge: 2.0.0 + parse-imports-exports: 0.2.4 + semver: 7.7.3 spdx-expression-parse: 4.0.0 - synckit: 0.9.3 + to-valid-identifier: 1.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-prettier@5.4.1(@types/eslint@9.6.1)(eslint@9.28.0)(prettier@3.5.3): + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint@9.39.2)(prettier@3.7.4): dependencies: - eslint: 9.28.0 - prettier: 3.5.3 + eslint: 9.39.2 + prettier: 3.7.4 prettier-linter-helpers: 1.0.0 synckit: 0.11.8 optionalDependencies: @@ -2219,38 +2156,37 @@ snapshots: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@8.3.0: + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} - eslint@9.28.0: + eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.0 - '@eslint/config-helpers': 0.2.2 - '@eslint/core': 0.14.0 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.28.0 - '@eslint/plugin-kit': 0.3.1 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint-scope: 8.3.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -2268,11 +2204,11 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.3.0: + espree@10.4.0: dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) - eslint-visitor-keys: 4.2.0 + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 esquery@1.6.0: dependencies: @@ -2292,30 +2228,10 @@ snapshots: events@3.3.0: {} - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -2324,9 +2240,9 @@ snapshots: fastest-levenshtein@1.0.16: {} - fastq@1.19.1: - dependencies: - reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 file-entry-cache@8.0.0: dependencies: @@ -2357,11 +2273,6 @@ snapshots: follow-redirects@1.15.9: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - form-data@4.0.3: dependencies: asynckit: 0.4.0 @@ -2396,26 +2307,17 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@8.0.1: {} - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 glob-to-regexp@0.4.1: {} - glob@10.4.5: + glob@13.0.0: dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 + minimatch: 10.1.1 minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 + path-scurry: 2.0.1 glob@7.2.3: dependencies: @@ -2432,8 +2334,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -2446,7 +2346,7 @@ snapshots: dependencies: function-bind: 1.1.2 - human-signals@5.0.0: {} + html-entities@2.6.0: {} husky@9.1.7: {} @@ -2483,8 +2383,6 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-fullwidth-code-point@4.0.0: {} - is-fullwidth-code-point@5.0.0: dependencies: get-east-asian-width: 1.3.0 @@ -2499,8 +2397,6 @@ snapshots: dependencies: isobject: 3.0.1 - is-stream@3.0.0: {} - isarray@0.0.1: {} isarray@1.0.0: {} @@ -2509,12 +2405,6 @@ snapshots: isobject@3.0.1: {} - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jest-worker@27.5.1: dependencies: '@types/node': 22.15.30 @@ -2525,7 +2415,7 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@4.0.0: {} + jsdoc-type-pratt-parser@6.10.0: {} json-buffer@3.0.1: {} @@ -2548,37 +2438,30 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lilconfig@3.1.3: {} - linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 - lint-staged@15.5.2: + lint-staged@16.2.7: dependencies: - chalk: 5.4.1 - commander: 13.1.0 - debug: 4.4.1 - execa: 8.0.1 - lilconfig: 3.1.3 - listr2: 8.3.3 + commander: 14.0.2 + listr2: 9.0.5 micromatch: 4.0.8 + nano-spawn: 2.0.0 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.8.0 - transitivePeerDependencies: - - supports-color + yaml: 2.8.2 - listr2@8.3.3: + listr2@9.0.5: dependencies: - cli-truncate: 4.0.0 + cli-truncate: 5.1.1 colorette: 2.0.20 eventemitter3: 5.0.1 log-update: 6.1.0 rfdc: 1.4.1 wrap-ansi: 9.0.0 - loader-runner@4.3.0: {} + loader-runner@4.3.1: {} locate-path@5.0.0: dependencies: @@ -2598,7 +2481,7 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - lru-cache@10.4.3: {} + lru-cache@11.2.4: {} lunr@2.3.9: {} @@ -2617,8 +2500,6 @@ snapshots: merge-stream@2.0.0: {} - merge2@1.4.1: {} - mhchemparser@4.2.1: {} micromatch@4.0.8: @@ -2632,10 +2513,12 @@ snapshots: dependencies: mime-db: 1.52.0 - mimic-fn@4.0.0: {} - mimic-function@5.0.1: {} + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -2646,35 +2529,33 @@ snapshots: minipass@7.1.2: {} - mj-context-menu@0.9.1: {} + mj-context-menu@1.0.0: + dependencies: + rimraf: 6.1.2 mkdirp@1.0.4: {} ms@2.1.3: {} + nano-spawn@2.0.0: {} + natural-compare@1.4.0: {} neo-async@2.6.2: {} - node-releases@2.0.19: {} + node-releases@2.0.27: {} noms@0.0.0: dependencies: inherits: 2.0.4 readable-stream: 1.0.34 - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 + object-deep-merge@2.0.0: {} once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -2712,10 +2593,11 @@ snapshots: dependencies: callsites: 3.1.0 - parse-imports@2.2.1: + parse-imports-exports@0.2.4: dependencies: - es-module-lexer: 1.7.0 - slashes: 3.0.12 + parse-statements: 1.0.11 + + parse-statements@1.0.11: {} path-exists@4.0.0: {} @@ -2723,19 +2605,19 @@ snapshots: path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} - path-scurry@1.11.1: + path-scurry@2.0.1: dependencies: - lru-cache: 10.4.3 + lru-cache: 11.2.4 minipass: 7.1.2 picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.3: {} + pidtree@0.6.0: {} pkg-dir@4.2.0: @@ -2748,7 +2630,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.5.3: {} + prettier@3.7.4: {} process-nextick-args@2.0.1: {} @@ -2758,8 +2640,6 @@ snapshots: punycode@2.3.1: {} - queue-microtask@1.2.3: {} - randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -2789,6 +2669,8 @@ snapshots: require-from-string@2.0.2: {} + reserved-identifiers@1.2.0: {} + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 @@ -2808,17 +2690,12 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - reusify@1.1.0: {} - rfdc@1.4.1: {} - rimraf@5.0.10: + rimraf@6.1.2: dependencies: - glob: 10.4.5 - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 + glob: 13.0.0 + package-json-from-dist: 1.0.1 safe-buffer@5.1.2: {} @@ -2837,7 +2714,14 @@ snapshots: ajv-formats: 2.1.1(ajv@8.17.1) ajv-keywords: 5.1.0(ajv@8.17.1) - semver@7.7.2: {} + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + + semver@7.7.3: {} serialize-javascript@6.0.2: dependencies: @@ -2855,13 +2739,6 @@ snapshots: signal-exit@4.1.0: {} - slashes@3.0.12: {} - - slice-ansi@5.0.0: - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 4.0.0 - slice-ansi@7.1.0: dependencies: ansi-styles: 6.2.1 @@ -2883,10 +2760,10 @@ snapshots: spdx-license-ids@3.0.21: {} - speech-rule-engine@5.0.0-beta.1: + speech-rule-engine@5.0.0-beta.3: dependencies: '@xmldom/xmldom': 0.9.8 - commander: 14.0.0 + commander: 14.0.2 wicked-good-xpath: 1.3.0 string-argv@0.3.2: {} @@ -2897,15 +2774,14 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@5.1.2: + string-width@7.2.0: dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 strip-ansi: 7.1.0 - string-width@7.2.0: + string-width@8.1.0: dependencies: - emoji-regex: 10.4.0 get-east-asian-width: 1.3.0 strip-ansi: 7.1.0 @@ -2923,8 +2799,6 @@ snapshots: dependencies: ansi-regex: 6.1.0 - strip-final-newline@3.0.0: {} - strip-json-comments@3.1.1: {} supports-color@7.2.0: @@ -2941,26 +2815,21 @@ snapshots: dependencies: '@pkgr/core': 0.2.7 - synckit@0.9.3: - dependencies: - '@pkgr/core': 0.1.2 - tslib: 2.8.1 + tapable@2.3.0: {} - tapable@2.2.2: {} - - terser-webpack-plugin@5.3.14(webpack@5.99.9): + terser-webpack-plugin@5.3.16(webpack@5.103.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.41.0 - webpack: 5.99.9(webpack-cli@5.1.4) + webpack: 5.103.0(webpack-cli@6.0.1) terser@5.41.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.14.1 + acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -2969,42 +2838,51 @@ snapshots: readable-stream: 2.3.8 xtend: 4.0.2 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - ts-api-utils@2.1.0(typescript@5.8.3): + to-valid-identifier@1.0.0: dependencies: - typescript: 5.8.3 + '@sindresorhus/base62': 1.0.0 + reserved-identifiers: 1.2.0 - tslib@2.8.1: {} + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typedoc@0.28.5(typescript@5.8.3): + typedoc@0.28.15(typescript@5.9.3): dependencies: - '@gerrit0/mini-shiki': 3.7.0 + '@gerrit0/mini-shiki': 3.20.0 lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 - typescript: 5.8.3 - yaml: 2.8.0 + typescript: 5.9.3 + yaml: 2.8.2 - typescript-eslint@8.33.1(eslint@9.28.0)(typescript@5.8.3): + typescript-eslint@8.50.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.33.1(@typescript-eslint/parser@8.33.1(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.33.1(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.33.1(eslint@9.28.0)(typescript@5.8.3) - eslint: 9.28.0 - typescript: 5.8.3 + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color typescript-tools@0.3.1: {} - typescript@5.8.3: {} + typescript@5.9.3: {} uc.micro@2.1.0: {} @@ -3012,9 +2890,9 @@ snapshots: untildify@4.0.0: {} - update-browserslist-db@1.1.3(browserslist@4.25.0): + update-browserslist-db@1.2.2(browserslist@4.28.1): dependencies: - browserslist: 4.25.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -3029,32 +2907,32 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - webpack-cli@5.1.4(webpack@5.99.9): + webpack-cli@6.0.1(webpack@5.103.0): dependencies: - '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.99.9) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.99.9) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack@5.99.9) + '@discoveryjs/json-ext': 0.6.3 + '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.103.0) + '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.103.0) + '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.103.0) colorette: 2.0.20 - commander: 10.0.1 + commander: 12.1.0 cross-spawn: 7.0.6 envinfo: 7.14.0 fastest-levenshtein: 1.0.16 import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.99.9(webpack-cli@5.1.4) - webpack-merge: 5.10.0 + webpack: 5.103.0(webpack-cli@6.0.1) + webpack-merge: 6.0.1 - webpack-merge@5.10.0: + webpack-merge@6.0.1: dependencies: clone-deep: 4.0.1 flat: 5.0.2 wildcard: 2.0.1 - webpack-sources@3.3.2: {} + webpack-sources@3.3.3: {} - webpack@5.99.9(webpack-cli@5.1.4): + webpack@5.103.0(webpack-cli@6.0.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -3062,8 +2940,9 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.14.1 - browserslist: 4.25.0 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.28.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.1 es-module-lexer: 1.7.0 @@ -3072,16 +2951,16 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 + loader-runner: 4.3.1 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 4.3.2 - tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(webpack@5.99.9) + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.16(webpack@5.103.0) watchpack: 2.4.4 - webpack-sources: 3.3.2 + webpack-sources: 3.3.3 optionalDependencies: - webpack-cli: 5.1.4(webpack@5.99.9) + webpack-cli: 6.0.1(webpack@5.103.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -3103,12 +2982,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 @@ -3128,7 +3001,7 @@ snapshots: y18n@5.0.8: {} - yaml@2.8.0: {} + yaml@2.8.2: {} yargs-parser@20.2.9: {} diff --git a/testsuite/package.json b/testsuite/package.json index ef7e0abda..b77609b5d 100644 --- a/testsuite/package.json +++ b/testsuite/package.json @@ -4,7 +4,7 @@ "description": "MathJax jest tests for v4", "type": "module", "scripts": { - "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' pnpm jest" + "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings --localstorage-file ./lib/localstorage' pnpm jest" }, "imports": { "#js/*": "../mjs/*", @@ -14,7 +14,7 @@ }, "dependencies": { "@jest/globals": "^29.7.0", - "@mathjax/mathjax-bbm-font-extension": "0.4.2-beta.8", + "@mathjax/mathjax-bbm-font-extension": "^4.0.0", "@types/jest": "^29.5.14", "jest": "^29.7.0", "ts-jest": "^29.3.4", diff --git a/testsuite/pnpm-lock.yaml b/testsuite/pnpm-lock.yaml index 9a5d25485..6d5af4376 100644 --- a/testsuite/pnpm-lock.yaml +++ b/testsuite/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^29.7.0 version: 29.7.0 '@mathjax/mathjax-bbm-font-extension': - specifier: 0.4.2-beta.8 - version: 0.4.2-beta.8 + specifier: ^4.0.0 + version: 4.0.0 '@types/jest': specifier: ^29.5.14 version: 29.5.14 @@ -296,8 +296,8 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@mathjax/mathjax-bbm-font-extension@0.4.2-beta.8': - resolution: {integrity: sha512-FU/M4IJ6dRCRxxfnxRSAwWgEEvcpgqqzupiSe3JW8TCl2zR0co3zaYtO1oaHGrmmlqAkARPRJnpxViIWhfPr6A==} + '@mathjax/mathjax-bbm-font-extension@4.0.0': + resolution: {integrity: sha512-4ElFHV3T4oXP9DhyLXcvnAfqqmjf4lINFvShXFkcx7/va/TUL3EpZm/s130JQH3Sp5xX6UG09wY2/dcLONLfjQ==} '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -1643,7 +1643,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@mathjax/mathjax-bbm-font-extension@0.4.2-beta.8': {} + '@mathjax/mathjax-bbm-font-extension@4.0.0': {} '@sinclair/typebox@0.27.8': {} diff --git a/testsuite/scripts/test-diff.el b/testsuite/scripts/test-diff.el index bf0c7d8be..5476156d2 100644 --- a/testsuite/scripts/test-diff.el +++ b/testsuite/scripts/test-diff.el @@ -2,9 +2,10 @@ ;;; Tools for working with Jest tests in Emacs. ;;; ;;; -;;; Copyright (c) 2024 The MathJax Consortium +;;; Copyright (c) 2024-5 The MathJax Consortium (require 'ediff) +(require 'cl-lib) ;;; Jest Tests ;;; ========== @@ -17,7 +18,7 @@ ;;; (defun jest-find-expected () - (block find-fail-block + (cl-block find-fail-block (let ((expected (condition-case nil (search-forward "Expected value") (error nil)))) @@ -31,19 +32,19 @@ (defun jest-find-fail () ;; Returns start end for actual and expected and position of fail o/w nil. (interactive) - (block find-fail-block + (cl-block find-fail-block (let ((pos (condition-case nil (search-forward "●" nil t) (error nil)))) (when (null pos) - (return-from find-fail-block nil)) + (cl-return-from find-fail-block nil)) (let ((expected (jest-find-expected)) (actual (condition-case nil (search-forward "Received:") (error nil))) ) (when (or (null actual) (null expected)) - (return-from find-fail-block nil)) + (cl-return-from find-fail-block nil)) (let* ((beg1 (progn (goto-char actual) (search-forward "\"") @@ -88,7 +89,7 @@ (condition-case nil (search-forward "Summary of all failing tests") (error (beginning-of-buffer))) - (do ((fail (jest-find-fail) (jest-find-fail))) + (cl-do ((fail (jest-find-fail) (jest-find-fail))) ((null fail) nil) (print fail) (append-to-buffer bufferA (caadr fail) (cdadr fail)) @@ -102,7 +103,7 @@ ;;; Go to position where you want the next test inserted. (defun jest-replace-expected-for-actual () (interactive) - (block expected-block + (cl-block expected-block (other-window 1) (let* ((fail (jest-find-fail)) (actual (car (fourth fail))) diff --git a/testsuite/tests/input/tex/Ams.test.ts b/testsuite/tests/input/tex/Ams.test.ts index 60534b58d..359e2ae84 100644 --- a/testsuite/tests/input/tex/Ams.test.ts +++ b/testsuite/tests/input/tex/Ams.test.ts @@ -48,10 +48,10 @@ describe('Ams', () => { toXmlMatch( tex2mml('\\left\\ulcorner A \\right\\urcorner'), ` - - + + A - + ` ); @@ -151,7 +151,7 @@ describe('Ams', () => { ` a - + n k @@ -168,7 +168,7 @@ describe('Ams', () => { ` a - + n k @@ -187,7 +187,7 @@ describe('Ams', () => { ` a - + n k @@ -274,7 +274,7 @@ describe('Ams', () => { ` a - + ( @@ -299,7 +299,7 @@ describe('Ams', () => { ` a - + ( @@ -326,7 +326,7 @@ describe('Ams', () => { ` a - + ( @@ -441,16 +441,19 @@ describe('Ams', () => { toXmlMatch( tex2mml('\\xleftarrow{abcd}'), ` - - - - a - b - c - d - - - + + + + + + a + b + c + d + + + + ` ); }); @@ -461,22 +464,25 @@ describe('Ams', () => { toXmlMatch( tex2mml('\\xleftarrow[xyz]{abcd}'), ` - - - - x - y - z - - - - a - b - c - d - - - + + + + + + x + y + z + + + + a + b + c + d + + + + ` ); }); @@ -488,16 +494,19 @@ describe('Ams', () => { tex2mml('A\\xleftarrow{abcd}B'), ` A - - - - a - b - c - d - - - + + + + + + a + b + c + d + + + + B ` ); @@ -509,16 +518,19 @@ describe('Ams', () => { toXmlMatch( tex2mml('\\xrightarrow{abcd}'), ` - - - - a - b - c - d - - - + + + + + + a + b + c + d + + + + ` ); }); @@ -529,22 +541,25 @@ describe('Ams', () => { toXmlMatch( tex2mml('\\xrightarrow[xyz]{abcd}'), ` - - - - x - y - z - - - - a - b - c - d - - - + + + + + + x + y + z + + + + a + b + c + d + + + + ` ); }); @@ -556,16 +571,19 @@ describe('Ams', () => { tex2mml('A\\xrightarrow{abcd}B'), ` A - - - - a - b - c - d - - - + + + + + + a + b + c + d + + + + B ` ); @@ -781,13 +799,13 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{subarray}{c}a\\end{subarray}'), ` - + - + a - + ` @@ -800,7 +818,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix}a\\end{smallmatrix}'), ` - + @@ -819,7 +837,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{align} a&=b \\\\ c&=d \\end{align}'), ` - + a @@ -855,7 +873,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{align*} a&=b \\\\ c&=d \\end{align*}'), ` - + a @@ -891,7 +909,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{multline} a\\\\ b \\\\ c \\end{multline}'), ` - + a @@ -918,7 +936,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{multline*} a\\\\ b \\\\ c \\end{multline*}'), ` - + a @@ -947,7 +965,7 @@ describe('Ams Environments', () => { '\\begin{align*} a&=b \\begin{split} r&=s\\\\ & =t \\end{split} \\\\ c&=d \\end{align*}' ), ` - + a @@ -957,7 +975,7 @@ describe('Ams Environments', () => { = b - + r @@ -1007,7 +1025,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{gather} a=b \\\\ c=d \\end{gather}'), ` - + a @@ -1033,7 +1051,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{gather*} a=b \\\\ c=d \\end{gather*}'), ` - + a @@ -1059,8 +1077,8 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{alignat}{2} a&=b \\\\ c&=d \\end{alignat}'), ` - - + + a @@ -1072,7 +1090,7 @@ describe('Ams Environments', () => { - + c @@ -1095,8 +1113,8 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{alignat*}{2} a&=b \\\\ c&=d \\end{alignat*}'), ` - - + + a @@ -1108,7 +1126,7 @@ describe('Ams Environments', () => { - + c @@ -1133,7 +1151,7 @@ describe('Ams Environments', () => { '\\begin{align*} a&=b \\begin{alignedat}{2} r&=s\\\\ & =t \\end{alignedat} \\\\ c&=d \\end{align*}' ), ` - + a @@ -1143,8 +1161,8 @@ describe('Ams Environments', () => { = b - - + + r @@ -1156,7 +1174,7 @@ describe('Ams Environments', () => { - + @@ -1195,7 +1213,7 @@ describe('Ams Environments', () => { '\\begin{align*} a&=b \\begin{aligned} r&=s\\\\ & =t \\end{aligned} \\\\ c&=d \\end{align*}' ), ` - + a @@ -1205,8 +1223,8 @@ describe('Ams Environments', () => { = b - - + + r @@ -1218,7 +1236,7 @@ describe('Ams Environments', () => { - + @@ -1257,7 +1275,7 @@ describe('Ams Environments', () => { '\\begin{align*} a&=b \\begin{gathered} r=s\\\\ =t \\end{gathered} \\\\ c&=d \\end{align*}' ), ` - + a @@ -1267,15 +1285,15 @@ describe('Ams Environments', () => { = b - - + + r = s - + = t @@ -1308,7 +1326,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{equation} a \\end{equation}'), ` - a + a ` ); }); @@ -1319,7 +1337,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{equation*} a \\end{equation*}'), ` - a + a ` ); }); @@ -1330,7 +1348,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{eqnarray} a & = & b\\\\ c & = & d \\end{eqnarray}'), ` - + a @@ -1370,7 +1388,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{eqnarray*} a & = & b\\\\ c & = & d \\end{eqnarray*}'), ` - + a @@ -1410,7 +1428,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{flalign} a & = & b\\\\ c & = & d \\end{flalign}'), ` - + a @@ -1452,8 +1470,8 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{xalignat}{2} a&b & c&d \\end{xalignat}'), ` - - + + a @@ -1492,8 +1510,8 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{xxalignat}{2} a&b & c&d \\end{xxalignat}'), ` - - + + a @@ -1530,8 +1548,8 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{xalignat}{2} a&b \\end{xalignat}'), ` - - + + a @@ -1559,11 +1577,13 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{xalignat}{2} a & b \\tag{1}\\end{xalignat}'), ` - - + + - (1) + ( + 1 + ) @@ -1593,7 +1613,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{flalign} a&b \\end{flalign}'), ` - + a @@ -1615,7 +1635,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{matrix} a & b \\\\ c & d \\end{matrix}'), ` - + a @@ -1643,7 +1663,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}'), ` - + ( @@ -1675,7 +1695,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{bmatrix} a & b \\\\ c & d \\end{bmatrix}'), ` - + [ @@ -1707,7 +1727,7 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{Bmatrix} a & b \\\\ c & d \\end{Bmatrix}'), ` - + { @@ -1739,8 +1759,8 @@ describe('Ams Environments', () => { toXmlMatch( tex2mml('\\begin{Vmatrix} a & b \\\\ c & d \\end{Vmatrix}'), ` - - + + @@ -1759,7 +1779,7 @@ describe('Ams Environments', () => { - + ` ); @@ -1776,7 +1796,7 @@ describe('Ams Environments', () => { x ) = - + { @@ -1825,9 +1845,9 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{subarray}{c}a\\end{subarray}'), ` - + - + a @@ -1844,7 +1864,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix}a\\end{smallmatrix}'), ` - + @@ -1863,10 +1883,12 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{align} a&=b \\\\ c&=d \\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -1881,7 +1903,9 @@ describe('Ams Tagged Environments', () => { - (2) + ( + 2 + ) c @@ -1905,7 +1929,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{align*} a&=b \\\\ c&=d \\end{align*}'), ` - + a @@ -1941,7 +1965,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{multline} a\\\\ b \\\\ c \\end{multline}'), ` - + a @@ -1954,7 +1978,9 @@ describe('Ams Tagged Environments', () => { - (1) + ( + 1 + ) c @@ -1971,7 +1997,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{multline*} a\\\\ b \\\\ c \\end{multline*}'), ` - + a @@ -2000,7 +2026,7 @@ describe('Ams Tagged Environments', () => { '\\begin{align*} a&=b \\begin{split} r&=s\\\\ & =t \\end{split} \\\\ c&=d \\end{align*}' ), ` - + a @@ -2010,7 +2036,7 @@ describe('Ams Tagged Environments', () => { = b - + r @@ -2060,10 +2086,12 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{gather} a=b \\\\ c=d \\end{gather}'), ` - + - (1) + ( + 1 + ) a @@ -2073,7 +2101,9 @@ describe('Ams Tagged Environments', () => { - (2) + ( + 2 + ) c @@ -2092,7 +2122,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{gather*} a=b \\\\ c=d \\end{gather*}'), ` - + a @@ -2118,10 +2148,12 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{alignat}{2} a&=b \\\\ c&=d \\end{alignat}'), ` - - + + - (1) + ( + 1 + ) a @@ -2134,9 +2166,11 @@ describe('Ams Tagged Environments', () => { - + - (2) + ( + 2 + ) c @@ -2160,8 +2194,8 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{alignat*}{2} a&=b \\\\ c&=d \\end{alignat*}'), ` - - + + a @@ -2173,7 +2207,7 @@ describe('Ams Tagged Environments', () => { - + c @@ -2198,7 +2232,7 @@ describe('Ams Tagged Environments', () => { '\\begin{align*} a&=b \\begin{alignedat}{2} r&=s\\\\ & =t \\end{alignedat} \\\\ c&=d \\end{align*}' ), ` - + a @@ -2208,8 +2242,8 @@ describe('Ams Tagged Environments', () => { = b - - + + r @@ -2221,7 +2255,7 @@ describe('Ams Tagged Environments', () => { - + @@ -2260,7 +2294,7 @@ describe('Ams Tagged Environments', () => { '\\begin{align*} a&=b \\begin{aligned} r&=s\\\\ & =t \\end{aligned} \\\\ c&=d \\end{align*}' ), ` - + a @@ -2270,8 +2304,8 @@ describe('Ams Tagged Environments', () => { = b - - + + r @@ -2283,7 +2317,7 @@ describe('Ams Tagged Environments', () => { - + @@ -2322,7 +2356,7 @@ describe('Ams Tagged Environments', () => { '\\begin{align*} a&=b \\begin{gathered} r=s\\\\ =t \\end{gathered} \\\\ c&=d \\end{align*}' ), ` - + a @@ -2332,15 +2366,15 @@ describe('Ams Tagged Environments', () => { = b - - + + r = s - + = t @@ -2373,10 +2407,12 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{equation} a \\end{equation}'), ` - + - (1) + ( + 1 + ) a @@ -2393,7 +2429,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{equation*} a \\end{equation*}'), ` - a + a ` ); }); @@ -2404,10 +2440,12 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{eqnarray} a & = & b\\\\ c & = & d \\end{eqnarray}'), ` - + - (1) + ( + 1 + ) a @@ -2424,7 +2462,9 @@ describe('Ams Tagged Environments', () => { - (2) + ( + 2 + ) c @@ -2450,7 +2490,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{eqnarray*} a & = & b\\\\ c & = & d \\end{eqnarray*}'), ` - + a @@ -2490,10 +2530,12 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{align} a&=b \\\\ &=c \\notag \\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -2527,11 +2569,13 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{xalignat}{1} a&b \\end{xalignat}'), ` - - + + - (1) + ( + 1 + ) @@ -2556,8 +2600,8 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{xalignat*}{1} a&b \\end{xalignat*}'), ` - - + + a @@ -2580,10 +2624,12 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{flalign} a&b & \\end{flalign}'), ` - + - (1) + ( + 1 + ) a @@ -2607,7 +2653,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{flalign*} a&b & \\end{flalign*}'), ` - + a @@ -2631,13 +2677,13 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{aligned} [b] a \\\\ b \\end{aligned}'), ` - - + + a - + b @@ -2653,7 +2699,7 @@ describe('Ams Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{aligned} [x] a \\\\ b \\end{aligned}'), ` - + [ @@ -2688,11 +2734,13 @@ describe('Ams Tagged Environments Left', () => { toXmlMatch( tex2mml('\\begin{xalignat}{2} a & b \\tag{1}\\end{xalignat}'), ` - - + + - (1) + ( + 1 + ) @@ -2722,10 +2770,12 @@ describe('Ams Tagged Environments Left', () => { toXmlMatch( tex2mml('\\begin{multline} a\\tag{1} \\end{multline}'), ` - + - (1) + ( + 1 + ) a @@ -2749,16 +2799,20 @@ describe('Ams Tagged Environments Left', () => { toXmlMatch( tex2mml('\\begin{gather}\\begin{align} a &= b \\end{align}\\end{gather}'), ` - + - (2) + ( + 2 + ) - + - (1) + ( + 1 + ) a @@ -2950,7 +3004,7 @@ describe('Multirel', () => { > ≈∼ - ⌢⌣=↕ + ⌢⌣=↕ b ` ); @@ -2997,7 +3051,7 @@ describe('MultlineShove', () => { toXmlMatch( tex2mml('\\begin{multline} a\\\\ b\\\\ c\\end{multline}'), ` - + a @@ -3024,7 +3078,7 @@ describe('MultlineShove', () => { toXmlMatch( tex2mml('\\begin{multline}\\shoveleft a\\\\ b\\\\ c\\end{multline}'), ` - + a @@ -3051,7 +3105,7 @@ describe('MultlineShove', () => { toXmlMatch( tex2mml('\\begin{multline} a\\\\\\shoveleft b\\\\ c\\end{multline}'), ` - + a @@ -3078,7 +3132,7 @@ describe('MultlineShove', () => { toXmlMatch( tex2mml('\\begin{multline} a\\\\ b\\\\\\shoveleft c\\end{multline}'), ` - + a @@ -3105,7 +3159,7 @@ describe('MultlineShove', () => { toXmlMatch( tex2mml('\\begin{multline}\\shoveright a\\\\ b\\\\ c\\end{multline}'), ` - + a @@ -3132,7 +3186,7 @@ describe('MultlineShove', () => { toXmlMatch( tex2mml('\\begin{multline} a\\\\\\shoveright b\\\\ c\\end{multline}'), ` - + a @@ -3159,7 +3213,7 @@ describe('MultlineShove', () => { toXmlMatch( tex2mml('\\begin{multline} a\\\\ b\\\\\\shoveright c\\end{multline}'), ` - + a @@ -3188,7 +3242,7 @@ describe('MultlineShove', () => { '\\begin{multline} a\\\\\\shoveright\\shoveleft b\\\\ c\\end{multline}' ), ` - + a @@ -3217,7 +3271,7 @@ describe('MultlineShove', () => { '\\begin{multline} a\\\\\\shoveleft\\shoveright b\\\\ c\\end{multline}' ), ` - + a @@ -3283,7 +3337,7 @@ describe('Ams Complex', () => { '\\begin{align}\\dot{x} & = \\sigma(y-x) \\\\\\dot{y} & = \\rho x - y - xz \\\\\\dot{z} & = -\\beta z + xy\\end{align}' ), ` - + @@ -3364,7 +3418,7 @@ describe('Ams Complex', () => { '\\begin{align} \\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\ \\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\ \\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \\end{align}' ), ` - + @@ -3534,7 +3588,7 @@ describe('Ams Complex', () => { '\\begin{eqnarray}(x+y)^{3}&=&(x+y)(x+y)(x+y)\\\\&=&xxx+xxy+xyx+{\\underline {xyy}}+yxx+{\\underline {yxy}}+{\\underline {yyx}}+yyy\\\\&=&x^{3}+3x^{2}y+{\\underline {3xy^{2}}}+y^{3}.\\end{eqnarray}' ), ` - + ( @@ -3713,7 +3767,7 @@ describe('Ams Complex', () => { 2 = - + | @@ -4009,20 +4063,20 @@ describe('Ams symbols', () => { '\\left\\llcorner X \\right\\lrcorner \\left\\lvert X \\right\\rvert \\left\\lVert X \\right\\rVert' ), ` - - + + X - + - - | + + | X - | + | - - + + X - + ` ); @@ -4131,14 +4185,14 @@ describe('Ams symbols', () => { toXmlMatch( tex2mml('\\substack{a\\\\b}'), ` - + - + a - + b @@ -4378,7 +4432,7 @@ describe('Ams symbols', () => { ), ` - ⌣∣⌢∥ + ⌣∣⌢∥ ≏≬≎⋔ @@ -4540,7 +4594,7 @@ describe('Ams symbols', () => { toXmlMatch( tex2mml('\\nrightarrow\\nLeftarrow\\nRightarrow\\nleftrightarrow\\nLeftrightarrow'), ` - ↛⇍⇏↮⇎ + ↛⇍⇏↮⇎ ` ); }); diff --git a/testsuite/tests/input/tex/Amscd.test.ts b/testsuite/tests/input/tex/Amscd.test.ts index ff09abb12..e61515126 100644 --- a/testsuite/tests/input/tex/Amscd.test.ts +++ b/testsuite/tests/input/tex/Amscd.test.ts @@ -15,7 +15,7 @@ describe('AmsCD', () => { toXmlMatch( tex2mml('\\begin{CD}A @>a>> B\\\\@VVbV @VVcV\\\\C @>d>> D\\end{CD}'), ` - + A @@ -37,12 +37,18 @@ describe('AmsCD', () => { - - - - b - - + + + + + + + b + + + + + @@ -50,12 +56,18 @@ describe('AmsCD', () => { - - - - c - - + + + + + + + c + + + + + @@ -89,7 +101,7 @@ describe('AmsCD', () => { toXmlMatch( tex2mml('\\begin{CD}A @<<< B @>>> C\\\\@. @| @AAA\\\\@. D @= E\\end{CD}'), ` - + A @@ -156,7 +168,7 @@ describe('AmsCD', () => { toXmlMatch( tex2mml('\\begin{CD}A @>a>b> B\\\\@VlVrV @AlArA\\\\C @ - + A @@ -180,40 +192,64 @@ describe('AmsCD', () => { - - - - l - - + + + + + + + l + + + + + - - - - r - - + + + + + + + r + + + + + - - - - l - - + + + + + + + l + + + + + - - - - r - - + + + + + + + r + + + + + @@ -252,7 +288,7 @@ describe('AmsCD', () => { '\\begin{CD}A @>>> B@>\\text{very long label}>>C\\\\@VVV @VVV @VVV\\\\D @>>> E@>>> F\\end{CD}' ), ` - + A @@ -336,7 +372,7 @@ describe('AmsCD', () => { '\\begin{CD}A @>>> B @>{\\text{very long label}}>> C \\\\@VVV @VVV @VVV \\\\D @>>> E @>{\\phantom{\\text{very long label}}}>> F\\end{CD}' ), ` - + A @@ -428,7 +464,7 @@ describe('AmsCD', () => { '\\begin{CD}A @>>> B @>{\\text{very long label}}>> C \\\\@VVV @VVV @VVV \\\\D @>>> E @>{\\rlap{\\scriptstyle{\\ \\ \\ \\text{shorter}}}\\phantom{\\text{very long label}}}>> F\\end{CD}' ), ` - + A @@ -497,9 +533,9 @@ describe('AmsCD', () => { -   -   -   +   +   +   shorter @@ -530,7 +566,7 @@ describe('AmsCD', () => { '\\minCDarrowwidth{5cm}\\begin{CD}A @>a>> B\\\\@VVbV @VVcV\\\\C @>d>> D\\end{CD}' ), ` - + A @@ -552,12 +588,18 @@ describe('AmsCD', () => { - - - - b - - + + + + + + + b + + + + + @@ -565,12 +607,18 @@ describe('AmsCD', () => { - - - - c - - + + + + + + + c + + + + + @@ -606,7 +654,7 @@ describe('AmsCD', () => { '\\minCDarrowheight{4cm}\\begin{CD}A @>a>> B\\\\@VVbV @VVcV\\\\C @>d>> D\\end{CD}' ), ` - + A @@ -628,12 +676,18 @@ describe('AmsCD', () => { - - - - b - - + + + + + + + b + + + + + @@ -641,12 +695,18 @@ describe('AmsCD', () => { - - - - c - - + + + + + + + c + + + + + @@ -682,7 +742,7 @@ describe('AmsCD', () => { '\\minCDarrowheight{4cm}\\minCDarrowwidth{5cm}\\begin{CD}A @>a>> B\\\\@VVbV @VVcV\\\\C @>d>> D\\end{CD}' ), ` - + A @@ -704,12 +764,18 @@ describe('AmsCD', () => { - - - - b - - + + + + + + + b + + + + + @@ -717,12 +783,18 @@ describe('AmsCD', () => { - - - - c - - + + + + + + + c + + + + + @@ -752,17 +824,44 @@ describe('AmsCD', () => { /********************************************************************************/ + it('Spaces', () => { + toXmlMatch( + tex2mml('\\begin{CD}A @ > x > > B \\end{CD}'), + ` + + + + A + + + + + + + x + + + + + B + + + + ` + ); + }); + + /********************************************************************************/ + it('Suspicious Return', () => { toXmlMatch( tex2mml('\\begin{CD}A @Ra>> BaD\\end{CD}'), ` - + A - - @ - + @ R a >> @@ -782,12 +881,12 @@ describe('AmsCD', () => { toXmlMatch( tex2mml(`\\begin{CD}A' @>>> B\\end{CD}`), ` - + A - + @@ -825,7 +924,7 @@ describe('AmsCD Options', () => { toXmlMatch( tex2mml('\\begin{CD}A @>a>> B\\\\@VVbV @VVcV\\\\C @>d>> D\\end{CD}'), ` - + A @@ -849,12 +948,18 @@ describe('AmsCD Options', () => { - - - - b - - + + + + + + + b + + + + + @@ -862,12 +967,18 @@ describe('AmsCD Options', () => { - - - - c - - + + + + + + + c + + + + + @@ -892,8 +1003,8 @@ describe('AmsCD Options', () => { D - - ` + + ` ); }); diff --git a/testsuite/tests/input/tex/Autoload.test.ts b/testsuite/tests/input/tex/Autoload.test.ts index e8fe288ba..c8491ddba 100644 --- a/testsuite/tests/input/tex/Autoload.test.ts +++ b/testsuite/tests/input/tex/Autoload.test.ts @@ -27,7 +27,7 @@ describe('Autoload', () => { toXmlMatch( await typeset2mml('\\begin{CD} a @>>> b\\end{CD}'), ` - + a diff --git a/testsuite/tests/input/tex/Base.test.ts b/testsuite/tests/input/tex/Base.test.ts index 7b70ffabe..c10f7c389 100644 --- a/testsuite/tests/input/tex/Base.test.ts +++ b/testsuite/tests/input/tex/Base.test.ts @@ -86,7 +86,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('^2'), ` - + 2 @@ -100,7 +100,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('{}^2'), ` - + 2 @@ -114,7 +114,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('x^2'), ` - + x 2 @@ -128,7 +128,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('x^3'), ` - + x 3 @@ -142,7 +142,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('\\sum^2_1'), ` - + 1 2 @@ -158,14 +158,14 @@ describe('Sub and Superscripts', () => { tex2mml('\\left( \\sum_1^n \\right)^{2}'), ` - - ( + + ( 1 n - ) + ) 2 @@ -181,7 +181,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('_3'), ` - + 3 @@ -195,7 +195,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('{}_3'), ` - + 3 @@ -209,7 +209,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('x_3'), ` - + x 3 @@ -223,7 +223,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('x^a_3'), ` - + x 3 a @@ -238,7 +238,7 @@ describe('Sub and Superscripts', () => { toXmlMatch( tex2mml('\\mathop{X}^2', false), ` - + X @@ -279,7 +279,7 @@ describe('Negations', () => { a b - + c d @@ -293,7 +293,7 @@ describe('Negations', () => { toXmlMatch( tex2mml(' \\not\\longrightarrow'), ` - ⟶̸ + ⟶̸ ` ); }); @@ -325,9 +325,9 @@ describe('Negations', () => { - - ( - + + ( + ` ); @@ -380,10 +380,10 @@ describe('Primes', () => { it('Double Prime', () => { toXmlMatch( tex2mml("x''"), - ` - + ` + x - + ` ); @@ -397,7 +397,7 @@ describe('Primes', () => { ` x - + ` ); @@ -411,7 +411,7 @@ describe('Primes', () => { ` x - + ` ); @@ -425,7 +425,7 @@ describe('Primes', () => { ` x - ′′′′′ + ′′′′′ ` ); @@ -608,8 +608,8 @@ describe('Digits', () => { toXmlMatch( tex2mml('1{,}0000.10'), ` - 1,000 - 0.10 + 1,000 + 0.10 ` ); }); @@ -691,8 +691,8 @@ describe('DigitsEuropean', () => { toXmlMatch( tex2mml('1{.}0000,10'), ` - 1.000 - 0,10 + 1.000 + 0,10 ` ); }); @@ -1160,18 +1160,18 @@ describe('Fenced', () => { toXmlMatch( tex2mml('\\left(\\frac{a}{\\left[bc\\right]}\\right)'), ` - - ( + + ( a - - [ + + [ b c - ] + ] - ) + ) ` ); @@ -1206,22 +1206,22 @@ describe('Fenced', () => { '\\left\\{\\left\\vert \\left[ \\left\\| A \\right.\\right| \\right]\\right\\}' ), ` - - { - - | - - [ - - + + { + + | + + [ + + A - + - | + | - ] + ] - } + } ` ); @@ -1233,14 +1233,14 @@ describe('Fenced', () => { toXmlMatch( tex2mml('\\left(a\\middle|b\\right)'), ` - - ( + + ( a - | + | b - ) + ) ` ); @@ -1273,11 +1273,11 @@ describe('Fenced', () => { toXmlMatch( tex2mml('\\left. ab \\right)'), ` - - + + a b - ) + ) ` ); @@ -1289,11 +1289,11 @@ describe('Fenced', () => { toXmlMatch( tex2mml('\\left( ab \\right.'), ` - - ( + + ( a b - + ` ); @@ -1307,18 +1307,18 @@ describe('Fenced', () => { '\\left\\{\\frac{a}{\\left\\uparrow bc\\right\\downarrow}\\right\\}' ), ` - - { + + { a - - + + b c - + - } + } ` ); @@ -1330,13 +1330,13 @@ describe('Fenced', () => { toXmlMatch( tex2mml('\\left\\uparrow \\frac{a}{b} \\right\\downarrow'), ` - - + + a b - + ` ); @@ -1366,17 +1366,17 @@ describe('Fenced', () => { '\\left\\uparrow \\frac{a}{b}\\middle\\downarrow c \\right\\uparrow' ), ` - - + + a b - + c - + ` ); @@ -1596,7 +1596,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('a \\over b'), ` - + a b @@ -1610,7 +1610,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('a + b \\over c'), ` - + a + @@ -1714,7 +1714,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('1 \\over 2'), ` - + 1 2 @@ -1728,7 +1728,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('a \\above 1pt b'), ` - + a b @@ -1742,7 +1742,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('n \\choose k'), ` - + ( @@ -1767,7 +1767,7 @@ describe('Stacking expressions', () => { X - + ( @@ -1797,7 +1797,7 @@ describe('Stacking expressions', () => { 1 - + ( @@ -1832,7 +1832,7 @@ describe('Stacking expressions', () => { 2 - + ( @@ -1872,7 +1872,7 @@ describe('Stacking expressions', () => { 3 - + ( @@ -1902,7 +1902,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('1 \\overwithdelims [ ] 2'), ` - + [ @@ -1927,7 +1927,7 @@ describe('Stacking expressions', () => { X - + [ @@ -1957,7 +1957,7 @@ describe('Stacking expressions', () => { 1 - + [ @@ -1992,7 +1992,7 @@ describe('Stacking expressions', () => { 2 - + [ @@ -2032,7 +2032,7 @@ describe('Stacking expressions', () => { 3 - + [ @@ -2062,7 +2062,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('a \\abovewithdelims [ ] 1pt b'), ` - + [ @@ -2087,7 +2087,7 @@ describe('Stacking expressions', () => { X - + [ @@ -2117,7 +2117,7 @@ describe('Stacking expressions', () => { 1 - + [ @@ -2152,7 +2152,7 @@ describe('Stacking expressions', () => { 2 - + [ @@ -2192,7 +2192,7 @@ describe('Stacking expressions', () => { 3 - + [ @@ -2228,7 +2228,7 @@ describe('Stacking expressions', () => { ) = - + ( @@ -2311,7 +2311,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('a \\atop b'), ` - + a b @@ -2325,7 +2325,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('a \\atopwithdelims [ ] b'), ` - + [ @@ -2347,7 +2347,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('n \\brack k'), ` - + [ @@ -2369,7 +2369,7 @@ describe('Stacking expressions', () => { toXmlMatch( tex2mml('n \\brace k'), ` - + { @@ -2503,7 +2503,7 @@ describe('MmlToken', () => { it('Token Invalid Attribute', () => { expectTexError('\\mmlToken{mi}[m1=true]{}') - .toBe('Invalid MathML attribute: m1=true'); + .toBe('Invalid MathML attribute: m1'); }); /********************************************************************************/ @@ -2559,7 +2559,7 @@ describe('Matrix', () => { tex2mml('\\matrix{a}'), ` - + a @@ -2576,7 +2576,7 @@ describe('Matrix', () => { tex2mml('\\array{a&b}'), ` - + a @@ -2596,7 +2596,7 @@ describe('Matrix', () => { tex2mml('\\array{a&b\\\\ c&d}'), ` - + a @@ -2604,7 +2604,7 @@ describe('Matrix', () => { b - + c @@ -2627,7 +2627,7 @@ describe('Matrix', () => { X - + a @@ -2651,7 +2651,7 @@ describe('Matrix', () => { ( - + a @@ -2678,7 +2678,7 @@ describe('Matrix', () => { ( - + a @@ -2704,7 +2704,7 @@ describe('Matrix', () => { ( - + a @@ -2728,7 +2728,7 @@ describe('Matrix', () => { { - + a @@ -2749,7 +2749,7 @@ describe('Matrix', () => { { - + a @@ -2770,7 +2770,7 @@ describe('Matrix', () => { { - + a @@ -2794,7 +2794,7 @@ describe('Matrix', () => { { - + a @@ -2818,7 +2818,7 @@ describe('Matrix', () => { { - + a @@ -2826,7 +2826,7 @@ describe('Matrix', () => { b - + c @@ -2850,7 +2850,7 @@ describe('Matrix', () => { { - + a @@ -2872,7 +2872,7 @@ describe('Matrix', () => { tex2mml('\\eqalignno{a&b&c}'), ` - + c @@ -2895,7 +2895,7 @@ describe('Matrix', () => { tex2mml('\\leqalignno{a&b&c}'), ` - + c @@ -2918,7 +2918,7 @@ describe('Matrix', () => { tex2mml('\\eqalign{a&b&c}'), ` - + a @@ -2941,12 +2941,12 @@ describe('Matrix', () => { tex2mml('\\displaylines{a\\\\ b}'), ` - + a - + b @@ -3104,8 +3104,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c}a\\end{array}'), ` - - + + a @@ -3121,9 +3121,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c|}a\\end{array}'), ` - + - + a @@ -3140,9 +3140,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c}a\\end{array}'), ` - + - + a @@ -3159,9 +3159,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c|}a\\end{array}'), ` - + - + a @@ -3178,9 +3178,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c}\\hline a\\end{array}'), ` - + - + a @@ -3197,9 +3197,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c} a\\\\\\hline\\end{array}'), ` - + - + a @@ -3216,9 +3216,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c}\\hline a\\\\\\hline\\end{array}'), ` - + - + a @@ -3235,8 +3235,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c|}\\hline a\\\\\\hline\\end{array}'), ` - - + + a @@ -3252,8 +3252,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{:c:}\\hline a\\\\\\hline\\end{array}'), ` - - + + a @@ -3269,8 +3269,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c:} a\\end{array}'), ` - - + + a @@ -3286,8 +3286,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c:c}a&c\\\\b&d\\end{array}'), ` - - + + a @@ -3295,7 +3295,7 @@ describe('Array', () => { c - + b @@ -3314,8 +3314,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c|c}a&c\\\\b&d\\end{array}'), ` - - + + a @@ -3323,7 +3323,7 @@ describe('Array', () => { c - + b @@ -3342,13 +3342,13 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c}a\\\\\\hdashline b\\end{array}'), ` - - + + a - + b @@ -3364,13 +3364,13 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c}a\\\\\\hline b\\end{array}'), ` - - + + a - + b @@ -3386,14 +3386,14 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c|}a\\\\\\hdashline b\\end{array}'), ` - + - + a - + b @@ -3410,14 +3410,14 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c|}a\\\\\\hline b\\end{array}'), ` - + - + a - + b @@ -3434,9 +3434,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c:c|}a&c\\\\b&d\\end{array}'), ` - + - + a @@ -3444,7 +3444,7 @@ describe('Array', () => { c - + b @@ -3464,9 +3464,9 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c|c|}a&c\\\\b&d\\end{array}'), ` - + - + a @@ -3474,7 +3474,7 @@ describe('Array', () => { c - + b @@ -3494,8 +3494,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}[b]{c}a\\end{array}'), ` - - + + a @@ -3511,8 +3511,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}[x]{c}a\\end{array}'), ` - - + + a @@ -3528,7 +3528,7 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{>{x}c} \\end{array}'), ` - + ` ); }); @@ -3540,7 +3540,7 @@ describe('Array', () => { tex2mml('\\eqalignno{a & & {\\hbox{(3)}}}'), ` - + @@ -3564,8 +3564,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\begin{array}{c|cc}a&b&c\\\\d&e&f\\end{array}'), ` - - + + a @@ -3576,7 +3576,7 @@ describe('Array', () => { c - + d @@ -3600,8 +3600,8 @@ describe('Array', () => { '\\begin{array}{ccc}a&b&c\\\\\\hline d&e&f\\\\ g&h&i \\end{array}' ), ` - - + + a @@ -3612,7 +3612,7 @@ describe('Array', () => { c - + d @@ -3623,7 +3623,7 @@ describe('Array', () => { f - + g @@ -3647,8 +3647,8 @@ describe('Array', () => { '\\begin{array}{c|cc}a&b&c\\\\\\hline d&e&f\\\\ g&h&i \\end{array}' ), ` - - + + a @@ -3659,7 +3659,7 @@ describe('Array', () => { c - + d @@ -3670,7 +3670,7 @@ describe('Array', () => { f - + g @@ -3694,8 +3694,8 @@ describe('Array', () => { '\\begin{array}{c|c:cc}0&a&b&c\\\\\\hline 1&d&e&f\\\\\\hdashline 2&g&h&i\\\\ 3&j&k&l \\end{array}' ), ` - - + + 0 @@ -3709,7 +3709,7 @@ describe('Array', () => { c - + 1 @@ -3723,7 +3723,7 @@ describe('Array', () => { f - + 2 @@ -3737,7 +3737,7 @@ describe('Array', () => { i - + 3 @@ -3764,10 +3764,10 @@ describe('Array', () => { '\\left( \\begin{array}{ccc}a & b & c \\\\d & e & f \\\\g & h & i \\end{array} \\right)' ), ` - - ( - - + + ( + + a @@ -3778,7 +3778,7 @@ describe('Array', () => { c - + d @@ -3789,7 +3789,7 @@ describe('Array', () => { f - + g @@ -3801,7 +3801,7 @@ describe('Array', () => { - ) + ) ` ); @@ -3813,8 +3813,8 @@ describe('Array', () => { toXmlMatch( tex2mml('\\newcolumntype{a}{c}\\begin{array}{a|a}a&b\\\\c&d\\end{array}'), ` - - + + a @@ -3822,7 +3822,7 @@ describe('Array', () => { b - + c @@ -3841,7 +3841,7 @@ describe('Array', () => { toXmlMatch( tex2mml('\\newcolumntype{a}[1]{m{#1}}\\begin{array}{a{1em}|a{2em}}a&b\\\\c&d\\end{array}'), ` - + @@ -3914,7 +3914,7 @@ describe('Moving limits', () => { toXmlMatch( tex2mml('\\int^2\\limits_3'), ` - + 3 2 @@ -3929,7 +3929,7 @@ describe('Moving limits', () => { toXmlMatch( tex2mml('\\lim_3\\nolimits^2'), ` - + lim 3 2 @@ -3944,7 +3944,7 @@ describe('Moving limits', () => { toXmlMatch( tex2mml('\\sum\\limits^2_3'), ` - + 3 2 @@ -3992,7 +3992,7 @@ describe('Moving limits', () => { - + 2 @@ -4053,7 +4053,7 @@ describe('Moving limits', () => { toXmlMatch( tex2mml('\\overline{\\mathop{a}}^2'), ` - + a @@ -4075,7 +4075,7 @@ describe('Moving limits', () => { - + 3 2 @@ -4093,7 +4093,7 @@ describe('Moving limits', () => { toXmlMatch( tex2mml('\\overline{\\sum}^2_3'), ` - + @@ -4136,7 +4136,7 @@ describe('Moving limits', () => { X - + y @@ -4208,7 +4208,7 @@ describe('Moving limits', () => { toXmlMatch( tex2mml('\\overbrace{\\mathop{a}}^2'), ` - + @@ -4245,7 +4245,7 @@ describe('Moving limits', () => { toXmlMatch( tex2mml('\\overbrace{\\sum}^2'), ` - + @@ -4371,8 +4371,8 @@ describe('Multirel', () => { tex2mml('a||b'), ` a - | - | + | + | b ` ); @@ -4684,9 +4684,7 @@ describe('Other', () => { toXmlMatch( tex2mml('$'), ` - - $ - + $ ` ); }); @@ -4697,9 +4695,7 @@ describe('Other', () => { toXmlMatch( tex2mml('˦'), ` - - ˦ - + ˦ ` ); }); @@ -4721,7 +4717,7 @@ describe('Other', () => { toXmlMatch( tex2mml('⤡'), ` - + ` ); }); @@ -4733,7 +4729,7 @@ describe('Other', () => { tex2mml('a⤡b'), ` a - + b ` ); @@ -4745,7 +4741,7 @@ describe('Other', () => { toXmlMatch( tex2mml('⤡b'), ` - + b ` ); @@ -4758,7 +4754,7 @@ describe('Other', () => { tex2mml('b⤡'), ` b - + ` ); }); @@ -4769,7 +4765,7 @@ describe('Other', () => { toXmlMatch( tex2mml('|'), ` - | + | ` ); }); @@ -4781,7 +4777,7 @@ describe('Other', () => { tex2mml('a|b'), ` a - | + | b ` ); @@ -4818,9 +4814,7 @@ describe('Other', () => { tex2mml('褯¥'), ` - - ¥ - + ¥ ` ); }); @@ -4929,8 +4923,8 @@ describe('Base Complex', () => { ), ` - - ( + + ( @@ -4948,7 +4942,7 @@ describe('Base Complex', () => { b k - ) + ) @@ -4961,8 +4955,8 @@ describe('Base Complex', () => { - - ( + + ( @@ -4977,10 +4971,10 @@ describe('Base Complex', () => { k 2 - ) + ) - - ( + + ( @@ -4995,7 +4989,7 @@ describe('Base Complex', () => { k 2 - ) + ) ` ); @@ -5050,7 +5044,7 @@ describe('Base Complex', () => { π - + 1 + @@ -5062,7 +5056,7 @@ describe('Base Complex', () => { π - + 1 + @@ -5074,7 +5068,7 @@ describe('Base Complex', () => { π - + 1 + @@ -5113,7 +5107,7 @@ describe('Base Complex', () => { 1 + - + q 2 @@ -5127,7 +5121,7 @@ describe('Base Complex', () => { + - + q 6 @@ -5200,7 +5194,7 @@ describe('Base Complex', () => { | q - | + | < 1 @@ -5216,17 +5210,17 @@ describe('Base Complex', () => { toXmlMatch( tex2mml('\\sum_{n=1}^\\infty {1\\over n^2} = {\\pi^2\\over 6}'), ` - + n = 1 - + - + 1 n @@ -5236,7 +5230,7 @@ describe('Base Complex', () => { = - + π 2 @@ -5259,9 +5253,9 @@ describe('Base Complex', () => { a ) = - + - γ + γ @@ -5337,7 +5331,7 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{eqnarray}a&=&b\\end{eqnarray}'), ` - + a @@ -5376,7 +5370,7 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{displaymath}a\\end{displaymath}'), ` - a + a ` ); }); @@ -5387,7 +5381,7 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{math}a\\end{math}'), ` - a + a ` ); }); @@ -5398,8 +5392,8 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{array}{c}a\\end{array}'), ` - - + + a @@ -5415,13 +5409,13 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{array}{c}a\\\\b\\end{array}'), ` - - + + a - + b @@ -5437,8 +5431,8 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{array}{rcl}a&=&b\\end{array}'), ` - - + + a @@ -5460,8 +5454,8 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{array}{rcl}a&=&b\\\\c&=&d\\end{array}'), ` - - + + a @@ -5472,7 +5466,7 @@ describe('Environments', () => { b - + c @@ -5494,8 +5488,8 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{darray}{c}a\\end{darray}'), ` - - + + a @@ -5511,13 +5505,13 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{darray}{c}a\\\\b\\end{darray}'), ` - - + + a - + b @@ -5533,8 +5527,8 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{darray}{rcl}a&=&b\\end{darray}'), ` - - + + a @@ -5556,8 +5550,8 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{darray}{rcl}a&=&b\\\\c&=&d\\end{darray}'), ` - - + + a @@ -5568,7 +5562,7 @@ describe('Environments', () => { b - + c @@ -5590,8 +5584,8 @@ describe('Environments', () => { toXmlMatch( tex2mml('\\begin{array}{rcl}a&{}{b}&c\\begin{array}{cc}f&h\\\\g\\end{array}\\end{array}'), ` - - + + a @@ -5603,8 +5597,8 @@ describe('Environments', () => { c - - + + f @@ -5612,7 +5606,7 @@ describe('Environments', () => { h - + g @@ -5716,7 +5710,7 @@ describe('BreakAlign', () => { toXmlMatch( tex2mml('\\begin{eqnarray}\\breakAlign{c}{t}a&=&b\\end{eqnarray}'), ` - + a @@ -5742,7 +5736,7 @@ describe('BreakAlign', () => { toXmlMatch( tex2mml('\\begin{eqnarray}a&\\breakAlign{c}{t}=&b\\end{eqnarray}'), ` - + a @@ -5768,7 +5762,7 @@ describe('BreakAlign', () => { toXmlMatch( tex2mml('\\begin{eqnarray}\\breakAlign{r}{t}a&=&b\\end{eqnarray}'), ` - + a @@ -5794,7 +5788,7 @@ describe('BreakAlign', () => { toXmlMatch( tex2mml('\\begin{eqnarray}\\breakAlign{r}{t}a&=&b\\\\\\breakAlign{r}{t}c&=&d\\end{eqnarray}'), ` - + a @@ -5834,7 +5828,7 @@ describe('BreakAlign', () => { toXmlMatch( tex2mml('\\begin{eqnarray}\\breakAlign{r}{tbmc}a&=&b\\end{eqnarray}'), ` - + a @@ -5860,7 +5854,7 @@ describe('BreakAlign', () => { toXmlMatch( tex2mml('\\begin{eqnarray}\\breakAlign{t}{t}a&=&b\\end{eqnarray}'), ` - + a @@ -6275,8 +6269,8 @@ describe('Spaces', () => { toXmlMatch( tex2mml('\\begin{array}{cc}a&\\hfil b\\\\d&ccc\\end{array}'), ` - - + + a @@ -6284,7 +6278,7 @@ describe('Spaces', () => { b - + d @@ -6305,8 +6299,8 @@ describe('Spaces', () => { toXmlMatch( tex2mml('\\begin{array}{cc}a&\\hfill b\\\\d&ccc\\end{array}'), ` - - + + a @@ -6314,7 +6308,7 @@ describe('Spaces', () => { b - + d @@ -6335,8 +6329,8 @@ describe('Spaces', () => { toXmlMatch( tex2mml('\\begin{array}{cc}a&\\hfilll b\\\\d&ccc\\end{array}'), ` - - + + a @@ -6344,7 +6338,7 @@ describe('Spaces', () => { b - + d @@ -6365,8 +6359,8 @@ describe('Spaces', () => { toXmlMatch( tex2mml('\\begin{array}{l} \\hfil x\\hfil \\end{array}'), ` - - + + x @@ -6405,10 +6399,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left< a \\right>'), ` - - + + a - + ` ); @@ -6433,10 +6427,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\lt a \\right\\gt'), ` - - + + a - + ` ); @@ -6448,9 +6442,7 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('/ a \\\\'), ` - - / - + / a ` @@ -6483,10 +6475,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\lmoustache a \\right\\rmoustache'), ` - - + + a - + ` ); @@ -6511,10 +6503,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\lgroup a \\right\\rgroup'), ` - - + + a - + ` ); @@ -6539,10 +6531,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\arrowvert a \\right\\Arrowvert'), ` - - + + a - + ` ); @@ -6567,10 +6559,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\bracevert a \\right\\Vert'), ` - - + + a - + ` ); @@ -6595,10 +6587,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\updownarrow a \\right\\Updownarrow'), ` - - + + a - + ` ); @@ -6610,9 +6602,7 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('/ a \\backslash'), ` - - / - + / a \\ ` @@ -6625,10 +6615,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left/ a \\right\\backslash'), ` - - / + + / a - \\ + \\ ` ); @@ -6653,10 +6643,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\Uparrow a \\right\\Downarrow'), ` - - + + a - + ` ); @@ -6681,10 +6671,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\rangle a \\right\\langle'), ` - - + + a - + ` ); @@ -6709,10 +6699,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\rbrace a \\right\\lbrace'), ` - - } + + } a - { + { ` ); @@ -6737,10 +6727,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\rceil a \\right\\lceil'), ` - - + + a - + ` ); @@ -6765,10 +6755,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\rfloor a \\right\\lfloor'), ` - - + + a - + ` ); @@ -6793,10 +6783,10 @@ describe('Delimiters', () => { toXmlMatch( tex2mml('\\left\\lbrack a \\right\\rbrack'), ` - - [ + + [ a - ] + ] ` ); @@ -6884,9 +6874,9 @@ describe('Named Functions', () => { ` sin - - ( - ) + + ( + ) ` ); @@ -7938,9 +7928,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\surd'), ` - - - + ` ); }); @@ -8776,9 +8764,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\perp'), ` - - - + ` ); }); @@ -8800,7 +8786,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\smile'), ` - + ` ); }); @@ -8811,7 +8797,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\frown'), ` - + ` ); }); @@ -9009,7 +8995,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\nearrow'), ` - + ` ); }); @@ -9020,7 +9006,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\searrow'), ` - + ` ); }); @@ -9031,7 +9017,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\nwarrow'), ` - + ` ); }); @@ -9042,7 +9028,7 @@ describe('Mathchar0mo', () => { toXmlMatch( tex2mml('\\swarrow'), ` - + ` ); }); @@ -9214,10 +9200,10 @@ describe('Dots', () => { tex2mml('\\dots\\left( A\\right)'), ` - - ( + + ( A - ) + ) ` ); @@ -9254,9 +9240,7 @@ describe('Dots', () => { toXmlMatch( tex2mml('\\vdots'), ` - - - + ` ); }); @@ -10517,7 +10501,7 @@ describe('Special macros', () => { tex2mml('A \\ B'), ` A -   +   B ` ); @@ -10530,7 +10514,7 @@ describe('Special macros', () => { tex2mml('A \\ B'), ` A -   +   B ` ); @@ -10552,7 +10536,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\big|'), ` - | + | ` ); @@ -10565,7 +10549,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\Big|'), ` - | + | ` ); @@ -10578,7 +10562,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\bigg|'), ` - | + | ` ); @@ -10591,7 +10575,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\Bigg|'), ` - | + | ` ); @@ -10604,7 +10588,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\Biggl|'), ` - | + | ` ); @@ -10617,7 +10601,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\Biggr|'), ` - | + | ` ); @@ -10630,7 +10614,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\bigm|'), ` - | + | ` ); @@ -10643,7 +10627,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\Bigm|'), ` - | + | ` ); @@ -10656,7 +10640,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\biggm|'), ` - | + | ` ); @@ -10669,7 +10653,7 @@ describe('Big Commands for Delimiters', () => { tex2mml('\\Biggm|'), ` - | + | ` ); @@ -11170,9 +11154,7 @@ describe('Moving Elements', () => { - - / - + / @@ -11197,9 +11179,7 @@ describe('Moving Elements', () => { - - / - + / @@ -11459,12 +11439,12 @@ describe('Linebreaks', () => { tex2mml('\\array{a\\cr b}'), ` - + a - + b @@ -11481,12 +11461,12 @@ describe('Linebreaks', () => { tex2mml('\\array{a\\\\[1cm] b}'), ` - + a - + b @@ -11692,9 +11672,7 @@ describe('Linebreaks', () => { toXmlMatch( tex2mml('\u2220\\goodbreak )\\goodbreak'), ` - - - + ) ` @@ -12198,6 +12176,33 @@ describe('Character Class Changes', () => { /********************************************************************************/ + it('Mathop Apply', () => { + toXmlMatch( + tex2mml('\\mathop{F} x'), + ` + + F + + + x + ` + ); + }); + + /********************************************************************************/ + + it('Mathop No Apply', () => { + toXmlMatch( + tex2mml('\\mathop{} x'), + ` + + x + ` + ); + }); + + /********************************************************************************/ + it('Mathrel', () => { toXmlMatch( tex2mml('\\mathrel{R}'), @@ -12315,16 +12320,16 @@ describe('Spacing', () => { toXmlMatch( tex2mml('\\left. a \\nonscript\\;\\middle|\\nonscript\\; b \\right.'), ` - - + + a - | + | b - + ` ); @@ -12339,14 +12344,14 @@ describe('Spacing', () => { X - - + + a - | + | b - + @@ -12482,7 +12487,9 @@ describe('Referencing', () => { - (1) + ( + 1 + ) a @@ -12502,7 +12509,9 @@ describe('Referencing', () => { - (1) + ( + 1 + ) a @@ -12519,10 +12528,12 @@ describe('Referencing', () => { toXmlMatch( tex2mml('\\begin{eqnarray}a\\label{A}\\\\c\\label{B}\\end{eqnarray}'), ` - + - (1) + ( + 1 + ) a @@ -12530,7 +12541,9 @@ describe('Referencing', () => { - (2) + ( + 2 + ) c @@ -12564,7 +12577,9 @@ describe('Referencing', () => { - (1) + ( + 1 + ) a @@ -12587,7 +12602,9 @@ describe('Referencing', () => { - (1) + ( + 1 + ) a @@ -12607,10 +12624,12 @@ describe('Referencing', () => { toXmlMatch( tex2mml('\\begin{eqnarray}a\\\\c\\nonumber\\end{eqnarray}'), ` - + - (1) + ( + 1 + ) a @@ -12641,8 +12660,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{rcl}a & b &c\\\\ d & e & f\\end{array}'), ` - - + + a @@ -12653,7 +12672,7 @@ describe('Complete Array', () => { c - + d @@ -12675,8 +12694,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r|c|l}a & b & c\\\\d & e & f\\end{array}'), ` - - + + a @@ -12687,7 +12706,7 @@ describe('Complete Array', () => { c - + d @@ -12709,8 +12728,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r:c:l}a & b &c\\\\ d & e & f\\end{array}'), ` - - + + a @@ -12721,7 +12740,7 @@ describe('Complete Array', () => { c - + d @@ -12743,7 +12762,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r@{h}c@{h}l}a & b &c\\\\ d & e & f\\end{array}'), ` - + a @@ -12789,7 +12808,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r!{h}c!{h}l}a & b &c\\\\ d & e & f\\end{array}'), ` - + a @@ -12835,7 +12854,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{p{1cm}p{1cm}p{1cm}}a & b &c\\\\ d & e & f\\end{array}'), ` - + @@ -12881,7 +12900,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{m{1cm}m{1cm}m{1cm}}a & b &c\\\\ d & e & f\\end{array}'), ` - + @@ -12927,7 +12946,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{b{1cm}b{1cm}b{1cm}}a & b &c\\\\ d & e & f\\end{array}'), ` - + @@ -12973,7 +12992,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{>{A}rcl}a & b &c\\\\ d & e & f\\end{array}'), ` - + A @@ -13009,7 +13028,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r<{A}cl}a & b &c\\\\ d & e & f\\end{array}'), ` - + a @@ -13045,7 +13064,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{>{A}c}a \\\\ d\\end{array}'), ` - + A @@ -13069,7 +13088,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{c<{A}}a\\\\ d \\end{array}'), ` - + a @@ -13093,7 +13112,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{c@{h}}a\\\\ d \\end{array}'), ` - + a @@ -13121,7 +13140,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{c@{\\alpha}c}a&\\hfill&b\\\\ d&\\hfill&e \\end{array}'), ` - + a @@ -13157,8 +13176,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{cw{c}{1cm}c}a&b&c\\\\ d&e&f \\end{array}'), ` - - + + a @@ -13171,7 +13190,7 @@ describe('Complete Array', () => { c - + d @@ -13195,8 +13214,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{cW{c}{1cm}c}a&b&c\\\\ d&e&f \\end{array}'), ` - - + + a @@ -13209,7 +13228,7 @@ describe('Complete Array', () => { c - + d @@ -13233,8 +13252,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{*{2}rc}a & b & c\\\\d & e & f\\end{array}'), ` - - + + a @@ -13245,7 +13264,7 @@ describe('Complete Array', () => { c - + d @@ -13267,8 +13286,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r*{2}c}a & b & c\\\\d & e & f\\end{array}'), ` - - + + a @@ -13279,7 +13298,7 @@ describe('Complete Array', () => { c - + d @@ -13301,7 +13320,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r*{2}|c}a& b & c\\\\d & e & f\\end{array}'), ` - + a @@ -13346,7 +13365,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r*{2}|c}a {\\hbox{(3)}}& b & c\\\\d & e & f\\end{array}'), ` - + a @@ -13393,13 +13412,13 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r*{2}|c}a {\\begin{array}{c}Q\\end{array}}& b & c\\\\d & e & f\\end{array}'), ` - + a - - + + Q @@ -13446,12 +13465,12 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{r*{2}|c}a \\begin{array}{c}Q\\end{array}& b & c\\\\d & e & f\\end{array}'), ` - + a - - + + Q @@ -13498,7 +13517,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{c@{\\alpha}c}a&&b\\\\ d&&e\\hfil \\end{array}'), ` - + a @@ -13541,8 +13560,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{rcl}a & b &c\\\\[2cm] d & e & f\\\\[2cm] \\end{array}'), ` - - + + a @@ -13553,7 +13572,7 @@ describe('Complete Array', () => { c - + d @@ -13575,7 +13594,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{eqnarray}{rcl}a & b \\\\d&c&c&c \\\\\\end{eqnarray}'), ` - + @@ -13616,13 +13635,13 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{|c|}\\hline a\\\\\\hline b\\\\\\hline\\end{array}'), ` - - + + a - + b @@ -13638,7 +13657,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{!{a}|@{b}c} X\\end{array}'), ` - + a @@ -13662,7 +13681,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{@{}|!{x}c} X \\end{array}'), ` - + @@ -13684,7 +13703,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{@{x}p{1em}m{1em}} X & Y \\end{array}'), ` - + x @@ -13711,8 +13730,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{cc@{x}} X & Y \\end{array}'), ` - - + + X @@ -13734,8 +13753,8 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{cc|c} X & Y & Z \\end{array}'), ` - - + + X @@ -13757,7 +13776,7 @@ describe('Complete Array', () => { toXmlMatch( tex2mml('\\begin{array}{> {x} c} X \\end{array}'), ` - + x @@ -13906,15 +13925,15 @@ describe('User Defined Macros', () => { toXmlMatch( tex2mml('\\left(A\\middle|B\\color{red}\\right)'), ` - - ( + + ( A - | + | B - ) + ) ` ); @@ -13926,15 +13945,15 @@ describe('User Defined Macros', () => { toXmlMatch( tex2mml('\\left(A\\color{red}\\middle|B\\right)'), ` - - ( + + ( A - | + | B - ) + ) ` ); @@ -14000,7 +14019,7 @@ describe('User Defined Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix} a & b \\\\ c & d \\end{smallmatrix}'), ` - + @@ -14030,7 +14049,7 @@ describe('User Defined Environments', () => { toXmlMatch( tex2mml('\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}'), ` - + ( @@ -14062,9 +14081,9 @@ describe('User Defined Environments', () => { toXmlMatch( tex2mml('\\begin{crampedsubarray}{cc} a & b \\\\ c & d \\end{crampedsubarray}'), ` - + - + a @@ -14072,7 +14091,7 @@ describe('User Defined Environments', () => { b - + c @@ -14092,7 +14111,7 @@ describe('User Defined Environments', () => { toXmlMatch( tex2mml('\\begin{gather}a\\end{gather}'), ` - + a @@ -14121,8 +14140,8 @@ describe('User Defined Environments', () => { it('Cases star', () => { toXmlMatch( tex2mml('\\begin{mmtool} a & test a\\\\ b & test b \\end{mmtool}'), - ` - + ` + { @@ -14154,7 +14173,7 @@ describe('User Defined Environments', () => { toXmlMatch( tex2mml('\\begin{eqntest} a & b \\end{eqntest}'), ` - + a @@ -14185,11 +14204,13 @@ describe('Tagged Environments', () => { it('EqnTest', () => { toXmlMatch( tex2mml('\\begin{eqntest} a & b \\end{eqntest}'), - ` - + ` + - (1) + ( + 1 + ) a @@ -14211,10 +14232,12 @@ describe('Tagged Environments', () => { toXmlMatch( tex2mml('\\begin{equation} x \\end{equation}'), ` - + - (1) + ( + 1 + ) x diff --git a/testsuite/tests/input/tex/Bbm.test.ts b/testsuite/tests/input/tex/Bbm.test.ts index 4b3ae11b1..224a45583 100644 --- a/testsuite/tests/input/tex/Bbm.test.ts +++ b/testsuite/tests/input/tex/Bbm.test.ts @@ -1,5 +1,5 @@ -import { afterAll, beforeEach, describe, test } from '@jest/globals'; -import { getTokens, toXmlMatch, setupTex, tex2mml } from '#helpers'; +import { afterAll, beforeEach, describe, test, expect } from '@jest/globals'; +import { getTokens, toXmlMatch, setupTex, setupComponents, tex2mml } from '#helpers'; import '#js/input/tex/bbm/BbmConfiguration'; /**********************************************************************************/ @@ -153,4 +153,18 @@ describe('Bbm', () => { /**********************************************************************************/ /**********************************************************************************/ +declare const MathJax: any; + +setupComponents({loader: {load: ['input/tex-base', 'output/chtml']}}); + +describe('Bbm', () => { + + test('bbm with no output', async() => { + await expect(MathJax.loader.load('[tex]/bbm').then(() => true)).resolves.toBe(true); + }); +}); + +/**********************************************************************************/ +/**********************************************************************************/ + afterAll(() => getTokens('bbm')); diff --git a/testsuite/tests/input/tex/Bboldx.test.ts b/testsuite/tests/input/tex/Bboldx.test.ts index a45fd2690..856f9efcc 100644 --- a/testsuite/tests/input/tex/Bboldx.test.ts +++ b/testsuite/tests/input/tex/Bboldx.test.ts @@ -17,7 +17,7 @@ describe('Bboldx', () => { tex2mml('\\mathbb{Aa\u{393}\u{3B3}}'), ` - Aa + Aa Γ γ @@ -32,7 +32,7 @@ describe('Bboldx', () => { tex2mml('\\mathbfbb{Aa\u{393}\u{3B3}}'), ` - Aa + Aa Γ γ @@ -479,7 +479,7 @@ describe('Bboldx light', () => { tex2mml('\\mathbb{Aa\u{393}\u{3B3}}'), ` - Aa + Aa Γ γ @@ -494,7 +494,7 @@ describe('Bboldx light', () => { tex2mml('\\mathbfbb{Aa\u{393}\u{3B3}}'), ` - Aa + Aa Γ γ @@ -534,7 +534,7 @@ describe('Bboldx bfbb', () => { tex2mml('\\mathbb{Aa\u{393}\u{3B3}}'), ` - Aa + Aa Γ γ @@ -549,7 +549,7 @@ describe('Bboldx bfbb', () => { tex2mml('\\mathbfbb{Aa\u{393}\u{3B3}}'), ` - Aa + Aa Γ γ diff --git a/testsuite/tests/input/tex/Begingroup.test.ts b/testsuite/tests/input/tex/Begingroup.test.ts index ccc4c7184..263a30103 100644 --- a/testsuite/tests/input/tex/Begingroup.test.ts +++ b/testsuite/tests/input/tex/Begingroup.test.ts @@ -40,10 +40,10 @@ describe('Begingroup', () => { test('Begingroup Def Single', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\def\\x{B} \\x \\endgroup \\x'), - ` - A - B - A + ` + A + B + A ` ); }); @@ -53,12 +53,12 @@ describe('Begingroup', () => { test('Begingroup Def Nested', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\def\\x{B} \\x \\begingroup \\def\\x{C} \\x \\endgroup \\x \\endgroup \\x'), - ` - A - B - C - B - A + ` + A + B + C + B + A ` ); }); @@ -68,10 +68,10 @@ describe('Begingroup', () => { test('Begingroup Let Single', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\let\\x=B \\x \\endgroup \\x'), - ` - A - B - A + ` + A + B + A ` ); }); @@ -81,12 +81,12 @@ describe('Begingroup', () => { test('Begingroup Let Nested', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\let\\x=B \\x \\begingroup \\let\\x=C \\x \\endgroup \\x \\endgroup \\x'), - ` - A - B - C - B - A + ` + A + B + C + B + A ` ); }); @@ -96,16 +96,16 @@ describe('Begingroup', () => { test('Begingroup Env Single', () => { toXmlMatch( tex2mml('\\newenvironment{test}{[}{]}\\begin{test}X\\end{test}\\begingroup\\newenvironment{test}{(}{)}\\begin{test}X\\end{test}\\endgroup\\begin{test}X\\end{test}'), - ` - [ - X - ] - ( - X - ) - [ - X - ] + ` + [ + X + ] + ( + X + ) + [ + X + ] ` ); }); @@ -115,21 +115,21 @@ describe('Begingroup', () => { test('Begingroup Delimiter Single', () => { toXmlMatch( tex2mml('\\let\\x=\\| \\left\\x A\\right\\x \\begingroup \\let\\x=| \\left\\x B \\right\\x \\endgroup \\left\\x C \\right\\x'), - ` - - - A - + ` + + + A + - - | - B - | + + | + B + | - - - C - + + + C + ` ); @@ -140,31 +140,31 @@ describe('Begingroup', () => { test('Begingroup Delimiter Nested', () => { toXmlMatch( tex2mml('\\let\\x=\\| \\left\\x A\\right\\x \\begingroup \\let\\x=| \\left\\x B \\right\\x \\begingroup \\let\\x=( \\left\\x C \\right\\x \\endgroup \\left\\x D \\right\\x \\endgroup \\left\\x E \\right\\x'), - ` - - - A - + ` + + + A + - - | - B - | + + | + B + | - - ( - C - ( + + ( + C + ( - - | - D - | + + | + D + | - - - E - + + + E + ` ); @@ -175,11 +175,11 @@ describe('Begingroup', () => { test('Begingroup Global', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\def\\x{B} \\x \\global\\def\\x{C} \\x \\endgroup \\x'), - ` - A - B - C - C + ` + A + B + C + C ` ); }); @@ -189,12 +189,12 @@ describe('Begingroup', () => { test('Begingroup Global Nested', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\def\\x{B} \\x \\begingroup \\gdef\\x{C} \\x \\endgroup \\x \\endgroup \\x'), - ` - A - B - C - C - C + ` + A + B + C + C + C ` ); }); @@ -204,11 +204,11 @@ describe('Begingroup', () => { test('Begingroup Global Let', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\let\\x=B \\x \\global\\let\\x=C \\x \\endgroup \\x'), - ` - A - B - C - C + ` + A + B + C + C ` ); }); @@ -218,13 +218,13 @@ describe('Begingroup', () => { test('Begingroup Persists', () => { toXmlMatch( tex2mml('\\def\\x{A} \\begingroup \\def\\x{B}'), - `` + `` ); toXmlMatch( tex2mml('\\x \\endgroup \\x'), - ` - B - A + ` + B + A ` ); }); @@ -234,8 +234,8 @@ describe('Begingroup', () => { test('Begingroup Reset', () => { toXmlMatch( tex2mml('\\def\\x{A} \\begingroup \\def\\x{B} \\begingroupReset \\x'), - ` - A + ` + A ` ); }); @@ -270,8 +270,8 @@ describe('Begingroup', () => { test('Begingroup reset 2', () => { toXmlMatch( tex2mml('\\def\\x{A} \\begingroup \\def\\x{B} \\begingroupReset \\x'), - ` - A + ` + A ` ); }); @@ -288,8 +288,8 @@ describe('Begingroup', () => { test('Begingroup let undefined 2', () => { toXmlMatch( tex2mml('\\def\\x{A} \\begingroup \\let\\x=\\undefined \\endgroup \\x'), - ` - A + ` + A ` ); }); @@ -299,12 +299,12 @@ describe('Begingroup', () => { test('Begingroup global def undefines local delimiter', () => { toXmlMatch( tex2mml('\\def\\x{A} \\x \\begingroup \\let\\x=\\| \\x \\begingroup \\gdef\\x{C} \\x \\endgroup \\x \\endgroup \\x'), - ` - A - - C - C - C + ` + A + + C + C + C ` ); }); @@ -314,12 +314,12 @@ describe('Begingroup', () => { test('Begingroup global let delimiter undefines local def', () => { toXmlMatch( tex2mml('\\let\\x=| \\x \\begingroup \\def\\x{A} \\x \\begingroup \\global\\let\\x=\\| \\x \\endgroup \\x \\endgroup \\x'), - ` - | - A - - - + ` + | + A + + + ` ); }); @@ -329,8 +329,8 @@ describe('Begingroup', () => { test('Begingroup sandbox', () => { toXmlMatch( tex2mml('\\def\\x{A} \\begingroupSandbox \\def\\x{B} \\begingroupReset \\x'), - ` - B + ` + B ` ); }); @@ -340,8 +340,8 @@ describe('Begingroup', () => { test('Begingroup double sandbox', () => { toXmlMatch( tex2mml('\\def\\x{A} \\begingroupSandbox \\def\\x{B} \\begingroupSandbox \\x'), - ` - A + ` + A ` ); }); diff --git a/testsuite/tests/input/tex/Braket.test.ts b/testsuite/tests/input/tex/Braket.test.ts index f651eccc5..01c30b930 100644 --- a/testsuite/tests/input/tex/Braket.test.ts +++ b/testsuite/tests/input/tex/Braket.test.ts @@ -53,15 +53,15 @@ describe('Braket', () => { tex2mml('\\Bra{\\frac{x}{y}}'), ` - - + + x y - | + | ` @@ -112,15 +112,15 @@ describe('Braket', () => { tex2mml('\\Ket{\\frac{x}{y}}'), ` - - | + + | x y - + ` @@ -232,22 +232,22 @@ describe('Braket', () => { tex2mml('\\Ketbra{\\frac{x}{y}}{z}'), ` - - | + + | x y - + - - + + z - | + | ` @@ -317,7 +317,7 @@ describe('Braket', () => { x - | + | x @@ -339,7 +339,7 @@ describe('Braket', () => { { - + x y @@ -359,7 +359,7 @@ describe('Braket', () => { x - | + | y @@ -379,7 +379,7 @@ describe('Braket', () => { x y - | + | z @@ -400,7 +400,7 @@ describe('Braket', () => { y - | + | z @@ -451,7 +451,7 @@ describe('Braket', () => { - | + | z @@ -480,7 +480,7 @@ describe('Braket', () => { - | + | z @@ -509,14 +509,14 @@ describe('Braket', () => { - | + | z - | + | y @@ -542,8 +542,8 @@ describe('Braket', () => { y - | - | + | + | z } @@ -568,10 +568,10 @@ describe('Braket', () => { - | + | y - | + | z } @@ -589,7 +589,7 @@ describe('Braket', () => { a - | + | b @@ -608,7 +608,7 @@ describe('Braket', () => { a - | + | b ` ); @@ -628,7 +628,7 @@ describe('Braket', () => { - | + | b ` ); @@ -645,7 +645,7 @@ describe('Braket', () => { a - | + | b @@ -665,6 +665,20 @@ describe('Braket', () => { /********************************************************************************/ + it('Braket-braces', () => { + expectTexError('\\braket{') + .toBe('Missing close brace'); + }); + + /********************************************************************************/ + + it('Braket-braces', () => { + expectTexError('\\braket}') + .toBe('Extra close brace or missing open brace'); + }); + + /********************************************************************************/ + }); /**********************************************************************************/ diff --git a/testsuite/tests/input/tex/Bussproofs.test.ts b/testsuite/tests/input/tex/Bussproofs.test.ts index 5ef5b0275..eed7ddbb1 100644 --- a/testsuite/tests/input/tex/Bussproofs.test.ts +++ b/testsuite/tests/input/tex/Bussproofs.test.ts @@ -16,7 +16,7 @@ describe('BussproofsRegInf', () => { toXmlMatch( tex2mml('\\begin{prooftree}\\AxiomC{A}\\end{prooftree}'), ` - + A @@ -33,7 +33,7 @@ describe('BussproofsRegInf', () => { toXmlMatch( tex2mml('\\begin{prooftree}\\AxiomC{A}\\UnaryInfC{B}\\end{prooftree}'), ` - + @@ -71,7 +71,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AxiomC{A}\\AxiomC{B}\\BinaryInfC{C}\\end{prooftree}' ), ` - + @@ -117,7 +117,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AxiomC{A}\\AxiomC{B}\\AxiomC{C}\\TrinaryInfC{D}\\end{prooftree}' ), ` - + @@ -171,7 +171,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AxiomC{A}\\AxiomC{B}\\AxiomC{C}\\AxiomC{D}\\QuaternaryInfC{E}\\end{prooftree}' ), ` - + @@ -233,7 +233,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AxiomC{A}\\AxiomC{B}\\AxiomC{C}\\AxiomC{D}\\AxiomC{E}\\QuinaryInfC{F}\\end{prooftree}' ), ` - + @@ -303,7 +303,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AxiomC{A}\\LeftLabel{L}\\UnaryInfC{B}\\end{prooftree}' ), ` - + L @@ -348,7 +348,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AxiomC{A}\\RightLabel{R}\\UnaryInfC{B}\\end{prooftree}' ), ` - + @@ -393,7 +393,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AxiomC{A}\\LeftLabel{L}\\RightLabel{R}\\UnaryInfC{B}\\end{prooftree}' ), ` - + L @@ -441,7 +441,7 @@ describe('BussproofsRegInf', () => { toXmlMatch( tex2mml('\\begin{prooftree}\\AXC{A}\\end{prooftree}'), ` - + A @@ -458,7 +458,7 @@ describe('BussproofsRegInf', () => { toXmlMatch( tex2mml('\\begin{prooftree}\\AXC{A}\\UIC{B}\\end{prooftree}'), ` - + @@ -494,7 +494,7 @@ describe('BussproofsRegInf', () => { toXmlMatch( tex2mml('\\begin{prooftree}\\AXC{A}\\AXC{B}\\BIC{C}\\end{prooftree}'), ` - + @@ -540,7 +540,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AXC{A}\\AXC{B}\\AXC{C}\\TIC{D}\\end{prooftree}' ), ` - + @@ -594,7 +594,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AXC{A}\\LeftLabel{L}\\UIC{B}\\end{prooftree}' ), ` - + L @@ -639,7 +639,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AXC{A}\\RightLabel{R}\\UIC{B}\\end{prooftree}' ), ` - + @@ -684,7 +684,7 @@ describe('BussproofsRegInf', () => { '\\begin{prooftree}\\AXC{A}\\LeftLabel{L}\\RightLabel{R}\\UIC{B}\\end{prooftree}' ), ` - + L @@ -744,7 +744,7 @@ describe('BussproofsRegProofs', () => { ), ` - + @@ -859,14 +859,10 @@ describe('BussproofsRegProofs', () => { '\\begin{prooftree}\\AxiomC{D}\\AxiomC{A}\\AxiomC{B}\\AxiomC{R}$\\alpha$\\BinaryInfC{$C \\rightarrow D \\rightarrow Q$}\\BinaryInfC{E}\\BinaryInfC{F}\\end{prooftree}' ), ` - - - $ - + + $ α - - $ - + $ @@ -984,7 +980,7 @@ describe('BussproofsRegProofs', () => { ` - + @@ -1178,7 +1174,7 @@ describe('BussproofsRegProofs', () => { ` - + @@ -1400,7 +1396,7 @@ describe('BussproofsRegProofs', () => { ` - + QERE @@ -1620,7 +1616,7 @@ describe('BussproofsRegProofs', () => { ` - + DD @@ -1848,7 +1844,7 @@ describe('BussproofsRegProofs', () => { ` - + @@ -2141,7 +2137,7 @@ describe('BussproofsRegProofs', () => { - + @@ -2241,7 +2237,7 @@ describe('BussproofsRegProofs', () => { - + Q 2 @@ -2340,7 +2336,7 @@ describe('BussproofsRegProofs', () => { Rit - + 2 @@ -2406,7 +2402,7 @@ describe('BussproofsRegProofs', () => { - + @@ -2437,7 +2433,7 @@ describe('BussproofsRegProofs', () => { - + @@ -2477,7 +2473,7 @@ describe('BussproofsRegProofs', () => { - + Q 2 @@ -2636,7 +2632,7 @@ describe('BussproofsRegProofs', () => { Rit - + 2 @@ -2702,7 +2698,7 @@ describe('BussproofsRegProofs', () => { - + @@ -2732,7 +2728,7 @@ describe('BussproofsRegProofs', () => { ` - + BBB diff --git a/testsuite/tests/input/tex/Cases.test.ts b/testsuite/tests/input/tex/Cases.test.ts index 870e4d7ee..1e46ec267 100644 --- a/testsuite/tests/input/tex/Cases.test.ts +++ b/testsuite/tests/input/tex/Cases.test.ts @@ -18,9 +18,11 @@ describe('Cases', () => { tex2mml('\\begin{numcases}{f(x)=} 1 & if $x > 0$ \\\\ 0 & otherwise \\end{numcases}'), ` - + - (1) + ( + 1 + ) @@ -35,9 +37,11 @@ describe('Cases', () => { - + - (1) + ( + 1 + ) 1 @@ -59,9 +63,11 @@ describe('Cases', () => { - + - (2) + ( + 2 + ) 0 @@ -82,9 +88,11 @@ describe('Cases', () => { - + - (1) + ( + 1 + ) 1 @@ -131,9 +139,11 @@ describe('Cases', () => { - + - (2) + ( + 2 + ) @@ -160,9 +170,11 @@ describe('Cases', () => { tex2mml('\\begin{subnumcases}{f(x)=} 1 & if $x > 0$ \\\\ 0 & otherwise \\end{subnumcases}'), ` - + - (1a) + ( + 1a + ) @@ -177,9 +189,11 @@ describe('Cases', () => { - + - (1a) + ( + 1a + ) 1 @@ -201,9 +215,11 @@ describe('Cases', () => { - + - (1b) + ( + 1b + ) 0 @@ -224,9 +240,11 @@ describe('Cases', () => { - + - (1a) + ( + 1a + ) 1 @@ -273,9 +291,11 @@ describe('Cases', () => { - + - (1b) + ( + 1b + ) @@ -302,9 +322,11 @@ describe('Cases', () => { tex2mml('\\begin{numcases}{A=} 1 & if {x\\\\y}\\$ \\end{numcases}'), ` - + - (1) + ( + 1 + ) @@ -316,9 +338,11 @@ describe('Cases', () => { - + - (1) + ( + 1 + ) 1 @@ -339,9 +363,11 @@ describe('Cases', () => { - + - (1) + ( + 1 + ) 1 @@ -397,8 +423,8 @@ describe('Cases', () => { toXmlMatch( tex2mml('\\begin{array}{cc} x & y \\end{array}'), ` - - + + x @@ -418,9 +444,11 @@ describe('Cases', () => { tex2mml('\\begin{numcases}{A=} x\\tag{A} & y \\end{numcases}'), ` - + - (A) + ( + A + ) @@ -432,9 +460,11 @@ describe('Cases', () => { - + - (A) + ( + A + ) x @@ -455,9 +485,11 @@ describe('Cases', () => { - + - (A) + ( + A + ) x diff --git a/testsuite/tests/input/tex/Colortbl.test.ts b/testsuite/tests/input/tex/Colortbl.test.ts index 1a0244457..d25ad1479 100644 --- a/testsuite/tests/input/tex/Colortbl.test.ts +++ b/testsuite/tests/input/tex/Colortbl.test.ts @@ -19,8 +19,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & \\cellcolor{red} b \\\\ \\cellcolor{yellow} c & d \\end{array}'), ` - - + + a @@ -28,7 +28,7 @@ describe('Colortbl', () => { b - + c @@ -47,8 +47,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & b \\cellcolor{red} \\end{array}'), ` - - + + a @@ -67,8 +67,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & b \\\\ \\rowcolor{yellow} c & d \\end{array}'), ` - - + + a @@ -76,7 +76,7 @@ describe('Colortbl', () => { b - + c @@ -102,8 +102,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & \\columncolor{yellow} b \\\\ c & d \\end{array}'), ` - - + + a @@ -111,7 +111,7 @@ describe('Colortbl', () => { b - + c @@ -137,8 +137,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{c>{\\columncolor{yellow}}c} a & b \\\\ c & d \\end{array}'), ` - - + + a @@ -146,7 +146,7 @@ describe('Colortbl', () => { b - + c @@ -165,8 +165,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{c>{\\cellcolor{yellow}}c} a & b \\\\ c & d \\end{array}'), ` - - + + a @@ -174,7 +174,7 @@ describe('Colortbl', () => { b - + c @@ -193,8 +193,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & b \\\\ \\rowcolor{red} c & \\cellcolor{yellow} d \\end{array}'), ` - - + + a @@ -202,7 +202,7 @@ describe('Colortbl', () => { b - + c @@ -221,8 +221,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & \\columncolor{red} b \\\\ c & \\cellcolor{yellow} d \\end{array}'), ` - - + + a @@ -230,7 +230,7 @@ describe('Colortbl', () => { b - + c @@ -249,8 +249,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & \\columncolor{red} b \\\\ \\rowcolor{yellow} c & d \\end{array}'), ` - - + + a @@ -258,7 +258,7 @@ describe('Colortbl', () => { b - + c @@ -277,8 +277,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & \\columncolor{red} b \\\\ \\rowcolor{yellow} c & \\cellcolor{green} d \\end{array}'), ` - - + + a @@ -286,7 +286,7 @@ describe('Colortbl', () => { b - + c @@ -305,8 +305,8 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{cc} a & \\columncolor{red}[ignore][ignore] b \\\\ c & d \\end{array}'), ` - - + + a @@ -314,7 +314,7 @@ describe('Colortbl', () => { b - + c @@ -347,9 +347,9 @@ describe('Colortbl', () => { toXmlMatch( tex2mml('\\begin{array}{|c|} \\cellcolor{red} a \\end{array}'), ` - + - + a @@ -369,7 +369,7 @@ describe('Colortbl', () => { ( - + a @@ -388,7 +388,7 @@ describe('Colortbl', () => { tex2mml('\\matrix{ \\cellcolor[rgb]{1,0,0} a }'), ` - + a @@ -416,7 +416,7 @@ describe('Colortbl', () => { tex2mml('\\matrix{ \\cellcolor[rgb]{1,0,0} a }'), ` - + a diff --git a/testsuite/tests/input/tex/Empheq.test.ts b/testsuite/tests/input/tex/Empheq.test.ts index 9837bb308..8e7f1129e 100644 --- a/testsuite/tests/input/tex/Empheq.test.ts +++ b/testsuite/tests/input/tex/Empheq.test.ts @@ -20,7 +20,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) @@ -32,7 +34,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -47,7 +51,9 @@ describe('Empheq', () => { - (2) + ( + 2 + ) c @@ -69,7 +75,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -100,7 +108,9 @@ describe('Empheq', () => { - (2) + ( + 2 + ) @@ -128,7 +138,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -150,7 +162,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -165,7 +179,9 @@ describe('Empheq', () => { - (2) + ( + 2 + ) c @@ -187,7 +203,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -208,7 +226,9 @@ describe('Empheq', () => { - (2) + ( + 2 + ) c @@ -233,9 +253,11 @@ describe('Empheq', () => { tex2mml('\\begin{numcases}{A=\\label{test}} a&=b \\end{numcases}'), ` - + - (1) + ( + 1 + ) @@ -247,9 +269,11 @@ describe('Empheq', () => { - + - (1) + ( + 1 + ) a @@ -270,9 +294,11 @@ describe('Empheq', () => { - + - (1) + ( + 1 + ) a @@ -379,7 +405,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -394,7 +422,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -402,7 +432,9 @@ describe('Empheq', () => { - (2) + ( + 2 + ) b @@ -424,7 +456,9 @@ describe('Empheq', () => { - (1) + ( + 1 + ) a @@ -438,7 +472,9 @@ describe('Empheq', () => { - (2) + ( + 2 + ) b @@ -463,9 +499,11 @@ describe('Empheq', () => { tex2mml('\\begin{empheq}{alignat=2} a & b & c & d \\end{empheq}'), ` - + - (1) + ( + 1 + ) a diff --git a/testsuite/tests/input/tex/Extpfeil.test.ts b/testsuite/tests/input/tex/Extpfeil.test.ts index a9f0181f0..c44882b5c 100644 --- a/testsuite/tests/input/tex/Extpfeil.test.ts +++ b/testsuite/tests/input/tex/Extpfeil.test.ts @@ -15,18 +15,21 @@ describe('Extpfeil', () => { toXmlMatch( tex2mml('\\xtwoheadrightarrow{abcxyz}'), ` - - - - a - b - c - x - y - z - - - + + + + + + a + b + c + x + y + z + + + + ` ); }); @@ -37,18 +40,21 @@ describe('Extpfeil', () => { toXmlMatch( tex2mml('\\xtwoheadleftarrow{abcxyz}'), ` - - - - a - b - c - x - y - z - - - + + + + + + a + b + c + x + y + z + + + + ` ); }); @@ -59,18 +65,21 @@ describe('Extpfeil', () => { toXmlMatch( tex2mml('\\xmapsto{abcxyz}'), ` - - - - a - b - c - x - y - z - - - + + + + + + a + b + c + x + y + z + + + + ` ); }); @@ -81,18 +90,21 @@ describe('Extpfeil', () => { toXmlMatch( tex2mml('\\xlongequal{abcxyz}'), ` - - = - - a - b - c - x - y - z - - - + + + + = + + a + b + c + x + y + z + + + + ` ); }); @@ -103,18 +115,21 @@ describe('Extpfeil', () => { toXmlMatch( tex2mml('\\xtofrom{abcxyz}'), ` - - - - a - b - c - x - y - z - - - + + + + + + a + b + c + x + y + z + + + + ` ); }); @@ -125,15 +140,18 @@ describe('Extpfeil', () => { toXmlMatch( tex2mml('\\Newextarrow{\\ab}{10,20}{8672}\\ab{xyz}'), ` - - - - x - y - z - - - + + + + + + x + y + z + + + + ` ); }); diff --git a/testsuite/tests/input/tex/Mathtools.test.ts b/testsuite/tests/input/tex/Mathtools.test.ts index c9975c4d7..b1974e6d7 100644 --- a/testsuite/tests/input/tex/Mathtools.test.ts +++ b/testsuite/tests/input/tex/Mathtools.test.ts @@ -275,7 +275,7 @@ describe('Mathtools Spacing Control', () => { 2 - + x 2 @@ -290,7 +290,7 @@ describe('Mathtools Spacing Control', () => { - + x 2 @@ -311,7 +311,7 @@ describe('Mathtools Spacing Control', () => { - + x 2 @@ -331,7 +331,7 @@ describe('Mathtools Spacing Control', () => { - + x 2 @@ -357,7 +357,7 @@ describe('Mathtools Spacing Control', () => { - + x 2 @@ -379,8 +379,8 @@ describe('Mathtools Spacing Control', () => { - - + + x @@ -388,7 +388,7 @@ describe('Mathtools Spacing Control', () => { - + y @@ -506,13 +506,15 @@ describe('Mathtools Tagging', () => { - [1] + [ + 1 + ] E = m - + c 2 @@ -532,13 +534,15 @@ describe('Mathtools Tagging', () => { - [\\textbf{1}] + [ + \\textbf{1} + ] E = m - + c 2 @@ -579,13 +583,15 @@ describe('Mathtools Tagging', () => { - ((1)) + (( + 1 + )) E = m - + c 2 @@ -602,7 +608,7 @@ describe('Mathtools Tagging', () => { toXmlMatch( tex2mml('\\begin{align}E=mc^2\\label{test}\\tag*{\\textsf{A}}\\\\ \\refeq{test}\\end{align}'), ` - + \\textsf{A} @@ -611,7 +617,7 @@ describe('Mathtools Tagging', () => { E = m - + c 2 @@ -639,7 +645,9 @@ describe('Mathtools Tagging', () => { - |1| + | + 1 + | x @@ -665,10 +673,10 @@ describe('Mathtools Symbols', () => { toXmlMatch( tex2mml('\\left\\lparen X \\right\\rparen'), ` - - ( + + ( X - ) + ) ` ); @@ -735,15 +743,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xleftrightarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -754,15 +765,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xLeftarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -773,15 +787,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xRightarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -792,15 +809,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xLeftrightarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -811,15 +831,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xhookleftarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -830,15 +853,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xhookrightarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -849,15 +875,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xmapsto{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -868,15 +897,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xrightharpoondown{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -887,15 +919,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xleftharpoondown{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -906,15 +941,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xrightleftharpoons{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -925,15 +963,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xleftrightharpoons{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -944,15 +985,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xrightharpoonup{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -963,15 +1007,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xleftharpoonup{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -982,15 +1029,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xlongleftarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -1001,15 +1051,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xLongleftarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -1020,15 +1073,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xlongrightarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -1039,15 +1095,18 @@ describe('Mathtools stretchy', () => { toXmlMatch( tex2mml('\\xLongrightarrow{x+y}'), ` - - - - x - + - y - - - + + + + + + x + + + y + + + + ` ); }); @@ -1403,8 +1462,8 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{matrix*} -1 & 3 \\\\ 2 & -4 \\end{matrix*}'), ` - - + + 1 @@ -1413,7 +1472,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1433,8 +1492,8 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{matrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{matrix*}'), ` - - + + 1 @@ -1443,7 +1502,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1463,8 +1522,8 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{matrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{matrix*}'), ` - - + + 1 @@ -1473,7 +1532,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1493,8 +1552,8 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{matrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{matrix*}'), ` - - + + 1 @@ -1503,7 +1562,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1523,10 +1582,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{pmatrix*} -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}'), ` - + ( - + 1 @@ -1535,7 +1594,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1557,10 +1616,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{pmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}'), ` - + ( - + 1 @@ -1569,7 +1628,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1591,10 +1650,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{pmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}'), ` - + ( - + 1 @@ -1603,7 +1662,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1625,10 +1684,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{pmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}'), ` - + ( - + 1 @@ -1637,7 +1696,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1659,10 +1718,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bmatrix*} -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}'), ` - + [ - + 1 @@ -1671,7 +1730,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1693,10 +1752,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}'), ` - + [ - + 1 @@ -1705,7 +1764,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1727,10 +1786,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}'), ` - + [ - + 1 @@ -1739,7 +1798,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1761,10 +1820,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}'), ` - + [ - + 1 @@ -1773,7 +1832,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1795,10 +1854,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bmatrix*} -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}'), ` - + { - + 1 @@ -1807,7 +1866,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1829,10 +1888,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}'), ` - + { - + 1 @@ -1841,7 +1900,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1863,10 +1922,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}'), ` - + { - + 1 @@ -1875,7 +1934,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1897,10 +1956,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}'), ` - + { - + 1 @@ -1909,7 +1968,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1931,10 +1990,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vmatrix*} -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}'), ` - + | - + 1 @@ -1943,7 +2002,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1965,10 +2024,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}'), ` - + | - + 1 @@ -1977,7 +2036,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -1999,10 +2058,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}'), ` - + | - + 1 @@ -2011,7 +2070,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -2033,10 +2092,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}'), ` - + | - + 1 @@ -2045,7 +2104,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -2067,10 +2126,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vmatrix*} -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}'), ` - - + + - + 1 @@ -2079,7 +2138,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -2089,7 +2148,7 @@ describe('Mathtools Matrix Environments', () => { - + ` ); @@ -2101,10 +2160,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}'), ` - - + + - + 1 @@ -2113,7 +2172,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -2123,7 +2182,7 @@ describe('Mathtools Matrix Environments', () => { - + ` ); @@ -2135,10 +2194,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}'), ` - - + + - + 1 @@ -2147,7 +2206,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -2157,7 +2216,7 @@ describe('Mathtools Matrix Environments', () => { - + ` ); @@ -2169,10 +2228,10 @@ describe('Mathtools Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}'), ` - - + + - + 1 @@ -2181,7 +2240,7 @@ describe('Mathtools Matrix Environments', () => { 3 - + 2 @@ -2191,7 +2250,7 @@ describe('Mathtools Matrix Environments', () => { - + ` ); @@ -2212,9 +2271,9 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix*} -a & b \\\\ c & -d \\end{smallmatrix*}'), ` - + - + a @@ -2223,7 +2282,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2244,9 +2303,9 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix*}[l] -a & b \\\\ c & -d \\end{smallmatrix*}'), ` - + - + a @@ -2255,7 +2314,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2276,9 +2335,9 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix*}[c] -a & b \\\\ c & -d \\end{smallmatrix*}'), ` - + - + a @@ -2287,7 +2346,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2308,9 +2367,9 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix*}[r] -a & b \\\\ c & -d \\end{smallmatrix*}'), ` - + - + a @@ -2319,7 +2378,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2340,11 +2399,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{psmallmatrix*} -a & b \\\\ c & -d \\end{psmallmatrix*}'), ` - + ( - + a @@ -2353,7 +2412,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2376,11 +2435,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{psmallmatrix*}[l] -a & b \\\\ c & -d \\end{psmallmatrix*}'), ` - + ( - + a @@ -2389,7 +2448,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2412,11 +2471,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{psmallmatrix*}[c] -a & b \\\\ c & -d \\end{psmallmatrix*}'), ` - + ( - + a @@ -2425,7 +2484,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2448,11 +2507,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{psmallmatrix*}[r] -a & b \\\\ c & -d \\end{psmallmatrix*}'), ` - + ( - + a @@ -2461,7 +2520,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2484,11 +2543,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bsmallmatrix*} -a & b \\\\ c & -d \\end{bsmallmatrix*}'), ` - + [ - + a @@ -2497,7 +2556,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2520,11 +2579,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bsmallmatrix*}[l] -a & b \\\\ c & -d \\end{bsmallmatrix*}'), ` - + [ - + a @@ -2533,7 +2592,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2556,11 +2615,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bsmallmatrix*}[c] -a & b \\\\ c & -d \\end{bsmallmatrix*}'), ` - + [ - + a @@ -2569,7 +2628,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2592,11 +2651,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bsmallmatrix*}[r] -a & b \\\\ c & -d \\end{bsmallmatrix*}'), ` - + [ - + a @@ -2605,7 +2664,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2628,11 +2687,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bsmallmatrix*} -a & b \\\\ c & -d \\end{Bsmallmatrix*}'), ` - + { - + a @@ -2641,7 +2700,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2664,11 +2723,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bsmallmatrix*}[l] -a & b \\\\ c & -d \\end{Bsmallmatrix*}'), ` - + { - + a @@ -2677,7 +2736,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2700,11 +2759,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bsmallmatrix*}[c] -a & b \\\\ c & -d \\end{Bsmallmatrix*}'), ` - + { - + a @@ -2713,7 +2772,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2736,11 +2795,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bsmallmatrix*}[r] -a & b \\\\ c & -d \\end{Bsmallmatrix*}'), ` - + { - + a @@ -2749,7 +2808,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2772,11 +2831,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vsmallmatrix*} -a & b \\\\ c & -d \\end{vsmallmatrix*}'), ` - + | - + a @@ -2785,7 +2844,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2808,11 +2867,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vsmallmatrix*}[l] -a & b \\\\ c & -d \\end{vsmallmatrix*}'), ` - + | - + a @@ -2821,7 +2880,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2844,11 +2903,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vsmallmatrix*}[c] -a & b \\\\ c & -d \\end{vsmallmatrix*}'), ` - + | - + a @@ -2857,7 +2916,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2880,11 +2939,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vsmallmatrix*}[r] -a & b \\\\ c & -d \\end{vsmallmatrix*}'), ` - + | - + a @@ -2893,7 +2952,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2916,11 +2975,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vsmallmatrix*} -a & b \\\\ c & -d \\end{Vsmallmatrix*}'), ` - - + + - + a @@ -2929,7 +2988,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2940,7 +2999,7 @@ describe('Mathtools Small Matrix Environments', () => { - + ` ); @@ -2952,11 +3011,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vsmallmatrix*}[l] -a & b \\\\ c & -d \\end{Vsmallmatrix*}'), ` - - + + - + a @@ -2965,7 +3024,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -2976,7 +3035,7 @@ describe('Mathtools Small Matrix Environments', () => { - + ` ); @@ -2988,11 +3047,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vsmallmatrix*}[c] -a & b \\\\ c & -d \\end{Vsmallmatrix*}'), ` - - + + - + a @@ -3001,7 +3060,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -3012,7 +3071,7 @@ describe('Mathtools Small Matrix Environments', () => { - + ` ); @@ -3024,11 +3083,11 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vsmallmatrix*}[r] -a & b \\\\ c & -d \\end{Vsmallmatrix*}'), ` - - + + - + a @@ -3037,7 +3096,7 @@ describe('Mathtools Small Matrix Environments', () => { b - + c @@ -3048,7 +3107,7 @@ describe('Mathtools Small Matrix Environments', () => { - + ` ); @@ -3060,7 +3119,7 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix} -a & b \\\\ c & -d \\end{smallmatrix}'), ` - + @@ -3092,7 +3151,7 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{smallmatrix} -a & b \\\\ c & -d \\end{smallmatrix}'), ` - + @@ -3124,7 +3183,7 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{psmallmatrix} -a & b \\\\ c & -d \\end{psmallmatrix}'), ` - + ( @@ -3160,7 +3219,7 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{bsmallmatrix} -a & b \\\\ c & -d \\end{bsmallmatrix}'), ` - + [ @@ -3196,7 +3255,7 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Bsmallmatrix} -a & b \\\\ c & -d \\end{Bsmallmatrix}'), ` - + { @@ -3232,7 +3291,7 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{vsmallmatrix} -a & b \\\\ c & -d \\end{vsmallmatrix}'), ` - + | @@ -3268,8 +3327,8 @@ describe('Mathtools Small Matrix Environments', () => { toXmlMatch( tex2mml('\\begin{Vsmallmatrix} -a & b \\\\ c & -d \\end{Vsmallmatrix}'), ` - - + + @@ -3292,7 +3351,7 @@ describe('Mathtools Small Matrix Environments', () => { - + ` ); @@ -3317,7 +3376,7 @@ describe('Mathtools More Environments', () => { ` A = - + @@ -3356,7 +3415,7 @@ describe('Mathtools More Environments', () => { '\\begin{multlined}[b][7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined} = B' ), ` - + @@ -3396,7 +3455,7 @@ describe('Mathtools More Environments', () => { '\\begin{multlined}[c][7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined} = B' ), ` - + @@ -3436,7 +3495,7 @@ describe('Mathtools More Environments', () => { '\\begin{multlined}[t][7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined} = B' ), ` - + @@ -3474,7 +3533,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{multlined}[7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined}'), ` - + @@ -3519,7 +3578,7 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - + @@ -3586,7 +3645,7 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - + @@ -3676,7 +3735,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{multlined} a \\\\ \\end{multlined}'), ` - + a @@ -3693,7 +3752,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{dcases} 1 & x>0 \\\\ -1 & x\\le 0 \\end{dcases}'), ` - + { @@ -3730,7 +3789,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{dcases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{dcases*}'), ` - + { @@ -3772,7 +3831,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{rcases} 1 & x>0 \\\\ -1 & x\\le 0 \\end{rcases}'), ` - + @@ -3809,7 +3868,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{rcases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{rcases*}'), ` - + @@ -3849,7 +3908,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{drcases} 1 & x>0 \\\\ -1 & x\\le 0 \\end{drcases}'), ` - + @@ -3886,7 +3945,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{drcases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{drcases*}'), ` - + @@ -3928,7 +3987,7 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{cases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{cases*}'), ` - + { @@ -3976,7 +4035,7 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - + @@ -4040,7 +4099,7 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - + a @@ -4099,7 +4158,7 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - + a @@ -4116,7 +4175,7 @@ describe('Mathtools More Environments', () => { - + x @@ -4140,8 +4199,8 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{lgathered} a+b+c \\\\ d \\end{lgathered}'), ` - - + + a + @@ -4150,7 +4209,7 @@ describe('Mathtools More Environments', () => { c - + d @@ -4166,8 +4225,8 @@ describe('Mathtools More Environments', () => { toXmlMatch( tex2mml('\\begin{rgathered} a+b+c \\\\ d \\end{rgathered}'), ` - - + + a + @@ -4176,7 +4235,7 @@ describe('Mathtools More Environments', () => { c - + d @@ -4200,8 +4259,8 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - - + + @@ -4225,13 +4284,13 @@ describe('Mathtools More Environments', () => { - + - + @@ -4274,8 +4333,8 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - - + + @@ -4303,7 +4362,7 @@ describe('Mathtools More Environments', () => { - + @@ -4313,7 +4372,7 @@ describe('Mathtools More Environments', () => { - + @@ -4376,7 +4435,7 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - + a @@ -4462,7 +4521,7 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - + A @@ -4525,8 +4584,8 @@ describe('Mathtools More Environments', () => { ].join('') ), ` - - + + A @@ -4554,7 +4613,7 @@ describe('Mathtools More Environments', () => { - + @@ -4590,7 +4649,7 @@ describe('Mathtools More Environments', () => { - + C @@ -4655,13 +4714,13 @@ describe('Mathtools Paired Delimiters', () => { tex2mml('\\DeclarePairedDelimiter\\abs{\\lvert}{\\rvert} \\abs*{\\frac{a}{b}}'), ` - - | + + | a b - | + | @@ -4676,14 +4735,14 @@ describe('Mathtools Paired Delimiters', () => { tex2mml('\\DeclarePairedDelimiter\\abs{\\lvert}{\\rvert} \\abs[\\Bigg]{\\frac{a}{b}}'), ` - | + | a b - | + | ` ); @@ -4718,11 +4777,11 @@ describe('Mathtools Paired Delimiters', () => { tex2mml('\\DeclarePairedDelimiterXPP\\x[1]{A}{\\lvert}{\\rvert}{B}{a#1b} \\x{X}'), ` A - | + | a X b - | + | B ` ); @@ -4758,7 +4817,7 @@ describe('Mathtools Boxed Equations', () => { toXmlMatch( tex2mml('\\begin{align*}\\Aboxed{ a & = b} \\\\ & = c \\end{align*}'), ` - + @@ -4823,8 +4882,8 @@ describe('Mathtools Boxed Equations', () => { toXmlMatch( tex2mml('\\begin{aligned} & \\Aboxed{ a & = b} \\end{aligned}'), ` - - + + @@ -4875,7 +4934,7 @@ describe('Mathtools Boxed Equations', () => { toXmlMatch( tex2mml('\\MakeAboxedCommand\\Afbox\\fbox \\begin{align}\\Afbox{a &= b}\\end{align}'), ` - + @@ -4926,7 +4985,7 @@ describe('Mathtools Boxed Equations', () => { '\\def\\mybox#1{\\bbox[yellow, 5px, border:2px solid]{#1}}\\MakeAboxedCommand*\\Afbox\\mybox \\begin{align}\\Afbox{a &= b}\\end{align}' ), ` - + @@ -5829,7 +5888,7 @@ describe('Mathtools setoptions', () => { ` a + - + a @@ -5862,7 +5921,7 @@ describe('Mathtools setoptions', () => { ` a + - + a @@ -5893,7 +5952,7 @@ describe('Mathtools setoptions', () => { ].join('') ), ` - + a @@ -5922,7 +5981,7 @@ describe('Mathtools setoptions', () => { ].join('') ), ` - + a @@ -5951,16 +6010,16 @@ describe('Mathtools setoptions', () => { ].join('') ), ` - + ( - + a - + b @@ -6106,7 +6165,9 @@ describe('Mathtools options', () => { - [[\\mathbf{x}]] + [[ + \\mathbf{x} + ]] a @@ -6149,7 +6210,9 @@ describe('Mathtools options', () => { - (1) + ( + 1 + ) a @@ -6164,7 +6227,7 @@ describe('Mathtools options', () => { test('non-ams tags', () => { class myTags extends AbstractTags { - formatTag(tag: string) {return `[[${tag}]]`} + formatTag(tag: string) {return ['[[', tag, ']]']} }; Configuration.create('mytags', { [ConfigurationType.TAGS]: {mytags: myTags}, @@ -6199,10 +6262,10 @@ describe('Mathtools options', () => { x ] - - [ + + [ x - ] + ] @@ -6222,25 +6285,25 @@ describe('Mathtools options', () => { [ x ] - | + | - - | + + | [ x ] - | + | - | + | [ x ] - | + | ` ); @@ -6255,12 +6318,12 @@ describe('Mathtools options', () => { } ⇐⇒ - - { + + { [ x ] - } + } @@ -6286,7 +6349,7 @@ describe('Mathtools options', () => { toXmlMatch( tex2mml('\\mathtoolsset{multlined-pos={}}\\begin{multlined} a \\end{multlined}'), ` - + a diff --git a/testsuite/tests/input/tex/Mhchem.test.ts b/testsuite/tests/input/tex/Mhchem.test.ts index 31fe0ab06..ba0fc030c 100644 --- a/testsuite/tests/input/tex/Mhchem.test.ts +++ b/testsuite/tests/input/tex/Mhchem.test.ts @@ -104,8 +104,8 @@ describe('Mhchem0', () => { ] = - - 75.3 + + 75.3   @@ -354,7 +354,7 @@ describe('Mhchem0', () => { Y - + @@ -364,8 +364,8 @@ describe('Mhchem0', () => { - - 99 + + 99 + @@ -393,7 +393,7 @@ describe('Mhchem1', () => { Y - + @@ -403,8 +403,8 @@ describe('Mhchem1', () => { - - 99 + + 99 + @@ -496,7 +496,7 @@ describe('Mhchem1', () => { tex2mml('\\ce{0.5 H2O}'), ` - 0.5 + 0.5 H @@ -579,9 +579,7 @@ describe('Mhchem1', () => { ( 1 - - / - + / 2 ) @@ -1622,8 +1620,8 @@ describe('Mhchem3', () => { 2 - - ( + + ( O @@ -1676,7 +1674,7 @@ describe('Mhchem3', () => { - ) + ) ` @@ -1889,7 +1887,7 @@ describe('Mhchem4', () => { - x + x @@ -2070,7 +2068,7 @@ describe('Mhchem4', () => { ` - β + β @@ -2204,7 +2202,7 @@ describe('Mhchem4', () => { - e + e @@ -2265,7 +2263,7 @@ describe('Mhchem4', () => { - + 6 2 @@ -2310,7 +2308,7 @@ describe('Mhchem5', () => { - i + i @@ -2357,7 +2355,7 @@ describe('Mhchem5', () => { - i + i @@ -2476,7 +2474,7 @@ describe('Mhchem5', () => { ( h P - 12 + 12 ) ` @@ -2946,13 +2944,13 @@ describe('Mhchem6', () => { A - + B - + C @@ -3018,7 +3016,7 @@ describe('Mhchem6', () => { - 12 + 12 H @@ -3105,7 +3103,7 @@ describe('Mhchem6', () => { - 12 + 12 H @@ -3192,7 +3190,7 @@ describe('Mhchem6', () => { - 12 + 12 H @@ -3570,7 +3568,7 @@ describe('Mhchem7', () => { , - 0.8 + 0.8 @@ -3596,7 +3594,7 @@ describe('Mhchem7', () => { , - 0.2 + 0.2 @@ -3781,7 +3779,7 @@ describe('Mhchem7', () => { - + ` ); @@ -4029,36 +4027,39 @@ describe('Mhchem8', () => { - - - - - + - - 2 - - - - - OH - - - - - - - A - - - - - - + + + + + + + + + + 2 + + + + + OH - - - - + + + + + + A + + + + + + + + + + + + @@ -4126,36 +4127,39 @@ describe('Mhchem8', () => { - - - - - + - - 2 - - - - - OH - - - - - - - A - - - - - - + + + + + + + + + + 2 + + + + + OH - - - - + + + + + + A + + + + + + + + + + + + @@ -4428,7 +4432,7 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 kJ}'), ` - 123 + 123   kJ @@ -4445,11 +4449,11 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 mm2}'), ` - 123 + 123   - mm + mm 2 @@ -4467,7 +4471,7 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 J s}'), ` - 123 + 123   J @@ -4488,7 +4492,7 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 J*s}'), ` - 123 + 123   J @@ -4513,14 +4517,12 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 kJ/mol}'), ` - 123 + 123   kJ - - / - + / mol @@ -4536,7 +4538,7 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 kJ//mol}'), ` - 123 + 123   @@ -4560,7 +4562,7 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 kJ mol-1}'), ` - 123 + 123   kJ @@ -4568,7 +4570,7 @@ describe('Mhchem9', () => { - mol + mol 1 @@ -4587,7 +4589,7 @@ describe('Mhchem9', () => { tex2mml('\\pu{123 kJ*mol-1}'), ` - 123 + 123   kJ @@ -4599,7 +4601,7 @@ describe('Mhchem9', () => { - mol + mol 1 @@ -4618,10 +4620,10 @@ describe('Mhchem9', () => { tex2mml('\\pu{1.2e3 kJ}'), ` - 1.2 + 1.2 - - 10 + + 10 3 @@ -4648,8 +4650,8 @@ describe('Mhchem9', () => { 2 - - 10 + + 10 3 @@ -4670,10 +4672,10 @@ describe('Mhchem9', () => { tex2mml('\\pu{1.2E3 kJ}'), ` - 1.2 + 1.2 × - - 10 + + 10 3 @@ -4700,8 +4702,8 @@ describe('Mhchem9', () => { 2 × - - 10 + + 10 3 @@ -4752,29 +4754,32 @@ describe('Mhchem-Ams', () => { - - - - - I - - - - - - - A - - - - - - + + + + + + + I - - - - + + + + + + A + + + + + + + + + + + + @@ -4800,29 +4805,32 @@ describe('Mhchem-Ams', () => { - - - - - I - - - - - - - A - - - - - - + + + + + + + I - - - - + + + + + + A + + + + + + + + + + + + [ @@ -4899,36 +4907,39 @@ describe('Mhchem-Ams', () => { - - - - - H - - - - - - - A - - - + + + + + + + H - - - - 2 - + + + + + + A + + + + + + + + 2 + + + + + O - - - O - - - - + + + + @@ -4951,23 +4962,26 @@ describe('Mhchem-Ams', () => { - - - - - - text below + + + + + + + + text below + - - - - - - text above - - - - + + + + + text above + + + + + @@ -4990,22 +5004,25 @@ describe('Mhchem-Ams', () => { - - - - - - x - i - - - - - - x - - - + + + + + + + + x + i + + + + + + x + + + + @@ -5074,15 +5091,18 @@ describe('Mhchem-Ams', () => { - - - - - Δ - - - - + + + + + + + Δ + + + + + ( @@ -5121,7 +5141,7 @@ describe('Mhchem-Ams', () => { - x + x @@ -5217,29 +5237,32 @@ describe('Mhchem-Ams', () => { - - - - - I - - - - - - - A - - - - - - + + + + + + + I - - - - + + + + + + A + + + + + + + + + + + + @@ -5272,29 +5295,32 @@ describe('Mhchem-Ams', () => { - - - - - I - - - - - - - A - - - - - - + + + + + + + I - - - - + + + + + + A + + + + + + + + + + + + @@ -5385,15 +5411,18 @@ describe('Mhchem-Ams', () => { - - - - - text - - - - + + + + + + + text + + + + + @@ -5416,15 +5445,18 @@ describe('Mhchem-Ams', () => { - - - - - text - - - - + + + + + + + text + + + + + @@ -5447,15 +5479,18 @@ describe('Mhchem-Ams', () => { - - - - - text - - - - + + + + + + + text + + + + + @@ -5478,15 +5513,18 @@ describe('Mhchem-Ams', () => { - - - - - text - - - - + + + + + + + text + + + + + @@ -5509,15 +5547,18 @@ describe('Mhchem-Ams', () => { - - - - - text - - - - + + + + + + + text + + + + + @@ -5540,15 +5581,18 @@ describe('Mhchem-Ams', () => { - - - - - text - - - - + + + + + + + text + + + + + @@ -5571,15 +5615,18 @@ describe('Mhchem-Ams', () => { - - - - - text - - - - + + + + + + + text + + + + + @@ -5592,7 +5639,7 @@ describe('Mhchem-Ams', () => { /********************************************************************************/ - it('Mhchem leftrightarrrow', () => { + it('Mhchem leftrightarrow', () => { toXmlMatch( tex2mml('\\ce{A\\leftrightarrow B}'), ` @@ -5611,6 +5658,25 @@ describe('Mhchem-Ams', () => { /********************************************************************************/ + it('Mhchem rightleftharpoons', () => { + toXmlMatch( + tex2mml('\\ce{A\\rightleftharpoons B}'), + ` + + + A + + + + B + + + ` + ); + }); + + /********************************************************************************/ + }); /**********************************************************************************/ diff --git a/testsuite/tests/input/tex/Newcommand.test.ts b/testsuite/tests/input/tex/Newcommand.test.ts index 4a036e365..74878c3a1 100644 --- a/testsuite/tests/input/tex/Newcommand.test.ts +++ b/testsuite/tests/input/tex/Newcommand.test.ts @@ -315,13 +315,13 @@ describe('Newcommand', () => { toXmlMatch( tex2mml('\\let\\lb=\\{\\left\\lb \\frac{1}{2} \\right\\}'), ` - - { + + { 1 2 - } + } ` ); @@ -333,13 +333,13 @@ describe('Newcommand', () => { toXmlMatch( tex2mml('\\let\\lb( \\left\\lb \\frac{1}{2} \\right)'), ` - - ( + + ( 1 2 - ) + ) ` ); @@ -420,17 +420,17 @@ describe('Newcommand', () => { '\\let\\lp(\\let\\rp)\\let\\mp\\rp\\left\\lp \\frac{a}{b}\\middle\\mp c \\right\\rp' ), ` - - ( + + ( a b - ) + ) c - ) + ) ` ); @@ -444,17 +444,17 @@ describe('Newcommand', () => { '\\let\\lp\\langle\\let\\rp\\rangle\\let\\mp\\rp\\left\\lp \\frac{a}{b}\\middle\\mp c \\right\\rp' ), ` - - + + a b - + c - + ` ); @@ -491,7 +491,7 @@ describe('Newcommand', () => { toXmlMatch( tex2mml('\\let\\sqrt\\choose a\\sqrt b'), ` - + ( @@ -835,7 +835,7 @@ describe('Newcommand Ams', () => { tex2mml('\\let\\b\\lvert\\let\\lvert\\langle\\vert\\b\\lvert'), ` | - | + | ` ); @@ -847,10 +847,10 @@ describe('Newcommand Ams', () => { toXmlMatch( tex2mml('\\let\\b\\lvert\\let\\lvert\\langle\\left\\b q \\right\\lvert'), ` - - | + + | q - + ` ); @@ -1085,10 +1085,10 @@ describe('Newcommand Overrides', () => { toXmlMatch( tex2mml('\\def\\test{x} \\let\\test=\\| \\left\\test X \\right\\test'), ` - - + + X - + ` ); @@ -1112,10 +1112,10 @@ describe('Newcommand Overrides', () => { toXmlMatch( tex2mml('\\let\\sqrt=\\| \\left\\sqrt X \\right\\sqrt'), ` - - + + X - + ` ); @@ -1154,10 +1154,10 @@ describe('Newcommand Overrides', () => { toXmlMatch( tex2mml('\\let\\test=< \\left\\test X \\right\\test'), ` - - + + X - + ` ); @@ -1195,8 +1195,8 @@ describe('Nested Environments', () => { ].join('') ), ` - - + + a @@ -1204,7 +1204,7 @@ describe('Nested Environments', () => { b - + c @@ -1228,11 +1228,11 @@ describe('Nested Environments', () => { ].join('') ), ` - - + + - - + + a @@ -1240,7 +1240,7 @@ describe('Nested Environments', () => { b - + c diff --git a/testsuite/tests/input/tex/Noerrors.test.ts b/testsuite/tests/input/tex/Noerrors.test.ts index dfc87d3d3..ddadeef37 100644 --- a/testsuite/tests/input/tex/Noerrors.test.ts +++ b/testsuite/tests/input/tex/Noerrors.test.ts @@ -288,7 +288,7 @@ describe('NoError', () => { toXmlMatch( tex2mml('\\mmlToken{mi}[m1=true]{}'), ` - + \\mmlToken{mi}[m1=true]{} ` diff --git a/testsuite/tests/input/tex/Physics.test.ts b/testsuite/tests/input/tex/Physics.test.ts index 31b928dfc..0ec2cce60 100644 --- a/testsuite/tests/input/tex/Physics.test.ts +++ b/testsuite/tests/input/tex/Physics.test.ts @@ -502,13 +502,13 @@ describe('Physics1_2', () => { toXmlMatch( tex2mml('\\pqty{\\frac{a}{b}}'), ` - - ( + + ( a b - ) + ) ` ); @@ -560,13 +560,13 @@ describe('Physics1_2', () => { toXmlMatch( tex2mml('\\Bqty{\\frac{a}{b}}'), ` - - { + + { a b - } + } ` ); @@ -652,14 +652,14 @@ describe('Physics1_3', () => { tex2mml('\\absolutevalue\\Bigg{\\frac{a}{b}}'), ` - | + | a b - | + | ` ); @@ -671,9 +671,9 @@ describe('Physics1_3', () => { toXmlMatch( tex2mml('\\absolutevalue{}'), ` - - | - | + + | + | ` ); @@ -686,14 +686,14 @@ describe('Physics1_3', () => { tex2mml('\\abs\\Bigg{\\frac{a}{b}}'), ` - | + | a b - | + | ` ); @@ -722,14 +722,14 @@ describe('Physics1_3', () => { tex2mml('\\norm\\Bigg{\\frac{a}{b}}'), ` - + a b - + ` ); @@ -757,9 +757,9 @@ describe('Physics1_3', () => { toXmlMatch( tex2mml('\\norm{}'), ` - - - + + + ` ); @@ -781,8 +781,8 @@ describe('Physics1_4', () => { tex2mml('\\evaluated{x}_0^\\infty'), ` - - + + x @@ -791,10 +791,10 @@ describe('Physics1_4', () => { - | + | 0 - + ` ); @@ -807,8 +807,8 @@ describe('Physics1_4', () => { tex2mml('\\eval{x}_0^\\infty'), ` - - + + x @@ -817,10 +817,10 @@ describe('Physics1_4', () => { - | + | 0 - + ` ); @@ -833,8 +833,8 @@ describe('Physics1_4', () => { tex2mml('\\eval*{x}_0^\\infty'), ` - - + + x @@ -847,10 +847,10 @@ describe('Physics1_4', () => { - | + | 0 - + ` ); @@ -876,7 +876,7 @@ describe('Physics1_4', () => { | 0 - + ` ); @@ -906,7 +906,7 @@ describe('Physics1_4', () => { | 0 - + ` ); @@ -919,8 +919,8 @@ describe('Physics1_4', () => { tex2mml('\\eval*{\\frac{A}{\\frac{A}{\\int x}}}_0^\\infty'), ` - - + + @@ -942,10 +942,10 @@ describe('Physics1_4', () => { - | + | 0 - + ` ); @@ -958,8 +958,8 @@ describe('Physics1_4', () => { tex2mml('\\eval{\\frac{A}{\\frac{A}{\\int x}}}_0^\\infty'), ` - - + + A @@ -977,10 +977,10 @@ describe('Physics1_4', () => { - | + | 0 - + ` ); @@ -1019,7 +1019,7 @@ describe('Physics1_4', () => { | 0 - + ` ); @@ -1054,7 +1054,7 @@ describe('Physics1_4', () => { | 0 - + ` ); @@ -1093,7 +1093,7 @@ describe('Physics1_4', () => { | 0 - + ` ); @@ -1128,7 +1128,7 @@ describe('Physics1_4', () => { | 0 - + ` ); @@ -1141,8 +1141,8 @@ describe('Physics1_4', () => { tex2mml('\\eval_0^\\infty'), ` - - + + @@ -1150,10 +1150,10 @@ describe('Physics1_4', () => { - | + | 0 - + ` ); @@ -1174,9 +1174,9 @@ describe('Physics1_5', () => { ` O - - ( - ) + + ( + ) ` ); @@ -1190,13 +1190,13 @@ describe('Physics1_5', () => { ` O - - ( + + ( x 2 - ) + ) ` ); @@ -1232,8 +1232,8 @@ describe('Physics1_5', () => { ` O - - ( + + ( A @@ -1244,7 +1244,7 @@ describe('Physics1_5', () => { - ) + ) ` ); @@ -1289,12 +1289,12 @@ describe('Physics1_6', () => { toXmlMatch( tex2mml('\\comm{A}{B}'), ` - - [ + + [ A , B - ] + ] ` ); @@ -1306,8 +1306,8 @@ describe('Physics1_6', () => { toXmlMatch( tex2mml('\\comm{\\frac{A}{\\frac{A}{\\int x}}}{B}'), ` - - [ + + [ A @@ -1320,7 +1320,7 @@ describe('Physics1_6', () => { , B - ] + ] ` ); @@ -1393,12 +1393,12 @@ describe('Physics1_6', () => { toXmlMatch( tex2mml('\\comm{A}B'), ` - - [ + + [ A , B - ] + ] ` ); @@ -1419,12 +1419,12 @@ describe('Physics1_7', () => { toXmlMatch( tex2mml('\\acomm{A}{B}'), ` - - { + + { A , B - } + } ` ); @@ -1436,12 +1436,12 @@ describe('Physics1_7', () => { toXmlMatch( tex2mml('\\anticommutator{A}{B}'), ` - - { + + { A , B - } + } ` ); @@ -1453,12 +1453,12 @@ describe('Physics1_7', () => { toXmlMatch( tex2mml('\\poissonbracket{A}{B}'), ` - - { + + { A , B - } + } ` ); @@ -1470,12 +1470,12 @@ describe('Physics1_7', () => { toXmlMatch( tex2mml('\\pb{A}{B}'), ` - - { + + { A , B - } + } ` ); @@ -1487,8 +1487,8 @@ describe('Physics1_7', () => { toXmlMatch( tex2mml('\\acomm{\\frac{A}{\\frac{A}{\\int x}}}{B}'), ` - - { + + { A @@ -1501,7 +1501,7 @@ describe('Physics1_7', () => { , B - } + } ` ); @@ -1556,12 +1556,12 @@ describe('Physics1_7', () => { toXmlMatch( tex2mml('\\acomm{A}B'), ` - - { + + { A , B - } + } ` ); @@ -1863,7 +1863,7 @@ describe('Physics2_1', () => { ` - + ^ @@ -2399,10 +2399,8 @@ describe('Physics2_4', () => { toXmlMatch( tex2mml('\\gradient '), ` - - - - + + ` ); @@ -2415,9 +2413,7 @@ describe('Physics2_4', () => { tex2mml('\\gradient(\\frac{a}{b})'), ` - - - + @@ -2439,9 +2435,7 @@ describe('Physics2_4', () => { tex2mml('\\gradient[\\frac{a}{b}]'), ` - - - + @@ -2463,9 +2457,7 @@ describe('Physics2_4', () => { tex2mml('\\gradient{\\frac{a}{b}}'), ` - - - + @@ -2482,10 +2474,8 @@ describe('Physics2_4', () => { toXmlMatch( tex2mml('\\grad '), ` - - - - + + ` ); @@ -2498,9 +2488,7 @@ describe('Physics2_4', () => { tex2mml('\\grad(\\frac{a}{b})'), ` - - - + @@ -2522,9 +2510,7 @@ describe('Physics2_4', () => { tex2mml('\\grad[\\frac{a}{b}]'), ` - - - + @@ -2546,9 +2532,7 @@ describe('Physics2_4', () => { tex2mml('\\grad{\\frac{a}{b}}'), ` - - - + @@ -2589,10 +2573,8 @@ describe('Physics2_5', () => { toXmlMatch( tex2mml('\\divergence{\\frac{a}{b}c}'), ` - - - - + + @@ -2610,10 +2592,8 @@ describe('Physics2_5', () => { toXmlMatch( tex2mml('\\div{\\frac{a}{b}c}'), ` - - - - + + @@ -2631,10 +2611,8 @@ describe('Physics2_5', () => { toXmlMatch( tex2mml('\\div{(\\frac{a}{b}c)}'), ` - - - - + + ( @@ -2654,10 +2632,8 @@ describe('Physics2_5', () => { toXmlMatch( tex2mml('\\div(\\frac{a}{b}c)'), ` - - - - + + @@ -2683,14 +2659,14 @@ describe('Physics2_5', () => { - - ( + + ( a b c - ) + ) ` ); @@ -2711,10 +2687,8 @@ describe('Physics2_6', () => { toXmlMatch( tex2mml('\\curl '), ` - - - - + + × ` @@ -2727,10 +2701,8 @@ describe('Physics2_6', () => { toXmlMatch( tex2mml('\\curl(\\frac{a}{b})'), ` - - - - + + × @@ -2751,10 +2723,8 @@ describe('Physics2_6', () => { toXmlMatch( tex2mml('\\curl[\\frac{a}{b}]'), ` - - - - + + × @@ -2775,10 +2745,8 @@ describe('Physics2_6', () => { toXmlMatch( tex2mml('\\curl{\\frac{a}{b}}'), ` - - - - + + × @@ -2804,7 +2772,7 @@ describe('Physics2_7', () => { toXmlMatch( tex2mml('\\laplacian '), ` - + 2 @@ -2818,7 +2786,7 @@ describe('Physics2_7', () => { toXmlMatch( tex2mml('\\laplacian(\\frac{a}{b})'), ` - + 2 @@ -2841,7 +2809,7 @@ describe('Physics2_7', () => { toXmlMatch( tex2mml('\\laplacian[\\frac{a}{b}]'), ` - + 2 @@ -2864,7 +2832,7 @@ describe('Physics2_7', () => { toXmlMatch( tex2mml('\\laplacian{\\frac{a}{b}}'), ` - + 2 @@ -3908,7 +3876,7 @@ describe('Physics3_4', () => { ` sin - | + | x y @@ -4346,13 +4314,13 @@ describe('Physics3_5', () => { Res - - { + + { x y - } + } ` ); @@ -4368,7 +4336,7 @@ describe('Physics3_5', () => { Res - | + | x y @@ -4446,7 +4414,7 @@ describe('Physics3_6', () => { z ) - + d z @@ -4471,7 +4439,7 @@ describe('Physics3_6', () => { z ) - + d z @@ -4496,7 +4464,7 @@ describe('Physics3_6', () => { z ) - + d z @@ -4512,7 +4480,7 @@ describe('Physics3_6', () => { toXmlMatch( tex2mml('\\pv\\int f(z) \\dd{z}a'), ` - + P @@ -4522,7 +4490,7 @@ describe('Physics3_6', () => { z ) - + d z @@ -4538,7 +4506,7 @@ describe('Physics3_6', () => { toXmlMatch( tex2mml('\\pv(\\int f(z))'), ` - + P @@ -4559,11 +4527,11 @@ describe('Physics3_6', () => { toXmlMatch( tex2mml('\\pv|\\int f(z)|'), ` - + P - | + | f ( @@ -4580,7 +4548,7 @@ describe('Physics3_6', () => { toXmlMatch( tex2mml('\\pv[\\int f(z)]'), ` - + P @@ -4614,7 +4582,7 @@ describe('Physics3_6', () => { z ) - + d z @@ -4642,7 +4610,7 @@ describe('Physics3_6', () => { z ) - + d z @@ -4658,7 +4626,7 @@ describe('Physics3_6', () => { toXmlMatch( tex2mml('\\PV\\int f(z) \\dd{z}a'), ` - + P . V @@ -4671,7 +4639,7 @@ describe('Physics3_6', () => { z ) - + d z @@ -4755,13 +4723,13 @@ describe('Physics3_7', () => { Re - - { + + { x y - } + } ` ); @@ -4777,7 +4745,7 @@ describe('Physics3_7', () => { Re - | + | x y @@ -4918,13 +4886,13 @@ describe('Physics3_7', () => { Im - - { + + { x y - } + } ` ); @@ -4940,7 +4908,7 @@ describe('Physics3_7', () => { Im - | + | x y @@ -5160,11 +5128,11 @@ describe('Physics5_0', () => { tex2mml('\\dv x'), ` - + d - - + + d x @@ -5181,11 +5149,11 @@ describe('Physics5_0', () => { tex2mml('\\dv x(ll)'), ` - + d - - + + d x @@ -5209,13 +5177,13 @@ describe('Physics5_0', () => { ` - + d x - - + + d y @@ -5233,8 +5201,8 @@ describe('Physics5_0', () => { ` - - + + d @@ -5243,8 +5211,8 @@ describe('Physics5_0', () => { f - - + + d @@ -5267,13 +5235,13 @@ describe('Physics5_0', () => { ` - + d f - - + + d x @@ -5294,13 +5262,13 @@ describe('Physics5_0', () => { ` - + d f - - + + d x @@ -5318,11 +5286,11 @@ describe('Physics5_0', () => { tex2mml('\\dv{x}y'), ` - + d - - + + d x @@ -5341,15 +5309,15 @@ describe('Physics5_0', () => { ` - + d n - - + + d @@ -5380,8 +5348,8 @@ describe('Physics5_0', () => { ` - - + + d @@ -5390,8 +5358,8 @@ describe('Physics5_0', () => { f - - + + d @@ -5421,10 +5389,10 @@ describe('Physics5_0', () => { toXmlMatch( tex2mml('\\dv*[n]{f}{x}{y}(\\frac{x}{y})'), ` - - - - + + + + d @@ -5433,9 +5401,9 @@ describe('Physics5_0', () => { f - / + / - + d @@ -5444,7 +5412,7 @@ describe('Physics5_0', () => { n - + y @@ -5465,26 +5433,26 @@ describe('Physics5_0', () => { toXmlMatch( tex2mml('\\dv*[]{f}{x}{y}(\\frac{x}{y})'), ` - - - - + + + + d f - / + / - + d x - + y @@ -5507,16 +5475,16 @@ describe('Physics5_0', () => { ` - - + + d f - - + + d @@ -5546,15 +5514,15 @@ describe('Physics5_0', () => { ` - + d 5 - - + + d @@ -5582,15 +5550,15 @@ describe('Physics5_0', () => { ` - + d 5 - - + + d @@ -5612,11 +5580,11 @@ describe('Physics5_0', () => { tex2mml('\\dv{f}'), ` - + d - - + + d f @@ -5641,14 +5609,14 @@ describe('Physics5_1', () => { toXmlMatch( tex2mml('\\flatfrac{x}{y}'), ` - - + + x - / + / y - + ` ); @@ -5660,17 +5628,17 @@ describe('Physics5_1', () => { toXmlMatch( tex2mml('\\flatfrac{x^2}{y}'), ` - - + + x 2 - / + / y - + ` ); @@ -5683,8 +5651,8 @@ describe('Physics5_1', () => { tex2mml('\\pdv x'), ` - - + + x @@ -5700,8 +5668,8 @@ describe('Physics5_1', () => { tex2mml('\\pdv x(ll)'), ` - - + + x @@ -5723,8 +5691,8 @@ describe('Physics5_1', () => { tex2mml('\\pdv{f}'), ` - - + + f @@ -5744,7 +5712,7 @@ describe('Physics5_1', () => { x - + y @@ -5769,7 +5737,7 @@ describe('Physics5_1', () => { f - + x @@ -5821,7 +5789,7 @@ describe('Physics5_1', () => { f - + x @@ -5838,8 +5806,8 @@ describe('Physics5_1', () => { tex2mml('\\pdv{x}y'), ` - - + + x @@ -5855,16 +5823,16 @@ describe('Physics5_1', () => { toXmlMatch( tex2mml('\\pdv*{f}{x}'), ` - - + + f - / + / x - + ` ); @@ -5876,8 +5844,8 @@ describe('Physics5_1', () => { toXmlMatch( tex2mml('\\pdv*[3]{f}{x}'), ` - - + + @@ -5886,7 +5854,7 @@ describe('Physics5_1', () => { f - / + / @@ -5895,7 +5863,7 @@ describe('Physics5_1', () => { 3 - + ` ); @@ -5914,7 +5882,7 @@ describe('Physics5_1', () => { n - + f @@ -5969,8 +5937,8 @@ describe('Physics5_1', () => { toXmlMatch( tex2mml('\\pdv*[n]{f}{x}{y}(\\frac{x}{y})'), ` - - + + @@ -5979,13 +5947,13 @@ describe('Physics5_1', () => { f - / + / x y - + ` ); @@ -5997,8 +5965,8 @@ describe('Physics5_1', () => { toXmlMatch( tex2mml('\\pdv*[]{f}{x}{y}(\\frac{x}{y})'), ` - - + + @@ -6007,13 +5975,13 @@ describe('Physics5_1', () => { f - / + / x y - + ` ); @@ -6059,7 +6027,7 @@ describe('Physics5_1', () => { 5 - + ( @@ -6091,7 +6059,7 @@ describe('Physics5_1', () => { 5 - + f @@ -6121,8 +6089,8 @@ describe('Physics5_2', () => { tex2mml('\\fdv x'), ` - δ - + δ + δ x @@ -6138,8 +6106,8 @@ describe('Physics5_2', () => { tex2mml('\\fdv x(ll)'), ` - δ - + δ + δ x @@ -6165,7 +6133,7 @@ describe('Physics5_2', () => { δ x - + δ y @@ -6181,8 +6149,8 @@ describe('Physics5_2', () => { tex2mml('\\fdv{f}'), ` - δ - + δ + δ f @@ -6207,7 +6175,7 @@ describe('Physics5_2', () => { f - + δ x @@ -6232,7 +6200,7 @@ describe('Physics5_2', () => { δ f - + δ x @@ -6255,7 +6223,7 @@ describe('Physics5_2', () => { δ f - + δ x @@ -6272,8 +6240,8 @@ describe('Physics5_2', () => { tex2mml('\\fdv{x}y'), ` - δ - + δ + δ x @@ -6289,16 +6257,16 @@ describe('Physics5_2', () => { toXmlMatch( tex2mml('\\functionalderivative*{F}{x}'), ` - - + + δ F - / + / δ x - + ` ); @@ -6310,16 +6278,16 @@ describe('Physics5_2', () => { toXmlMatch( tex2mml('\\fderivative*{F}{x}'), ` - - + + δ F - / + / δ x - + ` ); @@ -6331,16 +6299,16 @@ describe('Physics5_2', () => { toXmlMatch( tex2mml('\\fdv*{F}{x}'), ` - - + + δ F - / + / δ x - + ` ); @@ -6357,7 +6325,7 @@ describe('Physics5_2', () => { δ F - + δ x @@ -6382,7 +6350,7 @@ describe('Physics5_2', () => { F - + δ x @@ -6409,7 +6377,7 @@ describe('Physics5_2', () => { n - + δ f @@ -6447,7 +6415,7 @@ describe('Physics5_2', () => { f - + δ x @@ -6476,8 +6444,8 @@ describe('Physics5_2', () => { toXmlMatch( tex2mml('\\fdv*[n]{f}{x}{y}(\\frac{x}{y})'), ` - - + + δ @@ -6486,7 +6454,7 @@ describe('Physics5_2', () => { f - / + / δ @@ -6495,7 +6463,7 @@ describe('Physics5_2', () => { n - + y @@ -6516,22 +6484,22 @@ describe('Physics5_2', () => { toXmlMatch( tex2mml('\\fdv*[]{f}{x}{y}(\\frac{x}{y})'), ` - - + + δ f - / + / δ x - + y @@ -6560,7 +6528,7 @@ describe('Physics5_2', () => { f - + δ x @@ -6594,7 +6562,7 @@ describe('Physics5_2', () => { 5 - + δ f @@ -6706,7 +6674,7 @@ describe('Physics5_3', () => { toXmlMatch( tex2mml('\\var(E-TS)'), ` - δ + δ ( E @@ -6753,13 +6721,13 @@ describe('Physics5_3', () => { F [ g - - ( + + ( x y - ) + ) ] @@ -6773,7 +6741,7 @@ describe('Physics5_3', () => { toXmlMatch( tex2mml('\\var(\\frac{a}{b})'), ` - δ + δ ( @@ -6884,7 +6852,7 @@ describe('Physics5_4', () => { toXmlMatch( tex2mml('\\dd x'), ` - + d x @@ -6899,7 +6867,7 @@ describe('Physics5_4', () => { tex2mml('\\dd{x}'), ` - + d x @@ -6915,8 +6883,8 @@ describe('Physics5_4', () => { tex2mml('\\dd[3]{x}'), ` - - + + d @@ -6935,8 +6903,8 @@ describe('Physics5_4', () => { toXmlMatch( tex2mml('\\dd[3]x'), ` - - + + d @@ -6954,7 +6922,7 @@ describe('Physics5_4', () => { toXmlMatch( tex2mml('\\dd(\\frac{\\frac{\\cos}{\\theta}}{\\theta})'), ` - + d @@ -6979,7 +6947,7 @@ describe('Physics5_4', () => { tex2mml('\\dd[4](\\frac{\\frac{\\cos}{\\theta}}{\\theta})'), ` - + d @@ -7008,7 +6976,7 @@ describe('Physics5_4', () => { tex2mml('\\dd{x}(\\frac{\\frac{\\cos}{\\theta}}{\\theta})'), ` - + d x @@ -7033,8 +7001,8 @@ describe('Physics5_4', () => { tex2mml('\\dd[4]{x}(\\frac{\\frac{\\cos}{\\theta}}{\\theta})'), ` - - + + d @@ -7063,7 +7031,7 @@ describe('Physics5_4', () => { tex2mml('\\dd[5]'), ` - + d @@ -7081,7 +7049,7 @@ describe('Physics5_4', () => { tex2mml('{\\dd}'), ` - + d @@ -7105,7 +7073,7 @@ describe('Physics5_5', () => { tex2mml('A\\dd A'), ` A - + d A @@ -7120,7 +7088,7 @@ describe('Physics5_5', () => { tex2mml('A\\dd x A'), ` A - + d x @@ -7137,7 +7105,7 @@ describe('Physics5_5', () => { ` A - + d x @@ -7154,7 +7122,7 @@ describe('Physics5_5', () => { tex2mml('A\\dd xA'), ` A - + d x @@ -7191,8 +7159,8 @@ describe('Physics5_5', () => { ` A - - + + d @@ -7213,8 +7181,8 @@ describe('Physics5_5', () => { tex2mml('A\\dd[3]x A'), ` A - - + + d @@ -7234,7 +7202,7 @@ describe('Physics5_5', () => { tex2mml('A\\dd(\\frac{\\frac{\\cos}{\\theta}}{\\theta}) A'), ` A - + d @@ -7261,7 +7229,7 @@ describe('Physics5_5', () => { ` A - + d @@ -7292,7 +7260,7 @@ describe('Physics5_5', () => { ` A - + d x @@ -7319,8 +7287,8 @@ describe('Physics5_5', () => { ` A - - + + d @@ -7355,8 +7323,8 @@ describe('Physics5_5', () => { d - - ( + + ( cos @@ -7364,7 +7332,7 @@ describe('Physics5_5', () => { θ - ) + ) A ` @@ -7384,8 +7352,8 @@ describe('Physics5_5', () => { d - - ( + + ( cos @@ -7393,7 +7361,7 @@ describe('Physics5_5', () => { θ - ) + ) A @@ -7416,18 +7384,18 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra{\\phi}\\ket{\\psi}'), ` - - + + ϕ - | + | ψ - + ` ); @@ -7439,18 +7407,18 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra{A}\\ket{B}'), ` - - + + A - | + | B - + ` ); @@ -7462,30 +7430,30 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra{\\phi}\\dyad{\\psi}{\\xi}'), ` - - + + ϕ - | + | - - - | + + + | ψ - + - + ξ - | + | ` ); @@ -7497,16 +7465,16 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra A \\ket B'), ` - - + + A - | + | - + B ` @@ -7526,7 +7494,7 @@ describe('Physics6_0', () => { b - | + | a @@ -7551,7 +7519,7 @@ describe('Physics6_0', () => { b - | + | a @@ -7569,18 +7537,18 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra A\\ket{B}'), ` - - + + A - | + | B - + ` ); @@ -7592,16 +7560,16 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra A\\ket '), ` - - + + A - | + | - + ` ); @@ -7613,16 +7581,16 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra {A}\\ket '), ` - - + + A - | + | - + ` ); @@ -7634,16 +7602,16 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra {A}\\ket B'), ` - - + + A - | + | - + B ` @@ -7656,8 +7624,8 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\bra {\\frac{a}{b}} \\ket* \\alpha'), ` - - + + a @@ -7665,10 +7633,10 @@ describe('Physics6_0', () => { - | + | - + α @@ -7682,12 +7650,12 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\ket{A}'), ` - - | + + | A - + ` ); @@ -7699,15 +7667,15 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\ket{\\frac{a}{b}}'), ` - - | + + | a b - + ` ); @@ -7734,12 +7702,12 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\ket a'), ` - - | + + | a - + ` ); @@ -7766,12 +7734,12 @@ describe('Physics6_0', () => { toXmlMatch( tex2mml('\\ket \\alpha'), ` - - | + + | α - + ` ); @@ -7810,18 +7778,18 @@ describe('Physics6_1', () => { toXmlMatch( tex2mml('\\braket{A}'), ` - - + + A - | + | A - + ` ); @@ -7833,8 +7801,8 @@ describe('Physics6_1', () => { toXmlMatch( tex2mml('\\braket{\\frac{a}{b}}'), ` - - + + a @@ -7842,7 +7810,7 @@ describe('Physics6_1', () => { - | + | @@ -7850,7 +7818,7 @@ describe('Physics6_1', () => { b - + ` ); @@ -7866,7 +7834,7 @@ describe('Physics6_1', () => { A - | + | A @@ -7888,7 +7856,7 @@ describe('Physics6_1', () => { b - | + | a @@ -7906,18 +7874,18 @@ describe('Physics6_1', () => { toXmlMatch( tex2mml('\\braket a'), ` - - + + a - | + | a - + ` ); @@ -7933,7 +7901,7 @@ describe('Physics6_1', () => { a - | + | a @@ -7948,18 +7916,18 @@ describe('Physics6_1', () => { toXmlMatch( tex2mml('\\braket \\alpha'), ` - - + + α - | + | α - + ` ); @@ -7971,8 +7939,8 @@ describe('Physics6_1', () => { toXmlMatch( tex2mml('\\braket{\\frac{a}{b}}{A}'), ` - - + + a @@ -7980,12 +7948,12 @@ describe('Physics6_1', () => { - | + | A - + ` ); @@ -8004,7 +7972,7 @@ describe('Physics6_1', () => { b - | + | A @@ -8019,8 +7987,8 @@ describe('Physics6_1', () => { toXmlMatch( tex2mml('\\braket{\\frac{a}{b}} A'), ` - - + + a @@ -8028,7 +7996,7 @@ describe('Physics6_1', () => { - | + | @@ -8036,7 +8004,7 @@ describe('Physics6_1', () => { b - + A ` @@ -8056,7 +8024,7 @@ describe('Physics6_1', () => { b - | + | a @@ -8075,8 +8043,8 @@ describe('Physics6_1', () => { toXmlMatch( tex2mml('\\braket{\\frac{a}{b}}{} '), ` - - + + a @@ -8084,10 +8052,10 @@ describe('Physics6_1', () => { - | + | - + ` ); @@ -8106,7 +8074,7 @@ describe('Physics6_1', () => { b - | + | ` @@ -8128,22 +8096,22 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra{A}'), ` - - | + + | A - + - + A - | + | ` ); @@ -8155,8 +8123,8 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra{\\frac{a}{b}}'), ` - - | + + | a @@ -8164,11 +8132,11 @@ describe('Physics6_2', () => { - + - + @@ -8176,7 +8144,7 @@ describe('Physics6_2', () => { b - | + | ` ); @@ -8236,22 +8204,22 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra a'), ` - - | + + | a - + - + a - | + | ` ); @@ -8284,22 +8252,22 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra \\alpha'), ` - - | + + | α - + - + α - | + | ` ); @@ -8311,8 +8279,8 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra{\\frac{a}{b}}{A}'), ` - - | + + | a @@ -8320,16 +8288,16 @@ describe('Physics6_2', () => { - + - + A - | + | ` ); @@ -8365,8 +8333,8 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra{\\frac{a}{b}} A'), ` - - | + + | a @@ -8374,11 +8342,11 @@ describe('Physics6_2', () => { - + - + @@ -8386,7 +8354,7 @@ describe('Physics6_2', () => { b - | + | A ` @@ -8415,7 +8383,7 @@ describe('Physics6_2', () => { b - | + | A ` ); @@ -8427,8 +8395,8 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra{\\frac{a}{b}}{} '), ` - - | + + | a @@ -8436,14 +8404,14 @@ describe('Physics6_2', () => { - + - + - | + | ` ); @@ -8477,17 +8445,17 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\left\\vert A \\middle\\rangle\\middle\\langle B\\right\\vert'), ` - - | + + | A - + - + B - | + | ` ); @@ -8499,22 +8467,22 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\ketbra{A}{B}'), ` - - | + + | A - + - + B - | + | ` ); @@ -8526,22 +8494,22 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\outerproduct{A}{B}'), ` - - | + + | A - + - + B - | + | ` ); @@ -8553,22 +8521,22 @@ describe('Physics6_2', () => { toXmlMatch( tex2mml('\\dyad{a}{b}'), ` - - | + + | a - + - + b - | + | ` ); @@ -8589,12 +8557,12 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev{A}'), ` - - + + A - + ` ); @@ -8606,15 +8574,15 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev{\\frac{A}{B}}'), ` - - + + A B - + ` ); @@ -8662,28 +8630,28 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev{A}{\\frac{A}{B}}'), ` - - + + A B - | + | A - - | + + | A B - + ` ); @@ -8695,12 +8663,12 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev{\\frac{A}{B}}{A}'), ` - - + + A - | + | @@ -8708,12 +8676,12 @@ describe('Physics6_3', () => { B - - | + + | A - + ` ); @@ -8732,11 +8700,11 @@ describe('Physics6_3', () => { B - | + | A - | + | A @@ -8754,8 +8722,8 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev**{A} {\\frac{A}{B}}'), ` - - + + A @@ -8763,13 +8731,13 @@ describe('Physics6_3', () => { - | + | A - | + | @@ -8777,7 +8745,7 @@ describe('Physics6_3', () => { B - + ` ); @@ -8789,12 +8757,12 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev A B'), ` - - + + A - + B ` @@ -8807,28 +8775,28 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev A {\\frac{A}{B}}'), ` - - + + A B - | + | A - - | + + | A B - + ` ); @@ -8840,15 +8808,15 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev {\\frac{A}{B}} A'), ` - - + + A B - + A ` @@ -8868,11 +8836,11 @@ describe('Physics6_3', () => { B - | + | A - | + | A @@ -8890,8 +8858,8 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev** A {\\frac{A}{B}}'), ` - - + + A @@ -8899,13 +8867,13 @@ describe('Physics6_3', () => { - | + | A - | + | @@ -8913,7 +8881,7 @@ describe('Physics6_3', () => { B - + ` ); @@ -8925,15 +8893,15 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev{\\frac{A}{B}}{\\frac{\\Psi}{\\Phi}}'), ` - - + + Ψ Φ - | + | @@ -8941,15 +8909,15 @@ describe('Physics6_3', () => { B - - | + + | Ψ Φ - + ` ); @@ -8961,14 +8929,14 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev{\\frac{A}{B}}{{\\Psi}}'), ` - - + + Ψ - | + | @@ -8976,14 +8944,14 @@ describe('Physics6_3', () => { B - - | + + | Ψ - + ` ); @@ -9002,14 +8970,14 @@ describe('Physics6_3', () => { Φ - | + | A B - | + | Ψ @@ -9027,8 +8995,8 @@ describe('Physics6_3', () => { toXmlMatch( tex2mml('\\ev**{\\frac{A}{B}}{\\frac{\\Psi}{\\Phi}}'), ` - - + + Ψ @@ -9036,7 +9004,7 @@ describe('Physics6_3', () => { - | + | @@ -9045,7 +9013,7 @@ describe('Physics6_3', () => { - | + | @@ -9053,7 +9021,7 @@ describe('Physics6_3', () => { Φ - + ` ); @@ -9074,22 +9042,22 @@ describe('Physics6_4', () => { toXmlMatch( tex2mml('\\matrixel{n}{A}{m}'), ` - - + + n - | + | A - - | + + | m - + ` ); @@ -9101,22 +9069,22 @@ describe('Physics6_4', () => { toXmlMatch( tex2mml('\\mel{n}{A}{m}'), ` - - + + n - | + | A - - | + + | m - + ` ); @@ -9128,15 +9096,15 @@ describe('Physics6_4', () => { toXmlMatch( tex2mml('\\mel{\\frac{a}{b}}{\\frac{a}{b}}{\\frac{a}{b}}'), ` - - + + a b - | + | @@ -9144,15 +9112,15 @@ describe('Physics6_4', () => { b - - | + + | a b - + ` ); @@ -9164,22 +9132,22 @@ describe('Physics6_4', () => { toXmlMatch( tex2mml('\\mel A B C'), ` - - + + A - | + | B - - | + + | C - + ` ); @@ -9195,14 +9163,14 @@ describe('Physics6_4', () => { n - | + | a b - | + | m @@ -9224,14 +9192,14 @@ describe('Physics6_4', () => { b - | + | a b - | + | a @@ -9249,13 +9217,13 @@ describe('Physics6_4', () => { toXmlMatch( tex2mml('\\mel**{n}{\\frac{a}{b}}{m}'), ` - - + + n - | + | @@ -9264,12 +9232,12 @@ describe('Physics6_4', () => { - | + | m - + ` ); @@ -9281,8 +9249,8 @@ describe('Physics6_4', () => { toXmlMatch( tex2mml('\\mel**{\\frac{a}{b}}{\\frac{a}{b}}{\\frac{a}{b}}'), ` - - + + a @@ -9290,7 +9258,7 @@ describe('Physics6_4', () => { - | + | @@ -9299,7 +9267,7 @@ describe('Physics6_4', () => { - | + | @@ -9307,7 +9275,7 @@ describe('Physics6_4', () => { b - + ` ); @@ -9328,8 +9296,8 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\matrixquantity{Q}'), ` - - + + Q @@ -9345,8 +9313,8 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\matrixquantity*{a & b \\\\ c & d}'), ` - - + + a @@ -9354,7 +9322,7 @@ describe('Physics7_0', () => { b - + c @@ -9373,10 +9341,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\matrixquantity*(a & b \\\\ c & d)'), ` - - - - + + + + a @@ -9384,7 +9352,7 @@ describe('Physics7_0', () => { b - + c @@ -9393,7 +9361,7 @@ describe('Physics7_0', () => { - + ` ); @@ -9405,10 +9373,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\matrixquantity(a & b \\\\ c & d)'), ` - - ( - - + + ( + + a @@ -9416,7 +9384,7 @@ describe('Physics7_0', () => { b - + c @@ -9425,7 +9393,7 @@ describe('Physics7_0', () => { - ) + ) ` ); @@ -9437,10 +9405,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\matrixquantity[a & b \\\\ c & d]'), ` - - [ - - + + [ + + a @@ -9448,7 +9416,7 @@ describe('Physics7_0', () => { b - + c @@ -9457,7 +9425,7 @@ describe('Physics7_0', () => { - ] + ] ` ); @@ -9469,10 +9437,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\matrixquantity|a & b \\\\ c & d|'), ` - - | - - + + | + + a @@ -9480,7 +9448,7 @@ describe('Physics7_0', () => { b - + c @@ -9489,7 +9457,7 @@ describe('Physics7_0', () => { - | + | ` ); @@ -9501,8 +9469,8 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\mqty{a & b \\\\ c & d}'), ` - - + + a @@ -9510,7 +9478,7 @@ describe('Physics7_0', () => { b - + c @@ -9529,10 +9497,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\mqty(a & b \\\\ c & d)'), ` - - ( - - + + ( + + a @@ -9540,7 +9508,7 @@ describe('Physics7_0', () => { b - + c @@ -9549,7 +9517,7 @@ describe('Physics7_0', () => { - ) + ) ` ); @@ -9561,10 +9529,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\mqty*(a & b \\\\ c & d)'), ` - - - - + + + + a @@ -9572,7 +9540,7 @@ describe('Physics7_0', () => { b - + c @@ -9581,7 +9549,7 @@ describe('Physics7_0', () => { - + ` ); @@ -9593,10 +9561,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\mqty[a & b \\\\ c & d]'), ` - - [ - - + + [ + + a @@ -9604,7 +9572,7 @@ describe('Physics7_0', () => { b - + c @@ -9613,7 +9581,7 @@ describe('Physics7_0', () => { - ] + ] ` ); @@ -9625,10 +9593,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\mqty|a & b \\\\ c & d|'), ` - - | - - + + | + + a @@ -9636,7 +9604,7 @@ describe('Physics7_0', () => { b - + c @@ -9645,7 +9613,7 @@ describe('Physics7_0', () => { - | + | ` ); @@ -9657,10 +9625,10 @@ describe('Physics7_0', () => { toXmlMatch( tex2mml('\\mqty*|a & b\\\\ c& d|'), ` - - | - - + + | + + a @@ -9668,7 +9636,7 @@ describe('Physics7_0', () => { b - + c @@ -9677,7 +9645,7 @@ describe('Physics7_0', () => { - | + | ` ); @@ -9698,14 +9666,14 @@ describe('Physics7_10', () => { toXmlMatch( tex2mml('\\mqty(\\admat{1,2&3\\\\4&5})'), ` - - ( - - + + ( + + - - + + 1 @@ -9713,10 +9681,10 @@ describe('Physics7_10', () => { - + - - + + 2 @@ -9724,7 +9692,7 @@ describe('Physics7_10', () => { 3 - + 4 @@ -9736,7 +9704,7 @@ describe('Physics7_10', () => { - ) + ) ` ); @@ -9748,16 +9716,16 @@ describe('Physics7_10', () => { toXmlMatch( tex2mml('\\mqty(\\admat 1)'), ` - - ( - - + + ( + + 1 - ) + ) ` ); @@ -9769,10 +9737,10 @@ describe('Physics7_10', () => { toXmlMatch( tex2mml('\\mqty(\\admat 1,2)'), ` - - ( - - + + ( + + 1 , @@ -9780,7 +9748,7 @@ describe('Physics7_10', () => { - ) + ) ` ); @@ -9792,16 +9760,16 @@ describe('Physics7_10', () => { toXmlMatch( tex2mml('\\mqty(\\admat{1,2&3\\\\4&5&6,7,8})'), ` - - ( - - + + ( + + - - + + 1 @@ -9809,12 +9777,12 @@ describe('Physics7_10', () => { - + - - + + 2 @@ -9822,7 +9790,7 @@ describe('Physics7_10', () => { 3 - + 4 @@ -9836,11 +9804,11 @@ describe('Physics7_10', () => { - + - - + + 7 @@ -9848,10 +9816,10 @@ describe('Physics7_10', () => { - + - - + + 8 @@ -9860,7 +9828,7 @@ describe('Physics7_10', () => { - ) + ) ` ); @@ -9872,17 +9840,17 @@ describe('Physics7_10', () => { toXmlMatch( tex2mml('\\mqty(\\admat{1,2&3\\\\4&5,6,7,8})'), ` - - ( - - + + ( + + - - + + 1 @@ -9890,13 +9858,13 @@ describe('Physics7_10', () => { - + - - + + 2 @@ -9904,7 +9872,7 @@ describe('Physics7_10', () => { 3 - + 4 @@ -9915,12 +9883,12 @@ describe('Physics7_10', () => { - + - - + + 6 @@ -9928,11 +9896,11 @@ describe('Physics7_10', () => { - + - - + + 7 @@ -9940,10 +9908,10 @@ describe('Physics7_10', () => { - + - - + + 8 @@ -9952,7 +9920,7 @@ describe('Physics7_10', () => { - ) + ) ` ); @@ -9964,17 +9932,17 @@ describe('Physics7_10', () => { toXmlMatch( tex2mml('\\mqty(\\admat{1,2&3\\\\4&5&6,7,8,\\dmat{9,10}})'), ` - - ( - - + + ( + + - - + + 1 @@ -9982,13 +9950,13 @@ describe('Physics7_10', () => { - + - - + + 2 @@ -9996,7 +9964,7 @@ describe('Physics7_10', () => { 3 - + 4 @@ -10010,12 +9978,12 @@ describe('Physics7_10', () => { - + - - + + 7 @@ -10023,11 +9991,11 @@ describe('Physics7_10', () => { - + - - + + 8 @@ -10035,13 +10003,13 @@ describe('Physics7_10', () => { - + - - + + - - + + 9 @@ -10049,13 +10017,13 @@ describe('Physics7_10', () => { - + - - + + - 10 + 10 @@ -10065,7 +10033,7 @@ describe('Physics7_10', () => { - ) + ) ` ); @@ -10086,10 +10054,10 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\mqty a'), ` - - ( - - ) + + ( + + ) a ` @@ -10102,10 +10070,10 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\mqty1'), ` - - ( - - ) + + ( + + ) 1 ` @@ -10118,18 +10086,18 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\pmqty* 34'), ` - - ( - - + + ( + + - ) + ) - 34 + 34 ` ); }); @@ -10142,13 +10110,13 @@ describe('Physics7_11', () => { '\\mqty(\\dmat{1,2&3,4&4&5\\\\4&5,33,4,5,7,8\\\\0\\\\10&20\\\\3,200}) ' ), ` - - ( - - + + ( + + - - + + 1 @@ -10156,11 +10124,11 @@ describe('Physics7_11', () => { - + - - + + 2 @@ -10171,12 +10139,12 @@ describe('Physics7_11', () => { - + - - + + 4 @@ -10187,7 +10155,7 @@ describe('Physics7_11', () => { 5 - + 4 @@ -10198,28 +10166,28 @@ describe('Physics7_11', () => { - + - - + + - 33 + 33 - + - - + + 4 @@ -10227,15 +10195,15 @@ describe('Physics7_11', () => { - + - - + + 5 @@ -10243,7 +10211,7 @@ describe('Physics7_11', () => { - + @@ -10251,8 +10219,8 @@ describe('Physics7_11', () => { - - + + 7 @@ -10260,7 +10228,7 @@ describe('Physics7_11', () => { - + @@ -10269,26 +10237,26 @@ describe('Physics7_11', () => { - - + + 8 - + 0 - + - 10 + 10 - 20 + 20 - + 3 @@ -10296,7 +10264,7 @@ describe('Physics7_11', () => { - + @@ -10306,17 +10274,17 @@ describe('Physics7_11', () => { - - + + - 200 + 200 - ) + ) ` ); @@ -10328,13 +10296,13 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\mqty(\\dmat{1,2&3\\\\4&5}) '), ` - - ( - - + + ( + + - - + + 1 @@ -10342,11 +10310,11 @@ describe('Physics7_11', () => { - + - - + + 2 @@ -10354,7 +10322,7 @@ describe('Physics7_11', () => { 3 - + 4 @@ -10366,7 +10334,7 @@ describe('Physics7_11', () => { - ) + ) ` ); @@ -10378,13 +10346,13 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\mqty(\\dmat{1,2&3\\\\4&5&6,\\imat{3},7,8,\\dmat{9,10}})'), ` - - ( - - + + ( + + - - + + 1 @@ -10392,11 +10360,11 @@ describe('Physics7_11', () => { - + - - + + 2 @@ -10404,7 +10372,7 @@ describe('Physics7_11', () => { 3 - + 4 @@ -10418,12 +10386,12 @@ describe('Physics7_11', () => { - + - - + + 1 @@ -10434,7 +10402,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10445,7 +10413,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10459,13 +10427,13 @@ describe('Physics7_11', () => { - + - - + + 7 @@ -10473,14 +10441,14 @@ describe('Physics7_11', () => { - + - - + + 8 @@ -10488,18 +10456,18 @@ describe('Physics7_11', () => { - + - - + + - - + + 9 @@ -10507,13 +10475,13 @@ describe('Physics7_11', () => { - + - - + + - 10 + 10 @@ -10523,7 +10491,7 @@ describe('Physics7_11', () => { - ) + ) ` ); @@ -10537,13 +10505,13 @@ describe('Physics7_11', () => { '\\mqty(\\mqty{1}\\\\ & \\mqty{2 & 3\\\\ 4 & 5 & 6}\\\\ & & \\mqty{\\imat{3}} \\\\ & & & \\mqty{7})' ), ` - - ( - - + + ( + + - - + + 1 @@ -10551,11 +10519,11 @@ describe('Physics7_11', () => { - + - - + + 2 @@ -10563,7 +10531,7 @@ describe('Physics7_11', () => { 3 - + 4 @@ -10577,12 +10545,12 @@ describe('Physics7_11', () => { - + - - + + 1 @@ -10593,7 +10561,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10604,7 +10572,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10618,13 +10586,13 @@ describe('Physics7_11', () => { - + - - + + 7 @@ -10633,7 +10601,7 @@ describe('Physics7_11', () => { - ) + ) ` ); @@ -10645,13 +10613,13 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\left\\lgroup\\frac{a}{b}\\right\\rgroup'), ` - - + + a b - + ` ); @@ -10663,7 +10631,7 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\begin{smallmatrix} a & b \\\\ c & d \\end{smallmatrix}'), ` - + @@ -10693,7 +10661,7 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\smqty{\\imat{3}}'), ` - + @@ -10741,8 +10709,8 @@ describe('Physics7_11', () => { toXmlMatch( tex2mml('\\mqty{\\imat{10}}'), ` - - + + 1 @@ -10774,7 +10742,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10806,7 +10774,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10838,7 +10806,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10870,7 +10838,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10902,7 +10870,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10934,7 +10902,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10966,7 +10934,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -10998,7 +10966,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -11030,7 +10998,7 @@ describe('Physics7_11', () => { 0 - + 0 @@ -11082,27 +11050,27 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\pmqty{Q} \\mqty(R)'), ` - - ( - - + + ( + + Q - ) + ) - - ( - - + + ( + + R - ) + ) ` ); @@ -11114,27 +11082,27 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\Pmqty{Q} \\mqty*(R)'), ` - - - - + + + + Q - + - - - - + + + + R - + ` ); @@ -11146,27 +11114,27 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\bmqty{Q} \\mqty[R]'), ` - - [ - - + + [ + + Q - ] + ] - - [ - - + + [ + + R - ] + ] ` ); @@ -11178,27 +11146,27 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\vmqty{Q} \\mqty|R|'), ` - - | - - + + | + + Q - | + | - - | - - + + | + + R - | + | ` ); @@ -11210,10 +11178,10 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\pmqty{a & b \\\\ c & d}'), ` - - ( - - + + ( + + a @@ -11221,7 +11189,7 @@ describe('Physics7_1', () => { b - + c @@ -11230,7 +11198,7 @@ describe('Physics7_1', () => { - ) + ) ` ); @@ -11242,10 +11210,10 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\Pmqty{a & b \\\\ c & d}'), ` - - - - + + + + a @@ -11253,7 +11221,7 @@ describe('Physics7_1', () => { b - + c @@ -11262,7 +11230,7 @@ describe('Physics7_1', () => { - + ` ); @@ -11274,10 +11242,10 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\bmqty{a & b \\\\ c & d}'), ` - - [ - - + + [ + + a @@ -11285,7 +11253,7 @@ describe('Physics7_1', () => { b - + c @@ -11294,7 +11262,7 @@ describe('Physics7_1', () => { - ] + ] ` ); @@ -11306,10 +11274,10 @@ describe('Physics7_1', () => { toXmlMatch( tex2mml('\\vmqty{a & b \\\\ c & d}'), ` - - | - - + + | + + a @@ -11317,7 +11285,7 @@ describe('Physics7_1', () => { b - + c @@ -11326,7 +11294,7 @@ describe('Physics7_1', () => { - | + | ` ); @@ -11347,7 +11315,7 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smallmatrixquantity{Q}'), ` - + @@ -11367,7 +11335,7 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smallmatrixquantity*{a & b \\\\ c & d}'), ` - + @@ -11398,9 +11366,9 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smallmatrixquantity*(a & b \\\\ c & d)'), ` - - - + + + @@ -11421,7 +11389,7 @@ describe('Physics7_2', () => { - + ` ); @@ -11433,9 +11401,9 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smallmatrixquantity(a & b \\\\ c & d)'), ` - - ( - + + ( + @@ -11452,11 +11420,11 @@ describe('Physics7_2', () => { d - + - ) + ) ` ); @@ -11468,9 +11436,9 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smallmatrixquantity|a & b \\\\ c & d|'), ` - - | - + + | + @@ -11491,7 +11459,7 @@ describe('Physics7_2', () => { - | + | ` ); @@ -11503,7 +11471,7 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smqty{a & b \\\\ c & d}'), ` - + @@ -11534,9 +11502,9 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smqty(a & b \\\\ c & d)'), ` - - ( - + + ( + @@ -11557,7 +11525,7 @@ describe('Physics7_2', () => { - ) + ) ` ); @@ -11569,9 +11537,9 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smqty*(a & b \\\\ c & d)'), ` - - - + + + @@ -11592,7 +11560,7 @@ describe('Physics7_2', () => { - + ` ); @@ -11604,9 +11572,9 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smqty[a & b \\\\ c & d]'), ` - - [ - + + [ + @@ -11627,7 +11595,7 @@ describe('Physics7_2', () => { - ] + ] ` ); @@ -11639,9 +11607,9 @@ describe('Physics7_2', () => { toXmlMatch( tex2mml('\\smqty|a & b \\\\ c & d|'), ` - - | - + + | + @@ -11662,7 +11630,7 @@ describe('Physics7_2', () => { - | + | ` ); @@ -11683,9 +11651,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\spmqty{Q} \\smqty(R)'), ` - - ( - + + ( + @@ -11695,11 +11663,11 @@ describe('Physics7_3', () => { - ) + ) - - ( - + + ( + @@ -11709,7 +11677,7 @@ describe('Physics7_3', () => { - ) + ) ` ); @@ -11721,9 +11689,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\sPmqty{Q} \\smqty*(R)'), ` - - - + + + @@ -11733,11 +11701,11 @@ describe('Physics7_3', () => { - + - - - + + + @@ -11747,7 +11715,7 @@ describe('Physics7_3', () => { - + ` ); @@ -11759,9 +11727,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\sbmqty{Q} \\smqty[R]'), ` - - [ - + + [ + @@ -11771,11 +11739,11 @@ describe('Physics7_3', () => { - ] + ] - - [ - + + [ + @@ -11785,7 +11753,7 @@ describe('Physics7_3', () => { - ] + ] ` ); @@ -11797,9 +11765,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\svmqty{Q} \\smqty|R|'), ` - - | - + + | + @@ -11809,11 +11777,11 @@ describe('Physics7_3', () => { - | + | - - | - + + | + @@ -11823,7 +11791,7 @@ describe('Physics7_3', () => { - | + | ` ); @@ -11835,9 +11803,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\spmqty{a & b \\\\ c & d}'), ` - - ( - + + ( + @@ -11858,7 +11826,7 @@ describe('Physics7_3', () => { - ) + ) ` ); @@ -11870,9 +11838,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\sPmqty{a & b \\\\ c & d}'), ` - - - + + + @@ -11893,7 +11861,7 @@ describe('Physics7_3', () => { - + ` ); @@ -11905,9 +11873,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\sbmqty{a & b \\\\ c & d}'), ` - - [ - + + [ + @@ -11928,7 +11896,7 @@ describe('Physics7_3', () => { - ] + ] ` ); @@ -11940,9 +11908,9 @@ describe('Physics7_3', () => { toXmlMatch( tex2mml('\\svmqty{a & b \\\\ c & d}'), ` - - | - + + | + @@ -11963,7 +11931,7 @@ describe('Physics7_3', () => { - | + | ` ); @@ -11984,10 +11952,10 @@ describe('Physics7_4', () => { toXmlMatch( tex2mml('\\matrixdeterminant{a & b \\\\ c & d}'), ` - - | - - + + | + + a @@ -11995,7 +11963,7 @@ describe('Physics7_4', () => { b - + c @@ -12004,7 +11972,7 @@ describe('Physics7_4', () => { - | + | ` ); @@ -12016,10 +11984,10 @@ describe('Physics7_4', () => { toXmlMatch( tex2mml('\\mdet{a & b \\\\ c & d}'), ` - - | - - + + | + + a @@ -12027,7 +11995,7 @@ describe('Physics7_4', () => { b - + c @@ -12036,7 +12004,7 @@ describe('Physics7_4', () => { - | + | ` ); @@ -12048,9 +12016,9 @@ describe('Physics7_4', () => { toXmlMatch( tex2mml('\\smdet{a & b \\\\ c & d} '), ` - - | - + + | + @@ -12071,7 +12039,7 @@ describe('Physics7_4', () => { - | + | ` ); @@ -12083,16 +12051,16 @@ describe('Physics7_4', () => { toXmlMatch( tex2mml('\\matrixdeterminant a b'), ` - - | - - + + | + + a - | + | b ` @@ -12105,16 +12073,16 @@ describe('Physics7_4', () => { toXmlMatch( tex2mml('\\mdet a b'), ` - - | - - + + | + + a - | + | b ` @@ -12127,9 +12095,9 @@ describe('Physics7_4', () => { toXmlMatch( tex2mml('\\smdet a b'), ` - - | - + + | + @@ -12139,7 +12107,7 @@ describe('Physics7_4', () => { - | + | b ` @@ -12161,8 +12129,8 @@ describe('Physics7_5', () => { toXmlMatch( tex2mml('\\mqty{\\imat{3}}'), ` - - + + 1 @@ -12173,7 +12141,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12184,7 +12152,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12206,10 +12174,10 @@ describe('Physics7_5', () => { toXmlMatch( tex2mml('\\vmqty{\\imat{5}}'), ` - - | - - + + | + + 1 @@ -12226,7 +12194,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12243,7 +12211,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12260,7 +12228,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12277,7 +12245,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12295,7 +12263,7 @@ describe('Physics7_5', () => { - | + | ` ); @@ -12307,16 +12275,16 @@ describe('Physics7_5', () => { toXmlMatch( tex2mml('\\vmqty{\\imat{0}}'), ` - - | - - + + | + + 1 - | + | ` ); @@ -12328,16 +12296,16 @@ describe('Physics7_5', () => { toXmlMatch( tex2mml('\\vmqty{\\imat{1}}'), ` - - | - - + + | + + 1 - | + | ` ); @@ -12349,16 +12317,16 @@ describe('Physics7_5', () => { toXmlMatch( tex2mml('\\vmqty{\\imat{-1}}'), ` - - | - - + + | + + 1 - | + | ` ); @@ -12370,10 +12338,10 @@ describe('Physics7_5', () => { toXmlMatch( tex2mml('\\pmqty{\\imat{3}\\pmat{0}}'), ` - - ( - - + + ( + + 1 @@ -12384,7 +12352,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12395,7 +12363,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12410,7 +12378,7 @@ describe('Physics7_5', () => { 0 - + 0 @@ -12419,7 +12387,7 @@ describe('Physics7_5', () => { - ) + ) ` ); @@ -12440,9 +12408,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat{1}{2}{3})'), ` - - ( - + + ( + @@ -12469,7 +12437,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12481,9 +12449,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat{a}{3}{3}) '), ` - - ( - + + ( + @@ -12521,7 +12489,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12533,9 +12501,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat{a}{3}{1}) '), ` - - ( - + + ( + @@ -12555,7 +12523,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12567,15 +12535,15 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat{a}{1}{3})'), ` - - ( - + + ( + a - + a @@ -12585,7 +12553,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12597,9 +12565,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat*{1}{2}{3})'), ` - - ( - + + ( + @@ -12686,7 +12654,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12698,9 +12666,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat*{a}{3}{3})'), ` - - ( - + + ( + @@ -12815,7 +12783,7 @@ describe('Physics7_6', () => { a - + 3 @@ -12828,7 +12796,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12840,9 +12808,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat*{a}{3}{1})'), ` - - ( - + + ( + @@ -12877,7 +12845,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12889,9 +12857,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat*{a}{1}{3})'), ` - - ( - + + ( + @@ -12922,7 +12890,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12934,9 +12902,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat*{a}{1}{1})'), ` - - ( - + + ( + @@ -12946,7 +12914,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12958,9 +12926,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\xmat*{a}{-1}{-1})'), ` - - ( - + + ( + @@ -12970,7 +12938,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -12982,9 +12950,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\zmat{1}{3})'), ` - - ( - + + ( + @@ -13000,7 +12968,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -13012,9 +12980,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\zmat{2}{3})'), ` - - ( - + + ( + @@ -13041,7 +13009,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -13053,9 +13021,9 @@ describe('Physics7_6', () => { toXmlMatch( tex2mml('\\smqty(\\zmat{3}{1})'), ` - - ( - + + ( + @@ -13075,7 +13043,7 @@ describe('Physics7_6', () => { - ) + ) ` ); @@ -13096,8 +13064,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{0}}'), ` - - + + 1 @@ -13105,7 +13073,7 @@ describe('Physics7_7', () => { 0 - + 0 @@ -13124,10 +13092,10 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{0}}'), ` - - ( - - + + ( + + 1 @@ -13135,7 +13103,7 @@ describe('Physics7_7', () => { 0 - + 0 @@ -13144,7 +13112,7 @@ describe('Physics7_7', () => { - ) + ) ` ); @@ -13156,8 +13124,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{1}}'), ` - - + + 0 @@ -13165,7 +13133,7 @@ describe('Physics7_7', () => { 1 - + 1 @@ -13184,8 +13152,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{2}}'), ` - - + + 0 @@ -13194,7 +13162,7 @@ describe('Physics7_7', () => { i - + i @@ -13213,8 +13181,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{3}}'), ` - - + + 1 @@ -13222,7 +13190,7 @@ describe('Physics7_7', () => { 0 - + 0 @@ -13242,7 +13210,7 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{4}}'), ` - + ` ); }); @@ -13253,8 +13221,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{x}}'), ` - - + + 0 @@ -13262,7 +13230,7 @@ describe('Physics7_7', () => { 1 - + 1 @@ -13281,8 +13249,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{y}}'), ` - - + + 0 @@ -13291,7 +13259,7 @@ describe('Physics7_7', () => { i - + i @@ -13310,8 +13278,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{z}}'), ` - - + + 1 @@ -13319,7 +13287,7 @@ describe('Physics7_7', () => { 0 - + 0 @@ -13339,7 +13307,7 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{a}}'), ` - + ` ); }); @@ -13350,10 +13318,10 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{a}}'), ` - - ( - - ) + + ( + + ) ` ); @@ -13365,8 +13333,8 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\mqty{\\pmat{aa}}'), ` - - + + a @@ -13382,10 +13350,10 @@ describe('Physics7_7', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{0.a}}'), ` - - ( - - + + ( + + . a @@ -13395,7 +13363,7 @@ describe('Physics7_7', () => { 0 - + 0 @@ -13404,7 +13372,7 @@ describe('Physics7_7', () => { - ) + ) ` ); @@ -13425,10 +13393,10 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{1}}'), ` - - ( - - + + ( + + 0 @@ -13436,7 +13404,7 @@ describe('Physics7_8', () => { 1 - + 1 @@ -13445,7 +13413,7 @@ describe('Physics7_8', () => { - ) + ) ` ); @@ -13457,10 +13425,10 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{2}}'), ` - - ( - - + + ( + + 0 @@ -13469,7 +13437,7 @@ describe('Physics7_8', () => { i - + i @@ -13478,7 +13446,7 @@ describe('Physics7_8', () => { - ) + ) ` ); @@ -13490,10 +13458,10 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{3}}'), ` - - ( - - + + ( + + 1 @@ -13501,7 +13469,7 @@ describe('Physics7_8', () => { 0 - + 0 @@ -13511,7 +13479,7 @@ describe('Physics7_8', () => { - ) + ) ` ); @@ -13523,10 +13491,10 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{4}}'), ` - - ( - - ) + + ( + + ) ` ); @@ -13538,10 +13506,10 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{x}}'), ` - - ( - - + + ( + + 0 @@ -13549,7 +13517,7 @@ describe('Physics7_8', () => { 1 - + 1 @@ -13558,7 +13526,7 @@ describe('Physics7_8', () => { - ) + ) ` ); @@ -13570,10 +13538,10 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{y}}'), ` - - ( - - + + ( + + 0 @@ -13582,7 +13550,7 @@ describe('Physics7_8', () => { i - + i @@ -13591,7 +13559,7 @@ describe('Physics7_8', () => { - ) + ) ` ); @@ -13603,10 +13571,10 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{z}}'), ` - - ( - - + + ( + + 1 @@ -13614,7 +13582,7 @@ describe('Physics7_8', () => { 0 - + 0 @@ -13624,7 +13592,7 @@ describe('Physics7_8', () => { - ) + ) ` ); @@ -13636,16 +13604,16 @@ describe('Physics7_8', () => { toXmlMatch( tex2mml('\\pmqty{\\pmat{aa}}'), ` - - ( - - + + ( + + a - ) + ) ` ); @@ -13666,13 +13634,13 @@ describe('Physics7_9', () => { toXmlMatch( tex2mml('\\mqty(\\dmat{1,2&3\\\\4&5})'), ` - - ( - - + + ( + + - - + + 1 @@ -13680,11 +13648,11 @@ describe('Physics7_9', () => { - + - - + + 2 @@ -13692,7 +13660,7 @@ describe('Physics7_9', () => { 3 - + 4 @@ -13704,7 +13672,7 @@ describe('Physics7_9', () => { - ) + ) ` ); @@ -13716,16 +13684,16 @@ describe('Physics7_9', () => { toXmlMatch( tex2mml('\\mqty(\\dmat 1)'), ` - - ( - - + + ( + + 1 - ) + ) ` ); @@ -13737,10 +13705,10 @@ describe('Physics7_9', () => { toXmlMatch( tex2mml('\\mqty(\\dmat 1,2)'), ` - - ( - - + + ( + + 1 , @@ -13748,7 +13716,7 @@ describe('Physics7_9', () => { - ) + ) ` ); @@ -13760,13 +13728,13 @@ describe('Physics7_9', () => { toXmlMatch( tex2mml('\\mqty(\\dmat{1,2&3\\\\4&5&6,7,8})'), ` - - ( - - + + ( + + - - + + 1 @@ -13774,11 +13742,11 @@ describe('Physics7_9', () => { - + - - + + 2 @@ -13786,7 +13754,7 @@ describe('Physics7_9', () => { 3 - + 4 @@ -13800,12 +13768,12 @@ describe('Physics7_9', () => { - + - - + + 7 @@ -13813,13 +13781,13 @@ describe('Physics7_9', () => { - + - - + + 8 @@ -13828,7 +13796,7 @@ describe('Physics7_9', () => { - ) + ) ` ); @@ -13840,13 +13808,13 @@ describe('Physics7_9', () => { toXmlMatch( tex2mml('\\mqty(\\dmat{1,2&3\\\\4&5,6,7,8})'), ` - - ( - - + + ( + + - - + + 1 @@ -13854,11 +13822,11 @@ describe('Physics7_9', () => { - + - - + + 2 @@ -13866,7 +13834,7 @@ describe('Physics7_9', () => { 3 - + 4 @@ -13877,12 +13845,12 @@ describe('Physics7_9', () => { - + - - + + 6 @@ -13890,13 +13858,13 @@ describe('Physics7_9', () => { - + - - + + 7 @@ -13904,14 +13872,14 @@ describe('Physics7_9', () => { - + - - + + 8 @@ -13920,7 +13888,7 @@ describe('Physics7_9', () => { - ) + ) ` ); @@ -13932,13 +13900,13 @@ describe('Physics7_9', () => { toXmlMatch( tex2mml('\\mqty(\\dmat{1,2\\\\3\\\\4\\\\5\\\\6,7,8})'), ` - - ( - - + + ( + + - - + + 1 @@ -13946,31 +13914,31 @@ describe('Physics7_9', () => { - + - - + + 2 - + 3 - + 4 - + 5 - + 6 @@ -13978,12 +13946,12 @@ describe('Physics7_9', () => { - + - - + + 7 @@ -13991,13 +13959,13 @@ describe('Physics7_9', () => { - + - - + + 8 @@ -14006,7 +13974,7 @@ describe('Physics7_9', () => { - ) + ) ` ); @@ -14018,13 +13986,13 @@ describe('Physics7_9', () => { toXmlMatch( tex2mml('\\mqty(\\dmat{1,2&3\\\\4&5&6,7,8,\\dmat{9,10}})'), ` - - ( - - + + ( + + - - + + 1 @@ -14032,11 +14000,11 @@ describe('Physics7_9', () => { - + - - + + 2 @@ -14044,7 +14012,7 @@ describe('Physics7_9', () => { 3 - + 4 @@ -14058,12 +14026,12 @@ describe('Physics7_9', () => { - + - - + + 7 @@ -14071,13 +14039,13 @@ describe('Physics7_9', () => { - + - - + + 8 @@ -14085,17 +14053,17 @@ describe('Physics7_9', () => { - + - - + + - - + + 9 @@ -14103,13 +14071,13 @@ describe('Physics7_9', () => { - + - - + + - 10 + 10 @@ -14119,7 +14087,7 @@ describe('Physics7_9', () => { - ) + ) ` ); @@ -14217,11 +14185,11 @@ describe('Automatic Bracing Macros Rest', () => { tex2mml('\\vqty\\Bigg{a}'), ` - | + | a - | + | ` ); @@ -14819,11 +14787,11 @@ describe('Derivative Macros Rest', () => { tex2mml('\\derivative{x}'), ` - + d - - + + d x @@ -14841,13 +14809,13 @@ describe('Derivative Macros Rest', () => { ` - + d x - - + + d y @@ -14865,13 +14833,13 @@ describe('Derivative Macros Rest', () => { ` - + d x - - + + d y @@ -14891,8 +14859,8 @@ describe('Derivative Macros Rest', () => { tex2mml('\\partialderivative{x}'), ` - - + + x @@ -14912,7 +14880,7 @@ describe('Derivative Macros Rest', () => { x - + y @@ -14985,8 +14953,8 @@ describe('Derivative Macros Rest', () => { tex2mml('\\pderivative{x}'), ` - - + + x @@ -15006,7 +14974,7 @@ describe('Derivative Macros Rest', () => { x - + y @@ -15379,18 +15347,18 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\innerproduct{A}'), ` - - + + A - | + | A - + ` ); @@ -15402,18 +15370,18 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\innerproduct{A}{B}'), ` - - + + A - | + | B - + ` ); @@ -15425,18 +15393,18 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\innerproduct{A}{B}{C}'), ` - - + + A - | + | B - + C @@ -15451,18 +15419,18 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\ip{A}'), ` - - + + A - | + | A - + ` ); @@ -15474,18 +15442,18 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\ip{A}{B}'), ` - - + + A - | + | B - + ` ); @@ -15497,18 +15465,18 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\ip{A}{B}{C}'), ` - - + + A - | + | B - + C @@ -15523,22 +15491,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\op{A}'), ` - - | + + | A - + - + A - | + | ` ); @@ -15550,22 +15518,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\op{A}{B}'), ` - - | + + | A - + - + B - | + | ` ); @@ -15577,22 +15545,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\op{A}{B}{C}'), ` - - | + + | A - + - + B - | + | C @@ -15607,12 +15575,12 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\expectationvalue{A}'), ` - - + + A - + ` ); @@ -15624,22 +15592,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\expectationvalue{A}{B}'), ` - - + + B - | + | A - - | + + | B - + ` ); @@ -15651,22 +15619,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\expectationvalue{A}{B}{C}'), ` - - + + B - | + | A - - | + + | B - + C @@ -15681,12 +15649,12 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\expval{A}'), ` - - + + A - + ` ); @@ -15698,22 +15666,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\expval{A}{B}'), ` - - + + B - | + | A - - | + + | B - + ` ); @@ -15725,22 +15693,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\expval{A}{B}{C}'), ` - - + + B - | + | A - - | + + | B - + C @@ -15755,22 +15723,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\matrixelement{A}{B}{C}'), ` - - + + A - | + | B - - | + + | C - + ` ); @@ -15782,22 +15750,22 @@ describe('Bra-Ket Macros Rest', () => { toXmlMatch( tex2mml('\\matrixelement{A}{B}{C}{D}'), ` - - + + A - | + | B - - | + + | C - + D @@ -15821,10 +15789,10 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\zeromatrix{2}{3}}'), ` - - ( - - + + ( + + 0 @@ -15835,7 +15803,7 @@ describe('Matrix Macros Rest', () => { 0 - + 0 @@ -15847,7 +15815,7 @@ describe('Matrix Macros Rest', () => { - ) + ) ` ); @@ -15859,10 +15827,10 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\paulimatrix{0}}'), ` - - ( - - + + ( + + 1 @@ -15870,7 +15838,7 @@ describe('Matrix Macros Rest', () => { 0 - + 0 @@ -15879,7 +15847,7 @@ describe('Matrix Macros Rest', () => { - ) + ) ` ); @@ -15891,10 +15859,10 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\paulimatrix{1}}'), ` - - ( - - + + ( + + 0 @@ -15902,7 +15870,7 @@ describe('Matrix Macros Rest', () => { 1 - + 1 @@ -15911,7 +15879,7 @@ describe('Matrix Macros Rest', () => { - ) + ) ` ); @@ -15923,10 +15891,10 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\paulimatrix{2}}'), ` - - ( - - + + ( + + 0 @@ -15935,7 +15903,7 @@ describe('Matrix Macros Rest', () => { i - + i @@ -15944,7 +15912,7 @@ describe('Matrix Macros Rest', () => { - ) + ) ` ); @@ -15956,10 +15924,10 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\paulimatrix{3}}'), ` - - ( - - + + ( + + 1 @@ -15967,7 +15935,7 @@ describe('Matrix Macros Rest', () => { 0 - + 0 @@ -15977,7 +15945,7 @@ describe('Matrix Macros Rest', () => { - ) + ) ` ); @@ -15989,10 +15957,10 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\paulimatrix{4}}'), ` - - ( - - ) + + ( + + ) ` ); @@ -16004,13 +15972,13 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\diagonalmatrix{0,1\\\\2&3}}'), ` - - ( - - + + ( + + - - + + 0 @@ -16018,16 +15986,16 @@ describe('Matrix Macros Rest', () => { - + - - + + 1 - + 2 @@ -16039,7 +16007,7 @@ describe('Matrix Macros Rest', () => { - ) + ) ` ); @@ -16051,14 +16019,14 @@ describe('Matrix Macros Rest', () => { toXmlMatch( tex2mml('\\pmqty{\\antidiagonalmatrix{0,1\\\\2&3}}'), ` - - ( - - + + ( + + - - + + 0 @@ -16066,15 +16034,15 @@ describe('Matrix Macros Rest', () => { - + - - + + 1 - + 2 @@ -16086,7 +16054,7 @@ describe('Matrix Macros Rest', () => { - ) + ) ` ); @@ -16137,7 +16105,7 @@ describe('Rest for Completion', () => { ( - + 1 2 @@ -16165,9 +16133,7 @@ describe('Options', () => { ` - - - + diff --git a/testsuite/tests/input/tex/SetOptions.test.ts b/testsuite/tests/input/tex/SetOptions.test.ts index abdf100a5..fe1af8ce0 100644 --- a/testsuite/tests/input/tex/SetOptions.test.ts +++ b/testsuite/tests/input/tex/SetOptions.test.ts @@ -28,7 +28,9 @@ describe('Setoptions', () => { - (1) + ( + 1 + ) a @@ -75,7 +77,7 @@ describe('Setoptions', () => { toXmlMatch( tex2mml('\\setOptions[ams]{multlineWidth=50%} \\begin{multline} a \\end{multline}'), ` - + a @@ -159,7 +161,7 @@ describe('Setoptions options', () => { toXmlMatch( tex2mml('\\setOptions[ams]{multlineWidth=50%} \\begin{multline} a \\end{multline}'), ` - + a @@ -205,7 +207,7 @@ describe('Setoptions options', () => { toXmlMatch( tex2mml('\\setOptions[ams]{multlineWidth=50%} \\begin{multline} a \\end{multline}'), ` - + a @@ -229,7 +231,7 @@ describe('Setoptions options', () => { toXmlMatch( tex2mml('\\setOptions[ams]{multlineWidth=50%} \\begin{multline} a \\end{multline}'), ` - + a @@ -251,7 +253,7 @@ describe('Setoptions options', () => { toXmlMatch( tex2mml('\\setOptions[ams]{multlineWidth=50%} \\begin{multline} a \\end{multline}'), ` - + a @@ -273,7 +275,7 @@ describe('Setoptions options', () => { toXmlMatch( tex2mml('\\setOptions[ams]{multlineWidth=50%} \\begin{multline} a \\end{multline}'), ` - + a diff --git a/testsuite/tests/input/tex/Tag.test.ts b/testsuite/tests/input/tex/Tag.test.ts index c59313fd3..15ab047b7 100644 --- a/testsuite/tests/input/tex/Tag.test.ts +++ b/testsuite/tests/input/tex/Tag.test.ts @@ -23,10 +23,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('a'), ` - + - (1) + ( + 1 + ) a @@ -43,10 +45,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -63,7 +67,7 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{multline}a\\\\ b\\\\ c\\end{multline}'), ` - + a @@ -76,7 +80,9 @@ describe('TagAll', () => { - (1) + ( + 1 + ) c @@ -93,10 +99,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -113,10 +121,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -133,7 +143,7 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\notag\\end{align}'), ` - + a @@ -150,7 +160,7 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{multline}a\\\\ b\\\\ c\\notag\\end{multline}'), ` - + a @@ -177,7 +187,7 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\notag\\end{align}'), ` - + a @@ -194,10 +204,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{A}'), ` - + - (1) + ( + 1 + ) a @@ -217,10 +229,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{B}'), ` - + - (1) + ( + 1 + ) a @@ -240,10 +254,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\eqref{A}'), ` - + - (1) + ( + 1 + ) a @@ -263,10 +279,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a=b\\label{A}\\\\ c&=d \\label{B}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -276,7 +294,9 @@ describe('TagAll', () => { - (2) + ( + 2 + ) c @@ -328,10 +348,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -348,10 +370,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -368,10 +392,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\\\b\\end{align}'), ` - + - (A) + ( + A + ) a @@ -379,7 +405,9 @@ describe('TagAll', () => { - (1) + ( + 1 + ) b @@ -396,10 +424,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\\\b\\tag{B}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -407,7 +437,9 @@ describe('TagAll', () => { - (B) + ( + B + ) b @@ -424,10 +456,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -444,10 +478,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -464,10 +500,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\\\b\\label{B}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -475,7 +513,9 @@ describe('TagAll', () => { - (1) + ( + 1 + ) b @@ -494,10 +534,12 @@ describe('TagAll', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\tag{B}\\label{B}\\end{align}' ), ` - + - (A) + ( + A + ) a @@ -505,7 +547,9 @@ describe('TagAll', () => { - (B) + ( + B + ) b @@ -522,10 +566,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{A}'), ` - + - (1) + ( + 1 + ) a @@ -545,10 +591,12 @@ describe('TagAll', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\end{align}\\ref{A}'), ` - + - (A) + ( + A + ) a @@ -570,10 +618,12 @@ describe('TagAll', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\label{B}\\end{align}\\ref{A}\\ref{B}' ), ` - + - (A) + ( + A + ) a @@ -581,7 +631,9 @@ describe('TagAll', () => { - (1) + ( + 1 + ) b @@ -606,10 +658,12 @@ describe('TagAll', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\tag{B}\\label{B}\\end{align}\\ref{A}\\ref{B}' ), ` - + - (A) + ( + A + ) a @@ -617,7 +671,9 @@ describe('TagAll', () => { - (B) + ( + B + ) b @@ -664,7 +720,9 @@ describe('TagNone', () => { - (0) + ( + 0 + ) a @@ -681,7 +739,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\end{align}'), ` - + a @@ -698,7 +756,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{split}a\\end{split}'), ` - + a @@ -715,7 +773,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{multline}a\\\\ b\\\\ c\\end{multline}'), ` - + a @@ -742,7 +800,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{}\\end{align}'), ` - + a @@ -759,7 +817,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}'), ` - + a @@ -776,7 +834,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\notag\\end{align}'), ` - + a @@ -793,7 +851,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{multline}a\\\\ b\\\\ c\\notag\\end{multline}'), ` - + a @@ -820,7 +878,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\notag\\end{align}'), ` - + a @@ -837,7 +895,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{A}'), ` - + a @@ -857,7 +915,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{B}'), ` - + a @@ -877,7 +935,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\eqref{A}'), ` - + a @@ -932,7 +990,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\end{align}'), ` - + a @@ -949,10 +1007,12 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -969,10 +1029,12 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\\\b\\end{align}'), ` - + - (A) + ( + A + ) a @@ -994,10 +1056,12 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\\\b\\tag{B}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1005,7 +1069,9 @@ describe('TagNone', () => { - (B) + ( + B + ) b @@ -1022,7 +1088,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}'), ` - + a @@ -1039,10 +1105,12 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1059,10 +1127,12 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\\\b\\label{B}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1086,10 +1156,12 @@ describe('TagNone', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\tag{B}\\label{B}\\end{align}' ), ` - + - (A) + ( + A + ) a @@ -1097,7 +1169,9 @@ describe('TagNone', () => { - (B) + ( + B + ) b @@ -1114,7 +1188,7 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{A}'), ` - + a @@ -1134,10 +1208,12 @@ describe('TagNone', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\end{align}\\ref{A}'), ` - + - (A) + ( + A + ) a @@ -1159,10 +1235,12 @@ describe('TagNone', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\label{B}\\end{align}\\ref{A}\\ref{B}' ), ` - + - (A) + ( + A + ) a @@ -1192,10 +1270,12 @@ describe('TagNone', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\tag{B}\\label{B}\\end{align}\\ref{A}\\ref{B}' ), ` - + - (A) + ( + A + ) a @@ -1203,7 +1283,9 @@ describe('TagNone', () => { - (B) + ( + B + ) b @@ -1247,10 +1329,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -1267,7 +1351,7 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{split}a\\end{split}'), ` - + a @@ -1284,7 +1368,7 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{multline}a\\\\ b\\\\ c\\end{multline}'), ` - + a @@ -1297,7 +1381,9 @@ describe('TagAms', () => { - (1) + ( + 1 + ) c @@ -1314,10 +1400,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -1334,10 +1422,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -1354,7 +1444,7 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\notag\\end{align}'), ` - + a @@ -1371,7 +1461,7 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{multline}a\\\\ b\\\\ c\\notag\\end{multline}'), ` - + a @@ -1398,7 +1488,7 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\notag\\end{align}'), ` - + a @@ -1415,10 +1505,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{A}'), ` - + - (1) + ( + 1 + ) a @@ -1438,10 +1530,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{B}'), ` - + - (1) + ( + 1 + ) a @@ -1461,10 +1555,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\eqref{A}'), ` - + - (1) + ( + 1 + ) a @@ -1484,10 +1580,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a=b\\label{A}\\\\ c&=d \\label{B}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -1497,7 +1595,9 @@ describe('TagAms', () => { - (2) + ( + 2 + ) c @@ -1549,10 +1649,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -1569,10 +1671,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1589,10 +1693,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\\\b\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1600,7 +1706,9 @@ describe('TagAms', () => { - (1) + ( + 1 + ) b @@ -1617,10 +1725,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\\\b\\tag{B}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1628,7 +1738,9 @@ describe('TagAms', () => { - (B) + ( + B + ) b @@ -1645,10 +1757,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}'), ` - + - (1) + ( + 1 + ) a @@ -1665,10 +1779,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1685,10 +1801,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\\\b\\label{B}\\end{align}'), ` - + - (A) + ( + A + ) a @@ -1696,7 +1814,9 @@ describe('TagAms', () => { - (1) + ( + 1 + ) b @@ -1715,10 +1835,12 @@ describe('TagAms', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\tag{B}\\label{B}\\end{align}' ), ` - + - (A) + ( + A + ) a @@ -1726,7 +1848,9 @@ describe('TagAms', () => { - (B) + ( + B + ) b @@ -1743,10 +1867,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\label{A}\\end{align}\\ref{A}'), ` - + - (1) + ( + 1 + ) a @@ -1766,10 +1892,12 @@ describe('TagAms', () => { toXmlMatch( tex2mml('\\begin{align}a\\tag{A}\\label{A}\\end{align}\\ref{A}'), ` - + - (A) + ( + A + ) a @@ -1791,10 +1919,12 @@ describe('TagAms', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\label{B}\\end{align}\\ref{A}\\ref{B}' ), ` - + - (A) + ( + A + ) a @@ -1802,7 +1932,9 @@ describe('TagAms', () => { - (1) + ( + 1 + ) b @@ -1827,10 +1959,12 @@ describe('TagAms', () => { '\\begin{align}a\\tag{A}\\label{A}\\\\b\\tag{B}\\label{B}\\end{align}\\ref{A}\\ref{B}' ), ` - + - (A) + ( + A + ) a @@ -1838,7 +1972,9 @@ describe('TagAms', () => { - (B) + ( + B + ) b @@ -1877,7 +2013,9 @@ describe('Page References', () => { - (1) + ( + 1 + ) a @@ -1909,7 +2047,9 @@ describe('Page References', () => { - (1) + ( + 1 + ) a @@ -1931,7 +2071,9 @@ describe('Page References', () => { - (1) + ( + 1 + ) a diff --git a/testsuite/tests/input/tex/TagFormat.test.ts b/testsuite/tests/input/tex/TagFormat.test.ts index fe2ed47bc..22076df16 100644 --- a/testsuite/tests/input/tex/TagFormat.test.ts +++ b/testsuite/tests/input/tex/TagFormat.test.ts @@ -27,7 +27,9 @@ describe('Tagformat', () => { - [1] + [ + 1 + ] x @@ -47,7 +49,9 @@ describe('Tagformat', () => { - [x] + [ + x + ] x @@ -64,10 +68,12 @@ describe('Tagformat', () => { toXmlMatch( tex2mml('\\begin{align}x \\label{test}\\tag{x}\\\\ \\ref{test} \\end{align}'), ` - + - [x] + [ + x + ] x @@ -75,7 +81,9 @@ describe('Tagformat', () => { - [A1] + [ + A1 + ] @@ -94,10 +102,12 @@ describe('Tagformat', () => { toXmlMatch( tex2mml('\\begin{align}x \\label{test}\\tag{x}\\\\ \\eqref{test} \\end{align}'), ` - + - [x] + [ + x + ] x @@ -105,7 +115,9 @@ describe('Tagformat', () => { - [A1] + [ + A1 + ] @@ -130,10 +142,12 @@ describe('Tagformat', () => { toXmlMatch( tex2mml('\\begin{align}x \\label{test}\\tag{x}\\\\ \\eqref{test} \\end{align}'), ` - + - (x) + ( + x + ) x @@ -141,7 +155,9 @@ describe('Tagformat', () => { - (1) + ( + 1 + ) @@ -156,6 +172,88 @@ describe('Tagformat', () => { /********************************************************************************/ + test('Array tag', () => { + setupTex(['base', 'ams', 'tagformat'], { + tagformat: { + tag: (tag: string) => ['|', tag, '|'], + }, + tags: 'ams' + }); + toXmlMatch( + tex2mml('x \\tag{1}'), + ` + + + + | + 1 + | + + + x + + + + ` + ); + }); + + /********************************************************************************/ + + test('Array tag with empty entry', () => { + setupTex(['base', 'ams', 'tagformat'], { + tagformat: { + tag: (tag: string) => ['', tag, '.'], + }, + tags: 'ams' + }); + toXmlMatch( + tex2mml('x \\tag{1}'), + ` + + + + 1 + . + + + x + + + + ` + ); + }); + + /********************************************************************************/ + + test('Array tag with null entry', () => { + setupTex(['base', 'ams', 'tagformat'], { + tagformat: { + tag: (tag: string) => [ , tag, '.'], + }, + tags: 'ams' + }); + toXmlMatch( + tex2mml('x \\tag{1}'), + ` + + + + 1 + . + + + x + + + + ` + ); + }); + + /********************************************************************************/ + }); /**********************************************************************************/ diff --git a/testsuite/tests/input/tex/Tex.test.ts b/testsuite/tests/input/tex/Tex.test.ts index 131c4a4ff..5ec1a5d24 100644 --- a/testsuite/tests/input/tex/Tex.test.ts +++ b/testsuite/tests/input/tex/Tex.test.ts @@ -239,9 +239,7 @@ describe('FindTeX', () => { await page2mml('abc \\$ def'), [ ` - - $ - + $ ` ] ); @@ -438,21 +436,24 @@ describe('TexParser', () => { toXmlMatch( tex2mml('\\xrightarrow[{[x]}]{a}'), ` - - - - - [ - x - ] - - - - - a - - - + + + + + + + [ + x + ] + + + + + a + + + + ` ); }); @@ -503,7 +504,7 @@ describe('TexParser', () => { toXmlMatch( tex2mml('\\let\\vert=x \\begin{vmatrix} a \\end{vmatrix}'), ` - + a diff --git a/testsuite/tests/input/tex/TexHtml.test.ts b/testsuite/tests/input/tex/TexHtml.test.ts index f85b974e5..6c3dad532 100644 --- a/testsuite/tests/input/tex/TexHtml.test.ts +++ b/testsuite/tests/input/tex/TexHtml.test.ts @@ -25,7 +25,7 @@ describe('Texhtml', () => { ` x + - bold + bold + y ` @@ -40,9 +40,9 @@ describe('Texhtml', () => { ` x + - bold + bold + - italic + italic y ` ); @@ -56,7 +56,7 @@ describe('Texhtml', () => { ` x + -
bold and html
+
bold and html
+ y
` @@ -70,7 +70,7 @@ describe('Texhtml', () => { render2mml('x + + y'), ` x - + + + + y ` @@ -136,19 +136,15 @@ describe('Texhtml not enabled', () => { t m l - >< + >< b > a < - - / - + / b >< - - / - + / t e x diff --git a/testsuite/tests/input/tex/Unicode.test.ts b/testsuite/tests/input/tex/Unicode.test.ts index 7316c82ab..3a6bfbbff 100644 --- a/testsuite/tests/input/tex/Unicode.test.ts +++ b/testsuite/tests/input/tex/Unicode.test.ts @@ -255,9 +255,7 @@ describe('Unicode others', () => { toXmlMatch( tex2mml('\\char"A5'), ` - - ¥ - + ¥ ` ); }); diff --git a/testsuite/tests/input/tex/Units.test.ts b/testsuite/tests/input/tex/Units.test.ts index 2ee049e10..72253c610 100644 --- a/testsuite/tests/input/tex/Units.test.ts +++ b/testsuite/tests/input/tex/Units.test.ts @@ -29,7 +29,7 @@ describe('Units', () => { tex2mml('\\units{m^2}'), ` - + m 2 @@ -44,7 +44,7 @@ describe('Units', () => { toXmlMatch( tex2mml('\\units[2.5]{kg}'), ` - 2.5 + 2.5 kg @@ -77,14 +77,14 @@ describe('Units', () => { toXmlMatch( tex2mml('\\unitfrac[9.8]{m}{s^2}'), ` - 9.8 + 9.8 m - + s 2 diff --git a/testsuite/tests/util/Context-android.test.ts b/testsuite/tests/util/Context-android.test.ts index 4b9c88db1..d95322bde 100644 --- a/testsuite/tests/util/Context-android.test.ts +++ b/testsuite/tests/util/Context-android.test.ts @@ -7,6 +7,8 @@ describe('context object', () => { test('context', async () => { let {context, hasWindow} = await import("#js/util/context.js"); + expect(context.path('C:\\test.js')).toBe('C:\\test.js'); + delete context.path; expect(context).toEqual({window: window, document: window.document, os: 'Unix'}); expect(hasWindow).toBe(true); }); diff --git a/testsuite/tests/util/Context-browser.test.ts b/testsuite/tests/util/Context-browser.test.ts index cb5d959cf..56e939fec 100644 --- a/testsuite/tests/util/Context-browser.test.ts +++ b/testsuite/tests/util/Context-browser.test.ts @@ -7,6 +7,8 @@ describe('context object', () => { test('context', async () => { let {context, hasWindow} = await import("#js/util/context.js"); + expect(context.path('C:\\test.js')).toBe('C:\\test.js'); + delete context.path; expect(context).toEqual({window: window, document: window.document, os: 'Unix'}); expect(hasWindow).toBe(true); }); diff --git a/testsuite/tests/util/Context-node-unknown.test.ts b/testsuite/tests/util/Context-node-unknown.test.ts new file mode 100644 index 000000000..094e5514f --- /dev/null +++ b/testsuite/tests/util/Context-node-unknown.test.ts @@ -0,0 +1,15 @@ +import { describe, test, expect } from '@jest/globals'; + +global.process = {...process, platform: 'test'} as any; + +describe('context object', () => { + + test('context', async () => { + let {context, hasWindow} = await import("#js/util/context.js"); + expect(context.path('C:\\test.js')).toBe('C:\\test.js'); + delete context.path; + expect(context).toEqual({window: null, document: null, os: 'test'}); + expect(hasWindow).toBe(false); + }); + +}); diff --git a/testsuite/tests/util/Context-node.test.ts b/testsuite/tests/util/Context-node.test.ts index 8df0b38fe..4c5fc15fe 100644 --- a/testsuite/tests/util/Context-node.test.ts +++ b/testsuite/tests/util/Context-node.test.ts @@ -1,10 +1,30 @@ import { describe, test, expect } from '@jest/globals'; import { context, hasWindow } from '#js/util/context.js'; +const OS = { + 'linux': 'Unix', + 'android': 'Unix', + 'aix': 'Unix', + 'freebsd': 'Unix', + 'netbsd': 'Unix', + 'openbsd': 'Unix', + 'sunos': 'Unix', + 'darwin': 'MacOS', + 'win32': 'Windows', + 'cygwin': 'Windows', + 'haiku': 'unknown', +}[process.platform] || process.platform; + describe('context object', () => { test('context', () => { - expect(context).toEqual({window: null, document: null, os: 'unknown'}); + if (OS === 'Windows') { + expect(context.path('C:\\test.js')).toBe('C:/test.js'); + } else { + expect(context.path('C:\\test.js')).toBe('C:\\test.js'); + } + delete context.path; + expect(context).toEqual({window: null, document: null, os: OS}); expect(hasWindow).toBe(false); }); diff --git a/testsuite/tests/util/Context-unknown.test.ts b/testsuite/tests/util/Context-unknown.test.ts new file mode 100644 index 000000000..cc7ed9f63 --- /dev/null +++ b/testsuite/tests/util/Context-unknown.test.ts @@ -0,0 +1,16 @@ +import { describe, test, expect } from '@jest/globals'; + +const window = {document: {}, navigator: {userAgent: '', appVersion: ''}}; +(global as any).window = window; + +describe('context object', () => { + + test('context', async () => { + let {context, hasWindow} = await import("#js/util/context.js"); + expect(context.path('C:\\test.js')).toBe('C:\\test.js'); + delete context.path; + expect(context).toEqual({window: window, document: window.document, os: 'unknown'}); + expect(hasWindow).toBe(true); + }); + +}); diff --git a/testsuite/tests/util/Context-windows.test.ts b/testsuite/tests/util/Context-windows.test.ts new file mode 100644 index 000000000..8b8550e40 --- /dev/null +++ b/testsuite/tests/util/Context-windows.test.ts @@ -0,0 +1,18 @@ +import { describe, test, expect } from '@jest/globals'; + +const window = {document: {}, navigator: {appVersion: 'Win'}}; +(global as any).window = window; + +describe('context object', () => { + + test('context', async () => { + let {context, hasWindow} = await import("#js/util/context.js"); + expect(context.path('C:\\test.js')).toBe('C:/test.js'); + expect(context.path('/C:/test.js')).toBe('C:/test.js'); + expect(context.path('/test.js')).toBe('/test.js'); + delete context.path; + expect(context).toEqual({window: window, document: window.document, os: 'Windows'}); + expect(hasWindow).toBe(true); + }); + +}); diff --git a/testsuite/tests/util/StyleJson.test.ts b/testsuite/tests/util/StyleJson.test.ts index 84a013515..08d76dd2c 100644 --- a/testsuite/tests/util/StyleJson.test.ts +++ b/testsuite/tests/util/StyleJson.test.ts @@ -36,4 +36,20 @@ describe('StyleJsonSheet object', () => { expect(styles.cssText).toBe(''); }); + test('Compound style', () => { + expect(new StyleJsonSheet({ + '@media (prefers-color-scheme: dark)': { + 'mjx-container': { + 'color': '#E0E0E0', + }, + } + }).cssText).toBe([ + '@media (prefers-color-scheme: dark) {', + ' mjx-container {', + ' color: #E0E0E0;', + ' }', + '}' + ].join('\n')); + }); + }); diff --git a/testsuite/tests/util/Styles.test.ts b/testsuite/tests/util/Styles.test.ts index 1e80abaea..adcd0b459 100644 --- a/testsuite/tests/util/Styles.test.ts +++ b/testsuite/tests/util/Styles.test.ts @@ -82,9 +82,69 @@ describe('CssStyles object', () => { 'padding-right': '0', 'padding-top': '0' }, 'padding: 0;'); + cssTest('padding-left: 2px; padding: 0', { + 'padding': '0', + 'padding-bottom': '0', + 'padding-left': '0', + 'padding-right': '0', + 'padding-top': '0' + }, 'padding: 0;'); cssTest('padding:', {}, ''); }); + test('margin', () => { + cssTest('margin-left: 2px; margin: 0', { + 'margin': '0', + 'margin-bottom': '0', + 'margin-left': '0', + 'margin-right': '0', + 'margin-top': '0' + }, 'margin: 0;'); + cssTest('margin: 3px', { + 'margin': '3px', + 'margin-bottom': '3px', + 'margin-left': '3px', + 'margin-right': '3px', + 'margin-top': '3px' + }); + cssTest('margin: 3px; margin-right: 1px', { + 'margin': '3px 1px 3px 3px', + 'margin-bottom': '3px', + 'margin-left': '3px', + 'margin-right': '1px', + 'margin-top': '3px' + }, 'margin: 3px 1px 3px 3px;'); + cssTest('margin-top: 0; margin-right: 1px; margin-bottom: 0; margin-left: 1px', { + 'margin': '0 1px', + 'margin-bottom': '0', + 'margin-left': '1px', + 'margin-right': '1px', + 'margin-top': '0' + }, 'margin: 0 1px;'); + cssTest('margin-top: 0; margin-right: 1px; margin-bottom: 2px; margin-left: 1px', { + 'margin': '0 1px 2px', + 'margin-bottom': '2px', + 'margin-left': '1px', + 'margin-right': '1px', + 'margin-top': '0' + }, 'margin: 0 1px 2px;'); + cssTest('margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0', { + 'margin': '0', + 'margin-bottom': '0', + 'margin-left': '0', + 'margin-right': '0', + 'margin-top': '0' + }, 'margin: 0;'); + cssTest('margin-left: 2px; margin: 0', { + 'margin': '0', + 'margin-bottom': '0', + 'margin-left': '0', + 'margin-right': '0', + 'margin-top': '0' + }, 'margin: 0;'); + cssTest('margin:', {}, ''); + }), + test('border', () => { cssTest('border: 3px solid red', { 'border': '3px solid red', @@ -255,6 +315,31 @@ describe('CssStyles object', () => { 'background': 'red', 'background-clip': 'none', }); + cssTest(' border-top: inset blue 2px; border: 3px solid red', { + 'border': '3px solid red', + 'border-top': '3px solid red', + 'border-top-color': 'red', + 'border-top-style': 'solid', + 'border-top-width': '3px', + 'border-right': '3px solid red', + 'border-right-color': 'red', + 'border-right-style': 'solid', + 'border-right-width': '3px', + 'border-bottom': '3px solid red', + 'border-bottom-color': 'red', + 'border-bottom-style': 'solid', + 'border-bottom-width': '3px', + 'border-left': '3px solid red', + 'border-left-color': 'red', + 'border-left-style': 'solid', + 'border-left-width': '3px', + }, 'border: 3px solid red;'); + cssTest('border-top-color: blue; border-top: 3px solid red', { + 'border-top': '3px solid red', + 'border-top-color': 'red', + 'border-top-style': 'solid', + 'border-top-width': '3px', + }, 'border-top: 3px solid red;'); }); test('font', () => { @@ -356,4 +441,16 @@ describe('CssStyles object', () => { expect(styles.get('border')).toBe(''); }); + test('set()', () => { + const styles = new Styles('padding-left: 2px'); + styles.set('padding-left', '3px'); + expect(styles.get('padding-left')).toBe('3px'); + expect(styles.get('padding')).toBe(''); + expect(styles.cssText).toBe('padding-left: 3px;'); + styles.set('padding', ''); + expect(styles.get('padding-left')).toBe(''); + expect(styles.get('padding')).toBe(''); + expect(styles.cssText).toBe(''); + }); + }); diff --git a/ts/a11y/assistive-mml.ts b/ts/a11y/assistive-mml.ts index 4a4ada224..b2fe143b0 100644 --- a/ts/a11y/assistive-mml.ts +++ b/ts/a11y/assistive-mml.ts @@ -158,8 +158,11 @@ export function AssistiveMmlMathItemMixin< * @template T The Text node class * @template D The Document class */ -export interface AssistiveMmlMathDocument - extends AbstractMathDocument { +export interface AssistiveMmlMathDocument extends AbstractMathDocument< + N, + T, + D +> { /** * @param {MmlNode} node The node to be serializes * @returns {string} The serialization of the node diff --git a/ts/a11y/complexity.ts b/ts/a11y/complexity.ts index 7103ad9f2..dc2e845ba 100644 --- a/ts/a11y/complexity.ts +++ b/ts/a11y/complexity.ts @@ -129,8 +129,11 @@ export function ComplexityMathItemMixin>( * @template T The Text node class * @template D The Document class */ -export interface ComplexityMathDocument - extends EnrichedMathDocument { +export interface ComplexityMathDocument extends EnrichedMathDocument< + N, + T, + D +> { /** * Perform complexity computations on the MathItems in the MathDocument * diff --git a/ts/a11y/complexity/collapse.ts b/ts/a11y/complexity/collapse.ts index f20db9065..eb2a5abf3 100644 --- a/ts/a11y/complexity/collapse.ts +++ b/ts/a11y/complexity/collapse.ts @@ -26,6 +26,7 @@ import { AbstractMmlTokenNode, TextNode, } from '../../core/MmlTree/MmlNode.js'; +import { PropertyList } from '../../core/Tree/Node.js'; import { ComplexityVisitor } from './visitor.js'; /*==========================================================================*/ @@ -584,9 +585,10 @@ export class Collapse { const factory = this.complexity.factory; const marker = node.getProperty('collapse-marker') as string; const parent = node.parent; - const variant = node.getProperty('collapse-variant') - ? { mathvariant: '-tex-variant' } - : {}; + const variant = { 'data-mjx-collapsed': true } as PropertyList; + if (node.getProperty('collapse-variant')) { + variant.mathvariant = '-tex-variant'; + } const maction = factory.create( 'maction', { @@ -599,7 +601,7 @@ export class Collapse { ), }, [ - factory.create('mtext', { mathcolor: 'blue', ...variant }, [ + factory.create('mtext', variant, [ (factory.create('text') as TextNode).setText(marker), ]), ] diff --git a/ts/a11y/explorer.ts b/ts/a11y/explorer.ts index 7964807c0..598a13195 100644 --- a/ts/a11y/explorer.ts +++ b/ts/a11y/explorer.ts @@ -80,6 +80,11 @@ export interface ExplorerMathItem extends HTMLMATHITEM { */ none: string; + /** + * The string to use for when there is no Braille description; + */ + brailleNone: string; + /** * The Explorer objects for this math item */ @@ -138,6 +143,11 @@ export function ExplorerMathItemMixin
>( */ protected static none: string = '\u0091'; + /** + * Braille decription to use when set to none + */ + protected static brailleNone: string = '\u2800'; + public get ariaRole() { return (this.constructor as typeof BaseClass).ariaRole; } @@ -153,6 +163,10 @@ export function ExplorerMathItemMixin>( return (this.constructor as typeof BaseClass).none; } + public get brailleNone() { + return (this.constructor as typeof BaseClass).brailleNone; + } + /** * @override */ @@ -321,6 +335,7 @@ export function ExplorerMathDocumentMixin< public static OPTIONS: OptionList = { ...BaseDocument.OPTIONS, enableExplorer: hasWindow, // only activate in interactive contexts + enableExplorerHelp: true, // help dialog is enabled renderActions: expandable({ ...BaseDocument.OPTIONS.renderActions, explorable: [STATE.EXPLORER] @@ -350,6 +365,8 @@ export function ExplorerMathDocumentMixin< treeColoring: false, // tree color expression viewBraille: false, // display Braille output as subtitles voicing: false, // switch on speech output + brailleSpeech: false, // use aria-label for Braille + brailleCombine: false, // combine Braille with speech output help: true, // include "press h for help" messages on focus roleDescription: 'math', // the role description to use for math expressions tabSelects: 'all', // 'all' for whole expression, 'last' for last explored node @@ -378,17 +395,30 @@ export function ExplorerMathDocumentMixin< 'mjx-container .mjx-selected': { outline: '2px solid black', }, + + 'mjx-container a[data-mjx-href]': { + color: 'LinkText', + cursor: 'pointer', + }, + 'mjx-container a[data-mjx-href].mjx-visited': { + color: 'VisitedText', + }, + 'mjx-container > mjx-help': { display: 'none', position: 'absolute', - top: '-.33em', + top: '-.3em', right: '-.5em', width: '.6em', height: '.6em', cursor: 'pointer', }, 'mjx-container[display="true"] > mjx-help': { + position: 'sticky', + inset: '-100% 0 100% 0', + margin: '-.3em -.5em 0 -.1em', right: 0, + top: 'initial', }, 'mjx-help > svg': { stroke: 'black', @@ -403,7 +433,7 @@ export function ExplorerMathDocumentMixin< fill: 'white', }, 'mjx-help > svg > circle:nth-child(2)': { - fill: 'rgba(0, 0, 255, 0.2)', + fill: 'var(--mjx-bg1-color)', r: '7px', }, 'mjx-help > svg > line': { @@ -417,77 +447,20 @@ export function ExplorerMathDocumentMixin< display: 'inline-flex', 'align-items': 'center', }, - - 'mjx-help-sizer': { - position: 'fixed', - width: '40%', - 'max-width': '30em', - top: '3em', - left: '50%', - }, - 'mjx-help-dialog': { - position: 'absolute', - width: '200%', - left: '-100%', - border: '3px outset', - 'border-radius': '15px', - color: 'black', - 'background-color': '#DDDDDD', - 'z-index': '301', - 'text-align': 'right', - 'font-style': 'normal', - 'text-indent': 0, - 'text-transform': 'none', - 'line-height': 'normal', - 'letter-spacing': 'normal', - 'word-spacing': 'normal', - 'word-wrap': 'normal', - float: 'none', - 'box-shadow': '0px 10px 20px #808080', - outline: 'none', - }, - 'mjx-help-dialog > h1': { - 'font-size': '24px', - 'text-align': 'center', - margin: '.5em 0', - }, - 'mjx-help-dialog > div': { - margin: '0 1em', - padding: '3px', - overflow: 'auto', - height: '20em', - border: '2px inset black', - 'background-color': 'white', - 'text-align': 'left', - }, - 'mjx-help-dialog > input': { - margin: '.5em 2em', - }, - 'mjx-help-dialog kbd': { - display: 'inline-block', - padding: '3px 5px', - 'font-size': '11px', - 'line-height': '10px', - color: '#444d56', - 'vertical-align': 'middle', - 'background-color': '#fafbfc', - border: 'solid 1.5px #c6cbd1', - 'border-bottom-color': '#959da5', - 'border-radius': '3px', - 'box-shadow': 'inset -.5px -1px 0 #959da5', - }, - 'mjx-help-dialog ul': { - 'list-style-type': 'none', - }, - 'mjx-help-dialog li': { - 'margin-bottom': '.5em', - }, - 'mjx-help-background': { - position: 'fixed', - top: 0, - left: 0, - right: 0, - bottom: 0, + '@media (prefers-color-scheme: dark) /* explorer */': { + 'mjx-help > svg': { + stroke: '#E0E0E0', + }, + 'mjx-help > svg > circle': { + fill: '#404040', + }, + 'mjx-help > svg > circle:nth-child(2)': { + fill: 'rgba(132, 132, 255, .3)', + }, + 'mjx-help:hover > svg > circle:nth-child(2)': { + stroke: '#AAAAAA', + fill: '#404040', + }, }, }; @@ -556,7 +529,7 @@ export function ExplorerMathDocumentMixin< SVGNS ), ]); - this.tmpFocus = this.adaptor.node('mjx-focus', { + this.tmpFocus = adaptor.node('mjx-focus', { tabIndex: 0, style: { outline: 'none', diff --git a/ts/a11y/explorer/ExplorerPool.ts b/ts/a11y/explorer/ExplorerPool.ts index f497f1575..e631eb8fc 100644 --- a/ts/a11y/explorer/ExplorerPool.ts +++ b/ts/a11y/explorer/ExplorerPool.ts @@ -30,7 +30,6 @@ import * as me from './MouseExplorer.js'; import { TreeColorer, FlameColorer } from './TreeExplorer.js'; import { Highlighter, getHighlighter } from './Highlighter.js'; -// import * as Sre from '../sre.js'; /** * The regions objects needed for the explorers. @@ -360,6 +359,7 @@ export class ExplorerPool { protected setPrimaryHighlighter() { const [foreground, background] = this.colorOptions(); this._highlighter = getHighlighter( + LiveRegion.priority.primary, background, foreground, this.document.outputJax.name @@ -371,6 +371,7 @@ export class ExplorerPool { */ protected setSecondaryHighlighter() { this.secondaryHighlighter = getHighlighter( + LiveRegion.priority.secondary, { color: 'red' }, { color: 'black' }, this.document.outputJax.name diff --git a/ts/a11y/explorer/Highlighter.ts b/ts/a11y/explorer/Highlighter.ts index 7f520914c..82c1074e9 100644 --- a/ts/a11y/explorer/Highlighter.ts +++ b/ts/a11y/explorer/Highlighter.ts @@ -18,62 +18,32 @@ * @author v.sorge@mathjax.org (Volker Sorge) */ -interface NamedColor { - color: string; - alpha?: number; -} +import { LiveRegion } from './Region.js'; -interface ChannelColor { - red: number; - green: number; - blue: number; +export interface NamedColor { + color: string; alpha?: number; } -const namedColors: { [key: string]: ChannelColor } = { - red: { red: 255, green: 0, blue: 0 }, - green: { red: 0, green: 255, blue: 0 }, - blue: { red: 0, green: 0, blue: 255 }, - yellow: { red: 255, green: 255, blue: 0 }, - cyan: { red: 0, green: 255, blue: 255 }, - magenta: { red: 255, green: 0, blue: 255 }, - white: { red: 255, green: 255, blue: 255 }, - black: { red: 0, green: 0, blue: 0 }, -}; - -/** - * Turns a named color into a channel color. - * - * @param {NamedColor} color The definition. - * @param {NamedColor} deflt The default color name if the named color does not exist. - * @returns {string} The channel color. - */ -function getColorString(color: NamedColor, deflt: NamedColor): string { - const channel = namedColors[color.color] || namedColors[deflt.color]; - channel.alpha = color.alpha ?? deflt.alpha; - return rgba(channel); -} - -/** - * RGBa string version of the channel color. - * - * @param {ChannelColor} color The channel color. - * @returns {string} The color in RGBa format. - */ -function rgba(color: ChannelColor): string { - return `rgba(${color.red},${color.green},${color.blue},${color.alpha ?? 1})`; -} - /** * The default background color if a none existing color is provided. */ -const DEFAULT_BACKGROUND: NamedColor = { color: 'blue', alpha: 1 }; +const DEFAULT_BACKGROUND: NamedColor = { color: 'blue', alpha: 0.2 }; /** * The default color if a none existing color is provided. */ const DEFAULT_FOREGROUND: NamedColor = { color: 'black', alpha: 1 }; +/** + * The attributes for various markers + */ +export const ATTR = { + ENCLOSED: 'data-sre-enclosed', + BBOX: 'data-sre-highlighter-bbox', + ADDED: 'data-sre-highlighter-added', +}; + export interface Highlighter { /** * Sets highlighting on a node. @@ -99,6 +69,15 @@ export interface Highlighter { */ unhighlightAll(): void; + /** + * Encloses multiple nodes if they in the same line + * + * @param {HTMLElement[]} parts The elements to be selected + * @param {HTMLElement} node The root node of the expression + * @returns {HTMLElement[]} The elements that shoudl be highlighted + */ + encloseNodes(parts: HTMLElement[], node: HTMLElement): HTMLElement[]; + /** * Predicate to check if a node is an maction node. * @@ -108,14 +87,12 @@ export interface Highlighter { isMactionNode(node: Element): boolean; /** - * @returns {string} The foreground color as rgba string. - */ - get foreground(): string; - - /** - * @returns {string} The background color as rgba string. + * Returns the maction sub nodes of a given node. + * + * @param {HTMLElement} node The root node. + * @returns {HTMLElement[]} The list of maction sub nodes. */ - get background(): string; + getMactionNodes(node: HTMLElement): HTMLElement[]; /** * Sets of the color the highlighter is using. @@ -126,76 +103,63 @@ export interface Highlighter { setColor(background: NamedColor, foreground: NamedColor): void; } -/** - * Highlight information consisting of node, fore and background color. - */ -interface Highlight { - node: HTMLElement; - background?: string; - foreground?: string; -} - -let counter = 0; - abstract class AbstractHighlighter implements Highlighter { /** - * This counter creates a unique highlighter name. This is important in case - * we have more than a single highlighter on a node, e.g., during auto voicing - * with synchronised highlighting. + * The Attribute for marking highlighted nodes. */ - public counter = counter++; + protected ATTR: string; /** - * The Attribute for marking highlighted nodes. + * The CSS selector to use to find the line-box container. */ - protected ATTR = 'sre-highlight-' + this.counter.toString(); + protected static lineSelector: string; /** - * The foreground color. + * The attribute name for the line number. */ - private _foreground: string; + protected static lineAttr: string; /** - * The background color. + * Primary highlighter = 1, secondary highlighter = 2 */ - private _background: string; + protected priority: number; /** - * The maction name/class for a highlighter. + * List of currently highlighted nodes and their original background color. */ - protected mactionName = ''; + private currentHighlights: HTMLElement[][] = []; /** - * List of currently highlighted nodes and their original background color. + * @param {number} priority 1 = primary, 2 = secondary */ - private currentHighlights: Highlight[][] = []; + constructor(priority: number) { + this.priority = priority; + this.ATTR = 'data-sre-highlight-' + priority; + } /** * Highlights a single node. * - * @param node The node to be highlighted. - * @returns The old node information. + * @param {HTMLElement} node The node to be highlighted. */ - protected abstract highlightNode(node: HTMLElement): Highlight; + protected abstract highlightNode(node: HTMLElement): void; /** * Unhighlights a single node. * - * @param highlight The highlight info for the node to be unhighlighted. + * @param {HTMLElement} node The highlight info for the node to be unhighlighted. */ - protected abstract unhighlightNode(highlight: Highlight): void; + protected abstract unhighlightNode(node: HTMLElement): void; /** * @override */ public highlight(nodes: HTMLElement[]) { - this.currentHighlights.push( - nodes.map((node) => { - const info = this.highlightNode(node); - this.setHighlighted(node); - return info; - }) - ); + this.currentHighlights.push(nodes); + for (const node of nodes) { + this.highlightNode(node); + this.setHighlighted(node); + } } /** @@ -203,7 +167,7 @@ abstract class AbstractHighlighter implements Highlighter { */ public highlightAll(node: HTMLElement) { const mactions = this.getMactionNodes(node); - for (let i = 0, maction; (maction = mactions[i]); i++) { + for (const maction of mactions) { this.highlight([maction]); } } @@ -216,10 +180,10 @@ abstract class AbstractHighlighter implements Highlighter { if (!nodes) { return; } - nodes.forEach((highlight: Highlight) => { - if (this.isHighlighted(highlight.node)) { - this.unhighlightNode(highlight); - this.unsetHighlighted(highlight.node); + nodes.forEach((node: HTMLElement) => { + if (this.isHighlighted(node)) { + this.unhighlightNode(node); + this.unsetHighlighted(node); } }); } @@ -233,26 +197,88 @@ abstract class AbstractHighlighter implements Highlighter { } } + /** + * Create a container of a given size and position. + * + * @param {number} x The x-coordinate for the container + * @param {number} y The y-coordinate for the container + * @param {number} w The width for the container + * @param {number} h The height for the container + * @param {HTMLElement} node The mjx-container element + * @param {HTMLElement} part The first node in the line to be enclosed + * @returns {HTMLElement} The element of the given size + */ + protected abstract createEnclosure( + x: number, + y: number, + w: number, + h: number, + node: HTMLElement, + part: HTMLElement + ): HTMLElement; + /** * @override */ - public setColor(background: NamedColor, foreground: NamedColor) { - this._foreground = getColorString(foreground, DEFAULT_FOREGROUND); - this._background = getColorString(background, DEFAULT_BACKGROUND); + public encloseNodes(parts: HTMLElement[], node: HTMLElement): HTMLElement[] { + if (parts.length === 1) { + return parts; + } + const CLASS = this.constructor as typeof AbstractHighlighter; + const selector = CLASS.lineSelector; + const lineno = CLASS.lineAttr; + const lines: Map = new Map(); + for (const part of parts) { + const line = part.closest(selector); + const n = line ? line.getAttribute(lineno) : ''; + if (!lines.has(n)) { + lines.set(n, []); + } + lines.get(n).push(part); + } + for (const list of lines.values()) { + if (list.length > 1) { + let [L, T, R, B] = [Infinity, Infinity, -Infinity, -Infinity]; + for (const part of list) { + part.setAttribute(ATTR.ENCLOSED, 'true'); + const { left, top, right, bottom } = part.getBoundingClientRect(); + if (top === bottom && left === right) continue; + if (left < L) L = left; + if (top < T) T = top; + if (bottom > B) B = bottom; + if (right > R) R = right; + } + const enclosure = this.createEnclosure( + L, + B, + R - L, + B - T, + node, + list[0] + ); + parts.push(enclosure); + } + } + return parts; } /** - * @override + * @param {string} type fg or bg + * @param {NamedColor} color The color to set + * @param {NamedColor} def The defaults to use for missing parts of the color */ - public get foreground(): string { - return this._foreground; + protected setColorCSS(type: string, color: NamedColor, def: NamedColor) { + const name = color.color ?? def.color; + const alpha = color.alpha ?? def.alpha; + LiveRegion.setColor(type, this.priority, name, alpha); } /** * @override */ - public get background(): string { - return this._background; + public setColor(background: NamedColor, foreground: NamedColor) { + this.setColorCSS('fg', foreground, DEFAULT_FOREGROUND); + this.setColorCSS('bg', background, DEFAULT_BACKGROUND); } /** @@ -261,19 +287,12 @@ abstract class AbstractHighlighter implements Highlighter { * @param {HTMLElement} node The root node. * @returns {HTMLElement[]} The list of maction sub nodes. */ - public getMactionNodes(node: HTMLElement): HTMLElement[] { - return Array.from( - node.getElementsByClassName(this.mactionName) - ) as HTMLElement[]; - } + public abstract getMactionNodes(node: HTMLElement): HTMLElement[]; /** * @override */ - public isMactionNode(node: Element): boolean { - const className = node.className || node.getAttribute('class'); - return className ? !!className.match(new RegExp(this.mactionName)) : false; - } + public abstract isMactionNode(node: Element): boolean; /** * Check if a node is already highlighted. @@ -301,159 +320,192 @@ abstract class AbstractHighlighter implements Highlighter { */ public unsetHighlighted(node: HTMLElement) { node.removeAttribute(this.ATTR); + node.removeAttribute(ATTR.ENCLOSED); } } class SvgHighlighter extends AbstractHighlighter { - /** - * @override - */ - constructor() { - super(); - this.mactionName = 'maction'; - } + protected static lineSelector = '[data-mjx-linebox]'; + protected static lineAttr = 'data-mjx-lineno'; /** * @override */ public highlightNode(node: HTMLElement) { - let info: Highlight; - if (this.isHighlighted(node)) { - info = { - node: node, - background: this.background, - foreground: this.foreground, - }; - return info; - } - if (node.tagName === 'svg' || node.tagName === 'MJX-CONTAINER') { - info = { - node: node, - background: node.style.backgroundColor, - foreground: node.style.color, - }; - node.style.backgroundColor = this.background; - node.style.color = this.foreground; - return info; + if ( + this.isHighlighted(node) || + node.tagName === 'svg' || + node.tagName === 'MJX-CONTAINER' || + node.hasAttribute(ATTR.BBOX) || + node.hasAttribute(ATTR.ENCLOSED) + ) { + return; } - // This is a hack for v4. - // TODO: v4 Change - // const rect = (document ?? DomUtil).createElementNS( - const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); - rect.setAttribute( - 'sre-highlighter-added', // Mark highlighting rect. - 'true' + const { x, y, width, height } = ( + node as any as SVGGraphicsElement + ).getBBox(); + const rect = this.createRect( + x, + y, + width, + height, + node.getAttribute('transform') ); - const padding = 40; - const bbox: SVGRect = (node as any as SVGGraphicsElement).getBBox(); - rect.setAttribute('x', (bbox.x - padding).toString()); - rect.setAttribute('y', (bbox.y - padding).toString()); - rect.setAttribute('width', (bbox.width + 2 * padding).toString()); - rect.setAttribute('height', (bbox.height + 2 * padding).toString()); - const transform = node.getAttribute('transform'); - if (transform) { - rect.setAttribute('transform', transform); - } - rect.setAttribute('fill', this.background); - node.setAttribute(this.ATTR, 'true'); + this.setHighlighted(rect); node.parentNode.insertBefore(rect, node); - info = { node: node, foreground: node.getAttribute('fill') }; - if (node.nodeName !== 'rect') { - // We currently do not change foreground of collapsed nodes. - node.setAttribute('fill', this.foreground); - } - return info; } /** * @override */ - public setHighlighted(node: HTMLElement) { - if (node.tagName === 'svg') { - super.setHighlighted(node); + public unhighlightNode(node: HTMLElement) { + if (node.hasAttribute(ATTR.BBOX)) { + node.remove(); + return; + } + const previous = node.previousSibling as HTMLElement; + if (previous?.hasAttribute(ATTR.ADDED)) { + previous.remove(); } } /** * @override */ - public unhighlightNode(info: Highlight) { - const previous = info.node.previousSibling as HTMLElement; - if (previous && previous.hasAttribute('sre-highlighter-added')) { - info.foreground - ? info.node.setAttribute('fill', info.foreground) - : info.node.removeAttribute('fill'); - info.node.parentNode.removeChild(previous); - return; + protected createEnclosure( + x: number, + y: number, + w: number, + h: number, + _node: HTMLElement, + part: HTMLElement + ): HTMLElement { + const [x1, y1] = this.screen2svg(x, y, part); + const [x2, y2] = this.screen2svg(x + w, y - h, part); + const rect = this.createRect( + x1, + y1, + x2 - x1, + y2 - y1, + part.getAttribute('transform') + ); + rect.setAttribute(ATTR.BBOX, 'true'); + part.parentNode.insertBefore(rect, part); + return rect; + } + + /** + * Convert screen coordinates in px to local SVG coordinates. + * + * @param {number} x The screen x coordinate + * @param {number} y The screen y coordinate + * @param {HTMLElement} part The element whose coordinate system is to be used + * @returns {number[]} The x,y coordinates in the coordinates of part + */ + protected screen2svg(x: number, y: number, part: HTMLElement): number[] { + const node = part as any as SVGGraphicsElement; + const P = DOMPoint.fromPoint({ x, y }).matrixTransform( + node.getScreenCTM().inverse() + ); + return [P.x, P.y]; + } + + /** + * Create a rectangle of the given size and position. + * + * @param {number} x The x position of the rectangle + * @param {number} y The y position of the rectangle + * @param {number} w The width of the rectangle + * @param {number} h The height of the rectangle + * @param {string} transform The transform to apply, if any + * @returns {HTMLElement} The generated rectangle element + */ + protected createRect( + x: number, + y: number, + w: number, + h: number, + transform: string + ): HTMLElement { + const padding = 40; + const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute(ATTR.ADDED, 'true'); // Mark highlighting rect. + rect.setAttribute('x', String(x - padding)); + rect.setAttribute('y', String(y - padding)); + rect.setAttribute('width', String(w + 2 * padding)); + rect.setAttribute('height', String(h + 2 * padding)); + if (transform) { + rect.setAttribute('transform', transform); } - info.node.style.backgroundColor = info.background; - info.node.style.color = info.foreground; + return rect as any as HTMLElement; } /** * @override */ public isMactionNode(node: HTMLElement) { - return node.getAttribute('data-mml-node') === this.mactionName; + return node.getAttribute('data-mml-node') === 'maction'; } /** * @override */ - public getMactionNodes(node: HTMLElement) { - return Array.from( - node.querySelectorAll(`[data-mml-node="${this.mactionName}"]`) - ) as HTMLElement[]; + public getMactionNodes(node: HTMLElement): HTMLElement[] { + return Array.from(node.querySelectorAll('[data-mml-node="maction"]')); } } class ChtmlHighlighter extends AbstractHighlighter { + protected static lineSelector = 'mjx-linebox'; + protected static lineAttr = 'lineno'; + /** * @override */ - constructor() { - super(); - this.mactionName = 'mjx-maction'; - } + public highlightNode(_node: HTMLElement) {} /** * @override */ - public highlightNode(node: HTMLElement) { - const info = { - node: node, - background: node.style.backgroundColor, - foreground: node.style.color, - }; - if (!this.isHighlighted(node)) { - node.style.backgroundColor = this.background; - node.style.color = this.foreground; + public unhighlightNode(node: HTMLElement) { + if (node.tagName.toLowerCase() === 'mjx-bbox') { + node.remove(); } - return info; } /** * @override */ - public unhighlightNode(info: Highlight) { - info.node.style.backgroundColor = info.background; - info.node.style.color = info.foreground; + protected createEnclosure( + x: number, + y: number, + w: number, + h: number, + node: HTMLElement + ): HTMLElement { + const base = node.getBoundingClientRect(); + const enclosure = document.createElement('mjx-bbox'); + enclosure.style.width = w + 'px'; + enclosure.style.height = h + 'px'; + enclosure.style.left = x - base.left + 'px'; + enclosure.style.top = y - h - base.top + 'px'; + enclosure.style.position = 'absolute'; + node.prepend(enclosure); + return enclosure; } /** * @override */ public isMactionNode(node: HTMLElement) { - return node.tagName?.toUpperCase() === this.mactionName.toUpperCase(); + return node.tagName?.toLowerCase() === 'mjx-maction'; } /** * @override */ - public getMactionNodes(node: HTMLElement) { - return Array.from( - node.getElementsByTagName(this.mactionName) - ) as HTMLElement[]; + public getMactionNodes(node: HTMLElement): HTMLElement[] { + return Array.from(node.querySelectorAll('mjx-maction')); } } @@ -461,17 +513,19 @@ class ChtmlHighlighter extends AbstractHighlighter { * Highlighter factory that returns the highlighter that goes with the current * Mathjax renderer. * + * @param {number} priority 1 = primary, 2 = secondary highlighter. * @param {NamedColor} back A background color specification. * @param {NamedColor} fore A foreground color specification. * @param {string} renderer The renderer name. * @returns {Highlighter} A new highlighter. */ export function getHighlighter( + priority: number, back: NamedColor, fore: NamedColor, renderer: string ): Highlighter { - const highlighter = new highlighterMapping[renderer](); + const highlighter = new highlighterMapping[renderer](priority); highlighter.setColor(back, fore); return highlighter; } @@ -479,7 +533,9 @@ export function getHighlighter( /** * Mapping renderer names to highlighter constructor. */ -const highlighterMapping: { [key: string]: new () => Highlighter } = { +const highlighterMapping: { + [key: string]: new (priority: number) => Highlighter; +} = { SVG: SvgHighlighter, CHTML: ChtmlHighlighter, generic: ChtmlHighlighter, diff --git a/ts/a11y/explorer/KeyExplorer.ts b/ts/a11y/explorer/KeyExplorer.ts index 943def94d..2fe7c8591 100644 --- a/ts/a11y/explorer/KeyExplorer.ts +++ b/ts/a11y/explorer/KeyExplorer.ts @@ -30,6 +30,13 @@ import { MmlNode } from '../../core/MmlTree/MmlNode.js'; import { honk, SemAttr } from '../speech/SpeechUtil.js'; import { GeneratorPool } from '../speech/GeneratorPool.js'; import { context } from '../../util/context.js'; +import { InfoDialog } from '../../ui/dialog/InfoDialog.js'; + +/**********************************************************************/ + +const isWindows = context.os === 'Windows'; + +const BRAILLE_PADDING = Array(40).fill('\u2800').join(''); /** * Interface for keyboard explorers. Adds the necessary keyboard events. @@ -70,7 +77,7 @@ export interface KeyExplorer extends Explorer { /** * Type of function that implements a key press action */ -type keyMapping = ( +export type keyMapping = ( explorer: SpeechExplorer, event: KeyboardEvent ) => boolean | void; @@ -106,154 +113,194 @@ export function hasModifiers( ); } +/**********************************************************************/ /**********************************************************************/ /** - * Creates a customized help dialog + * @class + * @augments {AbstractExplorer} * - * @param {string} title The title to use for the message - * @param {string} select Additional ways to select the typeset math - * @returns {string} The customized message + * @template T The type that is consumed by the Region of this explorer. */ -function helpMessage(title: string, select: string): string { - return ` -

Exploring expressions ${title}

+export class SpeechExplorer + extends AbstractExplorer + implements KeyExplorer +{ + /** + * Creates a customized help dialog + * + * @param {string} title The title to use for the message + * @param {string} select Additional ways to select the typeset math + * @param {string} braille Additional Braille information + * @returns {string} The customized message + */ + protected static helpMessage( + title: string, + select: string, + braille: string + ): string { + return ` +

Exploring expressions ${title}

-

The mathematics on this page is being rendered by MathJax, which -generates both the text spoken by screen readers, as well as the -visual layout for sighted users.

+

The mathematics on this page is being rendered by MathJax, which + generates both the text spoken by screen readers, as well as the + visual layout for sighted users.

-

Expressions typeset by MathJax can be explored interactively, and -are focusable. You can use the Tab key to move to a typeset -expression${select}. Initially, the expression will be read in full, -but you can use the following keys to explore the expression -further:

+

Expressions typeset by MathJax can be explored interactively, and + are focusable. You can use the Tab key to move to a typeset + expression${select}. Initially, the expression will be read in full, + but you can use the following keys to explore the expression + further:

+ +
    -
      +
    • Down Arrow moves one level deeper into the + expression to allow you to explore the current subexpression term by + term.
    • -
    • Down Arrow moves one level deeper into the expression to -allow you to explore the current subexpression term by term.
    • +
    • Up Arrow moves back up a level within the + expression.
    • -
    • Up Arrow moves back up a level within the expression.
    • +
    • Right Arrow moves to the next term in the + current subexpression.
    • -
    • Right Arrow moves to the next term in the current -subexpression.
    • +
    • Left Arrow moves to the next term in the + current subexpression.
    • -
    • Left Arrow moves to the next term in the current -subexpression.
    • +
    • Shift+Arrow moves to a + neighboring cell within a table.
    • -
    • Shift+Arrow moves to a neighboring cell within a table. +
    • 0-9+0-9 jumps to a cell + by its index in the table, where 0 = 10.
    • -
    • 0-9+0-9 jumps to a cell by its index in the table, where 0 = 10. +
    • Home takes you to the top of the + expression.
    • -
    • Home takes you to the top of the expression.
    • +
    • Enter or Return clicks a + link or activates an active subexpression.
    • -
    • Enter or Return clicks a link or activates an active -subexpression.
    • +
    • Space opens the MathJax contextual menu + where you can view or copy the source format of the expression, or + modify MathJax's settings.
    • -
    • Space opens the MathJax contextual menu where you can view -or copy the source format of the expression, or modify MathJax's -settings.
    • +
    • Escape exits the expression + explorer.
    • -
    • Escape exits the expression explorer.
    • +
    • x gives a summary of the current + subexpression.
    • -
    • x gives a summary of the current subexpression.
    • +
    • z gives the full text of a collapsed + expression.
    • -
    • z gives the full text of a collapsed expression.
    • +
    • d gives the current depth within the + expression.
    • -
    • d gives the current depth within the expression.
    • +
    • s starts or stops auto-voicing with + synchronized highlighting.
    • -
    • s starts or stops auto-voicing with synchronized highlighting.
    • +
    • v marks the current position in the + expression.
    • -
    • v marks the current position in the expression.
    • +
    • p cycles through the marked positions in + the expression.
    • -
    • p cycles through the marked positions in the expression.
    • +
    • u clears all marked positions and returns + to the starting position.
    • -
    • u clears all marked positions and returns to the starting position.
    • +
    • > cycles through the available speech + rule sets (MathSpeak, ClearSpeak).
    • -
    • > cycles through the available speech rule sets -(MathSpeak, ClearSpeak).
    • +
    • < cycles through the verbosity levels + for the current rule set.
    • -
    • < cycles through the verbosity levels for the current -rule set.
    • +
    • b toggles whether Braille notation is combined + with speech text for tactile Braille devices, as discussed + below. -
    • h produces this help listing.
    • -
    +
  • h produces this help listing.
  • +
-

The MathJax contextual menu allows you to enable or disable speech -or Braille generation for mathematical expressions, the language to -use for the spoken mathematics, and other features of MathJax. In -particular, the Explorer submenu allows you to specify how the -mathematics should be identified in the page (e.g., by saying "math" -when the expression is spoken), and whether or not to include a -message about the letter "h" bringing up this dialog box.

+

The MathJax contextual menu allows you to enable or disable speech + or Braille generation for mathematical expressions, the language to + use for the spoken mathematics, and other features of MathJax. In + particular, the Explorer submenu allows you to specify how the + mathematics should be identified in the page (e.g., by saying "math" + when the expression is spoken), and whether or not to include a + message about the letter "h" bringing up this dialog box. Turning off + speech and Braille will disable the expression explorer, its + highlighting, and its help icon.

-

The contextual menu also provides options for viewing or copying a -MathML version of the expression or its original source format, -creating an SVG version of the expression, and viewing various other -information.

+

Support for tactile Braille devices varies across screen readers, + browsers, and operative systems. If you are using a Braille output + device, you may need to select the "Combine with Speech" option in the + contextual menu's Braille submenu in order to obtain Nemeth or Euro + Braille output rather than the speech text on your Braille + device. ${braille}

-

For more help, see the MathJax accessibility documentation.

-`; -} +

The contextual menu also provides options for viewing or copying a + MathML version of the expression or its original source format, + creating an SVG version of the expression, and viewing various other + information.

-/** - * Help for the different OS versions - */ -const helpData: Map = new Map([ - [ - 'MacOS', +

Finally, selecting the "Insert Hidden MathML" item from the options + submenu will turn of MathJax's speech and Braille generation and + instead use visually hidden MathML that some screen readers can voice, + though support for this is not universal across all screen readers and + operating systems. Selecting speech or Braille generation in their + submenus will remove the hidden MathML again.

+ +

For more help, see the MathJax accessibility documentation.

+ `; + } + + /** + * Help for the different OS versions + */ + protected static helpData: Map = new Map([ [ - 'on MacOS and iOS using VoiceOver', - ', or the VoiceOver arrow keys to select an expression', + 'MacOS', + [ + 'on MacOS and iOS using VoiceOver', + ', or the VoiceOver arrow keys to select an expression', + '', + ], ], - ], - [ - 'Windows', [ - 'in Windows using NVDA or JAWS', - `. The screen reader should enter focus or forms mode automatically -when the expression gets the browser focus, but if not, you can toggle -focus mode using NVDA+space in NVDA; for JAWS, Enter should start -forms mode while Numpad Plus leaves it. Also note that you can use -the NVDA or JAWS key plus the arrow keys to explore the expression -even in browse mode, and you can use NVDA+shift+arrow keys to -navigate out of an expression that has the focus in NVDA`, + 'Windows', + [ + 'in Windows using NVDA or JAWS', + `. The screen reader should enter focus or forms mode automatically + when the expression gets the browser focus, but if not, you can toggle + focus mode using NVDA+space in NVDA; for JAWS, Enter should start + forms mode while Numpad Plus leaves it. Also note that you can use + the NVDA or JAWS key plus the arrow keys to explore the expression + even in browse mode, and you can use NVDA+shift+arrow keys to + navigate out of an expression that has the focus in NVDA`, + `NVDA users need to select this option, while JAWS users should be able + to get Braille output without changing this setting.`, + ], ], - ], - [ - 'Unix', [ - 'in Unix using Orca', - `, and Orca should enter focus mode automatically. If not, use the -Orca+a key to toggle focus mode on or off. Also note that you can use -Orca+arrow keys to explore expressions even in browse mode`, + 'Unix', + [ + 'in Unix using Orca', + `, and Orca should enter focus mode automatically. If not, use the + Orca+a key to toggle focus mode on or off. Also note that you can use + Orca+arrow keys to explore expressions even in browse mode`, + '', + ], ], - ], - ['unknown', ['with a Screen Reader.', '']], -]); - -/**********************************************************************/ -/**********************************************************************/ + ['unknown', ['with a Screen Reader.', '', '']], + ]); -/** - * @class - * @augments {AbstractExplorer} - * - * @template T The type that is consumed by the Region of this explorer. - */ -export class SpeechExplorer - extends AbstractExplorer - implements KeyExplorer -{ /* * The explorer key mapping */ protected static keyMap: Map = new Map([ - ['Tab', [() => true]], + ['Tab', [(explorer, event) => explorer.tabKey(event)]], ['Escape', [(explorer) => explorer.escapeKey()]], ['Enter', [(explorer, event) => explorer.enterKey(event)]], ['Home', [(explorer) => explorer.homeKey()]], @@ -281,6 +328,7 @@ export class SpeechExplorer ['p', [(explorer) => explorer.prevMark(), false]], ['u', [(explorer) => explorer.clearMarks(), false]], ['s', [(explorer) => explorer.autoVoice(), false]], + ['b', [(explorer) => explorer.toggleBraille(), false]], ...[...'0123456789'].map((n) => [ n, [(explorer: SpeechExplorer) => explorer.numberKey(parseInt(n)), false], @@ -325,7 +373,18 @@ export class SpeechExplorer * @returns {string} The string to use for no description */ protected get none(): string { - return this.item.none; + return this.document.options.a11y.brailleSpeech + ? this.item.brailleNone + : this.item.none; + } + + /** + * Shorthand for the item's "brailleNone" indicator + * + * @returns {string} The string to use for no description + */ + protected get brailleNone(): string { + return this.item.brailleNone; } /** @@ -404,6 +463,16 @@ export class SpeechExplorer */ protected cellTypes: string[] = ['cell', 'line']; + /** + * The anchors in this expression + */ + protected anchors: HTMLElement[]; + + /** + * Whether the expression was focused by a back tab + */ + protected backTab: boolean = false; + /********************************************************************/ /* * The event handlers @@ -421,6 +490,11 @@ export class SpeechExplorer ['dblclick', this.DblClick.bind(this)], ]); + /** + * Semantic id to subtree map. + */ + private subtrees: Map> = null; + /** * @override */ @@ -434,6 +508,7 @@ export class SpeechExplorer } if (!this.clicked) { this.Start(); + this.backTab = _event.target === this.img; } this.clicked = null; } @@ -443,8 +518,10 @@ export class SpeechExplorer */ public FocusOut(_event: FocusEvent) { if (this.current && !this.focusSpeech) { - this.setCurrent(null); - this.Stop(); + if (!this.document.options.keepRegions) { + this.setCurrent(null); + this.Stop(); + } if (!document.hasFocus()) { this.focusTop(); } @@ -605,8 +682,13 @@ export class SpeechExplorer /** * Open the help dialog, and refocus when it closes. + * + * @returns {boolean | void} True cancels the event */ - protected hKey() { + protected hKey(): boolean | void { + if (!this.document.options.enableExplorerHelp) { + return true; + } this.refocus = this.current; this.help(); } @@ -619,6 +701,42 @@ export class SpeechExplorer protected escapeKey(): boolean { this.Stop(); this.focusTop(); + this.setCurrent(null); + return true; + } + + /** + * Tab to the next internal link, if any, and stop the event from + * propagating, or if no more links, let it propagate so that the + * browser moves to the next focusable item. + * + * @param {KeyboardEvent} event The event for the enter key + * @returns {void | boolean} False means play the honk sound + */ + protected tabKey(event: KeyboardEvent): void | boolean { + if (this.anchors.length === 0 || !this.current) return true; + if (this.backTab) { + if (!event.shiftKey) return true; + const link = this.linkFor(this.anchors[this.anchors.length - 1]); + if (this.anchors.length === 1 && link === this.current) { + return true; + } + this.setCurrent(link); + return; + } + const [anchors, position, current] = event.shiftKey + ? [ + this.anchors.slice(0).reverse(), + Node.DOCUMENT_POSITION_PRECEDING, + this.isLink() ? this.getAnchor() : this.current, + ] + : [this.anchors, Node.DOCUMENT_POSITION_FOLLOWING, this.current]; + for (const anchor of anchors) { + if (current.compareDocumentPosition(anchor) & position) { + this.setCurrent(this.linkFor(anchor)); + return; + } + } return true; } @@ -792,6 +910,15 @@ export class SpeechExplorer this.Update(); } + protected toggleBraille() { + const value = !this.document.options.a11y.brailleCombine; + if (this.document.menu) { + this.document.menu.menu.pool.lookup('brailleCombine').setValue(value); + } else { + this.document.options.a11y.brailleCombine = value; + } + } + /** * Get index for cell to jump to. * @@ -829,7 +956,7 @@ export class SpeechExplorer const parts = [ [ this.node.getAttribute('data-semantic-level') ?? 'Level', - this.current.getAttribute('aria-level') ?? '0', + this.current.getAttribute('data-semantic-level-number') ?? '0', ] .join(' ') .trim(), @@ -933,40 +1060,36 @@ export class SpeechExplorer * Displays the help dialog. */ protected help() { - const adaptor = this.document.adaptor; - const helpBackground = adaptor.node('mjx-help-background'); - const close = (event: Event) => { - helpBackground.remove(); - this.node.focus(); - this.stopEvent(event); - }; - helpBackground.addEventListener('click', close); - const helpSizer = adaptor.node('mjx-help-sizer', {}, [ - adaptor.node( - 'mjx-help-dialog', - { tabindex: 0, role: 'dialog', 'aria-labeledby': 'mjx-help-label' }, - [ - adaptor.node('h1', { id: 'mjx-help-label' }, [ - adaptor.text('MathJax Expression Explorer Help'), - ]), - adaptor.node('div'), - adaptor.node('input', { type: 'button', value: 'Close' }), - ] - ), - ]); - helpBackground.append(helpSizer); - const help = helpSizer.firstChild as HTMLElement; - help.addEventListener('click', (event) => this.stopEvent(event)); - help.lastChild.addEventListener('click', close); - help.addEventListener('keydown', (event: KeyboardEvent) => { - if (event.code === 'Escape') { - close(event); - } + if (!this.document.options.enableExplorerHelp) { + return; + } + const CLASS = this.constructor as typeof SpeechExplorer; + const [title, select, braille] = CLASS.helpData.get(context.os); + InfoDialog.post({ + title: 'MathJax Expression Explorer Help', + message: CLASS.helpMessage(title, select, braille), + node: this.node, + adaptor: this.document.adaptor, + styles: { + '.mjx-dialog': { + 'max-height': 'calc(min(35em, 90%))', + }, + 'mjx-dialog mjx-title': { + 'font-size': '133%', + margin: '.5em 1.75em', + }, + 'mjx-dialog h2': { + 'font-size': '20px', + margin: '.5em 0', + }, + 'mjx-dialog ul': { + 'list-style-type': 'none', + }, + 'mjx-dialog li': { + 'margin-bottom': '.5em', + }, + }, }); - const [title, select] = helpData.get(context.os); - (help.childNodes[1] as HTMLElement).innerHTML = helpMessage(title, select); - document.body.append(helpBackground); - help.focus(); } /********************************************************************/ @@ -981,6 +1104,7 @@ export class SpeechExplorer * @param {boolean} addDescription True if the speech node should get a description */ protected setCurrent(node: HTMLElement, addDescription: boolean = false) { + this.backTab = false; this.speechType = ''; if (!document.hasFocus()) { this.refocus = this.current; @@ -996,10 +1120,12 @@ export class SpeechExplorer // (i.e., we are focusing out) // if (this.current) { - for (const part of this.getSplitNodes(this.current)) { + this.pool.unhighlight(); + for (const part of Array.from( + this.node.querySelectorAll('.mjx-selected') + )) { part.classList.remove('mjx-selected'); } - this.pool.unhighlight(); if (this.document.options.a11y.tabSelects === 'last') { this.refocus = this.current; } @@ -1016,12 +1142,17 @@ export class SpeechExplorer this.current = node; this.currentMark = -1; if (this.current) { - const parts = this.getSplitNodes(this.current); + const parts = [...this.getSplitNodes(this.current)]; + this.highlighter.encloseNodes(parts, this.node); for (const part of parts) { - part.classList.add('mjx-selected'); + if (!part.getAttribute('data-sre-enclosed')) { + part.classList.add('mjx-selected'); + } } this.pool.highlight(parts); this.addSpeech(node, addDescription); + this.node.setAttribute('tabindex', '-1'); + this.Update(); } // // Done making changes @@ -1029,6 +1160,8 @@ export class SpeechExplorer this.node.removeAttribute('aria-busy'); } + private cacheParts: Map = new Map(); + /** * Get all nodes with the same semantic id (multiple nodes if there are line breaks). * @@ -1040,7 +1173,39 @@ export class SpeechExplorer if (!id) { return [node]; } - return Array.from(this.node.querySelectorAll(`[data-semantic-id="${id}"]`)); + // Here we need to cache the subtrees. + if (this.cacheParts.has(id)) { + return this.cacheParts.get(id); + } + const parts = Array.from( + this.node.querySelectorAll(`[data-semantic-id="${id}"]`) + ) as HTMLElement[]; + const subtree = this.subtree(id, parts); + this.cacheParts.set(id, [...parts, ...subtree]); + return this.cacheParts.get(id); + } + + /** + * Retrieve the elements in the semantic subtree that are not in the DOM subtree. + * + * @param {string} id The semantic id of the root node. + * @param {HTMLElement[]} nodes The list of nodes corresponding to that id + * (could be multiple for linebroken ones). + * @returns {HTMLElement[]} The list of nodes external to the DOM trees rooted + * by any of the input nodes. + */ + private subtree(id: string, nodes: HTMLElement[]): HTMLElement[] { + const sub = this.subtrees.get(id); + const children: Set = new Set(); + for (const node of nodes) { + ( + Array.from(node.querySelectorAll(`[data-semantic-id]`)) as HTMLElement[] + ).forEach((x) => children.add(this.nodeId(x))); + } + const rest = setdifference(sub, children); + return [...rest] + .map((child) => this.getNode(child)) + .filter((node) => node !== null); } /** @@ -1051,18 +1216,31 @@ export class SpeechExplorer * @param {boolean} describe True if the description should be added */ protected addSpeech(node: HTMLElement, describe: boolean) { - this.img?.remove(); - let speech = [ + if ( + !this.document.options.enableSpeech && + !this.document.options.enableBraille + ) { + return; + } + if (this.anchors.length) { + setTimeout(() => this.img?.remove(), 10); + } else { + this.img?.remove(); + } + let speech = this.addComma([ node.getAttribute(SemAttr.PREFIX), node.getAttribute(SemAttr.SPEECH), node.getAttribute(SemAttr.POSTFIX), - ] + ]) .join(' ') .trim(); if (describe) { let description = this.description === this.none ? '' : ', ' + this.description; - if (this.document.options.a11y.help) { + if ( + this.document.options.a11y.help && + this.document.options.enableExplorerHelp + ) { description += ', press h for help'; } speech += description; @@ -1072,7 +1250,20 @@ export class SpeechExplorer node.getAttribute(SemAttr.BRAILLE), this.SsmlAttributes(node, SemAttr.SPEECH_SSML) ); - this.node.setAttribute('tabindex', '-1'); + } + + /** + * In an array [prefix, center, postfix], the center gets a comma if + * there is a postfix. + * + * @param {string[]} words The words to check + * @returns {string[]} The modified array of words + */ + protected addComma(words: string[]): string[] { + if (words[2] && (words[1] || words[0])) { + words[1] += ','; + } + return words; } /** @@ -1081,7 +1272,7 @@ export class SpeechExplorer */ protected removeSpeech() { if (this.speech) { - this.speech.remove(); + this.unspeak(this.speech); this.speech = null; if (this.img) { this.node.append(this.img); @@ -1108,28 +1299,56 @@ export class SpeechExplorer description: string = this.none ) { const oldspeech = this.speech; - this.speech = document.createElement('mjx-speech'); - this.speech.setAttribute('role', this.role); - this.speech.setAttribute('aria-label', speech); - this.speech.setAttribute(SemAttr.SPEECH, speech); + const speechNode = (this.speech = document.createElement('mjx-speech')); + speechNode.setAttribute('role', this.role); + speechNode.setAttribute('aria-label', speech || this.none); + speechNode.setAttribute('aria-roledescription', description || this.none); + speechNode.setAttribute(SemAttr.SPEECH, speech); if (ssml) { - this.speech.setAttribute(SemAttr.PREFIX_SSML, ssml[0] || ''); - this.speech.setAttribute(SemAttr.SPEECH_SSML, ssml[1] || ''); - this.speech.setAttribute(SemAttr.POSTFIX_SSML, ssml[2] || ''); + speechNode.setAttribute(SemAttr.PREFIX_SSML, ssml[0] || ''); + speechNode.setAttribute(SemAttr.SPEECH_SSML, ssml[1] || ''); + speechNode.setAttribute(SemAttr.POSTFIX_SSML, ssml[2] || ''); } if (braille) { - this.speech.setAttribute('aria-braillelabel', braille); + if (this.document.options.a11y.brailleSpeech) { + speechNode.setAttribute('aria-label', braille); + speechNode.setAttribute('aria-roledescription', this.brailleNone); + } + speechNode.setAttribute('aria-braillelabel', braille); + speechNode.setAttribute('aria-brailleroledescription', this.brailleNone); + if (this.document.options.a11y.brailleCombine) { + speechNode.setAttribute( + 'aria-label', + braille + BRAILLE_PADDING + speech + ); + } + } + speechNode.setAttribute('tabindex', '0'); + if (isWindows) { + const container = document.createElement('mjx-speech-container'); + container.setAttribute('role', 'application'); + container.setAttribute('aria-roledescription', this.none); + container.setAttribute('aria-brailleroledescription', this.brailleNone); + container.append(speechNode); + this.node.append(container); + speechNode.setAttribute('role', 'img'); + } else { + this.node.append(speechNode); } - this.speech.setAttribute('aria-roledescription', description); - this.speech.setAttribute('tabindex', '0'); - this.node.append(this.speech); this.focusSpeech = true; - this.speech.focus(); + speechNode.focus(); this.focusSpeech = false; this.Update(); if (oldspeech) { - setTimeout(() => oldspeech.remove(), 100); + setTimeout(() => this.unspeak(oldspeech), 100); + } + } + + public unspeak(node: HTMLElement) { + if (isWindows) { + node = node.parentElement; } + node.remove(); } /** @@ -1154,7 +1373,20 @@ export class SpeechExplorer role: 'img', 'aria-roledescription': item.none, }); + const braille = container.getAttribute(SemAttr.BRAILLE); + if (braille) { + if (this.document.options.a11y.brailleSpeech) { + this.img.setAttribute('aria-label', braille); + this.img.setAttribute('aria-roledescription', this.brailleNone); + } + this.img.setAttribute('aria-braillelabel', braille); + this.img.setAttribute('aria-brailleroledescription', this.brailleNone); + if (this.document.options.a11y.brailleCombine) { + this.img.setAttribute('aria-label', braille + BRAILLE_PADDING + speech); + } + } container.appendChild(this.img); + this.adjustAnchors(); } /** @@ -1167,6 +1399,34 @@ export class SpeechExplorer for (const child of Array.from(container.childNodes) as HTMLElement[]) { child.removeAttribute('aria-hidden'); } + this.restoreAnchors(); + } + + /** + * Move all the href attributes to data-mjx-href attributes + * (so they won't be focusable links, as they are aria-hidden). + */ + protected adjustAnchors() { + this.anchors = Array.from(this.node.querySelectorAll('a[href]')); + for (const anchor of this.anchors) { + const href = anchor.getAttribute('href'); + anchor.setAttribute('data-mjx-href', href); + anchor.removeAttribute('href'); + } + if (this.anchors.length) { + this.img.setAttribute('tabindex', '0'); + } + } + + /** + * Move the links back to their href attributes. + */ + protected restoreAnchors() { + for (const anchor of this.anchors) { + anchor.setAttribute('href', anchor.getAttribute('data-mjx-href')); + anchor.removeAttribute('data-mjx-href'); + } + this.anchors = []; } /** @@ -1417,6 +1677,7 @@ export class SpeechExplorer if ( child !== this.speech && child !== this.img && + child.tagName && child.tagName.toLowerCase() !== 'rect' ) { const { left, right, top, bottom } = child.getBoundingClientRect(); @@ -1430,6 +1691,42 @@ export class SpeechExplorer return found; } + /** + * @param {HTMLElement} node The node to test for having an href + * @returns {boolean} True if the node has a link, false otherwise + */ + protected isLink(node: HTMLElement = this.current): boolean { + return !!node?.getAttribute('data-semantic-attributes')?.includes('href:'); + } + + /** + * @param {HTMLElement} node The link node whose node is desired + * @returns {HTMLElement} The node for the given link node + */ + protected getAnchor(node: HTMLElement = this.current): HTMLElement { + const anchor = node.closest('a'); + return anchor && this.node.contains(anchor) ? anchor : null; + } + + /** + * @param {HTMLElement} anchor The node whose speech node is desired + * @returns {HTMLElement} The node for which the is handling the href + */ + protected linkFor(anchor: HTMLElement): HTMLElement { + return anchor?.querySelector('[data-semantic-attributes*="href:"]'); + } + + /** + * @param {HTMLElement} node A node inside a link whose top-level link node is required + * @returns {HTMLElement} The parent node with an href that contains the given node + */ + protected parentLink(node: HTMLElement): HTMLElement { + const link = node?.closest( + '[data-semantic-attributes*="href:"]' + ) as HTMLElement; + return link && this.node.contains(link) ? link : null; + } + /** * Focus the container node without activating it (e.g., when Escape is pressed) */ @@ -1517,6 +1814,10 @@ export class SpeechExplorer * @override */ public async Start() { + if (!this.subtrees) { + this.subtrees = new Map(); + this.getSubtrees(); + } // // If we aren't attached or already active, return // @@ -1538,7 +1839,9 @@ export class SpeechExplorer // and add the info icon. // this.node.classList.add('mjx-explorer-active'); - this.node.append(this.document.infoIcon); + if (this.document.options.enableExplorerHelp) { + this.node.append(this.document.infoIcon); + } // // Get the node to make current, and determine if we need to add a // speech node (or just use the top-level node), then set the @@ -1553,13 +1856,13 @@ export class SpeechExplorer const options = this.document.options; const a11y = options.a11y; if (a11y.subtitles && a11y.speech && options.enableSpeech) { - this.region.Show(this.node, this.highlighter); + this.region.Show(this.node); } if (a11y.viewBraille && a11y.braille && options.enableBraille) { - this.brailleRegion.Show(this.node, this.highlighter); + this.brailleRegion.Show(this.node); } if (a11y.keyMagnifier) { - this.magnifyRegion.Show(this.current, this.highlighter); + this.magnifyRegion.Show(this.current); } this.Update(); } @@ -1569,12 +1872,10 @@ export class SpeechExplorer */ public Stop() { if (this.active) { - const description = this.description; - if (this.node.getAttribute('aria-roledescription') !== description) { - this.node.setAttribute('aria-roledescription', description); - } this.node.classList.remove('mjx-explorer-active'); - this.document.infoIcon.remove(); + if (this.document.options.enableExplorerHelp) { + this.document.infoIcon.remove(); + } this.pool.unhighlight(); this.magnifyRegion.Hide(); this.region.Hide(); @@ -1589,11 +1890,16 @@ export class SpeechExplorer public Update() { if (!this.active) return; this.region.node = this.node; - this.generators.updateRegions( - this.speech || this.node, - this.region, - this.brailleRegion - ); + if ( + this.document.options.enableSpeech || + this.document.options.enableBraille + ) { + this.generators.updateRegions( + this.speech || this.node, + this.region, + this.brailleRegion + ); + } this.magnifyRegion.Update(this.current); } @@ -1679,18 +1985,12 @@ export class SpeechExplorer * @returns {boolean} True if link was successfully triggered. */ protected triggerLink(node: HTMLElement): boolean { - const focus = node - ?.getAttribute('data-semantic-postfix') - ?.match(/(^| )link($| )/); - if (focus) { - while (node && node !== this.node) { - if (node instanceof HTMLAnchorElement) { - node.dispatchEvent(new MouseEvent('click')); - setTimeout(() => this.FocusOut(null), 50); - return true; - } - node = node.parentNode as HTMLElement; - } + if (this.isLink(node)) { + const anchor = this.getAnchor(node); + anchor.classList.add('mjx-visited'); + setTimeout(() => this.FocusOut(null), 50); + window.location.href = anchor.getAttribute('data-mjx-href'); + return true; } return false; } @@ -1701,12 +2001,9 @@ export class SpeechExplorer * @returns {boolean} True if link was successfully triggered. */ protected triggerLinkMouse(): boolean { - let node = this.refocus; - while (node && node !== this.node) { - if (this.triggerLink(node)) { - return true; - } - node = node.parentNode as HTMLElement; + const link = this.parentLink(this.refocus); + if (this.triggerLink(link)) { + return true; } return false; } @@ -1730,4 +2027,97 @@ export class SpeechExplorer } return focus.join(' '); } + + /** + * Populates the subtrees map from the data-semantic-structure attribute. + */ + private getSubtrees() { + const node = this.node.querySelector('[data-semantic-structure]'); + if (!node) return; + const sexp = node.getAttribute('data-semantic-structure'); + const tokens = tokenize(sexp); + const tree = parse(tokens); + buildMap(tree, this.subtrees); + } +} + +/**********************************************************************/ +/* + * Some Aux functions for parsing the semantic structure sexpression + */ +type SexpTree = string | SexpTree[]; + +/** + * Helper to tokenize input + * + * @param {string} str The semantic structure. + * @returns {string[]} The tokenized list. + */ +function tokenize(str: string): string[] { + return str.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ').trim().split(/\s+/); +} + +/** + * Recursive parser to convert tokens into a tree + * + * @param {string} tokens The tokens from the semantic structure. + * @returns {SexpTree} Array list for the semantic structure sexpression. + */ +function parse(tokens: string[]): SexpTree { + const stack: SexpTree[][] = [[]]; + for (const token of tokens) { + if (token === '(') { + const newNode: SexpTree = []; + stack[stack.length - 1].push(newNode); + stack.push(newNode); + } else if (token === ')') { + stack.pop(); + } else { + stack[stack.length - 1].push(token); + } + } + return stack[0][0]; +} + +/** + * Flattens the tree and builds the map. + * + * @param {SexpTree} tree The sexpression tree. + * @param {Map>} map The map to populate. + * @returns {Set} The descendant map. + */ +function buildMap(tree: SexpTree, map: Map>): Set { + if (typeof tree === 'string') { + if (!map.has(tree)) map.set(tree, new Set()); + return new Set(); + } + const [root, ...children] = tree; + const rootId = root as string; + const descendants: Set = new Set(); + for (const child of children) { + const childRoot = typeof child === 'string' ? child : child[0]; + const childDescendants = buildMap(child, map); + descendants.add(childRoot as string); + childDescendants.forEach((d: string) => descendants.add(d)); + } + map.set(rootId, descendants); + return descendants; +} + +// Can be replaced with ES2024 implementation of Set.prototyp.difference +/** + * Set difference between two sets A and B: A\B. + * + * @param {Set} a Initial set. + * @param {Set} b Set to remove from A. + * @returns {Set} The difference A\B. + */ +function setdifference(a: Set, b: Set): Set { + if (!a) { + return new Set(); + } + if (!b) { + return a; + } + return new Set([...a].filter((x) => !b.has(x))); } diff --git a/ts/a11y/explorer/MouseExplorer.ts b/ts/a11y/explorer/MouseExplorer.ts index 056b4b7a5..59c8f361e 100644 --- a/ts/a11y/explorer/MouseExplorer.ts +++ b/ts/a11y/explorer/MouseExplorer.ts @@ -136,7 +136,7 @@ export abstract class Hoverer extends AbstractMouseExplorer { this.highlighter.unhighlight(); this.highlighter.highlight([node]); this.region.Update(kind); - this.region.Show(node, this.highlighter); + this.region.Show(node); } /** diff --git a/ts/a11y/explorer/Region.ts b/ts/a11y/explorer/Region.ts index 109a42d2b..48c4261dc 100644 --- a/ts/a11y/explorer/Region.ts +++ b/ts/a11y/explorer/Region.ts @@ -23,7 +23,7 @@ import { MathDocument } from '../../core/MathDocument.js'; import { StyleJsonSheet } from '../../util/StyleJson.js'; -import { Highlighter, getHighlighter } from './Highlighter.js'; +import { Highlighter } from './Highlighter.js'; import { SsmlElement, buildSpeech } from '../speech/SpeechUtil.js'; export type A11yDocument = MathDocument; @@ -43,9 +43,8 @@ export interface Region { * Shows the live region in the document. * * @param {HTMLElement} node - * @param {Highlighter} highlighter */ - Show(node: HTMLElement, highlighter: Highlighter): void; + Show(node: HTMLElement): void; /** * Takes the element out of the document flow. @@ -73,13 +72,6 @@ export abstract class AbstractRegion implements Region { */ protected static className: string; - /** - * True if the style has already been added to the document. - * - * @type {boolean} - */ - protected static styleAdded: boolean = false; - /** * The CSS style that needs to be added for this type of region. * @@ -117,20 +109,34 @@ export abstract class AbstractRegion implements Region { this.AddStyles(); } + /** + * @returns {string} The stylesheet ID + */ + public static get sheetId(): string { + return 'MJX-' + this.name + '-styles'; + } + + /** + * @returns {HTMLStyleElement} The stylesheet for this region + */ + public static get styleSheet(): HTMLStyleElement { + return document.head.querySelector('#' + this.sheetId) as HTMLStyleElement; + } + /** * @override */ public AddStyles() { - if (this.CLASS.styleAdded) { + const id = this.CLASS.sheetId; + if ( + !this.CLASS.style || + this.document.adaptor.head().querySelector('#' + id) + ) { return; } - // TODO: should that be added to document.documentStyleSheet()? - const node = this.document.adaptor.node('style'); + const node = this.document.adaptor.node('style', { id }); node.innerHTML = this.CLASS.style.cssText; - this.document.adaptor - .head(this.document.adaptor.document) - .appendChild(node); - this.CLASS.styleAdded = true; + this.document.adaptor.head().appendChild(node); } /** @@ -151,10 +157,9 @@ export abstract class AbstractRegion implements Region { /** * @override */ - public Show(node: HTMLElement, highlighter: Highlighter) { + public Show(node: HTMLElement) { this.AddElement(); this.position(node); - this.highlight(highlighter); this.div.classList.add(this.CLASS.className + '_Show'); } @@ -165,19 +170,12 @@ export abstract class AbstractRegion implements Region { */ protected abstract position(node: HTMLElement): void; - /** - * Highlights the region. - * - * @param {Highlighter} highlighter The Sre highlighter. - */ - protected abstract highlight(highlighter: Highlighter): void; - /** * @override */ public Hide() { if (!this.div) return; - this.div.parentNode.removeChild(this.div); + this.div.remove(); this.div = null; this.inner = null; } @@ -260,11 +258,6 @@ export class DummyRegion extends AbstractRegion { * @override */ public position() {} - - /** - * @override - */ - public highlight(_highlighter: Highlighter) {} } export class StringRegion extends AbstractRegion { @@ -297,15 +290,6 @@ export class StringRegion extends AbstractRegion { protected position(node: HTMLElement) { this.stackRegions(node); } - - /** - * @override - */ - protected highlight(highlighter: Highlighter) { - if (!this.div) return; - this.inner.style.backgroundColor = highlighter.background; - this.inner.style.color = highlighter.foreground; - } } export class ToolTip extends StringRegion { @@ -318,7 +302,7 @@ export class ToolTip extends StringRegion { * @override */ protected static style: StyleJsonSheet = new StyleJsonSheet({ - ['.' + ToolTip.className]: { + [`.${ToolTip.className}`]: { width: 'auto', height: 'auto', opacity: 1, @@ -331,10 +315,17 @@ export class ToolTip extends StringRegion { 'background-color': 'white', 'z-index': 202, }, - ['.' + ToolTip.className + ' > div']: { + [`.${ToolTip.className} > div`]: { 'border-radius': 'inherit', padding: '0 2px', }, + '@media (prefers-color-scheme: dark)': { + ['.' + ToolTip.className]: { + 'background-color': '#222025', + 'box-shadow': '0px 5px 20px #000', + border: '1px solid #7C7C7C', + }, + }, }); } @@ -344,11 +335,61 @@ export class LiveRegion extends StringRegion { */ protected static className = 'MJX_LiveRegion'; + public static priority = { + primary: 1, + secondary: 2, + }; + /** * @override */ protected static style: StyleJsonSheet = new StyleJsonSheet({ - ['.' + LiveRegion.className]: { + ':root': { + '--mjx-fg-red': '255, 0, 0', + '--mjx-fg-green': '0, 255, 0', + '--mjx-fg-blue': '0, 0, 255', + '--mjx-fg-yellow': '255, 255, 0', + '--mjx-fg-cyan': '0, 255, 255', + '--mjx-fg-magenta': '255, 0, 255', + '--mjx-fg-white': '255, 255, 255', + '--mjx-fg-black': '0, 0, 0', + '--mjx-bg-red': '255, 0, 0', + '--mjx-bg-green': '0, 255, 0', + '--mjx-bg-blue': '0, 0, 255', + '--mjx-bg-yellow': '255, 255, 0', + '--mjx-bg-cyan': '0, 255, 255', + '--mjx-bg-magenta': '255, 0, 255', + '--mjx-bg-white': '255, 255, 255', + '--mjx-bg-black': '0, 0, 0', + '--mjx-live-bg-color': 'white', + '--mjx-live-shadow-color': '#888', + '--mjx-live-border-color': '#CCCCCC', + '--mjx-bg1-color': 'rgba(var(--mjx-bg-blue), var(--mjx-bg-alpha))', + '--mjx-fg1-color': 'rgba(var(--mjx-fg-black), 1)', + '--mjx-bg2-color': 'rgba(var(--mjx-bg-red), 1)', + '--mjx-fg2-color': 'rgba(var(--mjx-fg-black), 1)', + '--mjx-bg1-alpha': 0.2, + '--mjx-fg1-alpha': 1, + '--mjx-bg2-alpha': 1, + '--mjx-fg2-alpha': 1, + }, + '@media (prefers-color-scheme: dark)': { + ':root': { + '--mjx-bg-blue': '132, 132, 255', + '--mjx-bg-white': '0, 0, 0', + '--mjx-bg-black': '255, 255, 255', + '--mjx-fg-white': '0, 0, 0', + '--mjx-fg-black': '255, 255, 255', + '--mjx-live-bg-color': '#222025', + '--mjx-live-shadow-color': 'black', + '--mjx-live-border-color': '#7C7C7C', + '--mjx-bg1-alpha': 0.3, + '--mjx-fg1-alpha': 1, + '--mjx-bg2-alpha': 1, + '--mjx-fg2-alpha': 1, + }, + }, + [`.${LiveRegion.className}`]: { position: 'absolute', top: 0, display: 'none', @@ -360,20 +401,87 @@ export class LiveRegion extends StringRegion { left: 0, right: 0, margin: '0 auto', - 'background-color': 'white', - 'box-shadow': '0px 5px 20px #888', - border: '2px solid #CCCCCC', + 'background-color': 'var(--mjx-live-bg-color)', + 'box-shadow': '0px 5px 20px var(--mjx-live-shadow-color)', + border: '2px solid var(--mjx-live-border-color)', }, - ['.' + LiveRegion.className + '_Show']: { + [`.${LiveRegion.className}_Show`]: { display: 'block', }, + [`.${LiveRegion.className} > div`]: { + color: 'var(--mjx-fg1-color)', + 'background-color': 'var(--mjx-bg1-color)', + }, + // + // Primary highlighting colors + // + 'mjx-container [data-sre-highlight-1]:not([data-mjx-collapsed], rect)': { + color: 'var(--mjx-fg1-color) ! important', // // CHTML + fill: 'var(--mjx-fg1-color) ! important', // // SVG + }, + [[ + 'mjx-container:not([data-mjx-clone-container])', + '[data-sre-highlight-1]:not([data-sre-enclosed], rect)', + ].join(' ')]: { + 'background-color': 'var(--mjx-bg1-color) ! important', // // CHTML + }, + 'mjx-container rect[data-sre-highlight-1]:not([data-sre-enclosed])': { + fill: 'var(--mjx-bg1-color) ! important', // // SVG + }, + // + // Secondary highlighting colors + // + 'mjx-container [data-sre-highlight-2]': { + color: 'var(--mjx-fg2-color) ! important', // // CHTML + 'background-color': 'var(--mjx-bg2-color) ! important', // // CHTML + fill: 'var(--mjx-fg2-color) ! important', // // SVG + }, + 'mjx-container rect[data-sre-highlight-2]': { + fill: 'var(--mjx-bg2-color) ! important', // // SVG + }, }); + + /** + * Set the CSS styles for a given color type and priority + * + * @param {string} type The color type (fg or bg) + * @param {number} priority 1 = primary, 2 = secondary + * @param {string} color The color name (blue, red, black, etc.) + * @param {number} opacity The alpha channel for the color + */ + public static setColor( + type: string, + priority: number, + color: string, + opacity: number + ) { + const style = this.styleSheet; + if (style) { + const css = (style.sheet.cssRules[0] as any).style; + const alpha = opacity === 1 ? 1 : `var(--mjx-${type}${priority}-alpha)`; + const name = `--mjx-${type}${priority}-color`; + const value = `rgba(var(--mjx-${type}-${color}), ${alpha})`; + if (css.getPropertyValue(name) !== value) { + css.setProperty(name, value); + } + const oname = `--mjx-${type}${priority}-alpha`; + if (css.getPropertyValue(oname) !== String(opacity)) { + css.setProperty(oname, opacity); + (style.sheet.cssRules[1] as any).cssRules[0].style.setProperty( + oname, + opacity ** 0.7071 + ); + } + } + } } /** * Region class that enables auto voicing of content via SSML markup. */ export class SpeechRegion extends LiveRegion { + protected static style: StyleJsonSheet = null; + /** * Flag to activate auto voicing. */ @@ -393,21 +501,17 @@ export class SpeechRegion extends LiveRegion { private clear: boolean = false; /** - * The highlighter to use. + * The highlighter to use. (Set by ExplorerPool) */ - public highlighter: Highlighter = getHighlighter( - { color: 'red' }, - { color: 'black' }, - this.document.outputJax.name - ); + public highlighter: Highlighter; /** * @override */ - public Show(node: HTMLElement, highlighter: Highlighter) { + public Show(node: HTMLElement) { super.Update('\u00a0'); // Ensures region shown and cannot be overwritten. this.node = node; - super.Show(node, highlighter); + super.Show(node); } /** @@ -448,6 +552,9 @@ export class SpeechRegion extends LiveRegion { promise.then(() => this.makeVoice(speech)); } + /** + * @param {string} speech The speech string to voice + */ private makeVoice(speech: string) { this.active = this.document.options.a11y.voicing && @@ -566,7 +673,7 @@ export class HoverRegion extends AbstractRegion { * @override */ protected static style: StyleJsonSheet = new StyleJsonSheet({ - ['.' + HoverRegion.className]: { + [`.${HoverRegion.className}`]: { display: 'block', position: 'absolute', width: 'max-content', @@ -580,8 +687,27 @@ export class HoverRegion extends AbstractRegion { 'box-shadow': '0px 10px 20px #888', border: '2px solid #CCCCCC', }, - ['.' + HoverRegion.className + ' > div']: { + [`.${HoverRegion.className} > div`]: { overflow: 'hidden', + color: 'var(--mjx-fg1-color)', + 'background-color': 'var(--mjx-bg1-color)', + }, + '@media (prefers-color-scheme: dark)': { + ['.' + HoverRegion.className]: { + 'background-color': '#222025', + 'box-shadow': '0px 5px 20px #000', + border: '1px solid #7C7C7C', + }, + }, + 'mjx-container[data-mjx-clone-container]': { + padding: '2px ! important', + }, + 'mjx-math > mjx-mlabeledtr': { + display: 'inline-block', + 'margin-right': '.5em ! important', + }, + 'mjx-math > mjx-mtd': { + float: 'right', }, }); @@ -621,27 +747,11 @@ export class HoverRegion extends AbstractRegion { /** * @override */ - protected highlight(highlighter: Highlighter) { - if (!this.div) return; - // TODO Do this with styles to avoid the interaction of SVG/CHTML. - if ( - this.inner.firstChild && - !(this.inner.firstChild as HTMLElement).hasAttribute('sre-highlight') - ) { - return; - } - this.inner.style.backgroundColor = highlighter.background; - this.inner.style.color = highlighter.foreground; - } - - /** - * @override - */ - public Show(node: HTMLElement, highlighter: Highlighter) { + public Show(node: HTMLElement) { this.AddElement(); this.div.style.fontSize = this.document.options.a11y.magnify; this.Update(node); - super.Show(node, highlighter); + super.Show(node); } /** @@ -675,47 +785,126 @@ export class HoverRegion extends AbstractRegion { * @param {HTMLElement} node The original node. * @returns {HTMLElement} The cloned node. */ - private cloneNode(node: HTMLElement): HTMLElement { + protected cloneNode(node: HTMLElement): HTMLElement { let mjx = node.cloneNode(true) as HTMLElement; mjx.setAttribute('data-mjx-clone', 'true'); if (mjx.nodeName !== 'MJX-CONTAINER') { - // remove element spacing (could be done in CSS) if (mjx.nodeName !== 'g') { mjx.style.marginLeft = mjx.style.marginRight = '0'; } - let container = node; - while (container && container.nodeName !== 'MJX-CONTAINER') { - container = container.parentNode as HTMLElement; - } + const container = node.closest('mjx-container'); if (mjx.nodeName !== 'MJX-MATH' && mjx.nodeName !== 'svg') { - const child = container.firstChild; - mjx = child.cloneNode(false).appendChild(mjx).parentNode as HTMLElement; - // - // SVG specific - // - if (mjx.nodeName === 'svg') { - (mjx.firstChild as HTMLElement).setAttribute( - 'transform', - 'matrix(1 0 0 -1 0 0)' - ); - const W = parseFloat(mjx.getAttribute('viewBox').split(/ /)[2]); - const w = parseFloat(mjx.getAttribute('width')); - const { x, y, width, height } = (node as any).getBBox(); - mjx.setAttribute( - 'viewBox', - [x, -(y + height), width, height].join(' ') - ); - mjx.removeAttribute('style'); - mjx.setAttribute('width', (w / W) * width + 'ex'); - mjx.setAttribute('height', (w / W) * height + 'ex'); - container.setAttribute('sre-highlight', 'false'); + let math = container.firstChild; + if (math.nodeName === 'MJX-BBOX') { + math = math.nextSibling; } + mjx = math.cloneNode(false).appendChild(mjx).parentElement; + const enclosed = Array.from( + container.querySelectorAll('[data-sre-enclosed]') + ); + math.nodeName === 'svg' + ? this.svgClone(node, enclosed, mjx, container) + : this.chtmlClone(node, enclosed, mjx); } - mjx = container.cloneNode(false).appendChild(mjx) - .parentNode as HTMLElement; - // remove displayed math margins (could be done in CSS) + mjx = container.cloneNode(false).appendChild(mjx).parentElement; mjx.style.margin = '0'; + mjx.style.minWidth = ''; } + mjx.setAttribute('data-mjx-clone-container', 'true'); return mjx; } + + /** + * @param {HTMLElement} node The main node being shown + * @param {Element[]} enclosed The elements to be cloned + * @param {HTMLElement} mjx The container for the clones + */ + protected chtmlClone( + node: HTMLElement, + enclosed: Element[], + mjx: HTMLElement + ) { + for (const child of enclosed) { + if (child !== node) { + const id = child.getAttribute('data-semantic-id'); + if (!id || !mjx.querySelector(`[data-semantic-id="${id}"]`)) { + mjx.appendChild(child.cloneNode(true)); + } + } + } + } + + /** + * @param {HTMLElement} node The main node being shown + * @param {Element[]} enclosed The elements to be cloned + * @param {HTMLElement} mjx The container for the clones + * @param {Element} container The container for node + */ + protected svgClone( + node: Element, + enclosed: Element[], + mjx: HTMLElement, + container: Element + ) { + let { x, y, width, height } = (node as SVGGraphicsElement).getBBox(); + if (enclosed.length) { + mjx.firstChild.remove(); + const g = container.querySelector('g').cloneNode(false); + for (const child of enclosed) { + const clone = g.appendChild(child.cloneNode(true)) as HTMLElement; + if (child === node) { + clone.setAttribute('data-mjx-clone', 'true'); + } + const [cx, cy] = this.xy(child); + clone.setAttribute('transform', `translate(${cx}, ${cy})`); + } + mjx.appendChild(g); + const rect = node.previousSibling as SVGRectElement; + const bbox = rect.getBBox(); + width = bbox.width; + height = bbox.height; + const [X, Y] = this.xy(rect); + x = X; + y = Y + bbox.y; + } + // + // Handle top-level expression with a tag + // + const g = container.querySelector('g'); + if ( + container.getAttribute('width') === 'full' && + g.firstChild.lastChild === node + ) { + mjx.innerHTML = ''; + mjx.appendChild(container.cloneNode(true).firstChild); + mjx.querySelector('.mjx-selected').setAttribute('data-mjx-clone', 'true'); + mjx.querySelector('[data-sre-highlighter-added]')?.remove(); + return; + } + // + // All other expressions + // + (mjx.firstChild as HTMLElement).setAttribute('transform', 'scale(1, -1)'); + const W = parseFloat( + ( + mjx.getAttribute('viewBox') || mjx.getAttribute('data-mjx-viewBox') + ).split(/ /)[2] + ); + const w = parseFloat(mjx.style.minWidth || mjx.getAttribute('width')); + mjx.setAttribute('viewBox', [x, -(y + height), width, height].join(' ')); + mjx.removeAttribute('style'); + mjx.setAttribute('width', (w / W) * width + 'ex'); + mjx.setAttribute('height', (w / W) * height + 'ex'); + } + + /** + * @param {Element} node The node whose position is needed + * @returns {[number, number]} The position in viewport coordinates + */ + protected xy(node: Element): number[] { + const P = DOMPoint.fromPoint({ x: 0, y: 0 }).matrixTransform( + (node as SVGGraphicsElement).getCTM().inverse() + ); + return [-P.x, -P.y]; + } } diff --git a/ts/a11y/semantic-enrich.ts b/ts/a11y/semantic-enrich.ts index 61c776f3f..8899c35ef 100644 --- a/ts/a11y/semantic-enrich.ts +++ b/ts/a11y/semantic-enrich.ts @@ -209,6 +209,7 @@ export function EnrichedMathItemMixin< // math.math = math.math .replace(/ role="treeitem"/g, ' data-speech-node="true"') + .replace(/ aria-level/g, ' data-semantic-level-number') .replace(/ aria-(?:posinset|owns|setsize)=".*?"/g, ''); math.display = this.display; math.compile(document); @@ -273,8 +274,11 @@ export function EnrichedMathItemMixin< * @template T The Text node class * @template D The Document class */ -export interface EnrichedMathDocument - extends AbstractMathDocument { +export interface EnrichedMathDocument extends AbstractMathDocument< + N, + T, + D +> { /** * Perform enrichment on the MathItems in the MathDocument * diff --git a/ts/a11y/speech.ts b/ts/a11y/speech.ts index 0cf3d1547..10d3c0059 100644 --- a/ts/a11y/speech.ts +++ b/ts/a11y/speech.ts @@ -169,8 +169,11 @@ export function SpeechMathItemMixin< * @template T The Text node class * @template D The Document class */ -export interface SpeechMathDocument - extends EnrichedMathDocument { +export interface SpeechMathDocument extends EnrichedMathDocument< + N, + T, + D +> { /** * The webworker handler for the document */ diff --git a/ts/a11y/speech/SpeechMenu.ts b/ts/a11y/speech/SpeechMenu.ts index 97663197d..e76a927cc 100644 --- a/ts/a11y/speech/SpeechMenu.ts +++ b/ts/a11y/speech/SpeechMenu.ts @@ -22,7 +22,12 @@ */ import { ExplorerMathItem } from '../explorer.js'; -import { MJContextMenu } from '../../ui/menu/MJContextMenu.js'; +import { MJContextMenu, SubmenuCallback } from '../../ui/menu/MJContextMenu.js'; +import { + SelectionDialog, + SelectionOrder, + SelectionGrid, +} from '../../ui/dialog/SelectionDialog.js'; import { SubMenu, Submenu } from '../../ui/menu/mj-context-menu.js'; import * as Sre from '../sre.js'; @@ -113,7 +118,7 @@ let counter = 0; function csSelectionBox(menu: MJContextMenu, locale: string): object { const props = localePreferences.get(locale); csPrefsVariables(menu, Object.keys(props)); - const items = []; + const items: any[] = []; for (const prop of Object.getOwnPropertyNames(props)) { items.push({ title: prop, @@ -121,22 +126,19 @@ function csSelectionBox(menu: MJContextMenu, locale: string): object { variable: 'csprf_' + prop, }); } - const sb = menu.factory.get('selectionBox')( - menu.factory, - { - title: 'Clearspeak Preferences', - signature: '', - order: 'alphabetic', - grid: 'square', - selections: items, - }, + const sb = new SelectionDialog( + 'Clearspeak Preferences', + '', + items, + SelectionOrder.ALPHABETICAL, + SelectionGrid.SQUARE, menu ); return { type: 'command', id: 'ClearspeakPreferences', content: 'Select Preferences', - action: () => sb.post(0, 0), + action: () => sb.post(), }; } @@ -223,13 +225,13 @@ function smartPreferences( * * @param {MJContextMenu} menu The context menu. * @param {Submenu} sub The submenu to attach elements to. - * @param {(sub: SubMenu) => void} callback Callback to apply on the constructed + * @param {SubmenuCallback} callback Callback to apply on the constructed * submenu. */ export async function clearspeakMenu( menu: MJContextMenu, sub: Submenu, - callback: (sub: SubMenu) => void + callback: SubmenuCallback ) { const exit = (items: object[]) => { callback( @@ -288,13 +290,13 @@ let LOCALE_MENU: SubMenu = null; * * @param {MJContextMenu} menu The context menu. * @param {Submenu} sub The submenu to attach elements to. - * @param {(sub: SubMenu) => void} callback Callback to apply on the constructed + * @param {SubmenuCallback} callback Callback to apply on the constructed * submenu. */ export function localeMenu( menu: MJContextMenu, sub: Submenu, - callback: (sub: SubMenu) => void + callback: SubmenuCallback ) { if (LOCALE_MENU) { callback(LOCALE_MENU); diff --git a/ts/a11y/speech/WebWorker.ts b/ts/a11y/speech/WebWorker.ts index be0a65db5..72a54c5aa 100644 --- a/ts/a11y/speech/WebWorker.ts +++ b/ts/a11y/speech/WebWorker.ts @@ -328,6 +328,9 @@ export class WorkerHandler { continue; } node = adaptor.childNodes(node)[0] as N; + if (adaptor.kind(node) === 'rect') { + node = adaptor.next(node) as N; + } adaptor.setAttribute(node, 'data-semantic-type', 'dummy'); this.setSpecialAttributes(node, sid, ''); } diff --git a/ts/adaptors/HTMLAdaptor.ts b/ts/adaptors/HTMLAdaptor.ts index c1a9d68dc..c0f650ba7 100644 --- a/ts/adaptors/HTMLAdaptor.ts +++ b/ts/adaptors/HTMLAdaptor.ts @@ -192,13 +192,19 @@ export interface MinHTMLAdaptor extends DOMAdaptor { * @template D The Document class */ export class HTMLAdaptor< - N extends MinHTMLElement, - T extends MinText, - D extends MinDocument, - > + N extends MinHTMLElement, + T extends MinText, + D extends MinDocument, +> extends AbstractDOMAdaptor implements MinHTMLAdaptor { + /** + * The font size to use when it can't be measured (e.g., the element + * isn't in the DOM). + */ + public static DEFAULT_FONT_SIZE = 16; + /** * The HTML adaptor can measure DOM node sizes */ @@ -463,6 +469,9 @@ export class HTMLAdaptor< */ public setAttribute(node: N, name: string, value: string, ns: string = null) { if (!ns) { + if (name === 'style') { + value = value.replace(/\n/g, ' '); + } return node.setAttribute(name, value); } name = ns.replace(/.*\//, '') + ':' + name.replace(/^.*:/, ''); @@ -538,14 +547,14 @@ export class HTMLAdaptor< * @override */ public setStyle(node: N, name: string, value: string) { - (node.style as OptionList)[name] = value; + node.style[name] = String(value).replace(/\n/g, ' '); } /** * @override */ public getStyle(node: N, name: string) { - return (node.style as OptionList)[name]; + return node.style[name]; } /** @@ -585,7 +594,10 @@ export class HTMLAdaptor< */ public fontSize(node: N) { const style = this.window.getComputedStyle(node); - return parseFloat(style.fontSize); + return parseFloat( + style.fontSize || + String((this.constructor as typeof HTMLAdaptor).DEFAULT_FONT_SIZE) + ); } /** diff --git a/ts/components/cjs/root.ts b/ts/components/cjs/root.ts index 137c4a5a2..e22a9cc6f 100644 --- a/ts/components/cjs/root.ts +++ b/ts/components/cjs/root.ts @@ -25,10 +25,13 @@ * The location of this file */ declare const __dirname: string; +declare const require: (file: string) => any; /** * @return {string} The MathJax component root directory */ export function mjxRoot(): string { - return __dirname.replace(/[cm]js\/components\/[cm]js$/, 'bundle'); + return require('../../util/context.js') + .context.path(__dirname) + .replace(/[cm]js\/components\/[cm]js$/, 'bundle'); } diff --git a/ts/components/cjs/sre-root.ts b/ts/components/cjs/sre-root.ts index bb8cff7e9..867c41493 100644 --- a/ts/components/cjs/sre-root.ts +++ b/ts/components/cjs/sre-root.ts @@ -25,10 +25,13 @@ * The location of this file */ declare const __dirname: string; +declare const require: (file: string) => any; /** - * @return {string} The MathJax mjs SRE root directory + * @return {string} The MathJax cjs SRE root directory */ export function sreRoot(): string { - return __dirname.replace(/components\/[cm]js$/, 'a11y/sre'); + return require('../../util/context.js') + .context.path(__dirname) + .replace(/components\/[cm]js$/, 'a11y/sre'); } diff --git a/ts/components/loader.ts b/ts/components/loader.ts index 34c2e2e56..1ed0ae9ab 100644 --- a/ts/components/loader.ts +++ b/ts/components/loader.ts @@ -120,7 +120,7 @@ export const PathFilters: { [name: string]: PathFilterFunction } = { */ normalize: (data) => { const name = data.name; - if (!name.match(/^(?:[a-z]+:\/)?\/|[a-z]:\\|\[/i)) { + if (!name.match(/^(?:[a-z]+:\/)?\/|[a-z]:[/\\]|\[/i)) { data.name = '[mathjax]/' + name.replace(/^\.\//, ''); } return true; @@ -399,6 +399,9 @@ if (typeof MathJax.loader === 'undefined') { combineDefaults(MathJax.config, 'loader', { paths: { mathjax: Loader.getRoot(), + fonts: context.window + ? 'https://cdn.jsdelivr.net/npm/@mathjax' + : '@mathjax', }, source: {}, dependencies: {}, diff --git a/ts/components/mjs/root.ts b/ts/components/mjs/root.ts index d8e328158..9fae578fa 100644 --- a/ts/components/mjs/root.ts +++ b/ts/components/mjs/root.ts @@ -21,12 +21,13 @@ * @author dpvc@mathjax.org (Davide Cervone) */ +import { context } from '../../util/context.js'; + /** * @returns {string} The MathJax component root directory */ export function mjxRoot(): string { - return new URL(import.meta.url).pathname.replace( - /[cm]js\/components\/[cm]js\/root.js$/, - 'bundle' - ); + return context + .path(new URL(import.meta.url).pathname) + .replace(/[cm]js\/components\/[cm]js\/root.js$/, 'bundle'); } diff --git a/ts/components/mjs/sre-root.ts b/ts/components/mjs/sre-root.ts index 99c44f78f..103bdf2a9 100644 --- a/ts/components/mjs/sre-root.ts +++ b/ts/components/mjs/sre-root.ts @@ -21,12 +21,13 @@ * @author dpvc@mathjax.org (Davide Cervone) */ +import { context } from '../../util/context.js'; + /** * @returns {string} The MathJax mjs SRE root directory */ export function sreRoot(): string { - return new URL(import.meta.url).pathname.replace( - /components\/[cm]js\/sre-root.js$/, - 'a11y/sre' - ); + return context + .path(new URL(import.meta.url).pathname) + .replace(/components\/[cm]js\/sre-root.js$/, 'a11y/sre'); } diff --git a/ts/components/startup.ts b/ts/components/startup.ts index 777bd5c70..a85839459 100644 --- a/ts/components/startup.ts +++ b/ts/components/startup.ts @@ -620,6 +620,7 @@ if (typeof MathJax._.startup === 'undefined') { typeset: true, ready: Startup.defaultReady.bind(Startup), pageReady: Startup.defaultPageReady.bind(Startup), + polyfillHasOwn: true, // Can be removed with ES2024 implementation of Object.hasown }); combineWithMathJax({ startup: Startup, diff --git a/ts/components/version.ts b/ts/components/version.ts index 30ad036c5..b6fc39d1d 100644 --- a/ts/components/version.ts +++ b/ts/components/version.ts @@ -22,4 +22,4 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -export const VERSION = '4.0.0'; +export const VERSION = '4.1.0'; diff --git a/ts/core/DOMAdaptor.ts b/ts/core/DOMAdaptor.ts index 16889ddcb..1aa6bf3d9 100644 --- a/ts/core/DOMAdaptor.ts +++ b/ts/core/DOMAdaptor.ts @@ -430,9 +430,11 @@ export interface DOMAdaptor { * @template T The Text node class * @template D The Document class */ -export abstract class AbstractDOMAdaptor - implements DOMAdaptor -{ +export abstract class AbstractDOMAdaptor implements DOMAdaptor< + N, + T, + D +> { /** * The document in which the HTML nodes will be created */ diff --git a/ts/core/MathDocument.ts b/ts/core/MathDocument.ts index a4d068687..73086099f 100644 --- a/ts/core/MathDocument.ts +++ b/ts/core/MathDocument.ts @@ -655,9 +655,11 @@ class DefaultMathItem extends AbstractMathItem {} * @template T The Text node class * @template D The Document class */ -export abstract class AbstractMathDocument - implements MathDocument -{ +export abstract class AbstractMathDocument implements MathDocument< + N, + T, + D +> { /** * The type of MathDocument */ diff --git a/ts/core/MmlTree/MmlNode.ts b/ts/core/MmlTree/MmlNode.ts index c19b49414..2c94e6810 100644 --- a/ts/core/MmlTree/MmlNode.ts +++ b/ts/core/MmlTree/MmlNode.ts @@ -1182,6 +1182,8 @@ export abstract class AbstractMmlBaseNode extends AbstractMmlNode { if (this.isEmbellished || base.isKind('mi')) { result = base.setTeXclass(prev); this.updateTeXclass(this.core()); + } else if (base.isKind('TeXAtom')) { + this.texClass = base.texClass; } else { base.setTeXclass(null); } diff --git a/ts/core/MmlTree/MmlNodes/mo.ts b/ts/core/MmlTree/MmlNodes/mo.ts index 2f83d8b52..5b10ab0ad 100644 --- a/ts/core/MmlTree/MmlNodes/mo.ts +++ b/ts/core/MmlTree/MmlNodes/mo.ts @@ -463,8 +463,8 @@ export class MmlMo extends AbstractMmlTokenNode { for (const name of Object.keys(def[3] || {})) { this.attributes.setInherited(name, def[3][name]); } - this.lspace = ((def[0] || -1) + 1) / 18; - this.rspace = ((def[1] || -1) + 1) / 18; + this.lspace = def[0] / 18; + this.rspace = def[1] / 18; } /** @@ -552,8 +552,9 @@ export class MmlMo extends AbstractMmlTokenNode { this.getProperty('mathaccent') !== undefined || !parent || !parent.isKind('munderover') - ) + ) { return; + } const [base, under, over] = parent.childNodes; if (base.isEmbellished && base.coreMO() === this) return; const isUnder = !!(under && under.isEmbellished && under.coreMO() === this); diff --git a/ts/core/MmlTree/OperatorDictionary.ts b/ts/core/MmlTree/OperatorDictionary.ts index bf35d289c..fe7d07294 100644 --- a/ts/core/MmlTree/OperatorDictionary.ts +++ b/ts/core/MmlTree/OperatorDictionary.ts @@ -51,32 +51,12 @@ export function OPDEF( * The various kinds of operators in the dictionary */ export const MO = { - ORD: OPDEF(0, 0, TEXCLASS.ORD), - ORD11: OPDEF(1, 1, TEXCLASS.ORD), - ORD21: OPDEF(2, 1, TEXCLASS.ORD), - ORD02: OPDEF(0, 2, TEXCLASS.ORD), - ORD55: OPDEF(5, 5, TEXCLASS.ORD), - NONE: OPDEF(0, 0, TEXCLASS.NONE), - OP: OPDEF(1, 2, TEXCLASS.OP, { - largeop: true, - movablelimits: true, - symmetric: true, - }), - OPFIXED: OPDEF(1, 2, TEXCLASS.OP, { largeop: true, movablelimits: true }), - INTEGRAL: OPDEF(0, 1, TEXCLASS.OP, { largeop: true, symmetric: true }), - INTEGRAL2: OPDEF(1, 2, TEXCLASS.OP, { largeop: true, symmetric: true }), - BIN3: OPDEF(3, 3, TEXCLASS.BIN), - BIN4: OPDEF(4, 4, TEXCLASS.BIN), - BIN01: OPDEF(0, 1, TEXCLASS.BIN), - BIN5: OPDEF(5, 5, TEXCLASS.BIN), - TALLBIN: OPDEF(4, 4, TEXCLASS.BIN, { stretchy: true }), - BINOP: OPDEF(4, 4, TEXCLASS.BIN, { largeop: true, movablelimits: true }), REL: OPDEF(5, 5, TEXCLASS.REL), - REL1: OPDEF(1, 1, TEXCLASS.REL, { stretchy: true }), - REL4: OPDEF(4, 4, TEXCLASS.REL), - RELSTRETCH: OPDEF(5, 5, TEXCLASS.REL, { stretchy: true }), - RELACCENT: OPDEF(5, 5, TEXCLASS.REL, { accent: true }), WIDEREL: OPDEF(5, 5, TEXCLASS.REL, { accent: true, stretchy: true }), + BIN4: OPDEF(4, 4, TEXCLASS.BIN), + RELSTRETCH: OPDEF(5, 5, TEXCLASS.REL, { stretchy: true }), + ORD: OPDEF(0, 0, TEXCLASS.ORD), + BIN3: OPDEF(3, 3, TEXCLASS.BIN), OPEN: OPDEF(0, 0, TEXCLASS.OPEN, { fence: true, stretchy: true, @@ -87,10 +67,33 @@ export const MO = { stretchy: true, symmetric: true, }), - INNER: OPDEF(0, 0, TEXCLASS.INNER), - PUNCT: OPDEF(0, 3, TEXCLASS.PUNCT), + INTEGRAL: OPDEF(3, 3, TEXCLASS.OP, { largeop: true, symmetric: true }), ACCENT: OPDEF(0, 0, TEXCLASS.ORD, { accent: true }), WIDEACCENT: OPDEF(0, 0, TEXCLASS.ORD, { accent: true, stretchy: true }), + OP: OPDEF(3, 3, TEXCLASS.OP, { + largeop: true, + movablelimits: true, + symmetric: true, + }), + RELACCENT: OPDEF(5, 5, TEXCLASS.REL, { accent: true }), + BIN0: OPDEF(0, 0, TEXCLASS.BIN), + BIN5: OPDEF(5, 5, TEXCLASS.BIN), + FENCE: OPDEF(0, 0, TEXCLASS.ORD, { + fence: true, + stretchy: true, + symmetric: true, + }), + INNER: OPDEF(1, 1, TEXCLASS.INNER), + ORD30: OPDEF(3, 0, TEXCLASS.ORD), + NONE: OPDEF(0, 0, TEXCLASS.NONE), + ORDSTRETCH0: OPDEF(0, 0, TEXCLASS.ORD, { stretchy: true }), + BINSTRETCH0: OPDEF(0, 0, TEXCLASS.BIN, { stretchy: true }), + RELSTRETCH0: OPDEF(0, 0, TEXCLASS.REL, { stretchy: true }), + CLOSE0: OPDEF(0, 0, TEXCLASS.CLOSE, { fence: true }), + ORD3: OPDEF(3, 3, TEXCLASS.ORD), + PUNCT03: OPDEF(0, 3, TEXCLASS.PUNCT, { linebreakstyle: 'after' }), + OPEN0: OPDEF(0, 0, TEXCLASS.OPEN, { fence: true }), + STRETCH4: OPDEF(4, 4, TEXCLASS.BIN, { stretchy: true }), }; /** @@ -184,6 +187,7 @@ export const MMLSPACING = [ [0, 0], // OPEN [0, 0], // CLOSE [0, 3], // PUNCT + [1, 1], // INNER ]; /** @@ -192,1170 +196,1261 @@ export const MMLSPACING = [ /* prettier-ignore */ export const OPTABLE: {[form: string]: OperatorList} = { prefix: { - '(': MO.OPEN, // left parenthesis - '+': MO.BIN01, // plus sign - '-': MO.BIN01, // hyphen-minus - '[': MO.OPEN, // left square bracket - '{': MO.OPEN, // left curly bracket - '|': MO.OPEN, // vertical line - '||': [0, 0, TEXCLASS.BIN, {fence: true, stretchy: true, symmetric: true}], // multiple character operator: || - '|||': [0, 0, TEXCLASS.ORD, {fence: true, stretchy: true, symmetric: true}], // multiple character operator: ||| - '\u00AC': MO.ORD21, // not sign - '\u00B1': MO.BIN01, // plus-minus sign - '\u2016': [0, 0, TEXCLASS.ORD, {fence: true, stretchy: true}], // double vertical line - '\u2018': [0, 0, TEXCLASS.OPEN, {fence: true}], // left single quotation mark - '\u201C': [0, 0, TEXCLASS.OPEN, {fence: true}], // left double quotation mark - '\u2145': MO.ORD21, // double-struck italic capital d - '\u2146': OPDEF(2, 0, TEXCLASS.ORD), // double-struck italic small d - '\u2200': MO.ORD21, // for all - '\u2202': MO.ORD21, // partial differential - '\u2203': MO.ORD21, // there exists - '\u2204': MO.ORD21, // there does not exist - '\u2207': MO.ORD21, // nabla - '\u220F': MO.OP, // n-ary product - '\u2210': MO.OP, // n-ary coproduct - '\u2211': MO.OP, // n-ary summation - '\u2212': MO.BIN01, // minus sign - '\u2213': MO.BIN01, // minus-or-plus sign - '\u221A': [1, 1, TEXCLASS.ORD, {stretchy: true}], // square root - '\u221B': MO.ORD11, // cube root - '\u221C': MO.ORD11, // fourth root - '\u2220': MO.ORD, // angle - '\u2221': MO.ORD, // measured angle - '\u2222': MO.ORD, // spherical angle - '\u222B': MO.INTEGRAL, // integral - '\u222C': MO.INTEGRAL, // double integral - '\u222D': MO.INTEGRAL, // triple integral - '\u222E': MO.INTEGRAL, // contour integral - '\u222F': MO.INTEGRAL, // surface integral - '\u2230': MO.INTEGRAL, // volume integral - '\u2231': MO.INTEGRAL, // clockwise integral - '\u2232': MO.INTEGRAL, // clockwise contour integral - '\u2233': MO.INTEGRAL, // anticlockwise contour integral - '\u22C0': MO.OP, // n-ary logical and - '\u22C1': MO.OP, // n-ary logical or - '\u22C2': MO.OP, // n-ary intersection - '\u22C3': MO.OP, // n-ary union - '\u2308': MO.OPEN, // left ceiling - '\u230A': MO.OPEN, // left floor - '\u2329': MO.OPEN, // left-pointing angle bracket - '\u2772': MO.OPEN, // light left tortoise shell bracket ornament - '\u27E6': MO.OPEN, // mathematical left white square bracket - '\u27E8': MO.OPEN, // mathematical left angle bracket - '\u27EA': MO.OPEN, // mathematical left double angle bracket - '\u27EC': MO.OPEN, // mathematical left white tortoise shell bracket - '\u27EE': MO.OPEN, // mathematical left flattened parenthesis - '\u2980': [0, 0, TEXCLASS.ORD, {fence: true, stretchy: true}], // triple vertical bar delimiter - '\u2983': MO.OPEN, // left white curly bracket - '\u2985': MO.OPEN, // left white parenthesis - '\u2987': MO.OPEN, // z notation left image bracket - '\u2989': MO.OPEN, // z notation left binding bracket - '\u298B': MO.OPEN, // left square bracket with underbar - '\u298D': MO.OPEN, // left square bracket with tick in top corner - '\u298F': MO.OPEN, // left square bracket with tick in bottom corner - '\u2991': MO.OPEN, // left angle bracket with dot - '\u2993': MO.OPEN, // left arc less-than bracket - '\u2995': MO.OPEN, // double left arc greater-than bracket - '\u2997': MO.OPEN, // left black tortoise shell bracket - '\u29FC': MO.OPEN, // left-pointing curved angle bracket - '\u2A00': MO.OP, // n-ary circled dot operator - '\u2A01': MO.OP, // n-ary circled plus operator - '\u2A02': MO.OP, // n-ary circled times operator - '\u2A03': MO.OP, // n-ary union operator with dot - '\u2A04': MO.OP, // n-ary union operator with plus - '\u2A05': MO.OP, // n-ary square intersection operator - '\u2A06': MO.OP, // n-ary square union operator - '\u2A07': MO.OP, // two logical and operator - '\u2A08': MO.OP, // two logical or operator - '\u2A09': MO.OP, // n-ary times operator - '\u2A0A': MO.OP, // modulo two sum - '\u2A0B': MO.INTEGRAL2, // summation with integral - '\u2A0C': MO.INTEGRAL, // quadruple integral operator - '\u2A0D': MO.INTEGRAL2, // finite part integral - '\u2A0E': MO.INTEGRAL2, // integral with double stroke - '\u2A0F': MO.INTEGRAL2, // integral average with slash - '\u2A10': MO.OP, // circulation function - '\u2A11': MO.OP, // anticlockwise integration - '\u2A12': MO.OP, // line integration with rectangular path around pole - '\u2A13': MO.OP, // line integration with semicircular path around pole - '\u2A14': MO.OP, // line integration not including the pole - '\u2A15': MO.INTEGRAL2, // integral around a point operator - '\u2A16': MO.INTEGRAL2, // quaternion integral operator - '\u2A17': MO.INTEGRAL2, // integral with leftwards arrow with hook - '\u2A18': MO.INTEGRAL2, // integral with times sign - '\u2A19': MO.INTEGRAL2, // integral with intersection - '\u2A1A': MO.INTEGRAL2, // integral with union - '\u2A1B': MO.INTEGRAL2, // integral with overbar - '\u2A1C': MO.INTEGRAL2, // integral with underbar - '\u2AFC': MO.OP, // large triple vertical bar operator - '\u2AFF': MO.OP, // n-ary white vertical bar + '!': MO.ORD, // exclamation mark + '(': MO.OPEN, // left parenthesis + '+': MO.BIN0, // plus sign + '-': MO.BIN0, // hyphen-minus + '[': MO.OPEN, // left square bracket + '{': MO.OPEN, // left curly bracket + '|': MO.OPEN, // vertical line + '||': MO.BIN0, // multiple character operator: || + '\u00AC': MO.ORD, // not sign: \neg + '\u00B1': MO.BIN0, // plus-minus sign: \pm + '\u2016': MO.FENCE, // double vertical line + '\u2018': MO.OPEN0, // left single quotation mark + '\u201C': MO.OPEN0, // left double quotation mark + '\u2145': MO.ORD30, // double-struck italic capital d + '\u2146': MO.ORD30, // double-struck italic small d + '\u2200': MO.ORD, // for all: \forall + '\u2201': MO.ORD, // complement + '\u2202': MO.ORD30, // partial differential: \partial + '\u2203': MO.ORD, // there exists: \exists + '\u2204': MO.ORD, // there does not exist + '\u2207': MO.ORD, // nabla: \nabla + '\u220F': MO.OP, // n-ary product: \prod + '\u2210': MO.OP, // n-ary coproduct: \coprod + '\u2211': MO.OP, // n-ary summation: \sum + '\u2212': MO.BIN0, // minus sign + '\u2213': MO.BIN0, // minus-or-plus sign: \mp + '\u221A': [3, 0, TEXCLASS.ORD, {stretchy: true}], // square root: \surd + '\u221B': MO.ORD30, // cube root + '\u221C': MO.ORD30, // fourth root + '\u221F': MO.ORD, // right angle + '\u2220': MO.ORD, // angle: \angle + '\u2221': MO.ORD, // measured angle + '\u2222': MO.ORD, // spherical angle + '\u222B': MO.INTEGRAL, // integral: \smallint + '\u222C': MO.INTEGRAL, // double integral + '\u222D': MO.INTEGRAL, // triple integral + '\u222E': MO.INTEGRAL, // contour integral: \oint + '\u222F': MO.INTEGRAL, // surface integral + '\u2230': MO.INTEGRAL, // volume integral + '\u2231': MO.INTEGRAL, // clockwise integral + '\u2232': MO.INTEGRAL, // clockwise contour integral + '\u2233': MO.INTEGRAL, // anticlockwise contour integral + '\u2234': MO.REL, // therefore + '\u2235': MO.REL, // because + '\u223C': [0, 0, TEXCLASS.REL, {}], // tilde operator: \sim + '\u22BE': MO.ORD, // right angle with arc + '\u22BF': MO.ORD, // right triangle + '\u22C0': MO.OP, // n-ary logical and: \bigwedge + '\u22C1': MO.OP, // n-ary logical or: \bigvee + '\u22C2': MO.OP, // n-ary intersection: \bigcap + '\u22C3': MO.OP, // n-ary union: \bigcup + '\u2308': MO.OPEN, // left ceiling: \lceil + '\u230A': MO.OPEN, // left floor: \lfloor + '\u2310': MO.ORD, // reversed not sign + '\u2319': MO.ORD, // turned not sign + '\u2772': MO.OPEN, // light left tortoise shell bracket ornament + '\u2795': MO.ORD, // heavy plus sign + '\u2796': MO.ORD, // heavy minus sign + '\u27C0': MO.ORD, // three dimensional angle + '\u27E6': MO.OPEN, // mathematical left white square bracket + '\u27E8': MO.OPEN, // mathematical left angle bracket: \langle + '\u27EA': MO.OPEN, // mathematical left double angle bracket + '\u27EC': MO.OPEN, // mathematical left white tortoise shell bracket + '\u27EE': MO.OPEN, // mathematical left flattened parenthesis: \lgroup + '\u2980': MO.FENCE, // triple vertical bar delimiter + '\u2983': MO.OPEN, // left white curly bracket + '\u2985': MO.OPEN, // left white parenthesis + '\u2987': MO.OPEN, // z notation left image bracket + '\u2989': MO.OPEN, // z notation left binding bracket + '\u298B': MO.OPEN, // left square bracket with underbar + '\u298D': MO.OPEN, // left square bracket with tick in top corner + '\u298F': MO.OPEN, // left square bracket with tick in bottom corner + '\u2991': MO.OPEN, // left angle bracket with dot + '\u2993': MO.OPEN, // left arc less-than bracket + '\u2995': MO.OPEN, // double left arc greater-than bracket + '\u2997': MO.OPEN, // left black tortoise shell bracket + '\u2999': MO.FENCE, // dotted fence + '\u299B': MO.ORD, // measured angle opening left + '\u299C': MO.ORD, // right angle variant with square + '\u299D': MO.ORD, // measured right angle with dot + '\u299E': MO.ORD, // angle with s inside + '\u299F': MO.ORD, // acute angle + '\u29A0': MO.ORD, // spherical angle opening left + '\u29A1': MO.ORD, // spherical angle opening up + '\u29A2': MO.ORD, // turned angle + '\u29A3': MO.ORD, // reversed angle + '\u29A4': MO.ORD, // angle with underbar + '\u29A5': MO.ORD, // reversed angle with underbar + '\u29A6': MO.ORD, // oblique angle opening up + '\u29A7': MO.ORD, // oblique angle opening down + '\u29A8': MO.ORD, // measured angle with open arm ending in arrow pointing up and right + '\u29A9': MO.ORD, // measured angle with open arm ending in arrow pointing up and left + '\u29AA': MO.ORD, // measured angle with open arm ending in arrow pointing down and right + '\u29AB': MO.ORD, // measured angle with open arm ending in arrow pointing down and left + '\u29AC': MO.ORD, // measured angle with open arm ending in arrow pointing right and up + '\u29AD': MO.ORD, // measured angle with open arm ending in arrow pointing left and up + '\u29AE': MO.ORD, // measured angle with open arm ending in arrow pointing right and down + '\u29AF': MO.ORD, // measured angle with open arm ending in arrow pointing left and down + '\u29D8': MO.OPEN, // left wiggly fence + '\u29DA': MO.OPEN, // left double wiggly fence + '\u29FC': MO.OPEN, // left-pointing curved angle bracket + '\u2A00': MO.OP, // n-ary circled dot operator: \bigodot + '\u2A01': MO.OP, // n-ary circled plus operator: \bigoplus + '\u2A02': MO.OP, // n-ary circled times operator: \bigotimes + '\u2A03': MO.OP, // n-ary union operator with dot + '\u2A04': MO.OP, // n-ary union operator with plus: \biguplus + '\u2A05': MO.OP, // n-ary square intersection operator + '\u2A06': MO.OP, // n-ary square union operator: \bigsqcup + '\u2A07': MO.OP, // two logical and operator + '\u2A08': MO.OP, // two logical or operator + '\u2A09': MO.OP, // n-ary times operator + '\u2A0A': MO.OP, // modulo two sum + '\u2A0B': MO.INTEGRAL, // summation with integral + '\u2A0C': MO.INTEGRAL, // quadruple integral operator + '\u2A0D': MO.INTEGRAL, // finite part integral + '\u2A0E': MO.INTEGRAL, // integral with double stroke + '\u2A0F': MO.INTEGRAL, // integral average with slash + '\u2A10': MO.INTEGRAL, // circulation function + '\u2A11': MO.INTEGRAL, // anticlockwise integration + '\u2A12': MO.INTEGRAL, // line integration with rectangular path around pole + '\u2A13': MO.INTEGRAL, // line integration with semicircular path around pole + '\u2A14': MO.INTEGRAL, // line integration not including the pole + '\u2A15': MO.INTEGRAL, // integral around a point operator + '\u2A16': MO.INTEGRAL, // quaternion integral operator + '\u2A17': MO.INTEGRAL, // integral with leftwards arrow with hook + '\u2A18': MO.INTEGRAL, // integral with times sign + '\u2A19': MO.INTEGRAL, // integral with intersection + '\u2A1A': MO.INTEGRAL, // integral with union + '\u2A1B': MO.INTEGRAL, // integral with overbar + '\u2A1C': MO.INTEGRAL, // integral with underbar + '\u2A1D': MO.OP, // join + '\u2A1E': MO.OP, // large left triangle operator + '\u2AEC': MO.ORD, // double stroke not sign + '\u2AED': MO.ORD, // reversed double stroke not sign + '\u2AFC': MO.OP, // large triple vertical bar operator + '\u2AFF': MO.OP, // n-ary white vertical bar + '\u3008': MO.OPEN, // left-pointing angle bracket }, postfix: { - '!!': OPDEF(1, 0), // multiple character operator: !! - '!': [1, 0, TEXCLASS.CLOSE, null], // exclamation mark - '"': MO.ACCENT, // quotation mark - '&': MO.ORD, // ampersand - ')': MO.CLOSE, // right parenthesis - '++': OPDEF(0, 0), // multiple character operator: ++ - '--': OPDEF(0, 0), // multiple character operator: -- - '..': OPDEF(0, 0), // multiple character operator: .. - '...': MO.ORD, // multiple character operator: ... - '\'': MO.ACCENT, // apostrophe - ']': MO.CLOSE, // right square bracket - '^': MO.WIDEACCENT, // circumflex accent - '_': MO.WIDEACCENT, // low line - '`': MO.ACCENT, // grave accent - '|': MO.CLOSE, // vertical line - '}': MO.CLOSE, // right curly bracket - '~': MO.WIDEACCENT, // tilde - '||': [0, 0, TEXCLASS.BIN, {fence: true, stretchy: true, symmetric: true}], // multiple character operator: || - '|||': [0, 0, TEXCLASS.ORD, {fence: true, stretchy: true, symmetric: true}], // multiple character operator: ||| - '\u00A8': MO.ACCENT, // diaeresis - '\u00AA': MO.ACCENT, // feminie ordinal indicator - '\u00AF': MO.WIDEACCENT, // macron - '\u00B0': MO.ORD, // degree sign - '\u00B2': MO.ACCENT, // superscript 2 - '\u00B3': MO.ACCENT, // superscript 3 - '\u00B4': MO.ACCENT, // acute accent - '\u00B8': MO.ACCENT, // cedilla - '\u00B9': MO.ACCENT, // superscript 1 - '\u00BA': MO.ACCENT, // masculine ordinal indicator - '\u02C6': MO.WIDEACCENT, // modifier letter circumflex accent - '\u02C7': MO.WIDEACCENT, // caron - '\u02C9': MO.WIDEACCENT, // modifier letter macron - '\u02CA': MO.ACCENT, // modifier letter acute accent - '\u02CB': MO.ACCENT, // modifier letter grave accent - '\u02CD': MO.WIDEACCENT, // modifier letter low macron - '\u02D8': MO.ACCENT, // breve - '\u02D9': MO.ACCENT, // dot above - '\u02DA': MO.ACCENT, // ring above - '\u02DC': MO.WIDEACCENT, // small tilde - '\u02DD': MO.ACCENT, // double acute accent - '\u02F7': MO.WIDEACCENT, // modifier letter low tilde - '\u0302': MO.WIDEACCENT, // combining circumflex accent - '\u0311': MO.ACCENT, // combining inverted breve - '\u03F6': MO.REL, // greek reversed lunate epsilon symbol - '\u2016': [0, 0, TEXCLASS.ORD, {fence: true, stretchy: true}], // double vertical line - '\u2019': [0, 0, TEXCLASS.CLOSE, {fence: true}], // right single quotation mark - '\u201A': MO.ACCENT, // single low-9 quotation mark - '\u201B': MO.ACCENT, // single high-reversed-9 quotation mark - '\u201D': [0, 0, TEXCLASS.CLOSE, {fence: true}], // right double quotation mark - '\u201E': MO.ACCENT, // double low-9 quotation mark - '\u201F': MO.ACCENT, // double high-reversed-9 quotation mark - '\u2032': MO.ORD, // prime - '\u2033': MO.ORD, // double prime - '\u2034': MO.ORD, // triple prime - '\u2035': MO.ORD, // reversed prime - '\u2036': MO.ORD, // reversed double prime - '\u2037': MO.ORD, // reversed triple prime - '\u203E': MO.WIDEACCENT, // overline - '\u2057': MO.ORD, // quadruple prime - '\u20DB': MO.ACCENT, // combining three dots above - '\u20DC': MO.ACCENT, // combining four dots above - '\u2309': MO.CLOSE, // right ceiling - '\u230B': MO.CLOSE, // right floor - '\u232A': MO.CLOSE, // right-pointing angle bracket - '\u23B4': MO.WIDEACCENT, // top square bracket - '\u23B5': MO.WIDEACCENT, // bottom square bracket - '\u23DC': MO.WIDEACCENT, // top parenthesis - '\u23DD': MO.WIDEACCENT, // bottom parenthesis - '\u23DE': MO.WIDEACCENT, // top curly bracket - '\u23DF': MO.WIDEACCENT, // bottom curly bracket - '\u23E0': MO.WIDEACCENT, // top tortoise shell bracket - '\u23E1': MO.WIDEACCENT, // bottom tortoise shell bracket - '\u25A0': MO.BIN3, // black square - '\u25A1': MO.BIN3, // white square - '\u25AA': MO.BIN3, // black small square - '\u25AB': MO.BIN3, // white small square - '\u25AD': MO.BIN3, // white rectangle - '\u25AE': MO.BIN3, // black vertical rectangle - '\u25AF': MO.BIN3, // white vertical rectangle - '\u25B0': MO.BIN3, // black parallelogram - '\u25B1': MO.BIN3, // white parallelogram - '\u25B2': MO.BIN4, // black up-pointing triangle - '\u25B4': MO.BIN4, // black up-pointing small triangle - '\u25B6': MO.BIN4, // black right-pointing triangle - '\u25B7': MO.BIN4, // white right-pointing triangle - '\u25B8': MO.BIN4, // black right-pointing small triangle - '\u25BC': MO.BIN4, // black down-pointing triangle - '\u25BE': MO.BIN4, // black down-pointing small triangle - '\u25C0': MO.BIN4, // black left-pointing triangle - '\u25C1': MO.BIN4, // white left-pointing triangle - '\u25C2': MO.BIN4, // black left-pointing small triangle - '\u25C4': MO.BIN4, // black left-pointing pointer - '\u25C5': MO.BIN4, // white left-pointing pointer - '\u25C6': MO.BIN4, // black diamond - '\u25C7': MO.BIN4, // white diamond - '\u25C8': MO.BIN4, // white diamond containing black small diamond - '\u25C9': MO.BIN4, // fisheye - '\u25CC': MO.BIN4, // dotted circle - '\u25CD': MO.BIN4, // circle with vertical fill - '\u25CE': MO.BIN4, // bullseye - '\u25CF': MO.BIN4, // black circle - '\u25D6': MO.BIN4, // left half black circle - '\u25D7': MO.BIN4, // right half black circle - '\u25E6': MO.BIN4, // white bullet - '\u266D': MO.ORD02, // music flat sign - '\u266E': MO.ORD02, // music natural sign - '\u266F': MO.ORD02, // music sharp sign - '\u2773': MO.CLOSE, // light right tortoise shell bracket ornament - '\u27E7': MO.CLOSE, // mathematical right white square bracket - '\u27E9': MO.CLOSE, // mathematical right angle bracket - '\u27EB': MO.CLOSE, // mathematical right double angle bracket - '\u27ED': MO.CLOSE, // mathematical right white tortoise shell bracket - '\u27EF': MO.CLOSE, // mathematical right flattened parenthesis - '\u2980': [0, 0, TEXCLASS.ORD, {fence: true, stretchy: true}], // triple vertical bar delimiter - '\u2984': MO.CLOSE, // right white curly bracket - '\u2986': MO.CLOSE, // right white parenthesis - '\u2988': MO.CLOSE, // z notation right image bracket - '\u298A': MO.CLOSE, // z notation right binding bracket - '\u298C': MO.CLOSE, // right square bracket with underbar - '\u298E': MO.CLOSE, // right square bracket with tick in bottom corner - '\u2990': MO.CLOSE, // right square bracket with tick in top corner - '\u2992': MO.CLOSE, // right angle bracket with dot - '\u2994': MO.CLOSE, // right arc greater-than bracket - '\u2996': MO.CLOSE, // double right arc less-than bracket - '\u2998': MO.CLOSE, // right black tortoise shell bracket - '\u29FD': MO.CLOSE, // right-pointing curved angle bracket + '!!': MO.BIN0, // multiple character operator: !! + '!': MO.CLOSE0, // exclamation mark + '"': MO.ORD, // quotation mark + '%': MO.ORD, // percent sign + '&': MO.ORD, // ampersand + '\'': MO.ACCENT, // apostrophe + ')': MO.CLOSE, // right parenthesis + '++': MO.BIN0, // multiple character operator: ++ + '--': MO.BIN0, // multiple character operator: -- + ']': MO.CLOSE, // right square bracket + '^': MO.WIDEACCENT, // circumflex accent + '_': MO.WIDEACCENT, // low line + '`': MO.ACCENT, // grave accent + '|': MO.CLOSE, // vertical line + '||': MO.BIN0, // multiple character operator: || + '}': MO.CLOSE, // right curly bracket + '~': MO.WIDEACCENT, // tilde + '\u00A8': MO.ACCENT, // diaeresis: \ddot + '\u00AF': MO.WIDEACCENT, // macron + '\u00B0': MO.ACCENT, // degree sign + '\u00B2': MO.ORD, // superscript two + '\u00B3': MO.ORD, // superscript three + '\u00B4': MO.ACCENT, // acute accent + '\u00B8': MO.ACCENT, // cedilla + '\u00B9': MO.ORD, // superscript one + '\u02C6': MO.WIDEACCENT, // modifier letter circumflex accent: \hat + '\u02C7': MO.WIDEACCENT, // caron: \check + '\u02C9': MO.WIDEACCENT, // modifier letter macron: \bar + '\u02CA': MO.ACCENT, // modifier letter acute accent: \acute + '\u02CB': MO.ACCENT, // modifier letter grave accent: \grave + '\u02CD': MO.WIDEACCENT, // modifier letter low macron + '\u02D8': MO.ACCENT, // breve: \breve + '\u02D9': MO.ACCENT, // dot above: \dot + '\u02DA': MO.ACCENT, // ring above + '\u02DC': MO.WIDEACCENT, // small tilde: \tilde + '\u02DD': MO.ACCENT, // double acute accent + '\u02F7': MO.WIDEACCENT, // modifier letter low tilde + '\u0302': MO.WIDEACCENT, // combining circumflex accent: \hat + '\u0311': MO.ACCENT, // combining inverted breve + '\u2016': MO.FENCE, // double vertical line + '\u2019': MO.CLOSE0, // right single quotation mark + '\u201A': MO.ORD, // single low-9 quotation mark + '\u201B': MO.ORD, // single high-reversed-9 quotation mark + '\u201D': MO.CLOSE0, // right double quotation mark + '\u201E': MO.ORD, // double low-9 quotation mark + '\u201F': MO.ORD, // double high-reversed-9 quotation mark + '\u2032': MO.ORD, // prime: \prime + '\u2033': MO.ORD, // double prime + '\u2034': MO.ORD, // triple prime + '\u2035': MO.ORD, // reversed prime + '\u2036': MO.ORD, // reversed double prime + '\u2037': MO.ORD, // reversed triple prime + '\u203E': MO.WIDEACCENT, // overline + '\u2057': MO.ORD, // quadruple prime + '\u20DB': MO.ACCENT, // combining three dots above + '\u20DC': MO.ACCENT, // combining four dots above + '\u2309': MO.CLOSE, // right ceiling: \rceil + '\u230B': MO.CLOSE, // right floor: \rfloor + '\u2322': MO.RELSTRETCH0, // frown: \frown + '\u2323': MO.RELSTRETCH0, // smile: \smile + '\u23B4': MO.WIDEACCENT, // top square bracket + '\u23B5': MO.WIDEACCENT, // bottom square bracket + '\u23CD': MO.ORD, // square foot + '\u23DC': MO.WIDEACCENT, // top parenthesis + '\u23DD': MO.WIDEACCENT, // bottom parenthesis + '\u23DE': MO.WIDEACCENT, // top curly bracket: \overbrace + '\u23DF': MO.WIDEACCENT, // bottom curly bracket: \underbrace + '\u23E0': MO.WIDEACCENT, // top tortoise shell bracket + '\u23E1': MO.WIDEACCENT, // bottom tortoise shell bracket + '\u2773': MO.CLOSE, // light right tortoise shell bracket ornament + '\u27E7': MO.CLOSE, // mathematical right white square bracket + '\u27E9': MO.CLOSE, // mathematical right angle bracket: \rangle + '\u27EB': MO.CLOSE, // mathematical right double angle bracket + '\u27ED': MO.CLOSE, // mathematical right white tortoise shell bracket + '\u27EF': MO.CLOSE, // mathematical right flattened parenthesis: \rgroup + '\u2980': MO.FENCE, // triple vertical bar delimiter + '\u2984': MO.CLOSE, // right white curly bracket + '\u2986': MO.CLOSE, // right white parenthesis + '\u2988': MO.CLOSE, // z notation right image bracket + '\u298A': MO.CLOSE, // z notation right binding bracket + '\u298C': MO.CLOSE, // right square bracket with underbar + '\u298E': MO.CLOSE, // right square bracket with tick in bottom corner + '\u2990': MO.CLOSE, // right square bracket with tick in top corner + '\u2992': MO.CLOSE, // right angle bracket with dot + '\u2994': MO.CLOSE, // right arc greater-than bracket + '\u2996': MO.CLOSE, // double right arc less-than bracket + '\u2998': MO.CLOSE, // right black tortoise shell bracket + '\u2999': MO.FENCE, // dotted fence + '\u29D9': MO.CLOSE, // right wiggly fence + '\u29DB': MO.CLOSE, // right double wiggly fence + '\u29FD': MO.CLOSE, // right-pointing curved angle bracket + '\u3009': MO.CLOSE, // right-pointing angle bracket + '\u{1EEF0}': MO.BINSTRETCH0, // arabic mathematical operator meem with hah with tatweel + '\u{1EEF1}': MO.BINSTRETCH0, // arabic mathematical operator hah with dal }, infix: { - '!=': MO.BIN4, // multiple character operator: != - '#': MO.ORD, // # - '$': MO.ORD, // $ - '%': [3, 3, TEXCLASS.ORD, null], // percent sign - '&&': MO.BIN4, // multiple character operator: && - '': MO.ORD, // empty - '*': MO.BIN3, // asterisk - '**': OPDEF(1, 1), // multiple character operator: ** - '*=': MO.BIN4, // multiple character operator: *= - '+': MO.BIN4, // plus sign - '+=': MO.BIN4, // multiple character operator: += - ',': [0, 3, TEXCLASS.PUNCT, {linebreakstyle: 'after', separator: true}], // comma - '-': MO.BIN4, // hyphen-minus - '-=': MO.BIN4, // multiple character operator: -= - '->': MO.BIN5, // multiple character operator: -> - '.': [0, 3, TEXCLASS.PUNCT, {linebreakstyle: 'after', separator: true}], // \ldotp - '/': MO.ORD11, // solidus - '//': OPDEF(1, 1), // multiple character operator: // - '/=': MO.BIN4, // multiple character operator: /= - ':': [1, 2, TEXCLASS.REL, null], // colon - ':=': MO.BIN4, // multiple character operator: := - ';': [0, 3, TEXCLASS.PUNCT, {linebreakstyle: 'after', separator: true}], // semicolon - '<': MO.REL, // less-than sign - '<=': MO.BIN5, // multiple character operator: <= - '<>': OPDEF(1, 1), // multiple character operator: <> - '=': MO.REL, // equals sign - '==': MO.BIN4, // multiple character operator: == - '>': MO.REL, // greater-than sign - '>=': MO.BIN5, // multiple character operator: >= - '?': [1, 1, TEXCLASS.CLOSE, null], // question mark - '@': MO.ORD11, // commercial at - '\\': MO.ORD, // reverse solidus - '^': MO.ORD11, // circumflex accent - '_': MO.ORD11, // low line - '|': [2, 2, TEXCLASS.ORD, {fence: true, stretchy: true, symmetric: true}], // vertical line - '||': [2, 2, TEXCLASS.BIN, {fence: true, stretchy: true, symmetric: true}], // multiple character operator: || - '|||': [2, 2, TEXCLASS.ORD, {fence: true, stretchy: true, symmetric: true}], // multiple character operator: ||| - '\u00B1': MO.BIN4, // plus-minus sign - '\u00B7': MO.BIN4, // middle dot - '\u00D7': MO.BIN4, // multiplication sign - '\u00F7': MO.BIN4, // division sign - '\u02B9': MO.ORD, // prime - '\u0300': MO.ACCENT, // \grave - '\u0301': MO.ACCENT, // \acute - '\u0303': MO.WIDEACCENT, // \tilde - '\u0304': MO.ACCENT, // \bar - '\u0306': MO.ACCENT, // \breve - '\u0307': MO.ACCENT, // \dot - '\u0308': MO.ACCENT, // \ddot - '\u030C': MO.ACCENT, // \check - '\u0332': MO.WIDEACCENT, // horizontal line - '\u0338': MO.REL4, // \not - '\u2015': [0, 0, TEXCLASS.ORD, {stretchy: true}], // horizontal line - '\u2017': [0, 0, TEXCLASS.ORD, {stretchy: true}], // horizontal line - '\u2020': MO.BIN3, // \dagger - '\u2021': MO.BIN3, // \ddagger - '\u2022': MO.BIN4, // bullet - '\u2026': MO.INNER, // horizontal ellipsis - '\u2043': MO.BIN4, // hyphen bullet - '\u2044': MO.TALLBIN, // fraction slash - '\u2061': MO.NONE, // function application - '\u2062': MO.NONE, // invisible times - '\u2063': [0, 0, TEXCLASS.NONE, {linebreakstyle: 'after', separator: true}], // invisible separator - '\u2064': MO.NONE, // invisible plus - '\u20D7': MO.ACCENT, // \vec - '\u2111': MO.ORD, // \Im - '\u2113': MO.ORD, // \ell - '\u2118': MO.ORD, // \wp - '\u211C': MO.ORD, // \Re - '\u2190': MO.WIDEREL, // leftwards arrow - '\u2191': MO.RELSTRETCH, // upwards arrow - '\u2192': MO.WIDEREL, // rightwards arrow - '\u2193': MO.RELSTRETCH, // downwards arrow - '\u2194': MO.WIDEREL, // left right arrow - '\u2195': MO.RELSTRETCH, // up down arrow - '\u2196': MO.RELSTRETCH, // north west arrow - '\u2197': MO.RELSTRETCH, // north east arrow - '\u2198': MO.RELSTRETCH, // south east arrow - '\u2199': MO.RELSTRETCH, // south west arrow - '\u219A': MO.RELACCENT, // leftwards arrow with stroke - '\u219B': MO.RELACCENT, // rightwards arrow with stroke - '\u219C': MO.WIDEREL, // leftwards wave arrow - '\u219D': MO.WIDEREL, // rightwards wave arrow - '\u219E': MO.WIDEREL, // leftwards two headed arrow - '\u219F': MO.WIDEREL, // upwards two headed arrow - '\u21A0': MO.WIDEREL, // rightwards two headed arrow - '\u21A1': MO.RELSTRETCH, // downwards two headed arrow - '\u21A2': MO.WIDEREL, // leftwards arrow with tail - '\u21A3': MO.WIDEREL, // rightwards arrow with tail - '\u21A4': MO.WIDEREL, // leftwards arrow from bar - '\u21A5': MO.RELSTRETCH, // upwards arrow from bar - '\u21A6': MO.WIDEREL, // rightwards arrow from bar - '\u21A7': MO.RELSTRETCH, // downwards arrow from bar - '\u21A8': MO.RELSTRETCH, // up down arrow with base - '\u21A9': MO.WIDEREL, // leftwards arrow with hook - '\u21AA': MO.WIDEREL, // rightwards arrow with hook - '\u21AB': MO.WIDEREL, // leftwards arrow with loop - '\u21AC': MO.WIDEREL, // rightwards arrow with loop - '\u21AD': MO.WIDEREL, // left right wave arrow - '\u21AE': MO.RELACCENT, // left right arrow with stroke - '\u21AF': MO.RELSTRETCH, // downwards zigzag arrow - '\u21B0': MO.RELSTRETCH, // upwards arrow with tip leftwards - '\u21B1': MO.RELSTRETCH, // upwards arrow with tip rightwards - '\u21B2': MO.RELSTRETCH, // downwards arrow with tip leftwards - '\u21B3': MO.RELSTRETCH, // downwards arrow with tip rightwards - '\u21B4': MO.RELSTRETCH, // rightwards arrow with corner downwards - '\u21B5': MO.RELSTRETCH, // downwards arrow with corner leftwards - '\u21B6': MO.RELACCENT, // anticlockwise top semicircle arrow - '\u21B7': MO.RELACCENT, // clockwise top semicircle arrow - '\u21B8': MO.REL, // north west arrow to long bar - '\u21B9': MO.WIDEREL, // leftwards arrow to bar over rightwards arrow to bar - '\u21BA': MO.REL, // anticlockwise open circle arrow - '\u21BB': MO.REL, // clockwise open circle arrow - '\u21BC': MO.WIDEREL, // leftwards harpoon with barb upwards - '\u21BD': MO.WIDEREL, // leftwards harpoon with barb downwards - '\u21BE': MO.RELSTRETCH, // upwards harpoon with barb rightwards - '\u21BF': MO.RELSTRETCH, // upwards harpoon with barb leftwards - '\u21C0': MO.WIDEREL, // rightwards harpoon with barb upwards - '\u21C1': MO.WIDEREL, // rightwards harpoon with barb downwards - '\u21C2': MO.RELSTRETCH, // downwards harpoon with barb rightwards - '\u21C3': MO.RELSTRETCH, // downwards harpoon with barb leftwards - '\u21C4': MO.WIDEREL, // rightwards arrow over leftwards arrow - '\u21C5': MO.RELSTRETCH, // upwards arrow leftwards of downwards arrow - '\u21C6': MO.WIDEREL, // leftwards arrow over rightwards arrow - '\u21C7': MO.WIDEREL, // leftwards paired arrows - '\u21C8': MO.RELSTRETCH, // upwards paired arrows - '\u21C9': MO.WIDEREL, // rightwards paired arrows - '\u21CA': MO.RELSTRETCH, // downwards paired arrows - '\u21CB': MO.WIDEREL, // leftwards harpoon over rightwards harpoon - '\u21CC': MO.WIDEREL, // rightwards harpoon over leftwards harpoon - '\u21CD': MO.RELACCENT, // leftwards double arrow with stroke - '\u21CE': MO.RELACCENT, // left right double arrow with stroke - '\u21CF': MO.RELACCENT, // rightwards double arrow with stroke - '\u21D0': MO.WIDEREL, // leftwards double arrow - '\u21D1': MO.RELSTRETCH, // upwards double arrow - '\u21D2': MO.WIDEREL, // rightwards double arrow - '\u21D3': MO.RELSTRETCH, // downwards double arrow - '\u21D4': MO.WIDEREL, // left right double arrow - '\u21D5': MO.RELSTRETCH, // up down double arrow - '\u21D6': MO.RELSTRETCH, // north west double arrow - '\u21D7': MO.RELSTRETCH, // north east double arrow - '\u21D8': MO.RELSTRETCH, // south east double arrow - '\u21D9': MO.RELSTRETCH, // south west double arrow - '\u21DA': MO.WIDEREL, // leftwards triple arrow - '\u21DB': MO.WIDEREL, // rightwards triple arrow - '\u21DC': MO.WIDEREL, // leftwards squiggle arrow - '\u21DD': MO.WIDEREL, // rightwards squiggle arrow - '\u21DE': MO.REL, // upwards arrow with double stroke - '\u21DF': MO.REL, // downwards arrow with double stroke - '\u21E0': MO.WIDEREL, // leftwards dashed arrow - '\u21E1': MO.RELSTRETCH, // upwards dashed arrow - '\u21E2': MO.WIDEREL, // rightwards dashed arrow - '\u21E3': MO.RELSTRETCH, // downwards dashed arrow - '\u21E4': MO.WIDEREL, // leftwards arrow to bar - '\u21E5': MO.WIDEREL, // rightwards arrow to bar - '\u21E6': MO.WIDEREL, // leftwards white arrow - '\u21E7': MO.RELSTRETCH, // upwards white arrow - '\u21E8': MO.WIDEREL, // rightwards white arrow - '\u21E9': MO.RELSTRETCH, // downwards white arrow - '\u21EA': MO.RELSTRETCH, // upwards white arrow from bar - '\u21EB': MO.RELSTRETCH, // upwards white arrow on pedestal - '\u21EC': MO.RELSTRETCH, // upwards white arrow on pedestal with horizontal bar - '\u21ED': MO.RELSTRETCH, // upwards white arrow on pedestal with vertical bar - '\u21EE': MO.RELSTRETCH, // upwards white double arrow - '\u21EF': MO.RELSTRETCH, // upwards white double arrow on pedestal - '\u21F0': MO.WIDEREL, // rightwards white arrow from wall - '\u21F1': MO.REL, // north west arrow to corner - '\u21F2': MO.REL, // south east arrow to corner - '\u21F3': MO.RELSTRETCH, // up down white arrow - '\u21F4': MO.RELACCENT, // right arrow with small circle - '\u21F5': MO.RELSTRETCH, // downwards arrow leftwards of upwards arrow - '\u21F6': MO.WIDEREL, // three rightwards arrows - '\u21F7': MO.RELACCENT, // leftwards arrow with vertical stroke - '\u21F8': MO.RELACCENT, // rightwards arrow with vertical stroke - '\u21F9': MO.RELACCENT, // left right arrow with vertical stroke - '\u21FA': MO.RELACCENT, // leftwards arrow with double vertical stroke - '\u21FB': MO.RELACCENT, // rightwards arrow with double vertical stroke - '\u21FC': MO.RELACCENT, // left right arrow with double vertical stroke - '\u21FD': MO.WIDEREL, // leftwards open-headed arrow - '\u21FE': MO.WIDEREL, // rightwards open-headed arrow - '\u21FF': MO.WIDEREL, // left right open-headed arrow - '\u2201': OPDEF(1, 2, TEXCLASS.ORD), // complement - '\u2205': MO.ORD, // \emptyset - '\u2206': MO.BIN3, // increment - '\u2208': MO.REL, // element of - '\u2209': MO.REL, // not an element of - '\u220A': MO.REL, // small element of - '\u220B': MO.REL, // contains as member - '\u220C': MO.REL, // does not contain as member - '\u220D': MO.REL, // small contains as member - '\u220E': MO.BIN3, // end of proof - '\u2212': MO.BIN4, // minus sign - '\u2213': MO.BIN4, // minus-or-plus sign - '\u2214': MO.BIN4, // dot plus - '\u2215': MO.TALLBIN, // division slash - '\u2216': MO.BIN4, // set minus - '\u2217': MO.BIN4, // asterisk operator - '\u2218': MO.BIN4, // ring operator - '\u2219': MO.BIN4, // bullet operator - '\u221D': MO.REL, // proportional to - '\u221E': MO.ORD, // \infty - '\u221F': MO.REL, // right angle - '\u2223': MO.REL, // divides - '\u2224': MO.REL, // does not divide - '\u2225': MO.REL, // parallel to - '\u2226': MO.REL, // not parallel to - '\u2227': MO.BIN4, // logical and - '\u2228': MO.BIN4, // logical or - '\u2229': MO.BIN4, // intersection - '\u222A': MO.BIN4, // union - '\u2234': MO.REL, // therefore - '\u2235': MO.REL, // because - '\u2236': MO.REL, // ratio - '\u2237': MO.REL, // proportion - '\u2238': MO.BIN4, // dot minus - '\u2239': MO.REL, // excess - '\u223A': MO.BIN4, // geometric proportion - '\u223B': MO.REL, // homothetic - '\u223C': MO.REL, // tilde operator - '\u223D': MO.REL, // reversed tilde - '\u223D\u0331': MO.BIN3, // reversed tilde with underline - '\u223E': MO.REL, // inverted lazy s - '\u223F': MO.BIN3, // sine wave - '\u2240': MO.BIN4, // wreath product - '\u2241': MO.REL, // not tilde - '\u2242': MO.REL, // minus tilde - '\u2242\u0338': MO.REL, // minus tilde with slash - '\u2243': MO.REL, // asymptotically equal to - '\u2244': MO.REL, // not asymptotically equal to - '\u2245': MO.REL, // approximately equal to - '\u2246': MO.REL, // approximately but not actually equal to - '\u2247': MO.REL, // neither approximately nor actually equal to - '\u2248': MO.REL, // almost equal to - '\u2249': MO.REL, // not almost equal to - '\u224A': MO.REL, // almost equal or equal to - '\u224B': MO.REL, // triple tilde - '\u224C': MO.REL, // all equal to - '\u224D': MO.REL, // equivalent to - '\u224E': MO.REL, // geometrically equivalent to - '\u224E\u0338': MO.REL, // geometrically equivalent to with slash - '\u224F': MO.REL, // difference between - '\u224F\u0338': MO.REL, // difference between with slash - '\u2250': MO.REL, // approaches the limit - '\u2251': MO.REL, // geometrically equal to - '\u2252': MO.REL, // approximately equal to or the image of - '\u2253': MO.REL, // image of or approximately equal to - '\u2254': MO.REL, // colon equals - '\u2255': MO.REL, // equals colon - '\u2256': MO.REL, // ring in equal to - '\u2257': MO.REL, // ring equal to - '\u2258': MO.REL, // corresponds to - '\u2259': MO.REL, // estimates - '\u225A': MO.REL, // equiangular to - '\u225B': MO.REL, // star equals - '\u225C': MO.REL, // delta equal to - '\u225D': MO.REL, // equal to by definition - '\u225E': MO.REL, // measured by - '\u225F': MO.REL, // questioned equal to - '\u2260': MO.REL, // not equal to - '\u2261': MO.REL, // identical to - '\u2262': MO.REL, // not identical to - '\u2263': MO.REL, // strictly equivalent to - '\u2264': MO.REL, // less-than or equal to - '\u2265': MO.REL, // greater-than or equal to - '\u2266': MO.REL, // less-than over equal to - '\u2266\u0338': MO.REL, // less-than over equal to with slash - '\u2267': MO.REL, // greater-than over equal to - '\u2268': MO.REL, // less-than but not equal to - '\u2269': MO.REL, // greater-than but not equal to - '\u226A': MO.REL, // much less-than - '\u226A\u0338': MO.REL, // much less than with slash - '\u226B': MO.REL, // much greater-than - '\u226B\u0338': MO.REL, // much greater than with slash - '\u226C': MO.REL, // between - '\u226D': MO.REL, // not equivalent to - '\u226E': MO.REL, // not less-than - '\u226F': MO.REL, // not greater-than - '\u2270': MO.REL, // neither less-than nor equal to - '\u2271': MO.REL, // neither greater-than nor equal to - '\u2272': MO.REL, // less-than or equivalent to - '\u2273': MO.REL, // greater-than or equivalent to - '\u2274': MO.REL, // neither less-than nor equivalent to - '\u2275': MO.REL, // neither greater-than nor equivalent to - '\u2276': MO.REL, // less-than or greater-than - '\u2277': MO.REL, // greater-than or less-than - '\u2278': MO.REL, // neither less-than nor greater-than - '\u2279': MO.REL, // neither greater-than nor less-than - '\u227A': MO.REL, // precedes - '\u227B': MO.REL, // succeeds - '\u227C': MO.REL, // precedes or equal to - '\u227D': MO.REL, // succeeds or equal to - '\u227E': MO.REL, // precedes or equivalent to - '\u227F': MO.REL, // succeeds or equivalent to - '\u227F\u0338': MO.REL, // succeeds or equivalent to with slash - '\u2280': MO.REL, // does not precede - '\u2281': MO.REL, // does not succeed - '\u2282': MO.REL, // subset of - '\u2282\u20D2': MO.REL, // subset of with vertical line - '\u2283': MO.REL, // superset of - '\u2283\u20D2': MO.REL, // superset of with vertical line - '\u2284': MO.REL, // not a subset of - '\u2285': MO.REL, // not a superset of - '\u2286': MO.REL, // subset of or equal to - '\u2287': MO.REL, // superset of or equal to - '\u2288': MO.REL, // neither a subset of nor equal to - '\u2289': MO.REL, // neither a superset of nor equal to - '\u228A': MO.REL, // subset of with not equal to - '\u228B': MO.REL, // superset of with not equal to - '\u228C': MO.BIN4, // multiset - '\u228D': MO.BIN4, // multiset multiplication - '\u228E': MO.BIN4, // multiset union - '\u228F': MO.REL, // square image of - '\u228F\u0338': MO.REL, // square image of with slash - '\u2290': MO.REL, // square original of - '\u2290\u0338': MO.REL, // square original of with slash - '\u2291': MO.REL, // square image of or equal to - '\u2292': MO.REL, // square original of or equal to - '\u2293': MO.BIN4, // square cap - '\u2294': MO.BIN4, // square cup - '\u2295': MO.BIN4, // circled plus - '\u2296': MO.BIN4, // circled minus - '\u2297': MO.BIN4, // circled times - '\u2298': MO.BIN4, // circled division slash - '\u2299': MO.BIN4, // circled dot operator - '\u229A': MO.BIN4, // circled ring operator - '\u229B': MO.BIN4, // circled asterisk operator - '\u229C': MO.BIN4, // circled equals - '\u229D': MO.BIN4, // circled dash - '\u229E': MO.BIN4, // squared plus - '\u229F': MO.BIN4, // squared minus - '\u22A0': MO.BIN4, // squared times - '\u22A1': MO.BIN4, // squared dot operator - '\u22A2': MO.REL, // right tack - '\u22A3': MO.REL, // left tack - '\u22A4': MO.ORD55, // down tack - '\u22A5': MO.REL, // up tack - '\u22A6': MO.REL, // assertion - '\u22A7': MO.REL, // models - '\u22A8': MO.REL, // true - '\u22A9': MO.REL, // forces - '\u22AA': MO.REL, // triple vertical bar right turnstile - '\u22AB': MO.REL, // double vertical bar double right turnstile - '\u22AC': MO.REL, // does not prove - '\u22AD': MO.REL, // not true - '\u22AE': MO.REL, // does not force - '\u22AF': MO.REL, // negated double vertical bar double right turnstile - '\u22B0': MO.REL, // precedes under relation - '\u22B1': MO.REL, // succeeds under relation - '\u22B2': MO.REL, // normal subgroup of - '\u22B3': MO.REL, // contains as normal subgroup - '\u22B4': MO.REL, // normal subgroup of or equal to - '\u22B5': MO.REL, // contains as normal subgroup or equal to - '\u22B6': MO.REL, // original of - '\u22B7': MO.REL, // image of - '\u22B8': MO.REL, // multimap - '\u22B9': MO.REL, // hermitian conjugate matrix - '\u22BA': MO.BIN4, // intercalate - '\u22BB': MO.BIN4, // xor - '\u22BC': MO.BIN4, // nand - '\u22BD': MO.BIN4, // nor - '\u22BE': MO.BIN3, // right angle with arc - '\u22BF': MO.BIN3, // right triangle - '\u22C4': MO.BIN4, // diamond operator - '\u22C5': MO.BIN4, // dot operator - '\u22C6': MO.BIN4, // star operator - '\u22C7': MO.BIN4, // division times - '\u22C8': MO.REL, // bowtie - '\u22C9': MO.BIN4, // left normal factor semidirect product - '\u22CA': MO.BIN4, // right normal factor semidirect product - '\u22CB': MO.BIN4, // left semidirect product - '\u22CC': MO.BIN4, // right semidirect product - '\u22CD': MO.REL, // reversed tilde equals - '\u22CE': MO.BIN4, // curly logical or - '\u22CF': MO.BIN4, // curly logical and - '\u22D0': MO.REL, // double subset - '\u22D1': MO.REL, // double superset - '\u22D2': MO.BIN4, // double intersection - '\u22D3': MO.BIN4, // double union - '\u22D4': MO.REL, // pitchfork - '\u22D5': MO.REL, // equal and parallel to - '\u22D6': MO.REL, // less-than with dot - '\u22D7': MO.REL, // greater-than with dot - '\u22D8': MO.REL, // very much less-than - '\u22D9': MO.REL, // very much greater-than - '\u22DA': MO.REL, // less-than equal to or greater-than - '\u22DB': MO.REL, // greater-than equal to or less-than - '\u22DC': MO.REL, // equal to or less-than - '\u22DD': MO.REL, // equal to or greater-than - '\u22DE': MO.REL, // equal to or precedes - '\u22DF': MO.REL, // equal to or succeeds - '\u22E0': MO.REL, // does not precede or equal - '\u22E1': MO.REL, // does not succeed or equal - '\u22E2': MO.REL, // not square image of or equal to - '\u22E3': MO.REL, // not square original of or equal to - '\u22E4': MO.REL, // square image of or not equal to - '\u22E5': MO.REL, // square original of or not equal to - '\u22E6': MO.REL, // less-than but not equivalent to - '\u22E7': MO.REL, // greater-than but not equivalent to - '\u22E8': MO.REL, // precedes but not equivalent to - '\u22E9': MO.REL, // succeeds but not equivalent to - '\u22EA': MO.REL, // not normal subgroup of - '\u22EB': MO.REL, // does not contain as normal subgroup - '\u22EC': MO.REL, // not normal subgroup of or equal to - '\u22ED': MO.REL, // does not contain as normal subgroup or equal - '\u22EE': MO.ORD55, // vertical ellipsis - '\u22EF': MO.INNER, // midline horizontal ellipsis - '\u22F0': MO.REL, // up right diagonal ellipsis - '\u22F1': [5, 5, TEXCLASS.INNER, null], // down right diagonal ellipsis - '\u22F2': MO.REL, // element of with long horizontal stroke - '\u22F3': MO.REL, // element of with vertical bar at end of horizontal stroke - '\u22F4': MO.REL, // small element of with vertical bar at end of horizontal stroke - '\u22F5': MO.REL, // element of with dot above - '\u22F6': MO.REL, // element of with overbar - '\u22F7': MO.REL, // small element of with overbar - '\u22F8': MO.REL, // element of with underbar - '\u22F9': MO.REL, // element of with two horizontal strokes - '\u22FA': MO.REL, // contains with long horizontal stroke - '\u22FB': MO.REL, // contains with vertical bar at end of horizontal stroke - '\u22FC': MO.REL, // small contains with vertical bar at end of horizontal stroke - '\u22FD': MO.REL, // contains with overbar - '\u22FE': MO.REL, // small contains with overbar - '\u22FF': MO.REL, // z notation bag membership - '\u2305': MO.BIN3, // barwedge - '\u2306': MO.BIN3, // doublebarwedge - '\u2322': MO.REL4, // \frown - '\u2323': MO.REL4, // \smile - '\u2329': MO.OPEN, // langle - '\u232A': MO.CLOSE, // rangle - '\u23AA': MO.ORD, // \bracevert - '\u23AF': [0, 0, TEXCLASS.ORD, {stretchy: true}], // \underline - '\u23B0': MO.OPEN, // \lmoustache - '\u23B1': MO.CLOSE, // \rmoustache - '\u2500': MO.ORD, // horizontal line - '\u25B3': MO.BIN4, // white up-pointing triangle - '\u25B5': MO.BIN4, // white up-pointing small triangle - '\u25B9': MO.BIN4, // white right-pointing small triangle - '\u25BD': MO.BIN4, // white down-pointing triangle - '\u25BF': MO.BIN4, // white down-pointing small triangle - '\u25C3': MO.BIN4, // white left-pointing small triangle - '\u25EF': MO.BIN3, // \bigcirc - '\u2660': MO.ORD, // \spadesuit - '\u2661': MO.ORD, // \heartsuit - '\u2662': MO.ORD, // \diamondsuit - '\u2663': MO.ORD, // \clubsuit - '\u2758': MO.REL, // light vertical bar - '\u27F0': MO.RELSTRETCH, // upwards quadruple arrow - '\u27F1': MO.RELSTRETCH, // downwards quadruple arrow - '\u27F5': MO.WIDEREL, // long leftwards arrow - '\u27F6': MO.WIDEREL, // long rightwards arrow - '\u27F7': MO.WIDEREL, // long left right arrow - '\u27F8': MO.WIDEREL, // long leftwards double arrow - '\u27F9': MO.WIDEREL, // long rightwards double arrow - '\u27FA': MO.WIDEREL, // long left right double arrow - '\u27FB': MO.WIDEREL, // long leftwards arrow from bar - '\u27FC': MO.WIDEREL, // long rightwards arrow from bar - '\u27FD': MO.WIDEREL, // long leftwards double arrow from bar - '\u27FE': MO.WIDEREL, // long rightwards double arrow from bar - '\u27FF': MO.WIDEREL, // long rightwards squiggle arrow - '\u2900': MO.RELACCENT, // rightwards two-headed arrow with vertical stroke - '\u2901': MO.RELACCENT, // rightwards two-headed arrow with double vertical stroke - '\u2902': MO.RELACCENT, // leftwards double arrow with vertical stroke - '\u2903': MO.RELACCENT, // rightwards double arrow with vertical stroke - '\u2904': MO.RELACCENT, // left right double arrow with vertical stroke - '\u2905': MO.RELACCENT, // rightwards two-headed arrow from bar - '\u2906': MO.RELACCENT, // leftwards double arrow from bar - '\u2907': MO.RELACCENT, // rightwards double arrow from bar - '\u2908': MO.REL, // downwards arrow with horizontal stroke - '\u2909': MO.REL, // upwards arrow with horizontal stroke - '\u290A': MO.RELSTRETCH, // upwards triple arrow - '\u290B': MO.RELSTRETCH, // downwards triple arrow - '\u290C': MO.WIDEREL, // leftwards double dash arrow - '\u290D': MO.WIDEREL, // rightwards double dash arrow - '\u290E': MO.WIDEREL, // leftwards triple dash arrow - '\u290F': MO.WIDEREL, // rightwards triple dash arrow - '\u2910': MO.WIDEREL, // rightwards two-headed triple dash arrow - '\u2911': MO.RELACCENT, // rightwards arrow with dotted stem - '\u2912': MO.RELSTRETCH, // upwards arrow to bar - '\u2913': MO.RELSTRETCH, // downwards arrow to bar - '\u2914': MO.RELACCENT, // rightwards arrow with tail with vertical stroke - '\u2915': MO.RELACCENT, // rightwards arrow with tail with double vertical stroke - '\u2916': MO.RELACCENT, // rightwards two-headed arrow with tail - '\u2917': MO.RELACCENT, // rightwards two-headed arrow with tail with vertical stroke - '\u2918': MO.RELACCENT, // rightwards two-headed arrow with tail with double vertical stroke - '\u2919': MO.RELACCENT, // leftwards arrow-tail - '\u291A': MO.RELACCENT, // rightwards arrow-tail - '\u291B': MO.RELACCENT, // leftwards double arrow-tail - '\u291C': MO.RELACCENT, // rightwards double arrow-tail - '\u291D': MO.RELACCENT, // leftwards arrow to black diamond - '\u291E': MO.RELACCENT, // rightwards arrow to black diamond - '\u291F': MO.RELACCENT, // leftwards arrow from bar to black diamond - '\u2920': MO.RELACCENT, // rightwards arrow from bar to black diamond - '\u2921': MO.RELSTRETCH, // north west and south east arrow - '\u2922': MO.RELSTRETCH, // north east and south west arrow - '\u2923': MO.REL, // north west arrow with hook - '\u2924': MO.REL, // north east arrow with hook - '\u2925': MO.REL, // south east arrow with hook - '\u2926': MO.REL, // south west arrow with hook - '\u2927': MO.REL, // north west arrow and north east arrow - '\u2928': MO.REL, // north east arrow and south east arrow - '\u2929': MO.REL, // south east arrow and south west arrow - '\u292A': MO.REL, // south west arrow and north west arrow - '\u292B': MO.REL, // rising diagonal crossing falling diagonal - '\u292C': MO.REL, // falling diagonal crossing rising diagonal - '\u292D': MO.REL, // south east arrow crossing north east arrow - '\u292E': MO.REL, // north east arrow crossing south east arrow - '\u292F': MO.REL, // falling diagonal crossing north east arrow - '\u2930': MO.REL, // rising diagonal crossing south east arrow - '\u2931': MO.REL, // north east arrow crossing north west arrow - '\u2932': MO.REL, // north west arrow crossing north east arrow - '\u2933': MO.RELACCENT, // wave arrow pointing directly right - '\u2934': MO.REL, // arrow pointing rightwards then curving upwards - '\u2935': MO.REL, // arrow pointing rightwards then curving downwards - '\u2936': MO.REL, // arrow pointing downwards then curving leftwards - '\u2937': MO.REL, // arrow pointing downwards then curving rightwards - '\u2938': MO.REL, // right-side arc clockwise arrow - '\u2939': MO.REL, // left-side arc anticlockwise arrow - '\u293A': MO.RELACCENT, // top arc anticlockwise arrow - '\u293B': MO.RELACCENT, // bottom arc anticlockwise arrow - '\u293C': MO.RELACCENT, // top arc clockwise arrow with minus - '\u293D': MO.RELACCENT, // top arc anticlockwise arrow with plus - '\u293E': MO.REL, // lower right semicircular clockwise arrow - '\u293F': MO.REL, // lower left semicircular anticlockwise arrow - '\u2940': MO.REL, // anticlockwise closed circle arrow - '\u2941': MO.REL, // clockwise closed circle arrow - '\u2942': MO.RELACCENT, // rightwards arrow above short leftwards arrow - '\u2943': MO.RELACCENT, // leftwards arrow above short rightwards arrow - '\u2944': MO.RELACCENT, // short rightwards arrow above leftwards arrow - '\u2945': MO.RELACCENT, // rightwards arrow with plus below - '\u2946': MO.RELACCENT, // leftwards arrow with plus below - '\u2947': MO.RELACCENT, // rightwards arrow through x - '\u2948': MO.RELACCENT, // left right arrow through small circle - '\u2949': MO.REL, // upwards two-headed arrow from small circle - '\u294A': MO.RELACCENT, // left barb up right barb down harpoon - '\u294B': MO.RELACCENT, // left barb down right barb up harpoon - '\u294C': MO.REL, // up barb right down barb left harpoon - '\u294D': MO.REL, // up barb left down barb right harpoon - '\u294E': MO.WIDEREL, // left barb up right barb up harpoon - '\u294F': MO.RELSTRETCH, // up barb right down barb right harpoon - '\u2950': MO.WIDEREL, // left barb down right barb down harpoon - '\u2951': MO.RELSTRETCH, // up barb left down barb left harpoon - '\u2952': MO.WIDEREL, // leftwards harpoon with barb up to bar - '\u2953': MO.WIDEREL, // rightwards harpoon with barb up to bar - '\u2954': MO.RELSTRETCH, // upwards harpoon with barb right to bar - '\u2955': MO.RELSTRETCH, // downwards harpoon with barb right to bar - '\u2956': MO.RELSTRETCH, // leftwards harpoon with barb down to bar - '\u2957': MO.RELSTRETCH, // rightwards harpoon with barb down to bar - '\u2958': MO.RELSTRETCH, // upwards harpoon with barb left to bar - '\u2959': MO.RELSTRETCH, // downwards harpoon with barb left to bar - '\u295A': MO.WIDEREL, // leftwards harpoon with barb up from bar - '\u295B': MO.WIDEREL, // rightwards harpoon with barb up from bar - '\u295C': MO.RELSTRETCH, // upwards harpoon with barb right from bar - '\u295D': MO.RELSTRETCH, // downwards harpoon with barb right from bar - '\u295E': MO.WIDEREL, // leftwards harpoon with barb down from bar - '\u295F': MO.WIDEREL, // rightwards harpoon with barb down from bar - '\u2960': MO.RELSTRETCH, // upwards harpoon with barb left from bar - '\u2961': MO.RELSTRETCH, // downwards harpoon with barb left from bar - '\u2962': MO.RELACCENT, // leftwards harpoon with barb up above leftwards harpoon with barb down - '\u2963': MO.REL, // upwards harpoon with barb left beside upwards harpoon with barb right - '\u2964': MO.RELACCENT, // rightwards harpoon with barb up above rightwards harpoon with barb down - '\u2965': MO.REL, // downwards harpoon with barb left beside downwards harpoon with barb right - '\u2966': MO.RELACCENT, // leftwards harpoon with barb up above rightwards harpoon with barb up - '\u2967': MO.RELACCENT, // leftwards harpoon with barb down above rightwards harpoon with barb down - '\u2968': MO.RELACCENT, // rightwards harpoon with barb up above leftwards harpoon with barb up - '\u2969': MO.RELACCENT, // rightwards harpoon with barb down above leftwards harpoon with barb down - '\u296A': MO.RELACCENT, // leftwards harpoon with barb up above long dash - '\u296B': MO.RELACCENT, // leftwards harpoon with barb down below long dash - '\u296C': MO.RELACCENT, // rightwards harpoon with barb up above long dash - '\u296D': MO.RELACCENT, // rightwards harpoon with barb down below long dash - '\u296E': MO.RELSTRETCH, // upwards harpoon with barb left beside downwards harpoon with barb right - '\u296F': MO.RELSTRETCH, // downwards harpoon with barb left beside upwards harpoon with barb right - '\u2970': MO.RELACCENT, // right double arrow with rounded head - '\u2971': MO.RELACCENT, // equals sign above rightwards arrow - '\u2972': MO.RELACCENT, // tilde operator above rightwards arrow - '\u2973': MO.RELACCENT, // leftwards arrow above tilde operator - '\u2974': MO.RELACCENT, // rightwards arrow above tilde operator - '\u2975': MO.RELACCENT, // rightwards arrow above almost equal to - '\u2976': MO.RELACCENT, // less-than above leftwards arrow - '\u2977': MO.RELACCENT, // leftwards arrow through less-than - '\u2978': MO.RELACCENT, // greater-than above rightwards arrow - '\u2979': MO.RELACCENT, // subset above rightwards arrow - '\u297A': MO.RELACCENT, // leftwards arrow through subset - '\u297B': MO.RELACCENT, // superset above leftwards arrow - '\u297C': MO.RELACCENT, // left fish tail - '\u297D': MO.RELACCENT, // right fish tail - '\u297E': MO.REL, // up fish tail - '\u297F': MO.REL, // down fish tail - '\u2981': MO.BIN3, // z notation spot - '\u2982': MO.BIN3, // z notation type colon - '\u2999': MO.BIN3, // dotted fence - '\u299A': MO.BIN3, // vertical zigzag line - '\u299B': MO.BIN3, // measured angle opening left - '\u299C': MO.BIN3, // right angle variant with square - '\u299D': MO.BIN3, // measured right angle with dot - '\u299E': MO.BIN3, // angle with s inside - '\u299F': MO.BIN3, // acute angle - '\u29A0': MO.BIN3, // spherical angle opening left - '\u29A1': MO.BIN3, // spherical angle opening up - '\u29A2': MO.BIN3, // turned angle - '\u29A3': MO.BIN3, // reversed angle - '\u29A4': MO.BIN3, // angle with underbar - '\u29A5': MO.BIN3, // reversed angle with underbar - '\u29A6': MO.BIN3, // oblique angle opening up - '\u29A7': MO.BIN3, // oblique angle opening down - '\u29A8': MO.BIN3, // measured angle with open arm ending in arrow pointing up and right - '\u29A9': MO.BIN3, // measured angle with open arm ending in arrow pointing up and left - '\u29AA': MO.BIN3, // measured angle with open arm ending in arrow pointing down and right - '\u29AB': MO.BIN3, // measured angle with open arm ending in arrow pointing down and left - '\u29AC': MO.BIN3, // measured angle with open arm ending in arrow pointing right and up - '\u29AD': MO.BIN3, // measured angle with open arm ending in arrow pointing left and up - '\u29AE': MO.BIN3, // measured angle with open arm ending in arrow pointing right and down - '\u29AF': MO.BIN3, // measured angle with open arm ending in arrow pointing left and down - '\u29B0': MO.BIN3, // reversed empty set - '\u29B1': MO.BIN3, // empty set with overbar - '\u29B2': MO.BIN3, // empty set with small circle above - '\u29B3': MO.BIN3, // empty set with right arrow above - '\u29B4': MO.BIN3, // empty set with left arrow above - '\u29B5': MO.BIN3, // circle with horizontal bar - '\u29B6': MO.BIN4, // circled vertical bar - '\u29B7': MO.BIN4, // circled parallel - '\u29B8': MO.BIN4, // circled reverse solidus - '\u29B9': MO.BIN4, // circled perpendicular - '\u29BA': MO.BIN4, // circle divided by horizontal bar and top half divided by vertical bar - '\u29BB': MO.BIN4, // circle with superimposed x - '\u29BC': MO.BIN4, // circled anticlockwise-rotated division sign - '\u29BD': MO.BIN4, // up arrow through circle - '\u29BE': MO.BIN4, // circled white bullet - '\u29BF': MO.BIN4, // circled bullet - '\u29C0': MO.REL, // circled less-than - '\u29C1': MO.REL, // circled greater-than - '\u29C2': MO.BIN3, // circle with small circle to the right - '\u29C3': MO.BIN3, // circle with two horizontal strokes to the right - '\u29C4': MO.BIN4, // squared rising diagonal slash - '\u29C5': MO.BIN4, // squared falling diagonal slash - '\u29C6': MO.BIN4, // squared asterisk - '\u29C7': MO.BIN4, // squared small circle - '\u29C8': MO.BIN4, // squared square - '\u29C9': MO.BIN3, // two joined squares - '\u29CA': MO.BIN3, // triangle with dot above - '\u29CB': MO.BIN3, // triangle with underbar - '\u29CC': MO.BIN3, // s in triangle - '\u29CD': MO.BIN3, // triangle with serifs at bottom - '\u29CE': MO.REL, // right triangle above left triangle - '\u29CF': MO.REL, // left triangle beside vertical bar - '\u29CF\u0338': MO.REL, // left triangle beside vertical bar with slash - '\u29D0': MO.REL, // vertical bar beside right triangle - '\u29D0\u0338': MO.REL, // vertical bar beside right triangle with slash - '\u29D1': MO.REL, // bowtie with left half black - '\u29D2': MO.REL, // bowtie with right half black - '\u29D3': MO.REL, // black bowtie - '\u29D4': MO.REL, // times with left half black - '\u29D5': MO.REL, // times with right half black - '\u29D6': MO.BIN4, // white hourglass - '\u29D7': MO.BIN4, // black hourglass - '\u29D8': MO.BIN3, // left wiggly fence - '\u29D9': MO.BIN3, // right wiggly fence - '\u29DB': MO.BIN3, // right double wiggly fence - '\u29DC': MO.BIN3, // incomplete infinity - '\u29DD': MO.BIN3, // tie over infinity - '\u29DE': MO.REL, // infinity negated with vertical bar - '\u29DF': MO.BIN3, // double-ended multimap - '\u29E0': MO.BIN3, // square with contoured outline - '\u29E1': MO.REL, // increases as - '\u29E2': MO.BIN4, // shuffle product - '\u29E3': MO.REL, // equals sign and slanted parallel - '\u29E4': MO.REL, // equals sign and slanted parallel with tilde above - '\u29E5': MO.REL, // identical to and slanted parallel - '\u29E6': MO.REL, // gleich stark - '\u29E7': MO.BIN3, // thermodynamic - '\u29E8': MO.BIN3, // down-pointing triangle with left half black - '\u29E9': MO.BIN3, // down-pointing triangle with right half black - '\u29EA': MO.BIN3, // black diamond with down arrow - '\u29EB': MO.BIN3, // black lozenge - '\u29EC': MO.BIN3, // white circle with down arrow - '\u29ED': MO.BIN3, // black circle with down arrow - '\u29EE': MO.BIN3, // error-barred white square - '\u29EF': MO.BIN3, // error-barred black square - '\u29F0': MO.BIN3, // error-barred white diamond - '\u29F1': MO.BIN3, // error-barred black diamond - '\u29F2': MO.BIN3, // error-barred white circle - '\u29F3': MO.BIN3, // error-barred black circle - '\u29F4': MO.REL, // rule-delayed - '\u29F5': MO.BIN4, // reverse solidus operator - '\u29F6': MO.BIN4, // solidus with overbar - '\u29F7': MO.BIN4, // reverse solidus with horizontal stroke - '\u29F8': MO.BIN3, // big solidus - '\u29F9': MO.BIN3, // big reverse solidus - '\u29FA': MO.BIN3, // double plus - '\u29FB': MO.BIN3, // triple plus - '\u29FE': MO.BIN4, // tiny - '\u29FF': MO.BIN4, // miny - '\u2A1D': MO.BIN3, // join - '\u2A1E': MO.BIN3, // large left triangle operator - '\u2A1F': MO.BIN3, // z notation schema composition - '\u2A20': MO.BIN3, // z notation schema piping - '\u2A21': MO.BIN3, // z notation schema projection - '\u2A22': MO.BIN4, // plus sign with small circle above - '\u2A23': MO.BIN4, // plus sign with circumflex accent above - '\u2A24': MO.BIN4, // plus sign with tilde above - '\u2A25': MO.BIN4, // plus sign with dot below - '\u2A26': MO.BIN4, // plus sign with tilde below - '\u2A27': MO.BIN4, // plus sign with subscript two - '\u2A28': MO.BIN4, // plus sign with black triangle - '\u2A29': MO.BIN4, // minus sign with comma above - '\u2A2A': MO.BIN4, // minus sign with dot below - '\u2A2B': MO.BIN4, // minus sign with falling dots - '\u2A2C': MO.BIN4, // minus sign with rising dots - '\u2A2D': MO.BIN4, // plus sign in left half circle - '\u2A2E': MO.BIN4, // plus sign in right half circle - '\u2A2F': MO.BIN4, // vector or cross product - '\u2A30': MO.BIN4, // multiplication sign with dot above - '\u2A31': MO.BIN4, // multiplication sign with underbar - '\u2A32': MO.BIN4, // semidirect product with bottom closed - '\u2A33': MO.BIN4, // smash product - '\u2A34': MO.BIN4, // multiplication sign in left half circle - '\u2A35': MO.BIN4, // multiplication sign in right half circle - '\u2A36': MO.BIN4, // circled multiplication sign with circumflex accent - '\u2A37': MO.BIN4, // multiplication sign in double circle - '\u2A38': MO.BIN4, // circled division sign - '\u2A39': MO.BIN4, // plus sign in triangle - '\u2A3A': MO.BIN4, // minus sign in triangle - '\u2A3B': MO.BIN4, // multiplication sign in triangle - '\u2A3C': MO.BIN4, // interior product - '\u2A3D': MO.BIN4, // righthand interior product - '\u2A3E': MO.BIN4, // z notation relational composition - '\u2A3F': MO.BIN4, // amalgamation or coproduct - '\u2A40': MO.BIN4, // intersection with dot - '\u2A41': MO.BIN4, // union with minus sign - '\u2A42': MO.BIN4, // union with overbar - '\u2A43': MO.BIN4, // intersection with overbar - '\u2A44': MO.BIN4, // intersection with logical and - '\u2A45': MO.BIN4, // union with logical or - '\u2A46': MO.BIN4, // union above intersection - '\u2A47': MO.BIN4, // intersection above union - '\u2A48': MO.BIN4, // union above bar above intersection - '\u2A49': MO.BIN4, // intersection above bar above union - '\u2A4A': MO.BIN4, // union beside and joined with union - '\u2A4B': MO.BIN4, // intersection beside and joined with intersection - '\u2A4C': MO.BIN4, // closed union with serifs - '\u2A4D': MO.BIN4, // closed intersection with serifs - '\u2A4E': MO.BIN4, // double square intersection - '\u2A4F': MO.BIN4, // double square union - '\u2A50': MO.BIN4, // closed union with serifs and smash product - '\u2A51': MO.BIN4, // logical and with dot above - '\u2A52': MO.BIN4, // logical or with dot above - '\u2A53': MO.BIN4, // double logical and - '\u2A54': MO.BIN4, // double logical or - '\u2A55': MO.BIN4, // two intersecting logical and - '\u2A56': MO.BIN4, // two intersecting logical or - '\u2A57': MO.BIN4, // sloping large or - '\u2A58': MO.BIN4, // sloping large and - '\u2A59': MO.REL, // logical or overlapping logical and - '\u2A5A': MO.BIN4, // logical and with middle stem - '\u2A5B': MO.BIN4, // logical or with middle stem - '\u2A5C': MO.BIN4, // logical and with horizontal dash - '\u2A5D': MO.BIN4, // logical or with horizontal dash - '\u2A5E': MO.BIN4, // logical and with double overbar - '\u2A5F': MO.BIN4, // logical and with underbar - '\u2A60': MO.BIN4, // logical and with double underbar - '\u2A61': MO.BIN4, // small vee with underbar - '\u2A62': MO.BIN4, // logical or with double overbar - '\u2A63': MO.BIN4, // logical or with double underbar - '\u2A64': MO.BIN4, // z notation domain antirestriction - '\u2A65': MO.BIN4, // z notation range antirestriction - '\u2A66': MO.REL, // equals sign with dot below - '\u2A67': MO.REL, // identical with dot above - '\u2A68': MO.REL, // triple horizontal bar with double vertical stroke - '\u2A69': MO.REL, // triple horizontal bar with triple vertical stroke - '\u2A6A': MO.REL, // tilde operator with dot above - '\u2A6B': MO.REL, // tilde operator with rising dots - '\u2A6C': MO.REL, // similar minus similar - '\u2A6D': MO.REL, // congruent with dot above - '\u2A6E': MO.REL, // equals with asterisk - '\u2A6F': MO.REL, // almost equal to with circumflex accent - '\u2A70': MO.REL, // approximately equal or equal to - '\u2A71': MO.BIN4, // equals sign above plus sign - '\u2A72': MO.BIN4, // plus sign above equals sign - '\u2A73': MO.REL, // equals sign above tilde operator - '\u2A74': MO.REL, // double colon equal - '\u2A75': MO.REL, // two consecutive equals signs - '\u2A76': MO.REL, // three consecutive equals signs - '\u2A77': MO.REL, // equals sign with two dots above and two dots below - '\u2A78': MO.REL, // equivalent with four dots above - '\u2A79': MO.REL, // less-than with circle inside - '\u2A7A': MO.REL, // greater-than with circle inside - '\u2A7B': MO.REL, // less-than with question mark above - '\u2A7C': MO.REL, // greater-than with question mark above - '\u2A7D': MO.REL, // less-than or slanted equal to - '\u2A7D\u0338': MO.REL, // less-than or slanted equal to with slash - '\u2A7E': MO.REL, // greater-than or slanted equal to - '\u2A7E\u0338': MO.REL, // greater-than or slanted equal to with slash - '\u2A7F': MO.REL, // less-than or slanted equal to with dot inside - '\u2A80': MO.REL, // greater-than or slanted equal to with dot inside - '\u2A81': MO.REL, // less-than or slanted equal to with dot above - '\u2A82': MO.REL, // greater-than or slanted equal to with dot above - '\u2A83': MO.REL, // less-than or slanted equal to with dot above right - '\u2A84': MO.REL, // greater-than or slanted equal to with dot above left - '\u2A85': MO.REL, // less-than or approximate - '\u2A86': MO.REL, // greater-than or approximate - '\u2A87': MO.REL, // less-than and single-line not equal to - '\u2A88': MO.REL, // greater-than and single-line not equal to - '\u2A89': MO.REL, // less-than and not approximate - '\u2A8A': MO.REL, // greater-than and not approximate - '\u2A8B': MO.REL, // less-than above double-line equal above greater-than - '\u2A8C': MO.REL, // greater-than above double-line equal above less-than - '\u2A8D': MO.REL, // less-than above similar or equal - '\u2A8E': MO.REL, // greater-than above similar or equal - '\u2A8F': MO.REL, // less-than above similar above greater-than - '\u2A90': MO.REL, // greater-than above similar above less-than - '\u2A91': MO.REL, // less-than above greater-than above double-line equal - '\u2A92': MO.REL, // greater-than above less-than above double-line equal - '\u2A93': MO.REL, // less-than above slanted equal above greater-than above slanted equal - '\u2A94': MO.REL, // greater-than above slanted equal above less-than above slanted equal - '\u2A95': MO.REL, // slanted equal to or less-than - '\u2A96': MO.REL, // slanted equal to or greater-than - '\u2A97': MO.REL, // slanted equal to or less-than with dot inside - '\u2A98': MO.REL, // slanted equal to or greater-than with dot inside - '\u2A99': MO.REL, // double-line equal to or less-than - '\u2A9A': MO.REL, // double-line equal to or greater-than - '\u2A9B': MO.REL, // double-line slanted equal to or less-than - '\u2A9C': MO.REL, // double-line slanted equal to or greater-than - '\u2A9D': MO.REL, // similar or less-than - '\u2A9E': MO.REL, // similar or greater-than - '\u2A9F': MO.REL, // similar above less-than above equals sign - '\u2AA0': MO.REL, // similar above greater-than above equals sign - '\u2AA1': MO.REL, // double nested less-than - '\u2AA1\u0338': MO.REL, // double nested less-than with slash - '\u2AA2': MO.REL, // double nested greater-than - '\u2AA2\u0338': MO.REL, // double nested greater-than with slash - '\u2AA3': MO.REL, // double nested less-than with underbar - '\u2AA4': MO.REL, // greater-than overlapping less-than - '\u2AA5': MO.REL, // greater-than beside less-than - '\u2AA6': MO.REL, // less-than closed by curve - '\u2AA7': MO.REL, // greater-than closed by curve - '\u2AA8': MO.REL, // less-than closed by curve above slanted equal - '\u2AA9': MO.REL, // greater-than closed by curve above slanted equal - '\u2AAA': MO.REL, // smaller than - '\u2AAB': MO.REL, // larger than - '\u2AAC': MO.REL, // smaller than or equal to - '\u2AAD': MO.REL, // larger than or equal to - '\u2AAE': MO.REL, // equals sign with bumpy above - '\u2AAF': MO.REL, // precedes above single-line equals sign - '\u2AAF\u0338': MO.REL, // precedes above single-line equals sign with slash - '\u2AB0': MO.REL, // succeeds above single-line equals sign - '\u2AB0\u0338': MO.REL, // succeeds above single-line equals sign with slash - '\u2AB1': MO.REL, // precedes above single-line not equal to - '\u2AB2': MO.REL, // succeeds above single-line not equal to - '\u2AB3': MO.REL, // precedes above equals sign - '\u2AB4': MO.REL, // succeeds above equals sign - '\u2AB5': MO.REL, // precedes above not equal to - '\u2AB6': MO.REL, // succeeds above not equal to - '\u2AB7': MO.REL, // precedes above almost equal to - '\u2AB8': MO.REL, // succeeds above almost equal to - '\u2AB9': MO.REL, // precedes above not almost equal to - '\u2ABA': MO.REL, // succeeds above not almost equal to - '\u2ABB': MO.REL, // double precedes - '\u2ABC': MO.REL, // double succeeds - '\u2ABD': MO.REL, // subset with dot - '\u2ABE': MO.REL, // superset with dot - '\u2ABF': MO.REL, // subset with plus sign below - '\u2AC0': MO.REL, // superset with plus sign below - '\u2AC1': MO.REL, // subset with multiplication sign below - '\u2AC2': MO.REL, // superset with multiplication sign below - '\u2AC3': MO.REL, // subset of or equal to with dot above - '\u2AC4': MO.REL, // superset of or equal to with dot above - '\u2AC5': MO.REL, // subset of above equals sign - '\u2AC6': MO.REL, // superset of above equals sign - '\u2AC7': MO.REL, // subset of above tilde operator - '\u2AC8': MO.REL, // superset of above tilde operator - '\u2AC9': MO.REL, // subset of above almost equal to - '\u2ACA': MO.REL, // superset of above almost equal to - '\u2ACB': MO.REL, // subset of above not equal to - '\u2ACC': MO.REL, // superset of above not equal to - '\u2ACD': MO.REL, // square left open box operator - '\u2ACE': MO.REL, // square right open box operator - '\u2ACF': MO.REL, // closed subset - '\u2AD0': MO.REL, // closed superset - '\u2AD1': MO.REL, // closed subset or equal to - '\u2AD2': MO.REL, // closed superset or equal to - '\u2AD3': MO.REL, // subset above superset - '\u2AD4': MO.REL, // superset above subset - '\u2AD5': MO.REL, // subset above subset - '\u2AD6': MO.REL, // superset above superset - '\u2AD7': MO.REL, // superset beside subset - '\u2AD8': MO.REL, // superset beside and joined by dash with subset - '\u2AD9': MO.REL, // element of opening downwards - '\u2ADA': MO.REL, // pitchfork with tee top - '\u2ADB': MO.REL, // transversal intersection - '\u2ADD': MO.REL, // nonforking - '\u2ADD\u0338': MO.REL, // nonforking with slash - '\u2ADE': MO.REL, // short left tack - '\u2ADF': MO.REL, // short down tack - '\u2AE0': MO.REL, // short up tack - '\u2AE1': MO.REL, // perpendicular with s - '\u2AE2': MO.REL, // vertical bar triple right turnstile - '\u2AE3': MO.REL, // double vertical bar left turnstile - '\u2AE4': MO.REL, // vertical bar double left turnstile - '\u2AE5': MO.REL, // double vertical bar double left turnstile - '\u2AE6': MO.REL, // long dash from left member of double vertical - '\u2AE7': MO.REL, // short down tack with overbar - '\u2AE8': MO.REL, // short up tack with underbar - '\u2AE9': MO.REL, // short up tack above short down tack - '\u2AEA': MO.REL, // double down tack - '\u2AEB': MO.REL, // double up tack - '\u2AEC': MO.REL, // double stroke not sign - '\u2AED': MO.REL, // reversed double stroke not sign - '\u2AEE': MO.REL, // does not divide with reversed negation slash - '\u2AEF': MO.REL, // vertical line with circle above - '\u2AF0': MO.REL, // vertical line with circle below - '\u2AF1': MO.REL, // down tack with circle below - '\u2AF2': MO.REL, // parallel with horizontal stroke - '\u2AF3': MO.REL, // parallel with tilde operator - '\u2AF4': MO.BIN4, // triple vertical bar binary relation - '\u2AF5': MO.BIN4, // triple vertical bar with horizontal stroke - '\u2AF6': MO.BIN4, // triple colon operator - '\u2AF7': MO.REL, // triple nested less-than - '\u2AF8': MO.REL, // triple nested greater-than - '\u2AF9': MO.REL, // double-line slanted less-than or equal to - '\u2AFA': MO.REL, // double-line slanted greater-than or equal to - '\u2AFB': MO.BIN4, // triple solidus binary relation - '\u2AFD': MO.BIN4, // double solidus operator - '\u2AFE': MO.BIN3, // white vertical bar - '\u2B45': MO.RELSTRETCH, // leftwards quadruple arrow - '\u2B46': MO.RELSTRETCH, // rightwards quadruple arrow - '\u3008': MO.OPEN, // langle - '\u3009': MO.CLOSE, // rangle - '\uFE37': MO.WIDEACCENT, // horizontal brace down - '\uFE38': MO.WIDEACCENT, // horizontal brace up - } + '!': MO.ORD, // ! + '!=': MO.BIN5, // multiple character operator: != + '#': MO.ORD, // # + '$': MO.ORD, // $ + '%': MO.ORD3, // percent sign + '&&': MO.BIN4, // multiple character operator: && + '**': MO.BIN3, // multiple character operator: ** + '*': MO.BIN3, // asterisk + '*=': MO.BIN5, // multiple character operator: *= + '+': MO.BIN4, // plus sign + '+=': MO.BIN5, // multiple character operator: += + ',': MO.PUNCT03, // comma + '': MO.ORD, // empty + '-': MO.BIN4, // hyphen-minus + '-=': MO.BIN5, // multiple character operator: -= + '->': MO.BIN5, // multiple character operator: -> + '.': MO.ORD3, // full stop + '..': MO.BIN3, // .. + '...': MO.INNER, // ... + '/': [4, 4, TEXCLASS.ORD, {}], // solidus + '//': MO.BIN5, // multiple character operator: // + '/=': MO.BIN5, // multiple character operator: /= + ':': [0, 3, TEXCLASS.REL, {}], // colon + ':=': MO.BIN5, // multiple character operator: := + ';': MO.PUNCT03, // semicolon + '<': MO.REL, // less-than sign + '<=': MO.REL, // multiple character operator: <= + '<>': [3, 3, TEXCLASS.REL, {}], // multiple character operator: <> + '=': MO.REL, // equals sign + '==': MO.REL, // multiple character operator: == + '>': MO.REL, // greater-than sign + '>=': MO.REL, // multiple character operator: >= + '?': [3, 3, TEXCLASS.CLOSE, {fence: true}], // question mark + '@': MO.ORD3, // commercial at + '\\': MO.ORD, // reverse solidus: \backslash + '^': [3, 3, TEXCLASS.ORD, {accent: true, stretchy: true}], // circumflex accent + '_': MO.WIDEACCENT, // low line + '|': [5, 5, TEXCLASS.ORD, {}], // vertical line + '||': MO.BIN5, // multiple character operator: || + '\u00B1': MO.BIN4, // plus-minus sign: \pm + '\u00B7': MO.BIN3, // middle dot + '\u00D7': MO.BIN3, // multiplication sign: \times + '\u00F7': MO.BIN4, // division sign: \div + '\u02B9': MO.ORD, // prime + '\u0300': MO.ACCENT, // \grave + '\u0301': MO.ACCENT, // \acute + '\u0303': MO.WIDEACCENT, // \tilde + '\u0304': MO.ACCENT, // \bar + '\u0306': MO.ACCENT, // \breve + '\u0307': MO.ACCENT, // \dot + '\u0308': MO.ACCENT, // \ddot + '\u030C': MO.ACCENT, // \check + '\u0332': MO.WIDEACCENT, // horizontal line + '\u0338': MO.REL, // \not + '\u03F6': MO.REL, // contains + '\u2015': MO.ORDSTRETCH0, // horizontal line + '\u2017': MO.ORDSTRETCH0, // horizontal line + '\u2020': MO.BIN3, // \dagger + '\u2021': MO.BIN3, // \ddagger + '\u2022': MO.BIN3, // bullet + '\u2026': MO.INNER, // \ldots + '\u2043': MO.BIN3, // hyphen bullet + '\u2044': MO.STRETCH4, // fraction slash + '\u2061': MO.NONE, // function application + '\u2062': MO.NONE, // invisible times + '\u2063': [0, 0, TEXCLASS.NONE, {linebreakstyle: 'after'}], // invisible separator + '\u2064': MO.NONE, // invisible plus + '\u20D7': MO.ACCENT, // \vec + '\u2111': MO.ORD, // \Im + '\u2113': MO.ORD, // \ell + '\u2118': MO.ORD, // \wp + '\u211C': MO.ORD, // \Re + '\u2190': MO.WIDEREL, // leftwards arrow: \leftarrow + '\u2191': MO.RELSTRETCH, // upwards arrow: \uparrow + '\u2192': MO.WIDEREL, // rightwards arrow: \rightarrow + '\u2193': MO.RELSTRETCH, // downwards arrow: \downarrow + '\u2194': MO.WIDEREL, // left right arrow: \leftrightarrow + '\u2195': MO.RELSTRETCH, // up down arrow: \updownarrow + '\u2196': MO.REL, // north west arrow: \nwarrow + '\u2197': MO.REL, // north east arrow: \nearrow + '\u2198': MO.REL, // south east arrow: \searrow + '\u2199': MO.REL, // south west arrow: \swarrow + '\u219A': MO.WIDEREL, // leftwards arrow with stroke + '\u219B': MO.WIDEREL, // rightwards arrow with stroke + '\u219C': MO.WIDEREL, // leftwards wave arrow + '\u219D': MO.WIDEREL, // rightwards wave arrow + '\u219E': MO.WIDEREL, // leftwards two headed arrow + '\u219F': MO.RELSTRETCH, // upwards two headed arrow + '\u21A0': MO.WIDEREL, // rightwards two headed arrow + '\u21A1': MO.RELSTRETCH, // downwards two headed arrow + '\u21A2': MO.WIDEREL, // leftwards arrow with tail + '\u21A3': MO.WIDEREL, // rightwards arrow with tail + '\u21A4': MO.WIDEREL, // leftwards arrow from bar + '\u21A5': MO.RELSTRETCH, // upwards arrow from bar + '\u21A6': MO.WIDEREL, // rightwards arrow from bar: \mapsto + '\u21A7': MO.RELSTRETCH, // downwards arrow from bar + '\u21A8': MO.RELSTRETCH, // up down arrow with base + '\u21A9': MO.WIDEREL, // leftwards arrow with hook: \hookleftarrow + '\u21AA': MO.WIDEREL, // rightwards arrow with hook: \hookrightarrow + '\u21AB': MO.WIDEREL, // leftwards arrow with loop + '\u21AC': MO.WIDEREL, // rightwards arrow with loop + '\u21AD': MO.WIDEREL, // left right wave arrow + '\u21AE': MO.WIDEREL, // left right arrow with stroke + '\u21AF': MO.REL, // downwards zigzag arrow + '\u21B0': MO.RELSTRETCH, // upwards arrow with tip leftwards + '\u21B1': MO.RELSTRETCH, // upwards arrow with tip rightwards + '\u21B2': MO.RELSTRETCH, // downwards arrow with tip leftwards + '\u21B3': MO.RELSTRETCH, // downwards arrow with tip rightwards + '\u21B4': MO.RELSTRETCH, // rightwards arrow with corner downwards + '\u21B5': MO.RELSTRETCH, // downwards arrow with corner leftwards + '\u21B6': MO.REL, // anticlockwise top semicircle arrow + '\u21B7': MO.REL, // clockwise top semicircle arrow + '\u21B8': MO.REL, // north west arrow to long bar + '\u21B9': MO.WIDEREL, // leftwards arrow to bar over rightwards arrow to bar + '\u21BA': MO.REL, // anticlockwise open circle arrow + '\u21BB': MO.REL, // clockwise open circle arrow + '\u21BC': MO.WIDEREL, // leftwards harpoon with barb upwards: \leftharpoonup + '\u21BD': MO.WIDEREL, // leftwards harpoon with barb downwards: \leftharpoondown + '\u21BE': MO.RELSTRETCH, // upwards harpoon with barb rightwards + '\u21BF': MO.RELSTRETCH, // upwards harpoon with barb leftwards + '\u21C0': MO.WIDEREL, // rightwards harpoon with barb upwards: \rightharpoonup + '\u21C1': MO.WIDEREL, // rightwards harpoon with barb downwards: \rightharpoondown + '\u21C2': MO.RELSTRETCH, // downwards harpoon with barb rightwards + '\u21C3': MO.RELSTRETCH, // downwards harpoon with barb leftwards + '\u21C4': MO.WIDEREL, // rightwards arrow over leftwards arrow + '\u21C5': MO.RELSTRETCH, // upwards arrow leftwards of downwards arrow + '\u21C6': MO.WIDEREL, // leftwards arrow over rightwards arrow + '\u21C7': MO.WIDEREL, // leftwards paired arrows + '\u21C8': MO.RELSTRETCH, // upwards paired arrows + '\u21C9': MO.WIDEREL, // rightwards paired arrows + '\u21CA': MO.RELSTRETCH, // downwards paired arrows + '\u21CB': MO.WIDEREL, // leftwards harpoon over rightwards harpoon + '\u21CC': MO.WIDEREL, // rightwards harpoon over leftwards harpoon: \rightleftharpoons + '\u21CD': MO.WIDEREL, // leftwards double arrow with stroke + '\u21CE': MO.WIDEREL, // left right double arrow with stroke + '\u21CF': MO.WIDEREL, // rightwards double arrow with stroke + '\u21D0': MO.WIDEREL, // leftwards double arrow: \Leftarrow + '\u21D1': MO.RELSTRETCH, // upwards double arrow: \Uparrow + '\u21D2': MO.WIDEREL, // rightwards double arrow: \Rightarrow + '\u21D3': MO.RELSTRETCH, // downwards double arrow: \Downarrow + '\u21D4': MO.WIDEREL, // left right double arrow: \Leftrightarrow + '\u21D5': MO.RELSTRETCH, // up down double arrow: \Updownarrow + '\u21D6': MO.REL, // north west double arrow + '\u21D7': MO.REL, // north east double arrow + '\u21D8': MO.REL, // south east double arrow + '\u21D9': MO.REL, // south west double arrow + '\u21DA': MO.WIDEREL, // leftwards triple arrow + '\u21DB': MO.WIDEREL, // rightwards triple arrow + '\u21DC': MO.WIDEREL, // leftwards squiggle arrow + '\u21DD': MO.WIDEREL, // rightwards squiggle arrow + '\u21DE': MO.RELSTRETCH, // upwards arrow with double stroke + '\u21DF': MO.RELSTRETCH, // downwards arrow with double stroke + '\u21E0': MO.WIDEREL, // leftwards dashed arrow + '\u21E1': MO.RELSTRETCH, // upwards dashed arrow + '\u21E2': MO.WIDEREL, // rightwards dashed arrow + '\u21E3': MO.RELSTRETCH, // downwards dashed arrow + '\u21E4': MO.WIDEREL, // leftwards arrow to bar + '\u21E5': MO.WIDEREL, // rightwards arrow to bar + '\u21E6': MO.WIDEREL, // leftwards white arrow + '\u21E7': MO.RELSTRETCH, // upwards white arrow + '\u21E8': MO.WIDEREL, // rightwards white arrow + '\u21E9': MO.RELSTRETCH, // downwards white arrow + '\u21EA': MO.RELSTRETCH, // upwards white arrow from bar + '\u21EB': MO.RELSTRETCH, // upwards white arrow on pedestal + '\u21EC': MO.RELSTRETCH, // upwards white arrow on pedestal with horizontal bar + '\u21ED': MO.RELSTRETCH, // upwards white arrow on pedestal with vertical bar + '\u21EE': MO.RELSTRETCH, // upwards white double arrow + '\u21EF': MO.RELSTRETCH, // upwards white double arrow on pedestal + '\u21F0': MO.WIDEREL, // rightwards white arrow from wall + '\u21F1': MO.REL, // north west arrow to corner + '\u21F2': MO.REL, // south east arrow to corner + '\u21F3': MO.RELSTRETCH, // up down white arrow + '\u21F4': MO.WIDEREL, // right arrow with small circle + '\u21F5': MO.RELSTRETCH, // downwards arrow leftwards of upwards arrow + '\u21F6': MO.WIDEREL, // three rightwards arrows + '\u21F7': MO.WIDEREL, // leftwards arrow with vertical stroke + '\u21F8': MO.WIDEREL, // rightwards arrow with vertical stroke + '\u21F9': MO.WIDEREL, // left right arrow with vertical stroke + '\u21FA': MO.WIDEREL, // leftwards arrow with double vertical stroke + '\u21FB': MO.WIDEREL, // rightwards arrow with double vertical stroke + '\u21FC': MO.WIDEREL, // left right arrow with double vertical stroke + '\u21FD': MO.WIDEREL, // leftwards open-headed arrow + '\u21FE': MO.WIDEREL, // rightwards open-headed arrow + '\u21FF': MO.WIDEREL, // left right open-headed arrow + '\u2205': MO.ORD, // \emptyset + '\u2206': MO.ORD, // increment + '\u2208': MO.REL, // element of: \in + '\u2209': MO.REL, // not an element of: \notin + '\u220A': MO.REL, // small element of + '\u220B': MO.REL, // contains as member: \owns + '\u220C': MO.REL, // does not contain as member + '\u220D': MO.REL, // small contains as member + '\u2212': MO.BIN4, // minus sign + '\u2213': MO.BIN4, // minus-or-plus sign: \mp + '\u2214': MO.BIN4, // dot plus + '\u2215': MO.STRETCH4, // division slash + '\u2216': MO.BIN4, // set minus: \setminus + '\u2217': MO.BIN3, // asterisk operator: \ast + '\u2218': MO.BIN3, // ring operator: \circ + '\u2219': MO.BIN3, // bullet operator: \bullet + '\u221D': MO.REL, // proportional to: \propto + '\u221E': MO.ORD, // \infty + '\u2223': MO.REL, // divides: \mid + '\u2224': MO.REL, // does not divide + '\u2225': MO.REL, // parallel to: \parallel + '\u2226': MO.REL, // not parallel to + '\u2227': MO.BIN4, // logical and: \wedge + '\u2228': MO.BIN4, // logical or: \vee + '\u2229': MO.BIN4, // intersection: \cap + '\u222A': MO.BIN4, // union: \cup + '\u2236': MO.BIN4, // ratio + '\u2237': MO.REL, // proportion + '\u2238': MO.BIN4, // dot minus + '\u2239': MO.REL, // excess + '\u223A': MO.REL, // geometric proportion + '\u223B': MO.REL, // homothetic + '\u223C': MO.REL, // tilde operator: \sim + '\u223D': MO.REL, // reversed tilde + '\u223E': MO.REL, // inverted lazy s + '\u2240': MO.BIN3, // wreath product: \wr + '\u2241': MO.REL, // not tilde + '\u2242': MO.REL, // minus tilde + '\u2242\u0338': MO.REL, // not minus tilde + '\u2243': MO.REL, // asymptotically equal to: \simeq + '\u2244': MO.REL, // not asymptotically equal to + '\u2245': MO.REL, // approximately equal to: \cong + '\u2246': MO.REL, // approximately but not actually equal to + '\u2247': MO.REL, // neither approximately nor actually equal to + '\u2248': MO.REL, // almost equal to: \approx + '\u2249': MO.REL, // not almost equal to + '\u224A': MO.REL, // almost equal or equal to + '\u224B': MO.REL, // triple tilde + '\u224C': MO.REL, // all equal to + '\u224D': MO.REL, // equivalent to: \asymp + '\u224E': MO.REL, // geometrically equivalent to + '\u224F': MO.REL, // difference between + '\u2250': MO.REL, // approaches the limit: \doteq + '\u2251': MO.REL, // geometrically equal to + '\u2252': MO.REL, // approximately equal to or the image of + '\u2253': MO.REL, // image of or approximately equal to + '\u2254': MO.REL, // colon equals + '\u2255': MO.REL, // equals colon + '\u2256': MO.REL, // ring in equal to + '\u2257': MO.REL, // ring equal to + '\u2258': MO.REL, // corresponds to + '\u2259': MO.REL, // estimates + '\u225A': MO.REL, // equiangular to + '\u225B': MO.REL, // star equals + '\u225C': MO.REL, // delta equal to + '\u225D': MO.REL, // equal to by definition + '\u225E': MO.REL, // measured by + '\u225F': MO.REL, // questioned equal to + '\u2260': MO.REL, // not equal to: \ne + '\u2261': MO.REL, // identical to: \equiv + '\u2262': MO.REL, // not identical to + '\u2263': MO.REL, // strictly equivalent to + '\u2264': MO.REL, // less-than or equal to: \leq + '\u2265': MO.REL, // greater-than or equal to: \geq + '\u2266': MO.REL, // less-than over equal to + '\u2266\u0338': MO.REL, // not less-htan over equal to + '\u2267': MO.REL, // greater-than over equal to + '\u2267\u0338': MO.REL, // not greater-than over equal to + '\u2268': MO.REL, // less-than but not equal to + '\u2269': MO.REL, // greater-than but not equal to + '\u226A': MO.REL, // much less-than: \ll + '\u226A\u0338': MO.REL, // not much less-than + '\u226B': MO.REL, // much greater-than: \gg + '\u226B\u0338': MO.REL, // not much greater-than + '\u226C': MO.REL, // between + '\u226D': MO.REL, // not equivalent to + '\u226E': MO.REL, // not less-than + '\u226F': MO.REL, // not greater-than + '\u2270': MO.REL, // neither less-than nor equal to + '\u2271': MO.REL, // neither greater-than nor equal to + '\u2272': MO.REL, // less-than or equivalent to + '\u2273': MO.REL, // greater-than or equivalent to + '\u2274': MO.REL, // neither less-than nor equivalent to + '\u2275': MO.REL, // neither greater-than nor equivalent to + '\u2276': MO.REL, // less-than or greater-than + '\u2277': MO.REL, // greater-than or less-than + '\u2278': MO.REL, // neither less-than nor greater-than + '\u2279': MO.REL, // neither greater-than nor less-than + '\u227A': MO.REL, // precedes: \prec + '\u227B': MO.REL, // succeeds: \succ + '\u227C': MO.REL, // precedes or equal to + '\u227D': MO.REL, // succeeds or equal to + '\u227E': MO.REL, // precedes or equivalent to + '\u227E\u0338': MO.REL, // not precedes or equivalent to + '\u227F': MO.REL, // succeeds or equivalent to + '\u227F\u0338': MO.REL, // not succeeds or equivalent to + '\u2280': MO.REL, // does not precede + '\u2281': MO.REL, // does not succeed + '\u2282': MO.REL, // subset of: \subset + '\u2283': MO.REL, // superset of: \supset + '\u2284': MO.REL, // not a subset of + '\u2285': MO.REL, // not a superset of + '\u2286': MO.REL, // subset of or equal to: \subseteq + '\u2287': MO.REL, // superset of or equal to: \supseteq + '\u2288': MO.REL, // neither a subset of nor equal to + '\u2289': MO.REL, // neither a superset of nor equal to + '\u228A': MO.REL, // subset of with not equal to + '\u228B': MO.REL, // superset of with not equal to + '\u228C': MO.BIN4, // multiset + '\u228D': MO.BIN4, // multiset multiplication + '\u228E': MO.BIN4, // multiset union: \uplus + '\u228F': MO.REL, // square image of + '\u228F\u0338': MO.REL, // not square image of + '\u2290': MO.REL, // square original of + '\u2290\u0338': MO.REL, // not square original of + '\u2291': MO.REL, // square image of or equal to: \sqsubseteq + '\u2292': MO.REL, // square original of or equal to: \sqsupseteq + '\u2293': MO.BIN4, // square cap: \sqcap + '\u2294': MO.BIN4, // square cup: \sqcup + '\u2295': MO.BIN4, // circled plus: \oplus + '\u2296': MO.BIN4, // circled minus: \ominus + '\u2297': MO.BIN3, // circled times: \otimes + '\u2298': MO.BIN4, // circled division slash: \oslash + '\u2299': MO.BIN3, // circled dot operator: \odot + '\u229A': MO.BIN3, // circled ring operator + '\u229B': MO.BIN3, // circled asterisk operator + '\u229C': MO.REL, // circled equals + '\u229D': MO.BIN4, // circled dash + '\u229E': MO.BIN4, // squared plus + '\u229F': MO.BIN4, // squared minus + '\u22A0': MO.BIN3, // squared times + '\u22A1': MO.BIN3, // squared dot operator + '\u22A2': MO.REL, // right tack: \vdash + '\u22A3': MO.REL, // left tack: \dashv + '\u22A4': MO.ORD, // \top + '\u22A5': MO.ORD, // \bot + '\u22A6': MO.REL, // assertion + '\u22A7': MO.REL, // models + '\u22A8': MO.REL, // true: \models + '\u22A9': MO.REL, // forces + '\u22AA': MO.REL, // triple vertical bar right turnstile + '\u22AB': MO.REL, // double vertical bar double right turnstile + '\u22AC': MO.REL, // does not prove + '\u22AD': MO.REL, // not true + '\u22AE': MO.REL, // does not force + '\u22AF': MO.REL, // negated double vertical bar double right turnstile + '\u22B0': MO.REL, // precedes under relation + '\u22B1': MO.REL, // succeeds under relation + '\u22B2': MO.REL, // normal subgroup of + '\u22B3': MO.REL, // contains as normal subgroup + '\u22B4': MO.REL, // normal subgroup of or equal to + '\u22B5': MO.REL, // contains as normal subgroup or equal to + '\u22B6': MO.REL, // original of + '\u22B7': MO.REL, // image of + '\u22B8': MO.REL, // multimap + '\u22BA': MO.BIN3, // intercalate + '\u22BB': MO.BIN4, // xor + '\u22BC': MO.BIN4, // nand + '\u22BD': MO.BIN4, // nor + '\u22C4': MO.BIN3, // diamond operator: \diamond + '\u22C5': MO.BIN3, // dot operator: \cdot + '\u22C6': MO.BIN3, // star operator: \star + '\u22C7': MO.BIN3, // division times + '\u22C8': MO.REL, // bowtie: \bowtie + '\u22C9': MO.BIN3, // left normal factor semidirect product + '\u22CA': MO.BIN3, // right normal factor semidirect product + '\u22CB': MO.BIN3, // left semidirect product + '\u22CC': MO.BIN3, // right semidirect product + '\u22CD': MO.REL, // reversed tilde equals + '\u22CE': MO.BIN4, // curly logical or + '\u22CF': MO.BIN4, // curly logical and + '\u22D0': MO.REL, // double subset + '\u22D1': MO.REL, // double superset + '\u22D2': MO.BIN4, // double intersection + '\u22D3': MO.BIN4, // double union + '\u22D4': MO.REL, // pitchfork + '\u22D5': MO.REL, // equal and parallel to + '\u22D6': MO.REL, // less-than with dot + '\u22D7': MO.REL, // greater-than with dot + '\u22D8': MO.REL, // very much less-than + '\u22D9': MO.REL, // very much greater-than + '\u22DA': MO.REL, // less-than equal to or greater-than + '\u22DB': MO.REL, // greater-than equal to or less-than + '\u22DC': MO.REL, // equal to or less-than + '\u22DD': MO.REL, // equal to or greater-than + '\u22DE': MO.REL, // equal to or precedes + '\u22DF': MO.REL, // equal to or succeeds + '\u22E0': MO.REL, // does not precede or equal + '\u22E1': MO.REL, // does not succeed or equal + '\u22E2': MO.REL, // not square image of or equal to + '\u22E3': MO.REL, // not square original of or equal to + '\u22E4': MO.REL, // square image of or not equal to + '\u22E5': MO.REL, // square original of or not equal to + '\u22E6': MO.REL, // less-than but not equivalent to + '\u22E7': MO.REL, // greater-than but not equivalent to + '\u22E8': MO.REL, // precedes but not equivalent to + '\u22E9': MO.REL, // succeeds but not equivalent to + '\u22EA': MO.REL, // not normal subgroup of + '\u22EB': MO.REL, // does not contain as normal subgroup + '\u22EC': MO.REL, // not normal subgroup of or equal to + '\u22ED': MO.REL, // does not contain as normal subgroup or equal + '\u22EE': MO.ORD, // \vdots + '\u22EF': MO.INNER, // \cdots + '\u22F0': MO.INNER, // up right diagonal ellipses + '\u22F1': MO.INNER, // \ddots + '\u22F2': MO.REL, // element of with long horizontal stroke + '\u22F3': MO.REL, // element of with vertical bar at end of horizontal stroke + '\u22F4': MO.REL, // small element of with vertical bar at end of horizontal stroke + '\u22F5': MO.REL, // element of with dot above + '\u22F6': MO.REL, // element of with overbar + '\u22F7': MO.REL, // small element of with overbar + '\u22F8': MO.REL, // element of with underbar + '\u22F9': MO.REL, // element of with two horizontal strokes + '\u22FA': MO.REL, // contains with long horizontal stroke + '\u22FB': MO.REL, // contains with vertical bar at end of horizontal stroke + '\u22FC': MO.REL, // small contains with vertical bar at end of horizontal stroke + '\u22FD': MO.REL, // contains with overbar + '\u22FE': MO.REL, // small contains with overbar + '\u22FF': MO.REL, // z notation bag membership + '\u2301': MO.REL, // electric arrow + '\u2305': MO.BIN3, // projective + '\u2306': MO.BIN3, // perspective + '\u2329': MO.OPEN, // langle + '\u232A': MO.CLOSE, // rangle + '\u237C': MO.REL, // right angle with downwards zigzag arrow + '\u238B': MO.REL, // broken circle with northwest arrow + '\u23AA': MO.ORD, // \bracevert + '\u23AF': MO.ORDSTRETCH0, // horizontal line extension + '\u23B0': MO.OPEN, // \lmoustache + '\u23B1': MO.CLOSE, // \rmoustache + '\u2500': MO.ORD, // horizontal line + '\u25B3': MO.BIN3, // \bigtriangleup + '\u25B5': MO.BIN3, // triangle + '\u25B9': MO.BIN3, // \triangleright + '\u25BD': MO.BIN3, // \bigtriangledown + '\u25BF': MO.BIN3, // triangledown + '\u25C3': MO.BIN3, // \triangleleft + '\u25EF': MO.BIN3, // \bigcirc + '\u2660': MO.ORD, // \spadesuit + '\u2661': MO.ORD, // \heartsuit + '\u2662': MO.ORD, // \diamondsuit + '\u2663': MO.ORD, // \clubsuit + '\u266D': MO.ORD, // \flat + '\u266E': MO.ORD, // \natural + '\u266F': MO.ORD, // \sharp + '\u2758': [5, 5, TEXCLASS.REL, {stretchy: true, symmetric: true}], // vertical separator + '\u2794': MO.WIDEREL, // heavy wide-headed rightwards arrow + '\u2795': MO.BIN4, // heavy plus sign + '\u2796': MO.BIN4, // heavy minus sign + '\u2797': MO.BIN4, // heavy division sign + '\u2798': MO.REL, // heavy south east arrow + '\u2799': MO.WIDEREL, // heavy rightwards arrow + '\u279A': MO.REL, // heavy north east arrow + '\u279B': MO.WIDEREL, // drafting point rightwards arrow + '\u279C': MO.WIDEREL, // heavy round-tipped rightwards arrow + '\u279D': MO.WIDEREL, // triangle-headed rightwards arrow + '\u279E': MO.WIDEREL, // heavy triangle-headed rightwards arrow + '\u279F': MO.WIDEREL, // dashed triangle-headed rightwards arrow + '\u27A0': MO.WIDEREL, // heavy dashed triangle-headed rightwards arrow + '\u27A1': MO.WIDEREL, // black rightwards arrow + '\u27A5': MO.WIDEREL, // heavy black curved downwards and rightwards arrow + '\u27A6': MO.WIDEREL, // heavy black curved upwards and rightwards arrow + '\u27A7': MO.RELACCENT, // squat black rightwards arrow + '\u27A8': MO.WIDEREL, // heavy concave-pointed black rightwards arrow + '\u27A9': MO.WIDEREL, // right-shaded white rightwards arrow + '\u27AA': MO.WIDEREL, // left-shaded white rightwards arrow + '\u27AB': MO.WIDEREL, // back-tilted shadowed white rightwards arrow + '\u27AC': MO.WIDEREL, // front-tilted shadowed white rightwards arrow + '\u27AD': MO.WIDEREL, // heavy lower right-shadowed white rightwards arrow + '\u27AE': MO.WIDEREL, // heavy upper right-shadowed white rightwards arrow + '\u27AF': MO.WIDEREL, // notched lower right-shadowed white rightwards arrow + '\u27B1': MO.WIDEREL, // notched upper right-shadowed white rightwards arrow + '\u27B2': MO.RELACCENT, // circled heavy white rightwards arrow + '\u27B3': MO.WIDEREL, // white-feathered rightwards arrow + '\u27B4': MO.REL, // black-feathered south east arrow + '\u27B5': MO.WIDEREL, // black-feathered rightwards arrow + '\u27B6': MO.REL, // black-feathered north east arrow + '\u27B7': MO.REL, // heavy black-feathered south east arrow + '\u27B8': MO.WIDEREL, // heavy black-feathered rightwards arrow + '\u27B9': MO.REL, // heavy black-feathered north east arrow + '\u27BA': MO.WIDEREL, // teardrop-barbed rightwards arrow + '\u27BB': MO.WIDEREL, // heavy teardrop-shanked rightwards arrow + '\u27BC': MO.WIDEREL, // wedge-tailed rightwards arrow + '\u27BD': MO.WIDEREL, // heavy wedge-tailed rightwards arrow + '\u27BE': MO.WIDEREL, // open-outlined rightwards arrow + '\u27C2': MO.REL, // perpendicular: \perp + '\u27C2\u0338': MO.REL, // not perpendicular + '\u27CB': MO.BIN3, // mathematical rising diagonal + '\u27CD': MO.BIN3, // mathematical falling diagonal + '\u27F0': MO.RELSTRETCH, // upwards quadruple arrow + '\u27F1': MO.RELSTRETCH, // downwards quadruple arrow + '\u27F2': MO.REL, // anticlockwise gapped circle arrow + '\u27F3': MO.REL, // clockwise gapped circle arrow + '\u27F4': MO.RELSTRETCH, // right arrow with circled plus + '\u27F5': MO.WIDEREL, // long leftwards arrow: \longleftarrow + '\u27F6': MO.WIDEREL, // long rightwards arrow: \longrightarrow + '\u27F7': MO.WIDEREL, // long left right arrow: \longleftrightarrow + '\u27F8': MO.WIDEREL, // long leftwards double arrow: \Longleftarrow + '\u27F9': MO.WIDEREL, // long rightwards double arrow: \Longrightarrow + '\u27FA': MO.WIDEREL, // long left right double arrow: \Longleftrightarrow + '\u27FB': MO.WIDEREL, // long leftwards arrow from bar + '\u27FC': MO.WIDEREL, // long rightwards arrow from bar: \longmapsto + '\u27FD': MO.WIDEREL, // long leftwards double arrow from bar + '\u27FE': MO.WIDEREL, // long rightwards double arrow from bar + '\u27FF': MO.WIDEREL, // long rightwards squiggle arrow + '\u2900': MO.WIDEREL, // rightwards two-headed arrow with vertical stroke + '\u2901': MO.WIDEREL, // rightwards two-headed arrow with double vertical stroke + '\u2902': MO.WIDEREL, // leftwards double arrow with vertical stroke + '\u2903': MO.WIDEREL, // rightwards double arrow with vertical stroke + '\u2904': MO.WIDEREL, // left right double arrow with vertical stroke + '\u2905': MO.WIDEREL, // rightwards two-headed arrow from bar + '\u2906': MO.WIDEREL, // leftwards double arrow from bar + '\u2907': MO.WIDEREL, // rightwards double arrow from bar + '\u2908': MO.RELSTRETCH, // downwards arrow with horizontal stroke + '\u2909': MO.RELSTRETCH, // upwards arrow with horizontal stroke + '\u290A': MO.RELSTRETCH, // upwards triple arrow + '\u290B': MO.RELSTRETCH, // downwards triple arrow + '\u290C': MO.WIDEREL, // leftwards double dash arrow + '\u290D': MO.WIDEREL, // rightwards double dash arrow + '\u290E': MO.WIDEREL, // leftwards triple dash arrow + '\u290F': MO.WIDEREL, // rightwards triple dash arrow + '\u2910': MO.WIDEREL, // rightwards two-headed triple dash arrow + '\u2911': MO.WIDEREL, // rightwards arrow with dotted stem + '\u2912': MO.RELSTRETCH, // upwards arrow to bar + '\u2913': MO.RELSTRETCH, // downwards arrow to bar + '\u2914': MO.WIDEREL, // rightwards arrow with tail with vertical stroke + '\u2915': MO.WIDEREL, // rightwards arrow with tail with double vertical stroke + '\u2916': MO.WIDEREL, // rightwards two-headed arrow with tail + '\u2917': MO.WIDEREL, // rightwards two-headed arrow with tail with vertical stroke + '\u2918': MO.WIDEREL, // rightwards two-headed arrow with tail with double vertical stroke + '\u2919': MO.WIDEREL, // leftwards arrow-tail + '\u291A': MO.WIDEREL, // rightwards arrow-tail + '\u291B': MO.WIDEREL, // leftwards double arrow-tail + '\u291C': MO.WIDEREL, // rightwards double arrow-tail + '\u291D': MO.WIDEREL, // leftwards arrow to black diamond + '\u291E': MO.WIDEREL, // rightwards arrow to black diamond + '\u291F': MO.WIDEREL, // leftwards arrow from bar to black diamond + '\u2920': MO.WIDEREL, // rightwards arrow from bar to black diamond + '\u2921': MO.REL, // north west and south east arrow + '\u2922': MO.REL, // north east and south west arrow + '\u2923': MO.REL, // north west arrow with hook + '\u2924': MO.REL, // north east arrow with hook + '\u2925': MO.REL, // south east arrow with hook + '\u2926': MO.REL, // south west arrow with hook + '\u2927': MO.REL, // north west arrow and north east arrow + '\u2928': MO.REL, // north east arrow and south east arrow + '\u2929': MO.REL, // south east arrow and south west arrow + '\u292A': MO.REL, // south west arrow and north west arrow + '\u292B': MO.REL, // rising diagonal crossing falling diagonal + '\u292C': MO.REL, // falling diagonal crossing rising diagonal + '\u292D': MO.REL, // south east arrow crossing north east arrow + '\u292E': MO.REL, // north east arrow crossing south east arrow + '\u292F': MO.REL, // falling diagonal crossing north east arrow + '\u2930': MO.REL, // rising diagonal crossing south east arrow + '\u2931': MO.REL, // north east arrow crossing north west arrow + '\u2932': MO.REL, // north west arrow crossing north east arrow + '\u2933': MO.RELACCENT, // wave arrow pointing directly right + '\u2934': MO.RELSTRETCH, // arrow pointing rightwards then curving upwards + '\u2935': MO.RELSTRETCH, // arrow pointing rightwards then curving downwards + '\u2936': MO.RELSTRETCH, // arrow pointing downwards then curving leftwards + '\u2937': MO.RELSTRETCH, // arrow pointing downwards then curving rightwards + '\u2938': MO.REL, // right-side arc clockwise arrow + '\u2939': MO.REL, // left-side arc anticlockwise arrow + '\u293A': MO.RELACCENT, // top arc anticlockwise arrow + '\u293B': MO.RELACCENT, // bottom arc anticlockwise arrow + '\u293C': MO.RELACCENT, // top arc clockwise arrow with minus + '\u293D': MO.RELACCENT, // top arc anticlockwise arrow with plus + '\u293E': MO.REL, // lower right semicircular clockwise arrow + '\u293F': MO.REL, // lower left semicircular anticlockwise arrow + '\u2940': MO.REL, // anticlockwise closed circle arrow + '\u2941': MO.REL, // clockwise closed circle arrow + '\u2942': MO.WIDEREL, // rightwards arrow above short leftwards arrow + '\u2943': MO.WIDEREL, // leftwards arrow above short rightwards arrow + '\u2944': MO.WIDEREL, // short rightwards arrow above leftwards arrow + '\u2945': MO.RELSTRETCH, // rightwards arrow with plus below + '\u2946': MO.RELSTRETCH, // leftwards arrow with plus below + '\u2947': MO.WIDEREL, // rightwards arrow through x + '\u2948': MO.WIDEREL, // left right arrow through small circle + '\u2949': MO.RELSTRETCH, // upwards two-headed arrow from small circle + '\u294A': MO.WIDEREL, // left barb up right barb down harpoon + '\u294B': MO.WIDEREL, // left barb down right barb up harpoon + '\u294C': MO.RELSTRETCH, // up barb right down barb left harpoon + '\u294D': MO.RELSTRETCH, // up barb left down barb right harpoon + '\u294E': MO.WIDEREL, // left barb up right barb up harpoon + '\u294F': MO.RELSTRETCH, // up barb right down barb right harpoon + '\u2950': MO.WIDEREL, // left barb down right barb down harpoon + '\u2951': MO.RELSTRETCH, // up barb left down barb left harpoon + '\u2952': MO.WIDEREL, // leftwards harpoon with barb up to bar + '\u2953': MO.WIDEREL, // rightwards harpoon with barb up to bar + '\u2954': MO.RELSTRETCH, // upwards harpoon with barb right to bar + '\u2955': MO.RELSTRETCH, // downwards harpoon with barb right to bar + '\u2956': MO.WIDEREL, // leftwards harpoon with barb down to bar + '\u2957': MO.WIDEREL, // rightwards harpoon with barb down to bar + '\u2958': MO.RELSTRETCH, // upwards harpoon with barb left to bar + '\u2959': MO.RELSTRETCH, // downwards harpoon with barb left to bar + '\u295A': MO.WIDEREL, // leftwards harpoon with barb up from bar + '\u295B': MO.WIDEREL, // rightwards harpoon with barb up from bar + '\u295C': MO.RELSTRETCH, // upwards harpoon with barb right from bar + '\u295D': MO.RELSTRETCH, // downwards harpoon with barb right from bar + '\u295E': MO.WIDEREL, // leftwards harpoon with barb down from bar + '\u295F': MO.WIDEREL, // rightwards harpoon with barb down from bar + '\u2960': MO.RELSTRETCH, // upwards harpoon with barb left from bar + '\u2961': MO.RELSTRETCH, // downwards harpoon with barb left from bar + '\u2962': MO.WIDEREL, // leftwards harpoon with barb up above leftwards harpoon with barb down + '\u2963': MO.RELSTRETCH, // upwards harpoon with barb left beside upwards harpoon with barb right + '\u2964': MO.WIDEREL, // rightwards harpoon with barb up above rightwards harpoon with barb down + '\u2965': MO.RELSTRETCH, // downwards harpoon with barb left beside downwards harpoon with barb right + '\u2966': MO.WIDEREL, // leftwards harpoon with barb up above rightwards harpoon with barb up + '\u2967': MO.WIDEREL, // leftwards harpoon with barb down above rightwards harpoon with barb down + '\u2968': MO.WIDEREL, // rightwards harpoon with barb up above leftwards harpoon with barb up + '\u2969': MO.WIDEREL, // rightwards harpoon with barb down above leftwards harpoon with barb down + '\u296A': MO.WIDEREL, // leftwards harpoon with barb up above long dash + '\u296B': MO.WIDEREL, // leftwards harpoon with barb down below long dash + '\u296C': MO.WIDEREL, // rightwards harpoon with barb up above long dash + '\u296D': MO.WIDEREL, // rightwards harpoon with barb down below long dash + '\u296E': MO.RELSTRETCH, // upwards harpoon with barb left beside downwards harpoon with barb right + '\u296F': MO.RELSTRETCH, // downwards harpoon with barb left beside upwards harpoon with barb right + '\u2970': MO.WIDEREL, // right double arrow with rounded head + '\u2971': MO.WIDEREL, // equals sign above rightwards arrow + '\u2972': MO.WIDEREL, // tilde operator above rightwards arrow + '\u2973': MO.WIDEREL, // leftwards arrow above tilde operator + '\u2974': MO.WIDEREL, // rightwards arrow above tilde operator + '\u2975': MO.WIDEREL, // rightwards arrow above almost equal to + '\u2976': MO.RELACCENT, // less-than above leftwards arrow + '\u2977': MO.RELACCENT, // leftwards arrow through less-than + '\u2978': MO.RELACCENT, // greater-than above rightwards arrow + '\u2979': MO.RELACCENT, // subset above rightwards arrow + '\u297A': MO.RELACCENT, // leftwards arrow through subset + '\u297B': MO.RELACCENT, // superset above leftwards arrow + '\u297C': MO.WIDEREL, // left fish tail + '\u297D': MO.WIDEREL, // right fish tail + '\u297E': MO.RELSTRETCH, // up fish tail + '\u297F': MO.RELSTRETCH, // down fish tail + '\u2981': MO.REL, // z notation spot + '\u2982': MO.REL, // z notation type colon + '\u29B6': MO.REL, // circled vertical bar + '\u29B7': MO.REL, // circled parallel + '\u29B8': MO.BIN4, // circled reverse solidus + '\u29B9': MO.REL, // circled perpendicular + '\u29BC': MO.BIN4, // circled anticlockwise-rotated division sign + '\u29C0': MO.REL, // circled less-than + '\u29C1': MO.REL, // circled greater-than + '\u29C4': MO.BIN4, // squared rising diagonal slash + '\u29C5': MO.BIN4, // squared falling diagonal slash + '\u29C6': MO.BIN3, // squared asterisk + '\u29C7': MO.BIN3, // squared small circle + '\u29C8': MO.BIN3, // squared square + '\u29CE': MO.REL, // right triangle above left triangle + '\u29CF': MO.REL, // left triangle beside vertical bar + '\u29D0': MO.REL, // vertical bar beside right triangle + '\u29D1': MO.REL, // bowtie with left half black + '\u29D2': MO.REL, // bowtie with right half black + '\u29D3': MO.REL, // black bowtie + '\u29D4': MO.BIN3, // times with left half black + '\u29D5': MO.BIN3, // times with right half black + '\u29D6': MO.BIN3, // white hourglass + '\u29D7': MO.BIN3, // black hourglass + '\u29DF': MO.REL, // double-ended multimap + '\u29E1': MO.REL, // increases as + '\u29E2': MO.BIN3, // shuffle product + '\u29E3': MO.REL, // equals sign and slanted parallel + '\u29E4': MO.REL, // equals sign and slanted parallel with tilde above + '\u29E5': MO.REL, // identical to and slanted parallel + '\u29E6': MO.REL, // gleich stark + '\u29F4': MO.REL, // rule-delayed + '\u29F5': MO.BIN4, // reverse solidus operator + '\u29F6': MO.BIN4, // solidus with overbar + '\u29F7': MO.BIN4, // reverse solidus with horizontal stroke + '\u29F8': MO.BIN4, // big solidus + '\u29F9': MO.BIN4, // big reverse solidus + '\u29FA': MO.BIN4, // double plus + '\u29FB': MO.BIN4, // triple plus + '\u2A1D': MO.BIN3, // join + '\u2A1E': MO.BIN3, // large left triangle operator + '\u2A1F': MO.BIN4, // z notation schema composition + '\u2A20': MO.BIN4, // z notation schema piping + '\u2A21': MO.BIN4, // z notation schema projection + '\u2A22': MO.BIN4, // plus sign with small circle above + '\u2A23': MO.BIN4, // plus sign with circumflex accent above + '\u2A24': MO.BIN4, // plus sign with tilde above + '\u2A25': MO.BIN4, // plus sign with dot below + '\u2A26': MO.BIN4, // plus sign with tilde below + '\u2A27': MO.BIN4, // plus sign with subscript two + '\u2A28': MO.BIN4, // plus sign with black triangle + '\u2A29': MO.BIN4, // minus sign with comma above + '\u2A2A': MO.BIN4, // minus sign with dot below + '\u2A2B': MO.BIN4, // minus sign with falling dots + '\u2A2C': MO.BIN4, // minus sign with rising dots + '\u2A2D': MO.BIN4, // plus sign in left half circle + '\u2A2E': MO.BIN4, // plus sign in right half circle + '\u2A2F': MO.BIN3, // vector or cross product + '\u2A30': MO.BIN3, // multiplication sign with dot above + '\u2A31': MO.BIN3, // multiplication sign with underbar + '\u2A32': MO.BIN3, // semidirect product with bottom closed + '\u2A33': MO.BIN3, // smash product + '\u2A34': MO.BIN3, // multiplication sign in left half circle + '\u2A35': MO.BIN3, // multiplication sign in right half circle + '\u2A36': MO.BIN3, // circled multiplication sign with circumflex accent + '\u2A37': MO.BIN3, // multiplication sign in double circle + '\u2A38': MO.BIN4, // circled division sign + '\u2A39': MO.BIN4, // plus sign in triangle + '\u2A3A': MO.BIN4, // minus sign in triangle + '\u2A3B': MO.BIN3, // multiplication sign in triangle + '\u2A3C': MO.BIN3, // interior product + '\u2A3D': MO.BIN3, // righthand interior product + '\u2A3E': MO.BIN4, // z notation relational composition + '\u2A3F': MO.BIN3, // amalgamation or coproduct: \amalg + '\u2A40': MO.BIN4, // intersection with dot + '\u2A41': MO.BIN4, // union with minus sign + '\u2A42': MO.BIN4, // union with overbar + '\u2A43': MO.BIN4, // intersection with overbar + '\u2A44': MO.BIN4, // intersection with logical and + '\u2A45': MO.BIN4, // union with logical or + '\u2A46': MO.BIN4, // union above intersection + '\u2A47': MO.BIN4, // intersection above union + '\u2A48': MO.BIN4, // union above bar above intersection + '\u2A49': MO.BIN4, // intersection above bar above union + '\u2A4A': MO.BIN4, // union beside and joined with union + '\u2A4B': MO.BIN4, // intersection beside and joined with intersection + '\u2A4C': MO.BIN4, // closed union with serifs + '\u2A4D': MO.BIN4, // closed intersection with serifs + '\u2A4E': MO.BIN4, // double square intersection + '\u2A4F': MO.BIN4, // double square union + '\u2A50': MO.BIN3, // closed union with serifs and smash product + '\u2A51': MO.BIN4, // logical and with dot above + '\u2A52': MO.BIN4, // logical or with dot above + '\u2A53': MO.BIN4, // double logical and + '\u2A54': MO.BIN4, // double logical or + '\u2A55': MO.BIN4, // two intersecting logical and + '\u2A56': MO.BIN4, // two intersecting logical or + '\u2A57': MO.BIN4, // sloping large or + '\u2A58': MO.BIN4, // sloping large and + '\u2A59': MO.BIN4, // logical or overlapping logical and + '\u2A5A': MO.BIN4, // logical and with middle stem + '\u2A5B': MO.BIN4, // logical or with middle stem + '\u2A5C': MO.BIN4, // logical and with horizontal dash + '\u2A5D': MO.BIN4, // logical or with horizontal dash + '\u2A5E': MO.BIN4, // logical and with double overbar + '\u2A5F': MO.BIN4, // logical and with underbar + '\u2A60': MO.BIN4, // logical and with double underbar + '\u2A61': MO.BIN4, // small vee with underbar + '\u2A62': MO.BIN4, // logical or with double overbar + '\u2A63': MO.BIN4, // logical or with double underbar + '\u2A64': MO.BIN3, // z notation domain antirestriction + '\u2A65': MO.BIN3, // z notation range antirestriction + '\u2A66': MO.REL, // equals sign with dot below + '\u2A67': MO.REL, // identical with dot above + '\u2A68': MO.REL, // triple horizontal bar with double vertical stroke + '\u2A69': MO.REL, // triple horizontal bar with triple vertical stroke + '\u2A6A': MO.REL, // tilde operator with dot above + '\u2A6B': MO.REL, // tilde operator with rising dots + '\u2A6C': MO.REL, // similar minus similar + '\u2A6D': MO.REL, // congruent with dot above + '\u2A6E': MO.REL, // equals with asterisk + '\u2A6F': MO.REL, // almost equal to with circumflex accent + '\u2A70': MO.REL, // approximately equal or equal to + '\u2A71': MO.REL, // equals sign above plus sign + '\u2A72': MO.REL, // plus sign above equals sign + '\u2A73': MO.REL, // equals sign above tilde operator + '\u2A74': MO.REL, // double colon equal + '\u2A75': MO.REL, // two consecutive equals signs + '\u2A76': MO.REL, // three consecutive equals signs + '\u2A77': MO.REL, // equals sign with two dots above and two dots below + '\u2A78': MO.REL, // equivalent with four dots above + '\u2A79': MO.REL, // less-than with circle inside + '\u2A7A': MO.REL, // greater-than with circle inside + '\u2A7B': MO.REL, // less-than with question mark above + '\u2A7C': MO.REL, // greater-than with question mark above + '\u2A7D': MO.REL, // less-than or slanted equal to + '\u2A7D\u0338': MO.REL, // not less-than or slanted equal to + '\u2A7E': MO.REL, // greater-than or slanted equal to + '\u2A7E\u0338': MO.REL, // not greater-than or slanted equal to + '\u2A7F': MO.REL, // less-than or slanted equal to with dot inside + '\u2A80': MO.REL, // greater-than or slanted equal to with dot inside + '\u2A81': MO.REL, // less-than or slanted equal to with dot above + '\u2A82': MO.REL, // greater-than or slanted equal to with dot above + '\u2A83': MO.REL, // less-than or slanted equal to with dot above right + '\u2A84': MO.REL, // greater-than or slanted equal to with dot above left + '\u2A85': MO.REL, // less-than or approximate + '\u2A86': MO.REL, // greater-than or approximate + '\u2A87': MO.REL, // less-than and single-line not equal to + '\u2A88': MO.REL, // greater-than and single-line not equal to + '\u2A89': MO.REL, // less-than and not approximate + '\u2A8A': MO.REL, // greater-than and not approximate + '\u2A8B': MO.REL, // less-than above double-line equal above greater-than + '\u2A8C': MO.REL, // greater-than above double-line equal above less-than + '\u2A8D': MO.REL, // less-than above similar or equal + '\u2A8E': MO.REL, // greater-than above similar or equal + '\u2A8F': MO.REL, // less-than above similar above greater-than + '\u2A90': MO.REL, // greater-than above similar above less-than + '\u2A91': MO.REL, // less-than above greater-than above double-line equal + '\u2A92': MO.REL, // greater-than above less-than above double-line equal + '\u2A93': MO.REL, // less-than above slanted equal above greater-than above slanted equal + '\u2A94': MO.REL, // greater-than above slanted equal above less-than above slanted equal + '\u2A95': MO.REL, // slanted equal to or less-than + '\u2A96': MO.REL, // slanted equal to or greater-than + '\u2A97': MO.REL, // slanted equal to or less-than with dot inside + '\u2A98': MO.REL, // slanted equal to or greater-than with dot inside + '\u2A99': MO.REL, // double-line equal to or less-than + '\u2A9A': MO.REL, // double-line equal to or greater-than + '\u2A9B': MO.REL, // double-line slanted equal to or less-than + '\u2A9C': MO.REL, // double-line slanted equal to or greater-than + '\u2A9D': MO.REL, // similar or less-than + '\u2A9E': MO.REL, // similar or greater-than + '\u2A9F': MO.REL, // similar above less-than above equals sign + '\u2AA0': MO.REL, // similar above greater-than above equals sign + '\u2AA1': MO.REL, // double nested less-than + '\u2AA2': MO.REL, // double nested greater-than + '\u2AA3': MO.REL, // double nested less-than with underbar + '\u2AA4': MO.REL, // greater-than overlapping less-than + '\u2AA5': MO.REL, // greater-than beside less-than + '\u2AA6': MO.REL, // less-than closed by curve + '\u2AA7': MO.REL, // greater-than closed by curve + '\u2AA8': MO.REL, // less-than closed by curve above slanted equal + '\u2AA9': MO.REL, // greater-than closed by curve above slanted equal + '\u2AAA': MO.REL, // smaller than + '\u2AAB': MO.REL, // larger than + '\u2AAC': MO.REL, // smaller than or equal to + '\u2AAD': MO.REL, // larger than or equal to + '\u2AAE': MO.REL, // equals sign with bumpy above + '\u2AAF': MO.REL, // precedes above single-line equals sign: \preceq + '\u2AAF\u0338': MO.REL, // not precedes above single-line equals sign + '\u2AB0': MO.REL, // succeeds above single-line equals sign: \succeq + '\u2AB0\u0338': MO.REL, // not succeeds above single-line equals sign + '\u2AB1': MO.REL, // precedes above single-line not equal to + '\u2AB2': MO.REL, // succeeds above single-line not equal to + '\u2AB3': MO.REL, // precedes above equals sign + '\u2AB4': MO.REL, // succeeds above equals sign + '\u2AB5': MO.REL, // precedes above not equal to + '\u2AB6': MO.REL, // succeeds above not equal to + '\u2AB7': MO.REL, // precedes above almost equal to + '\u2AB8': MO.REL, // succeeds above almost equal to + '\u2AB9': MO.REL, // precedes above not almost equal to + '\u2ABA': MO.REL, // succeeds above not almost equal to + '\u2ABB': MO.REL, // double precedes + '\u2ABC': MO.REL, // double succeeds + '\u2ABD': MO.REL, // subset with dot + '\u2ABE': MO.REL, // superset with dot + '\u2ABF': MO.REL, // subset with plus sign below + '\u2AC0': MO.REL, // superset with plus sign below + '\u2AC1': MO.REL, // subset with multiplication sign below + '\u2AC2': MO.REL, // superset with multiplication sign below + '\u2AC3': MO.REL, // subset of or equal to with dot above + '\u2AC4': MO.REL, // superset of or equal to with dot above + '\u2AC5': MO.REL, // subset of above equals sign + '\u2AC6': MO.REL, // superset of above equals sign + '\u2AC7': MO.REL, // subset of above tilde operator + '\u2AC8': MO.REL, // superset of above tilde operator + '\u2AC9': MO.REL, // subset of above almost equal to + '\u2ACA': MO.REL, // superset of above almost equal to + '\u2ACB': MO.REL, // subset of above not equal to + '\u2ACC': MO.REL, // superset of above not equal to + '\u2ACD': MO.REL, // square left open box operator + '\u2ACE': MO.REL, // square right open box operator + '\u2ACF': MO.REL, // closed subset + '\u2AD0': MO.REL, // closed superset + '\u2AD1': MO.REL, // closed subset or equal to + '\u2AD2': MO.REL, // closed superset or equal to + '\u2AD3': MO.REL, // subset above superset + '\u2AD4': MO.REL, // superset above subset + '\u2AD5': MO.REL, // subset above subset + '\u2AD6': MO.REL, // superset above superset + '\u2AD7': MO.REL, // superset beside subset + '\u2AD8': MO.REL, // superset beside and joined by dash with subset + '\u2AD9': MO.REL, // element of opening downwards + '\u2ADA': MO.REL, // pitchfork with tee top + '\u2ADB': MO.BIN4, // transversal intersection + '\u2ADD': MO.BIN3, // nonforking + '\u2ADD\u0338': MO.REL, // forking + '\u2ADE': MO.REL, // short left tack + '\u2ADF': MO.REL, // short down tack + '\u2AE0': MO.REL, // short up tack + '\u2AE1': MO.REL, // perpendicular with s + '\u2AE2': MO.REL, // vertical bar triple right turnstile + '\u2AE3': MO.REL, // double vertical bar left turnstile + '\u2AE4': MO.REL, // vertical bar double left turnstile + '\u2AE5': MO.REL, // double vertical bar double left turnstile + '\u2AE6': MO.REL, // long dash from left member of double vertical + '\u2AE7': MO.REL, // short down tack with overbar + '\u2AE8': MO.REL, // short up tack with underbar + '\u2AE9': MO.REL, // short up tack above short down tack + '\u2AEA': MO.REL, // double down tack + '\u2AEB': MO.REL, // double up tack + '\u2AEE': MO.REL, // does not divide with reversed negation slash + '\u2AF2': MO.REL, // parallel with horizontal stroke + '\u2AF3': MO.REL, // parallel with tilde operator + '\u2AF4': MO.REL, // triple vertical bar binary relation + '\u2AF5': MO.REL, // triple vertical bar with horizontal stroke + '\u2AF6': MO.BIN4, // triple colon operator + '\u2AF7': MO.REL, // triple nested less-than + '\u2AF8': MO.REL, // triple nested greater-than + '\u2AF9': MO.REL, // double-line slanted less-than or equal to + '\u2AFA': MO.REL, // double-line slanted greater-than or equal to + '\u2AFB': MO.BIN4, // triple solidus binary relation + '\u2AFD': MO.BIN4, // double solidus operator + '\u2AFE': MO.BIN3, // white vertical bar + '\u2B00': MO.REL, // north east white arrow + '\u2B01': MO.REL, // north west white arrow + '\u2B02': MO.REL, // south east white arrow + '\u2B03': MO.REL, // south west white arrow + '\u2B04': MO.WIDEREL, // left right white arrow + '\u2B05': MO.WIDEREL, // leftwards black arrow + '\u2B06': MO.RELSTRETCH, // upwards black arrow + '\u2B07': MO.RELSTRETCH, // downwards black arrow + '\u2B08': MO.REL, // north east black arrow + '\u2B09': MO.REL, // north west black arrow + '\u2B0A': MO.REL, // south east black arrow + '\u2B0B': MO.REL, // south west black arrow + '\u2B0C': MO.WIDEREL, // left right black arrow + '\u2B0D': MO.RELSTRETCH, // up down black arrow + '\u2B0E': MO.RELSTRETCH, // rightwards arrow with tip downwards + '\u2B0F': MO.RELSTRETCH, // rightwards arrow with tip upwards + '\u2B10': MO.RELSTRETCH, // leftwards arrow with tip downwards + '\u2B11': MO.RELSTRETCH, // leftwards arrow with tip upwards + '\u2B30': MO.WIDEREL, // left arrow with small circle + '\u2B31': MO.WIDEREL, // three leftwards arrows + '\u2B32': MO.RELSTRETCH, // left arrow with circled plus + '\u2B33': MO.WIDEREL, // long leftwards squiggle arrow + '\u2B34': MO.WIDEREL, // leftwards two-headed arrow with vertical stroke + '\u2B35': MO.WIDEREL, // leftwards two-headed arrow with double vertical stroke + '\u2B36': MO.WIDEREL, // leftwards two-headed arrow from bar + '\u2B37': MO.WIDEREL, // leftwards two-headed triple dash arrow + '\u2B38': MO.WIDEREL, // leftwards arrow with dotted stem + '\u2B39': MO.WIDEREL, // leftwards arrow with tail with vertical stroke + '\u2B3A': MO.WIDEREL, // leftwards arrow with tail with double vertical stroke + '\u2B3B': MO.WIDEREL, // leftwards two-headed arrow with tail + '\u2B3C': MO.WIDEREL, // leftwards two-headed arrow with tail with vertical stroke + '\u2B3D': MO.WIDEREL, // leftwards two-headed arrow with tail with double vertical stroke + '\u2B3E': MO.WIDEREL, // leftwards arrow through x + '\u2B3F': MO.RELACCENT, // wave arrow pointing directly left + '\u2B40': MO.WIDEREL, // equals sign above leftwards arrow + '\u2B41': MO.WIDEREL, // reverse tilde operator above leftwards arrow + '\u2B42': MO.WIDEREL, // leftwards arrow above reverse almost equal to + '\u2B43': MO.WIDEREL, // rightwards arrow through greater-than + '\u2B44': MO.WIDEREL, // rightwards arrow through superset + '\u2B45': MO.WIDEREL, // leftwards quadruple arrow + '\u2B46': MO.WIDEREL, // rightwards quadruple arrow + '\u2B47': MO.WIDEREL, // reverse tilde operator above rightwards arrow + '\u2B48': MO.WIDEREL, // rightwards arrow above reverse almost equal to + '\u2B49': MO.WIDEREL, // tilde operator above leftwards arrow + '\u2B4A': MO.WIDEREL, // leftwards arrow above almost equal to + '\u2B4B': MO.WIDEREL, // leftwards arrow above reverse tilde operator + '\u2B4C': MO.WIDEREL, // rightwards arrow above reverse tilde operator + '\u2B4D': MO.REL, // downwards triangle-headed zigzag arrow + '\u2B4E': MO.REL, // short slanted north arrow + '\u2B4F': MO.REL, // short backslanted south arrow + '\u2B5A': MO.REL, // slanted north arrow with hooked head + '\u2B5B': MO.REL, // backslanted south arrow with hooked tail + '\u2B5C': MO.REL, // slanted north arrow with horizontal tail + '\u2B5D': MO.REL, // backslanted south arrow with horizontal tail + '\u2B5E': MO.REL, // bent arrow pointing downwards then north east + '\u2B5F': MO.REL, // short bent arrow pointing downwards then north east + '\u2B60': MO.WIDEREL, // leftwards triangle-headed arrow + '\u2B61': MO.RELSTRETCH, // upwards triangle-headed arrow + '\u2B62': MO.WIDEREL, // rightwards triangle-headed arrow + '\u2B63': MO.RELSTRETCH, // downwards triangle-headed arrow + '\u2B64': MO.WIDEREL, // left right triangle-headed arrow + '\u2B65': MO.RELSTRETCH, // up down triangle-headed arrow + '\u2B66': MO.REL, // north west triangle-headed arrow + '\u2B67': MO.REL, // north east triangle-headed arrow + '\u2B68': MO.REL, // south east triangle-headed arrow + '\u2B69': MO.REL, // south west triangle-headed arrow + '\u2B6A': MO.WIDEREL, // leftwards triangle-headed dashed arrow + '\u2B6B': MO.RELSTRETCH, // upwards triangle-headed dashed arrow + '\u2B6C': MO.WIDEREL, // rightwards triangle-headed dashed arrow + '\u2B6D': MO.RELSTRETCH, // downwards triangle-headed dashed arrow + '\u2B6E': MO.REL, // clockwise triangle-headed open circle arrow + '\u2B6F': MO.REL, // anticlockwise triangle-headed open circle arrow + '\u2B70': MO.WIDEREL, // leftwards triangle-headed arrow to bar + '\u2B71': MO.RELSTRETCH, // upwards triangle-headed arrow to bar + '\u2B72': MO.WIDEREL, // rightwards triangle-headed arrow to bar + '\u2B73': MO.RELSTRETCH, // downwards triangle-headed arrow to bar + '\u2B76': MO.REL, // north west triangle-headed arrow to bar + '\u2B77': MO.REL, // north east triangle-headed arrow to bar + '\u2B78': MO.REL, // south east triangle-headed arrow to bar + '\u2B79': MO.REL, // south west triangle-headed arrow to bar + '\u2B7A': MO.WIDEREL, // leftwards triangle-headed arrow with double horizontal stroke + '\u2B7B': MO.RELSTRETCH, // upwards triangle-headed arrow with double horizontal stroke + '\u2B7C': MO.WIDEREL, // rightwards triangle-headed arrow with double horizontal stroke + '\u2B7D': MO.RELSTRETCH, // downwards triangle-headed arrow with double horizontal stroke + '\u2B80': MO.WIDEREL, // leftwards triangle-headed arrow over rightwards triangle-headed arrow + '\u2B81': MO.RELSTRETCH, // upwards triangle-headed arrow leftwards of downwards triangle-headed arrow + '\u2B82': MO.WIDEREL, // rightwards triangle-headed arrow over leftwards triangle-headed arrow + '\u2B83': MO.RELSTRETCH, // downwards triangle-headed arrow leftwards of upwards triangle-headed arrow + '\u2B84': MO.WIDEREL, // leftwards triangle-headed paired arrows + '\u2B85': MO.RELSTRETCH, // upwards triangle-headed paired arrows + '\u2B86': MO.WIDEREL, // rightwards triangle-headed paired arrows + '\u2B87': MO.RELSTRETCH, // downwards triangle-headed paired arrows + '\u2B88': MO.RELACCENT, // leftwards black circled white arrow + '\u2B89': MO.REL, // upwards black circled white arrow + '\u2B8A': MO.RELACCENT, // rightwards black circled white arrow + '\u2B8B': MO.REL, // downwards black circled white arrow + '\u2B8C': MO.REL, // anticlockwise triangle-headed right u-shaped arrow + '\u2B8D': MO.REL, // anticlockwise triangle-headed bottom u-shaped arrow + '\u2B8E': MO.REL, // anticlockwise triangle-headed left u-shaped arrow + '\u2B8F': MO.REL, // anticlockwise triangle-headed top u-shaped arrow + '\u2B94': MO.REL, // four corner arrows circling anticlockwise + '\u2B95': MO.WIDEREL, // rightwards black arrow + '\u2BA0': MO.RELSTRETCH, // downwards triangle-headed arrow with long tip leftwards + '\u2BA1': MO.RELSTRETCH, // downwards triangle-headed arrow with long tip rightwards + '\u2BA2': MO.RELSTRETCH, // upwards triangle-headed arrow with long tip leftwards + '\u2BA3': MO.RELSTRETCH, // upwards triangle-headed arrow with long tip rightwards + '\u2BA4': MO.RELSTRETCH, // leftwards triangle-headed arrow with long tip upwards + '\u2BA5': MO.RELSTRETCH, // rightwards triangle-headed arrow with long tip upwards + '\u2BA6': MO.RELSTRETCH, // leftwards triangle-headed arrow with long tip downwards + '\u2BA7': MO.RELSTRETCH, // rightwards triangle-headed arrow with long tip downwards + '\u2BA8': MO.WIDEREL, // black curved downwards and leftwards arrow + '\u2BA9': MO.WIDEREL, // black curved downwards and rightwards arrow + '\u2BAA': MO.WIDEREL, // black curved upwards and leftwards arrow + '\u2BAB': MO.WIDEREL, // black curved upwards and rightwards arrow + '\u2BAC': MO.RELSTRETCH, // black curved leftwards and upwards arrow + '\u2BAD': MO.RELSTRETCH, // black curved rightwards and upwards arrow + '\u2BAE': MO.RELSTRETCH, // black curved leftwards and downwards arrow + '\u2BAF': MO.RELSTRETCH, // black curved rightwards and downwards arrow + '\u2BB0': MO.REL, // ribbon arrow down left + '\u2BB1': MO.REL, // ribbon arrow down right + '\u2BB2': MO.REL, // ribbon arrow up left + '\u2BB3': MO.REL, // ribbon arrow up right + '\u2BB4': MO.REL, // ribbon arrow left up + '\u2BB5': MO.REL, // ribbon arrow right up + '\u2BB6': MO.REL, // ribbon arrow left down + '\u2BB7': MO.REL, // ribbon arrow right down + '\u2BB8': MO.RELSTRETCH, // upwards white arrow from bar with horizontal bar + '\u2BD1': MO.REL, // uncertainty sign + '\u3ADC': MO.BIN3, // forking + '\uFE37': MO.WIDEACCENT, // horizontal brace down + '\uFE38': MO.WIDEACCENT, // horizontal brace up + }, }; - -// -// These are not in the W3C table, but we need them for \widehat and \underline -// -OPTABLE.infix['^'] = MO.WIDEREL; -OPTABLE.infix['_'] = MO.WIDEREL; - -// -// Remove from Appendix C, but perhaps that was a mistake? -// -OPTABLE.infix['\u2ADC'] = MO.REL; diff --git a/ts/core/Tree/Factory.ts b/ts/core/Tree/Factory.ts index dec2bd2b0..53132f474 100644 --- a/ts/core/Tree/Factory.ts +++ b/ts/core/Tree/Factory.ts @@ -121,8 +121,7 @@ interface AbstractFactoryClass< export abstract class AbstractFactory< N extends FactoryNode, C extends FactoryNodeClass, -> implements Factory -{ +> implements Factory { /** * The default collection of objects to use for the node map */ diff --git a/ts/core/Tree/Node.ts b/ts/core/Tree/Node.ts index 9e7fa8fcd..f02535922 100644 --- a/ts/core/Tree/Node.ts +++ b/ts/core/Tree/Node.ts @@ -159,8 +159,7 @@ export interface NodeClass, C extends NodeClass> { export abstract class AbstractNode< N extends Node, C extends NodeClass, -> implements Node -{ +> implements Node { /** * The parent node for this one */ diff --git a/ts/core/Tree/Visitor.ts b/ts/core/Tree/Visitor.ts index a4db1d7d8..f4b10ba40 100644 --- a/ts/core/Tree/Visitor.ts +++ b/ts/core/Tree/Visitor.ts @@ -109,9 +109,9 @@ export interface Visitor> { * @template N The node type being traversed * @template C The node class for N (the constructor rather than instance of the class) */ -export abstract class AbstractVisitor> - implements Visitor -{ +export abstract class AbstractVisitor< + N extends VisitorNode, +> implements Visitor { /** * Holds the mapping from node kinds to visitor funcitons */ diff --git a/ts/core/Tree/Wrapper.ts b/ts/core/Tree/Wrapper.ts index 21a2561dd..4df4f76a4 100644 --- a/ts/core/Tree/Wrapper.ts +++ b/ts/core/Tree/Wrapper.ts @@ -108,8 +108,7 @@ export class AbstractWrapper< N extends Node, C extends NodeClass, W extends Wrapper, -> implements Wrapper -{ +> implements Wrapper { /** * @override */ diff --git a/ts/core/Tree/WrapperFactory.ts b/ts/core/Tree/WrapperFactory.ts index 6f45eed53..ce0fb3da8 100644 --- a/ts/core/Tree/WrapperFactory.ts +++ b/ts/core/Tree/WrapperFactory.ts @@ -60,11 +60,11 @@ export interface WrapperFactory< * @template WC The Wrapper class (for static values) */ export abstract class AbstractWrapperFactory< - N extends Node, - C extends NodeClass, - WW extends Wrapper, - WC extends WrapperClass, - > + N extends Node, + C extends NodeClass, + WW extends Wrapper, + WC extends WrapperClass, +> extends AbstractFactory implements WrapperFactory { diff --git a/ts/handlers/html/HTMLMathItem.ts b/ts/handlers/html/HTMLMathItem.ts index 6e8f96b6c..b8042411c 100644 --- a/ts/handlers/html/HTMLMathItem.ts +++ b/ts/handlers/html/HTMLMathItem.ts @@ -83,7 +83,9 @@ export class HTMLMathItem extends AbstractMathItem { if (this.start.n) { node = this.adaptor.split(this.start.node as T, this.start.n); } - this.adaptor.replace(this.typesetRoot, node); + if (this.adaptor.parent(node)) { + this.adaptor.replace(this.typesetRoot, node); + } } else { if (this.start.n) { node = this.adaptor.split(node, this.start.n); diff --git a/ts/input/tex/FilterUtil.ts b/ts/input/tex/FilterUtil.ts index 6895b4605..aeab0530d 100644 --- a/ts/input/tex/FilterUtil.ts +++ b/ts/input/tex/FilterUtil.ts @@ -24,6 +24,7 @@ import { TEXCLASS, MMLNODE, MmlNode } from '../../core/MmlTree/MmlNode.js'; import NodeUtil from './NodeUtil.js'; import ParseOptions from './ParseOptions.js'; +import { TexConstant } from './TexConstants.js'; import { MmlMo } from '../../core/MmlTree/MmlNodes/mo.js'; import { Attributes } from '../../core/MmlTree/Attributes.js'; @@ -161,15 +162,9 @@ const FilterUtil = { for (const mo of options.getList('fixStretchy')) { if (NodeUtil.getProperty(mo, 'fixStretchy')) { const symbol = NodeUtil.getForm(mo); - if (symbol && symbol[3] && symbol[3]['stretchy']) { + if (symbol?.[3]?.['stretchy']) { NodeUtil.setAttribute(mo, 'stretchy', false); } - const parent = mo.parent; - if (!NodeUtil.getTexClass(mo) && (!symbol || !symbol[2])) { - const texAtom = options.nodeFactory.create('node', 'TeXAtom', [mo]); - parent.replaceChild(texAtom, mo); - texAtom.inheritAttributesFrom(mo); - } NodeUtil.removeProperties(mo, 'fixStretchy'); } } @@ -185,12 +180,12 @@ const FilterUtil = { */ cleanAttributes(arg: { data: ParseOptions }) { const node = arg.data.root; - node.walkTree((mml: MmlNode, _d: any) => { - const attribs = mml.attributes; + node.walkTree((mml: MmlNode) => { const keep = new Set( - ((attribs.get('mjx-keep-attrs') as string) || '').split(/ /) + ((mml.getProperty('keep-attrs') as string) || '').split(/ /) ); - attribs.unset('mjx-keep-attrs'); + const attribs = mml.attributes; + attribs.unset(TexConstant.Attr.LATEXITEM); for (const key of attribs.getExplicitNames()) { if ( !keep.has(key) && @@ -199,7 +194,7 @@ const FilterUtil = { attribs.unset(key); } } - }, {}); + }); }, /** diff --git a/ts/input/tex/NodeUtil.ts b/ts/input/tex/NodeUtil.ts index 3ccbe01c5..83517d9a7 100644 --- a/ts/input/tex/NodeUtil.ts +++ b/ts/input/tex/NodeUtil.ts @@ -228,7 +228,9 @@ const NodeUtil = { */ copyAttributes(oldNode: MmlNode, newNode: MmlNode) { newNode.attributes = oldNode.attributes; - this.setProperties(newNode, oldNode.getAllProperties()); + for (const [prop, value] of Object.entries(oldNode.getAllProperties())) { + newNode.setProperty(prop, value); + } }, /** diff --git a/ts/input/tex/ParseMethods.ts b/ts/input/tex/ParseMethods.ts index 934423f8c..c3e037dde 100644 --- a/ts/input/tex/ParseMethods.ts +++ b/ts/input/tex/ParseMethods.ts @@ -80,12 +80,12 @@ const ParseMethods = { digit(parser: TexParser, _c: string): ParseResult { const pattern = parser.configuration.options['numberPattern']; const n = parser.string.slice(parser.i - 1).match(pattern); - // @test Integer Font - const def = ParseUtil.getFontDef(parser); if (!n) { // @test Decimal Point, Decimal Point European return false; } + // @test Integer Font + const def = ParseUtil.getFontDef(parser); // @test Integer, Number, Decimal (European) const mml = parser.create('token', 'mn', def, n[0].replace(/[{}]/g, '')); parser.i += n[0].length - 1; diff --git a/ts/input/tex/StackItem.ts b/ts/input/tex/StackItem.ts index f230ecbbb..82c405367 100644 --- a/ts/input/tex/StackItem.ts +++ b/ts/input/tex/StackItem.ts @@ -25,6 +25,7 @@ import { MmlNode } from '../../core/MmlTree/MmlNode.js'; import { FactoryNodeClass } from '../../core/Tree/Factory.js'; import TexError from './TexError.js'; import StackItemFactory from './StackItemFactory.js'; +import { TexConstant } from './TexConstants.js'; // Union types for abbreviation. export type EnvProp = string | number | boolean; @@ -351,6 +352,14 @@ export interface StackItem extends NodeStack { * @returns {CheckType} True/false or an item or node. */ checkItem(item: StackItem): CheckType; + + /** + * Adds auxiliary attributes for LaTeX output to node. + * + * @param {MmlNode} node The current node. + * @param {string=} prefix A prefix for the LaTeX command. + */ + addLatexItem(node: MmlNode, prefix?: string): void; } export interface StackItemClass extends FactoryNodeClass { @@ -563,4 +572,21 @@ export abstract class BaseItem extends MmlStack implements StackItem { const CLASS = this.constructor as typeof BaseItem; return CLASS.errors[kind] || BaseItem.errors[kind]; } + + /** + * Adds auxiliary attributes for LaTeX output to node. + * + * @param {MmlNode} node The current node. + * @param {string=} prefix A prefix for the LaTeX command. + */ + public addLatexItem(node: MmlNode, prefix: string = '') { + const str = this.startStr.slice(this.startI, this.stopI); + if (str) { + const tex = prefix ? prefix + str : str; + node.attributes.set(TexConstant.Attr.LATEXITEM, tex); + if (tex !== '}') { + node.attributes.set(TexConstant.Attr.LATEX, tex); + } + } + } } diff --git a/ts/input/tex/Tags.ts b/ts/input/tex/Tags.ts index aad89bbf7..4daae6889 100644 --- a/ts/input/tex/Tags.ts +++ b/ts/input/tex/Tags.ts @@ -55,7 +55,7 @@ export class TagInfo { * is, but align* is not). * @param {string} tag The tag name (e.g., 1). * @param {string} tagId The unique id for that tag (e.g., mjx-eqn:1). - * @param {string} tagFormat The formatted tag (e.g., "(1)"). + * @param {string|string[]} tagFormat The formatted tag (e.g., "$\bullet$" or ['(','1',')']). * @param {boolean} noTag A no tagging command has been set (e.g., \notag, * \nonumber). * @param {string} labelId The label referring to the tag. @@ -66,7 +66,7 @@ export class TagInfo { readonly defaultTags: boolean = false, public tag: string = null, public tagId: string = '', - public tagFormat: string = '', + public tagFormat: string | string[] = '', public noTag: boolean = false, public labelId: string = '' ) {} @@ -149,7 +149,7 @@ export interface Tags { * @param {string} tag The tag string. * @returns {string} The formatted numbered tag. */ - formatTag(tag: string): string; + formatTag(tag: string): string | string[]; /** * How to format references to tags. @@ -157,7 +157,7 @@ export interface Tags { * @param {string} tag The tag string. * @returns {string} The formatted numbered tag. */ - formatRef(tag: string): string; + formatRef(tag: string): string | string[]; /** * How to format URLs for references. @@ -390,7 +390,7 @@ export class AbstractTags implements Tags { * @override */ public formatTag(tag: string) { - return '(' + tag + ')'; + return ['(', tag, ')']; } /** @@ -566,8 +566,12 @@ export class AbstractTags implements Tags { ); this.label = ''; } + const format = this.currentTag.tagFormat; + const tag = Array.isArray(format) + ? format + : format.match(/^(\(|\[|\{)(.*)(\}|\]|\))$/)?.slice(1) || [format]; const mml = new TexParser( - '\\text{' + this.currentTag.tagFormat + '}', + tag.map((part) => (part ? `\\text{${part}}` : '')).join(''), {}, this.configuration ).mml(); diff --git a/ts/input/tex/TexParser.ts b/ts/input/tex/TexParser.ts index 237812a46..766a089d8 100644 --- a/ts/input/tex/TexParser.ts +++ b/ts/input/tex/TexParser.ts @@ -99,7 +99,6 @@ export default class TexParser { this.stack = new Stack(this.itemFactory, ENV, inner ? isInner : true); this.Parse(); this.Push(this.itemFactory.create('stop')); - this.updateResult(this.string, this.i); this.stack.env = ENV; } @@ -149,9 +148,17 @@ export default class TexParser { */ public parse(kind: HandlerType, input: ParseInput): ParseResult { const i = this.saveI; - this.saveI = this.i; + // + // Back up one to pick up the parsed character (this.i is past it at this point). + // + this.saveI = this.i - (kind === 'character' && input[1] !== '&' ? 1 : 0); const result = this.configuration.handlers.get(kind).parse(input); - this.updateResult(input[1], i); + // + // The macro gets processed by the \\ character later + // + if (kind !== 'macro') { + this.updateResult(input[1], i); + } this.saveI = i; return result; } @@ -246,7 +253,10 @@ export default class TexParser { } const node = this.stack.Top().First; this.configuration.popParser(); - node.attributes.set(TexConstant.Attr.LATEX, this.string); + const latex = this.trimTex(this.string); + if (latex) { + node.attributes.set(TexConstant.Attr.LATEX, latex); + } return node; } @@ -613,6 +623,16 @@ export default class TexParser { return node; } + /** + * Trim spaces from the ends of a TeX string, leave a space when the last item is "\ " + * + * @param {string} tex The TeX string to trim + * @returns {string} The trimmed string + */ + protected trimTex(tex: string): string { + return tex.trim() + (tex.match(/(?:^|[^\\])(?:\\\\)*\\\s+$/) ? ' ' : ''); + } + /** * Finalizes the LaTeX for the topmost Mml element on the stack after parsing * has been completed. @@ -626,68 +646,99 @@ export default class TexParser { if (!node) { return; } - // TODO: This can probably be removed once processed. But needs more - // testing. + const LATEX = TexConstant.Attr.LATEX; + // + // Check if there is a latex-item and process it if there isn't already latex attached. + // + const latex = node.attributes.get(LATEX); const existing = node.attributes.get(TexConstant.Attr.LATEXITEM); if (existing !== undefined) { - node.attributes.set(TexConstant.Attr.LATEX, existing); - return; - } - old = old < this.saveI ? this.saveI : old; - let str = old !== this.i ? this.string.slice(old, this.i) : input; - str = str.trim(); - if (!str) { - return; - } - if (input === '\\') { - str = '\\' + str; - } - // These are the cases to handle sub and superscripts. - if ( - node.attributes.get(TexConstant.Attr.LATEX) === '^' && - str !== '^' && - str !== '\\^' - ) { - if (node.childNodes[2]) { - if (str === '}') { - this.composeBraces(node.childNodes[2]); + if (!latex) { + if (input === '}' || existing === '}') { + this.composeBraces(node); } else { - node.childNodes[2].attributes.set(TexConstant.Attr.LATEX, str); + node.attributes.set(LATEX, existing); } } - if (node.childNodes[1]) { - const sub = node.childNodes[1].attributes.get(TexConstant.Attr.LATEX); - this.composeLatex(node, `_${sub}^`, 0, 2); - } else { - this.composeLatex(node, '^', 0, 2); - } return; } - if ( - node.attributes.get(TexConstant.Attr.LATEX) === '_' && - str !== '_' && - str !== '\\_' - ) { - if (node.childNodes[1]) { - if (str === '}') { - this.composeBraces(node.childNodes[1]); - } else { - node.childNodes[1].attributes.set(TexConstant.Attr.LATEX, str); - } - } - if (node.childNodes[2]) { - const sub = node.childNodes[2].attributes.get(TexConstant.Attr.LATEX); - this.composeLatex(node, `^${sub}_`, 0, 1); - } else { - this.composeLatex(node, '_', 0, 1); - } + // + // Get the current latex sub-string and check that it isn't already the saved latex + // and that the input and string are not both backslashes (the start of a macro). + // + old = old < this.saveI ? this.saveI : old; + const str = this.trimTex( + old !== this.i ? this.string.slice(old, this.i) : input + ); + if (!str || str === latex || (input === '\\' && str === '\\')) { return; } - if (str === '}') { - this.composeBraces(node); - return; + if (str === '_' || str === '^') { + // + // If the input is _ or ^, mark that we are doing a sub-sup. + // + node.setProperty('sub-sup', str); + } else { + // + // Otherwise, if we are doing a sub-sup, do the proper one. + // + switch (node.getProperty('sub-sup')) { + case '^': + // + // If we have a superscript, process the superscript latex. + // + if (node.childNodes[2]) { + if (str === '}') { + this.composeBraces(node.childNodes[2]); + } else if (!node.childNodes[2].attributes.hasExplicit(LATEX)) { + node.childNodes[2].attributes.set(LATEX, str); + } + } + // + // Make the latex for the superscript (possibly with subscript). + // + if (node.childNodes[1]) { + const sub = node.childNodes[1].attributes.get(LATEX); + this.composeLatex(node, `_${sub}^`, 0, 2); + } else { + this.composeLatex(node, '^', 0, 2); + } + return; + + case '_': + // + // If we have a subscript, process the subscript latex. + // + if (node.childNodes[1]) { + if (str === '}') { + this.composeBraces(node.childNodes[1]); + } else if (!node.childNodes[1].attributes.hasExplicit(LATEX)) { + node.childNodes[1].attributes.set(LATEX, str); + } + } + // + // Update the latex for the subscript (possibly with superscript). + // + if (node.childNodes[2]) { + const sup = node.childNodes[2].attributes.get(LATEX); + this.composeLatex(node, `^${sup}_`, 0, 1); + } else { + this.composeLatex(node, '_', 0, 1); + } + return; + } + // + // If this is a close brace, get the braced contents. + // + if (str === '}') { + this.composeBraces(node); + return; + } } - node.attributes.set(TexConstant.Attr.LATEX, str); + // + // Otherwise, set the node's latex. + // + node.attributes.set(LATEX, str); } /** @@ -705,11 +756,12 @@ export default class TexParser { pos2: number ) { if (!node.childNodes[pos1] || !node.childNodes[pos2]) return; + const LATEX = TexConstant.Attr.LATEX; const expr = - (node.childNodes[pos1].attributes.get(TexConstant.Attr.LATEX) || '') + + (node.childNodes[pos1].attributes.get(LATEX) || '') + comp + - node.childNodes[pos2].attributes.get(TexConstant.Attr.LATEX); - node.attributes.set(TexConstant.Attr.LATEX, expr); + node.childNodes[pos2].attributes.get(LATEX); + node.attributes.set(LATEX, expr); } /** diff --git a/ts/input/tex/ams/AmsConfiguration.ts b/ts/input/tex/ams/AmsConfiguration.ts index 31d6186d1..0f3f77255 100644 --- a/ts/input/tex/ams/AmsConfiguration.ts +++ b/ts/input/tex/ams/AmsConfiguration.ts @@ -59,7 +59,7 @@ export const AmsConfiguration = Configuration.create('ams', { [ConfigurationType.OPTIONS]: { multlineWidth: '', ams: { - operatornamePattern: /^[-*a-zA-Z]+/, // multiLetterIdentifier for \operatorname + operatornamePattern: /^[-*a-zA-Z0-9]+/, // multiLetterIdentifier for \operatorname multlineWidth: '100%', // The width to use for multline environments. multlineIndent: '1em', // The margin to use on both sides of multline environments. }, diff --git a/ts/input/tex/ams/AmsMethods.ts b/ts/input/tex/ams/AmsMethods.ts index 0a6bb2958..d1ea310da 100644 --- a/ts/input/tex/ams/AmsMethods.ts +++ b/ts/input/tex/ams/AmsMethods.ts @@ -359,13 +359,18 @@ export const AmsMethods: { [key: string]: ParseMethod } = { font: TexConstant.Variant.NORMAL, multiLetterIdentifiers: parser.options.ams.operatornamePattern, operatorLetters: true, + noAutoOP: true, }, parser.configuration ).mml(); // - // If we get something other than a single mi, wrap in a TeXAtom. + // If we get a single mi, remove the autoOp property + // (it will get that automatically if more than one letter), + // otherwise wrap the results in a TeXAtom. // - if (!mml.isKind('mi')) { + if (mml.isKind('mi')) { + mml.removeProperty('autoOP'); + } else { mml = parser.create('node', 'TeXAtom', [mml]); } // @@ -555,7 +560,7 @@ export const AmsMethods: { [key: string]: ParseMethod } = { let arrow = parser.create( 'token', 'mo', - { stretchy: true, texClass: TEXCLASS.REL }, + { stretchy: true, texClass: TEXCLASS.ORD }, // REL is applied in a TeXAtom below String.fromCodePoint(chr) ); if (m) { @@ -583,7 +588,23 @@ export const AmsMethods: { [key: string]: ParseMethod } = { // @test Above Left Arrow, Above Right Arrow, Above Left Arrow in Context, // Above Right Arrow in Context NodeUtil.setProperty(mml, 'subsupOK', true); - parser.Push(mml); + // + // Use an empty item to prevent the xarrow from further stretching (see #3457) + // and enclose both in a TeXAtom to make the combination a REL. + // + parser.Push( + parser.create( + 'node', + 'TeXAtom', + [ + parser.create('node', 'TeXAtom', [], { + texClass: TEXCLASS.NONE, + }), + mml, + ], + { texClass: TEXCLASS.REL } + ) + ); }, /** diff --git a/ts/input/tex/amscd/AmsCdMethods.ts b/ts/input/tex/amscd/AmsCdMethods.ts index 94660fc9e..7afdc01ae 100644 --- a/ts/input/tex/amscd/AmsCdMethods.ts +++ b/ts/input/tex/amscd/AmsCdMethods.ts @@ -64,9 +64,11 @@ const AmsCdMethods: { [key: string]: ParseMethod } = { * @returns {void} No value. */ arrow(parser: TexParser, name: string): void { - const c = parser.string.charAt(parser.i); + const i = parser.i; + const c = parser.GetNext(); if (!c.match(/[>([ +export const MhchemReplacements = new Map< + string | ((match: string, arrow: string) => string), + RegExp +>([ [ '\\mhchemx$3[$1]{$2}', /\\underset{\\lower2mu{(.*?)}}{\\overset{(.*?)}{\\long(.*?)}}/g, @@ -72,7 +79,12 @@ export const MhchemReplacements = new Map([ /\\rlap\{\\lower\.2em\{-\}\}\\rlap\{\\raise.2em\{-\}\}\\tripledash/g, ], [ - '\\mhchem$1', + (match: string, arrow: string) => { + const mharrow = `mhchem${arrow}`; + return mhchemChars.lookup(mharrow) || mhchemMacros.lookup(mharrow) + ? `\\${mharrow}` + : match; + }, /\\(x?(?:long)?(?:left|right|[Ll]eftright|[Rr]ightleft)(?:arrow|harpoons))/g, ], ]); @@ -90,7 +102,7 @@ export const MhchemMethods: { [key: string]: ParseMethod } = { try { tex = mhchemParser.toTex(arg, machine); for (const [name, pattern] of MhchemReplacements.entries()) { - tex = tex.replace(pattern, name); + tex = tex.replace(pattern, name as string); } } catch (err) { throw new TexError(err[0], err[1]); @@ -106,7 +118,7 @@ export const MhchemMethods: { [key: string]: ParseMethod } = { /** * The command macros */ -new CommandMap('mhchem', { +const mhchemMacros = new CommandMap('mhchem', { ce: [MhchemMethods.Machine, 'ce'], pu: [MhchemMethods.Machine, 'pu'], mhchemxrightarrow: [MhchemMethods.xArrow, 0xe429, 5, 9], @@ -121,7 +133,7 @@ new CommandMap('mhchem', { /** * The character macros */ -new CharacterMap('mhchem-chars', MhchemUtils.relmo, { +const mhchemChars = new CharacterMap('mhchem-chars', MhchemUtils.relmo, { tripledash: ['\uE410', { stretchy: false }], mhchemBondTD: ['\uE411', { stretchy: false }], mhchemBondTDD: ['\uE412', { stretchy: false }], @@ -133,8 +145,8 @@ new CharacterMap('mhchem-chars', MhchemUtils.relmo, { mhchemlongRightleftharpoons: '\uE409', mhchemlongLeftrightharpoons: '\uE40A', mhchemlongleftrightarrows: '\uE42B', - mhchemrightarrow: '\uE42C', - mhchemleftarrow: '\uE42D', + mhchemrightarrow: '\uE42D', + mhchemleftarrow: '\uE42C', mhchemleftrightarrow: '\uE42E', }); diff --git a/ts/input/tex/tagformat/TagFormatConfiguration.ts b/ts/input/tex/tagformat/TagFormatConfiguration.ts index b3a5d08b7..0e1d16e23 100644 --- a/ts/input/tex/tagformat/TagFormatConfiguration.ts +++ b/ts/input/tex/tagformat/TagFormatConfiguration.ts @@ -125,7 +125,7 @@ export const TagFormatConfiguration = Configuration.create('tagformat', { [ConfigurationType.OPTIONS]: { tagformat: { number: (n: number) => n.toString(), - tag: (tag: string) => '(' + tag + ')', + tag: (tag: string) => ['(', tag, ')'], ref: '', // means use the tag function id: (id: string) => 'mjx-eqn:' + id.replace(/\s/g, '_'), url: (id: string, base: string) => base + '#' + encodeURIComponent(id), diff --git a/ts/output/chtml.ts b/ts/output/chtml.ts index 68bf483c0..f2edddf5c 100644 --- a/ts/output/chtml.ts +++ b/ts/output/chtml.ts @@ -110,13 +110,22 @@ export class CHTML extends CommonOutputJax< 'mjx-mtext > mjx-c', ].join(', ')]: { 'clip-path': - 'padding-box xywh(-1em -2px calc(100% + 2em) calc(100% + 4px))', + 'padding-box polygon(' + + [ + '-1em -2px', + 'calc(100% + 1em) -2px', + 'calc(100% + 1em) calc(100% + 2px)', + '-1em calc(100% + 2px)', + ].join(', ') + + ')', }, 'mjx-stretchy-h': { - 'clip-path': 'padding-box xywh(0 -2px 100% calc(100% + 4px))', + 'clip-path': + 'padding-box polygon(0 -2px, 100% -2px, 100% calc(100% + 2px), 0 calc(100% + 2px))', }, 'mjx-stretchy-v': { - 'clip-path': 'padding-box xywh(-2px 0 calc(100% + 4px) 100%)', + 'clip-path': + 'padding-box polygon(-2px 0, calc(100% + 2px) 0, calc(100% + 2px) 100%, -2px 100%)', }, 'mjx-container [space="1"]': { 'margin-left': '.111em' }, @@ -147,10 +156,21 @@ export class CHTML extends CommonOutputJax< 'mjx-block': { display: 'block' }, 'mjx-itable': { display: 'inline-table' }, 'mjx-row': { display: 'table-row' }, - 'mjx-row > *': { display: 'table-cell' }, + [['cell', 'base', 'under', 'over', 'den'] + .map((node) => `mjx-row > mjx-${node}`) + .join(', ')]: { display: 'table-cell' }, 'mjx-container [inline-breaks]': { display: 'inline' }, + 'mjx-container .mjx-selected': { + outline: '2px solid black', + }, + '@media (prefers-color-scheme: dark)': { + 'mjx-container .mjx-selected': { + outline: '2px solid #C8C8C8', + }, + }, + // // These don't have Wrapper subclasses, so add their styles here // diff --git a/ts/output/chtml/FontData.ts b/ts/output/chtml/FontData.ts index 4698d725e..a5180c61b 100644 --- a/ts/output/chtml/FontData.ts +++ b/ts/output/chtml/FontData.ts @@ -119,7 +119,7 @@ export class ChtmlFontData extends FontData< protected static defaultStyles = {}; /** - * The default @font-face declarations with %%URL%% where the font path should go + * The default `@font-face` declarations with `%%URL%%` where the font path should go */ protected static defaultFonts = {}; @@ -149,7 +149,7 @@ export class ChtmlFontData extends FontData< public fontUsage: StyleJson = {}; /** - * Number of new @font-face entries that have been processed + * Number of new `@font-face` entries that have been processed */ protected newFonts: number = 0; @@ -583,6 +583,9 @@ export class ChtmlFontData extends FontData< padding: this.padding(HDW as ChtmlCharData, w - HDW[2]), }; if (part === 'ext') { + const padding = (css.padding as string).split(/ /); + padding[1] = padding[3] = '0'; + css.padding = padding.join(' '); if (!w && options.dx) { w = 2 * options.dx - 0.06; } @@ -633,7 +636,7 @@ export class ChtmlFontData extends FontData< if (options.oc) { styles[selector + '[noic]'] = { 'padding-right': this.em(data[2]) }; } - this.checkCombiningChar(options, styles[selector]); + this.checkCombiningChar(options, styles[selector] as StyleJsonData); } /** @@ -653,6 +656,10 @@ export class ChtmlFontData extends FontData< pad.pop(); } css.padding = pad.join(' '); + if (css.width === '0' && options.dx) { + css.width = this.em(2 * options.dx); + css['margin-left'] = '-' + css.width; + } } /***********************************************************************/ diff --git a/ts/output/chtml/Wrapper.ts b/ts/output/chtml/Wrapper.ts index d4dda606f..0fba22898 100644 --- a/ts/output/chtml/Wrapper.ts +++ b/ts/output/chtml/Wrapper.ts @@ -71,31 +71,30 @@ export type ChtmlConstructor = Constructor>; /** * The type of the ChtmlWrapper class (used when creating the wrapper factory for this class) */ -export interface ChtmlWrapperClass - extends CommonWrapperClass< - // - // The HTMLElement, TextNode, and Document classes (for the DOM implementation in use) - // - N, - T, - D, - // - // The Wrapper type and its Factory and Class (these need to know N, T, and D) - // - CHTML, - ChtmlWrapper, - ChtmlWrapperFactory, - ChtmlWrapperClass, - // - // These are font-related objects that depend on the output jax; e,g. the character options - // for CHTML and SVG output differ (CHTML contains font information, while SVG has path data) - // - ChtmlCharOptions, - ChtmlVariantData, - ChtmlDelimiterData, - ChtmlFontData, - ChtmlFontDataClass - > { +export interface ChtmlWrapperClass extends CommonWrapperClass< + // + // The HTMLElement, TextNode, and Document classes (for the DOM implementation in use) + // + N, + T, + D, + // + // The Wrapper type and its Factory and Class (these need to know N, T, and D) + // + CHTML, + ChtmlWrapper, + ChtmlWrapperFactory, + ChtmlWrapperClass, + // + // These are font-related objects that depend on the output jax; e,g. the character options + // for CHTML and SVG output differ (CHTML contains font information, while SVG has path data) + // + ChtmlCharOptions, + ChtmlVariantData, + ChtmlDelimiterData, + ChtmlFontData, + ChtmlFontDataClass +> { /** * If true, this causes a style for the node type to be generated automatically * that sets display:inline-block (as needed for the output for MmlNodes). @@ -444,11 +443,15 @@ export class ChtmlWrapper extends CommonWrapper< const adaptor = this.adaptor; if (align === 'center' || align === 'left') { const L = this.getBBox().L; - adaptor.setStyle(chtml, 'margin-left', this.em(shift + L)); + if (shift + L) { + adaptor.setStyle(chtml, 'margin-left', this.em(shift + L)); + } } if (align === 'center' || align === 'right') { const R = this.getBBox().R; - adaptor.setStyle(chtml, 'margin-right', this.em(-shift + R)); + if (shift + R) { + adaptor.setStyle(chtml, 'margin-right', this.em(-shift + R)); + } } } diff --git a/ts/output/chtml/Wrappers/HtmlNode.ts b/ts/output/chtml/Wrappers/HtmlNode.ts index a61c41f36..0227a813b 100644 --- a/ts/output/chtml/Wrappers/HtmlNode.ts +++ b/ts/output/chtml/Wrappers/HtmlNode.ts @@ -48,8 +48,11 @@ export interface ChtmlHtmlNodeNTD extends ChtmlXmlNodeNTD {} * @template T The Text node class * @template D The Document class */ -export interface ChtmlHtmlNodeClass - extends ChtmlXmlNodeClass { +export interface ChtmlHtmlNodeClass extends ChtmlXmlNodeClass< + N, + T, + D +> { new ( factory: ChtmlWrapperFactory, node: MmlNode, diff --git a/ts/output/chtml/Wrappers/TeXAtom.ts b/ts/output/chtml/Wrappers/TeXAtom.ts index 0c6b16be4..2b104def3 100644 --- a/ts/output/chtml/Wrappers/TeXAtom.ts +++ b/ts/output/chtml/Wrappers/TeXAtom.ts @@ -48,7 +48,8 @@ import { MmlNode, TEXCLASSNAMES } from '../../../core/MmlTree/MmlNode.js'; * @template D The Document class */ export interface ChtmlTeXAtomNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonTeXAtom< N, T, @@ -72,7 +73,8 @@ export interface ChtmlTeXAtomNTD * @template D The Document class */ export interface ChtmlTeXAtomClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonTeXAtomClass< N, T, diff --git a/ts/output/chtml/Wrappers/TextNode.ts b/ts/output/chtml/Wrappers/TextNode.ts index 9a33ae2ab..fb1bd256c 100644 --- a/ts/output/chtml/Wrappers/TextNode.ts +++ b/ts/output/chtml/Wrappers/TextNode.ts @@ -50,7 +50,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlTextNodeNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonTextNode< N, T, @@ -74,7 +75,8 @@ export interface ChtmlTextNodeNTD * @template D The Document class */ export interface ChtmlTextNodeClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonTextNodeClass< N, T, diff --git a/ts/output/chtml/Wrappers/maction.ts b/ts/output/chtml/Wrappers/maction.ts index eddafb694..fb9df6c2b 100644 --- a/ts/output/chtml/Wrappers/maction.ts +++ b/ts/output/chtml/Wrappers/maction.ts @@ -53,7 +53,8 @@ import { STATE } from '../../../core/MathItem.js'; * @template D The Document class */ export interface ChtmlMactionNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMaction< N, T, @@ -95,7 +96,8 @@ export interface ChtmlMactionNTD * @template D The Document class */ export interface ChtmlMactionClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMactionClass< N, T, @@ -188,6 +190,25 @@ export const ChtmlMaction = (function (): ChtmlMactionClass { 'background-color': '#F8F8F8', color: 'black', }, + 'mjx-container [data-mjx-collapsed]': { + color: '#55F', + }, + + '@media (prefers-color-scheme: dark) /* chtml maction */': { + 'mjx-tool > mjx-tip': { + border: '1px solid #888', + 'background-color': '#303030', + color: '#E0E0E0', + 'box-shadow': '2px 2px 5px #000', + }, + 'mjx-status': { + 'background-color': '#303030', + color: '#E0E0E0', + }, + 'mjx-container [data-mjx-collapsed]': { + color: '#88F', + }, + }, }; /** diff --git a/ts/output/chtml/Wrappers/math.ts b/ts/output/chtml/Wrappers/math.ts index 98976c7c3..5c2047531 100644 --- a/ts/output/chtml/Wrappers/math.ts +++ b/ts/output/chtml/Wrappers/math.ts @@ -50,7 +50,8 @@ import { BBox } from '../../../util/BBox.js'; * @template D The Document class */ export interface ChtmlMathNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMath< N, T, @@ -74,7 +75,8 @@ export interface ChtmlMathNTD * @template D The Document class */ export interface ChtmlMathClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMathClass< N, T, diff --git a/ts/output/chtml/Wrappers/menclose.ts b/ts/output/chtml/Wrappers/menclose.ts index e1734d553..a6584cf93 100644 --- a/ts/output/chtml/Wrappers/menclose.ts +++ b/ts/output/chtml/Wrappers/menclose.ts @@ -70,7 +70,8 @@ const ANGLE = Angle(Notation.ARROWDX, Notation.ARROWY); * @template D The Document class */ export interface ChtmlMencloseNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMenclose< N, T, @@ -124,7 +125,8 @@ export interface ChtmlMencloseNTD * @template D The Document class */ export interface ChtmlMencloseClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMencloseClass< N, T, @@ -250,45 +252,63 @@ export const ChtmlMenclose = (function (): ChtmlMencloseClass< height: 0, width: 0, }, - 'mjx-menclose > mjx-arrow > *': { + 'mjx-menclose > mjx-arrow > mjx-aline': { display: 'block', position: 'absolute', - 'transform-origin': 'bottom', - 'border-left': em(Notation.THICKNESS * Notation.ARROWX) + ' solid', - 'border-right': 0, 'box-sizing': 'border-box', - }, - 'mjx-menclose > mjx-arrow > mjx-aline': { + 'transform-origin': 'bottom', left: 0, top: em(-Notation.THICKNESS / 2), right: em(Notation.THICKNESS * (Notation.ARROWX - 1)), height: 0, 'border-top': em(Notation.THICKNESS) + ' solid', 'border-left': 0, + 'border-right': 0, }, 'mjx-menclose > mjx-arrow[double] > mjx-aline': { + display: 'block', + position: 'absolute', + 'box-sizing': 'border-box', + 'transform-origin': 'bottom', left: em(Notation.THICKNESS * (Notation.ARROWX - 1)), height: 0, + 'border-left': em(Notation.THICKNESS * Notation.ARROWX) + ' solid', + 'border-right': 0, }, 'mjx-menclose > mjx-arrow > mjx-rthead': { + display: 'block', + position: 'absolute', + 'box-sizing': 'border-box', + 'transform-origin': 'bottom', transform: 'skewX(' + ANGLE + 'rad)', right: 0, bottom: '-1px', + 'border-left': em(Notation.THICKNESS * Notation.ARROWX) + ' solid', + 'border-right': 0, 'border-bottom': '1px solid transparent', 'border-top': em(Notation.THICKNESS * Notation.ARROWY) + ' solid transparent', }, 'mjx-menclose > mjx-arrow > mjx-rbhead': { + display: 'block', + position: 'absolute', + 'box-sizing': 'border-box', transform: 'skewX(-' + ANGLE + 'rad)', 'transform-origin': 'top', right: 0, top: '-1px', + 'border-left': em(Notation.THICKNESS * Notation.ARROWX) + ' solid', + 'border-right': 0, 'border-top': '1px solid transparent', 'border-bottom': em(Notation.THICKNESS * Notation.ARROWY) + ' solid transparent', }, 'mjx-menclose > mjx-arrow > mjx-lthead': { + display: 'block', + position: 'absolute', + 'box-sizing': 'border-box', transform: 'skewX(-' + ANGLE + 'rad)', + 'transform-origin': 'bottom', left: 0, bottom: '-1px', 'border-left': 0, @@ -298,6 +318,9 @@ export const ChtmlMenclose = (function (): ChtmlMencloseClass< em(Notation.THICKNESS * Notation.ARROWY) + ' solid transparent', }, 'mjx-menclose > mjx-arrow > mjx-lbhead': { + display: 'block', + position: 'absolute', + 'box-sizing': 'border-box', transform: 'skewX(' + ANGLE + 'rad)', 'transform-origin': 'top', left: 0, @@ -314,7 +337,7 @@ export const ChtmlMenclose = (function (): ChtmlMencloseClass< bottom: '50%', left: 0, width: em(1.5 * Notation.PADDING), - border: em(Notation.THICKNESS) + ' solid', + 'border-width': em(Notation.THICKNESS), 'border-style': 'solid solid none none', 'border-radius': '0 100% 0 0', 'box-sizing': 'border-box', diff --git a/ts/output/chtml/Wrappers/mfenced.ts b/ts/output/chtml/Wrappers/mfenced.ts index 33574d7d9..8d56d5e90 100644 --- a/ts/output/chtml/Wrappers/mfenced.ts +++ b/ts/output/chtml/Wrappers/mfenced.ts @@ -49,7 +49,8 @@ import { ChtmlInferredMrowNTD } from './mrow.js'; * @template D The Document class */ export interface ChtmlMfencedNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMfenced< N, T, @@ -73,7 +74,8 @@ export interface ChtmlMfencedNTD * @template D The Document class */ export interface ChtmlMfencedClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMfencedClass< N, T, diff --git a/ts/output/chtml/Wrappers/mfrac.ts b/ts/output/chtml/Wrappers/mfrac.ts index 61bf7e198..7d061c517 100644 --- a/ts/output/chtml/Wrappers/mfrac.ts +++ b/ts/output/chtml/Wrappers/mfrac.ts @@ -51,7 +51,8 @@ import { OptionList } from '../../../util/Options.js'; * @template D The Document class */ export interface ChtmlMfracNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMfrac< N, T, @@ -75,7 +76,8 @@ export interface ChtmlMfracNTD * @template D The Document class */ export interface ChtmlMfracClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMfracClass< N, T, @@ -154,7 +156,7 @@ export const ChtmlMfrac = (function (): ChtmlMfracClass { display: 'inline-table', width: '100%' }, - 'mjx-dtable > *': { + 'mjx-dtable > mjx-line, mjx-dtable > mjx-row': { 'font-size': '2000%' }, 'mjx-dbox': { diff --git a/ts/output/chtml/Wrappers/mglyph.ts b/ts/output/chtml/Wrappers/mglyph.ts index a035338ee..79bc72a72 100644 --- a/ts/output/chtml/Wrappers/mglyph.ts +++ b/ts/output/chtml/Wrappers/mglyph.ts @@ -50,7 +50,8 @@ import { StyleJson, StyleJsonData } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMglyphNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMglyph< N, T, @@ -74,7 +75,8 @@ export interface ChtmlMglyphNTD * @template D The Document class */ export interface ChtmlMglyphClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMglyphClass< N, T, diff --git a/ts/output/chtml/Wrappers/mi.ts b/ts/output/chtml/Wrappers/mi.ts index 69084660c..7dabcc60c 100644 --- a/ts/output/chtml/Wrappers/mi.ts +++ b/ts/output/chtml/Wrappers/mi.ts @@ -48,7 +48,8 @@ import { MmlMi } from '../../../core/MmlTree/MmlNodes/mi.js'; * @template D The Document class */ export interface ChtmlMiNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMi< N, T, @@ -72,7 +73,8 @@ export interface ChtmlMiNTD * @template D The Document class */ export interface ChtmlMiClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMiClass< N, T, diff --git a/ts/output/chtml/Wrappers/mmultiscripts.ts b/ts/output/chtml/Wrappers/mmultiscripts.ts index ae805837e..d73212a30 100644 --- a/ts/output/chtml/Wrappers/mmultiscripts.ts +++ b/ts/output/chtml/Wrappers/mmultiscripts.ts @@ -52,7 +52,8 @@ import { split } from '../../../util/string.js'; * @template D The Document class */ export interface ChtmlMmultiscriptsNTD - extends ChtmlMsubsupNTD, + extends + ChtmlMsubsupNTD, CommonMmultiscripts< N, T, @@ -76,7 +77,8 @@ export interface ChtmlMmultiscriptsNTD * @template D The Document class */ export interface ChtmlMmultiscriptsClass - extends ChtmlMsubsupClass, + extends + ChtmlMsubsupClass, CommonMmultiscriptsClass< N, T, diff --git a/ts/output/chtml/Wrappers/mn.ts b/ts/output/chtml/Wrappers/mn.ts index a547a864a..37101f478 100644 --- a/ts/output/chtml/Wrappers/mn.ts +++ b/ts/output/chtml/Wrappers/mn.ts @@ -48,7 +48,8 @@ import { MmlMn } from '../../../core/MmlTree/MmlNodes/mn.js'; * @template D The Document class */ export interface ChtmlMnNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMn< N, T, @@ -72,7 +73,8 @@ export interface ChtmlMnNTD * @template D The Document class */ export interface ChtmlMnClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMnClass< N, T, diff --git a/ts/output/chtml/Wrappers/mo.ts b/ts/output/chtml/Wrappers/mo.ts index 6d5e06142..404708054 100644 --- a/ts/output/chtml/Wrappers/mo.ts +++ b/ts/output/chtml/Wrappers/mo.ts @@ -51,7 +51,8 @@ import { DIRECTION } from '../FontData.js'; * @template D The Document class */ export interface ChtmlMoNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMo< N, T, @@ -75,7 +76,8 @@ export interface ChtmlMoNTD * @template D The Document class */ export interface ChtmlMoClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMoClass< N, T, @@ -141,13 +143,16 @@ export const ChtmlMo = (function (): ChtmlMoClass { 'mjx-stretchy-h': { display: 'inline-block', }, - 'mjx-stretchy-h > *': { + [['beg', 'ext', 'end', 'mid'] + .map((node) => `mjx-stretchy-h > mjx-${node}`) + .join(', ')]: { display: 'inline-block', width: 0, 'text-align': 'right', }, 'mjx-stretchy-h > mjx-ext': { - 'clip-path': 'padding-box xywh(0 -1em 100% calc(100% + 2em))', + 'clip-path': + 'padding-box polygon(0 -1em, 100% -1em, 100% calc(100% + 1em), 0 calc(100% + 1em))', width: '100%', border: '0px solid transparent', 'box-sizing': 'border-box', @@ -157,7 +162,9 @@ export const ChtmlMo = (function (): ChtmlMoClass { display: 'inline-block', 'text-align': 'center', }, - 'mjx-stretchy-v > *': { + [['beg', 'ext', 'end', 'mid'] + .map((node) => `mjx-stretchy-v > mjx-${node}`) + .join(', ')]: { display: 'block', height: 0, margin: '0 auto', @@ -166,11 +173,12 @@ export const ChtmlMo = (function (): ChtmlMoClass { display: 'block', }, 'mjx-stretchy-v > mjx-ext': { - 'clip-path': 'padding-box xywh(-1em 0 calc(100% + 2em) 100%)', + 'clip-path': + 'padding-box polygon(-1em 0, calc(100% + 1em) 0, calc(100% + 1em) 100%, -1em 100%)', height: '100%', border: '0.1px solid transparent', 'box-sizing': 'border-box', - 'white-space': 'wrap', + 'white-space': 'pre', }, 'mjx-mark': { display: 'inline-block', diff --git a/ts/output/chtml/Wrappers/mpadded.ts b/ts/output/chtml/Wrappers/mpadded.ts index 8f1498b5a..f2c47de65 100644 --- a/ts/output/chtml/Wrappers/mpadded.ts +++ b/ts/output/chtml/Wrappers/mpadded.ts @@ -49,7 +49,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMpaddedNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMpadded< N, T, @@ -73,7 +74,8 @@ export interface ChtmlMpaddedNTD * @template D The Document class */ export interface ChtmlMpaddedClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMpaddedClass< N, T, diff --git a/ts/output/chtml/Wrappers/mroot.ts b/ts/output/chtml/Wrappers/mroot.ts index 5b6f1b771..cfc8b23d9 100644 --- a/ts/output/chtml/Wrappers/mroot.ts +++ b/ts/output/chtml/Wrappers/mroot.ts @@ -50,7 +50,8 @@ import { MmlMroot } from '../../../core/MmlTree/MmlNodes/mroot.js'; * @template D The Document class */ export interface ChtmlMrootNTD - extends ChtmlMsqrtNTD, + extends + ChtmlMsqrtNTD, CommonMroot< N, T, @@ -74,7 +75,8 @@ export interface ChtmlMrootNTD * @template D The Document class */ export interface ChtmlMrootClass - extends ChtmlMsqrtClass, + extends + ChtmlMsqrtClass, CommonMrootClass< N, T, diff --git a/ts/output/chtml/Wrappers/mrow.ts b/ts/output/chtml/Wrappers/mrow.ts index 8ef1f5e83..6e3c5c7e6 100644 --- a/ts/output/chtml/Wrappers/mrow.ts +++ b/ts/output/chtml/Wrappers/mrow.ts @@ -57,7 +57,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMrowNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMrow< N, T, @@ -81,7 +82,8 @@ export interface ChtmlMrowNTD * @template D The Document class */ export interface ChtmlMrowClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMrowClass< N, T, @@ -250,8 +252,7 @@ export const ChtmlMrow = (function (): ChtmlMrowClass { ); adaptor.setAttribute(parents[i], 'align', align); if (shift) { - adaptor.setStyle(parents[i], 'position', 'relative'); - adaptor.setStyle(parents[i], 'left', this.em(shift)); + adaptor.setStyle(parents[i], 'margin-left', this.em(shift)); } if (i < n && this.jax.math.display) { adaptor.setStyle( @@ -362,7 +363,8 @@ export const ChtmlMrow = (function (): ChtmlMrowClass { * @template D The Document class */ export interface ChtmlInferredMrowNTD - extends ChtmlMrowNTD, + extends + ChtmlMrowNTD, CommonInferredMrow< N, T, @@ -386,7 +388,8 @@ export interface ChtmlInferredMrowNTD * @template D The Document class */ export interface ChtmlInferredMrowClass - extends ChtmlMrowClass, + extends + ChtmlMrowClass, CommonInferredMrowClass< N, T, diff --git a/ts/output/chtml/Wrappers/ms.ts b/ts/output/chtml/Wrappers/ms.ts index 8ff0643cc..76718ea3f 100644 --- a/ts/output/chtml/Wrappers/ms.ts +++ b/ts/output/chtml/Wrappers/ms.ts @@ -48,7 +48,8 @@ import { MmlMs } from '../../../core/MmlTree/MmlNodes/ms.js'; * @template D The Document class */ export interface ChtmlMsNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMs< N, T, @@ -72,7 +73,8 @@ export interface ChtmlMsNTD * @template D The Document class */ export interface ChtmlMsClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMsClass< N, T, diff --git a/ts/output/chtml/Wrappers/mspace.ts b/ts/output/chtml/Wrappers/mspace.ts index e791151a8..4f21136bd 100644 --- a/ts/output/chtml/Wrappers/mspace.ts +++ b/ts/output/chtml/Wrappers/mspace.ts @@ -48,7 +48,8 @@ import { MmlMspace } from '../../../core/MmlTree/MmlNodes/mspace.js'; * @template D The Document class */ export interface ChtmlMspaceNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMspace< N, T, @@ -72,7 +73,8 @@ export interface ChtmlMspaceNTD * @template D The Document class */ export interface ChtmlMspaceClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMspaceClass< N, T, diff --git a/ts/output/chtml/Wrappers/msqrt.ts b/ts/output/chtml/Wrappers/msqrt.ts index ee60298b3..e3b87c997 100644 --- a/ts/output/chtml/Wrappers/msqrt.ts +++ b/ts/output/chtml/Wrappers/msqrt.ts @@ -51,7 +51,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMsqrtNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMsqrt< N, T, @@ -75,7 +76,8 @@ export interface ChtmlMsqrtNTD * @template D The Document class */ export interface ChtmlMsqrtClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMsqrtClass< N, T, diff --git a/ts/output/chtml/Wrappers/msubsup.ts b/ts/output/chtml/Wrappers/msubsup.ts index be775d4d4..a60493832 100644 --- a/ts/output/chtml/Wrappers/msubsup.ts +++ b/ts/output/chtml/Wrappers/msubsup.ts @@ -65,7 +65,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMsubNTD - extends ChtmlScriptbaseNTD, + extends + ChtmlScriptbaseNTD, CommonMsub< N, T, @@ -89,7 +90,8 @@ export interface ChtmlMsubNTD * @template D The Document class */ export interface ChtmlMsubClass - extends ChtmlScriptbaseClass, + extends + ChtmlScriptbaseClass, CommonMsubClass< N, T, @@ -155,7 +157,8 @@ export const ChtmlMsub = (function (): ChtmlMsubClass { * @template D The Document class */ export interface ChtmlMsupNTD - extends ChtmlScriptbaseNTD, + extends + ChtmlScriptbaseNTD, CommonMsup< N, T, @@ -179,7 +182,8 @@ export interface ChtmlMsupNTD * @template D The Document class */ export interface ChtmlMsupClass - extends ChtmlScriptbaseClass, + extends + ChtmlScriptbaseClass, CommonMsupClass< N, T, @@ -245,7 +249,8 @@ export const ChtmlMsup = (function (): ChtmlMsupClass { * @template D The Document class */ export interface ChtmlMsubsupNTD - extends ChtmlScriptbaseNTD, + extends + ChtmlScriptbaseNTD, CommonMsubsup< N, T, @@ -269,7 +274,8 @@ export interface ChtmlMsubsupNTD * @template D The Document class */ export interface ChtmlMsubsupClass - extends ChtmlScriptbaseClass, + extends + ChtmlScriptbaseClass, CommonMsubsupClass< N, T, diff --git a/ts/output/chtml/Wrappers/mtable.ts b/ts/output/chtml/Wrappers/mtable.ts index f310d256d..7762d0c9a 100644 --- a/ts/output/chtml/Wrappers/mtable.ts +++ b/ts/output/chtml/Wrappers/mtable.ts @@ -52,7 +52,8 @@ import { OptionList } from '../../../util/Options.js'; * @template D The Document class */ export interface ChtmlMtableNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMtable< N, T, @@ -87,7 +88,8 @@ export interface ChtmlMtableNTD * @template D The Document class */ export interface ChtmlMtableClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMtableClass< N, T, diff --git a/ts/output/chtml/Wrappers/mtd.ts b/ts/output/chtml/Wrappers/mtd.ts index af54ec50a..bea6415c4 100644 --- a/ts/output/chtml/Wrappers/mtd.ts +++ b/ts/output/chtml/Wrappers/mtd.ts @@ -49,7 +49,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMtdNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMtd< N, T, @@ -73,7 +74,8 @@ export interface ChtmlMtdNTD * @template D The Document class */ export interface ChtmlMtdClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMtdClass< N, T, diff --git a/ts/output/chtml/Wrappers/mtext.ts b/ts/output/chtml/Wrappers/mtext.ts index 3956888c8..75f0e9f51 100644 --- a/ts/output/chtml/Wrappers/mtext.ts +++ b/ts/output/chtml/Wrappers/mtext.ts @@ -48,7 +48,8 @@ import { MmlMtext } from '../../../core/MmlTree/MmlNodes/mtext.js'; * @template D The Document class */ export interface ChtmlMtextNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMtext< N, T, @@ -72,7 +73,8 @@ export interface ChtmlMtextNTD * @template D The Document class */ export interface ChtmlMtextClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMtextClass< N, T, diff --git a/ts/output/chtml/Wrappers/mtr.ts b/ts/output/chtml/Wrappers/mtr.ts index 920807865..127369b22 100644 --- a/ts/output/chtml/Wrappers/mtr.ts +++ b/ts/output/chtml/Wrappers/mtr.ts @@ -54,7 +54,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMtrNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonMtr< N, T, @@ -78,7 +79,8 @@ export interface ChtmlMtrNTD * @template D The Document class */ export interface ChtmlMtrClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonMtrClass< N, T, @@ -164,6 +166,8 @@ export const ChtmlMtr = (function (): ChtmlMtrClass { if (align !== 'baseline') { this.adaptor.setAttribute(this.dom[0], 'rowalign', align); } + const { h, d } = this.getBBox(); + this.adaptor.setStyle(this.dom[0], 'height', this.em(h + d)); } }; })(); @@ -177,7 +181,8 @@ export const ChtmlMtr = (function (): ChtmlMtrClass { * @template D The Document class */ export interface ChtmlMlabeledtrNTD - extends ChtmlMtrNTD, + extends + ChtmlMtrNTD, CommonMlabeledtr< N, T, @@ -201,7 +206,8 @@ export interface ChtmlMlabeledtrNTD * @template D The Document class */ export interface ChtmlMlabeledtrClass - extends ChtmlMtrClass, + extends + ChtmlMtrClass, CommonMlabeledtrClass< N, T, diff --git a/ts/output/chtml/Wrappers/munderover.ts b/ts/output/chtml/Wrappers/munderover.ts index d342e3383..8d8d0e71c 100644 --- a/ts/output/chtml/Wrappers/munderover.ts +++ b/ts/output/chtml/Wrappers/munderover.ts @@ -71,7 +71,8 @@ import { StyleJson } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlMunderNTD - extends ChtmlMsubNTD, + extends + ChtmlMsubNTD, CommonMunder< N, T, @@ -95,7 +96,8 @@ export interface ChtmlMunderNTD * @template D The Document class */ export interface ChtmlMunderClass - extends ChtmlMsubClass, + extends + ChtmlMsubClass, CommonMunderClass< N, T, @@ -214,7 +216,8 @@ export const ChtmlMunder = (function (): ChtmlMunderClass { * @template D The Document class */ export interface ChtmlMoverNTD - extends ChtmlMsupNTD, + extends + ChtmlMsupNTD, CommonMover< N, T, @@ -238,7 +241,8 @@ export interface ChtmlMoverNTD * @template D The Document class */ export interface ChtmlMoverClass - extends ChtmlMsupClass, + extends + ChtmlMsupClass, CommonMoverClass< N, T, @@ -298,7 +302,9 @@ export const ChtmlMover = (function (): ChtmlMoverClass { 'mjx-mover:not([limits="false"])': { 'padding-top': '.1em', // big_op_spacing5 }, - 'mjx-mover:not([limits="false"]) > *': { + [['base', 'over'] + .map((node) => `mjx-mover:not([limits="false"]) > mjx-${node}`) + .join(', ')]: { display: 'block', 'text-align': 'left', }, @@ -345,7 +351,8 @@ export const ChtmlMover = (function (): ChtmlMoverClass { * @template D The Document class */ export interface ChtmlMunderoverNTD - extends ChtmlMsubsupNTD, + extends + ChtmlMsubsupNTD, CommonMunderover< N, T, @@ -369,7 +376,8 @@ export interface ChtmlMunderoverNTD * @template D The Document class */ export interface ChtmlMunderoverClass - extends ChtmlMsubsupClass, + extends + ChtmlMsubsupClass, CommonMunderoverClass< N, T, @@ -436,7 +444,9 @@ export const ChtmlMunderover = (function (): ChtmlMunderoverClass< 'mjx-munderover:not([limits="false"])': { 'padding-top': '.1em', // big_op_spacing5 }, - 'mjx-munderover:not([limits="false"]) > *': { + [['over', 'box'] + .map((node) => `mjx-munderover:not([limits="false"]) > mjx-${node}`) + .join(', ')]: { display: 'block', }, }; diff --git a/ts/output/chtml/Wrappers/scriptbase.ts b/ts/output/chtml/Wrappers/scriptbase.ts index c49fdfd65..4356305d4 100644 --- a/ts/output/chtml/Wrappers/scriptbase.ts +++ b/ts/output/chtml/Wrappers/scriptbase.ts @@ -53,7 +53,8 @@ import { StyleJsonData } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface ChtmlScriptbaseNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonScriptbase< N, T, @@ -101,7 +102,8 @@ export interface ChtmlScriptbaseNTD * @template D The Document class */ export interface ChtmlScriptbaseClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonScriptbaseClass< N, T, diff --git a/ts/output/chtml/Wrappers/semantics.ts b/ts/output/chtml/Wrappers/semantics.ts index 1ac76be9a..ecc7491b0 100644 --- a/ts/output/chtml/Wrappers/semantics.ts +++ b/ts/output/chtml/Wrappers/semantics.ts @@ -61,7 +61,8 @@ import { StyleList } from '../../../util/Styles.js'; * @template D The Document class */ export interface ChtmlSemanticsNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonSemantics< N, T, @@ -85,7 +86,8 @@ export interface ChtmlSemanticsNTD * @template D The Document class */ export interface ChtmlSemanticsClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonSemanticsClass< N, T, @@ -226,7 +228,8 @@ export const ChtmlAnnotationXML = (function (): ChtmlWrapperClass< * @template D The Document class */ export interface ChtmlXmlNodeNTD - extends ChtmlWrapper, + extends + ChtmlWrapper, CommonXmlNode< N, T, @@ -250,7 +253,8 @@ export interface ChtmlXmlNodeNTD * @template D The Document class */ export interface ChtmlXmlNodeClass - extends ChtmlWrapperClass, + extends + ChtmlWrapperClass, CommonXmlNodeClass< N, T, diff --git a/ts/output/common.ts b/ts/output/common.ts index 23e04e865..2fe819740 100644 --- a/ts/output/common.ts +++ b/ts/output/common.ts @@ -144,6 +144,7 @@ export abstract class CommonOutputJax< LinebreakVisitor: null, // The LinebreakVisitor to use }, font: '', // the font component to load + fontExtensions: [], // the font extensions to load htmlHDW: 'auto', // 'use', 'force', or 'ignore' data-mjx-hdw attributes wrapperFactory: null, // The wrapper factory to use fontData: null, // The FontData object to use @@ -167,8 +168,8 @@ export abstract class CommonOutputJax< display: 'block', 'text-align': 'center', 'justify-content': 'center', - margin: 'calc(1em - 2px) 0', - padding: '2px 0', + margin: '.7em 0', + padding: '.3em 2px', }, 'mjx-container[display][width="full"]': { display: 'flex', @@ -183,6 +184,15 @@ export abstract class CommonOutputJax< }, }; + /** + * The font to use for generic extensions + */ + public static genericFont: FontDataClass< + CharOptions, + VariantData, + DelimiterData + >; + /** * Used for collecting styles needed for the output jax */ @@ -305,6 +315,8 @@ export abstract class CommonOutputJax< this.styleJson = this.options.styleJson || new StyleJsonSheet(); this.font = font || new fontClass(fontOptions); this.font.setOptions({ mathmlSpacing: this.options.mathmlSpacing }); + /* prettier-ignore */ + (this.constructor as typeof CommonOutputJax).genericFont = fontClass; this.unknownCache = new Map(); const linebreaks = (this.options.linebreaks.LinebreakVisitor || LinebreakVisitor) as typeof Linebreaks; @@ -349,9 +361,17 @@ export abstract class CommonOutputJax< * @override */ public typeset(math: MathItem, html: MathDocument) { + /* prettier-ignore */ + const CLASS = (this.constructor as typeof CommonOutputJax); + const generic = CLASS.genericFont; + CLASS.genericFont = this.font.constructor as FontDataClass; this.setDocument(html); const node = this.createNode(); - this.toDOM(math, node, html); + try { + this.toDOM(math, node, html); + } finally { + CLASS.genericFont = generic; + } return node; } @@ -374,7 +394,8 @@ export abstract class CommonOutputJax< this.math.display ) { const w = wrapper.getOuterBBox().w; - const W = this.math.metrics.containerWidth / this.pxPerEm; + const W = + Math.max(0, this.math.metrics.containerWidth - 4) / this.pxPerEm; if (w > W && w) { scale *= W / w; } diff --git a/ts/output/common/Wrapper.ts b/ts/output/common/Wrapper.ts index 77a23b491..3d2844404 100644 --- a/ts/output/common/Wrapper.ts +++ b/ts/output/common/Wrapper.ts @@ -457,7 +457,7 @@ export class CommonWrapper< * @returns {number} The container width */ get containerWidth(): number { - return this.jax.containerWidth; + return this.parent ? this.parent.containerWidth : this.jax.containerWidth; } /** @@ -709,9 +709,10 @@ export class CommonWrapper< if (this.node.isEmbellished) { return [this, this.coreMO()] as any as [WW, WW]; } - const childNodes = this.childNodes[0]?.node?.isInferred - ? this.childNodes[0].childNodes - : this.childNodes; + const childNodes = + this.childNodes[0]?.node?.isInferred || this.node.isKind('semantics') + ? this.childNodes[0].childNodes + : this.childNodes; if (this.node.isToken || !childNodes[i]) { return [this, null] as any as [WW, WW]; } diff --git a/ts/output/common/Wrappers/mo.ts b/ts/output/common/Wrappers/mo.ts index 50b178927..1bd017db9 100644 --- a/ts/output/common/Wrappers/mo.ts +++ b/ts/output/common/Wrappers/mo.ts @@ -359,53 +359,64 @@ export function CommonMoMixin< * @override */ public getStretchedVariant(WH: number[], exact: boolean = false) { - if (this.stretch.dir !== DIRECTION.None) { - let D = this.getWH(WH); - const min = this.getSize('minsize', 0); - const max = this.getSize('maxsize', Infinity); - const mathaccent = this.node.getProperty('mathaccent'); - // - // Clamp the dimension to the max and min - // then get the target size via TeX rules - // - D = Math.max(min, Math.min(max, D)); - const df = this.font.params.delimiterfactor / 1000; - const ds = this.font.params.delimitershortfall; - const m = - min || exact - ? D - : mathaccent - ? Math.min(D / df, D + ds) - : Math.max(D * df, D - ds); - // - // Look through the delimiter sizes for one that matches - // - const delim = this.stretch; - const c = delim.c || this.getText().codePointAt(0); - let i = 0; - if (delim.sizes) { - for (const d of delim.sizes) { - if (d >= m) { - if (mathaccent && i) { - i--; - } - this.setDelimSize(c, i); - return; + if (this.stretch.dir === DIRECTION.None) { + return; + } + let D = this.getWH(WH); + const min = this.getSize('minsize', 0); + const max = this.getSize('maxsize', Infinity); + const mathaccent = this.node.getProperty('mathaccent'); + // + // Clamp the dimension to the max and min + // then get the target size via TeX rules + // + D = Math.max(min, Math.min(max, D)); + const df = this.font.params.delimiterfactor / 1000; + const ds = this.font.params.delimitershortfall; + const m = + min || exact + ? D + : mathaccent + ? Math.min(D / df, D + ds) + : Math.max(D * df, D - ds); + // + // Get the delimiter character, and look up the + // delimiter data again if we have already stretched it + // (in case a fixed size set the delim.c). See #3457. + // + const C = this.getText().codePointAt(0); + let delim = this.stretch; + if (this.size) { + this.stretch = delim = this.font.getDelimiter(C) as DD; + this.size = null; + } + const c = delim.c || C; + // + // Look through the delimiter sizes for one that matches + // + let i = 0; + if (delim.sizes) { + for (const d of delim.sizes) { + if (d >= m) { + if (mathaccent && i) { + i--; } - i++; + this.setDelimSize(c, i); + return; } + i++; } - // - // No size matches, so if we can make multi-character delimiters, - // record the data for that, otherwise, use the largest fixed size. - // - if (delim.stretch) { - this.size = -1; - this.invalidateBBox(); - this.getStretchBBox(WH, this.checkExtendedHeight(D, delim), delim); - } else { - this.setDelimSize(c, i - 1); - } + } + // + // No size matches, so if we can make multi-character delimiters, + // record the data for that, otherwise, use the largest fixed size. + // + if (delim.stretch) { + this.size = -1; + this.invalidateBBox(); + this.getStretchBBox(WH, this.checkExtendedHeight(D, delim), delim); + } else { + this.setDelimSize(c, i - 1); } } @@ -531,7 +542,7 @@ export function CommonMoMixin< */ public setBreakStyle(linebreak: string = '') { this.breakStyle = - this.node.parent.isEmbellished && !linebreak + this.node.parent?.isEmbellished && !linebreak ? '' : this.getBreakStyle(linebreak); if (!this.breakCount) return; diff --git a/ts/output/common/Wrappers/mpadded.ts b/ts/output/common/Wrappers/mpadded.ts index 7affee373..acbc5a6b0 100644 --- a/ts/output/common/Wrappers/mpadded.ts +++ b/ts/output/common/Wrappers/mpadded.ts @@ -171,6 +171,18 @@ export function CommonMpaddedMixin< extends Base implements CommonMpadded { + get containerWidth(): number { + const attributes = this.node.attributes; + const w = attributes.get('width').toString(); + if ( + !w.match(/^[-+]|%$/) && + attributes.get('data-overflow') === 'linebreak' + ) { + return this.length2em(w); + } + return this.parent.containerWidth; + } + /** * @override */ diff --git a/ts/output/common/Wrappers/mrow.ts b/ts/output/common/Wrappers/mrow.ts index e33b34f2f..01dd522d8 100644 --- a/ts/output/common/Wrappers/mrow.ts +++ b/ts/output/common/Wrappers/mrow.ts @@ -327,7 +327,7 @@ export function CommonMrowMixin< lines[n].R = this.bbox.R; } else { bbox.w = Math.max(...this.lineBBox.map((bbox) => bbox.w)); // natural width - this.shiftLines(bbox.w); + this.shiftLines(bbox); if (!this.jax.math.display && !this.linebreakOptions.inline) { bbox.pwidth = BBox.fullWidth; if (this.node.isInferred) { @@ -391,11 +391,12 @@ export function CommonMrowMixin< } /** - * Handle alignment and shifting if lines + * Handle alignment and shifting of lines * - * @param {number} W The width of the container + * @param {BBox} BBOX The bounding box of the container */ - protected shiftLines(W: number) { + protected shiftLines(BBOX: BBox) { + const W = BBOX.w; const lines = this.lineBBox; const n = lines.length - 1; const [alignfirst, shiftfirst] = lines[1].indentData?.[0] || [ @@ -417,6 +418,10 @@ export function CommonMrowMixin< ); bbox.L = 0; bbox.L = this.getAlignX(W, bbox, align) + shift; + const w = bbox.L + bbox.w; + if (w > BBOX.w) { + BBOX.w = w; + } } } @@ -432,7 +437,7 @@ export function CommonMrowMixin< if (recompute) return false; if (w !== null && this.bbox.w !== w) { this.bbox.w = w; - this.shiftLines(w); + this.shiftLines(this.bbox); } return true; } diff --git a/ts/output/common/Wrappers/mtable.ts b/ts/output/common/Wrappers/mtable.ts index ee78f71a0..3c908ad44 100644 --- a/ts/output/common/Wrappers/mtable.ts +++ b/ts/output/common/Wrappers/mtable.ts @@ -243,7 +243,6 @@ export interface CommonMtable< * @param {number[]} D The maximum depth for each of the rows * @param {number[]} W The maximum width for each column * @param {number} M The current height for items aligned top and bottom - * @returns {number} The updated value for M */ updateHDW( cell: WW, @@ -254,17 +253,7 @@ export interface CommonMtable< D: number[], W: number[], M: number - ): number; - - /** - * Extend the H and D of a row to cover the maximum height needed by top/bottom aligned items - * - * @param {number} i The row whose hight and depth should be adjusted - * @param {number[]} H The row heights - * @param {number[]} D The row depths - * @param {number} M The maximum height of top/bottom aligned items - */ - extendHD(i: number, H: number[], D: number[], M: number): void; + ): void; /** * @param {WW} cell The cell to check for percentage widths @@ -701,14 +690,13 @@ export function CommonMtableMixin< } } const count = stretchy.length; - const nodeCount = this.childNodes.length; - if (count && nodeCount > 1 && W === null) { + if (count && W === null) { W = 0; // // If all the children are stretchy, find the largest one, // otherwise, find the width of the non-stretchy children. // - const all = count > 1 && count === nodeCount; + const all = count === this.childNodes.length; for (const row of this.tableRows) { const cell = row.getChild(i); if (cell) { @@ -727,12 +715,15 @@ export function CommonMtableMixin< // // Stretch the stretchable children // + const TW = this.getTableData().W; for (const child of stretchy) { + const w = child.getBBox().w; child .coreMO() - .getStretchedVariant([ - Math.max(W, child.getBBox().w) / child.coreRScale(), - ]); + .getStretchedVariant([Math.max(W, w) / child.coreRScale()]); + if (w > TW[i]) { + TW[i] = w; + } } } } @@ -744,21 +735,29 @@ export function CommonMtableMixin< if ( this.jax.math.root.attributes.get('overflow') !== 'linebreak' || !this.jax.math.display - ) + ) { return; - const { D } = this.getTableData(); + } + const { H, D } = this.getTableData(); let j = 0; let w = 0; for (const row of this.tableRows) { const cell = row.getChild(i); - if (cell && cell.getBBox().w > W) { - cell.childNodes[0].breakToWidth(W); + if (cell) { + const r = row.getBBox().rscale; const bbox = cell.getBBox(); - D[j] = Math.max(D[j], bbox.d); - if (bbox.w > w) { - w = bbox.w; + if (cell && bbox.w * r > W) { + cell.childNodes[0].breakToWidth(W); + const align = row.node.attributes.get('rowalign') as string; + this.updateHDW(cell, i, j, align, H, D); + } + if (bbox.w * r > w) { + w = bbox.w * r; } } + const bbox = row.getBBox(); + bbox.h = H[j]; + bbox.d = D[j]; j++; } // @@ -791,27 +790,72 @@ export function CommonMtableMixin< const LW = [0]; const rows = this.tableRows; for (let j = 0; j < rows.length; j++) { - let M = 0; const row = rows[j]; const align = row.node.attributes.get('rowalign') as string; for (let i = 0; i < row.numCells; i++) { const cell = row.getChild(i); - M = this.updateHDW(cell, i, j, align, H, D, W, M); + this.updateHDW(cell, i, j, align, H, D, W); this.recordPWidthCell(cell, i); } NH[j] = H[j]; ND[j] = D[j]; if (row.labeled) { - M = this.updateHDW(row.childNodes[0], 0, j, align, H, D, LW, M); + this.updateHDW(row.childNodes[0], 0, j, align, H, D, LW); } - this.extendHD(j, H, D, M); - this.extendHD(j, NH, ND, M); + row.bbox.h = H[j]; + row.bbox.d = D[j]; } const L = LW[0]; this.data = { H, D, W, NH, ND, L }; return this.data; } + /** + * Functions for adjusting the H and D values for cells + * that are aligned by top, bottom, center, axis, and baseline. + */ + protected adjustHD: { + [name: string]: ( + h: number, + d: number, + H: number[], + D: number[], + j: number + ) => void; + } = { + top: (h, d, H, D, j) => { + if (h > H[j]) { + D[j] -= h - H[j]; + H[j] = h; + } + if (h + d > H[j] + D[j]) { + D[j] = h + d - H[j]; + } + }, + bottom: (h, d, H, D, j) => { + if (d > D[j]) { + H[j] -= d - D[j]; + D[j] = d; + } + if (h + d > H[j] + D[j]) { + H[j] = h + d - D[j]; + } + }, + center: (h, d, H, D, j) => { + if (h + d > H[j] + D[j]) { + H[j] = D[j] = (h + d) / 2; + } + }, + other: (h, d, H, D, j) => { + if (h > H[j]) { + H[j] = h; + } + if (d > D[j]) { + D[j] = d; + } + }, + }; + /** * @override */ @@ -822,9 +866,8 @@ export function CommonMtableMixin< align: string, H: number[], D: number[], - W: number[], - M: number - ): number { + W: number[] = null + ) { let { h, d, w } = cell.getBBox(); const scale = cell.parent.bbox.rscale; if (cell.parent.bbox.rscale !== 1) { @@ -836,27 +879,12 @@ export function CommonMtableMixin< if (h < 0.75) h = 0.75; if (d < 0.25) d = 0.25; } - let m = 0; align = (cell.node.attributes.get('rowalign') as string) || align; - if (align !== 'baseline' && align !== 'axis') { - m = h + d; - h = d = 0; + if (!Object.hasOwn(this.adjustHD, align)) { + align = 'other'; } - if (h > H[j]) H[j] = h; - if (d > D[j]) D[j] = d; - if (m > M) M = m; + this.adjustHD[align](h, d, H, D, j); if (W && w > W[i]) W[i] = w; - return M; - } - - /** - * @override - */ - public extendHD(i: number, H: number[], D: number[], M: number) { - const d = (M - (H[i] + D[i])) / 2; - if (d < 0.00001) return; - H[i] += d; - D[i] += d; } /** diff --git a/ts/output/svg.ts b/ts/output/svg.ts index 38844e48d..c712059c1 100644 --- a/ts/output/svg.ts +++ b/ts/output/svg.ts @@ -110,10 +110,21 @@ export class SVG extends CommonOutputJax< fill: 'blue', stroke: 'blue', }, - 'rect[sre-highlighter-added]:has(+ .mjx-selected)': { + [[ + 'rect[data-sre-highlighter-added]:has(+ .mjx-selected)', + 'rect[data-sre-highlighter-bbox].mjx-selected', + ].join(', ')]: { stroke: 'black', 'stroke-width': '80px', }, + '@media (prefers-color-scheme: dark)': { + [[ + 'rect[data-sre-highlighter-added]:has(+ .mjx-selected)', + 'rect[data-sre-highlighter-bbox].mjx-selected', + ].join(', ')]: { + stroke: '#C8C8C8', + }, + }, }; /** @@ -299,6 +310,11 @@ export class SVG extends CommonOutputJax< const adaptor = this.adaptor; adaptor.setStyle(svg, 'min-width', adaptor.getStyle(svg, 'width')); adaptor.setAttribute(svg, 'width', pwidth); + adaptor.setAttribute( + svg, + 'data-mjx-viewBox', + adaptor.getAttribute(svg, 'viewBox') + ); adaptor.removeAttribute(svg, 'viewBox'); const scale = this.fixed( wrapper.metrics.ex / (this.font.params.x_height * 1000), diff --git a/ts/output/svg/Wrapper.ts b/ts/output/svg/Wrapper.ts index ad31240f1..d6bd6a9ab 100644 --- a/ts/output/svg/Wrapper.ts +++ b/ts/output/svg/Wrapper.ts @@ -74,21 +74,20 @@ export type SvgConstructor = CommonWrapperConstructor< /** * The type of the SvgWrapper class (used when creating the wrapper factory for this class) */ -export interface SvgWrapperClass - extends CommonWrapperClass< - N, - T, - D, - SVG, - SvgWrapper, - SvgWrapperFactory, - SvgWrapperClass, - SvgCharOptions, - SvgVariantData, - SvgDelimiterData, - SvgFontData, - SvgFontDataClass - > {} +export interface SvgWrapperClass extends CommonWrapperClass< + N, + T, + D, + SVG, + SvgWrapper, + SvgWrapperFactory, + SvgWrapperClass, + SvgCharOptions, + SvgVariantData, + SvgDelimiterData, + SvgFontData, + SvgFontDataClass +> {} /*****************************************************************/ /** diff --git a/ts/output/svg/Wrappers/TeXAtom.ts b/ts/output/svg/Wrappers/TeXAtom.ts index b948a73b1..568737040 100644 --- a/ts/output/svg/Wrappers/TeXAtom.ts +++ b/ts/output/svg/Wrappers/TeXAtom.ts @@ -48,7 +48,8 @@ import { MmlNode, TEXCLASSNAMES } from '../../../core/MmlTree/MmlNode.js'; * @template D The Document class */ export interface SvgTeXAtomNTD - extends SvgWrapper, + extends + SvgWrapper, CommonTeXAtom< N, T, @@ -72,7 +73,8 @@ export interface SvgTeXAtomNTD * @template D The Document class */ export interface SvgTeXAtomClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonTeXAtomClass< N, T, diff --git a/ts/output/svg/Wrappers/TextNode.ts b/ts/output/svg/Wrappers/TextNode.ts index 2fb62f478..2db2a67f6 100644 --- a/ts/output/svg/Wrappers/TextNode.ts +++ b/ts/output/svg/Wrappers/TextNode.ts @@ -48,7 +48,8 @@ import { StyleJsonSheet } from '../../../util/StyleJson.js'; * @template D The Document class */ export interface SvgTextNodeNTD - extends SvgWrapper, + extends + SvgWrapper, CommonTextNode< N, T, @@ -72,7 +73,8 @@ export interface SvgTextNodeNTD * @template D The Document class */ export interface SvgTextNodeClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonTextNodeClass< N, T, diff --git a/ts/output/svg/Wrappers/maction.ts b/ts/output/svg/Wrappers/maction.ts index 0a551cce2..1d27955df 100644 --- a/ts/output/svg/Wrappers/maction.ts +++ b/ts/output/svg/Wrappers/maction.ts @@ -56,7 +56,8 @@ import { STATE } from '../../../core/MathItem.js'; * @template D The Document class */ export interface SvgMactionNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMaction< N, T, @@ -91,7 +92,8 @@ export interface SvgMactionNTD * @template D The Document class */ export interface SvgMactionClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMactionClass< N, T, @@ -187,6 +189,24 @@ export const SvgMaction = (function (): SvgMactionClass { 'background-color': '#F8F8F8', color: 'black', }, + 'g[data-mjx-collapsed]': { + fill: '#55F', + }, + + '@media (prefers-color-scheme: dark) /* svg maction */': { + 'mjx-tool > mjx-tip': { + 'background-color': '#303030', + color: '#E0E0E0', + 'box-shadow': '2px 2px 5px #000', + }, + 'mjx-status': { + 'background-color': '#303030', + color: '#E0E0E0', + }, + 'g[data-mjx-collapsed]': { + fill: '#88F', + }, + }, }; /** diff --git a/ts/output/svg/Wrappers/math.ts b/ts/output/svg/Wrappers/math.ts index 413a54459..bb39ab26c 100644 --- a/ts/output/svg/Wrappers/math.ts +++ b/ts/output/svg/Wrappers/math.ts @@ -51,7 +51,8 @@ import { ZeroFontDataUrl } from './zero.js'; * @template D The Document class */ export interface SvgMathNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMath< N, T, @@ -75,7 +76,8 @@ export interface SvgMathNTD * @template D The Document class */ export interface SvgMathClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMathClass< N, T, diff --git a/ts/output/svg/Wrappers/menclose.ts b/ts/output/svg/Wrappers/menclose.ts index b43d04969..8ccb9b054 100644 --- a/ts/output/svg/Wrappers/menclose.ts +++ b/ts/output/svg/Wrappers/menclose.ts @@ -51,7 +51,8 @@ import { OptionList } from '../../../util/Options.js'; * @template D The Document class */ export interface SvgMencloseNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMenclose< N, T, @@ -123,7 +124,8 @@ export interface SvgMencloseNTD * @template D The Document class */ export interface SvgMencloseClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMencloseClass< N, T, diff --git a/ts/output/svg/Wrappers/mfenced.ts b/ts/output/svg/Wrappers/mfenced.ts index f95fa5fdf..0c384e8f7 100644 --- a/ts/output/svg/Wrappers/mfenced.ts +++ b/ts/output/svg/Wrappers/mfenced.ts @@ -49,7 +49,8 @@ import { SvgInferredMrowNTD } from './mrow.js'; * @template D The Document class */ export interface SvgMfencedNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMfenced< N, T, @@ -73,7 +74,8 @@ export interface SvgMfencedNTD * @template D The Document class */ export interface SvgMfencedClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMfencedClass< N, T, diff --git a/ts/output/svg/Wrappers/mfrac.ts b/ts/output/svg/Wrappers/mfrac.ts index d058d1801..491e42533 100644 --- a/ts/output/svg/Wrappers/mfrac.ts +++ b/ts/output/svg/Wrappers/mfrac.ts @@ -49,7 +49,8 @@ import { SvgMoNTD } from './mo.js'; * @template D The Document class */ export interface SvgMfracNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMfrac< N, T, @@ -73,7 +74,8 @@ export interface SvgMfracNTD * @template D The Document class */ export interface SvgMfracClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMfracClass< N, T, diff --git a/ts/output/svg/Wrappers/mglyph.ts b/ts/output/svg/Wrappers/mglyph.ts index 422e0ad5b..086e98fc7 100644 --- a/ts/output/svg/Wrappers/mglyph.ts +++ b/ts/output/svg/Wrappers/mglyph.ts @@ -50,7 +50,8 @@ import { OptionList } from '../../../util/Options.js'; * @template D The Document class */ export interface SvgMglyphNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMglyph< N, T, @@ -74,7 +75,8 @@ export interface SvgMglyphNTD * @template D The Document class */ export interface SvgMglyphClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMglyphClass< N, T, diff --git a/ts/output/svg/Wrappers/mi.ts b/ts/output/svg/Wrappers/mi.ts index 2fafd4181..e7e86fc2b 100644 --- a/ts/output/svg/Wrappers/mi.ts +++ b/ts/output/svg/Wrappers/mi.ts @@ -48,7 +48,8 @@ import { MmlMi } from '../../../core/MmlTree/MmlNodes/mi.js'; * @template D The Document class */ export interface SvgMiNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMi< N, T, @@ -72,7 +73,8 @@ export interface SvgMiNTD * @template D The Document class */ export interface SvgMiClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMiClass< N, T, diff --git a/ts/output/svg/Wrappers/mmultiscripts.ts b/ts/output/svg/Wrappers/mmultiscripts.ts index 5e066073d..2823fc3de 100644 --- a/ts/output/svg/Wrappers/mmultiscripts.ts +++ b/ts/output/svg/Wrappers/mmultiscripts.ts @@ -75,7 +75,8 @@ export function AlignX(align: string): AlignFunction { * @template D The Document class */ export interface SvgMmultiscriptsNTD - extends SvgMsubsupNTD, + extends + SvgMsubsupNTD, CommonMmultiscripts< N, T, @@ -99,7 +100,8 @@ export interface SvgMmultiscriptsNTD * @template D The Document class */ export interface SvgMmultiscriptsClass - extends SvgMsubsupClass, + extends + SvgMsubsupClass, CommonMmultiscriptsClass< N, T, diff --git a/ts/output/svg/Wrappers/mn.ts b/ts/output/svg/Wrappers/mn.ts index 6df65a2d6..49d8ca29c 100644 --- a/ts/output/svg/Wrappers/mn.ts +++ b/ts/output/svg/Wrappers/mn.ts @@ -48,7 +48,8 @@ import { MmlMn } from '../../../core/MmlTree/MmlNodes/mn.js'; * @template D The Document class */ export interface SvgMnNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMn< N, T, @@ -72,7 +73,8 @@ export interface SvgMnNTD * @template D The Document class */ export interface SvgMnClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMnClass< N, T, diff --git a/ts/output/svg/Wrappers/mo.ts b/ts/output/svg/Wrappers/mo.ts index 8212ec8cb..087330bec 100644 --- a/ts/output/svg/Wrappers/mo.ts +++ b/ts/output/svg/Wrappers/mo.ts @@ -55,7 +55,8 @@ const HFUZZ = 0.1; // overlap for horizontal stretchy glyphs * @template D The Document class */ export interface SvgMoNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMo< N, T, @@ -79,7 +80,8 @@ export interface SvgMoNTD * @template D The Document class */ export interface SvgMoClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMoClass< N, T, diff --git a/ts/output/svg/Wrappers/mpadded.ts b/ts/output/svg/Wrappers/mpadded.ts index 5aeebc630..dac27709e 100644 --- a/ts/output/svg/Wrappers/mpadded.ts +++ b/ts/output/svg/Wrappers/mpadded.ts @@ -48,7 +48,8 @@ import { MmlMpadded } from '../../../core/MmlTree/MmlNodes/mpadded.js'; * @template D The Document class */ export interface SvgMpaddedNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMpadded< N, T, @@ -72,7 +73,8 @@ export interface SvgMpaddedNTD * @template D The Document class */ export interface SvgMpaddedClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMpaddedClass< N, T, diff --git a/ts/output/svg/Wrappers/mroot.ts b/ts/output/svg/Wrappers/mroot.ts index 0ba3d5e48..55379094b 100644 --- a/ts/output/svg/Wrappers/mroot.ts +++ b/ts/output/svg/Wrappers/mroot.ts @@ -50,7 +50,8 @@ import { BBox } from '../../../util/BBox.js'; * @template D The Document class */ export interface SvgMrootNTD - extends SvgMsqrtNTD, + extends + SvgMsqrtNTD, CommonMroot< N, T, @@ -74,7 +75,8 @@ export interface SvgMrootNTD * @template D The Document class */ export interface SvgMrootClass - extends SvgMsqrtClass, + extends + SvgMsqrtClass, CommonMrootClass< N, T, diff --git a/ts/output/svg/Wrappers/mrow.ts b/ts/output/svg/Wrappers/mrow.ts index e67b85e64..6d582082e 100644 --- a/ts/output/svg/Wrappers/mrow.ts +++ b/ts/output/svg/Wrappers/mrow.ts @@ -54,7 +54,8 @@ import { MmlNode } from '../../../core/MmlTree/MmlNode.js'; * @template D The Document class */ export interface SvgMrowNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMrow< N, T, @@ -78,7 +79,8 @@ export interface SvgMrowNTD * @template D The Document class */ export interface SvgMrowClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMrowClass< N, T, @@ -270,7 +272,8 @@ export const SvgMrow = (function (): SvgMrowClass { * @template D The Document class */ export interface SvgInferredMrowNTD - extends SvgMrowNTD, + extends + SvgMrowNTD, CommonInferredMrow< N, T, @@ -294,7 +297,8 @@ export interface SvgInferredMrowNTD * @template D The Document class */ export interface SvgInferredMrowClass - extends SvgMrowClass, + extends + SvgMrowClass, CommonInferredMrowClass< N, T, diff --git a/ts/output/svg/Wrappers/ms.ts b/ts/output/svg/Wrappers/ms.ts index 138d279db..9f7e857b2 100644 --- a/ts/output/svg/Wrappers/ms.ts +++ b/ts/output/svg/Wrappers/ms.ts @@ -48,7 +48,8 @@ import { MmlMs } from '../../../core/MmlTree/MmlNodes/ms.js'; * @template D The Document class */ export interface SvgMsNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMs< N, T, @@ -72,7 +73,8 @@ export interface SvgMsNTD * @template D The Document class */ export interface SvgMsClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMsClass< N, T, diff --git a/ts/output/svg/Wrappers/mspace.ts b/ts/output/svg/Wrappers/mspace.ts index 8af7bfabc..1cb9e0105 100644 --- a/ts/output/svg/Wrappers/mspace.ts +++ b/ts/output/svg/Wrappers/mspace.ts @@ -48,7 +48,8 @@ import { MmlMspace } from '../../../core/MmlTree/MmlNodes/mspace.js'; * @template D The Document class */ export interface SvgMspaceNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMspace< N, T, @@ -72,7 +73,8 @@ export interface SvgMspaceNTD * @template D The Document class */ export interface SvgMspaceClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMspaceClass< N, T, diff --git a/ts/output/svg/Wrappers/msqrt.ts b/ts/output/svg/Wrappers/msqrt.ts index 2a1091bc1..be0be034c 100644 --- a/ts/output/svg/Wrappers/msqrt.ts +++ b/ts/output/svg/Wrappers/msqrt.ts @@ -50,7 +50,8 @@ import { BBox } from '../../../util/BBox.js'; * @template D The Document class */ export interface SvgMsqrtNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMsqrt< N, T, @@ -79,7 +80,8 @@ export interface SvgMsqrtNTD * @template D The Document class */ export interface SvgMsqrtClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMsqrtClass< N, T, diff --git a/ts/output/svg/Wrappers/msubsup.ts b/ts/output/svg/Wrappers/msubsup.ts index f9987f1c8..13e04d136 100644 --- a/ts/output/svg/Wrappers/msubsup.ts +++ b/ts/output/svg/Wrappers/msubsup.ts @@ -64,7 +64,8 @@ import { * @template D The Document class */ export interface SvgMsubNTD - extends SvgScriptbaseNTD, + extends + SvgScriptbaseNTD, CommonMsub< N, T, @@ -88,7 +89,8 @@ export interface SvgMsubNTD * @template D The Document class */ export interface SvgMsubClass - extends SvgScriptbaseClass, + extends + SvgScriptbaseClass, CommonMsubClass< N, T, @@ -152,7 +154,8 @@ export const SvgMsub = (function (): SvgMsubClass { * @template D The Document class */ export interface SvgMsupNTD - extends SvgScriptbaseNTD, + extends + SvgScriptbaseNTD, CommonMsup< N, T, @@ -176,7 +179,8 @@ export interface SvgMsupNTD * @template D The Document class */ export interface SvgMsupClass - extends SvgScriptbaseClass, + extends + SvgScriptbaseClass, CommonMsupClass< N, T, @@ -240,7 +244,8 @@ export const SvgMsup = (function (): SvgMsupClass { * @template D The Document class */ export interface SvgMsubsupNTD - extends SvgScriptbaseNTD, + extends + SvgScriptbaseNTD, CommonMsubsup< N, T, @@ -264,7 +269,8 @@ export interface SvgMsubsupNTD * @template D The Document class */ export interface SvgMsubsupClass - extends SvgScriptbaseClass, + extends + SvgScriptbaseClass, CommonMsubsupClass< N, T, diff --git a/ts/output/svg/Wrappers/mtable.ts b/ts/output/svg/Wrappers/mtable.ts index 25f4034e6..ef652eb68 100644 --- a/ts/output/svg/Wrappers/mtable.ts +++ b/ts/output/svg/Wrappers/mtable.ts @@ -54,7 +54,8 @@ const CLASSPREFIX = 'mjx-'; * @template D The Document class */ export interface SvgMtableNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMtable< N, T, @@ -84,7 +85,8 @@ export interface SvgMtableNTD * @template D The Document class */ export interface SvgMtableClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMtableClass< N, T, diff --git a/ts/output/svg/Wrappers/mtd.ts b/ts/output/svg/Wrappers/mtd.ts index b7156a962..d701983f7 100644 --- a/ts/output/svg/Wrappers/mtd.ts +++ b/ts/output/svg/Wrappers/mtd.ts @@ -48,7 +48,8 @@ import { MmlMtd } from '../../../core/MmlTree/MmlNodes/mtd.js'; * @template D The Document class */ export interface SvgMtdNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMtd< N, T, @@ -96,7 +97,8 @@ export interface SvgMtdNTD * @template D The Document class */ export interface SvgMtdClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMtdClass< N, T, diff --git a/ts/output/svg/Wrappers/mtext.ts b/ts/output/svg/Wrappers/mtext.ts index f80023c6c..13812d990 100644 --- a/ts/output/svg/Wrappers/mtext.ts +++ b/ts/output/svg/Wrappers/mtext.ts @@ -48,7 +48,8 @@ import { MmlMtext } from '../../../core/MmlTree/MmlNodes/mtext.js'; * @template D The Document class */ export interface SvgMtextNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMtext< N, T, @@ -72,7 +73,8 @@ export interface SvgMtextNTD * @template D The Document class */ export interface SvgMtextClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMtextClass< N, T, diff --git a/ts/output/svg/Wrappers/mtr.ts b/ts/output/svg/Wrappers/mtr.ts index b193228ef..c85c05700 100644 --- a/ts/output/svg/Wrappers/mtr.ts +++ b/ts/output/svg/Wrappers/mtr.ts @@ -67,7 +67,8 @@ export type SizeData = { * @template D The Document class */ export interface SvgMtrNTD - extends SvgWrapper, + extends + SvgWrapper, CommonMtr< N, T, @@ -123,7 +124,8 @@ export interface SvgMtrNTD * @template D The Document class */ export interface SvgMtrClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonMtrClass< N, T, @@ -302,7 +304,8 @@ export const SvgMtr = (function (): SvgMtrClass { * @template D The Document class */ export interface SvgMlabeledtrNTD - extends SvgMtrNTD, + extends + SvgMtrNTD, CommonMlabeledtr< N, T, @@ -326,7 +329,8 @@ export interface SvgMlabeledtrNTD * @template D The Document class */ export interface SvgMlabeledtrClass - extends SvgMtrClass, + extends + SvgMtrClass, CommonMlabeledtrClass< N, T, diff --git a/ts/output/svg/Wrappers/munderover.ts b/ts/output/svg/Wrappers/munderover.ts index 80186b51f..cec563467 100644 --- a/ts/output/svg/Wrappers/munderover.ts +++ b/ts/output/svg/Wrappers/munderover.ts @@ -70,7 +70,8 @@ import { * @template D The Document class */ export interface SvgMunderNTD - extends SvgMsubNTD, + extends + SvgMsubNTD, CommonMunder< N, T, @@ -94,7 +95,8 @@ export interface SvgMunderNTD * @template D The Document class */ export interface SvgMunderClass - extends SvgMsubClass, + extends + SvgMsubClass, CommonMunderClass< N, T, @@ -187,7 +189,8 @@ export const SvgMunder = (function (): SvgMunderClass { * @template D The Document class */ export interface SvgMoverNTD - extends SvgMsupNTD, + extends + SvgMsupNTD, CommonMover< N, T, @@ -211,7 +214,8 @@ export interface SvgMoverNTD * @template D The Document class */ export interface SvgMoverClass - extends SvgMsupClass, + extends + SvgMsupClass, CommonMoverClass< N, T, @@ -301,7 +305,8 @@ export const SvgMover = (function (): SvgMoverClass { * @template D The Document class */ export interface SvgMunderoverNTD - extends SvgMsubsupNTD, + extends + SvgMsubsupNTD, CommonMunderover< N, T, @@ -325,7 +330,8 @@ export interface SvgMunderoverNTD * @template D The Document class */ export interface SvgMunderoverClass - extends SvgMsubsupClass, + extends + SvgMsubsupClass, CommonMunderoverClass< N, T, diff --git a/ts/output/svg/Wrappers/scriptbase.ts b/ts/output/svg/Wrappers/scriptbase.ts index 88ba2281a..5c13ddf37 100644 --- a/ts/output/svg/Wrappers/scriptbase.ts +++ b/ts/output/svg/Wrappers/scriptbase.ts @@ -50,7 +50,8 @@ import { MmlNode } from '../../../core/MmlTree/MmlNode.js'; * @template D The Document class */ export interface SvgScriptbaseNTD - extends SvgWrapper, + extends + SvgWrapper, CommonScriptbase< N, T, @@ -74,7 +75,8 @@ export interface SvgScriptbaseNTD * @template D The Document class */ export interface SvgScriptbaseClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonScriptbaseClass< N, T, diff --git a/ts/output/svg/Wrappers/semantics.ts b/ts/output/svg/Wrappers/semantics.ts index 83cbc1941..cdbf98fc9 100644 --- a/ts/output/svg/Wrappers/semantics.ts +++ b/ts/output/svg/Wrappers/semantics.ts @@ -61,7 +61,8 @@ import { StyleList } from '../../../util/Styles.js'; * @template D The Document class */ export interface SvgSemanticsNTD - extends SvgWrapper, + extends + SvgWrapper, CommonSemantics< N, T, @@ -85,7 +86,8 @@ export interface SvgSemanticsNTD * @template D The Document class */ export interface SvgSemanticsClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonSemanticsClass< N, T, @@ -216,7 +218,8 @@ export const SvgAnnotationXML = (function (): SvgWrapperClass< * @template D The Document class */ export interface SvgXmlNodeNTD - extends SvgWrapper, + extends + SvgWrapper, CommonXmlNode< N, T, @@ -240,7 +243,8 @@ export interface SvgXmlNodeNTD * @template D The Document class */ export interface SvgXmlNodeClass - extends SvgWrapperClass, + extends + SvgWrapperClass, CommonXmlNodeClass< N, T, diff --git a/ts/ui/dialog/CopyDialog.ts b/ts/ui/dialog/CopyDialog.ts new file mode 100644 index 000000000..d31caebc3 --- /dev/null +++ b/ts/ui/dialog/CopyDialog.ts @@ -0,0 +1,77 @@ +/************************************************************* + * + * Copyright (c) 2025 The MathJax Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Implements the CopyDialog class (InfoDialog with copy button). + * + * @author dpvc@mathjax.org (Davide Cervone) + */ + +import { InfoDialog, InfoDialogArgs } from './InfoDialog.js'; + +/** + * The args for a CopyDialog + */ +export type CopyDialogArgs = InfoDialogArgs & { code?: boolean }; + +/** + * The CopyDialog subclass of InfoDialog + */ +export class CopyDialog extends InfoDialog { + /** + * @override + */ + public static post(args: CopyDialogArgs) { + return super.post(args); + } + + /** + * @override + */ + protected html(args: CopyDialogArgs) { + // + // Add a copy-to-clipboard button + // + args.extraNodes ??= []; + const copy = args.adaptor.node('input', { + type: 'button', + value: 'Copy to Clipboard', + 'data-drag': 'none', + }); + copy.addEventListener('click', this.copyToClipboard.bind(this)); + args.extraNodes.push(copy); + // + // If this is a code dialog, format the source and set in a pre element + // + if (args.code) { + args.message = '
' + this.formatSource(args.message) + '
'; + } + return super.html(args); + } + + /** + * @param {string} text The text to be displayed in the Info box + * @returns {string} The text with HTML specials being escaped + */ + protected formatSource(text: string): string { + return text + .trim() + .replace(/&/g, '&') + .replace(//g, '>'); + } +} diff --git a/ts/ui/dialog/DraggableDialog.ts b/ts/ui/dialog/DraggableDialog.ts new file mode 100644 index 000000000..e7090e898 --- /dev/null +++ b/ts/ui/dialog/DraggableDialog.ts @@ -0,0 +1,1156 @@ +/************************************************************* + * + * Copyright (c) 2025 The MathJax Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Implements a draggable dialog class. + * + * @author dpvc@mathjax.org (Davide Cervone) + */ + +import { DOMAdaptor } from '../../core/DOMAdaptor.js'; +import { StyleJson, StyleJsonSheet } from '../../util/StyleJson.js'; +import { context } from '../../util/context.js'; + +export type ADAPTOR = DOMAdaptor; + +export type Action = ( + dialog: DraggableDialog, + event: MouseEvent +) => void | number[]; +export type ActionList = { [action: string]: Action }; +export type ActionMap = { [type: string]: ActionList }; + +/** + * The arguments that control the dialog contents + */ +export type DialogArgs = { + title?: string; // // the dialog title HTML + message: string; // // the dialog message HTML + adaptor: ADAPTOR; // // the adaptor to use to create the dialog + node?: HTMLElement; // // the node to focus when the dialog closes, if any + styles?: StyleJson; // // extra styles to use for the dialog + extraNodes?: HTMLElement[]; // // extra HTML nodes to put at the bottom of the dialog + className?: string; // // optional class to apply to the dialog +}; + +/** + * Type of function that implements a key press action + */ +export type keyMapping = ( + dialog: DraggableDialog, + event: KeyboardEvent +) => void; + +/** + * True if we can rely on an HTML dialog element. + */ +export const isDialog: boolean = !!context.window?.HTMLDialogElement; + +/*========================================================================*/ + +/** + * The draggable dialog class + */ +export class DraggableDialog { + /** + * The minimum width of the dialog + */ + protected minW = 200; + /** + * The maximum width of the dialog + */ + protected minH = 80; + + /** + * The current x translation of the dialog + */ + protected tx: number = 0; + /** + * The current y translation of the dialog + */ + protected ty: number = 0; + + /** + * The current mouse x position + */ + protected x: number; + /** + * The current mouse y position + */ + protected y: number; + /** + * The current dialog width + */ + protected w: number; + /** + * The current dialog height + */ + protected h: number; + /** + * True when the dialog is being dragged or sized + */ + protected dragging: boolean = false; + /** + * The drag acction being taken (move, left, right, top, bottom, etc.) + */ + protected action: string; + + /** + * Elements where clicking doesn't cause dragging + */ + protected noDrag: HTMLElement[]; + /** + * The title element + */ + protected title: HTMLElement; + /** + * The content div element + */ + protected content: HTMLElement; + + /** + * The node to focus when dialog closes + */ + protected node: HTMLElement; + /** + * The background element when is not available + */ + protected background: HTMLElement; + /** + * The dialog element node + */ + protected dialog: HTMLDialogElement; + + /** + * Events to add when dragging and remove when drag completes + */ + protected events = [ + ['mousemove', this.MouseMove.bind(this)], + ['mouseup', this.MouseUp.bind(this)], + ]; + + /* + * Key bindings for when dialog is open + */ + protected static keyActions: Map = new Map([ + ['Escape', (dialog, event) => dialog.escKey(event)], + ['a', (dialog, event) => dialog.aKey(event)], + ['m', (dialog, event) => dialog.mKey(event)], + ['s', (dialog, event) => dialog.sKey(event)], + ['ArrowRight', (dialog, event) => dialog.arrowKey(event, 'right')], + ['ArrowLeft', (dialog, event) => dialog.arrowKey(event, 'left')], + ['ArrowUp', (dialog, event) => dialog.arrowKey(event, 'up')], + ['ArrowDown', (dialog, event) => dialog.arrowKey(event, 'down')], + ]); + + /** + * The style element ID for dialog styles + */ + public static styleId: string = 'MJX-DIALOG-styles'; + /** + * The class name to use for the dialog, if any + */ + public static className: string = ''; + + /** + * An id incremented for each instance of a dialog + */ + public static id: number = 0; + + /** + * The default styles for all dialogs + */ + public static styles: StyleJson = { + // + // For when dialog element is not available + // + 'mjx-dialog-background': { + display: 'flex', + 'flex-direction': 'column', + 'justify-content': 'center', + position: 'fixed', + top: 0, + left: 0, + right: 0, + bottom: 0, + 'z-index': 1001, + }, + + // + // The main dialog box and background + // + '.mjx-dialog': { + 'max-width': 'calc(min(60em, 90%))', + 'max-height': 'calc(min(50em, 85%))', + border: '3px outset', + 'border-radius': '15px', + color: 'black', + 'background-color': '#DDDDDD', + 'box-shadow': '0px 10px 20px #808080', + padding: '4px 4px', + cursor: 'grab', + overflow: 'visible', + display: 'flex', + 'flex-direction': 'column', + 'align-items': 'center', + position: 'relative', + top: '-4%', + }, + '.mjx-dialog.mjx-moving': { + cursor: 'grabbing', + }, + '.mjx-dialog > input[type="button"]': { + width: 'fit-content', + }, + '.mjx-dialog > mjx-dialog-spacer': { + display: 'block', + height: '.75em', + 'flex-shrink': 0, + }, + '.mjx-dialog::backdrop': { + opacity: 0, + cursor: 'default', + }, + + // + // The contents of the dialog + // + 'mjx-dialog': { + all: 'initial', + cursor: 'inherit', + width: '100%', + display: 'flex', + 'flex-direction': 'column', + 'flex-grow': 1, + 'flex-shrink': 1, + overflow: 'hidden', + }, + 'mjx-dialog > mjx-title': { + display: 'block', + 'text-align': 'center', + margin: '.25em 1.75em', + overflow: 'hidden', + 'white-space': 'nowrap', + '-webkit-user-select': 'none', + 'user-select': 'none', + 'flex-shrink': 0, + }, + 'mjx-dialog > mjx-title > h1': { + 'font-size': '125%', + margin: 0, + }, + 'mjx-dialog > div': { + margin: '0 1em .5em', + padding: '8px 18px', + overflow: 'auto', + border: '2px inset black', + 'background-color': 'white', + 'text-align': 'left', + cursor: 'default', + 'flex-grow': 1, + 'flex-shrink': 1, + }, + 'mjx-dialog > div > pre': { + margin: 0, + }, + + // + // The dialog buttons + // + '.mjx-dialog-button': { + position: 'absolute', + top: '6px', + height: '17px', + width: '17px', + cursor: 'default', + display: 'block', + border: '2px solid #AAA', + 'border-radius': '18px', + 'font-family': '"Courier New", Courier', + 'text-align': 'center', + color: '#F0F0F0', + '-webkit-user-select': 'none', + 'user-select': 'none', + }, + '.mjx-dialog-button:hover': { + color: 'white !important', + border: '2px solid #CCC !important', + }, + '.mjx-dialog-button > mjx-dialog-icon': { + display: 'block', + 'background-color': '#AAA', + border: '1.5px solid', + 'border-radius': '18px', + 'line-height': 0, + padding: '8px 0 6px', + }, + '.mjx-dialog-button > mjx-dialog-icon:hover': { + 'background-color': '#CCC !important', + }, + + // + // The close button + // + 'mjx-dialog-close': { + right: '6px', + 'font-size': '20px;', + }, + + // + // The help button + // + 'mjx-dialog-help': { + left: '6px', + 'font-size': '14px;', + 'font-weight': 'bold', + }, + '.mjx-dialog-help mjx-dialog-help': { + display: 'none', + }, + + // + // Key icons in the dialogs + // + 'mjx-dialog kbd': { + display: 'inline-block', + padding: '3px 5px', + 'font-size': '11px', + 'line-height': '10px', + color: '#444d56', + 'vertical-align': 'middle', + 'background-color': '#fafbfc', + border: 'solid 1.5px #c6cbd1', + 'border-bottom-color': '#959da5', + 'border-radius': '3px', + 'box-shadow': 'inset -.5px -1px 0 #959da5', + }, + + // + // The drag edges and corners + // + 'mjx-dialog-drag[data-drag="top"]': { + height: '5px', + position: 'absolute', + top: '-3px', + left: '10px', + right: '10px', + cursor: 'ns-resize', + }, + 'mjx-dialog-drag[data-drag="bottom"]': { + height: '5px', + position: 'absolute', + bottom: '-3px', + left: '10px', + right: '10px', + cursor: 'ns-resize', + }, + 'mjx-dialog-drag[data-drag="left"]': { + width: '5px', + position: 'absolute', + left: '-3px', + top: '10px', + bottom: '10px', + cursor: 'ew-resize', + }, + 'mjx-dialog-drag[data-drag="right"]': { + width: '5px', + position: 'absolute', + right: '-3px', + top: '10px', + bottom: '10px', + cursor: 'ew-resize', + }, + 'mjx-dialog-drag[data-drag="topleft"]': { + width: '13px', + height: '13px', + position: 'absolute', + left: '-3px', + top: '-3px', + cursor: 'nwse-resize', + }, + 'mjx-dialog-drag[data-drag="topright"]': { + width: '13px', + height: '13px', + position: 'absolute', + right: '-3px', + top: '-3px', + cursor: 'nesw-resize', + }, + 'mjx-dialog-drag[data-drag="botleft"]': { + width: '13px', + height: '13px', + position: 'absolute', + left: '-3px', + bottom: '-3px', + cursor: 'nesw-resize', + }, + 'mjx-dialog-drag[data-drag="botright"]': { + width: '13px', + height: '13px', + position: 'absolute', + right: '-3px', + bottom: '-3px', + cursor: 'nwse-resize', + }, + + '@media (prefers-color-scheme: dark)': { + '.mjx-dialog': { + 'background-color': '#303030', + 'box-shadow': '0px 10px 20px #000', + border: '3px outset #7C7C7C', + }, + 'mjx-dialog': { + color: '#E0E0E0', + }, + 'mjx-dialog > div': { + border: '2px inset #7C7C7C', + 'background-color': '#222025', + }, + 'a[href]': { + color: '#86A7F5', + }, + 'a[href]:visited': { + color: '#DD98E2', + }, + 'mjx-dialog kbd': { + color: '#F8F8F8', + 'background-color': '#545454', + border: 'solid 1.5px #7A7C7E', + 'border-bottom-color': '#707070', + 'box-shadow': 'inset -.5px -1px 0 #818589', + }, + '.mjx-dialog-button': { + border: '2px solid #686868', + color: '#A4A4A4', + }, + '.mjx-dialog-button:hover': { + color: '#CBCBCB !important', + border: '2px solid #888888 !important', + }, + '.mjx-dialog-button > mjx-dialog-icon': { + 'background-color': '#646464', + }, + '.mjx-dialog-button > mjx-dialog-icon:hover': { + 'background-color': '#888888 !important', + }, + }, + }; + + protected static helpMessage: string = ` +

The dialog boxes in MathJax are movable and sizeable.

+ +

For mouse users, dragging any of the edges will enlarge or shrink + the dialog box by moving that side. Dragging any of the corners + changes the two sides that meet at that corner. Dragging elsewhere on + the dialog frame will move the dialog without changing its size.

+ +

For keyboard users, there are two ways to adjust the position + and size of the dialog box. The first is to hold the + Alt or Option key and press any of the arrow + keys to move the dialog box in the given direction. Hold the + Win or Command key and press any of the + arrow keys to enlarge or shrink the dialog box. Left and right + move the right-hand edge of the dialog, while up and down move the + bottom edge of the dialog. +

+ +

For some users, holding two keys down at once may be difficult, + so the second way is to press the m to start "move" + mode, then use the arrow keys to move the dialog box in the given + direction. Press m again to stop moving the dialog. + Similarly, press s to start and stop "sizing" mode, + where the arrows will change the size of the dialog box.

+ +

Holding a shift key along with the arrow key will + make larger changes in the size or position, for either method + described above.

+ +

Use Tab to move among the text, buttons, and links + within the dialog. The Enter or Space key + activates the focused item. The Escape key closes the + dialog, as does clicking outside the dialog box, or clicking the + "\u00D7" icon in the upper right-hand corner of the dialog.

+ `; + + /** + * When moving/sizing by keyboard, this gives which is being adjusted. + */ + protected mode: string = ''; + + /** + * @param {DialogArgs} args The data describing the dialog + */ + constructor(args: DialogArgs) { + const { adaptor, node = null } = args; + this.init(adaptor); + + this.node = node; + this.background = isDialog ? null : adaptor.node('mjx-dialog-background'); + + this.x = this.y = 0; + this.dragging = false; + this.action = ''; + + this.dialog = this.html(args); + this.title = this.dialog.firstChild.firstChild.firstChild as HTMLElement; + this.content = this.dialog.firstChild.firstChild.nextSibling as HTMLElement; + const close = this.dialog.lastChild; + close.addEventListener('click', this.closeDialog.bind(this)); + close.addEventListener( + 'keydown', + this.actionKey.bind(this, this.closeDialog.bind(this)) + ); + const help = this.dialog.lastChild.previousSibling; + help.addEventListener('click', this.helpDialog.bind(this, adaptor)); + help.addEventListener( + 'keydown', + this.actionKey.bind(this, this.helpDialog.bind(this, adaptor)) + ); + + this.noDrag = Array.from( + this.dialog.querySelectorAll('[data-drag="none"]') + ); + } + + /** + * Create the stylesheet, if it hasn't already been done + * + * @param {ADAPTOR} adaptor The DOM adaptor to use + */ + protected init(adaptor: ADAPTOR) { + const CLASS = this.constructor as typeof DraggableDialog; + const head = adaptor.document.head; + if (!head.querySelector('#' + CLASS.styleId)) { + const style = adaptor.node('style', { id: CLASS.styleId }); + style.textContent = new StyleJsonSheet(CLASS.styles).cssText; + adaptor.document.head.append(style); + } + } + + /** + * Create the HTML for the dialog layout + * + * @param {DialogArgs} args The data describing the dialog + * @returns {HTMLDialogElement} The dialog node + */ + protected html(args: DialogArgs): HTMLDialogElement { + // + // Deconstruct the data for easier access + // + const { + title, + message, + adaptor, + styles = null, + extraNodes = [], + className = DraggableDialog.className, + } = args; + + // + // Add a styleshee, if needed + // + if (styles) { + const stylesheet = adaptor.node('style'); + stylesheet.textContent = new StyleJsonSheet(styles).cssText; + extraNodes.unshift(stylesheet); + } + // + // Create the dialog HTML tree + // + const label = 'mjx-dialog-label-' + DraggableDialog.id++; + const dialog = adaptor.node( + 'dialog', + { closedby: 'any', class: ('mjx-dialog ' + className).trim() }, + [ + adaptor.node('mjx-dialog', { 'aria-labeledby': label }, [ + adaptor.node('mjx-title', {}, [ + adaptor.node('h1', { id: label, tabIndex: 0 }), + ]), + adaptor.node('div', { 'data-drag': 'none', tabIndex: 0 }), + ]), + ...extraNodes, + adaptor.node('mjx-dialog-spacer', { 'aria-hidden': true }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'top', + 'aria-hidden': true, + }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'bottom', + 'aria-hidden': true, + }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'left', + 'aria-hidden': true, + }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'right', + 'aria-hidden': true, + }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'topleft', + 'aria-hidden': true, + }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'topright', + 'aria-hidden': true, + }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'botleft', + 'aria-hidden': true, + }), + adaptor.node('mjx-dialog-drag', { + 'data-drag': 'botright', + 'aria-hidden': true, + }), + adaptor.node( + 'mjx-dialog-help', + { + class: 'mjx-dialog-button', + 'data-drag': 'none', + tabIndex: 0, + role: 'button', + 'aria-label': 'Dialog Help', + }, + [ + adaptor.node('mjx-dialog-icon', { 'aria-hidden': true }, [ + adaptor.text('?'), + ]), + ] + ), + adaptor.node( + 'mjx-dialog-close', + { + class: 'mjx-dialog-button', + 'data-drag': 'none', + tabIndex: 0, + role: 'button', + 'aria-label': 'Close Dialog Box', + }, + [ + adaptor.node('mjx-dialog-icon', { 'aria-hidden': true }, [ + adaptor.text('\u00d7'), + ]), + ] + ), + ] + ) as HTMLDialogElement; + // + // Set the title and message + // + (dialog.firstChild.firstChild.firstChild as HTMLElement).innerHTML = title; + (dialog.firstChild.childNodes[1] as HTMLElement).innerHTML = message; + return dialog; + } + + /** + * Add the dialog to the page and attach the event handlers + */ + public attach() { + if (isDialog) { + // + // For actual dialog elements, open as a model dialog + // + this.dialog.addEventListener('mousedown', this.MouseDown.bind(this)); + this.dialog.addEventListener('keydown', this.KeyDown.bind(this), true); + document.body.append(this.dialog); + this.dialog.showModal(); + } else { + // + // When a true dialog element isn't available, use the background element + // + this.background.addEventListener('mousedown', this.MouseDown.bind(this)); + this.background.addEventListener( + 'keydown', + this.KeyDown.bind(this), + true + ); + this.dialog.setAttribute('tabindex', '0'); + this.dialog.addEventListener('click', this.stop); + this.background.append(this.dialog); + document.body.append(this.background); + } + context.window.addEventListener( + 'visibilitychange', + this.Visibility.bind(this) + ); + // + // Adjust the min width and height, if the initial dialog is small + // + this.minW = Math.min(this.minW, this.dialog.clientWidth - 8); + this.minH = Math.min( + this.minH, + this.dialog.clientHeight - this.title.offsetHeight - 8 + ); + // + // Focus the title (VoiceOver needs this) + // + this.title.focus(); + } + + /** + * Functions to handle the various mouse events, returning + * an array [dx, dy, dw, dh] of changes to the position and size + * od the dialog. + */ + protected actions: ActionMap = { + // + // Mouse actions + // + + down: { + move: (d) => { + d.dialog.classList.add('mjx-moving'); + }, + }, + + move: { + move: (dg, ev) => [ev.x - dg.x, ev.y - dg.y, 0, 0], + top: (dg, ev) => [0, (ev.y - dg.y) / 2, 0, dg.y - ev.y], + bottom: (dg, ev) => [0, (ev.y - dg.y) / 2, 0, ev.y - dg.y], + left: (dg, ev) => [(ev.x - dg.x) / 2, 0, dg.x - ev.x, 0], + right: (dg, ev) => [(ev.x - dg.x) / 2, 0, ev.x - dg.x, 0], + topleft: (dg, ev) => [ + (ev.x - dg.x) / 2, + (ev.y - dg.y) / 2, + dg.x - ev.x, + dg.y - ev.y, + ], + topright: (dg, ev) => [ + (ev.x - dg.x) / 2, + (ev.y - dg.y) / 2, + ev.x - dg.x, + dg.y - ev.y, + ], + botleft: (dg, ev) => [ + (ev.x - dg.x) / 2, + (ev.y - dg.y) / 2, + dg.x - ev.x, + ev.y - dg.y, + ], + botright: (dg, ev) => [ + (ev.x - dg.x) / 2, + (ev.y - dg.y) / 2, + ev.x - dg.x, + ev.y - dg.y, + ], + }, + + up: { + move: (dg) => { + dg.dialog.classList.remove('mjx-moving'); + }, + }, + + // + // Keyboard actions + // + + keymove: { + left: () => [-5, 0, 0, 0], + right: () => [5, 0, 0, 0], + up: () => [0, -5, 0, 0], + down: () => [0, 5, 0, 0], + }, + + bigmove: { + left: () => [-20, 0, 0, 0], + right: () => [20, 0, 0, 0], + up: () => [0, -20, 0, 0], + down: () => [0, 20, 0, 0], + }, + + keysize: { + left: () => [-3, 0, -6, 0], + right: () => [3, 0, 6, 0], + up: () => [0, -3, 0, -6], + down: () => [0, 3, 0, 6], + }, + + bigsize: { + left: () => [-10, 0, -20, 0], + right: () => [10, 0, 20, 0], + up: () => [0, -10, 0, -20], + down: () => [0, 10, 0, 20], + }, + }; + + /** + * Perform a drag action (resize or move) + * + * @param {string} type The action type to perform + * @param {MouseEvent} event The event causing the action + */ + protected dragAction(type: string, event: MouseEvent = null) { + if (event) { + this.stop(event); + } + // + // Get the move/resize data for the action + // + const action = this.actions[type][this.action]; + const result = action ? action(this, event) : null; + if (!result) { + return; + } + let [dx, dy, dw, dh] = result; + // + // Adjust the width + // + if (dw) { + const W = this.w + dw; + if (W >= this.minW) { + this.x = event?.x; + this.w = W; + this.dialog.style.maxWidth = this.dialog.style.width = W + 'px'; + } else { + dx = 0; + } + } + // + // Adjust the height + // + if (dh) { + const H = this.h + dh; + if (H >= this.minH + this.title.offsetHeight) { + this.y = event?.y; + this.h = H; + this.dialog.style.maxHeight = this.dialog.style.height = H + 'px'; + } else { + dy = 0; + } + } + // + // Adjust the position + // + if (dx || dy) { + if (dx) { + this.x = event?.x; + this.tx += dx || 0; + } + if (dy) { + this.y = event?.y; + this.ty += dy || 0; + } + this.dialog.style.transform = `translate(${this.tx}px, ${this.ty}px)`; + } + } + + /** + * Handle a mousedown event + * + * @param {MouseEvent} event The mouse event to handle + */ + protected MouseDown(event: MouseEvent) { + const target = event.target as HTMLElement; + // + // Check that it is a plain click not on the background + // + if ( + event.buttons !== 1 || + event.shiftKey || + event.metaKey || + event.altKey || + event.ctrlKey + ) { + return; + } + if (!this.inDialog(event)) { + this.closeDialog(event); + return; + } + + // + // Check that it is not on an element marked as not for dragging + // + for (const node of this.noDrag) { + if (target === node || node.contains(target)) { + return; + } + } + // + // Start the drag action + // + this.action = target.getAttribute('data-drag') || 'move'; + this.startDrag(event); + this.dragAction('down', event); + } + + /** + * Handle a mousemove event + * + * @param {MouseEvent} event The mouse event to handle + */ + protected MouseMove(event: MouseEvent) { + if (event.buttons !== 1) { + this.endDrag(); // in case the nouse up occurred over a different element + } + if (this.dragging) { + this.dragAction('move', event); + } + } + + /** + * Handle a mouseup event + * + * @param {MouseEvent} event The mouse event to handle + */ + protected MouseUp(event: MouseEvent) { + if (this.dragging) { + this.dragAction('up', event); + this.endDrag(); + } + } + + /** + * Close the dialog if the pages becomes hidden (e.g., moving + * foreward or backward in the window's history). + */ + protected Visibility() { + if (context.document.hidden) { + this.closeDialog(); + } + } + + /** + * Handle a keydown event + * + * @param {KeyboardEvent} event The key event to handle + */ + protected KeyDown(event: KeyboardEvent) { + const CLASS = this.constructor as typeof DraggableDialog; + const action = CLASS.keyActions.get(event.key); + if (action) { + action(this, event); + } + } + + /** + * Handle the Escape key + * + * @param {KeyboardEvent} event The key event to handle + */ + protected escKey(event: KeyboardEvent) { + this.closeDialog(event); + } + + /** + * Handle the "a" key for selecting all + * + * @param {KeyboardEvent} event The key event to handle + */ + protected aKey(event: KeyboardEvent) { + if (event.ctrlKey || event.metaKey) { + this.selectAll(); + this.stop(event); + } + } + + /** + * Start or stop moving the dialog via arrow keys + * + * @param {KeyboardEvent} event The key event to handle + */ + protected mKey(event: KeyboardEvent) { + this.mode = this.mode === 'move' ? '' : 'move'; + this.stop(event); + } + + /** + * Start or stop sizing the dialog via arrow keys + * + * @param {KeyboardEvent} event The key event to handle + */ + protected sKey(event: KeyboardEvent) { + this.mode = this.mode === 'size' ? '' : 'size'; + this.stop(event); + } + + /** + * Handle the arrow keys + * + * @param {KeyboardEvent} event The key event to handle + * @param {string} direction The direction of the arrow + */ + protected arrowKey(event: KeyboardEvent, direction: string) { + if (event.ctrlKey || this.dragging) return; + this.action = direction; + this.getWH(); + if (event.altKey || this.mode === 'move') { + this.dragAction(event.shiftKey ? 'bigmove' : 'keymove'); + this.stop(event); + } else if (event.metaKey || this.mode === 'size') { + this.dragAction(event.shiftKey ? 'bigsize' : 'keysize'); + this.stop(event); + } + this.action = ''; + } + + /** + * Handle the enter or space key on a button icon + * + * @param {(event: KeyboardEvent) => void} action The action to take on enter or space + * @param {KeyboardEvent} event The event to check + */ + protected actionKey( + action: (event: KeyboardEvent) => void, + event: KeyboardEvent + ) { + if (event.code === 'Enter' || event.code === 'Space') { + action(event); + } + } + + /** + * Select the content of the dialog for copying + */ + protected selectAll() { + const selection = document.getSelection(); + selection.selectAllChildren(this.content); + } + + /** + * Implement the copy-to-clipboard action + */ + public copyToClipboard() { + this.selectAll(); + try { + document.execCommand('copy'); + } catch (err) { + alert(`Can't copy to clipboard: ${err.message}`); + } + document.getSelection().removeAllRanges(); + } + + /** + * Start dragging for a move or resize action. + * + * @param {MouseEvent} event The mousedown event starting the drag + */ + protected startDrag(event: MouseEvent) { + // + // Record the initial data + // + this.x = event.x; + this.y = event.y; + this.getWH(); + this.dragging = true; + // + // Add the mousemove and mouseup handlers + // + const node = this.background || this.dialog; + for (const [name, listener] of this.events) { + node.addEventListener(name, listener); + } + } + + /** + * Cache the current width and height values. + */ + protected getWH() { + this.w = this.dialog.clientWidth - 8; // adjust for the 4px padding on all sides + this.h = this.dialog.clientHeight - 8; + } + + /** + * End a dragging operation + */ + protected endDrag() { + // + // Clear the actions + // + this.action = ''; + this.dragging = false; + // + // Remove the mousemove and mouseup event handlers + // + const node = this.background || this.dialog; + for (const [name, listener] of this.events) { + node.removeEventListener(name, listener); + } + } + + /** + * Close the dialog + * + * @param {Event} event The event that caused the closure + */ + protected closeDialog(event?: Event) { + if (isDialog) { + this.dialog.close(); + this.dialog.remove(); + } else { + this.background.remove(); + } + this.node?.focus(); + if (event) { + this.stop(event); + } + } + + /** + * Display the dialog help message + * + * @param {ADAPTOR} adaptor The DOM adaptor to use + * @param {Event} event The event that triggered the help + */ + protected helpDialog(adaptor: ADAPTOR, event: Event) { + const help = new DraggableDialog({ + title: 'MathJax Dialog Help', + message: (this.constructor as typeof DraggableDialog).helpMessage, + adaptor: adaptor, + className: 'mjx-dialog-help', + styles: { + '.mjx-dialog-help': { + 'max-width': 'calc(min(50em, 80%))', + }, + }, + }); + help.attach(); + this.stop(event); + } + + /** + * Check if an event is inside the dialog. + * + * @param {MouseEvent} event The event to check + * @returns {boolean} True if the event is in the dialog, false if in the background + */ + protected inDialog(event: MouseEvent): boolean { + if (!this.dialog.contains(event.target as HTMLElement)) { + return false; + } + const { x, y } = event; + const { left, right, top, bottom } = this.dialog.getBoundingClientRect(); + return x >= left && x <= right && y >= top && y <= bottom; + } + + /** + * Stop event propagation + * + * @param {Event} event The event that is to be stopped + */ + protected stop(event: Event) { + if (event.preventDefault) { + event.preventDefault(); + } + if (event.stopImmediatePropagation) { + event.stopImmediatePropagation(); + } else if (event.stopPropagation) { + event.stopPropagation(); + } + } +} diff --git a/ts/ui/dialog/InfoDialog.ts b/ts/ui/dialog/InfoDialog.ts new file mode 100644 index 000000000..af44099ca --- /dev/null +++ b/ts/ui/dialog/InfoDialog.ts @@ -0,0 +1,43 @@ +/************************************************************* + * + * Copyright (c) 2025 The MathJax Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Implements the InfoDialog class. + * + * @author dpvc@mathjax.org (Davide Cervone) + */ + +import { DraggableDialog, DialogArgs } from './DraggableDialog.js'; + +export type InfoDialogArgs = DialogArgs; + +/** + * A generic info dialog box + */ +export class InfoDialog extends DraggableDialog { + /** + * Create and display a dialog with the given args + * + * @param {DialogArgs} args The data describing the dialog + * @returns {DraggableDialog} The dialog instance + */ + public static post(args: DialogArgs): DraggableDialog { + const dialog = new this(args); + dialog.attach(); + return dialog; + } +} diff --git a/ts/ui/dialog/SelectionDialog.ts b/ts/ui/dialog/SelectionDialog.ts new file mode 100644 index 000000000..be90fc30d --- /dev/null +++ b/ts/ui/dialog/SelectionDialog.ts @@ -0,0 +1,119 @@ +/************************************************************* + * + * Copyright (c) 2025 The MathJax Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Implements a selection info dialog box + * + * @author dpvc@mathjax.org (Davide P. Cervone) + */ + +import { InfoDialog } from './InfoDialog.js'; +import { MJContextMenu } from '../menu/MJContextMenu.js'; +import { + SelectionOrder, + SelectionGrid, + SelectionBox, +} from '#menu/selection_box.js'; +export { SelectionOrder, SelectionGrid } from '#menu/selection_box.js'; + +export type selection = { title: string; values: string[]; variable: string }; + +/** + * The Selection dialog class + */ +export class SelectionDialog extends SelectionBox { + /** + * @override + */ + constructor( + title: string, + signature: string, + selections: selection[], + order: SelectionOrder, + grid: SelectionGrid, + menu: MJContextMenu + ) { + super(title, signature, order, grid); + this.attachMenu(menu); + const factory = menu.factory; + this.selections = selections.map((x) => + factory.get('selectionMenu')(factory, x, this) + ); + } + + /** + * @override + */ + public post() { + // + // Get the active output jax (to get its adaptor) + // + const jax = Array.from(Object.values((this.menu as any).jax)).filter( + (j) => !!j + )[0] as any; + // + // Use an InfoDialog rather than the mj-context-menu Info object + // + const dialog = new InfoDialog({ + title: (this as any).title, // should be protected rather than private + message: '', + adaptor: jax.adaptor, + styles: { + 'mjx-dialog > div': { + padding: 0, + }, + }, + }); + dialog.attach(); + // + // Use the SelectionBox display function + // + this.contentDiv = (dialog as any).content as HTMLElement; + this.display(); + } + + /** + * @override + */ + public display() { + // + // This is the same as teh SelectionBox() function, but without the super.display() call. + // + const THIS = this as any; // the methods below are private, so work around that. + THIS.order(); + if (!this.selections.length) { + return; + } + const outerDivs: HTMLElement[] = []; + let maxWidth = 0; + let balancedColumn: number[] = []; + const chunks = THIS.getChunkSize(this.selections.length); + for (let i = 0; i < this.selections.length; i += chunks) { + const sels = this.selections.slice(i, i + chunks); + const [div, width, height, column] = THIS.rowDiv(sels); + outerDivs.push(div); + maxWidth = Math.max(maxWidth, width); + sels.forEach((sel) => (sel.html.style.height = height + 'px')); + balancedColumn = THIS.combineColumn(balancedColumn, column); + } + if (THIS._balanced) { + THIS.balanceColumn(outerDivs, balancedColumn); + maxWidth = balancedColumn.reduce((x, y) => x + y - 2, 20); // remove 2px for borders + } + outerDivs.forEach((div) => (div.style.width = maxWidth + 'px')); + } +} diff --git a/ts/ui/menu/AnnotationMenu.ts b/ts/ui/menu/AnnotationMenu.ts index d4ff01e63..d7a85e0bd 100644 --- a/ts/ui/menu/AnnotationMenu.ts +++ b/ts/ui/menu/AnnotationMenu.ts @@ -23,9 +23,12 @@ */ import { SubMenu, Submenu } from './mj-context-menu.js'; -import { MJContextMenu } from './MJContextMenu.js'; +import { + MJContextMenu, + DynamicSubmenu, + SubmenuCallback, +} from './MJContextMenu.js'; import { MmlNode } from '../../core/MmlTree/MmlNode.js'; -import { SelectableInfo } from './SelectableInfo.js'; import * as MenuUtil from './MenuUtil.js'; /** @@ -39,22 +42,20 @@ type AnnotationTypes = { [type: string]: string[] }; /** * Returns a method to create the dynamic submenu for showing annotations. * - * @param {SelectableInfo} box The info box in which to post annotation info. + * @param {() => void} box The info box in which to post annotation info. * @param {AnnotationTypes} types The legitimate annotation types. * @param {[string, string][]} cache We cache annotations of a math item, so we * only have to compute them once for the two annotation menus. - * @returns {(menu: MJContextMenu, sub: Submenu) => SubMenu} Method generating - * the show annotations submenu. + * @returns {DynamicSubmenu} Method generating the show annotations submenu. */ export function showAnnotations( - box: SelectableInfo, + box: () => void, types: AnnotationTypes, cache: [string, string][] -): (menu: MJContextMenu, sub: Submenu) => SubMenu { - return (menu: MJContextMenu, sub: Submenu) => { +): DynamicSubmenu { + return (menu: MJContextMenu, sub: Submenu, callback: SubmenuCallback) => { getAnnotation(getSemanticNode(menu), types, cache); - box.attachMenu(menu); - return createAnnotationMenu(menu, sub, cache, () => box.post()); + callback(createAnnotationMenu(menu, sub, cache, box)); }; } @@ -63,15 +64,17 @@ export function showAnnotations( * Clears the annotation cache parameter. * * @param {[string, string][]} cache The annotation cache. - * @returns {(menu: MJContextMenu, sub: Submenu) => SubMenu} Method generating + * @returns {DynamicSubmenu} Method generating * the copy annotations submenu. */ -export function copyAnnotations(cache: [string, string][]) { - return (menu: MJContextMenu, sub: Submenu) => { +export function copyAnnotations(cache: [string, string][]): DynamicSubmenu { + return (menu: MJContextMenu, sub: Submenu, callback: SubmenuCallback) => { const annotations = cache.slice(); cache.length = 0; - return createAnnotationMenu(menu, sub, annotations, () => - MenuUtil.copyToClipboard(annotation.trim()) + callback( + createAnnotationMenu(menu, sub, annotations, () => + MenuUtil.copyToClipboard(annotation.trim()) + ) ); }; } diff --git a/ts/ui/menu/MJContextMenu.ts b/ts/ui/menu/MJContextMenu.ts index c4554d287..42441f52a 100644 --- a/ts/ui/menu/MJContextMenu.ts +++ b/ts/ui/menu/MJContextMenu.ts @@ -34,6 +34,14 @@ import { Item, } from './mj-context-menu.js'; +export type SubmenuCallback = (sub: SubMenu) => void; + +export type DynamicSubmenu = ( + menu: MJContextMenu, + sub: Submenu, + callback: SubmenuCallback +) => void; + /*==========================================================================*/ /** @@ -44,19 +52,10 @@ export class MJContextMenu extends ContextMenu { /** * Static map to hold methods for re-computing dynamic submenus. * - * @type {Map Submenu>} + * @type {Map} */ - public static DynamicSubmenus: Map< - string, - [ - ( - menu: MJContextMenu, - sub: Submenu, - callback: (sub: SubMenu) => void - ) => void, - string, - ] - > = new Map(); + public static DynamicSubmenus: Map = + new Map(); /** * The MathItem that has posted the menu @@ -116,7 +115,9 @@ export class MJContextMenu extends ContextMenu { * @override */ public unpost() { - super.unpost(); + if ((this as any).posted) { + super.unpost(); + } if (this.mathItem) { this.mathItem.outputData.nofocus = this.nofocus; } diff --git a/ts/ui/menu/Menu.ts b/ts/ui/menu/Menu.ts index ca07756d2..6c8b260a2 100644 --- a/ts/ui/menu/Menu.ts +++ b/ts/ui/menu/Menu.ts @@ -35,6 +35,8 @@ import { expandable, } from '../../util/Options.js'; import { ExplorerMathItem } from '../../a11y/explorer.js'; +import { InfoDialog } from '../dialog/InfoDialog.js'; +import { CopyDialog } from '../dialog/CopyDialog.js'; import { SVG } from '../../output/svg.js'; @@ -42,11 +44,10 @@ import * as AnnotationMenu from './AnnotationMenu.js'; import { MJContextMenu } from './MJContextMenu.js'; import { RadioCompare } from './RadioCompare.js'; import { MmlVisitor } from './MmlVisitor.js'; -import { SelectableInfo } from './SelectableInfo.js'; import { MenuMathDocument } from './MenuHandler.js'; import * as MenuUtil from './MenuUtil.js'; -import { Info, Parser, Rule, CssStyles, Submenu } from './mj-context-menu.js'; +import { Parser, Rule, CssStyles, Submenu } from './mj-context-menu.js'; /*==========================================================================*/ @@ -90,6 +91,8 @@ export interface MenuSettings { backgroundOpacity: string; braille: boolean; brailleCode: string; + brailleSpeech: boolean; + brailleCombine: boolean; foregroundColor: string; foregroundOpacity: string; highlight: string; @@ -140,7 +143,7 @@ export class Menu { zoom: 'NoZoom', zscale: '200%', renderer: 'CHTML', - alt: false, + alt: true, cmd: false, ctrl: false, shift: false, @@ -155,9 +158,12 @@ export class Menu { speech: true, braille: true, brailleCode: 'nemeth', + brailleSpeech: false, + brailleCombine: false, speechRules: 'clearspeak-default', roleDescription: 'math', tabSelects: 'all', + help: true, }, jax: { CHTML: null, @@ -183,7 +189,7 @@ export class Menu { '.mjx-dashed{stroke-dasharray:140}', '.mjx-dotted{stroke-linecap:round;stroke-dasharray:0,140}', 'use[data-c]{stroke-width:3px}', - ].join(''); + ].join('\n'); /** * The number of startup modules that are currently being loaded @@ -290,27 +296,99 @@ export class Menu { /** * The "About MathJax" info box */ - protected about = new Info( - 'MathJax v' + mathjax.version, - () => { - const lines = [] as string[]; - lines.push( - 'Input Jax: ' + this.document.inputJax.map((jax) => jax.name).join(', ') - ); - lines.push('Output Jax: ' + this.document.outputJax.name); - lines.push('Document Type: ' + this.document.kind); - return lines.join('
'); - }, - '
www.mathjax.org' - ); + protected about() { + const lines = [] as string[]; + // + // Add the input and output jax and the document type + // + lines.push( + 'Input Jax: ' + this.document.inputJax.map((jax) => jax.name).join(', ') + ); + lines.push('Output Jax: ' + this.document.outputJax.name); + lines.push('Document Type: ' + this.document.kind); + // + // Add the loaded packages and their versions + // + if (MathJax && MathJax.loader) { + lines.push('
Modules Loaded:'); + const Package = MathJax._.components.package.Package; + const versions = (MathJax as any).loader.versions; + for (const name of Array.from(Package.packages.keys()).sort( + this.sortPackages + )) { + const version = versions.get(Package.resolvePath(name)); + if (version) { + lines.push( + `    ${name} (${version})` + ); + } + } + } + // + // Post the dialog + // + InfoDialog.post({ + title: 'MathJax v' + mathjax.version + '', + message: lines.join('
'), + adaptor: this.document.adaptor, + styles: { + '.mjx-dialog': { + 'max-height': 'calc(min(20em, 85%))', + }, + 'mjx-dialog > div': { + 'white-space': 'nowrap', + }, + 'dialog.mjx-dialog-help > mjx-dialog > div': { + 'white-space': 'normal', + }, + 'mjx-v': { + 'font-size': '80%', + }, + }, + extraNodes: [ + this.document.adaptor.node( + 'a', + { href: 'https://www.mathjax.org', 'data-drag': 'false' }, + [this.document.adaptor.text('https://www.mathjax.org')] + ), + ], + }); + } + + /** + * Function to sort the package names + * + * @param {string} a The first module name + * @param {string} b The second module name + * @returns {number} -1 of a < b, 1 if a > b + */ + protected sortPackages(a: string, b: string): number { + const [prefixA, rootA] = a.includes('/') ? a.split(/\//) : ['', a]; + const [prefixB, rootB] = b.includes('/') ? b.split(/\//) : ['', b]; + return prefixA === prefixB + ? rootA < rootB + ? -1 + : 1 + : prefixA.charAt(0) === '[' + ? prefixB.charAt(0) === '[' + ? prefixA < prefixB + ? -1 + : 1 + : 1 + : prefixB.charAt(0) === '[' + ? -1 + : prefixA < prefixB + ? -1 + : 1; + } /** * The "MathJax Help" info box */ - protected help = new Info( - 'MathJax Help', - () => { - return [ + protected help() { + InfoDialog.post({ + title: 'MathJax Help', + message: [ '

MathJax is a JavaScript library that allows page', ' authors to include mathematics within their web pages.', " As a reader, you don't need to do anything to make that happen.

", @@ -343,146 +421,125 @@ export class Menu { ' to save the preferences set via this menu locally in your browser. These', ' are not used to track you, and are not transferred or used remotely by', ' MathJax in any way.

', - ].join('\n'); - }, - 'www.mathjax.org' - ); + ].join('\n'), + adaptor: this.document.adaptor, + extraNodes: [ + this.document.adaptor.node( + 'a', + { href: 'https://www.mathjax.org', 'data-drag': 'none' }, + [this.document.adaptor.text('https://www.mathjax.org')] + ), + ], + }); + } /** * The "Show As MathML" info box */ - protected mathmlCode = new SelectableInfo( - 'MathJax MathML Expression', - () => { - if (!this.menu.mathItem) return ''; - const text = this.toMML(this.menu.mathItem); - return '
' + this.formatSource(text) + '
'; - }, - '' - ); + protected mathMLCode() { + CopyDialog.post({ + title: 'MathJax MathML Expression', + message: this.menu.mathItem ? this.toMML(this.menu.mathItem) : '', + adaptor: this.document.adaptor, + code: true, + }); + } /** * The "Show As (original form)" info box */ - protected originalText = new SelectableInfo( - 'MathJax Original Source', - () => { - if (!this.menu.mathItem) return ''; - const text = this.menu.mathItem.math; - return ( - '
' +
-        this.formatSource(text) +
-        '
' - ); - }, - '' - ); + protected originalText() { + CopyDialog.post({ + title: 'MathJax Original Source', + message: this.menu.mathItem?.math ?? '', + adaptor: this.document.adaptor, + code: true, + }); + } /** * The "Show As Annotation" info box */ - protected annotationBox = new SelectableInfo( - 'MathJax Annotation Text', - () => { - const text = AnnotationMenu.annotation; - return ( - '
' +
-        this.formatSource(text) +
-        '
' - ); - }, - '' - ); + protected annotationBox() { + CopyDialog.post({ + title: 'MathJax Annotation Text', + message: AnnotationMenu.annotation, + adaptor: this.document.adaptor, + code: true, + }); + } /** * The "Show As SVG Image" info box */ - protected svgImage = new SelectableInfo( - 'MathJax SVG Image', - () => { - // - // SVG image inserted after it is created - // - return ( - '
' + - 'Generative SVG Image...
' - ); - }, - '' - ); + public async svgImage() { + CopyDialog.post({ + title: 'MathJax SVG Image', + message: await this.toSVG(this.menu.mathItem), + adaptor: this.document.adaptor, + code: true, + }); + } /** * The "Show As Speech Text" info box */ - protected speechText = new SelectableInfo( - 'MathJax Speech Text', - () => { - if (!this.menu.mathItem) return ''; - return ( - '
' + - this.formatSource(this.menu.mathItem.outputData.speech) + - '
' - ); - }, - '' - ); + protected speechText() { + CopyDialog.post({ + title: 'MathJax Speech Text', + message: this.menu.mathItem?.outputData?.speech ?? '', + adaptor: this.document.adaptor, + code: true, + }); + } /** * The "Show As Speech Text" info box */ - protected brailleText = new SelectableInfo( - 'MathJax Braille Code', - () => { - if (!this.menu.mathItem) return ''; - return ( - '
' + - this.formatSource(this.menu.mathItem.outputData.braille) + - '
' - ); - }, - '' - ); + protected brailleText() { + CopyDialog.post({ + title: 'MathJax Braille Text', + message: this.menu.mathItem?.outputData?.braille ?? '', + adaptor: this.document.adaptor, + code: true, + }); + } /** * The "Show As Error Message" info box */ - protected errorMessage = new SelectableInfo( - 'MathJax Error Message', - () => { - if (!this.menu.mathItem) return ''; - return ( - '
' +
-        this.formatSource(this.menu.errorMsg) +
-        '
' - ); - }, - '' - ); + protected errorMessage() { + CopyDialog.post({ + title: 'MathJax Error Message', + message: this.menu.mathItem ? this.menu.errorMsg : '', + adaptor: this.document.adaptor, + code: true, + }); + } /** * The info box for zoomed expressions */ - protected zoomBox = new Info( - 'MathJax Zoomed Expression', - () => { - if (!this.menu.mathItem) return ''; - const element = (this.menu.mathItem.typesetRoot as any).cloneNode( - true - ) as HTMLElement; - element.style.margin = '0'; - const scale = 1.25 * parseFloat(this.settings.zscale); // 1.25 is to reverse the default 80% font-size - return ( - '
' + element.outerHTML + '
' - ); - }, - '' - ); - - protected postInfo(dialog: Info) { + protected zoomBox() { + let text = ''; if (this.menu.mathItem) { - this.menu.nofocus = !!this.menu.mathItem.outputData.nofocus; + const node = this.menu.mathItem.typesetRoot as HTMLElement; + const size = this.document.adaptor.fontSize(node); + const zoom = node.cloneNode(true) as HTMLElement; + zoom.style.margin = '0'; + const scale = (size * parseFloat(this.settings.zscale)) / 100; + text = `
${zoom.outerHTML}
`; } - dialog.post(); + InfoDialog.post({ + title: 'MathJax Zoomed Expression', + message: text, + adaptor: this.document.adaptor, + styles: { + 'mjx-dialog > div': { + padding: '1.8em', + }, + }, + }); } /*======================================================================*/ @@ -566,11 +623,25 @@ export class Menu { this.variable('brailleCode', (code) => this.setBrailleCode(code) ), + this.a11yVar('brailleSpeech', (speech) => + this.setBrailleSpeech(speech) + ), + this.a11yVar('brailleCombine', (speech) => + this.setBrailleCombine(speech) + ), this.a11yVar('highlight', (value) => this.setHighlight(value)), - this.a11yVar('backgroundColor'), - this.a11yVar('backgroundOpacity'), - this.a11yVar('foregroundColor'), - this.a11yVar('foregroundOpacity'), + this.a11yVar('backgroundColor', (color) => + this.setColor('bg', color) + ), + this.a11yVar('backgroundOpacity', (opacity) => + this.setColor('bg', null, opacity) + ), + this.a11yVar('foregroundColor', (color) => + this.setColor('fg', color) + ), + this.a11yVar('foregroundOpacity', (opacity) => + this.setColor('fg', null, opacity) + ), this.a11yVar('subtitles'), this.a11yVar('viewBraille'), this.a11yVar('voicing'), @@ -606,38 +677,23 @@ export class Menu { ], items: [ this.submenu('Show', 'Show Math As', [ - this.command('MathMLcode', 'MathML Code', () => - this.postInfo(this.mathmlCode) - ), - this.command('Original', 'Original Form', () => - this.postInfo(this.originalText) - ), + this.command('MathMLcode', 'MathML Code', () => this.mathMLCode()), + this.command('Original', 'Original Form', () => this.originalText()), this.rule(), - this.command( - 'Speech', - 'Speech Text', - () => this.postInfo(this.speechText), - { - disabled: true, - } - ), - this.command( - 'Braille', - 'Braille Code', - () => this.postInfo(this.brailleText), - { disabled: true } - ), - this.command('SVG', 'SVG Image', () => this.postSvgImage(), { + this.command('Speech', 'Speech Text', () => this.speechText(), { + disabled: true, + }), + this.command('Braille', 'Braille Code', () => this.brailleText(), { + disabled: true, + }), + this.command('SVG', 'SVG Image', () => this.svgImage(), { disabled: true, }), this.submenu('ShowAnnotation', 'Annotation'), this.rule(), - this.command( - 'Error', - 'Error Message', - () => this.postInfo(this.errorMessage), - { disabled: true } - ), + this.command('Error', 'Error Message', () => this.errorMessage(), { + disabled: true, + }), ]), this.submenu('Copy', 'Copy to Clipboard', [ this.command('MathMLcode', 'MathML Code', () => this.copyMathML()), @@ -693,9 +749,7 @@ export class Menu { this.submenu('Language', 'Language'), this.rule(), this.submenu('ZoomTrigger', 'Zoom Trigger', [ - this.command('ZoomNow', 'Zoom Once Now', () => - this.zoom(null, '', this.menu.mathItem) - ), + this.command('ZoomNow', 'Zoom Once Now', () => this.zoom(null, '')), this.rule(), this.radioGroup('zoom', [ ['Click'], @@ -713,7 +767,7 @@ export class Menu { hidden: !MenuUtil.isMac, }), this.checkbox('Control', 'Control', 'ctrl', { - hiddne: MenuUtil.isMac, + hidden: MenuUtil.isMac, }), this.checkbox('Shift', 'Shift', 'shift'), ]), @@ -764,6 +818,14 @@ export class Menu { this.submenu('Braille', '\xA0 \xA0 Braille', [ this.checkbox('Generate', 'Generate', 'braille'), this.checkbox('Subtitles', 'Show Subtitles', 'viewBraille'), + this.checkbox('BrailleSpeech', 'Replace Speech', 'brailleSpeech', { + hidden: true, + }), + this.checkbox( + 'BrailleCombine', + 'Combine with Speech', + 'brailleCombine' + ), this.rule(), this.label('Code', 'Code Format:'), this.radioGroup('brailleCode', [ @@ -863,8 +925,8 @@ export class Menu { ), ]), this.rule(), - this.command('About', 'About MathJax', () => this.postInfo(this.about)), - this.command('Help', 'MathJax Help', () => this.postInfo(this.help)), + this.command('About', 'About MathJax', () => this.about()), + this.command('Help', 'MathJax Help', () => this.help()), ], }) as MJContextMenu; const menu = this.menu; @@ -872,12 +934,11 @@ export class Menu { menu.findID('Settings', 'Overflow', 'Elide').disable(); menu.findID('Braille', 'ueb').hide(); menu.setJax(this.jax); - this.attachDialogMenus(menu); this.checkLoadableItems(); const cache: [string, string][] = []; MJContextMenu.DynamicSubmenus.set('ShowAnnotation', [ AnnotationMenu.showAnnotations( - this.annotationBox, + () => this.annotationBox(), this.options.annotationTypes, cache ), @@ -887,24 +948,7 @@ export class Menu { AnnotationMenu.copyAnnotations(cache), '', ]); - CssStyles.addInfoStyles(this.document.document as any); - CssStyles.addMenuStyles(this.document.document as any); - } - - /** - * @param {MJContextMenu} menu The menu to attach - */ - protected attachDialogMenus(menu: MJContextMenu) { - this.about.attachMenu(menu); - this.help.attachMenu(menu); - this.originalText.attachMenu(menu); - this.mathmlCode.attachMenu(menu); - this.originalText.attachMenu(menu); - this.svgImage.attachMenu(menu); - this.speechText.attachMenu(menu); - this.brailleText.attachMenu(menu); - this.errorMessage.attachMenu(menu); - this.zoomBox.attachMenu(menu); + CssStyles.addMenuStyles(this.document.document as Document); } /** @@ -1179,6 +1223,16 @@ export class Menu { */ protected setAssistiveMml(mml: boolean) { this.document.options.enableAssistiveMml = mml; + if (mml) { + this.noRerender(() => { + if (this.settings.speech) { + this.menu.pool.lookup('speech').setValue(false); + } + if (this.settings.braille) { + this.menu.pool.lookup('braille').setValue(false); + } + }); + } if (!mml || MathJax._?.a11y?.['assistive-mml']) { this.rerender(); } else { @@ -1212,6 +1266,18 @@ export class Menu { protected setSpeech(speech: boolean) { this.enableAccessibilityItems('Speech', speech); this.document.options.enableSpeech = speech; + if (speech) { + if (this.settings.assistiveMml) { + this.noRerender(() => + this.menu.pool.lookup('assistiveMml').setValue(false) + ); + } + if (this.settings.brailleSpeech) { + this.noRerender(() => + this.menu.pool.lookup('brailleSpeech').setValue(false) + ); + } + } if (!speech || MathJax._?.a11y?.explorer) { this.rerender(STATE.COMPILED); } else { @@ -1225,6 +1291,11 @@ export class Menu { protected setBraille(braille: boolean) { this.enableAccessibilityItems('Braille', braille); this.document.options.enableBraille = braille; + if (braille && this.settings.assistiveMml) { + this.noRerender(() => + this.menu.pool.lookup('assistiveMml').setValue(false) + ); + } if (!braille || MathJax._?.a11y?.explorer) { this.rerender(STATE.COMPILED); } else { @@ -1240,6 +1311,32 @@ export class Menu { this.rerender(STATE.COMPILED); } + /** + * @param {boolean} speech Whether to use aria-label for Braille + */ + protected setBrailleSpeech(speech: boolean) { + if (speech && this.settings.speech) { + this.noRerender(() => this.menu.pool.lookup('speech').setValue(false)); + } else { + this.enableAccessibilityItems('Speech', true); + } + this.settings.brailleCombine = + this.document.options.a11y.brailleCombine = false; + this.rerender(STATE.COMPILED); + } + + /** + * @param {boolean} _speech Whether to combine Braille into aria-label + */ + protected setBrailleCombine(_speech: boolean) { + if (this.settings.brailleSpeech) { + this.menu.pool.lookup('brailleSpeech').setValue(false); + } + this.settings.brailleSpeech = + this.document.options.a11y.brailleSpeech = false; + this.rerender(STATE.COMPILED); + } + /** * @param {string} locale The speech locale */ @@ -1284,9 +1381,6 @@ export class Menu { this.rerender(STATE.COMPILED); } else { this.loadA11y('complexity'); - if (!MathJax._?.a11y?.explorer) { - this.loadA11y('explorer'); - } } } @@ -1308,6 +1402,27 @@ export class Menu { } } + /** + * @param {string} type 'fg' or 'bg' + * @param {string} name The color name + * @param {string} opacity The color's opacity percentage + */ + protected setColor(type: string, name: string, opacity?: string) { + const a11y = this.document.options.a11y; + if (!name) { + name = a11y[type === 'fg' ? 'foregroundColor' : 'backgroundColor']; + } + if (!opacity) { + opacity = a11y[type === 'fg' ? 'foregroundOpacity' : 'backgroundOpacity']; + } + MathJax._.a11y.explorer.Region.LiveRegion.setColor( + type, + 1, + name.toLowerCase(), + parseInt(opacity) / 100 + ); + } + /** * Request the scaling value from the user and save it in the settings */ @@ -1342,24 +1457,24 @@ export class Menu { * Reset all menu settings to the (page) defaults */ protected resetDefaults() { - Menu.loading++; // pretend we're loading, to suppress rerendering for each variable change - const pool = this.menu.pool; - const settings = this.defaultSettings; - for (const name of Object.keys(settings) as (keyof MenuSettings)[]) { - const variable = pool.lookup(name); - if (variable) { - if (variable.getValue() !== settings[name]) { - variable.setValue(settings[name] as string | boolean); - const item = (variable as any).items[0]; - if (item) { - item.executeCallbacks_(); + this.noRerender(() => { + const pool = this.menu.pool; + const settings = this.defaultSettings; + for (const name of Object.keys(settings) as (keyof MenuSettings)[]) { + const variable = pool.lookup(name); + if (variable) { + if (variable.getValue() !== settings[name]) { + variable.setValue(settings[name] as string | boolean); + const item = (variable as any).items[0]; + if (item) { + item.executeCallbacks_(); + } } + } else if (Object.hasOwn(this.settings, name)) { + (this.settings as any)[name] = settings[name]; } - } else if (Object.hasOwn(this.settings, name)) { - (this.settings as any)[name] = settings[name]; } - } - Menu.loading--; + }); this.rerender(STATE.COMPILED); } @@ -1474,18 +1589,6 @@ export class Menu { } } - /** - * @param {string} text The text to be displayed in an Info box - * @returns {string} The text with HTML specials being escaped - */ - protected formatSource(text: string): string { - return text - .trim() - .replace(/&/g, '&') - .replace(//g, '>'); - } - /** * @param {HTMLMATHITEM} math The MathItem to serialize as MathML * @returns {string} The serialized version of the internal MathML @@ -1503,12 +1606,11 @@ export class Menu { * @param {HTMLMATHITEM} math The MathItem to serialize as SVG * @returns {Promise} A promise returning the serialized SVG */ - protected toSVG(math: HTMLMATHITEM): Promise { + protected async toSVG(math: HTMLMATHITEM): Promise { const jax = this.jax.SVG; - if (!jax) - return Promise.resolve( - "SVG can't be produced.
Try switching to SVG output first." - ); + if (!jax) { + return "SVG can't be produced.
Try switching to SVG output first."; + } const adaptor = jax.adaptor; const cache = jax.options.fontCache; const breaks = !!math.root.getProperty('process-breaks'); @@ -1519,9 +1621,7 @@ export class Menu { ) { for (const child of adaptor.childNodes(math.typesetRoot)) { if (adaptor.kind(child) === 'svg') { - return Promise.resolve( - this.formatSvg(adaptor.serializeXML(child as HTMLElement)) - ); + return this.formatSvg(adaptor.serializeXML(child as HTMLElement)); } } } @@ -1551,14 +1651,12 @@ export class Menu { jax.unmarkInlineBreaks(math.root); math.root.setProperty('inlineMarked', false); } - const promise = mathjax.handleRetriesFor(() => { + await mathjax.handleRetriesFor(() => { jax.toDOM(math, div, jax.document); }); - return promise.then(() => { - math.root = root; - jax.options.fontCache = cache; - return this.formatSvg(jax.adaptor.innerHTML(div)); - }); + math.root = root; + jax.options.fontCache = cache; + return this.formatSvg(jax.adaptor.serializeXML(div)); } /** @@ -1566,18 +1664,47 @@ export class Menu { * @returns {string} The adjusted SVG string */ protected formatSvg(svg: string): string { + // + // Insert the minimal CSS styles + // const css = (this.constructor as typeof Menu).SvgCss; svg = svg.match(/^/) ? svg.replace(//, ``) : svg.replace(/^()/, `$1`); + // + // Use black as default color + // svg = svg .replace(/ (?:role|focusable)=".*?"/g, '') .replace(/"currentColor"/g, '"black"'); + // + // Add newlines and indentation + // + const SVG = svg.split(/(<\/?[a-zA-Z].*?>)/); + for (let i = 2, spaces = ''; i < SVG.length; i += 2) { + const prev = SVG[i - 1]; + const next = SVG[i + 1]; + if (prev.charAt(1) !== '/' && prev.charAt(prev.length - 2) !== '/') { + spaces += ' '; + } + if (next) { + if (next.charAt(1) === '/') spaces = spaces.slice(2); + SVG[i + 1] = next.replace( + ' xmlns:xlink="http://www.w3.org/1999/xlink"', + '' + ); + } + if (SVG[i]) { + SVG[i] = '\n ' + spaces + SVG[i].replace(/\n/g, '\n ' + spaces); + } + SVG[i] += '\n' + spaces; + } + svg = SVG.join(''); + // + // Remove unwanted attributes + // if (!this.settings.showSRE) { - svg = svg.replace( - / (?:data-semantic-.*?|data-speech-node|role|aria-(?:level|posinset|setsize|owns))=".*?"/g, - '' - ); + svg = svg.replace(/ (?:data-semantic-.*?|data-speech-node)=".*?"/g, ''); } if (!this.settings.showTex) { svg = svg.replace(/ data-latex(?:-item)?=".*?"/g, ''); @@ -1590,39 +1717,21 @@ export class Menu { ) .replace(/ data-mml-node="TeXAtom"/g, ''); } + // + // Return the result + // return `${XMLDECLARATION}\n${svg}`; } - /** - * Get the SVG image and post it - */ - public postSvgImage() { - this.postInfo(this.svgImage); - this.toSVG(this.menu.mathItem).then((svg) => { - const html = this.svgImage.html.querySelector('#svg-image'); - html.innerHTML = this.formatSource(svg).replace(/\n/g, '
'); - }); - } - /*======================================================================*/ /** * @param {MouseEvent|null} event The event triggering the zoom (or null for from a menu pick) * @param {string} type The type of event occurring (click, dblclick) - * @param {HTMLMATHITEM} math The MathItem triggering the event */ - protected zoom(event: MouseEvent, type: string, math: HTMLMATHITEM) { + protected zoom(event: MouseEvent, type: string) { if (!event || this.isZoomEvent(event, type)) { - this.menu.mathItem = math; - if (event) { - // - // The zoomBox.post() below assumes the menu is open, - // so if this zoom() call is from an event (not the menu), - // make sure the menu is open before posting the zoom box - // - this.menu.post(event); - } - this.postInfo(this.zoomBox); + this.zoomBox(); } } @@ -1649,9 +1758,8 @@ export class Menu { */ protected rerender(start: number = STATE.TYPESET) { this.rerenderStart = Math.min(start, this.rerenderStart); - const startup = MathJax.startup; - if (!Menu.loading && startup.hasTypeset) { - startup.document.whenReady(async () => { + if (!Menu.loading && MathJax.startup.hasTypeset) { + this.document.whenReady(async () => { if (this.rerenderStart <= STATE.COMPILED) { this.document.reset({ inputJax: [] }); } @@ -1661,6 +1769,17 @@ export class Menu { } } + protected noRerender(exec: () => void) { + Menu.loading++; // pretend we're loading, to suppress rerendering durring exec() call + try { + exec(); + Menu.loading--; + } catch (err) { + Menu.loading--; // make sure this resets if there is an error + throw err; + } + } + /** * Copy the serialzied MathML to the clipboard */ @@ -1716,6 +1835,9 @@ export class Menu { math.typesetRoot.tabIndex = this.settings.inTabOrder ? 0 : -1; } + /** + * @param {HTMLMATHITEM} math The math item to which listeners are to be attached + */ public addEvents(math: HTMLMATHITEM) { const node = math.typesetRoot; node.addEventListener( @@ -1739,14 +1861,10 @@ export class Menu { true ); node.addEventListener('keydown', () => (this.menu.mathItem = math), true); - node.addEventListener( - 'click', - (event) => this.zoom(event, 'Click', math), - true - ); + node.addEventListener('click', (event) => this.zoom(event, 'Click'), true); node.addEventListener( 'dblclick', - (event) => this.zoom(event, 'DoubleClick', math), + (event) => this.zoom(event, 'DoubleClick'), true ); } diff --git a/ts/ui/menu/MenuHandler.ts b/ts/ui/menu/MenuHandler.ts index bf5498d93..edf9bd9c1 100644 --- a/ts/ui/menu/MenuHandler.ts +++ b/ts/ui/menu/MenuHandler.ts @@ -86,8 +86,11 @@ newState('CONTEXT_MENU', 170); /** * The new function for MathItem that adds the context menu */ -export interface MenuMathItem - extends ComplexityMathItem { +export interface MenuMathItem extends ComplexityMathItem< + HTMLElement, + Text, + Document +> { /** * @param {MenuMathDocument} document The document where the menu is being added * @param {boolean} force True if menu should be added even if enableMenu is false @@ -151,7 +154,8 @@ export function MenuMathItemMixin( * The properties needed in the MathDocument for context menus */ export interface MenuMathDocument - extends ComplexityMathDocument, + extends + ComplexityMathDocument, SpeechMathDocument { /** * The menu associated with this document @@ -201,6 +205,7 @@ export function MenuMathDocumentMixin( enableSpeech: true, enableBraille: true, enableExplorer: true, + enableExplorerHelp: true, enrichSpeech: 'none', enrichError: (_doc: MenuMathDocument, _math: MenuMathItem, err: Error) => console.warn('Enrichment Error:', err), diff --git a/ts/ui/menu/SelectableInfo.ts b/ts/ui/menu/SelectableInfo.ts deleted file mode 100644 index 34a4127e1..000000000 --- a/ts/ui/menu/SelectableInfo.ts +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************* - * - * Copyright (c) 2019-2025 The MathJax Consortium - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file An info box that allows text selection and has copy-to-clipboard functions - * - * @author dpvc@mathjax.org (Davide Cervone) - */ - -import { Info, HtmlClasses } from './mj-context-menu.js'; - -/*==========================================================================*/ - -/** - * The SelectableInfo class definition - */ -export class SelectableInfo extends Info { - /** - * Handle "select all" so that only the info-box's text is selected - * (not the whole page) - * - * @override - */ - public keydown(event: KeyboardEvent) { - if (event.key === 'a' && (event.ctrlKey || event.metaKey)) { - this.selectAll(); - this.stop(event); - return; - } - super.keydown(event); - } - - /** - * Select all the main text of the info box - */ - public selectAll() { - const selection = document.getSelection(); - selection.selectAllChildren( - this.html.querySelector('.CtxtMenu_InfoContent').firstChild - ); - } - - /** - * Implement the copy-to-clipboard action - */ - public copyToClipboard() { - this.selectAll(); - try { - document.execCommand('copy'); - } catch (err) { - alert(`Can't copy to clipboard: ${err.message}`); - } - document.getSelection().removeAllRanges(); - } - - /** - * Attach the copy-to-clipboard action to its button - */ - public generateHtml() { - super.generateHtml(); - const footer = this.html.querySelector( - 'span.' + HtmlClasses['INFOSIGNATURE'] - ); - const button = footer.appendChild(document.createElement('input')); - button.type = 'button'; - button.value = 'Copy to Clipboard'; - button.addEventListener('click', (_event: MouseEvent) => - this.copyToClipboard() - ); - } -} diff --git a/ts/ui/menu/mj-context-menu.ts b/ts/ui/menu/mj-context-menu.ts index 7ddaf2b89..0a69005d5 100644 --- a/ts/ui/menu/mj-context-menu.ts +++ b/ts/ui/menu/mj-context-menu.ts @@ -25,7 +25,6 @@ export { ContextMenu } from '#menu/context_menu.js'; export { SubMenu } from '#menu/sub_menu.js'; export { Submenu } from '#menu/item_submenu.js'; -export { Info } from '#menu/info.js'; export { Radio } from '#menu/item_radio.js'; export { Rule } from '#menu/item_rule.js'; export { ParserFactory } from '#menu/parser_factory.js'; diff --git a/ts/util/StyleJson.ts b/ts/util/StyleJson.ts index a27de84fa..c37e9e365 100644 --- a/ts/util/StyleJson.ts +++ b/ts/util/StyleJson.ts @@ -32,7 +32,7 @@ export type StyleJsonData = { * A list of selectors and their data (basically a stylesheet) */ export type StyleJson = { - [selector: string]: StyleJsonData; + [selector: string]: StyleJsonData | StyleJson; }; /******************************************************************************/ @@ -97,33 +97,49 @@ export class StyleJsonSheet { } /** - * @returns {string[]} An array of rule strings for the style list + * @param {StyleJson} styles The style list to convert + * @param {string} spaces The spaces to put at the beginning of each line + * @returns {string[]} An array of rule strings for the style list */ - public getStyleRules(): string[] { - const selectors = Object.keys(this.styles); + public getStyleRules( + styles: StyleJson = this.styles, + spaces: string = '' + ): string[] { + const selectors = Object.keys(styles); const defs: string[] = new Array(selectors.length); let i = 0; for (const selector of selectors) { + const data = styles[selector]; defs[i++] = - selector + - ' {\n' + - this.getStyleDefString(this.styles[selector]) + - '\n}'; + `${spaces}${selector} {\n${this.getStyleDefString(data, spaces)}\n${spaces}}`; } return defs; } /** - * @param {StyleJsonData} styles The style data to be stringified - * @returns {string} The CSS string for the given data + * @param {StyleJsonData | StyleJson} styles The style data to be stringified + * @param {string} spaces The spaces to put at the beginning of each line + * @returns {string} The CSS string for the given data */ - public getStyleDefString(styles: StyleJsonData): string { + public getStyleDefString( + styles: StyleJsonData | StyleJson, + spaces: string + ): string { const properties = Object.keys(styles); const values: string[] = new Array(properties.length); let i = 0; for (const property of properties) { - values[i++] = ' ' + property + ': ' + styles[property] + ';'; + values[i++] = + styles[property] instanceof Object + ? spaces + + this.getStyleRules( + { + [property]: styles[property], + } as StyleJson, + spaces + ' ' + ).join('\n' + spaces) + : ' ' + spaces + property + ': ' + styles[property] + ';'; } - return values.join('\n'); + return values.join('\n' + spaces); } } diff --git a/ts/util/Styles.ts b/ts/util/Styles.ts index 63042926b..72576c58b 100644 --- a/ts/util/Styles.ts +++ b/ts/util/Styles.ts @@ -56,7 +56,7 @@ export const WSC = ['width', 'style', 'color']; * @returns {string[]} Array of parts of the style (separated by spaces) */ function splitSpaces(text: string): string[] { - const parts = text.split(/((?:'[^']*'|"[^"]*"|,[\s\n]|[^\s\n])*)/g); + const parts = text.split(/((?:'[^'\n]*'|"[^"\n]*"|,[\s\n]|[^\s\n])*)/g); const split = [] as string[]; while (parts.length > 1) { parts.shift(); @@ -365,6 +365,12 @@ export class Styles { combine: combineTRBL, }, + margin: { + children: TRBL, + split: splitTRBL, + combine: combineTRBL, + }, + border: { children: TRBL, split: splitSame, diff --git a/ts/util/asyncLoad/esm.ts b/ts/util/asyncLoad/esm.ts index 2a66708e0..ae805e290 100644 --- a/ts/util/asyncLoad/esm.ts +++ b/ts/util/asyncLoad/esm.ts @@ -22,15 +22,15 @@ */ import { mathjax } from '../../mathjax.js'; +import { context } from '../context.js'; -let root = new URL(import.meta.url).href.replace( - /\/util\/asyncLoad\/esm.js$/, - '/' -); +let root = context + .path(new URL(import.meta.url, 'file://').href) + .replace(/\/util\/asyncLoad\/esm.js$/, '/'); if (!mathjax.asyncLoad) { mathjax.asyncLoad = async (name: string) => { - const file = name.charAt(0) === '.' ? new URL(name, root).pathname : name; + const file = name.charAt(0) === '.' ? new URL(name, root).href : name; return import(file).then((result) => result.default ?? result); }; } @@ -39,7 +39,7 @@ if (!mathjax.asyncLoad) { * @param {string} url the base URL to use for loading relative paths */ export function setBaseURL(url: string) { - root = new URL(url, 'file://').href; + root = new URL(context.path(url), 'file://').href; if (!root.match(/\/$/)) { root += '/'; } diff --git a/ts/util/asyncLoad/node-import.cjs b/ts/util/asyncLoad/node-import.cjs index 6759ea124..b5ed1d464 100644 --- a/ts/util/asyncLoad/node-import.cjs +++ b/ts/util/asyncLoad/node-import.cjs @@ -23,9 +23,9 @@ const { mathjax } = require('../../mathjax.js'); const path = require('path'); -const { src } = require('#source/source.cjs'); +const { dirname } = require('#source/source.cjs'); -let root = path.resolve(src, '..', '..', 'cjs'); +let root = path.resolve(dirname, '..', '..', 'cjs'); if (!mathjax.asyncLoad) { mathjax.asyncLoad = async (name) => { diff --git a/ts/util/asyncLoad/node.ts b/ts/util/asyncLoad/node.ts index 5189a7e11..5ba06e0c7 100644 --- a/ts/util/asyncLoad/node.ts +++ b/ts/util/asyncLoad/node.ts @@ -23,11 +23,11 @@ import { mathjax } from '../../mathjax.js'; import * as path from 'path'; -import { src } from '#source/source.cjs'; +import { dirname } from '#source/source.cjs'; declare const require: (name: string) => any; -let root = path.resolve(src, '..', '..', 'cjs'); +let root = path.resolve(dirname, '..', '..', 'cjs'); if (!mathjax.asyncLoad && typeof require !== 'undefined') { mathjax.asyncLoad = (name: string) => { diff --git a/ts/util/asyncLoad/system.ts b/ts/util/asyncLoad/system.ts index 1d0bcabb1..42b227b9a 100644 --- a/ts/util/asyncLoad/system.ts +++ b/ts/util/asyncLoad/system.ts @@ -22,11 +22,12 @@ */ import { mathjax } from '../../mathjax.js'; +import { context } from '../context.js'; declare const System: { import: (name: string, url?: string) => any }; declare const __dirname: string; -let root = 'file://' + __dirname.replace(/\/[^/]*\/[^/]*$/, '/'); +let root = 'file://' + context.path(__dirname).replace(/\/[^/]*\/[^/]*$/, '/'); if (!mathjax.asyncLoad && typeof System !== 'undefined' && System.import) { mathjax.asyncLoad = (name: string) => { @@ -41,7 +42,7 @@ if (!mathjax.asyncLoad && typeof System !== 'undefined' && System.import) { * @param {string} url the base URL to use for loading relative paths */ export function setBaseURL(url: string) { - root = new URL(url, 'file://').href; + root = new URL(context.path(url), 'file://').href; if (!root.match(/\/$/)) { root += '/'; } diff --git a/ts/util/context.ts b/ts/util/context.ts index 568a7c6fa..4f5b216b3 100644 --- a/ts/util/context.ts +++ b/ts/util/context.ts @@ -21,6 +21,8 @@ * @author dpvc@mathjax.org (Davide Cervone) */ +declare const process: { platform: string }; + /** * True if there is a window object */ @@ -49,7 +51,30 @@ export const context = { if (window.navigator.userAgent.includes('Android')) { return 'Unix'; } + } else if (typeof process !== 'undefined') { + return ( + { + linux: 'Unix', + android: 'Unix', + aix: 'Unix', + freebsd: 'Unix', + netbsd: 'Unix', + openbsd: 'Unix', + sunos: 'Unix', + darwin: 'MacOS', + win32: 'Windows', + cygwin: 'Windows', + }[process.platform] || process.platform + ); } return 'unknown'; })(), + path: (file: string) => file, }; + +if (context.os === 'Windows') { + context.path = (file: string) => + file.match(/^[/\\]?[a-zA-Z]:[/\\]/) + ? file.replace(/\\/g, '/').replace(/^\//, '') + : file; +}