diff --git a/components/bin/build b/components/bin/build index 3589a33ff0..28507e1c21 100755 --- a/components/bin/build +++ b/components/bin/build @@ -309,6 +309,7 @@ function processGlobal() { ); makeDir(''); fs.writeFileSync(path.join(LIB, COMPONENT + '.js'), lines.join('\n') + '\n'); + fs.writeFileSync(path.join(LIB, COMPONENT + '.d.ts'), 'export {};\n'); } /** diff --git a/components/bin/pack b/components/bin/pack index a4b27fbdab..fa872a30af 100755 --- a/components/bin/pack +++ b/components/bin/pack @@ -167,6 +167,14 @@ async function webpackLib(dir) { .concat(list.filter(a => a.slice(2, 4) !== './').sort()) .join('\n') ); + if (!config.noDTS) { + const dts = config.dist + ? path.resolve(dir, config.dist, path.basename(config.name) + '.d.ts') + : path.resolve(jsPath, '..', bundle, config.name + '.d.ts'); + if (!fs.existsSync(dts)) { + fs.writeFileSync(dts, `export {}`); + } + } } catch (err) { console.error(err); } diff --git a/components/json.d.cjs b/components/json.d.cjs new file mode 100644 index 0000000000..a75b5fa503 --- /dev/null +++ b/components/json.d.cjs @@ -0,0 +1,2 @@ +export function json(file: string): any; +export function require(file: string): any; diff --git a/components/json.d.cts b/components/json.d.cts new file mode 100644 index 0000000000..a75b5fa503 --- /dev/null +++ b/components/json.d.cts @@ -0,0 +1,2 @@ +export function json(file: string): any; +export function require(file: string): any; diff --git a/components/mjs/a11y/assistive-mml/assistive-mml.d.ts b/components/mjs/a11y/assistive-mml/assistive-mml.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/assistive-mml/assistive-mml.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/a11y/complexity/complexity.d.ts b/components/mjs/a11y/complexity/complexity.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/complexity/complexity.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/a11y/explorer/explorer.d.ts b/components/mjs/a11y/explorer/explorer.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/explorer/explorer.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/a11y/semantic-enrich/semantic-enrich.d.ts b/components/mjs/a11y/semantic-enrich/semantic-enrich.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/semantic-enrich/semantic-enrich.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/a11y/speech/speech.d.ts b/components/mjs/a11y/speech/speech.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/speech/speech.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/a11y/sre/sre.d.ts b/components/mjs/a11y/sre/sre.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/sre/sre.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/a11y/sre/worker/speech-worker.d.ts b/components/mjs/a11y/sre/worker/speech-worker.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/sre/worker/speech-worker.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/a11y/util.d.ts b/components/mjs/a11y/util.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/a11y/util.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/adaptors/jsdom/jsdom.d.ts b/components/mjs/adaptors/jsdom/jsdom.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/adaptors/jsdom/jsdom.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/adaptors/linkedom/linkedom.d.ts b/components/mjs/adaptors/linkedom/linkedom.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/adaptors/linkedom/linkedom.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/adaptors/liteDOM/liteDOM.d.ts b/components/mjs/adaptors/liteDOM/liteDOM.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/adaptors/liteDOM/liteDOM.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/core/core.d.ts b/components/mjs/core/core.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/core/core.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/core/locale.d.ts b/components/mjs/core/locale.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/core/locale.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/dependencies.d.ts b/components/mjs/dependencies.d.ts new file mode 100644 index 0000000000..4ff3f0ad1f --- /dev/null +++ b/components/mjs/dependencies.d.ts @@ -0,0 +1,12 @@ +export const dependencies: { + [component: string]: string[]; +}; +export const paths: { + [path: string]: string; +}; +export const provides: { + [component: string]: string[]; +}; +export const compatibility: { + [component: string]: string; +}; diff --git a/components/mjs/input/asciimath/asciimath.d.ts b/components/mjs/input/asciimath/asciimath.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/asciimath/asciimath.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/mml/entities/entities.d.ts b/components/mjs/input/mml/entities/entities.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/mml/entities/entities.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/mml/extensions/mml3/mml3.d.ts b/components/mjs/input/mml/extensions/mml3/mml3.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/mml/extensions/mml3/mml3.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/mml/init.d.ts b/components/mjs/input/mml/init.d.ts new file mode 100644 index 0000000000..c5fc4fce44 --- /dev/null +++ b/components/mjs/input/mml/init.d.ts @@ -0,0 +1,2 @@ +import {MathML} from '@mathjax/src/mjs/input/mathml.js'; +export {MathML}; diff --git a/components/mjs/input/mml/mml.d.ts b/components/mjs/input/mml/mml.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/mml/mml.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex-base/tex-base.d.ts b/components/mjs/input/tex-base/tex-base.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex-base/tex-base.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extension.d.ts b/components/mjs/input/tex/extension.d.ts new file mode 100644 index 0000000000..e6181654a6 --- /dev/null +++ b/components/mjs/input/tex/extension.d.ts @@ -0,0 +1 @@ +export function fontExtension(id: string, name: string, pkg?: string): void; diff --git a/components/mjs/input/tex/extensions/action/action.d.ts b/components/mjs/input/tex/extensions/action/action.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/action/action.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/ams/ams.d.ts b/components/mjs/input/tex/extensions/ams/ams.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/ams/ams.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/amscd/amscd.d.ts b/components/mjs/input/tex/extensions/amscd/amscd.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/amscd/amscd.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/autoload/autoload.d.ts b/components/mjs/input/tex/extensions/autoload/autoload.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/autoload/autoload.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/bbm/bbm.d.ts b/components/mjs/input/tex/extensions/bbm/bbm.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/bbm/bbm.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/bboldx/bboldx.d.ts b/components/mjs/input/tex/extensions/bboldx/bboldx.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/bboldx/bboldx.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/bbox/bbox.d.ts b/components/mjs/input/tex/extensions/bbox/bbox.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/bbox/bbox.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/begingroup/begingroup.d.ts b/components/mjs/input/tex/extensions/begingroup/begingroup.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/begingroup/begingroup.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/boldsymbol/boldsymbol.d.ts b/components/mjs/input/tex/extensions/boldsymbol/boldsymbol.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/boldsymbol/boldsymbol.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/braket/braket.d.ts b/components/mjs/input/tex/extensions/braket/braket.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/braket/braket.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/bussproofs/bussproofs.d.ts b/components/mjs/input/tex/extensions/bussproofs/bussproofs.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/bussproofs/bussproofs.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/cancel/cancel.d.ts b/components/mjs/input/tex/extensions/cancel/cancel.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/cancel/cancel.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/cases/cases.d.ts b/components/mjs/input/tex/extensions/cases/cases.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/cases/cases.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/centernot/centernot.d.ts b/components/mjs/input/tex/extensions/centernot/centernot.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/centernot/centernot.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/color/color.d.ts b/components/mjs/input/tex/extensions/color/color.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/color/color.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/colortbl/colortbl.d.ts b/components/mjs/input/tex/extensions/colortbl/colortbl.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/colortbl/colortbl.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/colorv2/colorv2.d.ts b/components/mjs/input/tex/extensions/colorv2/colorv2.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/colorv2/colorv2.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/configmacros/configmacros.d.ts b/components/mjs/input/tex/extensions/configmacros/configmacros.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/configmacros/configmacros.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/dsfont/dsfont.d.ts b/components/mjs/input/tex/extensions/dsfont/dsfont.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/dsfont/dsfont.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/empheq/empheq.d.ts b/components/mjs/input/tex/extensions/empheq/empheq.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/empheq/empheq.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/enclose/enclose.d.ts b/components/mjs/input/tex/extensions/enclose/enclose.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/enclose/enclose.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/extpfeil/extpfeil.d.ts b/components/mjs/input/tex/extensions/extpfeil/extpfeil.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/extpfeil/extpfeil.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/fontsizev3/fontsizev3.d.ts b/components/mjs/input/tex/extensions/fontsizev3/fontsizev3.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/fontsizev3/fontsizev3.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/gensymb/gensymb.d.ts b/components/mjs/input/tex/extensions/gensymb/gensymb.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/gensymb/gensymb.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/html/html.d.ts b/components/mjs/input/tex/extensions/html/html.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/html/html.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/mathtools/mathtools.d.ts b/components/mjs/input/tex/extensions/mathtools/mathtools.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/mathtools/mathtools.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/mhchem/mhchem.d.ts b/components/mjs/input/tex/extensions/mhchem/mhchem.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/mhchem/mhchem.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/newcommand/newcommand.d.ts b/components/mjs/input/tex/extensions/newcommand/newcommand.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/newcommand/newcommand.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/noerrors/noerrors.d.ts b/components/mjs/input/tex/extensions/noerrors/noerrors.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/noerrors/noerrors.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/noundefined/noundefined.d.ts b/components/mjs/input/tex/extensions/noundefined/noundefined.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/noundefined/noundefined.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/physics/physics.d.ts b/components/mjs/input/tex/extensions/physics/physics.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/physics/physics.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/require/require.d.ts b/components/mjs/input/tex/extensions/require/require.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/require/require.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/setoptions/setoptions.d.ts b/components/mjs/input/tex/extensions/setoptions/setoptions.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/setoptions/setoptions.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/tagformat/tagformat.d.ts b/components/mjs/input/tex/extensions/tagformat/tagformat.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/tagformat/tagformat.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/texhtml/texhtml.d.ts b/components/mjs/input/tex/extensions/texhtml/texhtml.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/texhtml/texhtml.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/textcomp/textcomp.d.ts b/components/mjs/input/tex/extensions/textcomp/textcomp.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/textcomp/textcomp.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/textmacros/textmacros.d.ts b/components/mjs/input/tex/extensions/textmacros/textmacros.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/textmacros/textmacros.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/unicode/unicode.d.ts b/components/mjs/input/tex/extensions/unicode/unicode.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/unicode/unicode.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/units/units.d.ts b/components/mjs/input/tex/extensions/units/units.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/units/units.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/upgreek/upgreek.d.ts b/components/mjs/input/tex/extensions/upgreek/upgreek.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/upgreek/upgreek.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/extensions/verb/verb.d.ts b/components/mjs/input/tex/extensions/verb/verb.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/extensions/verb/verb.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/input/tex/register.d.ts b/components/mjs/input/tex/register.d.ts new file mode 100644 index 0000000000..0155219532 --- /dev/null +++ b/components/mjs/input/tex/register.d.ts @@ -0,0 +1 @@ +export function registerTex(packageList?: string[], tex?: boolean): void; diff --git a/components/mjs/input/tex/tex.d.ts b/components/mjs/input/tex/tex.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/input/tex/tex.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/loader/loader.d.ts b/components/mjs/loader/loader.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/loader/loader.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/mml-chtml-nofont/mml-chtml-nofont.d.ts b/components/mjs/mml-chtml-nofont/mml-chtml-nofont.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/mml-chtml-nofont/mml-chtml-nofont.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/mml-chtml/mml-chtml.d.ts b/components/mjs/mml-chtml/mml-chtml.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/mml-chtml/mml-chtml.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/mml-svg-nofont/mml-svg-nofont.d.ts b/components/mjs/mml-svg-nofont/mml-svg-nofont.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/mml-svg-nofont/mml-svg-nofont.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/mml-svg/mml-svg.d.ts b/components/mjs/mml-svg/mml-svg.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/mml-svg/mml-svg.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/node-main/config.json b/components/mjs/node-main/config.json index e5c5cea388..3bd87da548 100644 --- a/components/mjs/node-main/config.json +++ b/components/mjs/node-main/config.json @@ -5,10 +5,13 @@ "copy": [ "node-main.mjs", "node-main.cjs", - "node-main-setup.cjs" + "node-main-setup.cjs", + "node-main.d.mts", + "node-main.d.cts" ] }, "webpack": { - "name": "node-main" + "name": "node-main", + "noDTS": true } } diff --git a/components/mjs/node-main/node-main.d.cts b/components/mjs/node-main/node-main.d.cts new file mode 100644 index 0000000000..364865e3f8 --- /dev/null +++ b/components/mjs/node-main/node-main.d.cts @@ -0,0 +1,7 @@ +import { EMPTY } from '@mathjax/src/mjs/types/Types.js'; +import { COMPONENT_LIST } from '@mathjax/src/mjs/types/mjx.js'; +import { LITE_DOM, MATHJAX_CONFIG, MATHJAX_OBJECT } from '@mathjax/src/mjs/types/dom/lite.js'; + +export function init = EMPTY>( + config: MATHJAX_CONFIG +): Promise>; diff --git a/components/mjs/node-main/node-main.d.mts b/components/mjs/node-main/node-main.d.mts new file mode 100644 index 0000000000..364865e3f8 --- /dev/null +++ b/components/mjs/node-main/node-main.d.mts @@ -0,0 +1,7 @@ +import { EMPTY } from '@mathjax/src/mjs/types/Types.js'; +import { COMPONENT_LIST } from '@mathjax/src/mjs/types/mjx.js'; +import { LITE_DOM, MATHJAX_CONFIG, MATHJAX_OBJECT } from '@mathjax/src/mjs/types/dom/lite.js'; + +export function init = EMPTY>( + config: MATHJAX_CONFIG +): Promise>; diff --git a/components/mjs/output/chtml/chtml.d.ts b/components/mjs/output/chtml/chtml.d.ts new file mode 100644 index 0000000000..5e3f86290d --- /dev/null +++ b/components/mjs/output/chtml/chtml.d.ts @@ -0,0 +1 @@ +export function loadFont(startup: (ready: () => void) => Promise, preload: boolean): Promise; diff --git a/components/mjs/output/chtml/chtml.js b/components/mjs/output/chtml/chtml.js index db898f0174..a2a54e8b54 100644 --- a/components/mjs/output/chtml/chtml.js +++ b/components/mjs/output/chtml/chtml.js @@ -9,4 +9,3 @@ OutputUtil.config('chtml', CHTML, fontName, DefaultFont); export function loadFont(startup, preload) { return OutputUtil.loadFont(startup, 'chtml', fontName, preload); } - diff --git a/components/mjs/output/svg/svg.d.ts b/components/mjs/output/svg/svg.d.ts new file mode 100644 index 0000000000..5e3f86290d --- /dev/null +++ b/components/mjs/output/svg/svg.d.ts @@ -0,0 +1 @@ +export function loadFont(startup: (ready: () => void) => Promise, preload: boolean): Promise; diff --git a/components/mjs/output/svg/svg.js b/components/mjs/output/svg/svg.js index 3c07780ad1..6f91e30ccc 100644 --- a/components/mjs/output/svg/svg.js +++ b/components/mjs/output/svg/svg.js @@ -9,4 +9,3 @@ OutputUtil.config('svg', SVG, fontName, DefaultFont); export function loadFont(startup, preload) { return OutputUtil.loadFont(startup, 'svg', fontName, preload); } - diff --git a/components/mjs/output/util.d.ts b/components/mjs/output/util.d.ts new file mode 100644 index 0000000000..11012bffcf --- /dev/null +++ b/components/mjs/output/util.d.ts @@ -0,0 +1,35 @@ +import { OutputJax } from '@mathjax/src/mjs/core/OutputJax.js'; +import { Font } from '@mathjax/src/mjs/output/common/FontData.js'; + +export function configFont( + font: string, + jax: string, + config: { fontPath?: string }, + extension?: string, +): string; + +export function configExtensions( + jax: string, + config: { + fontExtensions?: string[]; + fontPath?: string; + }, +): string[]; + +export const OutputUtil: { + + config( + jax: string, + jaxClass: OutputJax, + defaultFont: Font, + fontClass: any, + ): void, + + loadFont( + startup: (ready: () => void) => Promise, + jax: string, + font: string, + preloaded?: boolean, + ): Promise; + +}; diff --git a/components/mjs/require/config.json b/components/mjs/require/config.json index bbe1ea8c05..92ac452858 100644 --- a/components/mjs/require/config.json +++ b/components/mjs/require/config.json @@ -4,7 +4,9 @@ "from": "../..", "copy": [ "require.mjs", - "json.cjs" + "require.d.mts", + "json.cjs", + "json.d.cts" ] } } diff --git a/components/mjs/source.d.ts b/components/mjs/source.d.ts new file mode 100644 index 0000000000..07d6d894d0 --- /dev/null +++ b/components/mjs/source.d.ts @@ -0,0 +1,3 @@ +export const source: { + [component: string]: string; +} diff --git a/components/mjs/startup/hasown.d.ts b/components/mjs/startup/hasown.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/startup/hasown.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/startup/init.d.ts b/components/mjs/startup/init.d.ts new file mode 100644 index 0000000000..cad7a25876 --- /dev/null +++ b/components/mjs/startup/init.d.ts @@ -0,0 +1 @@ +export function startup(ready: () => Promise): Promise; diff --git a/components/mjs/startup/startup.d.ts b/components/mjs/startup/startup.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/startup/startup.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-chtml-nofont/tex-chtml-nofont.d.ts b/components/mjs/tex-chtml-nofont/tex-chtml-nofont.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-chtml-nofont/tex-chtml-nofont.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-chtml/tex-chtml.d.ts b/components/mjs/tex-chtml/tex-chtml.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-chtml/tex-chtml.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-mml-chtml-nofont/tex-mml-chtml-nofont.d.ts b/components/mjs/tex-mml-chtml-nofont/tex-mml-chtml-nofont.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-mml-chtml-nofont/tex-mml-chtml-nofont.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-mml-chtml/tex-mml-chtml.d.ts b/components/mjs/tex-mml-chtml/tex-mml-chtml.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-mml-chtml/tex-mml-chtml.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-mml-svg-nofont/tex-mml-svg-nofont.d.ts b/components/mjs/tex-mml-svg-nofont/tex-mml-svg-nofont.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-mml-svg-nofont/tex-mml-svg-nofont.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-mml-svg/tex-mml-svg.d.ts b/components/mjs/tex-mml-svg/tex-mml-svg.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-mml-svg/tex-mml-svg.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-svg-nofont/tex-svg-nofont.d.ts b/components/mjs/tex-svg-nofont/tex-svg-nofont.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-svg-nofont/tex-svg-nofont.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/tex-svg/tex-svg.d.ts b/components/mjs/tex-svg/tex-svg.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/tex-svg/tex-svg.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/ui/lazy/lazy.d.ts b/components/mjs/ui/lazy/lazy.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/ui/lazy/lazy.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/ui/menu/menu.d.ts b/components/mjs/ui/menu/menu.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/ui/menu/menu.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/ui/no-dark-mode/no-dark-mode.d.ts b/components/mjs/ui/no-dark-mode/no-dark-mode.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/ui/no-dark-mode/no-dark-mode.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/components/mjs/ui/safe/safe.d.ts b/components/mjs/ui/safe/safe.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/components/mjs/ui/safe/safe.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/testsuite/src/setupTex.ts b/testsuite/src/setupTex.ts index 6af7e0fe92..f88459e6c7 100644 --- a/testsuite/src/setupTex.ts +++ b/testsuite/src/setupTex.ts @@ -122,7 +122,7 @@ export function setupTex( Locale.setLocale(); const html = new HTMLDocument('', adaptor, { InputJax: tex }); convert = (expr: string, display: boolean) => - toMathML(html.convert(expr, { display: display, end: STATE.CONVERT })); + toMathML(html.convert(expr, { display: display, end: STATE.CONVERT }) as MmlNode); } /** @@ -235,7 +235,7 @@ export function setupTexWithOutput( const visitor = new SerializedMmlVisitor(); const toMathML = (node: MmlNode) => visitor.visitTree(node); convert = (expr: string, display: boolean) => - toMathML(html.convert(expr, { display: display, end: STATE.CONVERT })); + toMathML(html.convert(expr, { display: display, end: STATE.CONVERT }) as MmlNode); } /*********************************************************************/ diff --git a/ts/a11y/assistive-mml.ts b/ts/a11y/assistive-mml.ts index 87f877eb97..a361da18bd 100644 --- a/ts/a11y/assistive-mml.ts +++ b/ts/a11y/assistive-mml.ts @@ -26,17 +26,15 @@ import { MathDocument, AbstractMathDocument, MathDocumentConstructor, + RenderActions, + DOCUMENT_OPTIONS, } from '../core/MathDocument.js'; -import { - MathItem, - AbstractMathItem, - STATE, - newState, -} from '../core/MathItem.js'; +import { AbstractMathItem, STATE, newState } from '../core/MathItem.js'; import { MmlNode } from '../core/MmlTree/MmlNode.js'; import { SerializedMmlVisitor } from '../core/MmlTree/SerializedMmlVisitor.js'; -import { OptionList, expandable } from '../util/Options.js'; +import { expandable } from '../util/Options.js'; import { StyleJson } from '../util/StyleJson.js'; +import { DOM, DOM_TYPES, N, T, D, Constructor } from '../types/Types.js'; /*==========================================================================*/ @@ -52,11 +50,6 @@ export class LimitedMmlVisitor extends SerializedMmlVisitor { } } -/** - * Generic constructor for Mixins - */ -export type Constructor = new (...args: any[]) => T; - /*==========================================================================*/ /** @@ -71,7 +64,11 @@ newState('ASSISTIVEMML', 153); * @template T The Text node class * @template D The Document class */ -export interface AssistiveMmlMathItem extends MathItem { +export interface AssistiveMmlMathItem extends AbstractMathItem< + N, + T, + D +> { /** * @param {MathDocument} document The document where assistive MathML is being added * @param {boolean} force True to force assistive MathML even if enableAssistiveMml is false @@ -149,6 +146,30 @@ export function AssistiveMmlMathItemMixin< /*==========================================================================*/ +/** + * The assistive-mml option types. + */ +export type OPTIONS = { + enableAssistiveMml: boolean; +}; + +/** + * The AssistiveMmlDocument option types. + */ +export interface ASSISTIVEMML_OPTIONS + extends OPTIONS, DOCUMENT_OPTIONS { + MathItem: Constructor, T, D>>; +} + +/** + * The assistive-mml option defaults. + */ +const options: OPTIONS = { + enableAssistiveMml: true, +}; + +/*==========================================================================*/ + /** * The functions added to MathDocument for assistive MathML * @@ -161,6 +182,11 @@ export interface AssistiveMmlMathDocument extends AbstractMathDocument< T, D > { + /** + * @override + */ + options: ASSISTIVEMML_OPTIONS>; + /** * @param {MmlNode} node The node to be serializes * @returns {string} The serialization of the node @@ -172,7 +198,7 @@ export interface AssistiveMmlMathDocument extends AbstractMathDocument< * * @returns {AssistiveMmlMathDocument} The MathDocument (so calls can be chained) */ - assistiveMml(): AssistiveMmlMathDocument; + assistiveMml(): this; } /** @@ -190,23 +216,32 @@ export function AssistiveMmlMathDocumentMixin< N, T, D, - B extends MathDocumentConstructor>, + B extends MathDocumentConstructor< + AbstractMathDocument, + DOM + >, >( BaseDocument: B -): MathDocumentConstructor> & B { +): MathDocumentConstructor, DOM> & + B { return class BaseClass extends BaseDocument { /** * @override */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...BaseDocument.OPTIONS, - enableAssistiveMml: true, + ...options, renderActions: expandable({ ...BaseDocument.OPTIONS.renderActions, assistiveMml: [STATE.ASSISTIVEMML], - }), + }) as RenderActions, }; + /** + * @override + */ + options: ASSISTIVEMML_OPTIONS>; + /** * styles needed for the hidden MathML */ @@ -283,7 +318,7 @@ export function AssistiveMmlMathDocumentMixin< * * @returns {AssistiveMmlMathDocument} The assistive mml document. */ - public assistiveMml(): AssistiveMmlMathDocument { + public assistiveMml(): this { if (!this.processed.isSet('assistive-mml')) { for (const math of this.math) { (math as AssistiveMmlMathItem).assistiveMml(this); @@ -325,7 +360,7 @@ export function AssistiveMmlHandler( N, T, D, - MathDocumentConstructor> + MathDocumentConstructor, DOM> >(handler.documentClass); return handler; } diff --git a/ts/a11y/complexity.ts b/ts/a11y/complexity.ts index 534b230309..0e2ebeafa9 100644 --- a/ts/a11y/complexity.ts +++ b/ts/a11y/complexity.ts @@ -23,25 +23,21 @@ */ import { Handler } from '../core/Handler.js'; -import { MathDocumentConstructor } from '../core/MathDocument.js'; +import { + MathDocumentConstructor, + RenderActions, +} from '../core/MathDocument.js'; import { STATE, newState } from '../core/MathItem.js'; import { MathML } from '../input/mathml.js'; import { EnrichHandler, EnrichedMathItem, EnrichedMathDocument, + ENRICH_OPTIONS, } from './semantic-enrich.js'; import { ComplexityVisitor } from './complexity/visitor.js'; -import { - OptionList, - selectOptionsFromKeys, - expandable, -} from '../util/Options.js'; - -/** - * Generic constructor for Mixins - */ -export type Constructor = new (...args: any[]) => T; +import { selectOptionsFromKeys, expandable } from '../util/Options.js'; +import { DOM, DOM_TYPES, N, T, D, Constructor } from '../types/Types.js'; /** * Shorthands for constructors @@ -49,7 +45,8 @@ export type Constructor = new (...args: any[]) => T; export type EMItemC = Constructor>; export type CMItemC = Constructor>; export type EMDocC = MathDocumentConstructor< - EnrichedMathDocument + EnrichedMathDocument, + DOM >; export type CMDocC = Constructor>; @@ -122,6 +119,32 @@ export function ComplexityMathItemMixin>( /*==========================================================================*/ +/** + * The copmlexity option types. + */ +export type OPTIONS = { + enableComplexity: boolean; + ComplexityVisitor: typeof ComplexityVisitor; +}; + +/** + * The ComplexityMathDocument option types. + */ +export interface COMPLEXITY_OPTIONS + extends OPTIONS, ENRICH_OPTIONS { + MathItem: Constructor, T, D>>; +} + +/** + * The complexity option defaults. + */ +const options: OPTIONS = { + enableComplexity: true, + ComplexityVisitor: ComplexityVisitor, +}; + +/*==========================================================================*/ + /** * The functions added to MathDocument for complexity * @@ -134,12 +157,17 @@ export interface ComplexityMathDocument extends EnrichedMathDocument< T, D > { + /** + * @override + */ + options: COMPLEXITY_OPTIONS>; + /** * Perform complexity computations on the MathItems in the MathDocument * * @returns {ComplexityMathDocument} The MathDocument (so calls can be chained) */ - complexity(): ComplexityMathDocument; + complexity(): this; } /** @@ -160,17 +188,21 @@ export function ComplexityMathDocumentMixin>( /** * The options for this type of document */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...BaseDocument.OPTIONS, ...ComplexityVisitor.OPTIONS, - enableComplexity: true, - ComplexityVisitor: ComplexityVisitor, - renderActions: expandable({ + ...options, + renderActions: expandable>({ ...BaseDocument.OPTIONS.renderActions, complexity: [STATE.COMPLEXITY], }), }; + /** + * @override + */ + public options: COMPLEXITY_OPTIONS>; + /** * The visitor that computes complexities */ @@ -217,7 +249,7 @@ export function ComplexityMathDocumentMixin>( * * @returns {ComplexityMathDocument} The object for chaining. */ - public complexity(): ComplexityMathDocument { + public complexity(): this { if (!this.processed.isSet('complexity')) { if (this.options.enableComplexity) { for (const math of this.math) { diff --git a/ts/a11y/complexity/visitor.ts b/ts/a11y/complexity/visitor.ts index 823501d81a..fbcb3a3507 100644 --- a/ts/a11y/complexity/visitor.ts +++ b/ts/a11y/complexity/visitor.ts @@ -43,6 +43,26 @@ import type { SemanticMap } from '../speech/StructureUtil.js'; /*==========================================================================*/ +/** + * The complexity visitor option types. + */ +export interface COMPLEXITY_VISITOR_OPTIONS { + identifyCollapsible: boolean; // mark elements that should be collapsed + makeCollapsible: boolean; // insert maction to allow collapsing + Collapse: typeof Collapse; // the Collapse class to use +} + +/** + * The complexity visitor option defaults. + */ +const options: COMPLEXITY_VISITOR_OPTIONS = { + identifyCollapsible: true, + makeCollapsible: true, + Collapse: Collapse, +}; + +/*==========================================================================*/ + /** * A visitor pattern that computes complexities within the MathML tree */ @@ -50,18 +70,13 @@ export class ComplexityVisitor extends MmlVisitor { /** * The options for handling collapsing */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { - identifyCollapsible: true, // mark elements that should be collapsed - makeCollapsible: true, // insert maction to allow collapsing - Collapse: Collapse // the Collapse class to use - }; + public static OPTIONS = options; /** * Values used to compute complexities */ /* prettier-ignore */ - public complexity: {[name: string]: number} = { + public complexity = { text: .5, // each character of a token element adds this to complexity token: .5, // each token element gets this additional complexity child: 1, // child nodes add this to their parent node's complexity @@ -91,7 +106,7 @@ export class ComplexityVisitor extends MmlVisitor { /** * The options for this visitor */ - public options: OptionList; + public options: COMPLEXITY_VISITOR_OPTIONS; /** * @override @@ -99,7 +114,10 @@ export class ComplexityVisitor extends MmlVisitor { constructor(factory: MmlFactory, options: OptionList) { super(factory); const CLASS = this.constructor as typeof ComplexityVisitor; - this.options = userOptions(defaultOptions({}, CLASS.OPTIONS), options); + this.options = userOptions( + defaultOptions({}, CLASS.OPTIONS), + options + ) as COMPLEXITY_VISITOR_OPTIONS; this.collapse = new this.options.Collapse(this); this.factory = factory; } diff --git a/ts/a11y/explorer.ts b/ts/a11y/explorer.ts index 3078528af7..668e1cf89c 100644 --- a/ts/a11y/explorer.ts +++ b/ts/a11y/explorer.ts @@ -24,12 +24,24 @@ import { Handler } from '../core/Handler.js'; import { MathML } from '../input/mathml.js'; import { STATE, newState } from '../core/MathItem.js'; -import { SpeechMathItem, SpeechMathDocument, SpeechHandler } from './speech.js'; -import { MathDocumentConstructor } from '../core/MathDocument.js'; -import { OptionList, expandable } from '../util/Options.js'; +import { SRE_OPTIONS, ENRICH_OPTIONS } from './semantic-enrich.js'; +import { + SpeechMathItem, + SpeechMathDocument, + SpeechHandler, + A11Y_OPTIONS as SPEECH_A11Y_OPTIONS, + SPEECH_OPTIONS, +} from './speech.js'; +import { + MathDocumentConstructor, + RenderActions, +} from '../core/MathDocument.js'; +import { expandable, OptionList } from '../util/Options.js'; import { hasWindow } from '../util/context.js'; import { StyleJson } from '../util/StyleJson.js'; import { context } from '../util/context.js'; +import { Constructor } from '../types/Types.js'; +import { HTML_DOM } from '../types/dom/html.js'; import { SEM } from './semantic-enrich/strings.js'; import { SAVED_HREF } from './explorer/strings.js'; @@ -40,11 +52,6 @@ import * as Aria from './aria/__locales__/Component.js'; const isUnix = context.os === 'Unix'; -/** - * Generic constructor for Mixins - */ -export type Constructor = new (...args: any[]) => T; - /** * Shorthands for types with HTMLElement, Text, and Document instead of generics */ @@ -97,10 +104,10 @@ export interface ExplorerMathItem extends HTMLMATHITEM { refocus: string; /** - * @param {HTMLDocument} document The document where the Explorer is being added - * @param {boolean} force True to force the explorer even if enableExplorer is false + * @param {ExplorerMathDocument} document The document where the Explorer is being added + * @param {boolean} force True to force the explorer even if enableExplorer is false */ - explorable(document: HTMLDOCUMENT, force?: boolean): void; + explorable(document: ExplorerMathDocument, force?: boolean): void; /** * @param {ExplorerMathDocument} document The explorer document being used @@ -309,10 +316,127 @@ export function ExplorerMathItemMixin>( }; } +/** + * The pre-defined colors. + */ +export type COLOR = + 'Black' | 'White' | 'Blue' | 'Red' | 'Green' | 'Yellow' | 'Cyan' | 'Magenta'; + +/** + * The explorer's A11Y option types. + */ +export type A11Y_EXPLORER_OPTIONS = { + align: 'top' | 'bottom' | 'center'; // placement of magnified expression + backgroundColor: COLOR; // color for background of selected sub-expression + backgroundOpacity: number; // opacity for background of selected sub-expression + flame: boolean; // color collapsible sub-expressions + foregroundColor: COLOR; // color to use for text of selected sub-expression + foregroundOpacity: number; // opacity for text of selected sub-expression + highlight: 'None' | 'Hover' | 'Flame'; // type of highlighting for collapsible sub-expressions + hover: boolean; // show collapsible sub-expression on mouse hovering + infoPrefix: boolean; // show speech prefixes on mouse hovering + infoRole: boolean; // show semantic role on mouse hovering + infoType: boolean; // show semantic type on mouse hovering + keyMagnifier: boolean; // switch on magnification via key exploration + magnification: 'None' | 'Keyboard' | 'Mouse'; // type of magnification + magnify: string; // percentage of magnification of zoomed expressions + mouseMagnifier: boolean; // switch on magnification via mouse hovering + subtitles: boolean; // show speech as a subtitle + treeColoring: boolean; // tree color expression + viewBraille: boolean; // display Braille output as subtitles + voicing: boolean; // switch on/off speech output + brailleSpeech: boolean; // use aria-label for Braille + brailleCombine: boolean; // combine Braille with speech output + speechRules: string; // the speech domain and style string + help: boolean; // include "press h for help" messages on focus + roleDescription: string; // the role description to use for math expressions + inTabOrder: boolean; // true if expression gets tabindex = 0 + tabSelects: 'all' | 'last'; // 'all' for whole expression, 'last' for last explored node +}; + +/** + * The explorer's A11Y option defaults. + */ +const a11y_options: A11Y_EXPLORER_OPTIONS = { + align: 'top', + backgroundColor: 'Blue', + backgroundOpacity: 20, + flame: false, + foregroundColor: 'Black', + foregroundOpacity: 100, + highlight: 'None', + hover: false, + infoPrefix: false, + infoRole: false, + infoType: false, + keyMagnifier: false, + magnification: 'None', + magnify: '400%', + mouseMagnifier: false, + subtitles: false, + treeColoring: false, + viewBraille: false, + voicing: false, + brailleSpeech: false, + brailleCombine: false, + speechRules: 'clearspeak-default', + help: true, + roleDescription: 'math', + inTabOrder: true, + tabSelects: 'all', +}; + +/** + * The explorer document's A11Y option types. + */ +export type A11Y_OPTIONS = { + a11y: A11Y_EXPLORER_OPTIONS & SPEECH_A11Y_OPTIONS['a11y']; +}; + +/** + * The explorer option types. + */ +export type OPTIONS = { + enableExplorer: boolean; + enableExplorerHelp: boolean; + keepRegions: boolean; // debugging option to not remove explorer regions +}; + +/** + * The ExplorerMathDocument option types. + */ +export interface EXPLORER_OPTIONS + extends + OPTIONS, + A11Y_OPTIONS, + Omit, 'a11y'>, + ENRICH_OPTIONS { + MathItem: Constructor & { + ariaRole: string; + roleDescription: string; + none: string; + brailleNone: string; + }; +} + +/** + * The explorer option defaults. + */ +const options: OPTIONS = { + enableExplorer: hasWindow, // only activate in interactive contexts + enableExplorerHelp: true, // help dialog is enabled + keepRegions: false, // don't debug +}; + /** * The functions added to MathDocument for the Explorer */ export interface ExplorerMathDocument extends HTMLDOCUMENT { + /** + * @override + */ + options: EXPLORER_OPTIONS; + /** * The info icon for the selected expression */ @@ -336,9 +460,9 @@ export interface ExplorerMathDocument extends HTMLDOCUMENT { /** * Add the Explorer to the MathItems in the MathDocument * - * @returns {HTMLDocument} The MathDocument (so calls can be chained) + * @returns {ExplorerMathDocument} The MathDocument (so calls can be chained) */ - explorable(): HTMLDOCUMENT; + explorable(): this; } /** @@ -350,55 +474,37 @@ export interface ExplorerMathDocument extends HTMLDOCUMENT { * @template B The MathItem class to extend */ export function ExplorerMathDocumentMixin< - B extends MathDocumentConstructor, ->(BaseDocument: B): MathDocumentConstructor & B { + B extends MathDocumentConstructor, +>( + BaseDocument: B +): MathDocumentConstructor & B { return class BaseClass extends BaseDocument { /** * @override */ /* prettier-ignore */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...BaseDocument.OPTIONS, - enableExplorer: hasWindow, // only activate in interactive contexts - enableExplorerHelp: true, // help dialog is enabled - renderActions: expandable({ + ...options, + renderActions: expandable>({ ...BaseDocument.OPTIONS.renderActions, explorable: [STATE.EXPLORER] }), - sre: expandable({ - ...BaseDocument.OPTIONS.sre, + sre: expandable({ + ...(BaseDocument.OPTIONS as SPEECH_OPTIONS).sre, speech: 'none', // None as speech is explicitly computed }), - a11y: { - ...BaseDocument.OPTIONS.a11y, - align: 'top', // placement of magnified expression - backgroundColor: 'Blue', // color for background of selected sub-expression - backgroundOpacity: 20, // opacity for background of selected sub-expression - flame: false, // color collapsible sub-expressions - foregroundColor: 'Black', // color to use for text of selected sub-expression - foregroundOpacity: 100, // opacity for text of selected sub-expression - highlight: 'None', // type of highlighting for collapsible sub-expressions - hover: false, // show collapsible sub-expression on mouse hovering - infoPrefix: false, // show speech prefixes on mouse hovering - infoRole: false, // show semantic role on mouse hovering - infoType: false, // show semantic type on mouse hovering - keyMagnifier: false, // switch on magnification via key exploration - magnification: 'None', // type of magnification - magnify: '400%', // percentage of magnification of zoomed expressions - mouseMagnifier: false, // switch on magnification via mouse hovering - subtitles: false, // show speech as a subtitle - 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 - inTabOrder: true, // true if expressin get tabindex = 0 - tabSelects: 'all', // 'all' for whole expression, 'last' for last explored node - } + a11y: expandable({ + ...(BaseDocument.OPTIONS as SPEECH_OPTIONS).a11y, + ...a11y_options, + }), }; + /** + * @override + */ + public options: EXPLORER_OPTIONS; + /** * Styles to add for speech */ @@ -530,7 +636,7 @@ export function ExplorerMathDocumentMixin< const mathItem = (options.MathItem = ExplorerMathItemMixin( options.MathItem )); - mathItem.roleDescription = options.roleDescription; + mathItem.roleDescription = options.a11y.roleDescription; this.explorerRegions = new RegionPool(this); if ('addStyles' in this) { (this as any).addStyles( @@ -573,7 +679,7 @@ export function ExplorerMathDocumentMixin< * * @returns {ExplorerMathDocument} The MathDocument (so calls can be chained) */ - public explorable(): ExplorerMathDocument { + public explorable(): this { if (!this.processed.isSet('explorer')) { if (this.options.enableExplorer) { for (const math of this.math) { @@ -638,21 +744,21 @@ export function ExplorerHandler( /** * Sets a list of a11y options for a given document. * - * @param {HTMLDOCUMENT} document The current document. - * @param {{[key: string]: any}} options Association list for a11y option value pairs. + * @param {ExplorerMathDocument} document The current document. + * @param {OptionList} options Association list for a11y option value pairs. */ export function setA11yOptions( - document: HTMLDOCUMENT, - options: { [key: string]: any } + document: ExplorerMathDocument, + options: OptionList ) { // TODO (volker): This needs to be replace by the engine feature vector. // Minus rule sets etc. Breaking change in SRE. - const sreOptions = Sre.engineSetup() as { [name: string]: string }; + const sreOptions = Sre.engineSetup(); for (const key in options) { - if (document.options.a11y[key] !== undefined) { + if (Object.hasOwn(document.options.a11y, key)) { setA11yOption(document, key, options[key]); - } else if (sreOptions[key] !== undefined) { - document.options.sre[key] = options[key]; + } else if (Object.hasOwn(sreOptions, key)) { + (document.options.sre as any)[key] = options[key]; } } if (options.roleDescription) { @@ -667,20 +773,20 @@ export function setA11yOptions( /** * Sets a single a11y option for a menu name. * - * @param {HTMLDOCUMENT} document The current document. + * @param {ExplorerMathDocument} document The current document. * @param {string} option The option name in the menu. - * @param {string|boolean} value The new value. + * @param {string|number|boolean} value The new value. */ export function setA11yOption( - document: HTMLDOCUMENT, + document: ExplorerMathDocument, option: string, - value: string | boolean + value: string | number | boolean ) { switch (option) { case 'speechRules': { const [domain, style] = (value as string).split('-'); - document.options.sre.domain = domain; - document.options.sre.style = style; + document.options.sre.domain = domain as any; + document.options.sre.style = style as any; break; } case 'magnification': @@ -722,9 +828,9 @@ export function setA11yOption( } break; case 'locale': - document.options.sre.locale = value; + document.options.sre.locale = value as string; break; default: - document.options.a11y[option] = value; + (document.options.a11y as any)[option] = value; } } diff --git a/ts/a11y/explorer/Explorer.ts b/ts/a11y/explorer/Explorer.ts index 0e25614016..eeebe450fe 100644 --- a/ts/a11y/explorer/Explorer.ts +++ b/ts/a11y/explorer/Explorer.ts @@ -21,7 +21,8 @@ * @author v.sorge@mathjax.org (Volker Sorge) */ -import { A11yDocument, Region } from './Region.js'; +import type { ExplorerMathDocument } from '../explorer.js'; +import { Region } from './Region.js'; import { Highlighter } from './Highlighter.js'; import { HILITE } from './strings.js'; @@ -154,7 +155,7 @@ export class AbstractExplorer implements Explorer { /** * Creator pattern for explorers. * - * @param {A11yDocument} document The current document. + * @param {ExplorerMathDocument} document The current document. * @param {ExplorerPool} pool The explorer pool. * @param {Region} region A region to display results. * @param {HTMLElement} node The node on which the explorer works. @@ -164,7 +165,7 @@ export class AbstractExplorer implements Explorer { * @template T */ public static create( - document: A11yDocument, + document: ExplorerMathDocument, pool: ExplorerPool, region: Region, node: HTMLElement, @@ -176,14 +177,14 @@ export class AbstractExplorer implements Explorer { /** * @class - * @param {A11yDocument} document The current document. + * @param {ExplorerMathDocument} document The current document. * @param {ExplorerPool} pool The explorer pool. * @param {Region} region A region to display results. * @param {HTMLElement} node The node on which the explorer works. * @param {any[]} _rest Remaining information. */ protected constructor( - public document: A11yDocument, + public document: ExplorerMathDocument, public pool: ExplorerPool, public region: Region, protected node: HTMLElement, diff --git a/ts/a11y/explorer/ExplorerPool.ts b/ts/a11y/explorer/ExplorerPool.ts index 05bb145492..a220c275a6 100644 --- a/ts/a11y/explorer/ExplorerPool.ts +++ b/ts/a11y/explorer/ExplorerPool.ts @@ -236,16 +236,14 @@ export class ExplorerPool { public attach() { this.attached = []; const keyExplorers = []; - const a11y = this.document.options.a11y; + const a11y = this.document.options.a11y as { + [name: string]: string | number | boolean; + }; for (const [key, explorer] of Object.entries(this.explorers)) { if (explorer instanceof SpeechExplorer) { explorer.stoppable = false; keyExplorers.unshift(explorer); - if ( - this.speechExplorerKeys.some( - (exKey) => this.document.options.a11y[exKey] - ) - ) { + if (this.speechExplorerKeys.some((exKey) => a11y[exKey])) { explorer.Attach(); this.attached.push(key); } else { diff --git a/ts/a11y/explorer/MouseExplorer.ts b/ts/a11y/explorer/MouseExplorer.ts index 14a664a173..9642eb055f 100644 --- a/ts/a11y/explorer/MouseExplorer.ts +++ b/ts/a11y/explorer/MouseExplorer.ts @@ -21,8 +21,8 @@ * @author v.sorge@mathjax.org (Volker Sorge) */ +import type { ExplorerMathDocument } from '../explorer.js'; import { - A11yDocument, DummyRegion, Region, HoverRegion, @@ -118,7 +118,7 @@ export abstract class Hoverer extends AbstractMouseExplorer { * @class * @augments {AbstractMouseExplorer} * - * @param {A11yDocument} document The current document. + * @param {ExplorerMathDocument} document The current document. * @param {ExplorerPool} pool The explorer pool. * @param {Region} region A region to display results. * @param {HTMLElement} node The node on which the explorer works. @@ -129,7 +129,7 @@ export abstract class Hoverer extends AbstractMouseExplorer { * that is passed to the region. */ protected constructor( - public document: A11yDocument, + public document: ExplorerMathDocument, public pool: ExplorerPool, public region: Region, protected node: HTMLElement, @@ -218,7 +218,7 @@ export class ValueHoverer extends Hoverer { * @override */ protected constructor( - document: A11yDocument, + document: ExplorerMathDocument, pool: ExplorerPool, region: ToolTip, node: HTMLElement, @@ -248,7 +248,7 @@ export class ContentHoverer extends Hoverer { * @override */ protected constructor( - document: A11yDocument, + document: ExplorerMathDocument, pool: ExplorerPool, region: HoverRegion, node: HTMLElement, @@ -277,7 +277,7 @@ export class FlameHoverer extends Hoverer { * @override */ protected constructor( - document: A11yDocument, + document: ExplorerMathDocument, pool: ExplorerPool, _ignore: any, node: HTMLElement, diff --git a/ts/a11y/explorer/Region.ts b/ts/a11y/explorer/Region.ts index fd3edab627..ae2d2c7594 100644 --- a/ts/a11y/explorer/Region.ts +++ b/ts/a11y/explorer/Region.ts @@ -21,16 +21,15 @@ * @author v.sorge@mathjax.org (Volker Sorge) */ -import { MathDocument } from '../../core/MathDocument.js'; +//import { MathDocument } from '../../core/MathDocument.js'; import { StyleJsonSheet } from '../../util/StyleJson.js'; import { Highlighter } from './Highlighter.js'; import { SsmlElement, buildSpeech } from '../speech/SpeechUtil.js'; +import type { ExplorerMathDocument } from '../explorer.ts'; import { SEM } from '../semantic-enrich/strings.js'; import { MACTION } from '../semantic-enrich/maction.js'; import { HILITE, MAG } from './strings.js'; -export type A11yDocument = MathDocument; - export interface Region { /** * Adds a style sheet for the live region to the document. @@ -105,9 +104,9 @@ export abstract class AbstractRegion implements Region { /** * @class - * @param {A11yDocument} document The document the live region is added to. + * @param {ExplorerMathDocument} document The document the live region is added to. */ - constructor(public document: A11yDocument) { + constructor(public document: ExplorerMathDocument) { this.CLASS = this.constructor as typeof AbstractRegion; this.AddStyles(); } @@ -863,7 +862,6 @@ export class HoverRegion extends AbstractRegion { mjx: HTMLElement, container: Element ) { - let [x, y] = [0, 0]; let top, bot, left, right; const g = container.querySelector('g').cloneNode(false); for (const child of enclosed) { @@ -871,7 +869,7 @@ export class HoverRegion extends AbstractRegion { if (rect?.getAttribute(HILITE.ADDED)) { const bbox = rect.getBBox(); const [X, Y] = this.xy(rect); - [x, y] = [X, Y + bbox.y]; + const [x, y] = [X, Y + bbox.y]; if (left === undefined || x < left) left = x; if (right === undefined || x + bbox.width > right) { right = x + bbox.width; diff --git a/ts/a11y/explorer/TreeExplorer.ts b/ts/a11y/explorer/TreeExplorer.ts index e8b1a4b4f4..0a93d2c35a 100644 --- a/ts/a11y/explorer/TreeExplorer.ts +++ b/ts/a11y/explorer/TreeExplorer.ts @@ -22,7 +22,8 @@ * @author v.sorge@mathjax.org (Volker Sorge) */ -import { A11yDocument, Region } from './Region.js'; +import type { ExplorerMathDocument } from '../explorer.js'; +import { Region } from './Region.js'; import { AbstractExplorer } from './Explorer.js'; import { ExplorerPool } from './ExplorerPool.js'; import { SEM } from '../semantic-enrich/strings.js'; @@ -32,7 +33,7 @@ export class AbstractTreeExplorer extends AbstractExplorer { * @override */ protected constructor( - public document: A11yDocument, + public document: ExplorerMathDocument, public pool: ExplorerPool, public region: Region, protected node: HTMLElement diff --git a/ts/a11y/semantic-enrich.ts b/ts/a11y/semantic-enrich.ts index 0dc34b2547..82a9d93f45 100644 --- a/ts/a11y/semantic-enrich.ts +++ b/ts/a11y/semantic-enrich.ts @@ -26,6 +26,8 @@ import { MathDocument, AbstractMathDocument, MathDocumentConstructor, + RenderActions, + DOCUMENT_OPTIONS, } from '../core/MathDocument.js'; import { MathItem, @@ -37,7 +39,7 @@ import { MmlNode } from '../core/MmlTree/MmlNode.js'; import { HtmlNode } from '../core/MmlTree/MmlNodes/HtmlNode.js'; import { MathML } from '../input/mathml.js'; import { SerializedMmlVisitor } from '../core/MmlTree/SerializedMmlVisitor.js'; -import { OptionList, expandable } from '../util/Options.js'; +import { expandable } from '../util/Options.js'; import * as Sre from './sre.js'; import { SEM } from './semantic-enrich/strings.js'; import { SPEECH } from './speech/strings.js'; @@ -45,13 +47,7 @@ import { MACTION } from './semantic-enrich/maction.js'; import { StructureUtil, SemanticMap } from './speech/StructureUtil.js'; import { Locale } from '../util/Locale.js'; import { COMPONENT } from './semantic-enrich/__locales__/Component.js'; - -/*==========================================================================*/ - -/** - * Generic constructor for Mixins - */ -export type Constructor = new (...args: any[]) => T; +import { DOM, DOM_TYPES, N, T, D, Constructor } from '../types/Types.js'; /*==========================================================================*/ @@ -112,7 +108,7 @@ export class enrichVisitor extends SerializedMmlVisitor { * @template T The Text node class * @template D The Document class */ -export interface EnrichedMathItem extends MathItem { +export interface EnrichedMathItem extends AbstractMathItem { /** * Maps semantic ids to extra nodes outside the DOM subtree. */ @@ -216,10 +212,13 @@ export function EnrichedMathItemMixin< } /** - * @param {MathDocument} document The MathDocument for the MathItem - * @param {boolean} force True to force the enrichment even if not enabled + * @param {EnrichedMathDocument} document The MathDocument for the MathItem + * @param {boolean} force True to force the enrichment even if not enabled */ - public enrich(document: MathDocument, force: boolean = false) { + public enrich( + document: EnrichedMathDocument, + force: boolean = false + ) { if (this.state() >= STATE.ENRICHED) return; if (!this.isEscaped && (document.options.enableEnrichment || force)) { this.semanticNodes = null; @@ -232,7 +231,7 @@ export function EnrichedMathItemMixin< mml = this.adjustSelections(); } if (!document.processed.isSet('enriched')) { - Sre.setupEngine(document.options.sre); + Sre.setupEngine(document.options.sre as any); } const enriched = Sre.toEnriched(mml); this.inputData.enrichedMml = math.math = this.serializeMml(enriched); @@ -302,6 +301,65 @@ export function EnrichedMathItemMixin< /*==========================================================================*/ +/** + * The SRE option types. + */ +export interface SRE_OPTIONS { + speech: 'none' | 'shallow' | 'deep'; // The level os speech to include + locale: string; // switch the locale + domain: 'clearspeak' | 'mathspeak'; // speech rules domain + style: 'default' | 'brief' | 'sbrief'; // speech rules style + braille: 'nemeth' | 'ueb' | 'euro'; // TODO: Dummy switch for braille + structure: boolean; // Generates full aria structure + aria: boolean; // Generates ARIA attributes + rate?: string; // The speaking rate +} + +/** + * The SRE option defaults. + */ +const SRE_options: SRE_OPTIONS = { + speech: 'none', + locale: Locale.default, + domain: 'clearspeak', + style: 'default', + braille: 'nemeth', + structure: true, + aria: true, +}; + +/** + * The semantic-enrich option types. + */ +export interface OPTIONS { + enableEnrichment: boolean; + enrichError: ( + doc: EnrichedMathDocument, T, D>, + math: EnrichedMathItem, T, D>, + err: Error + ) => void; + sre: SRE_OPTIONS; +} + +/** + * The semantic0enrich option defaults. + */ +export interface ENRICH_OPTIONS + extends OPTIONS, DOCUMENT_OPTIONS { + MathItem: Constructor, T, D>>; +} + +/** + * The default options. + */ +const options: OPTIONS = { + enableEnrichment: true, + enrichError: (doc, math, err) => doc.enrichError(doc, math, err), + sre: expandable(SRE_options) as SRE_OPTIONS, +}; + +/*==========================================================================*/ + /** * The functions added to MathDocument for enrichment * @@ -314,12 +372,17 @@ export interface EnrichedMathDocument extends AbstractMathDocument< T, D > { + /** + * @override + */ + options: ENRICH_OPTIONS>; + /** * Perform enrichment on the MathItems in the MathDocument * * @returns {EnrichedMathDocument} The MathDocument (so calls can be chained) */ - enrich(): EnrichedMathDocument; + enrich(): this; /** * @param {EnrichedMathDocument} doc The MathDocument for the error @@ -349,39 +412,32 @@ export function EnrichedMathDocumentMixin< N, T, D, - B extends MathDocumentConstructor>, + B extends MathDocumentConstructor< + AbstractMathDocument, + DOM + >, >( BaseDocument: B, MmlJax: MathML -): MathDocumentConstructor> & B { +): MathDocumentConstructor, DOM> & B { return class extends BaseDocument { /** * @override */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...BaseDocument.OPTIONS, - enableEnrichment: true, - enrichError: ( - doc: EnrichedMathDocument, - math: EnrichedMathItem, - err: Error - ) => doc.enrichError(doc, math, err), - renderActions: expandable({ + ...options, + renderActions: expandable>({ ...BaseDocument.OPTIONS.renderActions, enrich: [STATE.ENRICHED], }), - /* prettier-ignore */ - sre: expandable({ - speech: 'none', // by default no speech is included - locale: Locale.default, // switch the locale - domain: 'clearspeak', // speech rules domain - style: 'default', // speech rules style - braille: 'nemeth', // TODO: Dummy switch for braille - structure: true, // Generates full aria structure - aria: true, - }), }; + /** + * @override + */ + public options: ENRICH_OPTIONS>; + /** * Enrich the MathItem class used for this MathDocument, and create the * temporary MathItem used for enrichment @@ -413,10 +469,10 @@ export function EnrichedMathDocumentMixin< * * @returns {EnrichedMathDocument} The object for chaining. */ - public enrich(): EnrichedMathDocument { + public enrich(): this { if (!this.processed.isSet('enriched')) { if (this.options.enableEnrichment) { - Sre.setupEngine(this.options.sre); + Sre.setupEngine(this.options.sre as any); for (const math of this.math) { (math as EnrichedMathItem).enrich(this); } @@ -477,7 +533,7 @@ export function EnrichHandler( N, T, D, - MathDocumentConstructor> + MathDocumentConstructor, DOM> >(handler.documentClass, MmlJax); return handler; } diff --git a/ts/a11y/speech.ts b/ts/a11y/speech.ts index 6ad9ba6045..2103ee9a04 100644 --- a/ts/a11y/speech.ts +++ b/ts/a11y/speech.ts @@ -22,26 +22,25 @@ */ import { Handler } from '../core/Handler.js'; -import { MathDocument, MathDocumentConstructor } from '../core/MathDocument.js'; +import { + MathDocument, + MathDocumentConstructor, + RenderActions, +} from '../core/MathDocument.js'; import { EnrichedMathItem, EnrichedMathDocument, EnrichHandler, + ENRICH_OPTIONS, } from './semantic-enrich.js'; import { STATE, newState } from '../core/MathItem.js'; import { MathML } from '../input/mathml.js'; -import { OptionList, expandable } from '../util/Options.js'; +import { expandable } from '../util/Options.js'; import { GeneratorPool } from './speech/GeneratorPool.js'; import { WorkerHandler } from './speech/WebWorker.js'; import { sreRoot } from '#root/sre-root.js'; import { localize } from './speech/__locales__/Component.js'; - -/*==========================================================================*/ - -/** - * Generic constructor for Mixins - */ -export type Constructor = new (...args: any[]) => T; +import { DOM, DOM_TYPES, N, T, D, Constructor } from '../types/Types.js'; /*==========================================================================*/ @@ -163,6 +162,65 @@ export function SpeechMathItemMixin< /*==========================================================================*/ +/** + * The speech A11Y option types. + */ +export type A11Y_SPEECH_OPTIONS = { + speech: boolean; + braille: boolean; +}; + +/** + * The speech option types. + */ +export type OPTIONS = { + enableSpeech: boolean; + enableBraille: boolean; + speechError: ( + doc: SpeechMathDocument, T, D>, + math: SpeechMathItem, T, D>, + err: string + ) => void; + worker: { + path: string; + maps: string; + worker: string; + debug: boolean; + }; +}; + +/** + * The A11Y option object. + */ +export type A11Y_OPTIONS = { + a11y: A11Y_SPEECH_OPTIONS; +}; + +/** + * The SpeechMathDocument option types. + */ +export interface SPEECH_OPTIONS + extends OPTIONS, A11Y_OPTIONS, ENRICH_OPTIONS { + MathItem: Constructor, T, D>>; +} + +/** + * The speech option defaults. + */ +const options: OPTIONS = { + enableSpeech: true, + enableBraille: true, + speechError: (doc, math, err) => doc.speechError(doc, math, err), + worker: { + path: sreRoot(), + maps: sreRoot().replace(/[cm]js\/a11y\/sre$/, 'bundle/sre/mathmaps'), + worker: 'speech-worker.js', + debug: false, + }, +}; + +/*==========================================================================*/ + /** * The functions added to MathDocument for enrichment * @@ -175,6 +233,11 @@ export interface SpeechMathDocument extends EnrichedMathDocument< T, D > { + /** + * @override + */ + options: SPEECH_OPTIONS>; + /** * The webworker handler for the document */ @@ -185,7 +248,7 @@ export interface SpeechMathDocument extends EnrichedMathDocument< * * @returns {SpeechMathDocument} The MathDocument (so calls can be chained) */ - attachSpeech(): SpeechMathDocument; + attachSpeech(): this; /** * @param {SpeechMathDocument} doc The MathDocument for the error @@ -219,39 +282,35 @@ export function SpeechMathDocumentMixin< N, T, D, - B extends MathDocumentConstructor>, + B extends MathDocumentConstructor< + EnrichedMathDocument, + DOM + >, >( EnrichedMathDocument: B -): MathDocumentConstructor> & B { +): MathDocumentConstructor, DOM> & B { return class extends EnrichedMathDocument { /** * @override */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...EnrichedMathDocument.OPTIONS, - enableSpeech: true, - enableBraille: true, - speechError: ( - doc: SpeechMathDocument, - math: SpeechMathItem, - err: string - ) => doc.speechError(doc, math, err), - renderActions: expandable({ + ...options, + renderActions: expandable>({ ...EnrichedMathDocument.OPTIONS.renderActions, attachSpeech: [STATE.ATTACHSPEECH], }), - worker: { - path: sreRoot(), - maps: sreRoot().replace(/[cm]js\/a11y\/sre$/, 'bundle/sre/mathmaps'), - worker: 'speech-worker.js', - debug: false, - }, - a11y: expandable({ - speech: true, // // switch on speech output - braille: true, // // switch on Braille output + a11y: expandable({ + speech: true, + braille: true, }), }; + /** + * @override + */ + public options: SPEECH_OPTIONS>; + /** * The webworker handler for the document */ @@ -293,7 +352,7 @@ export function SpeechMathDocumentMixin< * * @returns {SpeechMathDocument} The object for chaining. */ - public attachSpeech(): SpeechMathDocument { + public attachSpeech(): this { if (!this.processed.isSet('attach-speech')) { const options = this.options; if ( diff --git a/ts/adaptors/NodeMixin.ts b/ts/adaptors/NodeMixin.ts index 11526fb57e..dae300bda9 100644 --- a/ts/adaptors/NodeMixin.ts +++ b/ts/adaptors/NodeMixin.ts @@ -26,6 +26,8 @@ import { DOMAdaptor, minWorker } from '../core/DOMAdaptor.js'; import { userOptions, defaultOptions, OptionList } from '../util/Options.js'; import { asyncLoad } from '../util/AsyncLoad.js'; +import { Constructor } from '../types/Types.js'; +export { Constructor }; /** * A minimal worker thread interface @@ -36,25 +38,33 @@ export interface WebWorker { terminate(): void; } -/** - * A constructor for a given class - * - * @template T The class to construct - */ -export type Constructor = new (...args: any[]) => T; - /** * The type of an Adaptor class */ export type AdaptorConstructor = Constructor>; +export type MIXIN_OPTIONS = { + badCSS?: boolean; // getComputedStyles() is not implemented in the DOM + badSizes?: boolean; // element sizes (e.g., ClientWidth, etc.) are not implemented in the DOM +}; + /** * The options to the NodeMixin */ -/* prettier-ignore */ -export const NodeMixinOptions: OptionList = { - badCSS: true, // getComputedStyles() is not implemented in the DOM - badSizes: true, // element sizes (e.g., ClientWidth, etc.) are not implemented in the DOM +const NodeMixinOptions: MIXIN_OPTIONS = { + badCSS: true, // getComputedStyles() is not implemented in the DOM + badSizes: true, // element sizes (e.g., ClientWidth, etc.) are not implemented in the DOM +}; + +/** + * The adaptor option types. + */ +export type OPTIONS = { + fontSize?: number; + fontFamily?: string; + cjkCharWidth?: number; + unknownCharWidth?: number; + unknownCharHeight?: number; }; /** @@ -69,7 +79,7 @@ export const NodeMixinOptions: OptionList = { */ export function NodeMixin>( Base: A, - options: typeof NodeMixinOptions = {} + options: MIXIN_OPTIONS = {} ): A { options = userOptions(defaultOptions({}, NodeMixinOptions), options); @@ -77,17 +87,20 @@ export function NodeMixin>( /** * The default options */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { - ...(options.badCSS ? { - fontSize: 16, // We can't compute the font size, so always use this - fontFamily: 'Times', // We can't compute the font family, so always use this - } : {}), - ...(options.badSizes ? { - cjkCharWidth: 1, // Width (in em units) of full width characters - unknownCharWidth: .6, // Width (in em units) of unknown (non-full-width) characters - unknownCharHeight: .8, // Height (in em units) of unknown characters - } : {}) + public static OPTIONS = { + ...(options.badCSS + ? { + fontSize: 16, // We can't compute the font size, so always use this + fontFamily: 'Times', // We can't compute the font family, so always use this + } + : {}), + ...(options.badSizes + ? { + cjkCharWidth: 1, // Width (in em units) of full width characters + unknownCharWidth: 0.6, // Width (in em units) of unknown (non-full-width) characters + unknownCharHeight: 0.8, // Height (in em units) of unknown characters + } + : {}), }; /** @@ -97,7 +110,7 @@ export function NodeMixin>( [ '[', '\u1100-\u115F', // Hangul Jamo - '\u2329\u232A', // LEFT-POINTING ANGLE BRACKET, RIGHT-POINTING ANGLE BRACKET + '\u2329\u232A', // LEFT-POINTING ANGLE BRACKET, RIGHT-POINTING ANGLE BRACKET '\u2E80-\u303E', // CJK Radicals Supplement ... CJK Symbols and Punctuation '\u3040-\u3247', // Hiragana ... Enclosed CJK Letters and Months '\u3250-\u4DBF', // Enclosed CJK Letters and Months ... CJK Unified Ideographs Extension A @@ -124,7 +137,7 @@ export function NodeMixin>( /** * The options for the instance */ - public options: OptionList; + public options: OPTIONS; /** * @param {...any} args Parameters for the mixin class, where the first is diff --git a/ts/adaptors/jsdomAdaptor.ts b/ts/adaptors/jsdomAdaptor.ts index 6311e38240..b04525e424 100644 --- a/ts/adaptors/jsdomAdaptor.ts +++ b/ts/adaptors/jsdomAdaptor.ts @@ -22,8 +22,7 @@ */ import { HTMLAdaptor } from './HTMLAdaptor.js'; -import { NodeMixin, Constructor } from './NodeMixin.js'; -import { OptionList } from '../util/Options.js'; +import { NodeMixin, MIXIN_OPTIONS, Constructor } from './NodeMixin.js'; /** * The constructor for an HTMLAdaptor @@ -45,13 +44,13 @@ export class JsdomAdaptor extends NodeMixin< /** * Function for creating an HTML adaptor using jsdom * - * @param {any} JSDOM The jsdom object to use for this adaptor - * @param {OptionList} options The options for the adaptor - * @returns {HTMLAdaptor} The newly created adaptor + * @param {any} JSDOM The jsdom object to use for this adaptor + * @param {MIXIN_OPTIONS} options The options for the adaptor + * @returns {HTMLAdaptor} The newly created adaptor */ export function jsdomAdaptor( JSDOM: any, - options: OptionList = null + options: MIXIN_OPTIONS = null ): JsdomAdaptor { return new JsdomAdaptor(new JSDOM().window, options); } diff --git a/ts/adaptors/linkedomAdaptor.ts b/ts/adaptors/linkedomAdaptor.ts index 1854582743..75f8fda31c 100644 --- a/ts/adaptors/linkedomAdaptor.ts +++ b/ts/adaptors/linkedomAdaptor.ts @@ -22,8 +22,7 @@ */ import { HTMLAdaptor } from './HTMLAdaptor.js'; -import { NodeMixin, Constructor } from './NodeMixin.js'; -import { OptionList } from '../util/Options.js'; +import { NodeMixin, MIXIN_OPTIONS, Constructor } from './NodeMixin.js'; import { Locale } from '../util/Locale.js'; import { COMPONENT } from './linkedom/__locales__/Component.js'; @@ -68,13 +67,13 @@ export class LinkedomAdaptor extends NodeMixin< /** * Function for creating an HTML adaptor using linkedom * - * @param {any} parseHTML The linkedom HTML parser to use for this adaptor - * @param {OptionList} options The options for the adaptor - * @returns {LinkedomAdaptor} The newly created adaptor + * @param {any} parseHTML The linkedom HTML parser to use for this adaptor + * @param {MIXIN_OPTIONS} options The options for the adaptor + * @returns {LinkedomAdaptor} The newly created adaptor */ export function linkedomAdaptor( parseHTML: any, - options: OptionList = null + options: MIXIN_OPTIONS = null ): LinkedomAdaptor { const window = parseHTML(''); // diff --git a/ts/adaptors/liteAdaptor.ts b/ts/adaptors/liteAdaptor.ts index fef5120cfd..46cf5dc325 100644 --- a/ts/adaptors/liteAdaptor.ts +++ b/ts/adaptors/liteAdaptor.ts @@ -723,7 +723,7 @@ export class LiteAdaptor extends NodeMixin< * The function to call to obtain a LiteAdaptor * * @param {OptionList} options The options for the adaptor - * @returns {LiteAdaptor} The newly created adaptor + * @returns {LiteAdaptor} The newly created adaptor */ export function liteAdaptor(options: OptionList = null): LiteAdaptor { return new LiteAdaptor(null, options); diff --git a/ts/components/global.ts b/ts/components/global.ts index a10cb01738..1e2b33c768 100644 --- a/ts/components/global.ts +++ b/ts/components/global.ts @@ -66,7 +66,7 @@ export const GLOBAL = (() => { })() as any as Window & { MathJax: MathJaxObject | MathJaxConfig }; /** - * @param {any} x An item to test if it is an object + * @param {any} x An item to test if it is an object * @returns {boolean} True if the item is a non-null object */ export function isObject(x: any): boolean { @@ -80,7 +80,7 @@ export function isObject(x: any): boolean { * @param {any} dst The destination config object (to be merged into) * @param {any} src The source configuration object (to replace default values in dst} * @param {boolean} check True when combining into MathJax._ to avoid setting a property with a getter - * @returns {any} The resulting (modified) config object + * @returns {any} The resulting (modified) config object */ export function combineConfig(dst: any, src: any, check: boolean = false): any { for (const id of Object.keys(src)) { @@ -109,7 +109,7 @@ export function combineConfig(dst: any, src: any, check: boolean = false): any { * @param {any} dst The destination config object (to be merged into) * @param {string} name The id of the configuration block to modify (created if doesn't exist) * @param {any} src The source configuration object (to replace default values in dst} - * @returns {any} The resulting (modified) config object + * @returns {any} The resulting (modified) config object */ export function combineDefaults(dst: any, name: string, src: any): any { if (!dst[name]) { @@ -129,8 +129,8 @@ export function combineDefaults(dst: any, name: string, src: any): any { /** * Combine configuration or data with the existing MathJax object * - * @param {any} config The data to be merged into the MathJax object - * @returns {MathJaxObject} The combined configuration object + * @param {any} config The data to be merged into the MathJax object + * @returns {MathJaxObject} The combined configuration object */ export function combineWithMathJax(config: any): MathJaxObject { return combineConfig(MathJax, config); diff --git a/ts/components/loader.ts b/ts/components/loader.ts index a7b84664a4..d4853edd81 100644 --- a/ts/components/loader.ts +++ b/ts/components/loader.ts @@ -26,7 +26,6 @@ import { MathJax as MJGlobal, MathJaxObject as MJObject, - MathJaxLibrary, MathJaxConfig as MJConfig, combineWithMathJax, combineDefaults, @@ -41,6 +40,7 @@ import { import { FunctionList } from '../util/FunctionList.js'; import { mjxRoot } from '#root/root.js'; import { context } from '../util/context.js'; +import { OPTIONAL } from '../types/Types.js'; import { Locale } from '../util/Locale.js'; import { COMPONENT } from '../core/__locales__/Component.js'; @@ -55,48 +55,31 @@ export type PathFilterData = { }; export type PathFilterFunction = (data: PathFilterData) => boolean; export type PathFilterList = ( - | PathFilterFunction - | [PathFilterFunction, number] + PathFilterFunction | [PathFilterFunction, number] )[]; /** - * Update the configuration structure to include the loader configuration + * The loader option types. */ -/* prettier-ignore */ -export interface MathJaxConfig extends MJConfig { - loader?: { - paths?: {[name: string]: string}; // The path prefixes for use in locations - source?: {[name: string]: string}; // The URLs for the extensions, e.g., tex: [mathjax]/input/tex.js - dependencies?: {[name: string]: string[]}; // The dependencies for each package - provides?: {[name: string]: string[]}; // The sub-packages provided by each package - load?: string[]; // The packages to load (found in locations or [mathjax]/name]) - ready?: PackageReady; // A function to call when MathJax is ready - failed?: PackageFailed; // A function to call when MathJax fails to load - require?: (url: string) => any; // A function for loading URLs - pathFilters?: PathFilterList; // List of path filters (and optional priorities) to add - versionWarnings?: boolean; // True means warn when extension version doesn't match MJ version - [name: string]: any; // Other configuration blocks - }; -} +export type LOADER_OPTIONS = { + paths: { [name: string]: string }; // The path prefixes for use in locations + source: { [name: string]: string }; // The URLs for the extensions, e.g., tex: [mathjax]/input/tex.js + dependencies: { [name: string]: string[] }; // The dependencies for each package + provides: { [name: string]: string[] }; // The sub-packages provided by each package + load: string[]; // The packages to load (found in locations or [mathjax]/name]) + ready: PackageReady; // A function to call when MathJax is ready + failed: PackageFailed; // A function to call when MathJax fails to load + require: (url: string) => any; // A function for loading URLs + json: (url: string) => any; // A function for loading JSON files + pathFilters: PathFilterList; // List of path filters (and optional priorities) to add + versionWarnings: boolean; // True means warn when extension version doesn't match MJ version +}; /** - * Update the MathJax object to inclide the loader information + * The loader config option types. It allows for package-specific configuration. */ -export interface MathJaxObject extends MJObject { - _: MathJaxLibrary; - config: MathJaxConfig; - loader: { - ready: (...names: string[]) => Promise; // Get a promise for when all the named packages are loaded - load: (...names: string[]) => Promise; // Load the packages and return a promise for when ready - preLoaded: (...names: string[]) => void; // Indicate that packages are already loaded by hand - defaultReady: () => void; // The function performed when all packages are loaded - getRoot: () => string; // Find the root URL for the MathJax files - checkVersion: (name: string, version: string) => boolean; // Check the version of an extension - saveVersion: (name: string) => void; // Set the version for a combined component - pathFilters: FunctionList; // The filters to use for looking for package paths - addPackageData: (name: string, data: PackageConfig) => void; // Add more package data for a package - }; - startup?: any; +export interface LOADER_CONFIG extends LOADER_OPTIONS { + [component: string]: any; // should be PackageConfig, but that causes problems with LOADER_TYPES } /** @@ -302,13 +285,16 @@ export const Loader = { if (!config) { config = CONFIG[name] = {}; } - for (const [key, value] of Object.entries(data)) { + for (const [key, value] of Object.entries(data) as [ + keyof PackageConfig, + any, + ][]) { if (Array.isArray(value)) { if (!config[key]) { - config[key] = []; + config[key] = [] as any; } - const set = new Set([...config[key], ...value]); - config[key] = [...set]; + const set = new Set([...(config[key] as any), ...value]); + config[key] = [...set] as any; } else { config[key] = value; } @@ -381,6 +367,58 @@ Loader.pathFilters.add(PathFilters.normalize, 10); Loader.pathFilters.add(PathFilters.prefix, 20); Loader.pathFilters.add(PathFilters.addExtension, 30); +/** + * The loader default options. + */ +const options: LOADER_OPTIONS = { + paths: { + mathjax: Loader.getRoot(), + fonts: context.window + ? 'https://cdn.jsdelivr.net/npm/@mathjax' + : '@mathjax', + }, + source: {}, + dependencies: {}, + provides: {}, + load: [], + ready: Loader.defaultReady.bind(Loader), + failed: (error: PackageError) => + console.warn(`MathJax(${error.package || '?'}): ${error.message}`), + require: null, + json: null, + pathFilters: [], + versionWarnings: true, +}; + +/** + * The component type definition for the loader component. + */ +export type LOADER_TYPES = { + component: { loader: true }; + config: { + loader: LOADER_CONFIG; + }; + properties: { + loader: typeof Loader; + }; +}; + +/** + * Update the configuration structure to include the loader configuration + */ +export interface MathJaxConfig extends MJConfig { + loader?: OPTIONAL; +} + +/** + * Update the MathJax object to inclide the loader information + */ +export interface MathJaxObject extends MJObject { + config: MathJaxConfig; + loader: typeof Loader; + startup?: any; +} + /** * Export the global MathJax object for convenience */ @@ -392,28 +430,8 @@ export const MathJax = MJGlobal as MathJaxObject; * Add any path filters from the configuration. */ 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: {}, - provides: {}, - load: [], - ready: Loader.defaultReady.bind(Loader), - failed: (error: PackageError) => - console.warn(`MathJax(${error.package || '?'}): ${error.message}`), - require: null, - json: null, - pathFilters: [], - versionWarnings: true, - }); - combineWithMathJax({ - loader: Loader, - }); + combineDefaults(MathJax.config, 'loader', options); + combineWithMathJax({ loader: Loader }); // // Add any path filters from the configuration @@ -430,4 +448,4 @@ if (typeof MathJax.loader === 'undefined') { /** * Export the loader configuration for convenience */ -export const CONFIG = MathJax.config.loader; +export const CONFIG = MathJax.config.loader as LOADER_CONFIG; diff --git a/ts/components/startup.ts b/ts/components/startup.ts index 24ffd08216..bc9f6cae22 100644 --- a/ts/components/startup.ts +++ b/ts/components/startup.ts @@ -33,6 +33,7 @@ import { GLOBAL as global, } from './global.js'; +import { mathjax } from '../mathjax.js'; import { MathDocument } from '../core/MathDocument.js'; import { MmlNode } from '../core/MmlTree/MmlNode.js'; import { Handler } from '../core/Handler.js'; @@ -45,93 +46,109 @@ import { OptionList, OPTIONS } from '../util/Options.js'; import { context } from '../util/context.js'; import { Locale } from '../util/Locale.js'; import { COMPONENT } from '../core/__locales__/Component.js'; +import { DOM, DOM_TYPES, N, T, D, OPTIONAL } from '../types/Types.js'; import { TeX } from '../input/tex.js'; /** - * Update the configuration structure to include the startup configuration + * The option types for the startup component. */ -/* prettier-ignore */ -export interface MathJaxConfig extends MJConfig { - startup?: { - input?: string[]; // The names of the input jax to use - output?: string; // The name for the output jax to use - handler?: string; // The handler to register - adaptor?: string; // The name for the DOM adaptor to use - document?: any; // The document (or fragment or string) to work in - elements?: any[]; // The elements to typeset (default is document body) - typeset?: boolean; // Perform initial typeset? - ready?: () => void; // Function to perform when components are ready - pageReady?: () => void; // Function to perform when page is ready - invalidOption?: 'fatal' | 'warn'; // Do invalid options produce a warning, or throw an error? - optionError?: (message: string, key: string) => void, // Function to report invalid options - loadAllFontFiles: false; // true means force all dynamic font files to load initially - [name: string]: any; // Other configuration blocks - }; -} +export type STARTUP_OPTIONS = { + input: string[]; // The names of the input jax to use + output: string; // The name for the output jax to use + handler: string; // The handler to register + adaptor: string; // The name for the DOM adaptor to use + document: any; // The document (or fragment or string) to work in + elements: any[]; // The elements to typeset (default is document body) + typeset: boolean; // Perform initial typeset? + ready: () => void; // Function to perform when components are ready + pageReady: () => void; // Function to perform when page is ready + invalidOption?: 'fatal' | 'warn'; // Do invalid options produce a warning, or throw an error? + optionError?: (message: string, key: string) => void; // Function to report invalid options + loadAllFontFiles: boolean; // true means force all dynamic font files to load initially + polyfillHasOwn: boolean; // Can be removed with ES2024 implementation of Object.hasown +}; /** * Generic types for the standard MathJax objects */ -export type MATHDOCUMENT = MathDocument & { +export type MATHDOCUMENT
= MathDocument< + N
, + T
, + D
+> & { menu?: { loadingPromise: Promise }; + options: { elements?: N
[] }; }; -export type HANDLER = Handler; -export type DOMADAPTOR = DOMAdaptor; -export type INPUTJAX = InputJax; -export type OUTPUTJAX = OutputJax; +export type HANDLER
= Handler, T
, D
>; +export type DOMADAPTOR
= DOMAdaptor< + N
, + T
, + D
+>; +export type INPUTJAX
= InputJax< + N
, + T
, + D
+>; +export type OUTPUTJAX
= OutputJax< + N
, + T
, + D
+>; /* prettier-ignore */ -export type COMMONJAX = - CommonOutputJax; -export type TEX = TeX; +export type COMMONJAX
= + CommonOutputJax, T
, D
, any, any, any, any, any, any, any, any>; +export type TEX
= TeX, T
, D
>; /** * Array of InputJax also with keys using name of jax */ -export type JAXARRAY = INPUTJAX[] & { [name: string]: INPUTJAX }; +export type JAXARRAY
= INPUTJAX
[] & { + [name: string]: INPUTJAX
; +}; /** * A function to extend a handler class */ -export type HandlerExtension = (handler: HANDLER) => HANDLER; +export type HandlerExtension
= ( + handler: HANDLER
+) => HANDLER
; /** - * Update the MathJax object to inclide the startup information + * The startup object types. */ -export interface MathJaxObject extends MJObject { - config: MathJaxConfig; - startup: { - constructors: { [name: string]: any }; - input: JAXARRAY; - output: OUTPUTJAX; - handler: HANDLER; - adaptor: DOMADAPTOR; - elements: any[]; - document: MATHDOCUMENT; - promise: Promise; - registerConstructor(name: string, constructor: any): void; - useHandler(name: string, force?: boolean): void; - useAdaptor(name: string, force?: boolean): void; - useOutput(name: string, force?: boolean): void; - useInput(name: string, force?: boolean): void; - extendHandler(extend: HandlerExtension): void; - toMML(node: MmlNode): string; - defaultReady(): void; - defaultPageReady(): Promise; - defaultOptionError(message: string, key: string): void; - setLocale(): Promise; - getComponents(): void; - makeMethods(): void; - makeTypesetMethods(): void; - makeOutputMethods(iname: string, oname: string, input: INPUTJAX): void; - makeMmlMethods(name: string, input: INPUTJAX): void; - makeResetMethod(name: string, input: INPUTJAX): void; - getInputJax(): JAXARRAY; - getOutputJax(): OUTPUTJAX; - getAdaptor(): DOMADAPTOR; - getHandler(): HANDLER; - }; - [name: string]: any; // Needed for the methods created by the startup module +export interface STARTUP { + mathjax: typeof mathjax; + constructors: { [name: string]: any }; + input: JAXARRAY; + output: OUTPUTJAX; + handler: HANDLER; + adaptor: DOMADAPTOR; + elements: any[]; + document: MATHDOCUMENT; + promise: Promise; + registerConstructor(name: string, constructor: any): void; + useHandler(name: string, force?: boolean): void; + useAdaptor(name: string, force?: boolean): void; + useOutput(name: string, force?: boolean): void; + useInput(name: string, force?: boolean): void; + extendHandler(extend: HandlerExtension): void; + toMML(node: MmlNode): string; + defaultReady(): void; + defaultPageReady(): Promise; + defaultOptionError(message: string, key: string): void; + setLocale(): Promise; + getComponents(): void; + makeMethods(): void; + makeTypesetMethods(): void; + makeOutputMethods(iname: string, oname: string, input: INPUTJAX): void; + makeMmlMethods(name: string, input: INPUTJAX): void; + makeResetMethod(name: string, input: INPUTJAX): void; + getInputJax(): JAXARRAY; + getOutputJax(): OUTPUTJAX; + getAdaptor(): DOMADAPTOR; + getHandler(): HANDLER; } /** @@ -272,7 +289,7 @@ export abstract class Startup { * included an array of input jax */ public static useInput(name: string, force: boolean = false) { - if (!inputSpecified || force) { + if (CONFIG.input.length === 0 || force) { CONFIG.input.push(name); } } @@ -365,7 +382,7 @@ export abstract class Startup { public static getComponents() { Startup.visitor = new MathJax._.core.MmlTree.SerializedMmlVisitor.SerializedMmlVisitor(); - Startup.mathjax = MathJax._.mathjax.mathjax; + Startup.mathjax = mathjax; Startup.input = Startup.getInputJax(); Startup.output = Startup.getOutputJax(); Startup.adaptor = Startup.getAdaptor(); @@ -594,6 +611,54 @@ export abstract class Startup { } } +/** + * The startup option defaults. + */ +const options: STARTUP_OPTIONS = { + input: [], + output: '', + handler: null, + adaptor: null, + document: context.document || '', + elements: null, + typeset: true, + ready: Startup.defaultReady.bind(Startup), + pageReady: Startup.defaultPageReady.bind(Startup), + loadAllFontFiles: false, + polyfillHasOwn: true, // Can be removed with ES2024 implementation of Object.hasown +}; + +/** + * The startup type defintion + */ +export type STARTUP_TYPES = { + component: { startup: true }; + config: { + startup: STARTUP_OPTIONS; + }; + properties: { + startup: STARTUP; + done: () => void; + whenReady: (action: () => any) => Promise; + }; +}; + +/** + * Update the configuration structure to include the startup configuration + */ +export interface MathJaxConfig extends MJConfig { + startup?: OPTIONAL; +} + +/** + * Update the MathJax object to inclide the startup information + */ +export interface MathJaxObject extends MJObject { + config: MathJaxConfig; + startup: STARTUP; + [name: string]: any; // Needed for the methods created by the startup module +} + /** * Export the global MathJax object for convenience */ @@ -605,18 +670,7 @@ export const MathJax = MJGlobal as MathJaxObject; * set the method for handling invalid options, if provided. */ if (typeof MathJax._.startup === 'undefined') { - combineDefaults(MathJax.config, 'startup', { - input: [], - output: '', - handler: null, - adaptor: null, - document: context.document || '', - elements: null, - typeset: true, - ready: Startup.defaultReady.bind(Startup), - pageReady: Startup.defaultPageReady.bind(Startup), - polyfillHasOwn: true, // Can be removed with ES2024 implementation of Object.hasown - }); + combineDefaults(MathJax.config, 'startup', options) as STARTUP_OPTIONS; combineWithMathJax({ startup: Startup, options: {}, @@ -634,8 +688,3 @@ if (typeof MathJax._.startup === 'undefined') { * Export the startup configuration for convenience */ export const CONFIG = MathJax.config.startup; - -/* - * Tells if the user configuration included input jax or not - */ -const inputSpecified = CONFIG.input.length !== 0; diff --git a/ts/core/FilterFunctions.ts b/ts/core/FilterFunctions.ts new file mode 100644 index 0000000000..6a59c815ee --- /dev/null +++ b/ts/core/FilterFunctions.ts @@ -0,0 +1,48 @@ +/************************************************************* + * + * Copyright (c) 2026 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 Types for working with Filter functions + * + * @author dpvc@mathjax.org (Davide Cervone) + */ + +import { MathDocument } from './MathDocument.js'; +import { MathItem } from './MathItem.js'; +import { DOM_TYPES, N, T, D } from '../types/Types.js'; +import { FunctionList } from '../util/FunctionList.js'; + +/** + * Types for filter functions + */ +export type FilterData = { + math: MathItem, T, D>; + document: MathDocument, T, D>; + data: U; +}; +export type FilterFunction = ( + arg: FilterData +) => boolean | void; +export type FilterFunctionDef = + FilterFunction | [FilterFunction, number]; +export type FilterFunctionList = FilterFunctionDef< + T, + D +>[]; +export type FilterFunctions = FunctionList< + FilterFunction +>; diff --git a/ts/core/InputJax.ts b/ts/core/InputJax.ts index f98a40d985..96dbb88706 100644 --- a/ts/core/InputJax.ts +++ b/ts/core/InputJax.ts @@ -28,6 +28,8 @@ import { MmlFactory } from './MmlTree/MmlFactory.js'; import { userOptions, defaultOptions, OptionList } from '../util/Options.js'; import { FunctionList } from '../util/FunctionList.js'; import { DOMAdaptor } from '../core/DOMAdaptor.js'; +import type { DOM, DOM_TYPES } from '../types/Types.js'; +import type { FilterFunctions, FilterFunctionList } from './FilterFunctions.js'; /*****************************************************************/ /** @@ -57,8 +59,8 @@ export interface InputJax { /** * Lists of pre- and post-filters to call before and after processing the input */ - preFilters: FunctionList; - postFilters: FunctionList; + preFilters: FilterFunctions>; + postFilters: FilterFunctions>; /** * The DOM adaptor for managing HTML elements @@ -97,8 +99,8 @@ export interface InputJax { * * @param {N | string[]} which The element or array of strings to be searched for math * @param {OptionList} options The options for the search, if any - * @returns {ProtoItem[]} Array of proto math items found (further processed by the - * handler to produce actual MathItem objects) + * @returns {ProtoItem[]} Array of proto math items found (further processed by the + * handler to produce actual MathItem objects) */ findMath(which: N | string[], options?: OptionList): ProtoItem[]; @@ -107,20 +109,40 @@ export interface InputJax { * * @param {MathItem} math The MathItem whose math content is to processed * @param {MathDocument} document The MathDocument for this input jax. - * @returns {MmlNode} The resulting internal node tree for the math + * @returns {MmlNode} The resulting internal node tree for the math */ compile(math: MathItem, document: MathDocument): MmlNode; } +/** + * The InputJax option types. + */ +export type INPUTJAX_OPTIONS< + D extends DOM_TYPES = DOM, + PRE = any, + POST = PRE, +> = { + preFilters: FilterFunctionList; + postFilters: FilterFunctionList; +}; + /*****************************************************************/ /** * The abstract InputJax class * - * @template N The HTMLElement node class - * @template T The Text node class - * @template D The Document class + * @template N The HTMLElement node class + * @template T The Text node class + * @template D The Document class + * @template PRE The type of data passed to the pre-filter functions + * @template POST The type of data passed to the post-filter functions */ -export abstract class AbstractInputJax implements InputJax { +export abstract class AbstractInputJax< + N, + T, + D, + PRE = any, + POST = PRE, +> implements InputJax { /** * The name of the input jax */ @@ -129,7 +151,7 @@ export abstract class AbstractInputJax implements InputJax { /** * The default options for the input jax */ - public static OPTIONS: OptionList = { + public static OPTIONS: INPUTJAX_OPTIONS = { preFilters: [], postFilters: [], }; @@ -142,12 +164,12 @@ export abstract class AbstractInputJax implements InputJax { /** * Filters to run on the TeX string before it is processed */ - public preFilters: FunctionList; + public preFilters: FilterFunctions>; /** * Filters to run on the generated MathML after the TeX string is processed */ - public postFilters: FunctionList; + public postFilters: FilterFunctions>; /** * The DOMAdaptor for the MathDocument for this input jax @@ -231,13 +253,15 @@ export abstract class AbstractInputJax implements InputJax { * @param {MathItem} math The math item that is being processed * @param {MathDocument} document The math document containg the math item * @param {any} data Whatever other data is needed - * @returns {any} The (possibly modified) data + * @returns {any} The (possibly modified) data + * + * @template D The type of data being passed */ - protected executeFilters( + protected executeFilters( filters: FunctionList, math: MathItem, document: MathDocument, - data: any + data: DATA ): any { const args = { math: math, document: document, data: data }; filters.execute(args); diff --git a/ts/core/MathDocument.ts b/ts/core/MathDocument.ts index d0e68ca35e..0822def47d 100644 --- a/ts/core/MathDocument.ts +++ b/ts/core/MathDocument.ts @@ -38,6 +38,7 @@ import { BitField, BitFieldClass } from '../util/BitField.js'; import { PrioritizedList } from '../util/PrioritizedList.js'; import { localize } from './__locales__/Component.js'; import { mathjax } from '../mathjax.js'; +import { DOM, DOM_TYPES, N, T, D, Constructor } from '../types/Types.js'; /*****************************************************************/ @@ -388,7 +389,7 @@ export interface MathDocument { /** * The options for the document */ - options: OptionList; + options: DOCUMENT_OPTIONS>; /** * The list of MathItems found in this page @@ -443,14 +444,14 @@ export interface MathDocument { * * @returns {MathDocument} The math document instance */ - render(): MathDocument; + render(): this; /** * Perform the renderActions on the document with retry handling * * @returns {Promise} A promise that resolves when the render is complete */ - renderPromise(): Promise>; + renderPromise(): Promise; /** * Rerender the MathItems on the page @@ -458,7 +459,7 @@ export interface MathDocument { * @param {number} start The state to start rerendering at * @returns {MathDocument} The math document instance */ - rerender(start?: number): MathDocument; + rerender(start?: number): this; /** * Rerender the MathItems on the page @@ -466,7 +467,7 @@ export interface MathDocument { * @param {number} start The state to start rerendering at * @returns {Promise} A promise that resolves when the rerender is complete */ - rerenderPromise(start?: number): Promise>; + rerenderPromise(start?: number): Promise; /** * Convert a math string to the document's output format @@ -514,35 +515,35 @@ export interface MathDocument { * @param {OptionList} options The options for locating the math * @returns {MathDocument} The math document instance */ - findMath(options?: OptionList): MathDocument; + findMath(options?: OptionList): this; /** * Calls the input jax to process the MathItems in the MathList * * @returns {MathDocument} The math document instance */ - compile(): MathDocument | Promise; + compile(): this | Promise; /** * Gets the metric information for the MathItems * * @returns {MathDocument} The math document instance */ - getMetrics(): MathDocument; + getMetrics(): this; /** * Calls the output jax to process the compiled math in the MathList * * @returns {MathDocument} The math document instance */ - typeset(): MathDocument | Promise; + typeset(): this | Promise; /** * Updates the document to include the typeset math * * @returns {MathDocument} The math document instance */ - updateDocument(): MathDocument; + updateDocument(): this; /** * Removes the typeset math from the document @@ -551,7 +552,7 @@ export interface MathDocument { * back into the document as well * @returns {MathDocument} The math document instance */ - removeFromDocument(restore?: boolean): MathDocument; + removeFromDocument(restore?: boolean): this; /** * Set the state of the document (allowing you to roll back @@ -562,7 +563,7 @@ export interface MathDocument { * back into the document during the rollback * @returns {MathDocument} The math document instance */ - state(state: number, restore?: boolean): MathDocument; + state(state: number, restore?: boolean): this; /** * Clear the processed values so that the document can be reprocessed @@ -570,7 +571,7 @@ export interface MathDocument { * @param {ResetList} options The things to be reset * @returns {MathDocument} The math document instance */ - reset(options?: ResetList): MathDocument; + reset(options?: ResetList): this; /** * Reset the processed values and clear the MathList (so that new math @@ -578,7 +579,7 @@ export interface MathDocument { * * @returns {MathDocument} The math document instance */ - clear(): MathDocument; + clear(): this; /** * Indicate that the MathDocument is no longer needed. @@ -591,7 +592,7 @@ export interface MathDocument { * @param {MathList} list The MathList to be merged into this document's list * @returns {MathDocument} The math document instance */ - concat(list: MathList): MathDocument; + concat(list: MathList): this; /** * Clear the typeset MathItems that are within the given container @@ -675,6 +676,54 @@ class DefaultMathList extends AbstractMathList {} class DefaultMathItem extends AbstractMathItem {} /*****************************************************************/ + +/** + * The MathDocument option types. + */ +export type DOCUMENT_OPTIONS = { + OutputJax: OutputJax, T, D>; // instance of an OutputJax for the document + InputJax: + InputJax, T, D> | InputJax, T, D>[]; // instance of an InputJax or an array of them + MmlFactory: MmlFactory; // instance of a MmlFactory for this document + MathList: Constructor, T, D>>; // constructor for a MathList to use for the document + MathItem: Constructor, T, D>>; // constructor for a MathItem to use for the MathList + compileError: ( + doc: AbstractMathDocument, T, D>, + math: MathItem, T, D>, + err: Error + ) => void; + typesetError: ( + doc: AbstractMathDocument, T, D>, + math: MathItem, T, D>, + err: Error + ) => void; + renderActions: RenderActions, T, D>; +}; + +/** + * The MathDocument default options. + */ +const options: DOCUMENT_OPTIONS = { + OutputJax: null, + InputJax: null, + MmlFactory: null, + MathList: DefaultMathList, + MathItem: DefaultMathItem, + compileError(doc, math, err) { + doc.compileError(math, err); + }, + typesetError(doc, math, err) { + doc.typesetError(math, err); + }, + renderActions: expandable({ + find: [STATE.FINDMATH, 'findMath', '', false], + compile: [STATE.COMPILED, 'compileAction', 'compile'], + metrics: [STATE.METRICS, 'getMetrics', '', false], + typeset: [STATE.TYPESET, 'typesetAction', 'typeset'], + update: [STATE.INSERTED, 'updateDocument', false], + }), +}; + /** * Implements the abstract MathDocument class * @@ -695,34 +744,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * The default options for the document */ - public static OPTIONS: OptionList = { - OutputJax: null, // instance of an OutputJax for the document - InputJax: null, // instance of an InputJax or an array of them - MmlFactory: null, // instance of a MmlFactory for this document - MathList: DefaultMathList, // constructor for a MathList to use for the document - MathItem: DefaultMathItem, // constructor for a MathItem to use for the MathList - compileError: ( - doc: AbstractMathDocument, - math: MathItem, - err: Error - ) => { - doc.compileError(math, err); - }, - typesetError: ( - doc: AbstractMathDocument, - math: MathItem, - err: Error - ) => { - doc.typesetError(math, err); - }, - renderActions: expandable({ - find: [STATE.FINDMATH, 'findMath', '', false], - compile: [STATE.COMPILED, 'compileAction', 'compile'], - metrics: [STATE.METRICS, 'getMetrics', '', false], - typeset: [STATE.TYPESET, 'typesetAction', 'typeset'], - update: [STATE.INSERTED, 'updateDocument', false], - }) as RenderActions, - }; + public static OPTIONS = options; /** * A bit-field for the actions that have been processed @@ -742,7 +764,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * The actual options for this document (with user-supplied ones merged in) */ - public options: OptionList; + public options: DOCUMENT_OPTIONS>; /** * The list of MathItems for this document @@ -800,17 +822,17 @@ export abstract class AbstractMathDocument implements MathDocument< constructor(document: D, adaptor: DOMAdaptor, options: OptionList) { const CLASS = this.constructor as typeof AbstractMathDocument; this.document = document; - this.options = userOptions(defaultOptions({}, CLASS.OPTIONS), options); - this.math = new (this.options['MathList'] || DefaultMathList)(); - this.renderActions = RenderList.create( - this.options['renderActions'] - ); + this.options = userOptions( + defaultOptions({}, CLASS.OPTIONS), + options + ) as DOCUMENT_OPTIONS; + this.math = new (this.options.MathList ?? DefaultMathList)(); + this.renderActions = RenderList.create(this.options.renderActions); this._actionPromises = []; this._readyPromise = Promise.resolve(); this.processed = new AbstractMathDocument.ProcessBits(); - this.outputJax = - this.options['OutputJax'] || new DefaultOutputJax(); - let inputJax = this.options['InputJax'] || [new DefaultInputJax()]; + this.outputJax = this.options.OutputJax ?? new DefaultOutputJax(); + let inputJax = this.options.InputJax ?? [new DefaultInputJax()]; if (!Array.isArray(inputJax)) { inputJax = [inputJax]; } @@ -824,7 +846,7 @@ export abstract class AbstractMathDocument implements MathDocument< // // Pass the MmlFactory to the jax // - this.mmlFactory = this.options['MmlFactory'] || new MmlFactory(); + this.mmlFactory = this.options.MmlFactory || new MmlFactory(); this.inputJax.map((jax) => jax.setMmlFactory(this.mmlFactory)); // // Do any initialization that requires adaptors or factories @@ -854,7 +876,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public removeRenderAction(id: string) { + public removeRenderAction(id: string): void { const action = this.renderActions.findID(id); if (action) { this.renderActions.remove(action); @@ -873,7 +895,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public renderPromise() { + public renderPromise(): Promise { return this.whenReady(() => mathjax.handleRetriesFor(async () => { this.render(); @@ -887,7 +909,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public rerender(start: number = STATE.RERENDER) { + public rerender(start: number = STATE.RERENDER): this { this.state(start - 1); this.render(); return this; @@ -896,7 +918,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public rerenderPromise(start: number = STATE.RERENDER) { + public rerenderPromise(start: number = STATE.RERENDER): Promise { return this.whenReady(() => mathjax.handleRetriesFor(async () => { this.rerender(start); @@ -910,7 +932,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public convert(math: string, options: OptionList = {}) { + public convert(math: string, options: OptionList = {}): MmlNode | N { let { format, display, end, ex, em, containerWidth, scale, family } = userOptions( { @@ -952,7 +974,10 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public convertPromise(math: string, options: OptionList = {}) { + public convertPromise( + math: string, + options: OptionList = {} + ): Promise { return this.whenReady(() => mathjax.handleRetriesFor(async () => { const node = this.convert(math, options); @@ -1005,28 +1030,28 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public actionPromises() { + public actionPromises(): Promise { return Promise.all(this._actionPromises); } /** * @override */ - public clearPromises() { + public clearPromises(): void { this._actionPromises = []; } /** * @override */ - public savePromise(promise: Promise) { + public savePromise(promise: Promise): void { this._actionPromises.push(promise); } /** * @override */ - public findMath(_options: OptionList = null) { + public findMath(_options: OptionList = null): this { this.processed.set('findMath'); return this; } @@ -1034,7 +1059,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public compile() { + public compile(): this | Promise { return this.compileAction(false); } @@ -1050,7 +1075,7 @@ export abstract class AbstractMathDocument implements MathDocument< action: boolean = true, item: MathListItem = this.math.first(), recompile: MathItem[] = [] - ): MathDocument | Promise { + ): this | Promise { if (this.processed.isSet('compile')) return this; while (!item.isEnd) { const math = item.data as MathItem; @@ -1109,15 +1134,15 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @param {MathItem} math The item to compile */ - protected compileMath(math: MathItem) { + protected compileMath(math: MathItem): void { try { math.compile(this); } catch (err) { if (err.retry) { throw err; } - this.options['compileError'](this, math, err); - math.inputData['error'] = err; + this.options.compileError(this, math, err); + math.inputData.error = err; } } @@ -1127,7 +1152,7 @@ export abstract class AbstractMathDocument implements MathDocument< * @param {MathItem} math The MathItem producing the error * @param {Error} err The Error object for the error */ - public compileError(math: MathItem, err: Error) { + public compileError(math: MathItem, err: Error): void { math.root = this.mmlFactory.create('math', null, [ this.mmlFactory.create( 'merror', @@ -1150,7 +1175,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public typeset() { + public typeset(): this | Promise { return this.typesetAction(false); } @@ -1164,7 +1189,7 @@ export abstract class AbstractMathDocument implements MathDocument< protected typesetAction( action: boolean = true, item: MathListItem = this.math.first() - ): AbstractMathDocument | Promise { + ): this | Promise { if (this.processed.isSet('typeset')) return this; while (!item.isEnd) { const math = item.data as MathItem; @@ -1177,8 +1202,8 @@ export abstract class AbstractMathDocument implements MathDocument< } throw err; } - this.options['typesetError'](this, math, err); - math.outputData['error'] = err; + this.options.typesetError(this, math, err); + math.outputData.error = err; } item = item.next; } @@ -1192,7 +1217,7 @@ export abstract class AbstractMathDocument implements MathDocument< * @param {MathItem} math The MathItem producing the error * @param {Error} err The Error object for the error */ - public typesetError(math: MathItem, err: Error) { + public typesetError(math: MathItem, err: Error): void { math.typesetRoot = this.adaptor.node( 'mjx-container', { @@ -1230,7 +1255,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public getMetrics() { + public getMetrics(): this { if (!this.processed.isSet('getMetrics')) { this.outputJax.getMetrics(this); this.processed.set('getMetrics'); @@ -1241,7 +1266,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public updateDocument() { + public updateDocument(): this { if (!this.processed.isSet('updateDocument')) { for (const math of this.math.reversed()) { math.updateDocument(this); @@ -1254,14 +1279,14 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public removeFromDocument(_restore: boolean = false) { + public removeFromDocument(_restore: boolean = false): this { return this; } /** * @override */ - public state(state: number, restore: boolean = false) { + public state(state: number, restore: boolean = false): this { for (const math of this.math) { math.state(state, restore); } @@ -1284,7 +1309,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public reset(options: ResetList = { processed: true }) { + public reset(options: ResetList = { processed: true }): this { options = userOptions(Object.assign({}, resetOptions), options); if (options.all) { Object.assign(options, resetAllOptions); @@ -1304,7 +1329,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public clear() { + public clear(): this { this.reset(); this.math.clear(); return this; @@ -1313,14 +1338,14 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public done() { + public done(): Promise { return Promise.resolve(); } /** * @override */ - public concat(list: MathList) { + public concat(list: MathList): this { this.math.merge(list); return this; } @@ -1328,7 +1353,9 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public clearMathItemsWithin(containers: ContainerList) { + public clearMathItemsWithin( + containers: ContainerList + ): MathItem[] { const items = this.getMathItemsWithin(containers); for (const item of items.slice(0).reverse()) { item.clear(); @@ -1340,7 +1367,7 @@ export abstract class AbstractMathDocument implements MathDocument< /** * @override */ - public getMathItemsWithin(elements: ContainerList) { + public getMathItemsWithin(elements: ContainerList): MathItem[] { if (!Array.isArray(elements)) { elements = [elements]; } @@ -1362,13 +1389,15 @@ export abstract class AbstractMathDocument implements MathDocument< /** * The constructor type for a MathDocument * - * @template D The MathDocument type this constructor is for + * @template DOC The MathDocument type this constructor is for + * @template DD The DOM types in use */ export interface MathDocumentConstructor< - D extends MathDocument, + DOC extends MathDocument, T
, D
>, + DD extends DOM_TYPES = DOM, > { KIND: string; - OPTIONS: OptionList; + OPTIONS: DOCUMENT_OPTIONS
; ProcessBits: typeof BitField; - new (...args: any[]): D; + new (...args: any[]): DOC; } diff --git a/ts/core/MmlTree/MmlNode.ts b/ts/core/MmlTree/MmlNode.ts index aed5f2b052..a33ba44cf5 100644 --- a/ts/core/MmlTree/MmlNode.ts +++ b/ts/core/MmlTree/MmlNode.ts @@ -392,7 +392,7 @@ export abstract class AbstractMmlNode /** * This is the list of options for the verifyTree() method */ - public static verifyDefaults: PropertyList = { + public static verifyDefaults = { checkArity: true, checkAttributes: false, checkMathvariants: true, diff --git a/ts/core/OutputJax.ts b/ts/core/OutputJax.ts index 291060a855..8d4b514eb2 100644 --- a/ts/core/OutputJax.ts +++ b/ts/core/OutputJax.ts @@ -26,6 +26,8 @@ import { MathDocument } from './MathDocument.js'; import { MathItem } from './MathItem.js'; import { DOMAdaptor } from '../core/DOMAdaptor.js'; import { FunctionList } from '../util/FunctionList.js'; +import type { DOM, DOM_TYPES } from '../types/Types.js'; +import type { FilterFunctions, FilterFunctionList } from './FilterFunctions.js'; /*****************************************************************/ /** @@ -118,6 +120,18 @@ export interface OutputJax { pageElements(document: MathDocument): N; } +/** + * The OutputJax option types. + */ +export type OUTPUTJAX_OPTIONS< + D extends DOM_TYPES = DOM, + PRE = any, + POST = PRE, +> = { + preFilters: FilterFunctionList; + postFilters: FilterFunctionList; +}; + /*****************************************************************/ /** * The OutputJax abstract class @@ -135,7 +149,7 @@ export abstract class AbstractOutputJax implements OutputJax { /** * The default options for the output jax */ - public static OPTIONS: OptionList = { + public static OPTIONS: OUTPUTJAX_OPTIONS = { preFilters: [], postFilters: [], }; @@ -148,12 +162,12 @@ export abstract class AbstractOutputJax implements OutputJax { /** * Filters to run before the output is processed */ - public preFilters: FunctionList; + public preFilters: FilterFunctions>; /** * Filters to run after the output is processed */ - public postFilters: FunctionList; + public postFilters: FilterFunctions>; /** * The MathDocument's DOMAdaptor diff --git a/ts/handlers/html/HTMLDocument.ts b/ts/handlers/html/HTMLDocument.ts index d6df11a897..73f393da37 100644 --- a/ts/handlers/html/HTMLDocument.ts +++ b/ts/handlers/html/HTMLDocument.ts @@ -21,7 +21,11 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { AbstractMathDocument } from '../../core/MathDocument.js'; +import { + AbstractMathDocument, + RenderActions, + DOCUMENT_OPTIONS, +} from '../../core/MathDocument.js'; import { userOptions, separateOptions, @@ -35,6 +39,7 @@ import { DOMAdaptor } from '../../core/DOMAdaptor.js'; import { InputJax } from '../../core/InputJax.js'; import { STATE, newState, ProtoItem, Location } from '../../core/MathItem.js'; import { StyleJson } from '../../util/StyleJson.js'; +import { DOM, DOM_TYPES, N, T, D, Constructor } from '../../types/Types.js'; /*****************************************************************/ /** @@ -54,6 +59,30 @@ export type HTMLNodeArray = [N | T, number][][]; */ newState('STYLES', STATE.INSERTED + 1); +/*****************************************************************/ + +/** + * The new HTMLDocument option types. + */ +export type OPTIONS = { + DomStrings: HTMLDomStrings, T, D>; // The DomStrings parser +}; + +/** + * The HTMLDocument option types. + */ +export interface HTMLDOCUMENT_OPTIONS + extends OPTIONS, DOCUMENT_OPTIONS { + MathItem: Constructor, T, D>>; +} + +/** + * The HTMLDocument option defaults. + */ +const options: OPTIONS = { + DomStrings: null, // Use the default DomString parser +}; + /*****************************************************************/ /** * The HTMLDocument class (extends AbstractMathDocument) @@ -71,18 +100,22 @@ export class HTMLDocument extends AbstractMathDocument { /** * The default options for HTMLDocument */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...AbstractMathDocument.OPTIONS, - renderActions: expandable({ + ...options, + renderActions: expandable>({ ...AbstractMathDocument.OPTIONS.renderActions, - styles: [STATE.STYLES, '', 'updateStyleSheet', false] // update styles on a rerender() call + styles: [STATE.STYLES, '', 'updateStyleSheet', false], // update styles on a rerender() call }), - MathList: HTMLMathList, // Use the HTMLMathList for MathLists - MathItem: HTMLMathItem, // Use the HTMLMathItem for MathItem - DomStrings: null // Use the default DomString parser + MathList: HTMLMathList, // Use the HTMLMathList for MathLists + MathItem: HTMLMathItem, // Use the HTMLMathItem for MathItem }; + /** + * @override + */ + public options: HTMLDOCUMENT_OPTIONS> & { elements?: N[] }; + /** * Extra styles to be included in the document's stylesheet (added by extensions) */ @@ -106,7 +139,7 @@ export class HTMLDocument extends AbstractMathDocument { const [html, dom] = separateOptions(options, HTMLDomStrings.OPTIONS); super(document, adaptor, html); this.domStrings = - this.options['DomStrings'] || new HTMLDomStrings(dom); + this.options.DomStrings || new HTMLDomStrings(dom); this.domStrings.adaptor = adaptor; this.styles = []; } diff --git a/ts/handlers/html/HTMLDomStrings.ts b/ts/handlers/html/HTMLDomStrings.ts index 4211808acf..13c742bbe8 100644 --- a/ts/handlers/html/HTMLDomStrings.ts +++ b/ts/handlers/html/HTMLDomStrings.ts @@ -29,15 +29,63 @@ import { expandable, } from '../../util/Options.js'; import { DOMAdaptor } from '../../core/DOMAdaptor.js'; +import { DOM, DOM_TYPES, N, T, D } from '../../types/Types.js'; /** - * List of consecutive text nodes and their text lengths + * List of consecutive text nodes and their text lengths * * @template N The HTMLElement node class * @template T The Text node class */ export type HTMLNodeList = [N | T, number][]; +/** + * The data for HTML to be allowed in TeX expressions + * + * @template N The HTMLElement node class + * @template T The Text node class + * @template D The Document node class + */ +export type HTMLTagData = + | string + | ((node: N, adaptor: DOMAdaptor) => string); + +/*****************************************************************/ + +export type HTMLDOMSTRINGS_OPTIONS = { + // + // The names of the tags whose contents will not be scanned for math + // delimiters. + // + skipHtmlTags: string[]; + // + // Tags to be included in the text (and what text to replace them + // with). + // + includeHtmlTags: {[tag: string]: HTMLTagData, T, D>}; + // + // The class name of elements whose contents should NOT be processed + // by tex2jax. Note that this is used as a regular expression, so + // be sure to quote any regexp special characters. + // + ignoreHtmlClass: string, + // + // The class name of elements whose contents SHOULD be processed + // when they appear inside ones that are ignored. Note that this is + // used as a regular expression, so be sure to quote any regexp + // special characters. + // + processHtmlClass: string, +}; + +const options: HTMLDOMSTRINGS_OPTIONS = { + skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code', + 'math', 'select', 'option', 'mjx-container'], + includeHtmlTags: expandable({br: '\n', wbr: '', '#comment': ''}), + ignoreHtmlClass: 'mathjax_ignore', + processHtmlClass: 'mathjax_process', +}; + /*****************************************************************/ /** * The HTMLDocument class (extends AbstractMathDocument) @@ -52,27 +100,7 @@ export class HTMLDomStrings { /** * The default options for string processing */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { - skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code', - 'math', 'select', 'option', 'mjx-container'], - // The names of the tags whose contents will not be - // scanned for math delimiters - - includeHtmlTags: expandable({br: '\n', wbr: '', '#comment': ''}), - // tags to be included in the text (and what - // text to replace them with) - - ignoreHtmlClass: 'mathjax_ignore', // the class name of elements whose contents should - // NOT be processed by tex2jax. Note that this - // is a regular expression, so be sure to quote any - // regexp special characters - - processHtmlClass: 'mathjax_process' // the class name of elements whose contents SHOULD - // be processed when they appear inside ones that - // are ignored. Note that this is a regular expression, - // so be sure to quote any regexp special characters - }; + public static OPTIONS = options; /** * The options for this instance diff --git a/ts/input/asciimath.ts b/ts/input/asciimath.ts index a53d0a3299..c610c010c1 100644 --- a/ts/input/asciimath.ts +++ b/ts/input/asciimath.ts @@ -21,14 +21,34 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { AbstractInputJax } from '../core/InputJax.js'; +import { AbstractInputJax, INPUTJAX_OPTIONS } from '../core/InputJax.js'; import { LegacyAsciiMath } from './asciimath/legacy.js'; import { separateOptions, OptionList } from '../util/Options.js'; import { MathDocument } from '../core/MathDocument.js'; import { MathItem } from '../core/MathItem.js'; +import { DOM, DOM_TYPES, N, T, D } from '../types/Types.js'; import { FindAsciiMath } from './asciimath/FindAsciiMath.js'; +/*****************************************************************/ + +/** + * The AsciiMath option types. + */ +export interface ASCIIMATH_OPTIONS< + DOM extends DOM_TYPES, +> extends INPUTJAX_OPTIONS { + FindAsciiMath: FindAsciiMath, T, D>; +} + +/** + * The AsciiMath option defaults. + */ +const options: ASCIIMATH_OPTIONS = { + ...AbstractInputJax.OPTIONS, + FindAsciiMath: null, +}; + /*****************************************************************/ /** * Implements the AsciiMath class (extends AbstractInputJax) @@ -46,10 +66,7 @@ export class AsciiMath extends AbstractInputJax { /** * @override */ - public static OPTIONS: OptionList = { - ...AbstractInputJax.OPTIONS, - FindAsciiMath: null, - }; + public static OPTIONS = options; /** * The FindMath object used to search for AsciiMath in the document @@ -66,8 +83,7 @@ export class AsciiMath extends AbstractInputJax { AsciiMath.OPTIONS ); super(am); - this.findAsciiMath = - this.options['FindAsciiMath'] || new FindAsciiMath(find); + this.findAsciiMath = this.options.FindAsciiMath || new FindAsciiMath(find); } /** diff --git a/ts/input/asciimath/FindAsciiMath.ts b/ts/input/asciimath/FindAsciiMath.ts index 51f7b6f459..96afa521a2 100644 --- a/ts/input/asciimath/FindAsciiMath.ts +++ b/ts/input/asciimath/FindAsciiMath.ts @@ -32,6 +32,22 @@ import { ProtoItem, protoItem } from '../../core/MathItem.js'; export type EndItem = [string, boolean, RegExp]; export type Delims = [string, string]; +/*****************************************************************/ + +/** + * The FindAsciiMath option types. + */ +export type FINDASCIIMATH_OPTIONS = { + delimiters: [string, string][]; // The start/end delimiter pairs for asciimath code +}; + +/** + * The FindAsciiMath option defaults. + */ +const options: FINDASCIIMATH_OPTIONS = { + delimiters: [['`', '`']], +}; + /*****************************************************************/ /** * Implements the FindAsciiMath class (extends AbstractFindMath) @@ -46,9 +62,7 @@ export class FindAsciiMath extends AbstractFindMath { /** * @override */ - public static OPTIONS: OptionList = { - delimiters: [['`', '`']], // The start/end delimiter pairs for asciimath code - }; + public static OPTIONS = options; /** * The regular expression for any starting delimiter diff --git a/ts/input/mathml.ts b/ts/input/mathml.ts index dc5e5ce8e7..d1ab25d50e 100644 --- a/ts/input/mathml.ts +++ b/ts/input/mathml.ts @@ -21,22 +21,57 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { AbstractInputJax } from '../core/InputJax.js'; -import { - defaultOptions, - separateOptions, - OptionList, -} from '../util/Options.js'; +import { AbstractInputJax, INPUTJAX_OPTIONS } from '../core/InputJax.js'; +import { separateOptions, OptionList } from '../util/Options.js'; import { FunctionList } from '../util/FunctionList.js'; import { MathDocument } from '../core/MathDocument.js'; import { MathItem } from '../core/MathItem.js'; import { DOMAdaptor } from '../core/DOMAdaptor.js'; import { MmlFactory } from '../core/MmlTree/MmlFactory.js'; +import { MmlNode } from '../core/MmlTree/MmlNode.js'; +import { DOM, DOM_TYPES, N, T, D } from '../types/Types.js'; +import { + FilterFunctions, + FilterFunctionList, +} from '../core/FilterFunctions.js'; import { FindMathML } from './mathml/FindMathML.js'; import { MathMLCompile } from './mathml/MathMLCompile.js'; import { localize } from './mathml/__locales__/Component.js'; +/*****************************************************************/ + +/** + * The MathML option types. + */ +export interface MATHML_OPTIONS extends INPUTJAX_OPTIONS< + DOM, + string, + MmlNode +> { + parseAs: 'html' | 'xml'; + forceReparse: boolean; + mmlFilters: FilterFunctionList | T, DOM>; + FindMathML: FindMathML, T, D>; + MathMLCompile: MathMLCompile, T, D>; + parseError: (node: Node) => void; +} + +/** + * The MathML option defaults. + */ +const options: MATHML_OPTIONS = { + ...AbstractInputJax.OPTIONS, + parseAs: 'html', // Whether to use HTML or XML parsing for the MathML string + forceReparse: false, // Whether to force the string to be reparsed, or use the one from the document DOM + mmlFilters: [], // Filters to add to the mmlFilters lost + FindMathML: null, // The FindMathML instance to override the default one + MathMLCompile: null, // The MathMLCompile instance to override the default one + parseError(node: Node) { + this.error(this.adaptor.textContent(node).replace(/\n.*/g, '')); + }, +}; + /*****************************************************************/ /** * Implements the MathML class (extends AbstractInputJax) @@ -45,7 +80,13 @@ import { localize } from './mathml/__locales__/Component.js'; * @template T The Text node class * @template D The Document class */ -export class MathML extends AbstractInputJax { +export class MathML extends AbstractInputJax< + N, + T, + D, + string, + MmlNode +> { /** * The name of this input jax */ @@ -54,20 +95,7 @@ export class MathML extends AbstractInputJax { /** * @override */ - /* prettier-ignore */ - public static OPTIONS: OptionList = defaultOptions({ - parseAs: 'html', // Whether to use HTML or XML parsing for the MathML string - forceReparse: false, // Whether to force the string to be reparsed, or use the one from the document DOM - mmlFilters: [], // Filters to add to the mmlFilters lost - FindMathML: null, // The FindMathML instance to override the default one - MathMLCompile: null, // The MathMLCompile instance to override the default one - /* - * The function to use to handle a parsing error (throw an error by default) - */ - parseError: function (node: Node) { - this.error(this.adaptor.textContent(node).replace(/\n.*/g, '')); - } - }, AbstractInputJax.OPTIONS); + public static OPTIONS = options; /** * The FindMathML instance used to locate MathML in the document @@ -82,7 +110,12 @@ export class MathML extends AbstractInputJax { /** * A list of functions to call on the parsed MathML DOM before conversion to internal structure */ - public mmlFilters: FunctionList; + public mmlFilters: FilterFunctions>; + + /** + * @override + */ + public options: MATHML_OPTIONS>; /** * @override @@ -149,7 +182,7 @@ export class MathML extends AbstractInputJax { if ( !mml || !math.end.node || - this.options['forceReparse'] || + this.options.forceReparse || this.adaptor.kind(mml) === '#text' ) { let mathml = this.executeFilters( @@ -158,11 +191,11 @@ export class MathML extends AbstractInputJax { document, (math.math || '').trim() ); - if (this.options['parseAs'] === 'html') { + if (this.options.parseAs === 'html') { mathml = `${mathml}`; } const doc = this.checkForErrors( - this.adaptor.parse(mathml, 'text/' + this.options['parseAs']) + this.adaptor.parse(mathml, 'text/' + this.options.parseAs) ); const body = this.adaptor.body(doc); if (this.adaptor.childNodes(body).length !== 1) { @@ -175,7 +208,7 @@ export class MathML extends AbstractInputJax { ); } } - mml = this.executeFilters(this.mmlFilters, math, document, mml); + mml = this.executeFilters(this.mmlFilters, math, document, mml); let root = this.mathml.compile(mml as N); root = this.executeFilters(this.postFilters, math, document, root); math.display = root.attributes.get('display') === 'block'; @@ -186,7 +219,7 @@ export class MathML extends AbstractInputJax { * Check a parsed MathML string for errors. * * @param {D} doc The document returns from the DOMParser - * @returns {D} The document + * @returns {D} The document */ protected checkForErrors(doc: D): D { const err = this.adaptor.tags(this.adaptor.body(doc), 'parsererror')[0]; @@ -194,7 +227,7 @@ export class MathML extends AbstractInputJax { if (this.adaptor.textContent(err) === '') { this.error(localize('MmlError')); } - this.options['parseError'].call(this, err); + this.options.parseError.call(this, err); } return doc; } diff --git a/ts/input/mathml/MathMLCompile.ts b/ts/input/mathml/MathMLCompile.ts index a4fae07608..a525c7a6a1 100644 --- a/ts/input/mathml/MathMLCompile.ts +++ b/ts/input/mathml/MathMLCompile.ts @@ -37,6 +37,36 @@ import { DOMAdaptor } from '../../core/DOMAdaptor.js'; import { Locale } from '../../util/Locale.js'; import { COMPONENT } from './__locales__/Component.js'; +/********************************************************************/ + +type VERIFY_OPTIONS = typeof AbstractMmlNode.verifyDefaults; + +/** + * The MathMLCompile option types. + */ +export type MATHMLCOMPILE_OPTIONS = { + MmlFactory: MmlFactory; // The MmlFactory to use (defaults to a new MmlFactory) + allowHtmlInTokenNodes: boolean; // True if HTML is allowed in token nodes + fixMisplacedChildren: boolean; // True if we want to use heuristics to try to fix + // problems with the tree based on HTML not handling + // self-closing tags properly + verify: VERIFY_OPTIONS; // Options to pass to verifyTree() controlling MathML verification + translateEntities: boolean; // True means translate entities in text nodes +}; + +/** + * The MathMLCompile option defaults. + */ +const options: MATHMLCOMPILE_OPTIONS = { + MmlFactory: null, + allowHtmlInTokenNodes: false, + fixMisplacedChildren: true, + verify: { + ...AbstractMmlNode.verifyDefaults, + }, + translateEntities: true, +}; + /********************************************************************/ /** * The class for performing the MathML DOM node to @@ -50,18 +80,7 @@ export class MathMLCompile { /** * The default options for this object */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { - MmlFactory: null, // The MmlFactory to use (defaults to a new MmlFactory) - allowHtmlInTokenNodes: false, // True if HTML is allowed in token nodes - fixMisplacedChildren: true, // True if we want to use heuristics to try to fix - // problems with the tree based on HTML not handling - // self-closing tags properly - verify: { // Options to pass to verifyTree() controlling MathML verification - ...AbstractMmlNode.verifyDefaults - }, - translateEntities: true // True means translate entities in text nodes - }; + public static OPTIONS = options; /** * The DOMAdaptor for the document being processed diff --git a/ts/input/mathml/mml3/mml3.ts b/ts/input/mathml/mml3/mml3.ts index 9a2971caf4..465e877960 100644 --- a/ts/input/mathml/mml3/mml3.ts +++ b/ts/input/mathml/mml3/mml3.ts @@ -25,10 +25,23 @@ import { MathItem } from '../../../core/MathItem.js'; import { MathDocument } from '../../../core/MathDocument.js'; import { Handler } from '../../../core/Handler.js'; -import { OptionList } from '../../../util/Options.js'; import { createTransform } from './mml3-node.js'; import { MathML } from '../../mathml.js'; +/** + * The mml/mml3 option types. + */ +export type MML3_OPTIONS = { + enableMml3: boolean; +}; + +/** + * The mml/mml3 option defaults. + */ +const options: MML3_OPTIONS = { + enableMml3: true, +}; + /** * The data for a MathML prefilter. * @@ -38,7 +51,7 @@ import { MathML } from '../../mathml.js'; */ export type FILTERDATA = { math: MathItem; - document: MathDocument; + document: MathDocument & { options: MML3_OPTIONS }; data: N; }; @@ -114,9 +127,9 @@ export function Mml3Handler( /** * @override */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...handler.documentClass.OPTIONS, - enableMml3: true, + ...options, }; /** diff --git a/ts/input/tex.ts b/ts/input/tex.ts index 5b27367a27..2fb853bcce 100644 --- a/ts/input/tex.ts +++ b/ts/input/tex.ts @@ -21,12 +21,13 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { AbstractInputJax } from '../core/InputJax.js'; +import { AbstractInputJax, INPUTJAX_OPTIONS } from '../core/InputJax.js'; import { userOptions, separateOptions, OptionList } from '../util/Options.js'; import { MathDocument } from '../core/MathDocument.js'; import { MathItem } from '../core/MathItem.js'; import { MmlNode } from '../core/MmlTree/MmlNode.js'; import { MmlFactory } from '../core/MmlTree/MmlFactory.js'; +import { DOM, DOM_TYPES, N, T, D } from '../types/Types.js'; import { FindTeX } from './tex/FindTeX.js'; @@ -41,6 +42,40 @@ import { TexConstant } from './tex/TexConstants.js'; // Import base as it is the default package loaded. import './tex/base/BaseConfiguration.js'; +/*****************************************************************/ + +/** + * The TeX option types. + */ +export interface TEX_OPTIONS extends INPUTJAX_OPTIONS< + DOM, + ParseOptions +> { + FindTeX: FindTeX, T, D>; + packages: string[]; + // Maximum size of TeX string to process. + maxBuffer: number; + // Maximum number of array template substitutions (avoids infinite loop from @{\\} for example) + maxTemplateSubtitutions: number; + // math-style to use for Latin and Greek letters + mathStyle: 'TeX' | 'ISO' | 'French' | 'upright'; + formatError: (jax: TeX, T, D>, err: TexError) => MmlNode; +} + +/** + * The TeX option defaults. + */ +const options: TEX_OPTIONS = { + ...AbstractInputJax.OPTIONS, + FindTeX: null, + packages: ['base'], + maxBuffer: 5 * 1024, + maxTemplateSubtitutions: 10000, + mathStyle: 'TeX', + formatError: (jax: TeX, T, D>, err: TexError) => + jax.formatError(err), +}; + /*****************************************************************/ /* * Implements the TeX class (extends AbstractInputJax) @@ -51,32 +86,16 @@ import './tex/base/BaseConfiguration.js'; * @template T The Text node class * @template D The Document class */ -export class TeX extends AbstractInputJax { +export class TeX extends AbstractInputJax { /** * Name of input jax. - * - * @type {string} */ public static NAME: string = 'TeX'; /** * Default options for the jax. - * - * @type {OptionList} */ - public static OPTIONS: OptionList = { - ...AbstractInputJax.OPTIONS, - FindTeX: null, - packages: ['base'], - // Maximum size of TeX string to process. - maxBuffer: 5 * 1024, - // Maximum number of array template substitutions (avoids infinite loop from @{\\} for example) - maxTemplateSubtitutions: 10000, - // math-style to use for Latin and Greek letters - mathStyle: 'TeX', // one of TeX, ISO, French, or upright - formatError: (jax: TeX, err: TexError) => - jax.formatError(err), - }; + public static OPTIONS = options; /** * The FindTeX instance used for locating TeX in strings @@ -85,22 +104,16 @@ export class TeX extends AbstractInputJax { /** * The configuration of the TeX jax. - * - * @type {ParserConfiguration} */ protected configuration: ParserConfiguration; /** * The LaTeX code that is parsed. - * - * @type {string} */ protected latex: string; /** * The Math node that results from parsing. - * - * @type {MmlNode} */ protected mathNode: MmlNode; @@ -137,6 +150,11 @@ export class TeX extends AbstractInputJax { options.tags.configuration = options; } + /** + * @override + */ + public options: TEX_OPTIONS>; + /** * @override */ @@ -147,7 +165,7 @@ export class TeX extends AbstractInputJax { FindTeX.OPTIONS ); super(tex); - this.findTeX = this.options['FindTeX'] || new FindTeX(find); + this.findTeX = this.options.FindTeX || new FindTeX(find); const packages = this.options.packages; const configuration = (this.configuration = TeX.configure(packages)); const parseOptions = (this._parseOptions = new ParseOptions(configuration, [ diff --git a/ts/input/tex/FindTeX.ts b/ts/input/tex/FindTeX.ts index 792c004a37..994fe214f5 100644 --- a/ts/input/tex/FindTeX.ts +++ b/ts/input/tex/FindTeX.ts @@ -32,6 +32,33 @@ import { ProtoItem, protoItem } from '../../core/MathItem.js'; export type EndItem = [string, boolean, RegExp]; export type Delims = [string, string]; +/*****************************************************************/ + +/** + * The FindTeX option types. + */ +export type FINDTEX_OPTIONS = { + inlineMath: [string, string][]; // The start/end delimiter pairs for in-line math + displayMath: [string, string][]; // The start/end delimiter pairs for display math + processEscapes: boolean; // True allows \$ to produce a literal dollar sign + processEnvironments: boolean; // True processes \begin{xxx}...\end{xxx} outside math mode + processRefs: boolean; // True processed \ref{...} outside of math mode +}; + +/** + * The FindTeX option defaults. + */ +const options: FINDTEX_OPTIONS = { + inlineMath: [['\\(', '\\)']], + displayMath: [ + ['$$', '$$'], + ['\\[', '\\]'], + ], + processEscapes: true, + processEnvironments: true, + processRefs: true, +}; + /*****************************************************************/ /* * Implements the FindTeX class (extends AbstractFindMath) @@ -46,26 +73,9 @@ export type Delims = [string, string]; */ export class FindTeX extends AbstractFindMath { /** - * @type {OptionList} + * the default options */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { - inlineMath: [ // The start/end delimiter pairs for in-line math - // ['$', '$'], // (comment out any you don't want, or add your own, but - ['\\(', '\\)'] // be sure that you don't have an extra comma at the end) - ], - - displayMath: [ // The start/end delimiter pairs for display math - ['$$', '$$'], // (comment out any you don't want, or add your own, but - ['\\[', '\\]'] // be sure that you don't have an extra comma at the end) - ], - - processEscapes: true, // set to true to allow \$ to produce a dollar without - // starting in-line math mode - processEnvironments: true, // set to true to process \begin{xxx}...\end{xxx} outside - // of math mode, false to prevent that - processRefs: true, // set to true to process \ref{...} outside of math mode - }; + public static OPTIONS = options; /** * The regular expression for any starting delimiter diff --git a/ts/input/tex/Tags.ts b/ts/input/tex/Tags.ts index 703e9bc3d1..3364433d11 100644 --- a/ts/input/tex/Tags.ts +++ b/ts/input/tex/Tags.ts @@ -655,28 +655,44 @@ const tagsMapping = new Map([ let defaultTags = 'none'; +/** + * The TagsFactory option types. + */ +export type TAGS_OPTIONS = { + // Tagging style, used to be autonumber in v2. + tags: string; + // This specifies the side on which \tag{} macros will place the tags. + tagSide: 'left' | 'right'; + // This is the amount of indentation (from right or left) for the tags. + tagIndent: string; + // Determines whether element ID's use \label name rather than equation number + // MJ puts in an equation prefix: mjx-eqn + // When true it uses the label name XXX as mjx-eqn:XXX + // If false it uses the actual number N that is displayed: mjx-eqn:N + useLabelIds: boolean; + // Set to true in order to prevent error messages for duplicate label ids + ignoreDuplicateLabels: boolean; + // The rowalign value to use for tag cells. + tagAlign: 'top' | 'bottom' | 'center' | 'baseline' | 'axis'; +}; + +/** + * The TagsFactory option defaults. + */ +const options: TAGS_OPTIONS = { + tags: defaultTags, + tagSide: 'right', + tagIndent: '0.8em', + useLabelIds: true, + ignoreDuplicateLabels: false, + tagAlign: 'baseline', +}; + export const TagsFactory = { /** * The default options for tagging */ - OPTIONS: { - // Tagging style, used to be autonumber in v2. - tags: defaultTags, - // This specifies the side on which \tag{} macros will place the tags. - // Set to 'left' to place on the left-hand side. - tagSide: 'right', - // This is the amount of indentation (from right or left) for the tags. - tagIndent: '0.8em', - // make element ID's use \label name rather than equation number - // MJ puts in an equation prefix: mjx-eqn - // When true it uses the label name XXX as mjx-eqn:XXX - // If false it uses the actual number N that is displayed: mjx-eqn:N - useLabelIds: true, - // Set to true in order to prevent error messages for duplicate label ids - ignoreDuplicateLabels: false, - // The rowalign value to use for tag cells. - tagAlign: 'baseline', - }, + OPTIONS: options, /** * Add a tagging object. diff --git a/ts/input/tex/ams/AmsConfiguration.ts b/ts/input/tex/ams/AmsConfiguration.ts index 832f611054..d33c0fd2a2 100644 --- a/ts/input/tex/ams/AmsConfiguration.ts +++ b/ts/input/tex/ams/AmsConfiguration.ts @@ -37,6 +37,33 @@ export { COMPONENT } from './__locales__/Component.js'; */ export class AmsTags extends AbstractTags {} +/** + * The [tex]/ams option types. + */ +export type AMS_OPTIONS = { + multlineWidth: string; + ams: { + operatornamePattern: RegExp; + multlineWidth: string; + multlineIndent: string; + }; +}; + +/** + * The [tex]/ams option defaults. + */ +const options: AMS_OPTIONS = { + multlineWidth: '', + ams: { + 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. + }, +}; + +/** + * The configuration object for the `ams` package. + */ export const AmsConfiguration = Configuration.create('ams', { [ConfigurationType.HANDLER]: { [HandlerType.CHARACTER]: ['AMSmath-operatorLetter'], @@ -57,13 +84,6 @@ export const AmsConfiguration = Configuration.create('ams', { [FlalignItem.prototype.kind]: FlalignItem, }, [ConfigurationType.TAGS]: { ams: AmsTags }, - [ConfigurationType.OPTIONS]: { - multlineWidth: '', - ams: { - 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. - }, - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.CONFIG]: NewcommandConfig, }); diff --git a/ts/input/tex/amscd/AmsCdConfiguration.ts b/ts/input/tex/amscd/AmsCdConfiguration.ts index 209a58d7a8..12ddc22f4c 100644 --- a/ts/input/tex/amscd/AmsCdConfiguration.ts +++ b/ts/input/tex/amscd/AmsCdConfiguration.ts @@ -25,19 +25,40 @@ import { HandlerType, ConfigurationType } from '../HandlerTypes.js'; import { Configuration } from '../Configuration.js'; import './AmsCdMappings.js'; +/** + * The [tex]/amscd option types. + */ +export type AMSCD_OPTIONS = { + amscd: { + colspace: string; + rowspace: string; + harrowsize: string; + varrowsize: string; + hideHorizontalLabels: boolean; + }; +}; + +/** + * The [tex]/amscd options defaults. + */ +const options: AMSCD_OPTIONS = { + amscd: { + colspace: '5pt', + rowspace: '5pt', + harrowsize: '2.75em', + varrowsize: '1.75em', + hideHorizontalLabels: false, + }, +}; + +/** + * The configuration object for the `amscd` package. + */ export const AmsCdConfiguration = Configuration.create('amscd', { [ConfigurationType.HANDLER]: { [HandlerType.CHARACTER]: ['amscd_special'], [HandlerType.MACRO]: ['amscd_macros'], [HandlerType.ENVIRONMENT]: ['amscd_environment'], }, - [ConfigurationType.OPTIONS]: { - amscd: { - colspace: '5pt', - rowspace: '5pt', - harrowsize: '2.75em', - varrowsize: '1.75em', - hideHorizontalLabels: false, - }, - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/autoload/AutoloadConfiguration.ts b/ts/input/tex/autoload/AutoloadConfiguration.ts index 1cb6e97ae0..60ccd346e9 100644 --- a/ts/input/tex/autoload/AutoloadConfiguration.ts +++ b/ts/input/tex/autoload/AutoloadConfiguration.ts @@ -33,7 +33,11 @@ import { RequireConfiguration, } from '../require/RequireConfiguration.js'; import { Package } from '../../../components/package.js'; -import { expandable, defaultOptions } from '../../../util/Options.js'; +import { + expandable, + EXPANDABLE_LIST_OF, + defaultOptions, +} from '../../../util/Options.js'; /** * Autoload an extension when the first macro for it is encountered @@ -145,64 +149,81 @@ const AutoloadMacros = new CommandMap('autoload-macros', {}); const AutoloadEnvironments = new CommandMap('autoload-environments', {}); /** - * The configuration object for configmacros + * An autoload specification. + */ +export type AUTOLOAD_DEFS = EXPANDABLE_LIST_OF; + +/** + * The [tex]/autoload option types. + */ +export type AUTOLOAD_OPTIONS = { + autoload: AUTOLOAD_DEFS; +}; + +/** + * The [tex]/autoload option defaults. + */ +const options: AUTOLOAD_OPTIONS = { + // + // These are the extension names and the macros and environments they contain. + // The format is [macros...] or [[macros...], [environments...]] + // You can prevent one from being autoloaded by setting + // it to [] in the options when the TeX input jax is created. + // You can include the prefix if it is not the default one from require + // + autoload: expandable({ + action: ['toggle', 'mathtip', 'texttip'], + amscd: [[], ['CD']], + bbox: ['bbox'], + boldsymbol: ['boldsymbol'], + braket: [ + 'bra', + 'ket', + 'braket', + 'set', + 'Bra', + 'Ket', + 'Braket', + 'Set', + 'ketbra', + 'Ketbra', + ], + bussproofs: [[], ['prooftree']], + cancel: ['cancel', 'bcancel', 'xcancel', 'cancelto'], + color: ['color', 'definecolor', 'textcolor', 'colorbox', 'fcolorbox'], + enclose: ['enclose'], + extpfeil: [ + 'xtwoheadrightarrow', + 'xtwoheadleftarrow', + 'xmapsto', + 'xlongequal', + 'xtofrom', + 'Newextarrow', + ], + html: ['data', 'href', 'class', 'style', 'cssId'], + mhchem: ['ce', 'pu'], + newcommand: [ + 'newcommand', + 'renewcommand', + 'newenvironment', + 'renewenvironment', + 'def', + 'let', + ], + unicode: ['unicode', 'U', 'char'], + verb: ['verb'], + }), +}; + +/** + * The configuration object for the `autoload` component. */ export const AutoloadConfiguration = Configuration.create('autoload', { [ConfigurationType.HANDLER]: { [HandlerType.MACRO]: ['autoload-macros'], [HandlerType.ENVIRONMENT]: ['autoload-environments'], }, - [ConfigurationType.OPTIONS]: { - // - // These are the extension names and the macros and environments they contain. - // The format is [macros...] or [[macros...], [environments...]] - // You can prevent one from being autoloaded by setting - // it to [] in the options when the TeX input jax is created. - // You can include the prefix if it is not the default one from require - // - autoload: expandable({ - action: ['toggle', 'mathtip', 'texttip'], - amscd: [[], ['CD']], - bbox: ['bbox'], - boldsymbol: ['boldsymbol'], - braket: [ - 'bra', - 'ket', - 'braket', - 'set', - 'Bra', - 'Ket', - 'Braket', - 'Set', - 'ketbra', - 'Ketbra', - ], - bussproofs: [[], ['prooftree']], - cancel: ['cancel', 'bcancel', 'xcancel', 'cancelto'], - color: ['color', 'definecolor', 'textcolor', 'colorbox', 'fcolorbox'], - enclose: ['enclose'], - extpfeil: [ - 'xtwoheadrightarrow', - 'xtwoheadleftarrow', - 'xmapsto', - 'xlongequal', - 'xtofrom', - 'Newextarrow', - ], - html: ['data', 'href', 'class', 'style', 'cssId'], - mhchem: ['ce', 'pu'], - newcommand: [ - 'newcommand', - 'renewcommand', - 'newenvironment', - 'renewenvironment', - 'def', - 'let', - ], - unicode: ['unicode', 'U', 'char'], - verb: ['verb'], - }), - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.CONFIG]: configAutoload, [ConfigurationType.INIT]: initAutoload, [ConfigurationType.PRIORITY]: 10, diff --git a/ts/input/tex/base/BaseConfiguration.ts b/ts/input/tex/base/BaseConfiguration.ts index fb3ef2b680..b6e9e8a385 100644 --- a/ts/input/tex/base/BaseConfiguration.ts +++ b/ts/input/tex/base/BaseConfiguration.ts @@ -152,9 +152,37 @@ function filterNonscript({ data }: { data: ParseOptions }) { export class BaseTags extends AbstractTags {} /** - * The base configuration. - * - * @type {Configuration} + * The base option types. + */ +export type BASE_OPTIONS = { + maxMacros: number; // Maximum number of macro substitutions to process allowed + digits: string; // Backward compatibility // FIXME: remove in a later version + numberPattern: RegExp; // Pattern for a number + initialDigit: RegExp; // Pattern for initial digit or decimal point for a number + identifierPattern: RegExp; // Pattern for multiLetterIdentifiers in \mathrm, etc. + initialLetter: RegExp; // Pattern for initial letter in identifiers + baseURL: string; // The base URL for additional loads +}; + +/** + * The base option defaults. + */ +const options: BASE_OPTIONS = { + maxMacros: 1000, // // Maximum number of macro substitutions to process allowed + digits: '', // // backward compatibility // FIXME: remove in a later version + numberPattern: /^(?:[0-9]+(?:\{,\}[0-9]{3})*(?:\.[0-9]*)?|\.[0-9]+)/, + initialDigit: /[0-9.,]/, // // pattern for initial digit or decimal point for a number + identifierPattern: /^[a-zA-Z]+/, // pattern for multiLetterIdentifiers in \mathrm, etc. + initialLetter: /[a-zA-Z]/, // // pettern for initial letter in identifiers + baseURL: + !context.document || + context.document.getElementsByTagName('base').length === 0 + ? '' + : String(context.document.location).replace(/#.*$/, ''), +}; + +/** + * The configuration for the `base` package. */ export const BaseConfiguration: Configuration = Configuration.create('base', { [ConfigurationType.CONFIG]: function (config, jax) { @@ -217,19 +245,7 @@ export const BaseConfiguration: Configuration = Configuration.create('base', { [bitem.EquationItem.prototype.kind]: bitem.EquationItem, [bitem.MstyleItem.prototype.kind]: bitem.MstyleItem, }, - [ConfigurationType.OPTIONS]: { - maxMacros: 1000, // // Maximum number of macro substitutions to process allowed - digits: '', // // backward compatibility // FIXME: remove in a later version - numberPattern: /^(?:[0-9]+(?:\{,\}[0-9]{3})*(?:\.[0-9]*)?|\.[0-9]+)/, - initialDigit: /[0-9.,]/, // // pattern for initial digit or decimal point for a number - identifierPattern: /^[a-zA-Z]+/, // pattern for multiLetterIdentifiers in \mathrm, etc. - initialLetter: /[a-zA-Z]/, // // pettern for initial letter in identifiers - baseURL: - !context.document || - context.document.getElementsByTagName('base').length === 0 - ? '' - : String(context.document.location).replace(/#.*$/, ''), - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.TAGS]: { base: BaseTags, }, diff --git a/ts/input/tex/bbm/BbmConfiguration.ts b/ts/input/tex/bbm/BbmConfiguration.ts index de78e2bf2c..1901ea5cfd 100644 --- a/ts/input/tex/bbm/BbmConfiguration.ts +++ b/ts/input/tex/bbm/BbmConfiguration.ts @@ -55,16 +55,30 @@ new CommandMap('bbm', { mathbbmtt: [BbmMethods.MathFont, '-bbm-monospace'], }); -// -// Define the package configuration, including switch for sans serif. -// +/** + * The [tex]/bbm option types. + */ +export type BBM_OPTIONS = { + bbm: { + bold: boolean; + }; +}; + +/** + * The [tex]/bbom option defaults. + */ +const options: BBM_OPTIONS = { + bbm: { + bold: false, + }, +}; + +/** + * The configuration object for the `bbm` package. + */ export const BbmConfiguration = Configuration.create('bbm', { [ConfigurationType.HANDLER]: { [HandlerType.MACRO]: ['bbm'], }, - [ConfigurationType.OPTIONS]: { - bbm: { - bold: false, - }, - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/bboldx/BboldxConfiguration.ts b/ts/input/tex/bboldx/BboldxConfiguration.ts index 3f3014f62d..8af38b346f 100644 --- a/ts/input/tex/bboldx/BboldxConfiguration.ts +++ b/ts/input/tex/bboldx/BboldxConfiguration.ts @@ -42,10 +42,29 @@ Configuration.create('text-bboldx', { }, }); -// -// Define the package configuration, including switches for light and always -// bold bb. -// +/** + * The option types. + */ +export type BBOLDX_OPTIONS = { + bboldx: { + bfbb: boolean; + light: boolean; + }; +}; + +/** + * The default options. + */ +const options: BBOLDX_OPTIONS = { + bboldx: { + bfbb: false, + light: false, + }, +}; + +/** + * The configuration object for the `bboldx` package. + */ export const BboldxConfiguration = Configuration.create('bboldx', { [ConfigurationType.HANDLER]: { [HandlerType.MACRO]: [ @@ -56,18 +75,13 @@ export const BboldxConfiguration = Configuration.create('bboldx', { 'bboldx-delimiterBold', ], }, - [ConfigurationType.OPTIONS]: { - bboldx: { - bfbb: false, - light: false, - }, - }, - config(_config, jax) { + [ConfigurationType.OPTIONS]: options, + [ConfigurationType.CONFIG]: (_config, jax) => { const textmacros = jax.parseOptions.packageData.get('textmacros'); if (textmacros) { textmacros.parseOptions.options.textmacros.packages.push('text-bboldx'); textmacros.textConf.add('text-bboldx', jax, {}); } }, - priority: 3, // load before base, since we override \mathbb + [ConfigurationType.PRIORITY]: 3, // load before base, since we override \mathbb }); diff --git a/ts/input/tex/begingroup/BegingroupConfiguration.ts b/ts/input/tex/begingroup/BegingroupConfiguration.ts index d2aaa6e33c..cf0f7b126b 100644 --- a/ts/input/tex/begingroup/BegingroupConfiguration.ts +++ b/ts/input/tex/begingroup/BegingroupConfiguration.ts @@ -41,7 +41,31 @@ new CommandMap('begingroup', { }); /** - * Create the begingroup configuration. + * The [tex]/begingroup options types. + */ +export type BEGINGROUP_OPTIONS = { + begingroup: { + allowGlobal: string[]; + }; +}; + +/** + * The [tex]/begingroup option defaults. + */ +const options: BEGINGROUP_OPTIONS = { + begingroup: { + allowGlobal: [ + 'let', + 'def', + 'newcommand', + 'DeclareMathOperator', + 'Newextarrow', + ], + }, +}; + +/** + * The configuration object for the `begingroup` package. */ export const BegingroupConfiguration = Configuration.create('begingroup', { [ConfigurationType.HANDLER]: { @@ -55,17 +79,7 @@ export const BegingroupConfiguration = Configuration.create('begingroup', { stack: new BegingroupStack(jax.parseOptions), }); }, - [ConfigurationType.OPTIONS]: { - begingroup: { - allowGlobal: [ - 'let', - 'def', - 'newcommand', - 'DeclareMathOperator', - 'Newextarrow', - ], - }, - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.PREPROCESSORS]: [ ({ data: parser }) => begingroupStack(parser).remove(), ], diff --git a/ts/input/tex/color/ColorConfiguration.ts b/ts/input/tex/color/ColorConfiguration.ts index 4b76f3c0eb..9d13dfa589 100644 --- a/ts/input/tex/color/ColorConfiguration.ts +++ b/ts/input/tex/color/ColorConfiguration.ts @@ -43,8 +43,8 @@ new CommandMap('color', { /** * Config method for Color package. * - * @param {Configuration} _config The current configuration. - * @param {TeX} jax The TeX jax having that configuration + * @param {Configuration} _config The current configuration. + * @param {TeX} jax The TeX jax having that configuration */ const config = function ( _config: ParserConfiguration, @@ -54,17 +54,32 @@ const config = function ( }; /** - * The configuration for the color macros + * The [tex]/color option types. + */ +export type COLOR_OPTIONS = { + color: { + padding: string; + borderWidth: string; + }; +}; + +/** + * The [tex]/color option defaults. + */ +const options: COLOR_OPTIONS = { + color: { + padding: '5px', + borderWidth: '2px', + }, +}; + +/** + * The configuration object for the `color` package. */ export const ColorConfiguration = Configuration.create('color', { [ConfigurationType.HANDLER]: { [HandlerType.MACRO]: ['color'], }, - [ConfigurationType.OPTIONS]: { - color: { - padding: '5px', - borderWidth: '2px', - }, - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.CONFIG]: config, }); diff --git a/ts/input/tex/configmacros/ConfigMacrosConfiguration.ts b/ts/input/tex/configmacros/ConfigMacrosConfiguration.ts index c8aad8ae0b..ebe632a67b 100644 --- a/ts/input/tex/configmacros/ConfigMacrosConfiguration.ts +++ b/ts/input/tex/configmacros/ConfigMacrosConfiguration.ts @@ -23,7 +23,7 @@ import { HandlerType, ConfigurationType } from '../HandlerTypes.js'; import { Configuration, ParserConfiguration } from '../Configuration.js'; -import { expandable } from '../../../util/Options.js'; +import { expandable, EXPANDABLE_LIST_OF } from '../../../util/Options.js'; import { CommandMap, EnvironmentMap, MacroMap } from '../TokenMap.js'; import ParseMethods from '../ParseMethods.js'; import { Macro } from '../Token.js'; @@ -145,7 +145,32 @@ function configEnvironments(jax: TEX) { } /** - * The configuration object for configmacros + * The definition types for macros, environments, and active characters. + */ +export type MACRO_DEF = string | [string, number?, (string | string[])?]; +export type ENVIRONMENT_DEF = [string, string, number?, string?]; +export type ACTIVE_DEF = string | [string, number]; + +/** + * The [tex]/configmacros option types. + */ +export type CONFIGMACROS_OPTIONS = { + active: EXPANDABLE_LIST_OF; + macros: EXPANDABLE_LIST_OF; + environments: EXPANDABLE_LIST_OF; +}; + +/** + * The [tex]/configmacros option defaults. + */ +const options: CONFIGMACROS_OPTIONS = { + active: expandable({}), + macros: expandable({}), + environments: expandable({}), +}; + +/** + * The configuration object for the `configmacros` package. */ export const ConfigMacrosConfiguration = Configuration.create('configmacros', { [ConfigurationType.INIT]: configmacrosInit, @@ -153,9 +178,5 @@ export const ConfigMacrosConfiguration = Configuration.create('configmacros', { [ConfigurationType.ITEMS]: { [BeginEnvItem.prototype.kind]: BeginEnvItem, }, - [ConfigurationType.OPTIONS]: { - active: expandable({}), - macros: expandable({}), - environments: expandable({}), - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/dsfont/DsfontConfiguration.ts b/ts/input/tex/dsfont/DsfontConfiguration.ts index b39a70429a..5c874bc490 100644 --- a/ts/input/tex/dsfont/DsfontConfiguration.ts +++ b/ts/input/tex/dsfont/DsfontConfiguration.ts @@ -41,20 +41,37 @@ function ChooseFont(parser: TexParser, name: string) { ); } +/** + * The dsfont command map. + */ new CommandMap('dsfont', { mathds: ChooseFont, }); -// -// Define the package configuration, including switch for sans serif. -// +/** + * The [tex]/dsfont option types. + */ +export type DSFONT_OPTIONS = { + dsfont: { + sans: boolean; + }; +}; + +/** + * The [tex]/dsfonts option defaults. + */ +const options: DSFONT_OPTIONS = { + dsfont: { + sans: false, + }, +}; + +/** + * The configuration object for the `dsfont` package. + */ export const DsfontConfiguration = Configuration.create('dsfont', { [ConfigurationType.HANDLER]: { [HandlerType.MACRO]: ['dsfont'], }, - [ConfigurationType.OPTIONS]: { - dsfont: { - sans: false, - }, - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/mathtools/MathtoolsConfiguration.ts b/ts/input/tex/mathtools/MathtoolsConfiguration.ts index e01f494fa9..1cacef0120 100644 --- a/ts/input/tex/mathtools/MathtoolsConfiguration.ts +++ b/ts/input/tex/mathtools/MathtoolsConfiguration.ts @@ -26,7 +26,7 @@ import { Configuration } from '../Configuration.js'; import { Macro } from '../Token.js'; import { CommandMap } from '../TokenMap.js'; import NodeUtil from '../NodeUtil.js'; -import { expandable } from '../../../util/Options.js'; +import { expandable, EXPANDABLE_LIST_OF } from '../../../util/Options.js'; import { ParserConfiguration } from '../Configuration.js'; import { TeX } from '../../tex.js'; import ParseOptions from '../ParseOptions.js'; @@ -88,8 +88,82 @@ export function fixPrescripts({ data }: { data: ParseOptions }) { } } +type PAIRED_DELIM_DEFS = EXPANDABLE_LIST_OF< + | [left: string, right: string] + | [left: string, right: string, body: string, argcount: number] + | [ + left: string, + right: string, + body: string, + argcount: number, + pre: string, + post: string, + ] +>; + +type TAGFORM_DEFS = EXPANDABLE_LIST_OF< + [left: string, right: string, format: string] +>; + +/** + * The [tex]/mathtools option types. + */ +export type MATHTOOLS_OPTIONS = { + mathtools: { + 'multlined-gap': string; // Horizontal space for multlined environments + 'multlined-pos': string; // Default alignment for multlined environments + 'multlined-width': string; // Default width for mutlined environments + 'firstline-afterskip': string; // Space for first line of multlined (overrides multlined-gap) + 'lastline-preskip': string; // Space for last line of multlined (overrides multlined-gap) + 'smallmatrix-align': string; // Default alignment for smallmatrix environments + shortvdotsadjustabove: string; // Space to remove above \shortvdots + shortvdotsadjustbelow: string; // Space to remove below \shortvdots + centercolon: boolean; // True to have colon automatically centered + 'centercolon-offset': string; // Vertical adjustment for centered colons + 'thincolon-dx': string; // Horizontal adjustment for thin colons (e.g., \coloneqq) + 'thincolon-dw': string; // Width adjustment for thin colons + 'use-unicode': boolean; // True to use unicode characters rather than multi-character + // version for \coloneqq, etc., when possible + legacycolonsymbols: boolean; // True to use legacy \coloneq, etc. + 'prescript-sub-format': string; // Format for \prescript subscript + 'prescript-sup-format': string; // Format for \prescript superscript + 'prescript-arg-format': string; // Format for \prescript base + 'allow-mathtoolsset': boolean; // True to allow \mathtoolsset to change settings + pairedDelimiters: PAIRED_DELIM_DEFS; // Predefined paired delimiters + tagforms: TAGFORM_DEFS; // Tag form definitions + }; +}; + /** - * The configuration for the mathtools package + * The [tex]/mathtools option defaults. + */ +const options: MATHTOOLS_OPTIONS = { + mathtools: { + 'multlined-gap': '1em', + 'multlined-pos': 'c', + 'multlined-width': '', + 'firstline-afterskip': '', + 'lastline-preskip': '', + 'smallmatrix-align': 'c', + shortvdotsadjustabove: '.2em', + shortvdotsadjustbelow: '.2em', + centercolon: false, + 'centercolon-offset': '.04em', + 'thincolon-dx': '-.04em', + 'thincolon-dw': '-.08em', + 'use-unicode': false, + legacycolonsymbols: false, + 'prescript-sub-format': '', + 'prescript-sup-format': '', + 'prescript-arg-format': '', + 'allow-mathtoolsset': true, + pairedDelimiters: expandable({}), + tagforms: expandable({}), + }, +}; + +/** + * The configuration object for the `mathtools` package. */ export const MathtoolsConfiguration = Configuration.create('mathtools', { [ConfigurationType.HANDLER]: { @@ -103,32 +177,5 @@ export const MathtoolsConfiguration = Configuration.create('mathtools', { }, [ConfigurationType.CONFIG]: configMathtools, [ConfigurationType.POSTPROCESSORS]: [[fixPrescripts, -6]], - /* prettier-ignore */ - [ConfigurationType.OPTIONS]: { - mathtools: { - 'multlined-gap': '1em', // horizontal space for multlined environments - 'multlined-pos': 'c', // default alignment for multlined environments - 'multlined-width': '', // default width for mutlined environments - 'firstline-afterskip': '', // space for first line of multlined (overrides multlined-gap) - 'lastline-preskip': '', // space for last line of multlined (overrides multlined-gap) - 'smallmatrix-align': 'c', // default alignment for smallmatrix environments - 'shortvdotsadjustabove': '.2em', // space to remove above \shortvdots - 'shortvdotsadjustbelow': '.2em', // space to remove below \shortvdots - 'centercolon': false, // true to have colon automatically centered - 'centercolon-offset': '.04em', // vertical adjustment for centered colons - 'thincolon-dx': '-.04em', // horizontal adjustment for thin colons (e.g., \coloneqq) - 'thincolon-dw': '-.08em', // width adjustment for thin colons - 'use-unicode': false, // true to use unicode characters rather than multi-character - // version for \coloneqq, etc., when possible - 'legacycolonsymbols': false, // true to use legacy \coloneq, etc. - 'prescript-sub-format': '', // format for \prescript subscript - 'prescript-sup-format': '', // format for \prescript superscript - 'prescript-arg-format': '', // format for \prescript base - 'allow-mathtoolsset': true, // true to allow \mathtoolsset to change settings - pairedDelimiters: expandable({}), // predefined paired delimiters - // name: [left, right, body, argcount, pre, post] - tagforms: expandable({}), // tag form definitions - // name: [left, right, format] - } - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/newcommand/NewcommandConfiguration.ts b/ts/input/tex/newcommand/NewcommandConfiguration.ts index 9530712dc2..95004746c8 100644 --- a/ts/input/tex/newcommand/NewcommandConfiguration.ts +++ b/ts/input/tex/newcommand/NewcommandConfiguration.ts @@ -76,6 +76,25 @@ export function NewcommandConfig( ); } +/** + * The [tex]/newcommand option types. + */ +export type NEWCOMMAND_OPTIONS = { + maxMacros: number; + protectedMacros: string[]; +}; + +/** + * The [tex]/newcommand option defaults. + */ +const options: NEWCOMMAND_OPTIONS = { + maxMacros: 1000, + protectedMacros: ['begingroupSandbox'], +}; + +/** + * The configuration object for the `newcommand` package. + */ export const NewcommandConfiguration = Configuration.create('newcommand', { [ConfigurationType.HANDLER]: { macro: ['Newcommand-macros'], @@ -83,9 +102,6 @@ export const NewcommandConfiguration = Configuration.create('newcommand', { [ConfigurationType.ITEMS]: { [BeginEnvItem.prototype.kind]: BeginEnvItem, }, - [ConfigurationType.OPTIONS]: { - maxMacros: 1000, - protectedMacros: ['begingroupSandbox'], - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.CONFIG]: NewcommandConfig, }); diff --git a/ts/input/tex/noundefined/NoUndefinedConfiguration.ts b/ts/input/tex/noundefined/NoUndefinedConfiguration.ts index dc3ccb86bc..bdbc58a9ed 100644 --- a/ts/input/tex/noundefined/NoUndefinedConfiguration.ts +++ b/ts/input/tex/noundefined/NoUndefinedConfiguration.ts @@ -44,14 +44,33 @@ function noUndefined(parser: TexParser, name: string) { parser.Push(parser.create('node', 'mtext', [], def, textNode)); } +/** + * The [tex]/noundefined option types. + */ +export type NOUNDEFINED_OPTIONS = { + noundefined: { + color: string; + background: string; + size: string; + }; +}; + +/** + * The [tex]/noundefined t option defaults. + */ +const options: NOUNDEFINED_OPTIONS = { + noundefined: { + color: 'red', + background: '', + size: '', + }, +}; + +/** + * The configuration object for the `noundefined` package. + */ export const NoUndefinedConfiguration = Configuration.create('noundefined', { [ConfigurationType.FALLBACK]: { [HandlerType.MACRO]: noUndefined }, - [ConfigurationType.OPTIONS]: { - noundefined: { - color: 'red', - background: '', - size: '', - }, - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.PRIORITY]: 3, }); diff --git a/ts/input/tex/physics/PhysicsConfiguration.ts b/ts/input/tex/physics/PhysicsConfiguration.ts index dde9672e6f..24e1e02deb 100644 --- a/ts/input/tex/physics/PhysicsConfiguration.ts +++ b/ts/input/tex/physics/PhysicsConfiguration.ts @@ -27,6 +27,29 @@ import { AutoOpen } from './PhysicsItems.js'; import './PhysicsMappings.js'; export { COMPONENT } from './__locales__/Component.js'; +/** + * The [tex]/physics option types. + */ +export type PHYSICS_OPTIONS = { + physics: { + italicdiff: boolean; + arrowdel: boolean; + }; +}; + +/** + * The [tex]/physics option defaults. + */ +const options: PHYSICS_OPTIONS = { + physics: { + italicdiff: false, + arrowdel: false, + }, +}; + +/** + * The configuration object for the `physics` package. + */ export const PhysicsConfiguration = Configuration.create('physics', { [ConfigurationType.HANDLER]: { macro: [ @@ -46,10 +69,5 @@ export const PhysicsConfiguration = Configuration.create('physics', { [ConfigurationType.ITEMS]: { [AutoOpen.prototype.kind]: AutoOpen, }, - [ConfigurationType.OPTIONS]: { - physics: { - italicdiff: false, - arrowdel: false, - }, - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/require/RequireConfiguration.ts b/ts/input/tex/require/RequireConfiguration.ts index 673e687d54..8b251d9073 100644 --- a/ts/input/tex/require/RequireConfiguration.ts +++ b/ts/input/tex/require/RequireConfiguration.ts @@ -37,7 +37,7 @@ import { MathJax } from '../../../components/startup.js'; import { Package } from '../../../components/package.js'; import { Loader, CONFIG as LOADERCONFIG } from '../../../components/loader.js'; import { mathjax } from '../../../mathjax.js'; -import { expandable } from '../../../util/Options.js'; +import { expandable, EXPANDABLE_LIST_OF } from '../../../util/Options.js'; import { MenuMathDocument } from '../../../ui/menu/MenuHandler.js'; import { Locale } from '../../../util/Locale.js'; @@ -237,15 +237,33 @@ export const RequireMethods: { [key: string]: ParseMethod } = { }; /** - * The options for the require extension + * The [tex]/require option types. */ -export const options = { +export type REQUIRE_OPTIONS = { require: { // // Specifies which extensions can/can't be required. // The keys are the names of extensions, and the value is true // if the extension can be required, and false if it can't // + allow: EXPANDABLE_LIST_OF; + // + // The default allow value if the extension isn't in the list above + // + defaultAllow: boolean; + // + // The path prefix to use for exensions: 'tex' means use '[tex]/' + // before the extension name. + // + prefix: string; + }; +}; + +/** + * The [tex]/require option defaults. + */ +const options: REQUIRE_OPTIONS = { + require: { allow: expandable({ base: false, autoload: false, @@ -254,14 +272,7 @@ export const options = { setoptions: false, texhtml: false, }), - // - // The default allow value if the extension isn't in the list above - // defaultAllow: true, - // - // The path prefix to use for exensions: 'tex' means use '[tex]/' - // before the extension name. - // prefix: 'tex', }, }; @@ -272,7 +283,7 @@ export const options = { new CommandMap('require', { require: RequireMethods.Require }); /** - * The configuration for the \require macro + * The configuration object for the `require` package. */ export const RequireConfiguration = Configuration.create('require', { [ConfigurationType.HANDLER]: { diff --git a/ts/input/tex/setoptions/SetOptionsConfiguration.ts b/ts/input/tex/setoptions/SetOptionsConfiguration.ts index b33595ed1f..96827d5919 100644 --- a/ts/input/tex/setoptions/SetOptionsConfiguration.ts +++ b/ts/input/tex/setoptions/SetOptionsConfiguration.ts @@ -34,7 +34,11 @@ import { texError } from '../TexError.js'; import { ParseUtil } from '../ParseUtil.js'; import { Macro } from '../Token.js'; import BaseMethods from '../base/BaseMethods.js'; -import { expandable, isObject } from '../../../util/Options.js'; +import { + expandable, + EXPANDABLE_LIST_OF, + isObject, +} from '../../../util/Options.js'; import { PrioritizedList } from '../../../util/PrioritizedList.js'; import { COMPONENT } from './__locales__/Component.js'; export { COMPONENT }; @@ -170,42 +174,67 @@ function setoptionsConfig( } } +type ALLOW_LIST = EXPANDABLE_LIST_OF< + boolean | { [option: string]: boolean | null } +>; + +/** + * The [tex]/setoptions option types. + */ +export type SETOPTIONS_OPTIONS = { + setoptions: { + filterPackage: typeof SetOptionsUtil.filterPackage; // Filter for whether a package can be configured + filterOption: typeof SetOptionsUtil.filterOption; // Filter for whether an option can be set + filterValue: typeof SetOptionsUtil.filterValue; // Filter for the value to assign to an option + allowPackageDefault: boolean; // Default for allowing packages when not explicitly set in allowOptions + allowOptionsDefault: boolean; // Default for allowing option that isn't explicitly set in allowOptions + allowOptions: ALLOW_LIST; // List of packages to allow/disallow; and their options to allow/disallow + }; +}; + +/** + * The [tex]/setoptions option defaults. + */ +const options: SETOPTIONS_OPTIONS = { + setoptions: { + filterPackage: SetOptionsUtil.filterPackage, + filterOption: SetOptionsUtil.filterOption, + filterValue: SetOptionsUtil.filterValue, + allowPackageDefault: true, + allowOptionsDefault: true, + allowOptions: expandable({ + // + // top-level tex items can be set, but not these + // (that leaves digits and the tagging options) + // + tex: { + FindTeX: false, + formatError: false, + package: false, + baseURL: false, + tags: false, + maxBuffer: false, + maxMaxros: false, + macros: false, + environments: false, + }, + // + // These packages can't be configured at all + // + setoptions: false, + autoload: false, + require: false, + configmacros: false, + tagformat: false, + }), + }, +}; + +/** + * The configuration object for the `setoptions` package. + */ export const SetOptionsConfiguration = Configuration.create('setoptions', { [ConfigurationType.CONFIG]: setoptionsConfig, [ConfigurationType.PRIORITY]: 3, // must be less than the priority of the require package (which is 5). - /* prettier-ignore */ - [ConfigurationType.OPTIONS]: { - setoptions: { - filterPackage: SetOptionsUtil.filterPackage, // filter for whether a package can be configured - filterOption: SetOptionsUtil.filterOption, // filter for whether an option can be set - filterValue: SetOptionsUtil.filterValue, // filter for the value to assign to an option - allowPackageDefault: true, // default for allowing packages when not explicitly set in allowOptions - allowOptionsDefault: true, // default for allowing option that isn't explicitly set in allowOptions - allowOptions: expandable({ // list of packages to allow/disallow, and their options to allow/disallow - // - // top-level tex items can be set, but not these - // (that leaves digits and the tagging options) - // - tex: { - FindTeX: false, - formatError: false, - package: false, - baseURL: false, - tags: false, - maxBuffer: false, - maxMaxros: false, - macros: false, - environments: false - }, - // - // These packages can't be configured at all - // - setoptions: false, - autoload: false, - require: false, - configmacros: false, - tagformat: false - }) - } - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/tagformat/TagFormatConfiguration.ts b/ts/input/tex/tagformat/TagFormatConfiguration.ts index 6694e1392d..84f9180695 100644 --- a/ts/input/tex/tagformat/TagFormatConfiguration.ts +++ b/ts/input/tex/tagformat/TagFormatConfiguration.ts @@ -118,17 +118,35 @@ export function tagformatConfig( } /** - * The configuration object for configTags + * The [tex]/tagformat option types. + */ +export type TAGFORMAT_OPTIONS = { + tagformat: { + number: (n: number) => string; + tag: (tag: string) => string | [string, string, string]; + ref: string; // '' means use the tag function + id: (id: string) => string; + url: (id: string, base: string) => string; + }; +}; + +/** + * The [tex]/tagformat option defaults. + */ +const options: TAGFORMAT_OPTIONS = { + tagformat: { + number: (n: number) => n.toString(), + 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), + }, +}; + +/** + * The configuration object for the `configTags` package; */ export const TagFormatConfiguration = Configuration.create('tagformat', { [ConfigurationType.CONFIG]: [tagformatConfig, 10], - [ConfigurationType.OPTIONS]: { - tagformat: { - number: (n: number) => n.toString(), - 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), - }, - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/texhtml/TexHtmlConfiguration.ts b/ts/input/tex/texhtml/TexHtmlConfiguration.ts index adb05ce32c..4a8ae52f45 100644 --- a/ts/input/tex/texhtml/TexHtmlConfiguration.ts +++ b/ts/input/tex/texhtml/TexHtmlConfiguration.ts @@ -85,13 +85,28 @@ export const HtmlNodeMethods: { [key: string]: ParseMethod } = { new MacroMap('tex-html', { '<': HtmlNodeMethods.TexHTML }); +/** + * The [tex]/texhtml option types. + */ +export type TEXHTML_OPTIONS = { + allowTexHTML: boolean; +}; + +/** + * The [tex]/texhtml option defaults. + */ +const options: TEXHTML_OPTIONS = { + allowTexHTML: false, // Must turn this on explicitly, since it allows unfiltered HTML insertion. +}; + +/** + * The configuration object for the `texhtml` package. + */ export const TexHtmlConfiguration = Configuration.create('texhtml', { [ConfigurationType.HANDLER]: { [HandlerType.CHARACTER]: ['tex-html'], }, - [ConfigurationType.OPTIONS]: { - allowTexHTML: false, // Must turn this on explicitly, since it allows unfiltered HTML insertion. - }, + [ConfigurationType.OPTIONS]: options, [ConfigurationType.CONFIG]: () => { if (HTMLDomStrings) { // diff --git a/ts/input/tex/textmacros/TextMacrosConfiguration.ts b/ts/input/tex/textmacros/TextMacrosConfiguration.ts index 6e3abe5508..d05e6aebc9 100644 --- a/ts/input/tex/textmacros/TextMacrosConfiguration.ts +++ b/ts/input/tex/textmacros/TextMacrosConfiguration.ts @@ -107,9 +107,27 @@ function internalMath( ]; } -// -// The textmacros package configuration -// +/** + * The [tex]/textmacros option types. + */ +export type TEXTMACROS_OPTIONS = { + textmacros: { + packages: string[]; // textmacro packages to load + }; +}; + +/** + * The [tex]textmacros option defaults. + */ +const options: TEXTMACROS_OPTIONS = { + textmacros: { + packages: ['text-base'], + }, +}; + +/** + * The configuration object for the `textmacros` package. + */ export const TextMacrosConfiguration = Configuration.create('textmacros', { [ConfigurationType.PRIORITY]: 1, /** @@ -161,9 +179,5 @@ export const TextMacrosConfiguration = Configuration.create('textmacros', { config.parseOptions.clear(); }, ], - [ConfigurationType.OPTIONS]: { - textmacros: { - packages: ['text-base'], // textmacro packages to load - }, - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/input/tex/units/UnitsConfiguration.ts b/ts/input/tex/units/UnitsConfiguration.ts index 88ff55b882..e07e23de3b 100644 --- a/ts/input/tex/units/UnitsConfiguration.ts +++ b/ts/input/tex/units/UnitsConfiguration.ts @@ -96,12 +96,30 @@ new CommandMap('units', { nicefrac: UnitsMethods.NiceFrac, }); +/** + * The [tex]/units option types. + */ +export type UNITS_OPTIONS = { + units: { + loose: boolean; + ugly: boolean; + }; +}; + +/** + * The [tex]/units option defaults. + */ +const options: UNITS_OPTIONS = { + units: { + loose: false, + ugly: false, + }, +}; + +/** + * The configuration object for the `units` package. + */ export const UnitsConfiguration = Configuration.create('units', { [ConfigurationType.HANDLER]: { [HandlerType.MACRO]: ['units'] }, - [ConfigurationType.OPTIONS]: { - units: { - loose: false, - ugly: false, - }, - }, + [ConfigurationType.OPTIONS]: options, }); diff --git a/ts/output/chtml.ts b/ts/output/chtml.ts index 9ea19d5655..e20f080fff 100644 --- a/ts/output/chtml.ts +++ b/ts/output/chtml.ts @@ -21,8 +21,8 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { CommonOutputJax } from './common.js'; -import { CommonWrapper as _CommonWrapper } from './common/Wrapper.js'; +import { CommonOutputJax, COMMON_OPTIONS } from './common.js'; +import { CommonWrapper } from './common/Wrapper.js'; import { StyleList } from '../util/Styles.js'; import { StyleJson, StyleJsonSheet } from '../util/StyleJson.js'; import { OptionList } from '../util/Options.js'; @@ -42,6 +42,36 @@ import { Usage } from './chtml/Usage.js'; import * as LENGTHS from '../util/lengths.js'; import { unicodeChars } from '../util/string.js'; import { DefaultFont } from './chtml/DefaultFont.js'; +import { DOM, DOM_TYPES, N, T, D } from '../types/Types.js'; + +/*****************************************************************/ + +/** + * The CHTML option types. + */ +export interface CHTML_OPTIONS extends COMMON_OPTIONS< + DOM, + ChtmlWrapper, T, D>, + ChtmlWrapperFactory, T, D>, + ChtmlWrapperClass, T, D>, + ChtmlCharOptions, + ChtmlVariantData, + ChtmlDelimiterData, + ChtmlFontData, + ChtmlFontDataClass +> { + adaptiveCSS: boolean; // true means only produce CSS that is used in the processed equations + matchFontHeight: boolean; // true to match ex-height of surrounding font +} + +/** + * The CHTML option defaults. + */ +const options: CHTML_OPTIONS = { + ...CommonOutputJax.OPTIONS, + adaptiveCSS: true, + matchFontHeight: true, +}; /*****************************************************************/ /** @@ -82,11 +112,12 @@ export class CHTML extends CommonOutputJax< /** * @override */ - public static OPTIONS: OptionList = { - ...CommonOutputJax.OPTIONS, - adaptiveCSS: true, // true means only produce CSS that is used in the processed equations - matchFontHeight: true, // true to match ex-height of surrounding font - }; + public options: CHTML_OPTIONS>; + + /** + * @override + */ + public static OPTIONS = options; /** * The default styles for CommonHTML @@ -278,7 +309,7 @@ export class CHTML extends CommonOutputJax< for (const kind of this.wrapperUsage.update()) { const wrapper = this.factory.getNodeClass( kind - ) as any as typeof _CommonWrapper; + ) as any as typeof CommonWrapper; if (wrapper) { this.addClassStyles(wrapper, styles); } @@ -289,7 +320,7 @@ export class CHTML extends CommonOutputJax< * @override */ protected addClassStyles( - wrapper: typeof _CommonWrapper, + wrapper: typeof CommonWrapper, styles: StyleJsonSheet ) { const CLASS = wrapper as typeof ChtmlWrapper; diff --git a/ts/output/chtml/FontData.ts b/ts/output/chtml/FontData.ts index 9e041df1d6..00aa2a9460 100644 --- a/ts/output/chtml/FontData.ts +++ b/ts/output/chtml/FontData.ts @@ -30,6 +30,7 @@ import { FontData, FontExtensionData, DIRECTION, + FONTDATA_OPTIONS, } from '../common/FontData.js'; import { Usage } from './Usage.js'; import { StringMap } from './Wrapper.js'; @@ -87,6 +88,16 @@ export interface ChtmlFontExtensionData< /****************************************************************************/ +export type OPTIONS = { + fontURL: string; // The URL where the font woff2 files are +}; + +export interface CHTML_FONT_OPTIONS extends OPTIONS, FONTDATA_OPTIONS {} + +const options: OPTIONS = { + fontURL: './chtml/woff2', +}; + /** * The CHTML FontData class */ @@ -100,8 +111,8 @@ export class ChtmlFontData extends FontData< */ public static OPTIONS = { ...FontData.OPTIONS, + ...options, dynamicPrefix: './chtml/dynamic', - fontURL: './chtml/woff2', }; /** @@ -134,6 +145,11 @@ export class ChtmlFontData extends FontData< /***********************************************************************/ + public options: CHTML_FONT_OPTIONS & { + adaptiveCSS: boolean; + mathmlSpacing: boolean; + }; + /** * Data about the characters used (for adaptive CSS) */ diff --git a/ts/output/chtml/Wrapper.ts b/ts/output/chtml/Wrapper.ts index 90ab36dd02..071bb30499 100644 --- a/ts/output/chtml/Wrapper.ts +++ b/ts/output/chtml/Wrapper.ts @@ -25,7 +25,6 @@ import { OptionList } from '../../util/Options.js'; import { CommonWrapper, CommonWrapperClass, - Constructor, StringMap, SPACE, } from '../common/Wrapper.js'; @@ -39,8 +38,10 @@ import { ChtmlFontData, ChtmlFontDataClass, } from './FontData.js'; +import { Constructor } from '../../types/Types.js'; -export { Constructor, StringMap } from '../common/Wrapper.js'; +export { Constructor } from '../../types/Types.js'; +export { StringMap } from '../common/Wrapper.js'; /*****************************************************************/ diff --git a/ts/output/common.ts b/ts/output/common.ts index 73541edb87..8920d8c68e 100644 --- a/ts/output/common.ts +++ b/ts/output/common.ts @@ -21,7 +21,7 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { AbstractOutputJax } from '../core/OutputJax.js'; +import { AbstractOutputJax, OUTPUTJAX_OPTIONS } from '../core/OutputJax.js'; import { MathDocument } from '../core/MathDocument.js'; import { MathItem, Metrics, STATE } from '../core/MathItem.js'; import { MmlNode, TEXCLASS } from '../core/MmlTree/MmlNode.js'; @@ -46,6 +46,7 @@ import { StyleList, Styles } from '../util/Styles.js'; import { StyleJson, StyleJsonSheet } from '../util/StyleJson.js'; import { BBox } from '../util/BBox.js'; import { SEM } from '../a11y/semantic-enrich/strings.js'; +import { DOM, DOM_TYPES, N, T, D, EMPTY } from '../types/Types.js'; /*****************************************************************/ @@ -71,6 +72,110 @@ export const FONTPATH = '@mathjax/%%FONT%%-font'; /*****************************************************************/ +/** + * The option types for linebreaking. + */ +/* prettier-ignore */ +export type LINEBREAKS = { + inline: boolean; // true for browser-based breaking of inline equations + width: string; // a fixed size or a percentage of the container width + lineleading: number; // the default lineleading in em units + LinebreakVisitor: // The LinebreakVisitor to use + typeof LinebreakVisitor; +}; + +/** + * The option types for the common output jax. + */ +/* prettier-ignore */ +export interface COMMON_OPTIONS< + DOM extends DOM_TYPES, + WW extends CommonWrapper< + N, T, D, + CommonOutputJax, T, D, WW, WF, WC, CC, VV, DD, FD, FC>, + WW, WF, WC, CC, VV, DD, FD, FC + >, + WF extends CommonWrapperFactory< + N, T, D, + CommonOutputJax, T, D, WW, WF, WC, CC, VV, DD, FD, FC>, + WW, WF, WC, CC, VV, DD, FD, FC + >, + WC extends CommonWrapperClass< + N, T, D, + CommonOutputJax, T, D, WW, WF, WC, CC, VV, DD, FD, FC>, + WW, WF, WC, CC, VV, DD, FD, FC + >, + CC extends CharOptions, + VV extends VariantData, + DD extends DelimiterData, + FD extends FontData, + FC extends FontDataClass, +> extends OUTPUTJAX_OPTIONS { + scale: number; // global scaling factor for all expressions + minScale: number; // smallest scaling factor to use + mtextInheritFont: boolean; // true to make mtext elements use surrounding font + merrorInheritFont: boolean; // true to make merror text use surrounding font + mtextFont: string; // font to use for mtext, if not inheriting (empty means use MathJax fonts) + merrorFont: string; // font to use for merror, if not inheriting (empty means use MathJax fonts) + mathmlSpacing: boolean; // true for MathML spacing rules, false for TeX rules + skipAttributes: EMPTY; // RFDa and other attributes NOT to copy to the output + exFactor: number; // default size of ex in em units + displayAlign: // default for indentalign when set to 'auto' + 'left' | 'center' | 'right' | 'auto'; + displayIndent: string; // default for indentshift when set to 'auto' + displayOverflow: // default for how to handle wide expressions + 'overflow' | 'scroll' | 'scale' | 'truncate' | 'elide' | 'linebreak'; + linebreaks: LINEBREAKS; // the line-breaking options + font: string; // the font component to load + fontExtensions: string[]; // the font extensions to load + htmlHDW: // How to handle data-mjx-hdw attributes + 'auto' | 'use' | 'force' | 'ignore'; + wrapperFactory: // The wrapper factory to use + CommonWrapperFactory< + N, T, D, + CommonOutputJax, T, D, WW, WF, WC, CC, VV, DD, FD, FC>, + WW, WF, WC, CC, VV, DD, FD, FC + >, + fontData: // The FontData object to use + FontData | typeof FontData; + fontPath: string; // The path to the font definitions + styleJson: StyleJsonSheet; // The StyleJsonSheet object to use +} + +/** + * The default options. + */ +const options: COMMON_OPTIONS = { + ...AbstractOutputJax.OPTIONS, + scale: 1, + minScale: 0.5, + mtextInheritFont: false, + merrorInheritFont: false, + mtextFont: '', + merrorFont: 'serif', + mathmlSpacing: false, + skipAttributes: {}, + exFactor: 0.5, + displayAlign: 'center', + displayIndent: '0', + displayOverflow: 'overflow', + linebreaks: { + inline: true, + width: '100%', + lineleading: 0.2, + LinebreakVisitor: null, + }, + font: '', + fontExtensions: [], + htmlHDW: 'auto', + wrapperFactory: null, + fontData: null, + fontPath: FONTPATH, + styleJson: null, +}; + +/*****************************************************************/ + /** * The CommonOutputJax class on which the CHTML and SVG jax are built * @@ -123,35 +228,7 @@ export abstract class CommonOutputJax< /** * @override */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { - ...AbstractOutputJax.OPTIONS, - scale: 1, // global scaling factor for all expressions - minScale: .5, // smallest scaling factor to use - mtextInheritFont: false, // true to make mtext elements use surrounding font - merrorInheritFont: false, // true to make merror text use surrounding font - mtextFont: '', // font to use for mtext, if not inheriting (empty means use MathJax fonts) - merrorFont: 'serif', // font to use for merror, if not inheriting (empty means use MathJax fonts) - mathmlSpacing: false, // true for MathML spacing rules, false for TeX rules - skipAttributes: {}, // RFDa and other attributes NOT to copy to the output - exFactor: .5, // default size of ex in em units - displayAlign: 'center', // default for indentalign when set to 'auto' - displayIndent: '0', // default for indentshift when set to 'auto' - displayOverflow: 'overflow', // default for overflow (scroll/scale/truncate/elide/linebreak/overflow) - linebreaks: { // options for when overflow is linebreak - inline: true, // true for browser-based breaking of inline equations - width: '100%', // a fixed size or a percentage of the container width - lineleading: .2, // the default lineleading in em units - 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 - fontPath: FONTPATH, // The path to the font definitions - styleJson: null // The StyleJsonSheet object to use - }; + public static OPTIONS = options; /** * The default styles for the output jax @@ -279,6 +356,11 @@ export abstract class CommonOutputJax< */ protected unknownCache: UnknownVariantMap; + /** + * @override + */ + public options: COMMON_OPTIONS, WW, WF, WC, CC, VV, DD, FD, FC>; + /*****************************************************************/ /** @@ -305,7 +387,7 @@ export abstract class CommonOutputJax< ); super(jaxOptions); this.factory = - this.options.wrapperFactory || + (this.options.wrapperFactory as any) || /* prettier-ignore */ new defaultFactory< N, T, D, @@ -511,7 +593,7 @@ export abstract class CommonOutputJax< public getLinebreakWidth() { const W = this.math.metrics.containerWidth / this.pxPerEm; const width = - this.math.root.attributes.get('maxwidth') || + (this.math.root.attributes.get('maxwidth') as string) || this.options.linebreaks.width; this.containerWidth = length2em(width, W, 1, this.pxPerEm); } @@ -838,7 +920,9 @@ export abstract class CommonOutputJax< 2; const scale = Math.max( this.options.minScale, - this.options.matchFontHeight ? ex / this.font.params.x_height / em : 1 + 'matchFontHeight' in this.options && this.options.matchFontHeight + ? ex / this.font.params.x_height / em + : 1 ); return { em, ex, containerWidth, scale, family }; } diff --git a/ts/output/common/FontData.ts b/ts/output/common/FontData.ts index 26570eb45c..d7b17cbc4c 100644 --- a/ts/output/common/FontData.ts +++ b/ts/output/common/FontData.ts @@ -59,8 +59,7 @@ export interface CharOptions { * [height, depth, width, {italic-correction, skew, options}] */ export type CharDataArray = - | [number, number, number] - | [number, number, number, C]; + [number, number, number] | [number, number, number, C]; /** * Data about a character or a dynamic file object @@ -369,6 +368,23 @@ export function mergeOptions( } /****************************************************************************/ + +/** + * The FontData option types. + */ +export type FONTDATA_OPTIONS = { + unknownFamily: string; // family to use with unknown character + dynamicPrefix: string; // Location of dynamically loaded files +}; + +/** + * The FontData option defaults. + */ +const options: FONTDATA_OPTIONS = { + unknownFamily: 'serif', // Should use 'monospace' with LiteAdaptor + dynamicPrefix: '.', +}; + /** * The FontData class (for storing character bounding box data by variant, * and the stretchy delimiter data). @@ -385,11 +401,7 @@ export class FontData< /** * Options for the font */ - /* prettier-ignore */ - public static OPTIONS: OptionList = { - unknownFamily: 'serif', // Should use 'monospace' with LiteAdaptor - dynamicPrefix: '.', // Location of dynamically loaded files - }; + public static OPTIONS = options; /** * The name of the output jax this font data is for (used by extensions) @@ -676,7 +688,7 @@ export class FontData< /** * The font options */ - protected options: OptionList; + protected options: FONTDATA_OPTIONS; /** * The actual variant information for this font @@ -921,7 +933,10 @@ export class FontData< */ constructor(options: OptionList = null) { const CLASS = this.CLASS; - this.options = userOptions(defaultOptions({}, CLASS.OPTIONS), options); + this.options = userOptions( + defaultOptions({}, CLASS.OPTIONS), + options + ) as FONTDATA_OPTIONS; this.params = { ...CLASS.defaultParams }; this.sizeVariants = [...CLASS.defaultSizeVariants]; this.stretchVariants = [...CLASS.defaultStretchVariants]; @@ -1251,8 +1266,13 @@ export class FontData< dynamic.promise = asyncLoad(this.dynamicFileName(dynamic)).catch( (err) => { dynamic.failed = true; - Locale.warn(COMPONENT, 'FontData/CantLoad', dynamic.file, err.message); - return dynamic.promise = Promise.resolve(); + Locale.warn( + COMPONENT, + 'FontData/CantLoad', + dynamic.file, + err.message + ); + return (dynamic.promise = Promise.resolve()); } ); } diff --git a/ts/output/common/Wrapper.ts b/ts/output/common/Wrapper.ts index 27b7983f8d..0de08b3cef 100644 --- a/ts/output/common/Wrapper.ts +++ b/ts/output/common/Wrapper.ts @@ -45,6 +45,7 @@ import { CommonMrow } from './Wrappers/mrow.js'; import { BBox } from '../../util/BBox.js'; import { LineBBox } from './LineBBox.js'; import { Linebreaks } from './LinebreakVisitor.js'; +import { LINEBREAKS as LINEBREAK_OPTIONS } from '../common.js'; import { FontData, FontDataClass, @@ -58,6 +59,8 @@ import { import { Locale } from '../../util/Locale.js'; import { COMPONENT } from '../../core/__locales__/Component.js'; +export { Constructor } from '../../types/Types.js'; + /*****************************************************************/ /** @@ -116,10 +119,6 @@ export type StyleData = { }; /*********************************************************/ -/** - * Generic constructor type - */ -export type Constructor = new (...args: any[]) => T; /** * Generic CommonWrapper constructor @@ -472,24 +471,17 @@ export class CommonWrapper< get linebreaks(): Linebreaks< N, T, D, CommonOutputJax, - WW, WF, WC, CC, VV, DD, FD, FC> { + WW, WF, WC, CC, VV, DD, FD, FC + > { return this.jax.linebreaks; } /** * Easy access to the linebreak options * - * @returns {{inline: boolean, - * width: string, - * lineleading: number, - * LinebreakVisitor: null}} The linebreak options - */ - get linebreakOptions(): { - inline: boolean; - width: string; - lineleading: number; - LinebreakVisitor: null; - } { + * @returns {LINEBREAK_OPTIONS} The linebreak options + */ + get linebreakOptions(): LINEBREAK_OPTIONS { return this.jax.options.linebreaks; } diff --git a/ts/output/svg.ts b/ts/output/svg.ts index 23435f5dfb..4a045b23be 100644 --- a/ts/output/svg.ts +++ b/ts/output/svg.ts @@ -21,7 +21,7 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { CommonOutputJax, UnknownBBox } from './common.js'; +import { CommonOutputJax, UnknownBBox, COMMON_OPTIONS } from './common.js'; import { OptionList } from '../util/Options.js'; import { MathDocument } from '../core/MathDocument.js'; import { MathItem } from '../core/MathItem.js'; @@ -40,10 +40,44 @@ import { unicodeChars } from '../util/string.js'; import * as LENGTHS from '../util/lengths.js'; import { SPACE } from './common/Wrapper.js'; import { DefaultFont } from './svg/DefaultFont.js'; +import { DOM, DOM_TYPES, N, T, D } from '../types/Types.js'; export const SVGNS = 'http://www.w3.org/2000/svg'; export const XLINKNS = 'http://www.w3.org/1999/xlink'; +/*****************************************************************/ + +/** + * The SVG option types. + */ +export interface SVG_OPTIONS extends COMMON_OPTIONS< + DOM, + SvgWrapper, T, D>, + SvgWrapperFactory, T, D>, + SvgWrapperClass, T, D>, + SvgCharOptions, + SvgVariantData, + SvgDelimiterData, + SvgFontData, + SvgFontDataClass +> { + blacker: number; // Stroke-width to use for SVG character paths (in thousands of an em) + fontCache: 'local' | 'global' | 'none'; // The type of character cache to use + localID: string; // ID to use for local font cache (for single equation processing) + useXlink: boolean; // true to include xlink namespace for hrefs, false to not +} + +/** + * The svg option defaults. + */ +const options: SVG_OPTIONS = { + ...CommonOutputJax.OPTIONS, + blacker: 3, + fontCache: 'local', + localID: null, + useXlink: true, +}; + /*****************************************************************/ /** * Implements the SVG class (extends AbstractOutputJax) @@ -84,13 +118,7 @@ export class SVG extends CommonOutputJax< * @override */ /* prettier-ignore */ - public static OPTIONS: OptionList = { - ...CommonOutputJax.OPTIONS, - blacker: 3, // the stroke-width to use for SVG character paths - fontCache: 'local', // or 'global' or 'none' - localID: null, // ID to use for local font cache (for single equation processing) - useXlink: true, // true to include xlink namespace for hrefs, false to not - }; + public static OPTIONS = options; /** * The default styles for SVG @@ -166,6 +194,11 @@ export class SVG extends CommonOutputJax< */ public svgStyles: N = null; + /** + * @override + */ + public options: SVG_OPTIONS> & { matchFontHeight: boolean }; + /** * @override * @class diff --git a/ts/output/svg/Notation.ts b/ts/output/svg/Notation.ts index b94ee93bb5..a1f01ccf9c 100644 --- a/ts/output/svg/Notation.ts +++ b/ts/output/svg/Notation.ts @@ -42,8 +42,7 @@ export type DEFPAIR = Notation.DefPair, N>; * The kinds of lines that can be drawn */ export type LineName = - | Notation.Side - | ('vertical' | 'horizontal' | 'up' | 'down'); + Notation.Side | ('vertical' | 'horizontal' | 'up' | 'down'); /** * [x1,y1, x2,y2] endpoints for a line diff --git a/ts/types/Components.ts b/ts/types/Components.ts new file mode 100644 index 0000000000..5de7dc243e --- /dev/null +++ b/ts/types/Components.ts @@ -0,0 +1,264 @@ +/************************************************************* + * + * Copyright (c) 2026 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 The component type definitions + * + * @author dpvc@mathjax.org (Davide Cervone) + */ + +import type { + DOM_TYPES, + DOC_OPTIONS, + DOC_TYPE, + TEX_CONFIG, + TEX_PACKAGE, + MML_PACKAGE, + EMPTY_COMPONENT, + INPUTJAX, + OUTPUTJAX, + N, + T, + D, +} from './Types.js'; + +import type { STARTUP_TYPES } from '../components/startup.js'; +import type { LOADER_TYPES } from '../components/loader.js'; + +import type { HTMLDOCUMENT_OPTIONS } from '../handlers/html/HTMLDocument.js'; +import type { HTMLDOMSTRINGS_OPTIONS } from '../handlers/html/HTMLDomStrings.js'; + +import type { TEX_OPTIONS } from '../input/tex.js'; +import type { FINDTEX_OPTIONS } from '../input/tex/FindTeX.js'; +import type { TAGS_OPTIONS } from '../input/tex/Tags.js'; +import type { BASE_OPTIONS } from '../input/tex/base/BaseConfiguration.js'; + +import type { AMS_OPTIONS } from '../input/tex/ams/AmsConfiguration.js'; +import type { AMSCD_OPTIONS } from '../input/tex/amscd/AmsCdConfiguration.js'; +import type { AUTOLOAD_OPTIONS } from '../input/tex/autoload/AutoloadConfiguration.js'; +import type { BBM_OPTIONS } from '../input/tex/bbm/BbmConfiguration.js'; +import type { BBOLDX_OPTIONS } from '../input/tex/bboldx/BboldxConfiguration.js'; +import type { BEGINGROUP_OPTIONS } from '../input/tex/begingroup/BegingroupConfiguration.js'; +import type { COLOR_OPTIONS } from '../input/tex/color/ColorConfiguration.js'; +import type { CONFIGMACROS_OPTIONS } from '../input/tex/configmacros/ConfigMacrosConfiguration.js'; +import type { DSFONT_OPTIONS } from '../input/tex/dsfont/DsfontConfiguration.js'; +import type { MATHTOOLS_OPTIONS } from '../input/tex/mathtools/MathtoolsConfiguration.js'; +import type { NEWCOMMAND_OPTIONS } from '../input/tex/newcommand/NewcommandConfiguration.js'; +import type { NOUNDEFINED_OPTIONS } from '../input/tex/noundefined/NoUndefinedConfiguration.js'; +import type { PHYSICS_OPTIONS } from '../input/tex/physics/PhysicsConfiguration.js'; +import type { REQUIRE_OPTIONS } from '../input/tex/require/RequireConfiguration.js'; +import type { SETOPTIONS_OPTIONS } from '../input/tex/setoptions/SetOptionsConfiguration.js'; +import type { TAGFORMAT_OPTIONS } from '../input/tex/tagformat/TagFormatConfiguration.js'; +import type { TEXHTML_OPTIONS } from '../input/tex/texhtml/TexHtmlConfiguration.js'; +import type { TEXTMACROS_OPTIONS } from '../input/tex/textmacros/TextMacrosConfiguration.js'; +import type { UNITS_OPTIONS } from '../input/tex/units/UnitsConfiguration.js'; + +import type { MATHML_OPTIONS } from '../input/mathml.js'; +import type { MATHMLCOMPILE_OPTIONS } from '../input/mathml/MathMLCompile.js'; +import type { MML3_OPTIONS } from '../input/mathml/mml3/mml3.js'; + +import type { ASCIIMATH_OPTIONS } from '../input/asciimath.js'; +import type { FINDASCIIMATH_OPTIONS } from '../input/asciimath/FindAsciiMath.js'; + +import type { CHTML_OPTIONS } from '../output/chtml.js'; +import type { CHTML_FONT_OPTIONS } from '../output/chtml/FontData.ts'; +import type { SVG_OPTIONS } from '../output/svg.js'; + +import type { + OPTIONS as ASSISTIVEMML_OPTIONS, + AssistiveMmlMathDocument, +} from '../a11y/assistive-mml.js'; +import type { + OPTIONS as ENRICH_OPTIONS, + EnrichedMathDocument, +} from '../a11y/semantic-enrich.js'; +import type { + OPTIONS as COMPLEXITY_OPTIONS, + ComplexityMathDocument, +} from '../a11y/complexity.js'; +import type { SPEECH_OPTIONS, SpeechMathDocument } from '../a11y/speech.js'; +import type { + OPTIONS as EXPLORER_OPTIONS, + ExplorerMathDocument, +} from '../a11y/explorer.js'; + +import type { + OPTIONS as MENU_OPTIONS, + MenuMathDocument, +} from '../ui/menu/MenuHandler.js'; +import type { + OPTIONS as LAZY_OPTIONS, + LazyMathDocument, +} from '../ui/lazy/LazyHandler.js'; +import type { + OPTIONS as SAFE_OPTIONS, + SafeMathDocument, +} from '../ui/safe/SafeHandler.js'; + +/** + * The mapping of component names to their component definitions. + * + * @template DOM The DOM node types to use in the types that need them. + */ +export type COMPONENTS = { + 'input/asciimath': INPUTJAX< + 'asciimath', + ASCIIMATH_OPTIONS & FINDASCIIMATH_OPTIONS + >; + 'input/mml': INPUTJAX< + 'mathml', + MATHML_OPTIONS & MATHMLCOMPILE_OPTIONS, + 'mml' + >; + 'input/tex': + | INPUTJAX<'tex', TEX_OPTIONS & FINDTEX_OPTIONS & TAGS_OPTIONS> + | '__PACKAGES__'; + 'input/tex-base': + | INPUTJAX< + 'tex', + TEX_OPTIONS & FINDTEX_OPTIONS & TAGS_OPTIONS, + 'tex-base' + > + | '__base__'; + + '[tex]/action': EMPTY_COMPONENT<'[tex]/action'>; + '[tex]/ams': TEX_PACKAGE<'ams', AMS_OPTIONS>; + '[tex]/amscd': TEX_PACKAGE<'amscd', AMSCD_OPTIONS>; + '[tex]/autoload': TEX_PACKAGE<'autoload', AUTOLOAD_OPTIONS>; + '[tex]/bbm': TEX_PACKAGE<'bbm', BBM_OPTIONS>; + '[tex]/bboldx': TEX_PACKAGE<'bboldx', BBOLDX_OPTIONS>; + '[tex]/bbox': EMPTY_COMPONENT<'[tex]/bbox'>; + '[tex]/begingroup': TEX_PACKAGE<'begingroup', BEGINGROUP_OPTIONS>; + '[tex]/boldsymbol': EMPTY_COMPONENT<'[tex]/boldsymbol'>; + '[tex]/braket': EMPTY_COMPONENT<'[tex]/braket'>; + '[tex]/bussproofs': EMPTY_COMPONENT<'[tex]/bussproofs'>; + '[tex]/cancel': EMPTY_COMPONENT<'[tex]/cancel'>; + '[tex]/cases': EMPTY_COMPONENT<'[tex]/cases'>; + '[tex]/centernot': EMPTY_COMPONENT<'[tex]/centernot'>; + '[tex]/color': TEX_PACKAGE<'color', COLOR_OPTIONS>; + '[tex]/colorv2': EMPTY_COMPONENT<'[tex]/colorv2'>; + '[tex]/configmacros': TEX_PACKAGE<'configmacros', CONFIGMACROS_OPTIONS>; + '[tex]/dsfont': TEX_PACKAGE<'dsfont', DSFONT_OPTIONS>; + '[tex]/empheq': EMPTY_COMPONENT<'[tex]/empheq'>; + '[tex]/enclose': EMPTY_COMPONENT<'[tex]/enclose'>; + '[tex]/extpfeil': EMPTY_COMPONENT<'[tex]/extpfeil'>; + '[tex]/fontsizev3': EMPTY_COMPONENT<'[tex]/fontsizev3'>; + '[tex]/gensymb': EMPTY_COMPONENT<'[tex]/gensymb'>; + '[tex]/html': EMPTY_COMPONENT<'[tex]/html'>; + '[tex]/mathtools': TEX_PACKAGE<'mathtools', MATHTOOLS_OPTIONS>; + '[tex]/mhchem': EMPTY_COMPONENT<'[tex]/mhchem'>; + '[tex]/newcommand': TEX_PACKAGE<'newcommand', NEWCOMMAND_OPTIONS>; + '[tex]/noerrors': EMPTY_COMPONENT<'[tex]/noerrors'>; + '[tex]/noundefined': TEX_PACKAGE<'noundefined', NOUNDEFINED_OPTIONS>; + '[tex]/physics': TEX_PACKAGE<'physics', PHYSICS_OPTIONS>; + '[tex]/require': TEX_PACKAGE<'require', REQUIRE_OPTIONS>; + '[tex]/setoptions': TEX_PACKAGE<'setoptions', SETOPTIONS_OPTIONS>; + '[tex]/tagformat': TEX_PACKAGE<'tagformat', TAGFORMAT_OPTIONS>; + '[tex]/texhtml': TEX_PACKAGE<'texhtml', TEXHTML_OPTIONS>; + '[tex]/textcomp': EMPTY_COMPONENT<'[tex]/textcomp'>; + '[tex]/textmacros': TEX_PACKAGE<'textmacros', TEXTMACROS_OPTIONS>; + '[tex]/unicode': EMPTY_COMPONENT<'[tex]/unicode'>; + '[tex]/units': TEX_PACKAGE<'units', UNITS_OPTIONS>; + '[tex]/upgreek': EMPTY_COMPONENT<'[tex]/upgreek'>; + '[tex]/verb': EMPTY_COMPONENT<'[tex]/verb'>; + + '[mml]/mml3': MML_PACKAGE<'mml3', MML3_OPTIONS>; + 'input/mml/entities': EMPTY_COMPONENT<'input/mml/entities'>; + + 'output/chtml': OUTPUTJAX< + 'chtml', + CHTML_OPTIONS & CHTML_FONT_OPTIONS, + DOM + >; + 'output/svg': OUTPUTJAX<'svg', SVG_OPTIONS, DOM>; + + 'a11y/semantic-enrich': + | DOC_OPTIONS> + | DOC_TYPE, T, D>>; + 'a11y/assistive-mml': + | DOC_OPTIONS + | DOC_TYPE, T, D>>; + 'a11y/complexity': + | 'a11y/semantic-enrich' + | DOC_OPTIONS + | DOC_TYPE, T, D>>; + 'a11y/speech': + | 'a11y/semantic-enrich' + | DOC_OPTIONS> + | DOC_TYPE, T, D>>; + 'a11y/explorer': + | 'a11y/speech' + | DOC_OPTIONS + | DOC_TYPE; + + 'ui/menu': DOC_OPTIONS | DOC_TYPE; + 'ui/lazy': + | DOC_OPTIONS> + | DOC_TYPE, T, D>>; + 'ui/safe': + | DOC_OPTIONS + | DOC_TYPE, T, D>>; + 'ui/no-dark-mode': EMPTY_COMPONENT<'ui/no-dark-mode'>; + + 'adaptors/liteDOM': EMPTY_COMPONENT<'adaptors/liteDOM'>; + 'adaptors/jsdom': EMPTY_COMPONENT<'adaptors/jsdom'>; + 'adaptors/linkedom': EMPTY_COMPONENT<'adaptors/linkedom'>; + + startup: STARTUP_TYPES & + LOADER_TYPES & + DOC_OPTIONS & HTMLDOMSTRINGS_OPTIONS>; + loader: LOADER_TYPES; + + 'mml-chtml': + 'input/mml' | 'output/chtml' | 'ui/menu' | '__a11y__' | 'startup'; + 'mml-chtml-nofont': 'mml-chtml'; + 'mml-svg': 'input/mml' | 'output/svg' | 'ui/menu' | '__a11y__' | 'startup'; + 'mml-svg-nofont': 'mml-svg'; + 'tex-chtml': + 'input/tex' | 'output/chtml' | 'ui/menu' | '__a11y__' | 'startup'; + 'tex-chtml-nofont': 'tex-chtml'; + 'tex-mml-chtml': + | 'input/tex' + | 'input/mml' + | 'output/chtml' + | 'ui/menu' + | '__a11y__' + | 'startup'; + 'tex-mml-chtml-nofont': 'tex-mml-chtml'; + 'tex-svg': 'input/tex' | 'output/svg' | 'ui/menu' | '__a11y__' | 'startup'; + 'tex-svg-nofont': 'tex-svg'; + 'tex-mml-svg': + | 'input/tex' + | 'input/mml' + | 'output/svg' + | 'ui/menu' + | '__a11y__' + | 'startup'; + 'tex-mml-svg-nofont': 'tex-mml-svg'; + + __base__: TEX_CONFIG; + __PACKAGES__: + | '__base__' + | '[tex]/ams' + | '[tex]/configmacros' + | '[tex]/newcommand' + | '[tex]/textmacros' + | '[tex]/noundefined' + | '[tex]/autoload' + | '[tex]/require'; + __a11y__: 'a11y/explorer' | 'input/mml'; +}; diff --git a/ts/types/Types.ts b/ts/types/Types.ts new file mode 100644 index 0000000000..fb9e0c6b73 --- /dev/null +++ b/ts/types/Types.ts @@ -0,0 +1,345 @@ +/************************************************************* + * + * Copyright (c) 2026 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 The MathJax object type construction utilities + * + * @author dpvc@mathjax.org (Davide Cervone) + */ + +import type { ExtendedMetrics } from '../output/common.js'; +import type { PackageConfig } from '../components/package.js'; +import type { mathjax } from '../mathjax.js'; + +/** + * The types for node, text, and document + */ +export type DOM_TYPES = { + N: any; + T: any; + D: any; +}; + +/** + * Extract types from DOM type + */ +export type N = DOM['N']; +export type T = DOM['T']; +export type D = DOM['D']; + +/** + * A DOM_TYPES instance + */ +export type DOM = { N: N; T: T; D: D }; + +/** + * A constructor for a given type + */ +export type Constructor = new (...args: any[]) => T; + +/** + * An object key + */ +export type KEY = string | number | symbol; + +/** + * An empty configuration object + */ +// eslint-disable-next-line +export type EMPTY = {}; // Record; + +/** + * A list with jax names as keys + */ +export type JAXLIST = { [jax: string]: true }; + +/** + * A list with component names as keys + */ +export type COMPONENTLIST = { [name: string]: true }; + +/** + * A component configuration object + */ +export type COMPONENT_DEF = { + config?: object; + properties?: object; + input?: JAXLIST; + output?: JAXLIST; + component?: COMPONENTLIST; +}; + +/** + * Combine "|" types into "&" types. + */ +/* prettier-ignore */ +export type COMBINE = + (T extends any ? (x: T) => void : never) extends (x: infer I) => void ? I : never; + +/** + * Recursively makes all object keys optional + */ +/* prettier-ignore */ +export type OPTIONAL = { + [t in keyof T]?: + T[t] extends Array + ? T[t] + : T[t] extends (...args: any) => any + ? T[t] + : T[t] extends object + ? OPTIONAL + : T[t]; +}; + +/** + * Recursively add `[+]` and `[-]` options for array configuration options + */ +/* prettier-ignore */ +export type CONFIG_ARRAYS = { + [t in keyof T]?: + T[t] extends Array + ? T[t] | { '[+]'?: T[t], '[-]'?: T[t] } + : T[t] extends (...args: any) => any + ? T[t] + : T[t] extends object + ? CONFIG_ARRAYS + : T[t]; +} + +/** + * The convert() option list + */ +export type CONVERT_OPTIONS = OPTIONAL<{ + display: boolean; + em: number; + ex: number; + containerWidth: number; + scale: number; + family: string; +}>; + +/** + * Create input2output and input2outputPromise from jax names + * + * @template I The input jax name + * @template O The output jax name + * @template D The DOM node types to use + */ +/* prettier-ignore */ +export type CONVERT = + I extends string + ? O extends string + ? { [i in I as `${i}2${O}`]?: (math: string, options?: CONVERT_OPTIONS) => N } & + { [i in I as `${i}2${O}Promise`]?: (math: string, options?: CONVERT_OPTIONS) => Promise> } + : EMPTY + : EMPTY; + +/** + * Create all input2output and input2outputPromise methods from the jax lists + * + * @template I Object containing the names of the input jax as its keys + * @template O Object containing the names of the output jax as its keys + * @template D The DOM node types to use + */ +/* prettier-ignore */ +export type CONVERTJAX = + I extends JAXLIST + ? O extends JAXLIST + ? COMBINE> & + { + typeset?: (elements?: N[]) => void; + typesetPromise?: (elements?: N[]) => Promise; + typesetClear?: (elements?: N[]) => void; + } + : EMPTY + : EMPTY; + +/** + * Add the Loader types for a list of component names (as keys of an object) + * + * @template T Object whose keys are the names of the components to add to the loader configuration + */ +/* prettier-ignore */ +export type LOADER = + T extends COMPONENTLIST + ? { + loader: { + [component in keyof T]: PackageConfig; + }; + } + : EMPTY; + +/** + * Define document options + * + * @template C The options to define + */ +export type DOC_OPTIONS = { + config: { + options: C; + }; +}; + +/** + * Define the startup document type + * + * @template T The document type to use + */ +export type DOC_TYPE = { + properties: { + startup: { + document: T; + }; + }; +}; + +/** + * Configuration for an empty component. + * + * @template N The name of the component + */ +export type EMPTY_COMPONENT = { + component: { [name in N]: true }; +}; + +/** + * Configuration for a TeX package. + * + * @template N The name of the package + * @template C The package options + */ +export type TEX_PACKAGE = { + component: { [name in N as `[tex]/${name}`]: true }; + config: { tex: C }; +}; + +/** + * Configuration for TeX (not from package) + * + * @template C The package options + */ +export type TEX_CONFIG = { + config: { tex: C }; +}; + +/** + * Configuration for a MathML package. + * + * @template N The name of the package + * @template C The package options + */ +export type MML_PACKAGE = { + component: { [name in N as `[mml]/${name}`]: true }; + config: { mml: C }; +}; + +/** + * Configuration for an InputJax. + * + * @template N The name of the input jax + * @template C The jax options + * @template M The name for the component if not the same as the jax + */ +/* prettier-ignore */ +export type INPUTJAX = { + component: { [name in M as `input/${name}`]: true }; + config: { [name in N]: C }; + input: { [name in N]: true }; + properties: + { [name in N as `${name}2mml`]: (math: string, options?: CONVERT_OPTIONS) => string } & + { [name in N as `${name}2mmlPromise`]: (math: string, options?: CONVERT_OPTIONS) => Promise } & + { [name in N as `${name}Reset`]: (...args: any[]) => void }; +}; + +/** + * Configuration for an OutputJax. + * + * @template J The name of the output jax + * @template C The jax options + */ +/* prettier-ignore */ +export type OUTPUTJAX = { + component: { [name in J as `output/${name}`]: true }; + config: { [name in J]: C } & { output: C }; + output: { [name in J]: true }; + properties: + { [name in J as `${name}Stylesheet`]: () => N } & + { getMetricsFor: (node: N, display: boolean) => ExtendedMetrics }; +}; + +/** + * Create the `config` property from the definition's `config` and `component` properties + */ +/* prettier-ignore */ +export type CONFIG = + OPTIONAL>; + +/** + * Add types for the MathJax.startup.mathjax.document function so its + * options are checked, and its output reflects the type of the + * document created by the loaded components. + * + * @template T The typ eobject to modify + */ +/* prettier-ignore */ +export type ADD_MATHJAX = + T extends { config?: {options?: any}; startup: { document: any } } + ? { + startup: { + mathjax: Omit & + { + document: (doc: any, options: Partial) => T['startup']['document']; + }; + } & Omit; + } & Omit + : T; + +/** + * The type for the MathJax object based on a collection of component definitions. + * This is for both configuration and after Mathjax is loaded. + * + * @template T The definitions of the components to include (as DEF1 & ... & DEFn) + * @template D The DOM node types to use + */ +/* prettier-ignore */ +export type TYPES2MJX = + ADD_MATHJAX<{ version?: string, _?: any } & + OPTIONAL>> & + { config?: CONFIG } & + CONVERTJAX>; + +/** + * The type for the completed MathJax object (after MathJax is loaded). + * + * @template T The definitions of the components to include (as DEF1 & ... & DEFn) + * @template D The DOM node types to use + */ +/* prettier-ignore */ +export type TYPES2MJX_OBJECT = + ADD_MATHJAX<{ version: string, _: any } & + T['properties'] & + { config: CONFIG_ARRAYS> } & + CONVERTJAX>; + +/** + * The type for the MathJax object as a config object (before loading MathJax). + * + * @template T The definitions of the components to include (as DEF1 & ... & DEFn) + */ +export type TYPES2MJX_CONFIG = CONFIG_ARRAYS< + CONFIG +>; diff --git a/ts/types/dom/html.ts b/ts/types/dom/html.ts new file mode 100644 index 0000000000..6bcfd712f0 --- /dev/null +++ b/ts/types/dom/html.ts @@ -0,0 +1,34 @@ +import { DOM } from '../Types.js'; +import { COMPONENT_LIST, MJX, MJX_OBJECT, MJX_CONFIG } from '../mjx.js'; + +/** + * The HTML DOM elements + */ +export type N = HTMLElement; +export type T = Text; +export type D = Document; +export type HTML_DOM = DOM; + +/** + * The type for the MathJax object based on a collection of component names. + * This is for both configuration and after Mathjax is loaded. + * + * @template T The union of component names, array of names, or definitions. + */ +export type MATHJAX> = MJX; + +/** + * The type for the completed MathJax object (after MathJax is loaded). + * + * @template T The union of component names, array of names, or definitions. + */ +/* prettier-ignore */ +export type MATHJAX_OBJECT> = MJX_OBJECT; + +/** + * The type for the MathJax object as a config object (before loading MathJax). + * + * @template T The union of component names, array of names, or definitions. + */ +/* prettier-ignore */ +export type MATHJAX_CONFIG> = MJX_CONFIG; diff --git a/ts/types/dom/lite.ts b/ts/types/dom/lite.ts new file mode 100644 index 0000000000..acd01fb296 --- /dev/null +++ b/ts/types/dom/lite.ts @@ -0,0 +1,32 @@ +import { LiteElement } from '../../adaptors/lite/Element.js'; +import { LiteDocument } from '../../adaptors/lite/Document.js'; +import { LiteText } from '../../adaptors/lite/Text.js'; + +import { DOM } from '../Types.js'; +import { COMPONENT_LIST, MJX, MJX_OBJECT, MJX_CONFIG } from '../mjx.js'; + +/** + * The HTML DOM elements + */ +export type N = LiteElement; +export type T = LiteText; +export type D = LiteDocument; +export type LITE_DOM = DOM; + +/** + * The type for the MathJax object based on a collection of component names. + * This is for both configuration and after Mathjax is loaded. + */ +export type MATHJAX> = MJX; + +/** + * The type for the completed MathJax object (after MathJax is loaded). + */ +/* prettier-ignore */ +export type MATHJAX_OBJECT> = MJX_OBJECT; + +/** + * The type for the MathJax object as a config object (before loading MathJax). + */ +/* prettier-ignore */ +export type MATHJAX_CONFIG> = MJX_CONFIG; diff --git a/ts/types/mjx.ts b/ts/types/mjx.ts new file mode 100644 index 0000000000..14aa0ee4d9 --- /dev/null +++ b/ts/types/mjx.ts @@ -0,0 +1,92 @@ +/************************************************************* + * + * Copyright (c) 2026 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 Conversion of type lists to MathJax object types. + * + * @author dpvc@mathjax.org (Davide Cervone) + */ + +import type { + DOM_TYPES, + COMPONENT_DEF, + COMBINE, + TYPES2MJX, + TYPES2MJX_OBJECT, + TYPES2MJX_CONFIG, +} from './Types.js'; +import { COMPONENTS } from './Components.js'; + +/** + * A union of component names, array of names, or explicit component definitions. + */ +export type COMPONENT_LIST = + COMPONENT_DEF | keyof COMPONENTS | (keyof COMPONENTS)[]; + +/** + * Map component names to their type defintions, leaving explicit definitions as they are. + * + * @template T A union of compnent names and/or explicit definitions. + * @template D The DOM node types to use in the types that need them. + */ +/* prettier-ignore */ +export type MJX_TYPES, D extends DOM_TYPES> = + T extends keyof COMPONENTS + ? MJX_TYPES[T], D> + : T; + +/** + * Map compent names or array of names or explicit definitions + * to a combined definition object (the intersection of all the definitions). + * + * @template T A union of compnent names and/or explicit definitions. + * @template D The DOM node types to use in the types that need them. + */ +/* prettier-ignore */ +export type MJX_DEF, D extends DOM_TYPES> = + COMBINE)[] ? T[number] : T, D>>; + +/** + * The type for the MathJax object based on a collection of component names and DOM element types. + * This is for both configuration and after Mathjax is loaded. + * + * @template T The union of component names, array of names, or definitions. + * @template D The DOM node types to use in the types that need them. + */ +/* prettier-ignore */ +export type MJX, D extends DOM_TYPES> = + TYPES2MJX, D>; + +/** + * The type for the completed MathJax object (after MathJax is loaded). + * + * @template T The union of component names, array of names, or definitions. + * @template D The DOM node types to use in the types that need them. + */ +/* prettier-ignore */ +export type MJX_OBJECT, D extends DOM_TYPES> = + TYPES2MJX_OBJECT, D>; + +/** + * The type for the MathJax object as a config object (before loading MathJax). + * + * @template T The union of component names, array of names, or definitions. + * @template D The DOM node types to use in the types that need them. + */ +/* prettier-ignore */ +export type MJX_CONFIG, D extends DOM_TYPES> = + TYPES2MJX_CONFIG>; diff --git a/ts/ui/lazy/LazyHandler.ts b/ts/ui/lazy/LazyHandler.ts index a48f726485..23d83c75a5 100644 --- a/ts/ui/lazy/LazyHandler.ts +++ b/ts/ui/lazy/LazyHandler.ts @@ -24,14 +24,20 @@ import { MathDocumentConstructor, ContainerList, + RenderActions, } from '../../core/MathDocument.js'; -import { MathItem, STATE, newState } from '../../core/MathItem.js'; -import { HTMLDocument } from '../../handlers/html/HTMLDocument.js'; +import { STATE, newState } from '../../core/MathItem.js'; +import { + HTMLDocument, + HTMLDOCUMENT_OPTIONS, +} from '../../handlers/html/HTMLDocument.js'; +import { HTMLMathItem } from '../../handlers/html/HTMLMathItem.js'; import { HTMLHandler } from '../../handlers/html/HTMLHandler.js'; // import { EnrichedMathItem } from '../../a11y/semantic-enrich.js'; import { SpeechMathItem } from '../../a11y/speech.js'; -import { OptionList } from '../../util/Options.js'; +import { expandable } from '../../util/Options.js'; import { StyleJson } from '../../util/StyleJson.js'; +import { DOM, DOM_TYPES, N, T, D, Constructor } from '../../types/Types.js'; /** * Add the needed function to the window object. @@ -44,11 +50,6 @@ declare const window: { }; }; -/** - * Generic constructor for Mixins - */ -export type Constructor = new (...args: any[]) => T; - /** * A set of lazy MathItems */ @@ -112,6 +113,9 @@ newState('LAZYALWAYS', STATE.FINDMATH + 3); */ export const LAZYID = 'data-mjx-lazy'; +export type HTMLSpeechItem = HTMLMathItem & + SpeechMathItem; + /** * The properties added to MathItem for lazy typesetting * @@ -119,7 +123,7 @@ export const LAZYID = 'data-mjx-lazy'; * @template T The Text node class * @template D The Document class */ -export interface LazyMathItem extends MathItem { +export interface LazyMathItem extends HTMLSpeechItem { /** * True when the MathItem needs to be lazy compiled */ @@ -156,7 +160,7 @@ export function LazyMathItemMixin< N, T, D, - B extends Constructor>, + B extends Constructor>, >(BaseMathItem: B): Constructor> & B { return class extends BaseMathItem { /** @@ -282,6 +286,32 @@ export function LazyMathItemMixin< /*==========================================================================*/ +/** + * The ui/lazy option types. + */ +export type OPTIONS = { + lazyMargin: string; // The size of the observer margin + lazyAlwaysTypeset: N; +}; + +/** + * The LazyMathDocument option types. + */ +export interface LAZY_OPTIONS + extends OPTIONS, HTMLDOCUMENT_OPTIONS { + MathItem: Constructor, T, D>>; +} + +/** + * The ui/lazy option defaults. + */ +const options: OPTIONS = { + lazyMargin: '500px', + lazyAlwaysTypeset: null, +}; + +/*==========================================================================*/ + /** * The properties added to MathDocument for lazy typesetting * @@ -290,6 +320,11 @@ export function LazyMathItemMixin< * @template D The Document class */ export interface LazyMathDocument extends HTMLDocument { + /** + * @override + */ + options: LAZY_OPTIONS>; + /** * The Intersection Observer used to track the appearance of the expression markers */ @@ -331,22 +366,28 @@ export function LazyMathDocumentMixin< N, T, D, - B extends MathDocumentConstructor>, ->(BaseDocument: B): MathDocumentConstructor> & B { + B extends MathDocumentConstructor, DOM>, +>( + BaseDocument: B +): MathDocumentConstructor, DOM> & B { return class BaseClass extends BaseDocument { /** * @override */ - public static OPTIONS: OptionList = { + public static OPTIONS = { ...BaseDocument.OPTIONS, - lazyMargin: '500px', - lazyAlwaysTypeset: null, - renderActions: { + ...options, + renderActions: expandable>({ ...BaseDocument.OPTIONS.renderActions, lazyAlways: [STATE.LAZYALWAYS, 'lazyAlways', '', false], - }, + }), }; + /** + * @override + */ + public options: LAZY_OPTIONS>; + /** * The Intersection Observer used to track the appearance of the expression markers */ @@ -421,7 +462,7 @@ export function LazyMathDocumentMixin< N, T, D, - Constructor> + Constructor & SpeechMathItem> >(this.options.MathItem); // // Allocate a process bit for lazyAlways diff --git a/ts/ui/menu/Menu.ts b/ts/ui/menu/Menu.ts index 7858486c83..fc189af8d2 100644 --- a/ts/ui/menu/Menu.ts +++ b/ts/ui/menu/Menu.ts @@ -33,8 +33,10 @@ import { userOptions, defaultOptions, expandable, + EXPANDABLE_LIST_OF, } from '../../util/Options.js'; -import { ExplorerMathItem } from '../../a11y/explorer.js'; +import { ExplorerMathItem, A11Y_OPTIONS } from '../../a11y/explorer.js'; +import { SRE_OPTIONS } from '../../a11y/semantic-enrich.js'; import { InfoDialog } from '../dialog/InfoDialog.js'; import { CopyDialog } from '../dialog/CopyDialog.js'; @@ -70,60 +72,96 @@ const XMLDECLARATION = ''; /*==========================================================================*/ -/** - * The various values that are stored in the menu - */ -export interface MenuSettings { +export type HTMLMATHITEM = MathItem; + +export type JaxList = { + [name: string]: OutputJax; +}; + +type A11Y = Partial; + +type RENDERER = 'CHTML' | 'SVG'; +type OVERFLOW = + 'Overflow' | 'Scroll' | 'Linebreak' | 'Scale' | 'Truncate' | 'Elide'; +type ANNOTATION_DEFS = EXPANDABLE_LIST_OF; + +export type SETTINGS = { showSRE: boolean; showTex: boolean; texHints: boolean; semantics: boolean; zoom: string; zscale: string; - renderer: string; + renderer: RENDERER; + locale: string; alt: boolean; cmd: boolean; ctrl: boolean; shift: boolean; - scale: string; - overflow: string; + scale: number; + overflow: OVERFLOW; breakInline: boolean; autocollapse: boolean; collapsible: boolean; enrich: boolean; assistiveMml: boolean; - // A11y settings - backgroundColor: string; - backgroundOpacity: string; - braille: boolean; - brailleCode: string; - brailleSpeech: boolean; - brailleCombine: boolean; - foregroundColor: string; - foregroundOpacity: string; - highlight: string; - infoPrefix: boolean; - infoRole: boolean; - infoType: boolean; - inTabOrder: boolean; - locale: string; - magnification: string; - magnify: string; - speech: boolean; - speechRules: string; - subtitles: boolean; - treeColoring: boolean; - viewBraille: boolean; - voicing: boolean; - help: boolean; - roleDescription: string; - tabSelects: string; -} + brailleCode: SRE_OPTIONS['braille']; +}; -export type HTMLMATHITEM = MathItem; +/** + * The various values that are stored in the menu + */ +export interface MenuSettings extends SETTINGS, A11Y {} -export type JaxList = { - [name: string]: OutputJax; +export type OPTIONS = { + settings: MenuSettings; + jax: JaxList; + annotationTypes: ANNOTATION_DEFS; +}; + +const options: Omit & { settings: SETTINGS & A11Y } = { + settings: { + showSRE: false, + showTex: false, + texHints: true, + semantics: false, + zoom: 'NoZoom', + zscale: '200%', + renderer: 'CHTML', + locale: Locale.default, + alt: true, + cmd: false, + ctrl: false, + shift: false, + scale: 1, + overflow: 'Scroll', + breakInline: true, + autocollapse: false, + collapsible: false, + enrich: true, + assistiveMml: false, + speech: true, + braille: true, + brailleCode: 'nemeth', + brailleSpeech: false, + brailleCombine: false, + speechRules: 'clearspeak-default', + roleDescription: 'math', + inTabOrder: true, + tabSelects: 'all', + help: true, + }, + jax: { + CHTML: null, + SVG: null, + }, + annotationTypes: expandable({ + TeX: ['TeX', 'LaTeX', 'application/x-tex'], + StarMath: ['StarMath 5.0'], + Maple: ['Maple'], + ContentMathML: ['MathML-Content', 'application/mathml-content+xml'], + OpenMath: ['OpenMath'], + }), }; /*==========================================================================*/ @@ -146,50 +184,7 @@ export class Menu { * The options for the menu, including the default settings, the various output jax * and the list of annotation types and their encodings */ - public static OPTIONS: OptionList = { - settings: { - showSRE: false, - showTex: false, - texHints: true, - semantics: false, - zoom: 'NoZoom', - zscale: '200%', - renderer: 'CHTML', - locale: Locale.default, - alt: true, - cmd: false, - ctrl: false, - shift: false, - scale: 1, - overflow: 'Scroll', - breakInline: true, - autocollapse: false, - collapsible: false, - enrich: true, - assistiveMml: false, - speech: true, - braille: true, - brailleCode: 'nemeth', - brailleSpeech: false, - brailleCombine: false, - speechRules: 'clearspeak-default', - roleDescription: 'math', - inTabOrder: true, - tabSelects: 'all', - help: true, - }, - jax: { - CHTML: null, - SVG: null, - }, - annotationTypes: expandable({ - TeX: ['TeX', 'LaTeX', 'application/x-tex'], - StarMath: ['StarMath 5.0'], - Maple: ['Maple'], - ContentMathML: ['MathML-Content', 'application/mathml-content+xml'], - OpenMath: ['OpenMath'], - }), - }; + public static OPTIONS = options; /** * The CSS to include in SVG images @@ -564,7 +559,7 @@ export class Menu { this.jax = this.options.jax; const jax = this.document.outputJax; this.jax[jax.name] = jax; - this.settings.renderer = jax.name; + this.settings.renderer = jax.name as RENDERER; this.settings.scale = jax.options.scale; if (jax.options.displayOverflow) { this.settings.overflow = @@ -599,8 +594,8 @@ export class Menu { this.variable('semantics'), this.variable('zoom'), this.variable('zscale'), - this.variable('renderer', (jax) => this.setRenderer(jax)), - this.variable('overflow', (overflow) => + this.variable('renderer', (jax) => this.setRenderer(jax)), + this.variable('overflow', (overflow) => this.setOverflow(overflow) ), this.variable('breakInline', (breaks) => @@ -610,7 +605,7 @@ export class Menu { this.variable('cmd'), this.variable('ctrl'), this.variable('shift'), - this.variable('scale', (scale) => this.setScale(scale)), + this.variable('scale', (scale) => this.setScale(scale)), this.a11yVar('speech', (speech) => this.setSpeech(speech)), this.a11yVar('braille', (braille) => this.setBraille(braille)), this.variable('brailleCode', (code) => @@ -644,9 +639,10 @@ export class Menu { this.a11yVar('help'), this.a11yVar('locale', (locale) => this.setLocale(locale)), this.variable('speechRules', (value) => { + const sre = this.document.options.sre; const [domain, style] = value.split('-'); - this.document.options.sre.domain = domain; - this.document.options.sre.style = style; + sre.domain = domain as typeof sre.domain; + sre.style = style as typeof sre.style; this.rerender(STATE.COMPILED); }), this.a11yVar('magnification'), @@ -1015,14 +1011,15 @@ export class Menu { * Get the the value of an a11y option * * @param {string} option The name of the ptions to get - * @returns {any} The value of the option + * @returns {any} The value of the option */ protected getA11y(option: string): any { if (MathJax._?.a11y?.explorer) { - if (this.document.options.a11y[option] !== undefined) { - return this.document.options.a11y[option]; + const options = this.document.options as any; + if (options.a11y[option] !== undefined) { + return options.a11y[option]; } - return this.document.options.sre[option]; + return options.sre[option]; } } @@ -1038,8 +1035,8 @@ export class Menu { this.enableAccessibilityItems('Speech', this.settings.speech); this.enableAccessibilityItems('Braille', this.settings.braille); this.setAccessibilityMenus(); - const renderer = - this.settings.renderer.replace(/[^a-zA-Z0-9]/g, '') || 'CHTML'; + const renderer = (this.settings.renderer.replace(/[^a-zA-Z0-9]/g, '') || + 'CHTML') as RENDERER; (Menu._loadingPromise || Promise.resolve()).then(() => { const settings = this.settings; this.applyRendererOptions(this.document.outputJax); @@ -1076,8 +1073,8 @@ export class Menu { /** * @param {string} scale The new scaling value */ - protected setScale(scale: string) { - this.document.outputJax.options.scale = parseFloat(scale); + protected setScale(scale: number) { + this.document.outputJax.options.scale = scale; if (!Menu.loading && this.initialized) { this.document.rerenderPromise(); } @@ -1086,12 +1083,15 @@ export class Menu { /** * If the jax is already on record, just use it, otherwise load the new one * - * @param {string} jax The name of the jax to switch to + * @param {RENDERER} jax The name of the jax to switch to * @param {boolean} rerender True if the document should be rerendered - * @returns {Promise} A promise that is resolved when the renderer is set + * @returns {Promise} A promise that is resolved when the renderer is set * and rerendering complete */ - protected setRenderer(jax: string, rerender: boolean = true): Promise { + protected setRenderer( + jax: RENDERER, + rerender: boolean = true + ): Promise { if (Object.hasOwn(this.jax, jax) && this.jax[jax]) { this.applyRendererOptions(this.jax[jax]); return this.setOutputJax(jax, rerender); @@ -1124,7 +1124,7 @@ export class Menu { ): OutputJax { const settings = this.settings; const options = output.options; - options.scale = parseFloat(settings.scale); + options.scale = settings.scale; options.displayOverflow = settings.overflow.toLowerCase(); if (options.linebreaks) { options.linebreaks.inline = settings.breakInline; @@ -1138,7 +1138,7 @@ export class Menu { * * @param {string} jax The name of the jax to switch to * @param {boolean} rerender True if the document should be rerendered - * @returns {Promise} A promise that is resolved when the renderer is set + * @returns {Promise} A promise that is resolved when the renderer is set * and rerendering complete */ protected setOutputJax(jax: string, rerender: boolean = true): Promise { @@ -1155,14 +1155,14 @@ export class Menu { * * @returns {Promise} The promise combining all loading promises */ - protected loadRequiredExtensions(): Promise { + protected loadRequiredExtensions(): Promise { const jax = this.document.outputJax.name.toLowerCase(); - const promises = []; + const paths = []; for (const path of this.requiredExtensions) { - promises.push(MathJax.loader.load(`[${path}]/${jax}`)); + paths.push(`[${path}]/${jax}`); } this.requiredExtensions = []; - return Promise.all(promises); + return MathJax.loader.load(...paths); } /** @@ -1224,7 +1224,7 @@ export class Menu { enable; if (!enable) { this.settings.collapsible = false; - this.document.options.enableCollapsible = false; + this.document.options.enableComplexity = false; } } @@ -1272,10 +1272,11 @@ export class Menu { } /** - * @param {string} code The Braille code format (nemeth or euro) + * @param {string} code The Braille code format (nemeth or euro) */ protected setBrailleCode(code: string) { - this.document.options.sre.braille = code; + const sre = this.document.options.sre; + sre.braille = code as typeof sre.braille; this.rerender(STATE.COMPILED); } @@ -1306,7 +1307,7 @@ export class Menu { } /** - * @param {string} locale The speech locale + * @param {string} locale The speech locale */ protected setLocale(locale: string) { this.document.options.sre.locale = locale; @@ -1381,10 +1382,14 @@ export class Menu { protected setColor(type: string, name: string, opacity?: string) { const a11y = this.document.options.a11y; if (!name) { - name = a11y[type === 'fg' ? 'foregroundColor' : 'backgroundColor']; + name = a11y[ + type === 'fg' ? 'foregroundColor' : 'backgroundColor' + ] as string; } if (!opacity) { - opacity = a11y[type === 'fg' ? 'foregroundOpacity' : 'backgroundOpacity']; + opacity = String( + a11y[type === 'fg' ? 'foregroundOpacity' : 'backgroundOpacity'] + ); } MathJax._.a11y.explorer.Region.LiveRegion.setColor( type, @@ -1398,9 +1403,7 @@ export class Menu { * Request the scaling value from the user and save it in the settings */ protected scaleAllMath() { - const scale = (parseFloat(this.settings.scale) * 100) - .toFixed(1) - .replace(/.0$/, ''); + const scale = (this.settings.scale * 100).toFixed(1).replace(/.0$/, ''); const percent = prompt(localize('Scale/Prompt'), scale + '%'); if (this.current) { const speech = (this.menu.mathItem as ExplorerMathItem).explorers.speech; @@ -1411,7 +1414,7 @@ export class Menu { if (percent.match(/^\s*\d+(\.\d*)?\s*%?\s*$/)) { const scale = parseFloat(percent) / 100; if (scale) { - this.menu.pool.lookup('scale').setValue(String(scale)); + this.menu.pool.lookup('scale').setValue(scale as any); } else { alert(localize('Scale/NonZero')); } @@ -1429,7 +1432,7 @@ export class Menu { const pool = this.menu.pool; const settings = this.defaultSettings; for (const name of Object.keys(settings) as (keyof MenuSettings)[]) { - const variable = pool.lookup(name); + const variable = pool.lookup(name as string); if (variable) { if (variable.getValue() !== settings[name]) { variable.setValue(settings[name] as string | boolean); @@ -1451,7 +1454,7 @@ export class Menu { /** * Check if a component is loading, and restart if it is * - * @param {string} name The name of the component to check if it is loading + * @param {string} name The name of the component to check if it is loading */ public checkComponent(name: string) { const promise = Menu.loadingPromises.get(name); @@ -1463,8 +1466,8 @@ export class Menu { /** * Attempt to load a component and perform a callback when done * - * @param {string} name The name of the component to load - * @param {() => void} callback The callback for after loading + * @param {string} name The name of the component to load + * @param {() => void} callback The callback for after loading */ protected loadComponent(name: string, callback: () => void) { if (Menu.loadingPromises.has(name)) return; @@ -1604,7 +1607,7 @@ export class Menu { */ protected async typesetSVG( math: HTMLMATHITEM, - cache: string, + cache: 'local' | 'global' | 'none', breaks: boolean ): Promise { const jax = this.jax.SVG as SVG; @@ -1629,7 +1632,7 @@ export class Menu { /** * @param {string} svg The serialzied SVG to adjust - * @returns {string} The adjusted SVG string + * @returns {string} The adjusted SVG string */ protected formatSvg(svg: string): string { // @@ -1849,13 +1852,13 @@ export class Menu { /** * Create JSON for a variable controlling a menu setting * - * @param {keyof MenuSettings} name The setting for which to make a variable - * @param {(value: T) => void} action Optional function to perform after setting the value - * @returns {object} The JSON for the variable + * @param {keyof MenuSettings} name The setting for which to make a variable + * @param {(value: T) => void} action Optional function to perform after setting the value + * @returns {object} The JSON for the variable * * @template T The type of variable being defined */ - public variable( + public variable( name: keyof MenuSettings, action?: (value: T) => void ): object { @@ -1888,7 +1891,7 @@ export class Menu { ): object { return { name: name, - getter: () => this.getA11y(name), + getter: () => this.getA11y(name as string), setter: (value: T) => { (this.settings as any)[name] = value; this.setA11y({ [name]: value }); @@ -2023,8 +2026,8 @@ export class Menu { /** * Create JSON for a label item * - * @param {string} id The id for the item - * @returns {object} The JSON for the label item + * @param {string} id The id for the item + * @returns {object} The JSON for the label item */ public label(id: string): object { const content = localize(id); @@ -2034,7 +2037,7 @@ export class Menu { /** * Create JSON for a menu rule * - * @returns {object} The JSON for the rule item + * @returns {object} The JSON for the rule item */ public rule(): object { return { type: 'rule' }; @@ -2043,11 +2046,11 @@ export class Menu { /** * Create JSON for a slider * - * @param {string} variable The (pool) variable to attach to this slider - * @returns {object} The JSON for the slider item + * @param {string} variable The (pool) variable to attach to this slider + * @returns {object} The JSON for the slider item */ public slider(variable: string): object { - return {type: 'slider', variable, content: ' '}; + return { type: 'slider', variable, content: ' ' }; } /*======================================================================*/ diff --git a/ts/ui/menu/MenuHandler.ts b/ts/ui/menu/MenuHandler.ts index 8baf28194b..1a965ca1e0 100644 --- a/ts/ui/menu/MenuHandler.ts +++ b/ts/ui/menu/MenuHandler.ts @@ -24,33 +24,43 @@ import { mathjax } from '../../mathjax.js'; import { STATE, newState } from '../../core/MathItem.js'; -import { MathDocumentConstructor } from '../../core/MathDocument.js'; -import { Handler } from '../../core/Handler.js'; import { + MathDocumentConstructor, + RenderActions, +} from '../../core/MathDocument.js'; +import { Handler } from '../../core/Handler.js'; +import type { ComplexityMathDocument, ComplexityMathItem, + COMPLEXITY_OPTIONS, } from '../../a11y/complexity.js'; -import { +import type { AssistiveMmlMathDocument, AssistiveMmlMathItem, + ASSISTIVEMML_OPTIONS, } from '../../a11y/assistive-mml.js'; -import { SpeechMathDocument } from '../../a11y/speech.js'; +import type { + SpeechMathDocument, + SpeechMathItem, + SPEECH_OPTIONS, +} from '../../a11y/speech.js'; +import type { EXPLORER_OPTIONS } from '../../a11y/explorer.ts'; import { expandable } from '../../util/Options.js'; +import { DOM, Constructor } from '../../types/Types.js'; +import { HTML_DOM } from '../../types/dom/html.js'; -import { Menu } from './Menu.js'; +import { Menu, OPTIONS as SETTINGS } from './Menu.js'; import '../../a11y/speech/SpeechMenu.js'; /*==========================================================================*/ -/** - * Generic constructor for Mixins - */ -export type Constructor = new (...args: any[]) => T; - /** * Generic A11Y MathItem type */ -export type A11yMathItem = ComplexityMathItem & +/* prettier-ignore */ +export type A11yMathItem = + SpeechMathItem & + ComplexityMathItem & AssistiveMmlMathItem; /** @@ -63,18 +73,19 @@ export type A11yMathItemConstructor = { /** * Generic A11Y MathDocument type */ -export type A11yMathDocument = ComplexityMathDocument< - HTMLElement, - Text, - Document -> & +/* prettier-ignore */ +export type A11yMathDocument = + ComplexityMathDocument & SpeechMathDocument & AssistiveMmlMathDocument; /** * Constructor for base document for MenuMathDocument */ -export type A11yDocumentConstructor = MathDocumentConstructor; +export type A11yDocumentConstructor = MathDocumentConstructor< + A11yMathDocument, + DOM +>; /*==========================================================================*/ @@ -86,11 +97,7 @@ newState('CONTEXT_MENU', 170); /** * The new function for MathItem that adds the context menu */ -export interface MenuMathItem extends ComplexityMathItem< - HTMLElement, - Text, - Document -> { +export interface MenuMathItem extends A11yMathItem { /** * @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,12 +158,69 @@ export function MenuMathItemMixin( /*==========================================================================*/ /** - * The properties needed in the MathDocument for context menus + * The ui/menu option types. */ -export interface MenuMathDocument +export type OPTIONS = { + // + // These options are from the a11y extensions, which may not be loaded + // initially, and so would cause "undefined option" error messages + // if a user tries to configure them. So we include them here. + // They are overridden by the options from the extensions when + // those are loaded (via ...BaseDocument.OPTIONS). + // + enableEnrichment: boolean; + enableComplexity: boolean; + enableSpeech: boolean; + enableBraille: boolean; + enableExplorer: boolean; + enableExplorerHelp: boolean; + enrichError: SPEECH_OPTIONS['enrichError']; + // + enableMenu: true; + menuOptions: SETTINGS; + MenuClass: typeof Menu; +}; + +/** + * The MenuMathDocument option types. + */ +export interface MENU_OPTIONS extends - ComplexityMathDocument, - SpeechMathDocument { + OPTIONS, + COMPLEXITY_OPTIONS, + Omit, + ASSISTIVEMML_OPTIONS { + MathItem: Constructor; +} + +/** + * The ui/menu option defaults. + */ +const options: OPTIONS = { + enableEnrichment: true, + enableComplexity: true, + enableSpeech: true, + enableBraille: true, + enableExplorer: true, + enableExplorerHelp: true, + enrichError: (doc: MenuMathDocument, math: MenuMathItem, err: Error) => + doc.enrichError(doc, math, err), + enableMenu: true, + menuOptions: Menu.OPTIONS, + MenuClass: Menu, +}; + +/*==========================================================================*/ + +/** + * The properties needed in the MathDocument for context menus + */ +export interface MenuMathDocument extends A11yMathDocument { + /** + * @override + */ + options: MENU_OPTIONS; + /** * The menu associated with this document */ @@ -167,7 +231,7 @@ export interface MenuMathDocument * * @returns {MenuMathDocument} The MathDocument (so calls can be chained) */ - addMenu(): MenuMathDocument; + addMenu(): this; /** * Checks if there are files being loaded by the menu, and cancels the typesetting if so. @@ -193,41 +257,32 @@ export function MenuMathDocumentMixin( * @override */ public static OPTIONS = { - // - // These options are from the a11y extensions, which may not be loaded - // initially, and so would cause "undefined option" error messages - // if a user tries to configure them. So we include them here. - // They are overridden by the options from the extensions when - // those are loaded (via ...BaseDocument.OPTIONS). - // - enableEnrichment: true, - enableComplexity: true, - enableSpeech: true, - enableBraille: true, - enableExplorer: true, - enableExplorerHelp: true, - enrichSpeech: 'none', - enrichError: (doc: MenuMathDocument, math: MenuMathItem, err: Error) => - doc.enrichError(doc, math, err), + ...options, ...BaseDocument.OPTIONS, - MenuClass: Menu, - menuOptions: Menu.OPTIONS, - enableMenu: true, - sre: BaseDocument.OPTIONS.sre || expandable({}), - a11y: BaseDocument.OPTIONS.a11y || expandable({}), - renderActions: expandable({ + sre: + (BaseDocument.OPTIONS as SPEECH_OPTIONS).sre || + expandable({}), + a11y: + (BaseDocument.OPTIONS as SPEECH_OPTIONS).a11y || + expandable({}), + renderActions: expandable>({ ...BaseDocument.OPTIONS.renderActions, addMenu: [STATE.CONTEXT_MENU], getMenus: [STATE.INSERTED + 5, false], checkLoading: [ STATE.UNPROCESSED + 1, (doc: MenuMathDocument) => doc.checkLoading(), - '', + null, false, ], }), }; + /** + * @override + */ + options: MENU_OPTIONS; + /** * The menu associated with this document */ @@ -265,7 +320,7 @@ export function MenuMathDocumentMixin( * * @returns {MenuMathDocument} The MathDocument (so calls can be chained) */ - public addMenu(): MenuMathDocument { + public addMenu(): this { if (!this.processed.isSet('context-menu')) { for (const math of this.math) { (math as MenuMathItem).addMenu(this); @@ -303,7 +358,7 @@ export function MenuMathDocumentMixin( * * @returns {MenuMathDocument} The MathDocument (so calls can be chained) */ - public _checkLoading(): MenuMathDocument { + public _checkLoading(): this { if (this.menu.isLoading) { mathjax.retryAfter( this.menu.loadingPromise.catch((err) => console.warn(err)) diff --git a/ts/ui/menu/Slider.ts b/ts/ui/menu/Slider.ts index 8fccd01e4c..908e41fc43 100644 --- a/ts/ui/menu/Slider.ts +++ b/ts/ui/menu/Slider.ts @@ -21,7 +21,7 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import {Slider} from './mj-context-menu.js'; +import { Slider } from './mj-context-menu.js'; // // Fix slider actions (FIXME: remove when mj-context-menu is merged into MathJax-src repo) @@ -50,47 +50,47 @@ export class mjSlider extends Slider { keydown(event: KeyboardEvent) { let value = parseInt(((this as any).input as HTMLInputElement).value); switch (event.key) { - case 'ArrowLeft': - case 'ArrowDown': - if (!event.shiftKey) { - super.keydown(event); - return; - } - /* @eslint-ignore: no-fallthrough */ - case '-': - value = Math.max(0, value - (event.ctrlKey ? 5 : 1)); - break; + case 'ArrowLeft': + case 'ArrowDown': + if (!event.shiftKey) { + super.keydown(event); + return; + } + /* @eslint-ignore: no-fallthrough */ + case '-': + value = Math.max(0, value - (event.ctrlKey ? 5 : 1)); + break; - case 'ArrowRight': - case 'ArrowUp': - if (!event.shiftKey) { - super.keydown(event); - return; - } - /* @eslint-ignore: no-fallthrough */ - case '+': - value = Math.min(100, value + (event.ctrlKey ? 5 : 1)); - break + case 'ArrowRight': + case 'ArrowUp': + if (!event.shiftKey) { + super.keydown(event); + return; + } + /* @eslint-ignore: no-fallthrough */ + case '+': + value = Math.min(100, value + (event.ctrlKey ? 5 : 1)); + break; - case 'PageDown': - value = Math.max(0, value - 5); - break; + case 'PageDown': + value = Math.max(0, value - 5); + break; - case 'PageUp': - value= Math.max(0, value + 5); - break; + case 'PageUp': + value = Math.max(0, value + 5); + break; - case 'Home': - value = 0; - break; + case 'Home': + value = 0; + break; - case 'End': - value = 100; - break; + case 'End': + value = 100; + break; - default: - super.keydown(event); - return; + default: + super.keydown(event); + return; } this.variable.setValue(String(value)); this.stop(event); diff --git a/ts/ui/safe/SafeHandler.ts b/ts/ui/safe/SafeHandler.ts index 83e2bb9316..7549026337 100644 --- a/ts/ui/safe/SafeHandler.ts +++ b/ts/ui/safe/SafeHandler.ts @@ -23,26 +23,70 @@ import { MathItem } from '../../core/MathItem.js'; import { - MathDocument, + AbstractMathDocument, MathDocumentConstructor, + DOCUMENT_OPTIONS, } from '../../core/MathDocument.js'; import { Handler } from '../../core/Handler.js'; +import { DOM, DOM_TYPES, Constructor } from '../../types/Types.js'; import { Safe } from './safe.js'; /*==========================================================================*/ +export type SAFE_ALLOW = 'all' | 'safe' | 'none'; +export type LENGTH_LIST = { + [name: string]: string | [number, number] | boolean; +}; +export type SAFE_LIST = { [name: string]: boolean }; + /** - * Generic constructor for Mixins + * The ui/safe option types. */ -export type Constructor = new (...args: any[]) => T; +export type OPTIONS = { + safeOptions: { + allow: { + URLs: SAFE_ALLOW; // Safe are in safeProtocols below + classes: SAFE_ALLOW; // Safe start with mjx- (can be set by pattern below) + cssIDs: SAFE_ALLOW; // Safe start with mjx- (can be set by pattern below) + styles: SAFE_ALLOW; // Safe are in safeStyles below + }; + lengthMax: number; // Largest padding/border/margin, etc. in em's + scriptsizemultiplierRange: [number, number]; // Valid range for scriptsizemultiplier + scriptlevelRange: [number, number]; // Valid range for scriptlevel + classPattern: RegExp; // Pattern for allowed class names + idPattern: RegExp; // Pattern for allowed ids + dataPattern: RegExp; // Pattern for data attributes + safeProtocols: SAFE_LIST; // Which URL protocols are allowed + safeStyles: SAFE_LIST; // Which styles are allowed + styleParts: SAFE_LIST; // CSS styles that have Top/Right/Bottom/Left versions + styleLengths: LENGTH_LIST; // CSS styles that are lengths needing max/min testing + }; + SafeClass: typeof Safe; +}; -/*==========================================================================*/ +/** + * The SafeMathDocument option types. + */ +export interface SAFE_OPTIONS + extends OPTIONS, DOCUMENT_OPTIONS {} + +/** + * The ui/safe option defaults. + */ +const options: OPTIONS = { + safeOptions: Safe.OPTIONS, + SafeClass: Safe, +}; /** * The properties needed in the MathDocument for sanitizing the internal MathML */ -export interface SafeMathDocument extends MathDocument { +export interface SafeMathDocument extends AbstractMathDocument< + N, + T, + D +> { /** * The Safe object for this document */ @@ -52,7 +96,7 @@ export interface SafeMathDocument extends MathDocument { /** * The mixin for adding safe render action to MathDocuments * - * @param {B} BaseDocument The MathDocument class to be extended + * @param {B} BaseDocument The MathDocument class to be extended * @returns {SafeMathDocument} The extended MathDocument class * * @template N The HTMLElement node class @@ -64,7 +108,10 @@ export function SafeMathDocumentMixin< N, T, D, - B extends MathDocumentConstructor>, + B extends MathDocumentConstructor< + AbstractMathDocument, + DOM + >, >(BaseDocument: B): Constructor> & B { return class extends BaseDocument { /** @@ -72,12 +119,11 @@ export function SafeMathDocumentMixin< */ public static OPTIONS = { ...BaseDocument.OPTIONS, - safeOptions: { - ...Safe.OPTIONS, - }, - SafeClass: Safe, + ...options, }; + public options: SAFE_OPTIONS>; + /** * An instance of the Safe object */ @@ -128,7 +174,7 @@ export function SafeMathDocumentMixin< * Add context-menu support to a Handler instance * * @param {Handler} handler The Handler instance to enhance - * @returns {Handler} The handler that was modified (for purposes of chaining extensions) + * @returns {Handler} The handler that was modified (for purposes of chaining extensions) */ export function SafeHandler( handler: Handler diff --git a/ts/ui/safe/safe.ts b/ts/ui/safe/safe.ts index 211c3aa5c9..43c23a6d69 100644 --- a/ts/ui/safe/safe.ts +++ b/ts/ui/safe/safe.ts @@ -24,10 +24,11 @@ import { Property } from '../../core/Tree/Node.js'; import { MmlNode } from '../../core/MmlTree/MmlNode.js'; import { MathItem } from '../../core/MathItem.js'; -import { MathDocument } from '../../core/MathDocument.js'; +import { MathDocument, AbstractMathDocument } from '../../core/MathDocument.js'; import { OptionList, expandable } from '../../util/Options.js'; import { DOMAdaptor } from '../../core/DOMAdaptor.js'; import { SafeMethods } from './SafeMethods.js'; +import type { OPTIONS, SAFE_LIST, LENGTH_LIST } from './SafeHandler.js'; /** * Function type for filtering attributes @@ -42,6 +43,104 @@ export type FilterFunction = ( ...args: any[] ) => Property; +const options: OPTIONS['safeOptions'] = { + allow: { + // + // Values can be "all", "safe", or "none" + // + URLs: 'safe', // safe are in safeProtocols below + classes: 'safe', // safe start with mjx- (can be set by pattern below) + cssIDs: 'safe', // safe start with mjx- (can be set by pattern below) + styles: 'safe', // safe are in safeStyles below + }, + // + // Largest padding/border/margin, etc. in em's + // + lengthMax: 3, + // + // Valid range for scriptsizemultiplier + // + scriptsizemultiplierRange: [0.6, 1], + // + // Valid range for scriptlevel + // + scriptlevelRange: [-2, 2], + // + // Pattern for allowed class names + // + classPattern: /^mjx-[-a-zA-Z0-9_.]+$/, + // + // Pattern for allowed ids + // + idPattern: /^mjx-(?:eqn:.+|[-a-zA-Z0-9_.]+)$/, + // + // Pattern for data attributes + // + dataPattern: /^data-mjx-/, + // + // Which URL protocols are allowed + // + safeProtocols: expandable({ + http: true, + https: true, + file: true, + javascript: false, + data: false, + }), + // + // Which styles are allowed + // + safeStyles: expandable({ + color: true, + backgroundColor: true, + border: true, + cursor: true, + margin: true, + padding: true, + textShadow: true, + fontFamily: true, + fontSize: true, + fontStyle: true, + fontWeight: true, + opacity: true, + outline: true, + }), + // + // CSS styles that have Top/Right/Bottom/Left versions + // + styleParts: expandable({ + border: true, + padding: true, + margin: true, + outline: true, + }), + // + // CSS styles that are lengths needing max/min testing + // A string value means test that style value; + // An array gives [min,max] in em's + // Otherwise use [-lengthMax,lengthMax] from above + // + styleLengths: expandable({ + borderTop: 'borderTopWidth', + borderRight: 'borderRightWidth', + borderBottom: 'borderBottomWidth', + borderLeft: 'borderLeftWidth', + paddingTop: true, + paddingRight: true, + paddingBottom: true, + paddingLeft: true, + marginTop: true, + marginRight: true, + marginBottom: true, + marginLeft: true, + outlineTop: true, + outlineRight: true, + outlineBottom: true, + outlineLeft: true, + fontSize: [0.707, 1.44], + }), +}; + /** * The Safe object for sanitizing the internal MathML representation of an expression * @@ -53,103 +152,7 @@ export class Safe { /** * The options controlling the handling of the safe extension */ - public static OPTIONS: OptionList = { - allow: { - // - // Values can be "all", "safe", or "none" - // - URLs: 'safe', // safe are in safeProtocols below - classes: 'safe', // safe start with mjx- (can be set by pattern below) - cssIDs: 'safe', // safe start with mjx- (can be set by pattern below) - styles: 'safe', // safe are in safeStyles below - }, - // - // Largest padding/border/margin, etc. in em's - // - lengthMax: 3, - // - // Valid range for scriptsizemultiplier - // - scriptsizemultiplierRange: [0.6, 1], - // - // Valid range for scriptlevel - // - scriptlevelRange: [-2, 2], - // - // Pattern for allowed class names - // - classPattern: /^mjx-[-a-zA-Z0-9_.]+$/, - // - // Pattern for allowed ids - // - idPattern: /^mjx-(?:eqn:.+|[-a-zA-Z0-9_.]+)$/, - // - // Pattern for data attributes - // - dataPattern: /^data-mjx-/, - // - // Which URL protocols are allowed - // - safeProtocols: expandable({ - http: true, - https: true, - file: true, - javascript: false, - data: false, - }), - // - // Which styles are allowed - // - safeStyles: expandable({ - color: true, - backgroundColor: true, - border: true, - cursor: true, - margin: true, - padding: true, - textShadow: true, - fontFamily: true, - fontSize: true, - fontStyle: true, - fontWeight: true, - opacity: true, - outline: true, - }), - // - // CSS styles that have Top/Right/Bottom/Left versions - // - styleParts: expandable({ - border: true, - padding: true, - margin: true, - outline: true, - }), - // - // CSS styles that are lengths needing max/min testing - // A string value means test that style value; - // An array gives [min,max] in em's - // Otherwise use [-lengthMax,lengthMax] from above - // - styleLengths: expandable({ - borderTop: 'borderTopWidth', - borderRight: 'borderRightWidth', - borderBottom: 'borderBottomWidth', - borderLeft: 'borderLeftWidth', - paddingTop: true, - paddingRight: true, - paddingBottom: true, - paddingLeft: true, - marginTop: true, - marginRight: true, - marginBottom: true, - marginLeft: true, - outlineTop: true, - outlineRight: true, - outlineBottom: true, - outlineLeft: true, - fontSize: [0.707, 1.44], - }), - }; + public static OPTIONS = options; /** * The attribute-to-filter-method mapping @@ -215,7 +218,11 @@ export class Safe { try { math.root.walkTree(this.sanitizeNode.bind(this)); } catch (err) { - document.options.compileError(document, math, err); + document.options.compileError( + document as AbstractMathDocument, + math, + err + ); } } diff --git a/ts/util/FunctionList.ts b/ts/util/FunctionList.ts index e21dea57c0..14f72afc0e 100644 --- a/ts/util/FunctionList.ts +++ b/ts/util/FunctionList.ts @@ -24,27 +24,33 @@ import { PrioritizedList, PrioritizedListItem } from './PrioritizedList.js'; export type AnyFunction = (...args: unknown[]) => unknown; -export type AnyFunctionDef = AnyFunction | [AnyFunction, number]; -export type AnyFunctionList = AnyFunctionDef[]; +export type AnyFunctionDef = + F | [F, number]; +export type AnyFunctionList = + AnyFunctionDef[]; /*****************************************************************/ /** * The FunctionListItem interface (extends PrioritizedListItem) */ -export interface FunctionListItem extends PrioritizedListItem {} +export interface FunctionListItem< + F extends AnyFunction = AnyFunction, +> extends PrioritizedListItem {} /*****************************************************************/ /** * Implements the FunctionList class (extends PrioritizedList) */ -export class FunctionList extends PrioritizedList { +export class FunctionList< + F extends AnyFunction = AnyFunction, +> extends PrioritizedList { /** * @override * @param {AnyFunctionList} list The initial list of functions to add */ - constructor(list: AnyFunctionList = null) { + constructor(list: AnyFunctionList = null) { super(); if (list) { this.addList(list); @@ -56,7 +62,7 @@ export class FunctionList extends PrioritizedList { * * @param {AnyFunctionList} list The list of functions to add */ - public addList(list: AnyFunctionList) { + public addList(list: AnyFunctionList) { for (const item of list) { if (Array.isArray(item)) { this.add(item[0], item[1]); diff --git a/ts/util/Options.ts b/ts/util/Options.ts index cfc4d08cb3..2471292d5b 100644 --- a/ts/util/Options.ts +++ b/ts/util/Options.ts @@ -112,13 +112,20 @@ export class Expandable {} * * without reporting an error. * - * @param {OptionList} def The options list - * @returns {OptionList} The augmented options list + * @param {T} def The options list + * @returns {T} The augmented options list + * + * @template T The type of the list */ -export function expandable(def: OptionList): OptionList { +export function expandable(def: T): T { return Object.assign(Object.create(Expandable.prototype), def); } +/** + * An expandable list of a given type + */ +export type EXPANDABLE_LIST_OF = { [name: string]: T }; + /*****************************************************************/ /** * Make sure an option is an Array