diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..aff82a10 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" diff --git a/.gitignore b/.gitignore index 00c91583..44d646d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,2 @@ node_modules -.yarn -.yarn/builds/ -.yarn/unplugged/ -.yarn/install-state.gz -.pnp.* +dist/ diff --git a/.yarnrc.yml b/.yarnrc.yml deleted file mode 100644 index 3186f3f0..00000000 --- a/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/dist/Temml-Asana.css b/dist/Temml-Asana.css deleted file mode 100644 index 5067a0a4..00000000 --- a/dist/Temml-Asana.css +++ /dev/null @@ -1,342 +0,0 @@ -/* -Asana Math is released under the SIL Open Font License. See the files in this -directory for details. The font can be obtained from several TeX distributions -or package managers. The font does not seem to have a corresponding "non-MATH" -font, it is recommended to use a Palatino-like font for the surrounding text. - -The WOFF fonts have been obtained from -http://mirrors.ctan.org/fonts/ -*/ - -@font-face { - font-family: Asana Math; - src: local('Asana Math'), local('Asana-Math'), - url('./Asana-Math.woff2'); -} - -math { - font-style: normal; - font-weight: normal; - line-height: normal; - font-size-adjust: none; - text-indent: 0; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - direction: ltr; - /* Prevent Firefox from omitting the dot on i or j. */ - font-feature-settings: "dtls" off; -} - -math * { - border-color: currentColor; -} - -math { - font-family: Asana Math, math; -} - -/* display: block is necessary in Firefox and Safari. - * Not in Chromium, which recognizes display: "block math" written inline. */ -math.tml-display { - display: block; - width: 100%; -} - -/* Cramped superscripts in WebKit */ -mfrac > :nth-child(2), -msqrt, -mover > :first-child { - math-shift: compact -} - -.menclose { - display: inline-block; - position: relative; - padding: 0.5ex 0ex; -} -.tml-cancelto { - display: inline-block; - position: absolute; - top: 0; - left: 0; - padding: 0.5ex 0ex; - background-color: currentColor; - /* Use the SVG as an alpha mask (painted by background-color) */ - -webkit-mask-image: url("data:image/svg+xml,"); - mask-image: url("data:image/svg+xml,"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100%; - -webkit-mask-position: 0 0; - mask-position: 0 0; -} - -@supports (-moz-appearance: none) { - /* \vec w/o italic correction for Firefox */ - .tml-vec { - transform: scale(0.75) - } - /* Fix \cancelto in Firefox */ - .ff-narrow { - width: 0em; - } - .ff-nudge-left { - margin-left: -0.2em; - } -} - -@supports (not (-moz-appearance: none)) { - /* Chromium and WebKit */ - /* prime vertical alignment */ - mo.tml-prime { - font-feature-settings: 'salt'; - } - /* Italic correction on superscripts */ - .tml-sml-pad { - padding-left: 0.05em; - } - .tml-med-pad { - padding-left: 0.10em; - } - .tml-lrg-pad { - padding-left: 0.15em; - } -} - -@supports (-webkit-backdrop-filter: blur(1px)) { - /* WebKit vertical & italic correction on accents */ - .wbk-acc { - /* lower by x-height distance */ - transform: translate(0em, 0.431em); - } - .wbk-sml { - transform: translate(0.07em, 0); - } - .wbk-sml-acc { - transform: translate(0.07em, 0.431em); - } - .wbk-sml-vec { - transform: scale(0.75) translate(0.07em, 0); - } - .wbk-med { - transform: translate(0.14em, 0); - } - .wbk-med-acc { - transform: translate(0.14em, 0.431em); - } - .wbk-med-vec { - transform: scale(0.75) translate(0.14em, 0); - } - .wbk-lrg { - transform: translate(0.21em, 0); - } - .wbk-lrg-acc { - transform: translate(0.21em, 0.431em); - } - .wbk-lrg-vec { - transform: scale(0.75) translate(0.21em, 0); - } -} - -/* \cancel & \phase use background images. Get them to print. */ -menclose { - -webkit-print-color-adjust: exact; /* Chrome & Edge */ - print-color-adjust: exact; -} - -/* Array cell justification in Firefox & WebKit */ -.tml-right { - text-align: right; -} -.tml-left { - text-align: left; -} - -/* For CD labels that grow to the left in Firefox and WebKit */ -.tml-shift-left { margin-left:-200% } - -/* Styles for Chromium only */ -@supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) { - /* Italic correction on accents */ - .chr-sml { - transform: translate(0.07em, 0) - } - .chr-sml-vec { - transform: scale(0.75) translate(0.07em, 0) - } - .chr-med { - transform: translate(0.14em, 0) - } - .chr-med-vec { - transform: scale(0.75) translate(0.14em, 0) - } - .chr-lrg { - transform: translate(0.21em, 0) - } - .chr-lrg-vec { - transform: scale(0.75) translate(0.21em, 0) - } - - /* For CD labels that grow to the left */ - .tml-shift-left { margin-left:-100% } - - /* MathML Core & Chromium do not support the MathML 3.0 element attributes. */ - /* So use styles. */ - menclose { - position: relative; - padding: 0.5ex 0ex; - } - - .tml-overline { - padding: 0.1em 0 0 0; - border-top: 0.065em solid; - } - - .tml-underline { - padding: 0 0 0.1em 0; - border-bottom: 0.065em solid; - } - - .tml-cancel { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: 0; - width: 100%; - height: 100%; - background-color: currentColor; - } - .upstrike { - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - .downstrike { - clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%); - } - .sout { - clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em)); - } - .tml-xcancel { - clip-path: polygon(0.05em 0em, 0em 0.05em, calc(50% - 0.05em) 50%, 0em calc(100% - 0.05em), 0.05em 100%, 50% calc(50% + 0.05em), calc(100% - 0.05em) 100%, 100% calc(100% - 0.05em), calc(50% + 0.05em) 50%, 100% 0.05em, calc(100% - 0.05em) 0%, 50% calc(50% - 0.05em)); - } - - .longdiv-top { - border-top: 0.067em solid; - padding: 0.1em 0.2em 0.2em 0.433em; - } - .longdiv-arc { - position: absolute; - top: 0; - bottom: 0.1em; - left: -0.4em; - width: 0.7em; - border: 0.067em solid; - transform: translateY(-0.067em); - border-radius: 70%; - clip-path: inset(0 0 0 0.4em); - box-sizing: border-box;} - .menclose {display: inline-block; - text-align: left; - position: relative; - } - - .phasor-bottom { - border-bottom: 0.067em solid; - padding: 0.2em 0.2em 0.1em 0.6em; - } - .phasor-angle { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: -0.04em; - height: 100%; - aspect-ratio: 0.5; - background-color: currentColor; - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - - .tml-fbox { - padding: 3pt; - border: 1px solid; - } - - .circle-pad { - padding: 0.267em; - } - .textcircle { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - border: 0.067em solid; - border-radius: 50%; - } - - .actuarial { - padding: 0.03889em 0.03889em 0 0.03889em; - border-width: 0.08em 0.08em 0em 0em; - border-style: solid; - margin-right: 0.03889em; - } - - /* Stretch \widetilde */ - .tml-crooked-2 { - transform: scale(2.0, 1.1) - } - .tml-crooked-3 { - transform: scale(3.0, 1.3) - } - .tml-crooked-4 { - transform: scale(4.0, 1.4) - } - /* set array cell justification */ - .tml-right { - text-align: -webkit-right; - } - .tml-left { - text-align: -webkit-left; - } -} - -/* flex-wrap for line-breaking in Chromium */ -math { - display: inline-flex; - flex-wrap: wrap; - align-items: baseline; -} -math > mrow { - padding: 0.5ex 0ex; -} - -/* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */ -/* We adjust for jot and small */ -mtable.tml-jot mtd { - padding-top: 0.7ex; - padding-bottom: 0.7ex; -} -mtable.tml-small mtd { - padding-top: 0.35ex; - padding-bottom: 0.35ex; -} - -/* Firefox */ -@-moz-document url-prefix() { - /* Avoid flex-wrap */ - math { display: inline; } - math > mrow { padding: 0 } - /* Adjust Firefox spacing between array rows */ - mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; } - mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; } -} - -/* AMS environment auto-numbering via CSS counter. */ -.tml-eqn::before { - counter-increment: tmlEqnNo; - content: "(" counter(tmlEqnNo) ")"; -} - -body { - counter-reset: tmlEqnNo; -} diff --git a/dist/Temml-Latin-Modern.css b/dist/Temml-Latin-Modern.css deleted file mode 100644 index dd8959f9..00000000 --- a/dist/Temml-Latin-Modern.css +++ /dev/null @@ -1,360 +0,0 @@ -/* -The Latin Modern fonts are released under the GUST font license, which is -legally equivalent to the LaTeX Project Public License. See the files in this -directory for details. The fonts can be obtained from several TeX distributions -or package managers. - -The Latin Modern WOFF font has been obtained from -http://www.gust.org.pl/projects/e-foundry/ - -The Temml.woff2 is a clone of KaTeX_Script-Regular, except that the code points -have been changed from ASCII to Unicode Mathematical Alphanumeric Symbols Script capitals, -Unicode range 1D49C to 1D4B5. -*/ - -@font-face { - font-family: 'Temml'; - src: url('Temml.woff2') format('woff2'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: Latin Modern Math; - src: url('./latinmodernmath.woff2'); -} - -math { - font-style: normal; - font-weight: normal; - line-height: normal; - font-size-adjust: none; - text-indent: 0; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - direction: ltr; - /* Prevent Firefox from omitting the dot on i or j. */ - font-feature-settings: "dtls" off; -} - -math * { - border-color: currentColor; -} - -math { - font-family: "Latin Modern Math", math; -} - -/* display: block is necessary in Firefox and Safari. - * Not in Chromium, which recognizes display: "block math" written inline. */ - math.tml-display { - display: block; - width: 100%; -} - -*.mathscr { - font-family: "Temml"; -} - -/* Cramped superscripts in WebKit */ -mfrac > :nth-child(2), -msqrt, -mover > :first-child { - math-shift: compact -} - -.menclose { - display: inline-block; - position: relative; - padding: 0.5ex 0ex; -} -.tml-cancelto { - display: inline-block; - position: absolute; - top: 0; - left: 0; - padding: 0.5ex 0ex; - background-color: currentColor; - /* Use the SVG as an alpha mask (painted by background-color) */ - -webkit-mask-image: url("data:image/svg+xml,"); - mask-image: url("data:image/svg+xml,"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100%; - -webkit-mask-position: 0 0; - mask-position: 0 0; -} - -@supports (-moz-appearance: none) { - /* \vec w/o italic correction for Firefox */ - .tml-vec { - transform: scale(0.75) - } - /* Fix \cancelto in Firefox */ - .ff-narrow { - width: 0em; - } - .ff-nudge-left { - margin-left: -0.2em; - } -} - -@supports (not (-moz-appearance: none)) { - /* Chromium and WebKit */ - /* prime vertical alignment */ - mo.tml-prime { - font-family: Temml; - } - /* Italic correction on superscripts */ - .tml-sml-pad { - padding-left: 0.05em; - } - .tml-med-pad { - padding-left: 0.10em; - } - .tml-lrg-pad { - padding-left: 0.15em; - } -} - -@supports (-webkit-backdrop-filter: blur(1px)) { - /* WebKit vertical & italic correction on accents */ - .wbk-acc { - /* lower by x-height distance */ - transform: translate(0em, 0.431em); - } - .wbk-sml { - transform: translate(0.07em, 0); - } - .wbk-sml-acc { - transform: translate(0.07em, 0.431em); - } - .wbk-sml-vec { - transform: scale(0.75) translate(0.07em, 0); - } - .wbk-med { - transform: translate(0.14em, 0); - } - .wbk-med-acc { - transform: translate(0.14em, 0.431em); - } - .wbk-med-vec { - transform: scale(0.75) translate(0.14em, 0); - } - .wbk-lrg { - transform: translate(0.21em, 0); - } - .wbk-lrg-acc { - transform: translate(0.21em, 0.431em); - } - .wbk-lrg-vec { - transform: scale(0.75) translate(0.21em, 0); - } -} - -/* \cancel & \phase use background images. Get them to print. */ -menclose { - -webkit-print-color-adjust: exact; /* Chrome & Edge */ - print-color-adjust: exact; -} - -/* Array cell justification in Firefox & WebKit */ -.tml-right { - text-align: right; -} -.tml-left { - text-align: left; -} - -/* For CD labels that grow to the left in Firefox and WebKit */ -.tml-shift-left { margin-left:-200% } - -/* Styles for Chromium only */ -@supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) { - /* Italic correction on accents */ - .chr-sml { - transform: translate(0.07em, 0) - } - .chr-sml-vec { - transform: scale(0.75) translate(0.07em, 0) - } - .chr-med { - transform: translate(0.14em, 0) - } - .chr-med-vec { - transform: scale(0.75) translate(0.14em, 0) - } - .chr-lrg { - transform: translate(0.21em, 0) - } - .chr-lrg-vec { - transform: scale(0.75) translate(0.21em, 0) - } - - /* For CD labels that grow to the left */ - .tml-shift-left { margin-left:-100% } - - /* MathML Core & Chromium do not support the MathML 3.0 element attributes. */ - /* So use styles. */ - menclose { - position: relative; - padding: 0.5ex 0ex; - } - - .tml-overline { - padding: 0.1em 0 0 0; - border-top: 0.065em solid; - } - - .tml-underline { - padding: 0 0 0.1em 0; - border-bottom: 0.065em solid; - } - - .tml-cancel { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: 0; - width: 100%; - height: 100%; - background-color: currentColor; - } - .upstrike { - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - .downstrike { - clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%); - } - .sout { - clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em)); - } - .tml-xcancel { - clip-path: polygon(0.05em 0em, 0em 0.05em, calc(50% - 0.05em) 50%, 0em calc(100% - 0.05em), 0.05em 100%, 50% calc(50% + 0.05em), calc(100% - 0.05em) 100%, 100% calc(100% - 0.05em), calc(50% + 0.05em) 50%, 100% 0.05em, calc(100% - 0.05em) 0%, 50% calc(50% - 0.05em)); - } - - .longdiv-top { - border-top: 0.067em solid; - padding: 0.1em 0.2em 0.2em 0.433em; - } - .longdiv-arc { - position: absolute; - top: 0; - bottom: 0.1em; - left: -0.4em; - width: 0.7em; - border: 0.067em solid; - transform: translateY(-0.067em); - border-radius: 70%; - clip-path: inset(0 0 0 0.4em); - box-sizing: border-box;} - .menclose {display: inline-block; - text-align: left; - position: relative; - } - - .phasor-bottom { - border-bottom: 0.067em solid; - padding: 0.2em 0.2em 0.1em 0.6em; - } - .phasor-angle { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: -0.04em; - height: 100%; - aspect-ratio: 0.5; - background-color: currentColor; - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - - .tml-fbox { - padding: 3pt; - border: 1px solid; - } - - .circle-pad { - padding: 0.267em; - } - .textcircle { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - border: 0.067em solid; - border-radius: 50%; - } - - .actuarial { - padding: 0.03889em 0.03889em 0 0.03889em; - border-width: 0.08em 0.08em 0em 0em; - border-style: solid; - margin-right: 0.03889em; - } - - /* Stretch \widetilde */ - .tml-crooked-2 { - transform: scale(2.0, 1.1) - } - .tml-crooked-3 { - transform: scale(3.0, 1.3) - } - .tml-crooked-4 { - transform: scale(4.0, 1.4) - } - /* set array cell justification */ - .tml-right { - text-align: -webkit-right; - } - .tml-left { - text-align: -webkit-left; - } -} - -.special-fraction { - font-family: 'Times New Roman', Times, 'STIX TWO', Tinos, serif; -} - -/* flex-wrap for line-breaking in Chromium */ -math { - display: inline-flex; - flex-wrap: wrap; - align-items: baseline; -} -math > mrow { - padding: 0.5ex 0ex; -} - -/* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */ -/* We adjust for jot and small */ -mtable.tml-jot mtd { - padding-top: 0.7ex; - padding-bottom: 0.7ex; -} -mtable.tml-small mtd { - padding-top: 0.35ex; - padding-bottom: 0.35ex; -} - -/* Firefox */ -@-moz-document url-prefix() { - /* Avoid flex-wrap */ - math { display: inline; } - math > mrow { padding: 0 } - /* Adjust Firefox spacing between array rows */ - mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; } - mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; } -} - -/* AMS environment auto-numbering via CSS counter. */ -.tml-eqn::before { - counter-increment: tmlEqnNo; - content: "(" counter(tmlEqnNo) ")"; -} - -body { - counter-reset: tmlEqnNo; -} diff --git a/dist/Temml-Libertinus.css b/dist/Temml-Libertinus.css deleted file mode 100644 index 06f3a4a7..00000000 --- a/dist/Temml-Libertinus.css +++ /dev/null @@ -1,358 +0,0 @@ -/* -The Latin Modern fonts are released under the Open Font License, version 1.1. -See https://github.com/alerque/libertinus/blob/master/OFL.txt. - -The Libertinus WOFF font has been obtained from -https://github.com/alerque/libertinus - -The Temml.woff2 is a clone of KaTeX_Script-Regular, except that the code points -have been changed from ASCII to Unicode Mathematical Alphanumeric Symbols Script capitals, -Unicode range 1D49C to 1D4B5. -*/ - -@font-face { - font-family: 'Temml'; - src: url('Temml.woff2') format('woff2'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: Libertinus Math; - src: url('./LibertinusMath-Regular.woff2'); -} - -math { - font-style: normal; - font-weight: normal; - line-height: normal; - font-size-adjust: none; - text-indent: 0; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - direction: ltr; - /* Prevent Firefox from omitting the dot on i or j. */ - font-feature-settings: "dtls" off; -} - -math * { - border-color: currentColor; -} - -math { - font-family: Libertinus Math, math; -} - -/* display: block is necessary in Firefox and Safari. - * Not in Chromium, which recognizes display: "block math" written inline. */ - math.tml-display { - display: block; - width: 100%; -} - -*.mathcal { - font-family: "Cambria Math", 'STIXTwoMath-Regular', "Times New Roman", math; -} - -mo.tml-prime { - font-feature-settings: 'ssty'; -} - -/* Cramped superscripts in WebKit */ -mfrac > :nth-child(2), -msqrt, -mover > :first-child { - math-shift: compact -} - -.menclose { - display: inline-block; - position: relative; - padding: 0.5ex 0ex; -} -.tml-cancelto { - display: inline-block; - position: absolute; - top: 0; - left: 0; - padding: 0.5ex 0ex; - background-color: currentColor; - /* Use the SVG as an alpha mask (painted by background-color) */ - -webkit-mask-image: url("data:image/svg+xml,"); - mask-image: url("data:image/svg+xml,"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100%; - -webkit-mask-position: 0 0; - mask-position: 0 0; -} - -@supports (-moz-appearance: none) { - /* \vec w/o italic correction for Firefox */ - .tml-vec { - transform: scale(0.75) - } - /* Fix \cancelto in Firefox */ - .ff-narrow { - width: 0em; - } - .ff-nudge-left { - margin-left: -0.2em; - } -} - -@supports (not (-moz-appearance: none)) { - /* Chromium and WebKit */ - /* prime vertical alignment */ - mo.tml-prime { - font-family: Temml; - } - /* Italic correction on superscripts */ - .tml-sml-pad { - padding-left: 0.05em; - } - .tml-med-pad { - padding-left: 0.10em; - } - .tml-lrg-pad { - padding-left: 0.15em; - } -} - -@supports (-webkit-backdrop-filter: blur(1px)) { - /* WebKit vertical & italic correction on accents */ - .wbk-acc { - /* lower by x-height distance */ - transform: translate(0em, 0.431em); - } - .wbk-sml { - transform: translate(0.07em, 0); - } - .wbk-sml-acc { - transform: translate(0.07em, 0.431em); - } - .wbk-sml-vec { - transform: scale(0.75) translate(0.07em, 0); - } - .wbk-med { - transform: translate(0.14em, 0); - } - .wbk-med-acc { - transform: translate(0.14em, 0.431em); - } - .wbk-med-vec { - transform: scale(0.75) translate(0.14em, 0); - } - .wbk-lrg { - transform: translate(0.21em, 0); - } - .wbk-lrg-acc { - transform: translate(0.21em, 0.431em); - } - .wbk-lrg-vec { - transform: scale(0.75) translate(0.21em, 0); - } -} - -/* \cancel & \phase use background images. Get them to print. */ -menclose { - -webkit-print-color-adjust: exact; /* Chrome & Edge */ - print-color-adjust: exact; -} - -/* Array cell justification in Firefox & WebKit */ -.tml-right { - text-align: right; -} -.tml-left { - text-align: left; -} - -/* For CD labels that grow to the left in Firefox and WebKit */ -.tml-shift-left { margin-left:-200% } - -/* Styles for Chromium only */ -@supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) { - /* Italic correction on accents */ - .chr-sml { - transform: translate(0.07em, 0) - } - .chr-sml-vec { - transform: scale(0.75) translate(0.07em, 0) - } - .chr-med { - transform: translate(0.14em, 0) - } - .chr-med-vec { - transform: scale(0.75) translate(0.14em, 0) - } - .chr-lrg { - transform: translate(0.21em, 0) - } - .chr-lrg-vec { - transform: scale(0.75) translate(0.21em, 0) - } - - /* For CD labels that grow to the left */ - .tml-shift-left { margin-left:-100% } - - /* MathML Core & Chromium do not support the MathML 3.0 element attributes. */ - /* So use styles. */ - menclose { - position: relative; - padding: 0.5ex 0ex; - } - - .tml-overline { - padding: 0.1em 0 0 0; - border-top: 0.065em solid; - } - - .tml-underline { - padding: 0 0 0.1em 0; - border-bottom: 0.065em solid; - } - - .tml-cancel { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: 0; - width: 100%; - height: 100%; - background-color: currentColor; - } - .upstrike { - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - .downstrike { - clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%); - } - .sout { - clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em)); - } - .tml-xcancel { - clip-path: polygon(0.05em 0em, 0em 0.05em, calc(50% - 0.05em) 50%, 0em calc(100% - 0.05em), 0.05em 100%, 50% calc(50% + 0.05em), calc(100% - 0.05em) 100%, 100% calc(100% - 0.05em), calc(50% + 0.05em) 50%, 100% 0.05em, calc(100% - 0.05em) 0%, 50% calc(50% - 0.05em)); - } - - .longdiv-top { - border-top: 0.067em solid; - padding: 0.1em 0.2em 0.2em 0.433em; - } - .longdiv-arc { - position: absolute; - top: 0; - bottom: 0.1em; - left: -0.4em; - width: 0.7em; - border: 0.067em solid; - transform: translateY(-0.067em); - border-radius: 70%; - clip-path: inset(0 0 0 0.4em); - box-sizing: border-box;} - .menclose {display: inline-block; - text-align: left; - position: relative; - } - - .phasor-bottom { - border-bottom: 0.067em solid; - padding: 0.2em 0.2em 0.1em 0.6em; - } - .phasor-angle { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: -0.04em; - height: 100%; - aspect-ratio: 0.5; - background-color: currentColor; - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - - .tml-fbox { - padding: 3pt; - border: 1px solid; - } - - .circle-pad { - padding: 0.267em; - } - .textcircle { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - border: 0.067em solid; - border-radius: 50%; - } - - .actuarial { - padding: 0.03889em 0.03889em 0 0.03889em; - border-width: 0.08em 0.08em 0em 0em; - border-style: solid; - margin-right: 0.03889em; - } - - /* Stretch \widetilde */ - .tml-crooked-2 { - transform: scale(2.0, 1.1) - } - .tml-crooked-3 { - transform: scale(3.0, 1.3) - } - .tml-crooked-4 { - transform: scale(4.0, 1.4) - } - /* set array cell justification */ - .tml-right { - text-align: -webkit-right; - } - .tml-left { - text-align: -webkit-left; - } -} - -/* flex-wrap for line-breaking in Chromium */ -math { - display: inline-flex; - flex-wrap: wrap; - align-items: baseline; -} -math > mrow { - padding: 0.5ex 0ex; -} - -/* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */ -/* We adjust for jot and small */ -mtable.tml-jot mtd { - padding-top: 0.7ex; - padding-bottom: 0.7ex; -} -mtable.tml-small mtd { - padding-top: 0.35ex; - padding-bottom: 0.35ex; -} - -/* Firefox */ -@-moz-document url-prefix() { - /* Avoid flex-wrap */ - math { display: inline; } - math > mrow { padding: 0 } - /* Adjust Firefox spacing between array rows */ - mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; } - mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; } -} - -/* AMS environment auto-numbering via CSS counter. */ -.tml-eqn::before { - counter-increment: tmlEqnNo; - content: "(" counter(tmlEqnNo) ")"; -} - -body { - counter-reset: tmlEqnNo; -} diff --git a/dist/Temml-Local.css b/dist/Temml-Local.css deleted file mode 100644 index 0c1c1531..00000000 --- a/dist/Temml-Local.css +++ /dev/null @@ -1,353 +0,0 @@ -/* -Temml.woff2 is a clone of KaTeX_Script-Regular, except that the code points -have been changed from ASCII to Unicode Mathematical Alphanumeric Symbols Script capitals, -Unicode range 1D49C to 1D4B5. -*/ - -@font-face { - font-family: 'Temml'; - src: url('Temml.woff2') format('woff2'); - font-weight: normal; - font-style: normal; -} - -math { - font-family: "Cambria Math", 'STIXTwoMath-Regular', 'NotoSansMath-Regular', math; - font-style: normal; - font-weight: normal; - line-height: normal; - font-size-adjust: none; - text-indent: 0; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - direction: ltr; - /* Prevent Firefox from omitting the dot on i or j. */ - font-feature-settings: "dtls" off; -} - -math * { - border-color: currentColor; -} - -/* display: block is necessary in Firefox and Safari. - * Not in Chromium, which recognizes display: "block math" written inline. */ - math.tml-display { - display: block; - width: 100%; -} - -*.mathcal { - /* NotoSans */ - font-feature-settings: 'ss01'; -} - -math .mathscr { - font-family: "Temml"; -} - -mo.tml-prime { - font-family: Temml; -} - -/* Cramped superscripts in WebKit */ -mfrac > :nth-child(2), -msqrt, -mover > :first-child { - math-shift: compact -} - -.menclose { - display: inline-block; - position: relative; - padding: 0.5ex 0ex; -} -.tml-cancelto { - display: inline-block; - position: absolute; - top: 0; - left: 0; - padding: 0.5ex 0ex; - background-color: currentColor; - /* Use the SVG as an alpha mask (painted by background-color) */ - -webkit-mask-image: url("data:image/svg+xml,"); - mask-image: url("data:image/svg+xml,"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100%; - -webkit-mask-position: 0 0; - mask-position: 0 0; -} - -@supports (-moz-appearance: none) { - /* \vec w/o italic correction for Firefox */ - .tml-vec { - transform: scale(0.75) - } - /* Fix \cancelto in Firefox */ - .ff-narrow { - width: 0em; - } - .ff-nudge-left { - margin-left: -0.2em; - } -} - -@supports (not (-moz-appearance: none)) { - /* Chromium and WebKit */ - /* prime vertical alignment */ - mo.tml-prime { - font-family: Temml; - } - /* Italic correction on superscripts */ - .tml-sml-pad { - padding-left: 0.05em; - } - .tml-med-pad { - padding-left: 0.10em; - } - .tml-lrg-pad { - padding-left: 0.15em; - } -} - -@supports (-webkit-backdrop-filter: blur(1px)) { - /* WebKit vertical & italic correction on accents */ - .wbk-acc { - /* lower by x-height distance */ - transform: translate(0em, 0.431em); - } - .wbk-sml { - transform: translate(0.07em, 0); - } - .wbk-sml-acc { - transform: translate(0.07em, 0.431em); - } - .wbk-sml-vec { - transform: scale(0.75) translate(0.07em, 0); - } - .wbk-med { - transform: translate(0.14em, 0); - } - .wbk-med-acc { - transform: translate(0.14em, 0.431em); - } - .wbk-med-vec { - transform: scale(0.75) translate(0.14em, 0); - } - .wbk-lrg { - transform: translate(0.21em, 0); - } - .wbk-lrg-acc { - transform: translate(0.21em, 0.431em); - } - .wbk-lrg-vec { - transform: scale(0.75) translate(0.21em, 0); - } -} - -/* \cancel & \phase use background images. Get them to print. */ -menclose { - -webkit-print-color-adjust: exact; /* Chrome & Edge */ - print-color-adjust: exact; -} - -/* Array cell justification in Firefox & WebKit */ -.tml-right { - text-align: right; -} -.tml-left { - text-align: left; -} - -/* For CD labels that grow to the left in Firefox and WebKit */ -.tml-shift-left { margin-left:-200% } - -/* Styles for Chromium only */ -@supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) { - /* Italic correction on accents */ - .chr-sml { - transform: translate(0.07em, 0) - } - .chr-sml-vec { - transform: scale(0.75) translate(0.07em, 0) - } - .chr-med { - transform: translate(0.14em, 0) - } - .chr-med-vec { - transform: scale(0.75) translate(0.14em, 0) - } - .chr-lrg { - transform: translate(0.21em, 0) - } - .chr-lrg-vec { - transform: scale(0.75) translate(0.21em, 0) - } - - /* For CD labels that grow to the left */ - .tml-shift-left { margin-left:-100% } - - /* MathML Core & Chromium do not support the MathML 3.0 element attributes. */ - /* So use styles. */ - menclose { - position: relative; - padding: 0.5ex 0ex; - } - - .tml-overline { - padding: 0.1em 0 0 0; - border-top: 0.065em solid; - } - - .tml-underline { - padding: 0 0 0.1em 0; - border-bottom: 0.065em solid; - } - - .tml-cancel { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: 0; - width: 100%; - height: 100%; - background-color: currentColor; - } - .upstrike { - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - .downstrike { - clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%); - } - .sout { - clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em)); - } - .tml-xcancel { - clip-path: polygon(0.05em 0em, 0em 0.05em, calc(50% - 0.05em) 50%, 0em calc(100% - 0.05em), 0.05em 100%, 50% calc(50% + 0.05em), calc(100% - 0.05em) 100%, 100% calc(100% - 0.05em), calc(50% + 0.05em) 50%, 100% 0.05em, calc(100% - 0.05em) 0%, 50% calc(50% - 0.05em)); - } - - .longdiv-top { - border-top: 0.067em solid; - padding: 0.1em 0.2em 0.2em 0.433em; - } - .longdiv-arc { - position: absolute; - top: 0; - bottom: 0.1em; - left: -0.4em; - width: 0.7em; - border: 0.067em solid; - transform: translateY(-0.067em); - border-radius: 70%; - clip-path: inset(0 0 0 0.4em); - box-sizing: border-box;} - .menclose {display: inline-block; - text-align: left; - position: relative; - } - - .phasor-bottom { - border-bottom: 0.067em solid; - padding: 0.2em 0.2em 0.1em 0.6em; - } - .phasor-angle { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: -0.04em; - height: 100%; - aspect-ratio: 0.5; - background-color: currentColor; - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - - .tml-fbox { - padding: 3pt; - border: 1px solid; - } - - .circle-pad { - padding: 0.267em; - } - .textcircle { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - border: 0.067em solid; - border-radius: 50%; - } - - .actuarial { - padding: 0.03889em 0.03889em 0 0.03889em; - border-width: 0.08em 0.08em 0em 0em; - border-style: solid; - margin-right: 0.03889em; - } - - /* Stretch \widetilde */ - .tml-crooked-2 { - transform: scale(2.0, 1.1) - } - .tml-crooked-3 { - transform: scale(3.0, 1.3) - } - .tml-crooked-4 { - transform: scale(4.0, 1.4) - } - /* set array cell justification */ - .tml-right { - text-align: -webkit-right; - } - .tml-left { - text-align: -webkit-left; - } -} - -.special-fraction { - font-family: 'STIX TWO', 'Times New Roman', Times, Tinos, serif; -} - -/* flex-wrap for line-breaking in Chromium */ -math { - display: inline-flex; - flex-wrap: wrap; - align-items: baseline; -} -math > mrow { - padding: 0.5ex 0ex; -} - -/* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */ -/* We adjust for jot and small */ -mtable.tml-jot mtd { - padding-top: 0.7ex; - padding-bottom: 0.7ex; -} -mtable.tml-small mtd { - padding-top: 0.35ex; - padding-bottom: 0.35ex; -} - -/* Firefox */ -@-moz-document url-prefix() { - /* Avoid flex-wrap */ - math { display: inline; } - math > mrow { padding: 0 } - /* Adjust Firefox spacing between array rows */ - mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; } - mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; } -} - -/* AMS environment auto-numbering via CSS counter. */ -.tml-eqn::before { - counter-increment: tmlEqnNo; - content: "(" counter(tmlEqnNo) ")"; -} - -body { - counter-reset: tmlEqnNo; -} diff --git a/dist/Temml-NotoSans.css b/dist/Temml-NotoSans.css deleted file mode 100644 index cfa4db42..00000000 --- a/dist/Temml-NotoSans.css +++ /dev/null @@ -1,357 +0,0 @@ -/* -Noto Math fonts are released under the SIL OPEN FONT LICENSE Version 1.1. - -The NotoMath font has been obtained from -https://github.com/notofonts/math -*/ - -@font-face { - font-family: NotoSans Math; - src: url('NotoSansMath-Regular.ttf'); -} - -math { - font-style: normal; - font-weight: normal; - line-height: normal; - font-size-adjust: none; - text-indent: 0; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - direction: ltr; - /* Prevent Firefox from omitting the dot on i or j. */ - font-feature-settings: "dtls" off; -} - -math * { - border-color: currentColor; -} - -math { - font-family: "NotoSans Math", math; -} - -/* display: block is necessary in Firefox and Safari. - * Not in Chromium, which recognizes display: "block math" written inline. */ - math.tml-display { - display: block; - width: 100%; -} - -math .mathscr { - font-family: "ssty1"; -} - -*.mathcal { - font-feature-settings: 'ss01'; -} - -/* Chromium prime glyph */ -mo.tml-prime { - font-feature-settings: 'ssty'; -} - -/* Cramped superscripts in WebKit */ -mfrac > :nth-child(2), -msqrt, -mover > :first-child { - math-shift: compact -} - -.menclose { - display: inline-block; - position: relative; - padding: 0.5ex 0ex; -} -.tml-cancelto { - display: inline-block; - position: absolute; - top: 0; - left: 0; - padding: 0.5ex 0ex; - background-color: currentColor; - /* Use the SVG as an alpha mask (painted by background-color) */ - -webkit-mask-image: url("data:image/svg+xml,"); - mask-image: url("data:image/svg+xml,"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100%; - -webkit-mask-position: 0 0; - mask-position: 0 0; -} - -@supports (-moz-appearance: none) { - /* \vec w/o italic correction for Firefox */ - .tml-vec { - transform: scale(0.75) - } - /* Fix \cancelto in Firefox */ - .ff-narrow { - width: 0em; - } - .ff-nudge-left { - margin-left: -0.2em; - } -} - -@supports (not (-moz-appearance: none)) { - /* Chromium and WebKit */ - /* prime vertical alignment */ - mo.tml-prime { - font-feature-settings: 'ssty'; - } - /* Italic correction on superscripts */ - .tml-sml-pad { - padding-left: 0.05em; - } - .tml-med-pad { - padding-left: 0.10em; - } - .tml-lrg-pad { - padding-left: 0.15em; - } -} - -@supports (-webkit-backdrop-filter: blur(1px)) { - /* WebKit vertical & italic correction on accents */ - .wbk-acc { - /* lower by x-height distance */ - transform: translate(0em, 0.431em); - } - .wbk-sml { - transform: translate(0.07em, 0); - } - .wbk-sml-acc { - transform: translate(0.07em, 0.431em); - } - .wbk-sml-vec { - transform: scale(0.75) translate(0.07em, 0); - } - .wbk-med { - transform: translate(0.14em, 0); - } - .wbk-med-acc { - transform: translate(0.14em, 0.431em); - } - .wbk-med-vec { - transform: scale(0.75) translate(0.14em, 0); - } - .wbk-lrg { - transform: translate(0.21em, 0); - } - .wbk-lrg-acc { - transform: translate(0.21em, 0.431em); - } - .wbk-lrg-vec { - transform: scale(0.75) translate(0.21em, 0); - } -} - -/* \cancel & \phase use background images. Get them to print. */ -menclose { - -webkit-print-color-adjust: exact; /* Chrome & Edge */ - print-color-adjust: exact; -} - -/* Array cell justification in Firefox & WebKit */ -.tml-right { - text-align: right; -} -.tml-left { - text-align: left; -} - -/* For CD labels that grow to the left in Firefox and WebKit */ -.tml-shift-left { margin-left:-200% } - -/* Styles for Chromium only */ -@supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) { - /* Italic correction on accents */ - .chr-sml { - transform: translate(0.07em, 0) - } - .chr-sml-vec { - transform: scale(0.75) translate(0.07em, 0) - } - .chr-med { - transform: translate(0.14em, 0) - } - .chr-med-vec { - transform: scale(0.75) translate(0.14em, 0) - } - .chr-lrg { - transform: translate(0.21em, 0) - } - .chr-lrg-vec { - transform: scale(0.75) translate(0.21em, 0) - } - - /* For CD labels that grow to the left */ - .tml-shift-left { margin-left:-100% } - - /* MathML Core & Chromium do not support the MathML 3.0 element attributes. */ - /* So use styles. */ - menclose { - position: relative; - padding: 0.5ex 0ex; - } - - .tml-overline { - padding: 0.1em 0 0 0; - border-top: 0.065em solid; - } - - .tml-underline { - padding: 0 0 0.1em 0; - border-bottom: 0.065em solid; - } - - .tml-cancel { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: 0; - width: 100%; - height: 100%; - background-color: currentColor; - } - .upstrike { - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - .downstrike { - clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%); - } - .sout { - clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em)); - } - .tml-xcancel { - clip-path: polygon(0.05em 0em, 0em 0.05em, calc(50% - 0.05em) 50%, 0em calc(100% - 0.05em), 0.05em 100%, 50% calc(50% + 0.05em), calc(100% - 0.05em) 100%, 100% calc(100% - 0.05em), calc(50% + 0.05em) 50%, 100% 0.05em, calc(100% - 0.05em) 0%, 50% calc(50% - 0.05em)); - } - - .longdiv-top { - border-top: 0.067em solid; - padding: 0.1em 0.2em 0.2em 0.433em; - } - .longdiv-arc { - position: absolute; - top: 0; - bottom: 0.1em; - left: -0.4em; - width: 0.7em; - border: 0.067em solid; - transform: translateY(-0.067em); - border-radius: 70%; - clip-path: inset(0 0 0 0.4em); - box-sizing: border-box;} - .menclose {display: inline-block; - text-align: left; - position: relative; - } - - .phasor-bottom { - border-bottom: 0.067em solid; - padding: 0.2em 0.2em 0.1em 0.6em; - } - .phasor-angle { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: -0.04em; - height: 100%; - aspect-ratio: 0.5; - background-color: currentColor; - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - - .tml-fbox { - padding: 3pt; - border: 1px solid; - } - - .circle-pad { - padding: 0.267em; - } - .textcircle { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - border: 0.067em solid; - border-radius: 50%; - } - - .actuarial { - padding: 0.03889em 0.03889em 0 0.03889em; - border-width: 0.08em 0.08em 0em 0em; - border-style: solid; - margin-right: 0.03889em; - } - - /* Stretch \widetilde */ - .tml-crooked-2 { - transform: scale(2.0, 1.1) - } - .tml-crooked-3 { - transform: scale(3.0, 1.3) - } - .tml-crooked-4 { - transform: scale(4.0, 1.4) - } - /* set array cell justification */ - .tml-right { - text-align: -webkit-right; - } - .tml-left { - text-align: -webkit-left; - } -} - -/* Adjust WebKit accents */ -@supports (-webkit-backdrop-filter: blur(1px)) { - .tml-xshift { transform: translate(0px, 0.45em) } - .tml-capshift { transform: translate(0px, 0.35em) } -} - -/* flex-wrap for line-breaking in Chromium */ -math { - display: inline-flex; - flex-wrap: wrap; - align-items: baseline; -} -math > mrow { - padding: 0.5ex 0ex; -} - -/* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */ -/* We adjust for jot and small */ -mtable.tml-jot mtd { - padding-top: 0.7ex; - padding-bottom: 0.7ex; -} -mtable.tml-small mtd { - padding-top: 0.35ex; - padding-bottom: 0.35ex; -} - -/* Firefox */ -@-moz-document url-prefix() { - /* Avoid flex-wrap */ - math { display: inline; } - math > mrow { padding: 0 } - /* Adjust Firefox spacing between array rows */ - mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; } - mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; } -} - -/* AMS environment auto-numbering via CSS counter. */ -.tml-eqn::before { - counter-increment: tmlEqnNo; - content: "(" counter(tmlEqnNo) ")"; -} - -body { - counter-reset: tmlEqnNo; -} diff --git a/dist/Temml-STIX2.css b/dist/Temml-STIX2.css deleted file mode 100644 index 46906915..00000000 --- a/dist/Temml-STIX2.css +++ /dev/null @@ -1,348 +0,0 @@ -/* -XITS is released under the SIL Open Font License. -See https://github.com/stipub/stixfonts/blob/master/OFL.txt details. - -The STIX2 WOFF font has been obtained from -https://github.com/stipub/stixfonts -*/ - -@font-face { - font-family: STIX2; - src: local('STIXTwoMath'), - url('./STIXTwoMath.woff2'); -} - -math { - font-style: normal; - font-weight: normal; - line-height: normal; - font-size-adjust: none; - text-indent: 0; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - direction: ltr; - /* Prevent Firefox from omitting the dot on i or j. */ - font-feature-settings: "dtls" off; -} - -math * { - border-color: currentColor; -} - -/* display: block is necessary in Firefox and Safari. - * Not in Chromium, which recognizes display: "block math" written inline. */ - math.tml-display { - display: block; - width: 100%; -} - -math { - font-family: STIX2, math; -} - -*.mathscr { - font-feature-settings: 'ss01'; -} - -mo.tml-prime { - font-feature-settings: 'ss04'; -} - -/* Cramped superscripts in WebKit */ -mfrac > :nth-child(2), -msqrt, -mover > :first-child { - math-shift: compact -} - -.menclose { - display: inline-block; - position: relative; - padding: 0.5ex 0ex; -} -.tml-cancelto { - display: inline-block; - position: absolute; - top: 0; - left: 0; - padding: 0.5ex 0ex; - background-color: currentColor; - /* Use the SVG as an alpha mask (painted by background-color) */ - -webkit-mask-image: url("data:image/svg+xml,"); - mask-image: url("data:image/svg+xml,"); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: 100% 100%; - mask-size: 100% 100%; - -webkit-mask-position: 0 0; - mask-position: 0 0; -} - -@supports (-moz-appearance: none) { - /* \vec w/o italic correction for Firefox */ - .tml-vec { - transform: scale(0.75) - } - /* Fix \cancelto in Firefox */ - .ff-narrow { - width: 0em; - } - .ff-nudge-left { - margin-left: -0.2em; - } -} - -@supports (not (-moz-appearance: none)) { - /* Chromium and WebKit */ - /* prime vertical alignment */ - mo.tml-prime { - font-feature-settings: 'ss04'; - } - /* Italic correction on superscripts */ - .tml-sml-pad { - padding-left: 0.05em; - } - .tml-med-pad { - padding-left: 0.10em; - } - .tml-lrg-pad { - padding-left: 0.15em; - } -} - -@supports (-webkit-backdrop-filter: blur(1px)) { - /* WebKit vertical & italic correction on accents */ - .wbk-acc { - /* lower by x-height distance */ - transform: translate(0em, 0.431em); - } - .wbk-sml { - transform: translate(0.07em, 0); - } - .wbk-sml-acc { - transform: translate(0.07em, 0.431em); - } - .wbk-sml-vec { - transform: scale(0.75) translate(0.07em, 0); - } - .wbk-med { - transform: translate(0.14em, 0); - } - .wbk-med-acc { - transform: translate(0.14em, 0.431em); - } - .wbk-med-vec { - transform: scale(0.75) translate(0.14em, 0); - } - .wbk-lrg { - transform: translate(0.21em, 0); - } - .wbk-lrg-acc { - transform: translate(0.21em, 0.431em); - } - .wbk-lrg-vec { - transform: scale(0.75) translate(0.21em, 0); - } -} - -/* \cancel & \phase use background images. Get them to print. */ -menclose { - -webkit-print-color-adjust: exact; /* Chrome & Edge */ - print-color-adjust: exact; -} - -/* Array cell justification in Firefox & WebKit */ -.tml-right { - text-align: right; -} -.tml-left { - text-align: left; -} - -/* For CD labels that grow to the left in Firefox and WebKit */ -.tml-shift-left { margin-left:-200% } - -/* Styles for Chromium only */ -@supports (not (-webkit-backdrop-filter: blur(1px))) and (not (-moz-appearance: none)) { - /* Italic correction on accents */ - .chr-sml { - transform: translate(0.07em, 0) - } - .chr-sml-vec { - transform: scale(0.75) translate(0.07em, 0) - } - .chr-med { - transform: translate(0.14em, 0) - } - .chr-med-vec { - transform: scale(0.75) translate(0.14em, 0) - } - .chr-lrg { - transform: translate(0.21em, 0) - } - .chr-lrg-vec { - transform: scale(0.75) translate(0.21em, 0) - } - - /* For CD labels that grow to the left */ - .tml-shift-left { margin-left:-100% } - - /* MathML Core & Chromium do not support the MathML 3.0 element attributes. */ - /* So use styles. */ - menclose { - position: relative; - padding: 0.5ex 0ex; - } - - .tml-overline { - padding: 0.1em 0 0 0; - border-top: 0.065em solid; - } - - .tml-underline { - padding: 0 0 0.1em 0; - border-bottom: 0.065em solid; - } - - .tml-cancel { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: 0; - width: 100%; - height: 100%; - background-color: currentColor; - } - .upstrike { - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - .downstrike { - clip-path: polygon(0em 0.05em, 0.05em 0em, 100% calc(100% - 0.05em), calc(100% - 0.05em) 100%); - } - .sout { - clip-path: polygon(0em calc(55% + 0.0333em), 0em calc(55% - 0.0333em), 100% calc(55% - 0.0333em), 100% calc(55% + 0.0333em)); - } - .tml-xcancel { - clip-path: polygon(0.05em 0em, 0em 0.05em, calc(50% - 0.05em) 50%, 0em calc(100% - 0.05em), 0.05em 100%, 50% calc(50% + 0.05em), calc(100% - 0.05em) 100%, 100% calc(100% - 0.05em), calc(50% + 0.05em) 50%, 100% 0.05em, calc(100% - 0.05em) 0%, 50% calc(50% - 0.05em)); - } - - .longdiv-top { - border-top: 0.067em solid; - padding: 0.1em 0.2em 0.2em 0.433em; - } - .longdiv-arc { - position: absolute; - top: 0; - bottom: 0.1em; - left: -0.4em; - width: 0.7em; - border: 0.067em solid; - transform: translateY(-0.067em); - border-radius: 70%; - clip-path: inset(0 0 0 0.4em); - box-sizing: border-box;} - .menclose {display: inline-block; - text-align: left; - position: relative; - } - - .phasor-bottom { - border-bottom: 0.067em solid; - padding: 0.2em 0.2em 0.1em 0.6em; - } - .phasor-angle { - display: inline-block; - position: absolute; - left: 0.5px; - bottom: -0.04em; - height: 100%; - aspect-ratio: 0.5; - background-color: currentColor; - clip-path: polygon(0.05em 100%, 0em calc(100% - 0.05em), calc(100% - 0.05em) 0em, 100% 0.05em); - } - - .tml-fbox { - padding: 3pt; - border: 1px solid; - } - - .circle-pad { - padding: 0.267em; - } - .textcircle { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - border: 0.067em solid; - border-radius: 50%; - } - - .actuarial { - padding: 0.03889em 0.03889em 0 0.03889em; - border-width: 0.08em 0.08em 0em 0em; - border-style: solid; - margin-right: 0.03889em; - } - - /* Stretch \widetilde */ - .tml-crooked-2 { - transform: scale(2.0, 1.1) - } - .tml-crooked-3 { - transform: scale(3.0, 1.3) - } - .tml-crooked-4 { - transform: scale(4.0, 1.4) - } - /* set array cell justification */ - .tml-right { - text-align: -webkit-right; - } - .tml-left { - text-align: -webkit-left; - } -} - -/* flex-wrap for line-breaking in Chromium */ -math { - display: inline-flex; - flex-wrap: wrap; - align-items: baseline; -} -math > mrow { - padding: 0.5ex 0ex; -} - -/* Default mtd top padding is 0.5ex per MathML-Core and user-agent CSS */ -/* We adjust for jot and small */ -mtable.tml-jot mtd { - padding-top: 0.7ex; - padding-bottom: 0.7ex; -} -mtable.tml-small mtd { - padding-top: 0.35ex; - padding-bottom: 0.35ex; -} - -/* Firefox */ -@-moz-document url-prefix() { - /* Avoid flex-wrap */ - math { display: inline; } - math > mrow { padding: 0 } - /* Adjust Firefox spacing between array rows */ - mtd, mtable.tml-small mtd { padding-top: 0; padding-bottom: 0; } - mtable.tml-jot mtd { padding-top: 0.2ex; padding-bottom: 0.ex; } -} - -/* AMS environment auto-numbering via CSS counter. */ -.tml-eqn::before { - counter-increment: tmlEqnNo; - content: "(" counter(tmlEqnNo) ")"; -} - -body { - counter-reset: tmlEqnNo; -} diff --git a/dist/Temml.woff2 b/dist/Temml.woff2 deleted file mode 100644 index fccefda4..00000000 Binary files a/dist/Temml.woff2 and /dev/null differ diff --git a/dist/temml.cjs b/dist/temml.cjs deleted file mode 100644 index 97262217..00000000 --- a/dist/temml.cjs +++ /dev/null @@ -1,14685 +0,0 @@ -'use strict'; - -/** - * This is the ParseError class, which is the main error thrown by Temml - * functions when something has gone wrong. This is used to distinguish internal - * errors from errors in the expression that the user provided. - * - * If possible, a caller should provide a Token or ParseNode with information - * about where in the source string the problem occurred. - */ -class ParseError { - constructor( - message, // The error message - token // An object providing position information - ) { - let error = " " + message; - let start; - - const loc = token && token.loc; - if (loc && loc.start <= loc.end) { - // If we have the input and a position, make the error a bit fancier - - // Get the input - const input = loc.lexer.input; - - // Prepend some information - start = loc.start; - const end = loc.end; - if (start === input.length) { - error += " at end of input: "; - } else { - error += " at position " + (start + 1) + ": \n"; - } - - // Underline token in question using combining underscores - const underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332"); - - // Extract some context from the input and add it to the error - let left; - if (start > 15) { - left = "…" + input.slice(start - 15, start); - } else { - left = input.slice(0, start); - } - let right; - if (end + 15 < input.length) { - right = input.slice(end, end + 15) + "…"; - } else { - right = input.slice(end); - } - error += left + underlined + right; - } - - // Some hackery to make ParseError a prototype of Error - // See http://stackoverflow.com/a/8460753 - const self = new Error(error); - self.name = "ParseError"; - self.__proto__ = ParseError.prototype; - self.position = start; - return self; - } -} - -ParseError.prototype.__proto__ = Error.prototype; - -// -/** - * This file contains a list of utility functions which are useful in other - * files. - */ - -/** - * Provide a default value if a setting is undefined - */ -const deflt = function(setting, defaultIfUndefined) { - return setting === undefined ? defaultIfUndefined : setting; -}; - -// hyphenate and escape adapted from Facebook's React under Apache 2 license - -const uppercase = /([A-Z])/g; -const hyphenate = function(str) { - return str.replace(uppercase, "-$1").toLowerCase(); -}; - -const ESCAPE_LOOKUP = { - "&": "&", - ">": ">", - "<": "<", - '"': """, - "'": "'" -}; - -const ESCAPE_REGEX = /[&><"']/g; - -/** - * Escapes text to prevent scripting attacks. - */ -function escape(text) { - return String(text).replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]); -} - -/** - * Sometimes we want to pull out the innermost element of a group. In most - * cases, this will just be the group itself, but when ordgroups and colors have - * a single element, we want to pull that out. - */ -const getBaseElem = function(group) { - if (group.type === "ordgroup") { - if (group.body.length === 1) { - return getBaseElem(group.body[0]); - } else { - return group; - } - } else if (group.type === "color") { - if (group.body.length === 1) { - return getBaseElem(group.body[0]); - } else { - return group; - } - } else if (group.type === "font") { - return getBaseElem(group.body); - } else { - return group; - } -}; - -/** - * TeXbook algorithms often reference "character boxes", which are simply groups - * with a single character in them. To decide if something is a character box, - * we find its innermost group, and see if it is a single character. - */ -const isCharacterBox = function(group) { - const baseElem = getBaseElem(group); - - // These are all the types of groups which hold single characters - return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom" -}; - -const assert = function(value) { - if (!value) { - throw new Error("Expected non-null, but got " + String(value)); - } - return value; -}; - -/** - * Return the protocol of a URL, or "_relative" if the URL does not specify a - * protocol (and thus is relative), or `null` if URL has invalid protocol - * (so should be outright rejected). - */ -const protocolFromUrl = function(url) { - // Check for possible leading protocol. - // https://url.spec.whatwg.org/#url-parsing strips leading whitespace - // (\x00) or C0 control (\x00-\x1F) characters. - // eslint-disable-next-line no-control-regex - const protocol = /^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(url); - if (!protocol) { - return "_relative"; - } - // Reject weird colons - if (protocol[2] !== ":") { - return null; - } - // Reject invalid characters in scheme according to - // https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 - if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(protocol[1])) { - return null; - } - // Lowercase the protocol - return protocol[1].toLowerCase(); -}; - -/** - * Round `n` to 4 decimal places, or to the nearest 1/10,000th em. The TeXbook - * gives an acceptable rounding error of 100sp (which would be the nearest - * 1/6551.6em with our ptPerEm = 10): - * http://www.ctex.org/documents/shredder/src/texbook.pdf#page=69 - */ -const round = function(n) { - return +n.toFixed(4); -}; - -// Identify short letters. Used for accents and \cancelto. -const smalls = "acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚𝐜𝐞𝐠𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐮𝐯𝐰𝐱𝐲𝐳"; - -/** - * This is a module for storing settings passed into Temml. It correctly handles - * default settings. - */ - - -/** - * The main Settings object - */ -class Settings { - constructor(options) { - // allow null options - options = options || {}; - this.displayMode = deflt(options.displayMode, false); // boolean - this.annotate = deflt(options.annotate, false); // boolean - this.leqno = deflt(options.leqno, false); // boolean - this.throwOnError = deflt(options.throwOnError, false); // boolean - this.errorColor = deflt(options.errorColor, "#b22222"); // string - this.macros = options.macros || {}; - this.wrap = deflt(options.wrap, "none"); // "none" | "tex" | "=" - this.xml = deflt(options.xml, false); // boolean - this.colorIsTextColor = deflt(options.colorIsTextColor, false); // boolean - this.strict = deflt(options.strict, false); // boolean - this.trust = deflt(options.trust, false); // trust context. See html.js. - this.maxSize = (options.maxSize === undefined - ? [Infinity, Infinity] - : Array.isArray(options.maxSize) - ? options.maxSize - : [Infinity, Infinity] - ); - this.maxExpand = Math.max(0, deflt(options.maxExpand, 1000)); // number - this.wrapDelimiterPairs = true; // boolean - } - - /** - * Check whether to test potentially dangerous input, and return - * `true` (trusted) or `false` (untrusted). The sole argument `context` - * should be an object with `command` field specifying the relevant LaTeX - * command (as a string starting with `\`), and any other arguments, etc. - * If `context` has a `url` field, a `protocol` field will automatically - * get added by this function (changing the specified object). - */ - isTrusted(context) { - if (context.url && !context.protocol) { - const protocol = protocolFromUrl(context.url); - if (protocol == null) { - return false - } - context.protocol = protocol; - } - const trust = typeof this.trust === "function" ? this.trust(context) : this.trust; - return Boolean(trust); - } -} - -/** - * All registered functions. - * `functions.js` just exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary. - */ -const _functions = {}; - -/** - * All MathML builders. Should be only used in the `define*` and the `build*ML` - * functions. - */ -const _mathmlGroupBuilders = {}; - -function defineFunction({ - type, - names, - props, - handler, - mathmlBuilder -}) { - // Set default values of functions - const data = { - type, - numArgs: props.numArgs, - argTypes: props.argTypes, - allowedInArgument: !!props.allowedInArgument, - allowedInText: !!props.allowedInText, - allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath, - numOptionalArgs: props.numOptionalArgs || 0, - infix: !!props.infix, - primitive: !!props.primitive, - handler: handler - }; - for (let i = 0; i < names.length; ++i) { - _functions[names[i]] = data; - } - if (type) { - if (mathmlBuilder) { - _mathmlGroupBuilders[type] = mathmlBuilder; - } - } -} - -/** - * Use this to register only the MathML builder for a function(e.g. - * if the function's ParseNode is generated in Parser.js rather than via a - * stand-alone handler provided to `defineFunction`). - */ -function defineFunctionBuilders({ type, mathmlBuilder }) { - defineFunction({ - type, - names: [], - props: { numArgs: 0 }, - handler() { - throw new Error("Should never be called.") - }, - mathmlBuilder - }); -} - -const normalizeArgument = function(arg) { - return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg -}; - -// Since the corresponding buildMathML function expects a -// list of elements, we normalize for different kinds of arguments -const ordargument = function(arg) { - return arg.type === "ordgroup" ? arg.body : [arg] -}; - -/** - * This node represents a document fragment, which contains elements, but when - * placed into the DOM doesn't have any representation itself. It only contains - * children and doesn't have any DOM node properties. - */ -class DocumentFragment { - constructor(children) { - this.children = children; - this.classes = []; - this.style = {}; - } - - hasClass(className) { - return this.classes.includes(className); - } - - /** Convert the fragment into a node. */ - toNode() { - const frag = document.createDocumentFragment(); - - for (let i = 0; i < this.children.length; i++) { - frag.appendChild(this.children[i].toNode()); - } - - return frag; - } - - /** Convert the fragment into HTML markup. */ - toMarkup() { - let markup = ""; - - // Simply concatenate the markup for the children together. - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - return markup; - } - - /** - * Converts the math node into a string, similar to innerText. Applies to - * MathDomNode's only. - */ - toText() { - // To avoid this, we would subclass documentFragment separately for - // MathML, but polyfills for subclassing is expensive per PR 1469. - const toText = (child) => child.toText(); - return this.children.map(toText).join(""); - } -} - -/** - * These objects store the data about the DOM nodes we create, as well as some - * extra data. They can then be transformed into real DOM nodes with the - * `toNode` function or HTML markup using `toMarkup`. They are useful for both - * storing extra properties on the nodes, as well as providing a way to easily - * work with the DOM. - * - * Similar functions for working with MathML nodes exist in mathMLTree.js. - * - */ - -/** - * Create an HTML className based on a list of classes. In addition to joining - * with spaces, we also remove empty classes. - */ -const createClass = function(classes) { - return classes.filter((cls) => cls).join(" "); -}; - -const initNode = function(classes, style) { - this.classes = classes || []; - this.attributes = {}; - this.style = style || {}; -}; - -/** - * Convert into an HTML node - */ -const toNode = function(tagName) { - const node = document.createElement(tagName); - - // Apply the class - node.className = createClass(this.classes); - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - // Apply attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { - node.setAttribute(attr, this.attributes[attr]); - } - } - - // Append the children, also as HTML nodes - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - - return node; -}; - -/** - * Convert into an HTML markup string - */ -const toMarkup = function(tagName) { - let markup = `<${tagName}`; - - // Add the class - if (this.classes.length) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - - let styles = ""; - - // Add the styles, after hyphenation - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - styles += `${hyphenate(style)}:${this.style[style]};`; - } - } - - if (styles) { - markup += ` style="${styles}"`; - } - - // Add the attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { - markup += ` ${attr}="${escape(this.attributes[attr])}"`; - } - } - - markup += ">"; - - // Add the markup of the children, also as markup - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - markup += ``; - - return markup; -}; - -/** - * This node represents a span node, with a className, a list of children, and - * an inline style. - * - */ -class Span { - constructor(classes, children, style) { - initNode.call(this, classes, style); - this.children = children || []; - } - - setAttribute(attribute, value) { - this.attributes[attribute] = value; - } - - toNode() { - return toNode.call(this, "span"); - } - - toMarkup() { - return toMarkup.call(this, "span"); - } -} - -let TextNode$1 = class TextNode { - constructor(text) { - this.text = text; - } - toNode() { - return document.createTextNode(this.text); - } - toMarkup() { - return escape(this.text); - } -}; - -// Create an node. -class AnchorNode { - constructor(href, classes, children) { - this.href = href; - this.classes = classes; - this.children = children || []; - } - - toNode() { - const node = document.createElement("a"); - node.setAttribute("href", this.href); - if (this.classes.length > 0) { - node.className = createClass(this.classes); - } - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - return node - } - - toMarkup() { - let markup = ` 0) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - markup += ">"; - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - markup += ""; - return markup - } -} - -/* - * This node represents an image embed () element. - */ -class Img { - constructor(src, alt, style) { - this.alt = alt; - this.src = src; - this.classes = ["mord"]; - this.style = style; - } - - hasClass(className) { - return this.classes.includes(className); - } - - toNode() { - const node = document.createElement("img"); - node.src = this.src; - node.alt = this.alt; - node.className = "mord"; - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - return node; - } - - toMarkup() { - let markup = `${this.alt}` and - * `` tags). - */ -class MathNode { - constructor(type, children, classes, style) { - this.type = type; - this.attributes = {}; - this.children = children || []; - this.classes = classes || []; - this.style = style || {}; // Used for elements - this.label = ""; - } - - /** - * Sets an attribute on a MathML node. MathML depends on attributes to convey a - * semantic content, so this is used heavily. - */ - setAttribute(name, value) { - this.attributes[name] = value; - } - - /** - * Gets an attribute on a MathML node. - */ - getAttribute(name) { - return this.attributes[name]; - } - - setLabel(value) { - this.label = value; - } - - /** - * Converts the math node into a MathML-namespaced DOM element. - */ - toNode() { - const node = document.createElementNS("http://www.w3.org/1998/Math/MathML", this.type); - - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { - node.setAttribute(attr, this.attributes[attr]); - } - } - - if (this.classes.length > 0) { - node.className = createClass(this.classes); - } - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - - return node; - } - - /** - * Converts the math node into an HTML markup string. - */ - toMarkup() { - let markup = "<" + this.type; - - // Add the attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { - markup += " " + attr + '="'; - markup += escape(this.attributes[attr]); - markup += '"'; - } - } - - if (this.classes.length > 0) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - - let styles = ""; - - // Add the styles, after hyphenation - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - styles += `${hyphenate(style)}:${this.style[style]};`; - } - } - - if (styles) { - markup += ` style="${styles}"`; - } - - markup += ">"; - - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - markup += ""; - - return markup; - } - - /** - * Converts the math node into a string, similar to innerText, but escaped. - */ - toText() { - return this.children.map((child) => child.toText()).join(""); - } -} - -/** - * This node represents a piece of text. - */ -class TextNode { - constructor(text) { - this.text = text; - } - - /** - * Converts the text node into a DOM text node. - */ - toNode() { - return document.createTextNode(this.text); - } - - /** - * Converts the text node into escaped HTML markup - * (representing the text itself). - */ - toMarkup() { - return escape(this.toText()); - } - - /** - * Converts the text node into a string - * (representing the text itself). - */ - toText() { - return this.text; - } -} - -// Do not make an the only child of a . -// An acts as its own implicit . -const wrapWithMstyle = expression => { - let node; - if (expression.length === 1 && expression[0].type === "mrow") { - node = expression.pop(); - node.type = "mstyle"; - } else { - node = new MathNode("mstyle", expression); - } - return node -}; - -/** - * This file provides support for building horizontal stretchy elements. - */ - - -// TODO: Remove when Chromium stretches \widetilde & \widehat -const estimatedWidth = node => { - let width = 0; - if (node.body && Array.isArray(node.body)) { - for (const item of node.body) { - width += estimatedWidth(item); - } - } else if (node.body) { - width += estimatedWidth(node.body); - } else if (node.type === "supsub") { - width += estimatedWidth(node.base); - if (node.sub) { width += 0.7 * estimatedWidth(node.sub); } - if (node.sup) { width += 0.7 * estimatedWidth(node.sup); } - } else if (node.type === "mathord" || node.type === "textord") { - for (const ch of node.text.split('')) { - const codePoint = ch.codePointAt(0); - if ((0x60 < codePoint && codePoint < 0x7B) || (0x03B0 < codePoint && codePoint < 0x3CA)) { - width += 0.56; // lower case latin or greek. Use advance width of letter n - } else if (0x2F < codePoint && codePoint < 0x3A) { - width += 0.50; // numerals. - } else { - width += 0.92; // advance width of letter M - } - } - } else { - width += 1.0; - } - return width -}; - -const stretchyCodePoint = { - widehat: "^", - widecheck: "ˇ", - widetilde: "~", - wideparen: "⏜", // \u23dc - utilde: "~", - overleftarrow: "\u2190", - underleftarrow: "\u2190", - xleftarrow: "\u2190", - overrightarrow: "\u2192", - underrightarrow: "\u2192", - xrightarrow: "\u2192", - underbrace: "\u23df", - overbrace: "\u23de", - overbracket: "\u23b4", - underbracket: "\u23b5", - overgroup: "\u23e0", - overparen: "⏜", - undergroup: "\u23e1", - underparen: "\u23dd", - overleftrightarrow: "\u2194", - underleftrightarrow: "\u2194", - xleftrightarrow: "\u2194", - Overrightarrow: "\u21d2", - xRightarrow: "\u21d2", - overleftharpoon: "\u21bc", - xleftharpoonup: "\u21bc", - overrightharpoon: "\u21c0", - xrightharpoonup: "\u21c0", - xLeftarrow: "\u21d0", - xLeftrightarrow: "\u21d4", - xhookleftarrow: "\u21a9", - xhookrightarrow: "\u21aa", - xmapsto: "\u21a6", - xrightharpoondown: "\u21c1", - xleftharpoondown: "\u21bd", - xtwoheadleftarrow: "\u219e", - xtwoheadrightarrow: "\u21a0", - xlongequal: "=", - xrightleftarrows: "\u21c4", - xtofrom: "\u21c4", - xleftrightharpoons: "\u21cb", - xrightleftharpoons: "\u21cc", - yields: "\u2192", - yieldsLeft: "\u2190", - mesomerism: "\u2194", - longrightharpoonup: "\u21c0", - longleftharpoondown: "\u21bd", - eqrightharpoonup: "\u21c0", - eqleftharpoondown: "\u21bd", - "\\cdrightarrow": "\u2192", - "\\cdleftarrow": "\u2190", - "\\cdlongequal": "=", - yieldsLeftRight: "\u21c4", - chemequilibrium: "\u21cc" -}; - -const mathMLnode = function(label) { - const child = new TextNode(stretchyCodePoint[label.slice(1)]); - const node = new MathNode("mo", [child]); - node.setAttribute("stretchy", "true"); - return node -}; - -const crookedWides = ["\\widetilde", "\\widehat", "\\widecheck", "\\utilde"]; - -// TODO: Remove when Chromium stretches \widetilde & \widehat -const accentNode = (group) => { - const mo = mathMLnode(group.label); - if (crookedWides.includes(group.label)) { - const width = estimatedWidth(group.base); - if (1 < width && width < 1.6) { - mo.classes.push("tml-crooked-2"); - } else if (1.6 <= width && width < 2.5) { - mo.classes.push("tml-crooked-3"); - } else if (2.5 <= width) { - mo.classes.push("tml-crooked-4"); - } - } - return mo -}; - -/** - * This file holds a list of all no-argument functions and single-character - * symbols (like 'a' or ';'). - * - * For each of the symbols, there are two properties they can have: - * - group (required): the ParseNode group type the symbol should have (i.e. - "textord", "mathord", etc). - * - replace: the character that this symbol or function should be - * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi - * character in the main font). - * - * The outermost map in the table indicates what mode the symbols should be - * accepted in (e.g. "math" or "text"). - */ - -// Some of these have a "-token" suffix since these are also used as `ParseNode` -// types for raw text tokens, and we want to avoid conflicts with higher-level -// `ParseNode` types. These `ParseNode`s are constructed within `Parser` by -// looking up the `symbols` map. -const ATOMS = { - bin: 1, - close: 1, - inner: 1, - open: 1, - punct: 1, - rel: 1 -}; -const NON_ATOMS = { - "accent-token": 1, - mathord: 1, - "op-token": 1, - spacing: 1, - textord: 1 -}; - -const symbols = { - math: {}, - text: {} -}; - -/** `acceptUnicodeChar = true` is only applicable if `replace` is set. */ -function defineSymbol(mode, group, replace, name, acceptUnicodeChar) { - symbols[mode][name] = { group, replace }; - - if (acceptUnicodeChar && replace) { - symbols[mode][replace] = symbols[mode][name]; - } -} - -// Some abbreviations for commonly used strings. -// This helps minify the code, and also spotting typos using jshint. - -// modes: -const math = "math"; -const text = "text"; - -// groups: -const accent = "accent-token"; -const bin = "bin"; -const close = "close"; -const inner = "inner"; -const mathord = "mathord"; -const op = "op-token"; -const open = "open"; -const punct = "punct"; -const rel = "rel"; -const spacing = "spacing"; -const textord = "textord"; - -// Now comes the symbol table - -// Relation Symbols -defineSymbol(math, rel, "\u2261", "\\equiv", true); -defineSymbol(math, rel, "\u227a", "\\prec", true); -defineSymbol(math, rel, "\u227b", "\\succ", true); -defineSymbol(math, rel, "\u223c", "\\sim", true); -defineSymbol(math, rel, "\u27c2", "\\perp", true); -defineSymbol(math, rel, "\u2aaf", "\\preceq", true); -defineSymbol(math, rel, "\u2ab0", "\\succeq", true); -defineSymbol(math, rel, "\u2243", "\\simeq", true); -defineSymbol(math, rel, "\u224c", "\\backcong", true); -defineSymbol(math, rel, "|", "\\mid", true); -defineSymbol(math, rel, "\u226a", "\\ll", true); -defineSymbol(math, rel, "\u226b", "\\gg", true); -defineSymbol(math, rel, "\u224d", "\\asymp", true); -defineSymbol(math, rel, "\u2225", "\\parallel"); -defineSymbol(math, rel, "\u2323", "\\smile", true); -defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true); -defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true); -defineSymbol(math, rel, "\u2250", "\\doteq", true); -defineSymbol(math, rel, "\u2322", "\\frown", true); -defineSymbol(math, rel, "\u220b", "\\ni", true); -defineSymbol(math, rel, "\u220c", "\\notni", true); -defineSymbol(math, rel, "\u221d", "\\propto", true); -defineSymbol(math, rel, "\u22a2", "\\vdash", true); -defineSymbol(math, rel, "\u22a3", "\\dashv", true); -defineSymbol(math, rel, "\u220b", "\\owns"); -defineSymbol(math, rel, "\u2258", "\\arceq", true); -defineSymbol(math, rel, "\u2259", "\\wedgeq", true); -defineSymbol(math, rel, "\u225a", "\\veeeq", true); -defineSymbol(math, rel, "\u225b", "\\stareq", true); -defineSymbol(math, rel, "\u225d", "\\eqdef", true); -defineSymbol(math, rel, "\u225e", "\\measeq", true); -defineSymbol(math, rel, "\u225f", "\\questeq", true); -defineSymbol(math, rel, "\u2260", "\\ne", true); -defineSymbol(math, rel, "\u2260", "\\neq"); -// unicodemath -defineSymbol(math, rel, "\u2a75", "\\eqeq", true); -defineSymbol(math, rel, "\u2a76", "\\eqeqeq", true); -// mathtools.sty -defineSymbol(math, rel, "\u2237", "\\dblcolon", true); -defineSymbol(math, rel, "\u2254", "\\coloneqq", true); -defineSymbol(math, rel, "\u2255", "\\eqqcolon", true); -defineSymbol(math, rel, "\u2239", "\\eqcolon", true); -defineSymbol(math, rel, "\u2A74", "\\Coloneqq", true); - -// Punctuation -defineSymbol(math, punct, "\u002e", "\\ldotp"); -defineSymbol(math, punct, "\u00b7", "\\cdotp"); - -// Misc Symbols -defineSymbol(math, textord, "\u0023", "\\#"); -defineSymbol(text, textord, "\u0023", "\\#"); -defineSymbol(math, textord, "\u0026", "\\&"); -defineSymbol(text, textord, "\u0026", "\\&"); -defineSymbol(math, textord, "\u2135", "\\aleph", true); -defineSymbol(math, textord, "\u2200", "\\forall", true); -defineSymbol(math, textord, "\u210f", "\\hbar", true); -defineSymbol(math, textord, "\u2203", "\\exists", true); -defineSymbol(math, open, "\u2207", "\\nabla", true); -defineSymbol(math, textord, "\u266d", "\\flat", true); -defineSymbol(math, textord, "\u2113", "\\ell", true); -defineSymbol(math, textord, "\u266e", "\\natural", true); -defineSymbol(math, textord, "Å", "\\Angstrom", true); -defineSymbol(text, textord, "Å", "\\Angstrom", true); -defineSymbol(math, textord, "\u2663", "\\clubsuit", true); -defineSymbol(math, textord, "\u2667", "\\varclubsuit", true); -defineSymbol(math, textord, "\u2118", "\\wp", true); -defineSymbol(math, textord, "\u266f", "\\sharp", true); -defineSymbol(math, textord, "\u2662", "\\diamondsuit", true); -defineSymbol(math, textord, "\u2666", "\\vardiamondsuit", true); -defineSymbol(math, textord, "\u211c", "\\Re", true); -defineSymbol(math, textord, "\u2661", "\\heartsuit", true); -defineSymbol(math, textord, "\u2665", "\\varheartsuit", true); -defineSymbol(math, textord, "\u2111", "\\Im", true); -defineSymbol(math, textord, "\u2660", "\\spadesuit", true); -defineSymbol(math, textord, "\u2664", "\\varspadesuit", true); -defineSymbol(math, textord, "\u2640", "\\female", true); -defineSymbol(math, textord, "\u2642", "\\male", true); -defineSymbol(math, textord, "\u00a7", "\\S", true); -defineSymbol(text, textord, "\u00a7", "\\S"); -defineSymbol(math, textord, "\u00b6", "\\P", true); -defineSymbol(text, textord, "\u00b6", "\\P"); -defineSymbol(text, textord, "\u263a", "\\smiley", true); -defineSymbol(math, textord, "\u263a", "\\smiley", true); - -// Math and Text -defineSymbol(math, textord, "\u2020", "\\dag"); -defineSymbol(text, textord, "\u2020", "\\dag"); -defineSymbol(text, textord, "\u2020", "\\textdagger"); -defineSymbol(math, textord, "\u2021", "\\ddag"); -defineSymbol(text, textord, "\u2021", "\\ddag"); -defineSymbol(text, textord, "\u2021", "\\textdaggerdbl"); - -// Large Delimiters -defineSymbol(math, close, "\u23b1", "\\rmoustache", true); -defineSymbol(math, open, "\u23b0", "\\lmoustache", true); -defineSymbol(math, close, "\u27ef", "\\rgroup", true); -defineSymbol(math, open, "\u27ee", "\\lgroup", true); - -// Binary Operators -defineSymbol(math, bin, "\u2213", "\\mp", true); -defineSymbol(math, bin, "\u2296", "\\ominus", true); -defineSymbol(math, bin, "\u228e", "\\uplus", true); -defineSymbol(math, bin, "\u2293", "\\sqcap", true); -defineSymbol(math, bin, "\u2217", "\\ast"); -defineSymbol(math, bin, "\u2294", "\\sqcup", true); -defineSymbol(math, bin, "\u25ef", "\\bigcirc", true); -defineSymbol(math, bin, "\u2219", "\\bullet", true); -defineSymbol(math, bin, "\u2021", "\\ddagger"); -defineSymbol(math, bin, "\u2240", "\\wr", true); -defineSymbol(math, bin, "\u2a3f", "\\amalg"); -defineSymbol(math, bin, "\u0026", "\\And"); // from amsmath -defineSymbol(math, bin, "\u2AFD", "\\sslash", true); // from stmaryrd - -// Arrow Symbols -defineSymbol(math, rel, "\u27f5", "\\longleftarrow", true); -defineSymbol(math, rel, "\u21d0", "\\Leftarrow", true); -defineSymbol(math, rel, "\u27f8", "\\Longleftarrow", true); -defineSymbol(math, rel, "\u27f6", "\\longrightarrow", true); -defineSymbol(math, rel, "\u21d2", "\\Rightarrow", true); -defineSymbol(math, rel, "\u27f9", "\\Longrightarrow", true); -defineSymbol(math, rel, "\u2194", "\\leftrightarrow", true); -defineSymbol(math, rel, "\u27f7", "\\longleftrightarrow", true); -defineSymbol(math, rel, "\u21d4", "\\Leftrightarrow", true); -defineSymbol(math, rel, "\u27fa", "\\Longleftrightarrow", true); -defineSymbol(math, rel, "\u21a4", "\\mapsfrom", true); -defineSymbol(math, rel, "\u21a6", "\\mapsto", true); -defineSymbol(math, rel, "\u27fc", "\\longmapsto", true); -defineSymbol(math, rel, "\u2197", "\\nearrow", true); -defineSymbol(math, rel, "\u21a9", "\\hookleftarrow", true); -defineSymbol(math, rel, "\u21aa", "\\hookrightarrow", true); -defineSymbol(math, rel, "\u2198", "\\searrow", true); -defineSymbol(math, rel, "\u21bc", "\\leftharpoonup", true); -defineSymbol(math, rel, "\u21c0", "\\rightharpoonup", true); -defineSymbol(math, rel, "\u2199", "\\swarrow", true); -defineSymbol(math, rel, "\u21bd", "\\leftharpoondown", true); -defineSymbol(math, rel, "\u21c1", "\\rightharpoondown", true); -defineSymbol(math, rel, "\u2196", "\\nwarrow", true); -defineSymbol(math, rel, "\u21cc", "\\rightleftharpoons", true); -defineSymbol(math, mathord, "\u21af", "\\lightning", true); -defineSymbol(math, mathord, "\u220E", "\\QED", true); -defineSymbol(math, mathord, "\u2030", "\\permil", true); -defineSymbol(text, textord, "\u2030", "\\permil"); -defineSymbol(math, mathord, "\u2609", "\\astrosun", true); -defineSymbol(math, mathord, "\u263c", "\\sun", true); -defineSymbol(math, mathord, "\u263e", "\\leftmoon", true); -defineSymbol(math, mathord, "\u263d", "\\rightmoon", true); -defineSymbol(math, mathord, "\u2295", "\\Earth"); - -// AMS Negated Binary Relations -defineSymbol(math, rel, "\u226e", "\\nless", true); -// Symbol names preceded by "@" each have a corresponding macro. -defineSymbol(math, rel, "\u2a87", "\\lneq", true); -defineSymbol(math, rel, "\u2268", "\\lneqq", true); -defineSymbol(math, rel, "\u2268\ufe00", "\\lvertneqq"); -defineSymbol(math, rel, "\u22e6", "\\lnsim", true); -defineSymbol(math, rel, "\u2a89", "\\lnapprox", true); -defineSymbol(math, rel, "\u2280", "\\nprec", true); -// unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym. -defineSymbol(math, rel, "\u22e0", "\\npreceq", true); -defineSymbol(math, rel, "\u22e8", "\\precnsim", true); -defineSymbol(math, rel, "\u2ab9", "\\precnapprox", true); -defineSymbol(math, rel, "\u2241", "\\nsim", true); -defineSymbol(math, rel, "\u2224", "\\nmid", true); -defineSymbol(math, rel, "\u2224", "\\nshortmid"); -defineSymbol(math, rel, "\u22ac", "\\nvdash", true); -defineSymbol(math, rel, "\u22ad", "\\nvDash", true); -defineSymbol(math, rel, "\u22ea", "\\ntriangleleft"); -defineSymbol(math, rel, "\u22ec", "\\ntrianglelefteq", true); -defineSymbol(math, rel, "\u2284", "\\nsubset", true); -defineSymbol(math, rel, "\u2285", "\\nsupset", true); -defineSymbol(math, rel, "\u228a", "\\subsetneq", true); -defineSymbol(math, rel, "\u228a\ufe00", "\\varsubsetneq"); -defineSymbol(math, rel, "\u2acb", "\\subsetneqq", true); -defineSymbol(math, rel, "\u2acb\ufe00", "\\varsubsetneqq"); -defineSymbol(math, rel, "\u226f", "\\ngtr", true); -defineSymbol(math, rel, "\u2a88", "\\gneq", true); -defineSymbol(math, rel, "\u2269", "\\gneqq", true); -defineSymbol(math, rel, "\u2269\ufe00", "\\gvertneqq"); -defineSymbol(math, rel, "\u22e7", "\\gnsim", true); -defineSymbol(math, rel, "\u2a8a", "\\gnapprox", true); -defineSymbol(math, rel, "\u2281", "\\nsucc", true); -// unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym. -defineSymbol(math, rel, "\u22e1", "\\nsucceq", true); -defineSymbol(math, rel, "\u22e9", "\\succnsim", true); -defineSymbol(math, rel, "\u2aba", "\\succnapprox", true); -// unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym. -defineSymbol(math, rel, "\u2246", "\\ncong", true); -defineSymbol(math, rel, "\u2226", "\\nparallel", true); -defineSymbol(math, rel, "\u2226", "\\nshortparallel"); -defineSymbol(math, rel, "\u22af", "\\nVDash", true); -defineSymbol(math, rel, "\u22eb", "\\ntriangleright"); -defineSymbol(math, rel, "\u22ed", "\\ntrianglerighteq", true); -defineSymbol(math, rel, "\u228b", "\\supsetneq", true); -defineSymbol(math, rel, "\u228b", "\\varsupsetneq"); -defineSymbol(math, rel, "\u2acc", "\\supsetneqq", true); -defineSymbol(math, rel, "\u2acc\ufe00", "\\varsupsetneqq"); -defineSymbol(math, rel, "\u22ae", "\\nVdash", true); -defineSymbol(math, rel, "\u2ab5", "\\precneqq", true); -defineSymbol(math, rel, "\u2ab6", "\\succneqq", true); -defineSymbol(math, bin, "\u22b4", "\\unlhd"); -defineSymbol(math, bin, "\u22b5", "\\unrhd"); - -// AMS Negated Arrows -defineSymbol(math, rel, "\u219a", "\\nleftarrow", true); -defineSymbol(math, rel, "\u219b", "\\nrightarrow", true); -defineSymbol(math, rel, "\u21cd", "\\nLeftarrow", true); -defineSymbol(math, rel, "\u21cf", "\\nRightarrow", true); -defineSymbol(math, rel, "\u21ae", "\\nleftrightarrow", true); -defineSymbol(math, rel, "\u21ce", "\\nLeftrightarrow", true); - -// AMS Misc -defineSymbol(math, rel, "\u25b3", "\\vartriangle"); -defineSymbol(math, textord, "\u210f", "\\hslash"); -defineSymbol(math, textord, "\u25bd", "\\triangledown"); -defineSymbol(math, textord, "\u25ca", "\\lozenge"); -defineSymbol(math, textord, "\u24c8", "\\circledS"); -defineSymbol(math, textord, "\u00ae", "\\circledR", true); -defineSymbol(text, textord, "\u00ae", "\\circledR"); -defineSymbol(text, textord, "\u00ae", "\\textregistered"); -defineSymbol(math, textord, "\u2221", "\\measuredangle", true); -defineSymbol(math, textord, "\u2204", "\\nexists"); -defineSymbol(math, textord, "\u2127", "\\mho"); -defineSymbol(math, textord, "\u2132", "\\Finv", true); -defineSymbol(math, textord, "\u2141", "\\Game", true); -defineSymbol(math, textord, "\u2035", "\\backprime"); -defineSymbol(math, textord, "\u2036", "\\backdprime"); -defineSymbol(math, textord, "\u2037", "\\backtrprime"); -defineSymbol(math, textord, "\u25b2", "\\blacktriangle"); -defineSymbol(math, textord, "\u25bc", "\\blacktriangledown"); -defineSymbol(math, textord, "\u25a0", "\\blacksquare"); -defineSymbol(math, textord, "\u29eb", "\\blacklozenge"); -defineSymbol(math, textord, "\u2605", "\\bigstar"); -defineSymbol(math, textord, "\u2222", "\\sphericalangle", true); -defineSymbol(math, textord, "\u2201", "\\complement", true); -defineSymbol(math, textord, "\u2571", "\\diagup"); -defineSymbol(math, textord, "\u2572", "\\diagdown"); -defineSymbol(math, textord, "\u25a1", "\\square"); -defineSymbol(math, textord, "\u25a1", "\\Box"); -defineSymbol(math, textord, "\u25ca", "\\Diamond"); -// unicode-math maps U+A5 to \mathyen. We map to AMS function \yen -defineSymbol(math, textord, "\u00a5", "\\yen", true); -defineSymbol(text, textord, "\u00a5", "\\yen", true); -defineSymbol(math, textord, "\u2713", "\\checkmark", true); -defineSymbol(text, textord, "\u2713", "\\checkmark"); -defineSymbol(math, textord, "\u2717", "\\ballotx", true); -defineSymbol(text, textord, "\u2717", "\\ballotx"); -defineSymbol(text, textord, "\u2022", "\\textbullet"); - -// AMS Hebrew -defineSymbol(math, textord, "\u2136", "\\beth", true); -defineSymbol(math, textord, "\u2138", "\\daleth", true); -defineSymbol(math, textord, "\u2137", "\\gimel", true); - -// AMS Greek -defineSymbol(math, textord, "\u03dd", "\\digamma", true); -defineSymbol(math, textord, "\u03f0", "\\varkappa"); - -// AMS Delimiters -defineSymbol(math, open, "\u231C", "\\ulcorner", true); -defineSymbol(math, close, "\u231D", "\\urcorner", true); -defineSymbol(math, open, "\u231E", "\\llcorner", true); -defineSymbol(math, close, "\u231F", "\\lrcorner", true); - -// AMS Binary Relations -defineSymbol(math, rel, "\u2266", "\\leqq", true); -defineSymbol(math, rel, "\u2a7d", "\\leqslant", true); -defineSymbol(math, rel, "\u2a95", "\\eqslantless", true); -defineSymbol(math, rel, "\u2272", "\\lesssim", true); -defineSymbol(math, rel, "\u2a85", "\\lessapprox", true); -defineSymbol(math, rel, "\u224a", "\\approxeq", true); -defineSymbol(math, bin, "\u22d6", "\\lessdot"); -defineSymbol(math, rel, "\u22d8", "\\lll", true); -defineSymbol(math, rel, "\u2276", "\\lessgtr", true); -defineSymbol(math, rel, "\u22da", "\\lesseqgtr", true); -defineSymbol(math, rel, "\u2a8b", "\\lesseqqgtr", true); -defineSymbol(math, rel, "\u2251", "\\doteqdot"); -defineSymbol(math, rel, "\u2253", "\\risingdotseq", true); -defineSymbol(math, rel, "\u2252", "\\fallingdotseq", true); -defineSymbol(math, rel, "\u223d", "\\backsim", true); -defineSymbol(math, rel, "\u22cd", "\\backsimeq", true); -defineSymbol(math, rel, "\u2ac5", "\\subseteqq", true); -defineSymbol(math, rel, "\u22d0", "\\Subset", true); -defineSymbol(math, rel, "\u228f", "\\sqsubset", true); -defineSymbol(math, rel, "\u227c", "\\preccurlyeq", true); -defineSymbol(math, rel, "\u22de", "\\curlyeqprec", true); -defineSymbol(math, rel, "\u227e", "\\precsim", true); -defineSymbol(math, rel, "\u2ab7", "\\precapprox", true); -defineSymbol(math, rel, "\u22b2", "\\vartriangleleft"); -defineSymbol(math, rel, "\u22b4", "\\trianglelefteq"); -defineSymbol(math, rel, "\u22a8", "\\vDash", true); -defineSymbol(math, rel, "\u22ab", "\\VDash", true); -defineSymbol(math, rel, "\u22aa", "\\Vvdash", true); -defineSymbol(math, rel, "\u2323", "\\smallsmile"); -defineSymbol(math, rel, "\u2322", "\\smallfrown"); -defineSymbol(math, rel, "\u224f", "\\bumpeq", true); -defineSymbol(math, rel, "\u224e", "\\Bumpeq", true); -defineSymbol(math, rel, "\u2267", "\\geqq", true); -defineSymbol(math, rel, "\u2a7e", "\\geqslant", true); -defineSymbol(math, rel, "\u2a96", "\\eqslantgtr", true); -defineSymbol(math, rel, "\u2273", "\\gtrsim", true); -defineSymbol(math, rel, "\u2a86", "\\gtrapprox", true); -defineSymbol(math, bin, "\u22d7", "\\gtrdot"); -defineSymbol(math, rel, "\u22d9", "\\ggg", true); -defineSymbol(math, rel, "\u2277", "\\gtrless", true); -defineSymbol(math, rel, "\u22db", "\\gtreqless", true); -defineSymbol(math, rel, "\u2a8c", "\\gtreqqless", true); -defineSymbol(math, rel, "\u2256", "\\eqcirc", true); -defineSymbol(math, rel, "\u2257", "\\circeq", true); -defineSymbol(math, rel, "\u225c", "\\triangleq", true); -defineSymbol(math, rel, "\u223c", "\\thicksim"); -defineSymbol(math, rel, "\u2248", "\\thickapprox"); -defineSymbol(math, rel, "\u2ac6", "\\supseteqq", true); -defineSymbol(math, rel, "\u22d1", "\\Supset", true); -defineSymbol(math, rel, "\u2290", "\\sqsupset", true); -defineSymbol(math, rel, "\u227d", "\\succcurlyeq", true); -defineSymbol(math, rel, "\u22df", "\\curlyeqsucc", true); -defineSymbol(math, rel, "\u227f", "\\succsim", true); -defineSymbol(math, rel, "\u2ab8", "\\succapprox", true); -defineSymbol(math, rel, "\u22b3", "\\vartriangleright"); -defineSymbol(math, rel, "\u22b5", "\\trianglerighteq"); -defineSymbol(math, rel, "\u22a9", "\\Vdash", true); -defineSymbol(math, rel, "\u2223", "\\shortmid"); -defineSymbol(math, rel, "\u2225", "\\shortparallel"); -defineSymbol(math, rel, "\u226c", "\\between", true); -defineSymbol(math, rel, "\u22d4", "\\pitchfork", true); -defineSymbol(math, rel, "\u221d", "\\varpropto"); -defineSymbol(math, rel, "\u25c0", "\\blacktriangleleft"); -// unicode-math says that \therefore is a mathord atom. -// We kept the amssymb atom type, which is rel. -defineSymbol(math, rel, "\u2234", "\\therefore", true); -defineSymbol(math, rel, "\u220d", "\\backepsilon"); -defineSymbol(math, rel, "\u25b6", "\\blacktriangleright"); -// unicode-math says that \because is a mathord atom. -// We kept the amssymb atom type, which is rel. -defineSymbol(math, rel, "\u2235", "\\because", true); -defineSymbol(math, rel, "\u22d8", "\\llless"); -defineSymbol(math, rel, "\u22d9", "\\gggtr"); -defineSymbol(math, bin, "\u22b2", "\\lhd"); -defineSymbol(math, bin, "\u22b3", "\\rhd"); -defineSymbol(math, rel, "\u2242", "\\eqsim", true); -defineSymbol(math, rel, "\u2251", "\\Doteq", true); -defineSymbol(math, rel, "\u297d", "\\strictif", true); -defineSymbol(math, rel, "\u297c", "\\strictfi", true); - -// AMS Binary Operators -defineSymbol(math, bin, "\u2214", "\\dotplus", true); -defineSymbol(math, bin, "\u2216", "\\smallsetminus"); -defineSymbol(math, bin, "\u22d2", "\\Cap", true); -defineSymbol(math, bin, "\u22d3", "\\Cup", true); -defineSymbol(math, bin, "\u2a5e", "\\doublebarwedge", true); -defineSymbol(math, bin, "\u229f", "\\boxminus", true); -defineSymbol(math, bin, "\u229e", "\\boxplus", true); -defineSymbol(math, bin, "\u29C4", "\\boxslash", true); -defineSymbol(math, bin, "\u22c7", "\\divideontimes", true); -defineSymbol(math, bin, "\u22c9", "\\ltimes", true); -defineSymbol(math, bin, "\u22ca", "\\rtimes", true); -defineSymbol(math, bin, "\u22cb", "\\leftthreetimes", true); -defineSymbol(math, bin, "\u22cc", "\\rightthreetimes", true); -defineSymbol(math, bin, "\u22cf", "\\curlywedge", true); -defineSymbol(math, bin, "\u22ce", "\\curlyvee", true); -defineSymbol(math, bin, "\u229d", "\\circleddash", true); -defineSymbol(math, bin, "\u229b", "\\circledast", true); -defineSymbol(math, bin, "\u22ba", "\\intercal", true); -defineSymbol(math, bin, "\u22d2", "\\doublecap"); -defineSymbol(math, bin, "\u22d3", "\\doublecup"); -defineSymbol(math, bin, "\u22a0", "\\boxtimes", true); -defineSymbol(math, bin, "\u22c8", "\\bowtie", true); -defineSymbol(math, bin, "\u22c8", "\\Join"); -defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true); -defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true); -defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true); - -// stix Binary Operators -defineSymbol(math, bin, "\u2238", "\\dotminus", true); -defineSymbol(math, bin, "\u27D1", "\\wedgedot", true); -defineSymbol(math, bin, "\u27C7", "\\veedot", true); -defineSymbol(math, bin, "\u2A62", "\\doublebarvee", true); -defineSymbol(math, bin, "\u2A63", "\\veedoublebar", true); -defineSymbol(math, bin, "\u2A5F", "\\wedgebar", true); -defineSymbol(math, bin, "\u2A60", "\\wedgedoublebar", true); -defineSymbol(math, bin, "\u2A54", "\\Vee", true); -defineSymbol(math, bin, "\u2A53", "\\Wedge", true); -defineSymbol(math, bin, "\u2A43", "\\barcap", true); -defineSymbol(math, bin, "\u2A42", "\\barcup", true); -defineSymbol(math, bin, "\u2A48", "\\capbarcup", true); -defineSymbol(math, bin, "\u2A40", "\\capdot", true); -defineSymbol(math, bin, "\u2A47", "\\capovercup", true); -defineSymbol(math, bin, "\u2A46", "\\cupovercap", true); -defineSymbol(math, bin, "\u2A4D", "\\closedvarcap", true); -defineSymbol(math, bin, "\u2A4C", "\\closedvarcup", true); -defineSymbol(math, bin, "\u2A2A", "\\minusdot", true); -defineSymbol(math, bin, "\u2A2B", "\\minusfdots", true); -defineSymbol(math, bin, "\u2A2C", "\\minusrdots", true); -defineSymbol(math, bin, "\u22BB", "\\Xor", true); -defineSymbol(math, bin, "\u22BC", "\\Nand", true); -defineSymbol(math, bin, "\u22BD", "\\Nor", true); -defineSymbol(math, bin, "\u22BD", "\\barvee"); -defineSymbol(math, bin, "\u2AF4", "\\interleave", true); -defineSymbol(math, bin, "\u29E2", "\\shuffle", true); -defineSymbol(math, bin, "\u2AF6", "\\threedotcolon", true); -defineSymbol(math, bin, "\u2982", "\\typecolon", true); -defineSymbol(math, bin, "\u223E", "\\invlazys", true); -defineSymbol(math, bin, "\u2A4B", "\\twocaps", true); -defineSymbol(math, bin, "\u2A4A", "\\twocups", true); -defineSymbol(math, bin, "\u2A4E", "\\Sqcap", true); -defineSymbol(math, bin, "\u2A4F", "\\Sqcup", true); -defineSymbol(math, bin, "\u2A56", "\\veeonvee", true); -defineSymbol(math, bin, "\u2A55", "\\wedgeonwedge", true); -defineSymbol(math, bin, "\u29D7", "\\blackhourglass", true); -defineSymbol(math, bin, "\u29C6", "\\boxast", true); -defineSymbol(math, bin, "\u29C8", "\\boxbox", true); -defineSymbol(math, bin, "\u29C7", "\\boxcircle", true); -defineSymbol(math, bin, "\u229C", "\\circledequal", true); -defineSymbol(math, bin, "\u29B7", "\\circledparallel", true); -defineSymbol(math, bin, "\u29B6", "\\circledvert", true); -defineSymbol(math, bin, "\u29B5", "\\circlehbar", true); -defineSymbol(math, bin, "\u27E1", "\\concavediamond", true); -defineSymbol(math, bin, "\u27E2", "\\concavediamondtickleft", true); -defineSymbol(math, bin, "\u27E3", "\\concavediamondtickright", true); -defineSymbol(math, bin, "\u22C4", "\\diamond", true); -defineSymbol(math, bin, "\u29D6", "\\hourglass", true); -defineSymbol(math, bin, "\u27E0", "\\lozengeminus", true); -defineSymbol(math, bin, "\u233D", "\\obar", true); -defineSymbol(math, bin, "\u29B8", "\\obslash", true); -defineSymbol(math, bin, "\u2A38", "\\odiv", true); -defineSymbol(math, bin, "\u29C1", "\\ogreaterthan", true); -defineSymbol(math, bin, "\u29C0", "\\olessthan", true); -defineSymbol(math, bin, "\u29B9", "\\operp", true); -defineSymbol(math, bin, "\u2A37", "\\Otimes", true); -defineSymbol(math, bin, "\u2A36", "\\otimeshat", true); -defineSymbol(math, bin, "\u22C6", "\\star", true); -defineSymbol(math, bin, "\u25B3", "\\triangle", true); -defineSymbol(math, bin, "\u2A3A", "\\triangleminus", true); -defineSymbol(math, bin, "\u2A39", "\\triangleplus", true); -defineSymbol(math, bin, "\u2A3B", "\\triangletimes", true); -defineSymbol(math, bin, "\u27E4", "\\whitesquaretickleft", true); -defineSymbol(math, bin, "\u27E5", "\\whitesquaretickright", true); -defineSymbol(math, bin, "\u2A33", "\\smashtimes", true); - -// AMS Arrows -// Note: unicode-math maps \u21e2 to their own function \rightdasharrow. -// We'll map it to AMS function \dashrightarrow. It produces the same atom. -defineSymbol(math, rel, "\u21e2", "\\dashrightarrow", true); -// unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym. -defineSymbol(math, rel, "\u21e0", "\\dashleftarrow", true); -defineSymbol(math, rel, "\u21c7", "\\leftleftarrows", true); -defineSymbol(math, rel, "\u21c6", "\\leftrightarrows", true); -defineSymbol(math, rel, "\u21da", "\\Lleftarrow", true); -defineSymbol(math, rel, "\u219e", "\\twoheadleftarrow", true); -defineSymbol(math, rel, "\u21a2", "\\leftarrowtail", true); -defineSymbol(math, rel, "\u21ab", "\\looparrowleft", true); -defineSymbol(math, rel, "\u21cb", "\\leftrightharpoons", true); -defineSymbol(math, rel, "\u21b6", "\\curvearrowleft", true); -// unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym. -defineSymbol(math, rel, "\u21ba", "\\circlearrowleft", true); -defineSymbol(math, rel, "\u21b0", "\\Lsh", true); -defineSymbol(math, rel, "\u21c8", "\\upuparrows", true); -defineSymbol(math, rel, "\u21bf", "\\upharpoonleft", true); -defineSymbol(math, rel, "\u21c3", "\\downharpoonleft", true); -defineSymbol(math, rel, "\u22b6", "\\origof", true); -defineSymbol(math, rel, "\u22b7", "\\imageof", true); -defineSymbol(math, rel, "\u22b8", "\\multimap", true); -defineSymbol(math, rel, "\u21ad", "\\leftrightsquigarrow", true); -defineSymbol(math, rel, "\u21c9", "\\rightrightarrows", true); -defineSymbol(math, rel, "\u21c4", "\\rightleftarrows", true); -defineSymbol(math, rel, "\u21a0", "\\twoheadrightarrow", true); -defineSymbol(math, rel, "\u21a3", "\\rightarrowtail", true); -defineSymbol(math, rel, "\u21ac", "\\looparrowright", true); -defineSymbol(math, rel, "\u21b7", "\\curvearrowright", true); -// unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym. -defineSymbol(math, rel, "\u21bb", "\\circlearrowright", true); -defineSymbol(math, rel, "\u21b1", "\\Rsh", true); -defineSymbol(math, rel, "\u21ca", "\\downdownarrows", true); -defineSymbol(math, rel, "\u21be", "\\upharpoonright", true); -defineSymbol(math, rel, "\u21c2", "\\downharpoonright", true); -defineSymbol(math, rel, "\u21dd", "\\rightsquigarrow", true); -defineSymbol(math, rel, "\u21dd", "\\leadsto"); -defineSymbol(math, rel, "\u21db", "\\Rrightarrow", true); -defineSymbol(math, rel, "\u21be", "\\restriction"); - -defineSymbol(math, textord, "\u2018", "`"); -defineSymbol(math, textord, "$", "\\$"); -defineSymbol(text, textord, "$", "\\$"); -defineSymbol(text, textord, "$", "\\textdollar"); -defineSymbol(math, textord, "¢", "\\cent"); -defineSymbol(text, textord, "¢", "\\cent"); -defineSymbol(math, textord, "%", "\\%"); -defineSymbol(text, textord, "%", "\\%"); -defineSymbol(math, textord, "_", "\\_"); -defineSymbol(text, textord, "_", "\\_"); -defineSymbol(text, textord, "_", "\\textunderscore"); -defineSymbol(text, textord, "\u2423", "\\textvisiblespace", true); -defineSymbol(math, textord, "\u2220", "\\angle", true); -defineSymbol(math, textord, "\u221e", "\\infty", true); -defineSymbol(math, textord, "\u2032", "\\prime"); -defineSymbol(math, textord, "\u2033", "\\dprime"); -defineSymbol(math, textord, "\u2034", "\\trprime"); -defineSymbol(math, textord, "\u2057", "\\qprime"); -defineSymbol(math, textord, "\u25b3", "\\triangle"); -defineSymbol(text, textord, "\u0391", "\\Alpha", true); -defineSymbol(text, textord, "\u0392", "\\Beta", true); -defineSymbol(text, textord, "\u0393", "\\Gamma", true); -defineSymbol(text, textord, "\u0394", "\\Delta", true); -defineSymbol(text, textord, "\u0395", "\\Epsilon", true); -defineSymbol(text, textord, "\u0396", "\\Zeta", true); -defineSymbol(text, textord, "\u0397", "\\Eta", true); -defineSymbol(text, textord, "\u0398", "\\Theta", true); -defineSymbol(text, textord, "\u0399", "\\Iota", true); -defineSymbol(text, textord, "\u039a", "\\Kappa", true); -defineSymbol(text, textord, "\u039b", "\\Lambda", true); -defineSymbol(text, textord, "\u039c", "\\Mu", true); -defineSymbol(text, textord, "\u039d", "\\Nu", true); -defineSymbol(text, textord, "\u039e", "\\Xi", true); -defineSymbol(text, textord, "\u039f", "\\Omicron", true); -defineSymbol(text, textord, "\u03a0", "\\Pi", true); -defineSymbol(text, textord, "\u03a1", "\\Rho", true); -defineSymbol(text, textord, "\u03a3", "\\Sigma", true); -defineSymbol(text, textord, "\u03a4", "\\Tau", true); -defineSymbol(text, textord, "\u03a5", "\\Upsilon", true); -defineSymbol(text, textord, "\u03a6", "\\Phi", true); -defineSymbol(text, textord, "\u03a7", "\\Chi", true); -defineSymbol(text, textord, "\u03a8", "\\Psi", true); -defineSymbol(text, textord, "\u03a9", "\\Omega", true); -defineSymbol(math, mathord, "\u0391", "\\Alpha", true); -defineSymbol(math, mathord, "\u0392", "\\Beta", true); -defineSymbol(math, mathord, "\u0393", "\\Gamma", true); -defineSymbol(math, mathord, "\u0394", "\\Delta", true); -defineSymbol(math, mathord, "\u0395", "\\Epsilon", true); -defineSymbol(math, mathord, "\u0396", "\\Zeta", true); -defineSymbol(math, mathord, "\u0397", "\\Eta", true); -defineSymbol(math, mathord, "\u0398", "\\Theta", true); -defineSymbol(math, mathord, "\u0399", "\\Iota", true); -defineSymbol(math, mathord, "\u039a", "\\Kappa", true); -defineSymbol(math, mathord, "\u039b", "\\Lambda", true); -defineSymbol(math, mathord, "\u039c", "\\Mu", true); -defineSymbol(math, mathord, "\u039d", "\\Nu", true); -defineSymbol(math, mathord, "\u039e", "\\Xi", true); -defineSymbol(math, mathord, "\u039f", "\\Omicron", true); -defineSymbol(math, mathord, "\u03a0", "\\Pi", true); -defineSymbol(math, mathord, "\u03a1", "\\Rho", true); -defineSymbol(math, mathord, "\u03a3", "\\Sigma", true); -defineSymbol(math, mathord, "\u03a4", "\\Tau", true); -defineSymbol(math, mathord, "\u03a5", "\\Upsilon", true); -defineSymbol(math, mathord, "\u03a6", "\\Phi", true); -defineSymbol(math, mathord, "\u03a7", "\\Chi", true); -defineSymbol(math, mathord, "\u03a8", "\\Psi", true); -defineSymbol(math, mathord, "\u03a9", "\\Omega", true); -defineSymbol(math, open, "\u00ac", "\\neg", true); -defineSymbol(math, open, "\u00ac", "\\lnot"); -defineSymbol(math, textord, "\u22a4", "\\top"); -defineSymbol(math, textord, "\u22a5", "\\bot"); -defineSymbol(math, textord, "\u2205", "\\emptyset"); -defineSymbol(math, textord, "\u2300", "\\varnothing"); -defineSymbol(math, mathord, "\u03b1", "\\alpha", true); -defineSymbol(math, mathord, "\u03b2", "\\beta", true); -defineSymbol(math, mathord, "\u03b3", "\\gamma", true); -defineSymbol(math, mathord, "\u03b4", "\\delta", true); -defineSymbol(math, mathord, "\u03f5", "\\epsilon", true); -defineSymbol(math, mathord, "\u03b6", "\\zeta", true); -defineSymbol(math, mathord, "\u03b7", "\\eta", true); -defineSymbol(math, mathord, "\u03b8", "\\theta", true); -defineSymbol(math, mathord, "\u03b9", "\\iota", true); -defineSymbol(math, mathord, "\u03ba", "\\kappa", true); -defineSymbol(math, mathord, "\u03bb", "\\lambda", true); -defineSymbol(math, mathord, "\u03bc", "\\mu", true); -defineSymbol(math, mathord, "\u03bd", "\\nu", true); -defineSymbol(math, mathord, "\u03be", "\\xi", true); -defineSymbol(math, mathord, "\u03bf", "\\omicron", true); -defineSymbol(math, mathord, "\u03c0", "\\pi", true); -defineSymbol(math, mathord, "\u03c1", "\\rho", true); -defineSymbol(math, mathord, "\u03c3", "\\sigma", true); -defineSymbol(math, mathord, "\u03c4", "\\tau", true); -defineSymbol(math, mathord, "\u03c5", "\\upsilon", true); -defineSymbol(math, mathord, "\u03d5", "\\phi", true); -defineSymbol(math, mathord, "\u03c7", "\\chi", true); -defineSymbol(math, mathord, "\u03c8", "\\psi", true); -defineSymbol(math, mathord, "\u03c9", "\\omega", true); -defineSymbol(math, mathord, "\u03b5", "\\varepsilon", true); -defineSymbol(math, mathord, "\u03d1", "\\vartheta", true); -defineSymbol(math, mathord, "\u03d6", "\\varpi", true); -defineSymbol(math, mathord, "\u03f1", "\\varrho", true); -defineSymbol(math, mathord, "\u03c2", "\\varsigma", true); -defineSymbol(math, mathord, "\u03c6", "\\varphi", true); -defineSymbol(math, mathord, "\u03d8", "\\Coppa", true); -defineSymbol(math, mathord, "\u03d9", "\\coppa", true); -defineSymbol(math, mathord, "\u03d9", "\\varcoppa", true); -defineSymbol(math, mathord, "\u03de", "\\Koppa", true); -defineSymbol(math, mathord, "\u03df", "\\koppa", true); -defineSymbol(math, mathord, "\u03e0", "\\Sampi", true); -defineSymbol(math, mathord, "\u03e1", "\\sampi", true); -defineSymbol(math, mathord, "\u03da", "\\Stigma", true); -defineSymbol(math, mathord, "\u03db", "\\stigma", true); -defineSymbol(math, mathord, "\u2aeb", "\\Bot"); - -// unicode-math maps U+F0 to \matheth. We map to AMS function \eth -defineSymbol(math, textord, "\u00f0", "\\eth", true); // ð -defineSymbol(text, textord, "\u00f0", "\u00f0"); -// Extended ASCII and non-ASCII Letters -defineSymbol(math, textord, "\u00C5", "\\AA"); // Å -defineSymbol(text, textord, "\u00C5", "\\AA", true); -defineSymbol(math, textord, "\u00C6", "\\AE", true); // Æ -defineSymbol(text, textord, "\u00C6", "\\AE", true); -defineSymbol(math, textord, "\u00D0", "\\DH", true); // Ð -defineSymbol(text, textord, "\u00D0", "\\DH", true); -defineSymbol(math, textord, "\u00DE", "\\TH", true); // Þ -defineSymbol(text, textord, "\u00DE", "\\TH", true); -defineSymbol(math, textord, "\u00DF", "\\ss", true); // ß -defineSymbol(text, textord, "\u00DF", "\\ss", true); -defineSymbol(math, textord, "\u00E5", "\\aa"); // å -defineSymbol(text, textord, "\u00E5", "\\aa", true); -defineSymbol(math, textord, "\u00E6", "\\ae", true); // æ -defineSymbol(text, textord, "\u00E6", "\\ae", true); -defineSymbol(math, textord, "\u00F0", "\\dh"); // ð -defineSymbol(text, textord, "\u00F0", "\\dh", true); -defineSymbol(math, textord, "\u00FE", "\\th", true); // þ -defineSymbol(text, textord, "\u00FE", "\\th", true); -defineSymbol(math, textord, "\u0110", "\\DJ", true); // Đ -defineSymbol(text, textord, "\u0110", "\\DJ", true); -defineSymbol(math, textord, "\u0111", "\\dj", true); // đ -defineSymbol(text, textord, "\u0111", "\\dj", true); -defineSymbol(math, textord, "\u0141", "\\L", true); // Ł -defineSymbol(text, textord, "\u0141", "\\L", true); -defineSymbol(math, textord, "\u0141", "\\l", true); // ł -defineSymbol(text, textord, "\u0141", "\\l", true); -defineSymbol(math, textord, "\u014A", "\\NG", true); // Ŋ -defineSymbol(text, textord, "\u014A", "\\NG", true); -defineSymbol(math, textord, "\u014B", "\\ng", true); // ŋ -defineSymbol(text, textord, "\u014B", "\\ng", true); -defineSymbol(math, textord, "\u0152", "\\OE", true); // Œ -defineSymbol(text, textord, "\u0152", "\\OE", true); -defineSymbol(math, textord, "\u0153", "\\oe", true); // œ -defineSymbol(text, textord, "\u0153", "\\oe", true); - -defineSymbol(math, bin, "\u2217", "\u2217", true); -defineSymbol(math, bin, "+", "+"); -defineSymbol(math, bin, "\u2217", "*"); -defineSymbol(math, bin, "\u2044", "/", true); -defineSymbol(math, bin, "\u2044", "\u2044"); -defineSymbol(math, bin, "\u2212", "-", true); -defineSymbol(math, bin, "\u22c5", "\\cdot", true); -defineSymbol(math, bin, "\u2218", "\\circ", true); -defineSymbol(math, bin, "\u00f7", "\\div", true); -defineSymbol(math, bin, "\u00b1", "\\pm", true); -defineSymbol(math, bin, "\u00d7", "\\times", true); -defineSymbol(math, bin, "\u2229", "\\cap", true); -defineSymbol(math, bin, "\u222a", "\\cup", true); -defineSymbol(math, bin, "\u2216", "\\setminus", true); -defineSymbol(math, bin, "\u2227", "\\land"); -defineSymbol(math, bin, "\u2228", "\\lor"); -defineSymbol(math, bin, "\u2227", "\\wedge", true); -defineSymbol(math, bin, "\u2228", "\\vee", true); -defineSymbol(math, open, "\u27e6", "\\llbracket", true); // stmaryrd/semantic packages -defineSymbol(math, close, "\u27e7", "\\rrbracket", true); -defineSymbol(math, open, "\u27e8", "\\langle", true); -defineSymbol(math, open, "\u27ea", "\\lAngle", true); -defineSymbol(math, open, "\u2989", "\\llangle", true); -defineSymbol(math, open, "|", "\\lvert"); -defineSymbol(math, open, "\u2016", "\\lVert", true); -defineSymbol(math, textord, "!", "\\oc"); // cmll package -defineSymbol(math, textord, "?", "\\wn"); -defineSymbol(math, textord, "\u2193", "\\shpos"); -defineSymbol(math, textord, "\u2195", "\\shift"); -defineSymbol(math, textord, "\u2191", "\\shneg"); -defineSymbol(math, close, "?", "?"); -defineSymbol(math, close, "!", "!"); -defineSymbol(math, close, "‼", "‼"); -defineSymbol(math, close, "\u27e9", "\\rangle", true); -defineSymbol(math, close, "\u27eb", "\\rAngle", true); -defineSymbol(math, close, "\u298a", "\\rrangle", true); -defineSymbol(math, close, "|", "\\rvert"); -defineSymbol(math, close, "\u2016", "\\rVert"); -defineSymbol(math, open, "\u2983", "\\lBrace", true); // stmaryrd/semantic packages -defineSymbol(math, close, "\u2984", "\\rBrace", true); -defineSymbol(math, rel, "=", "\\equal", true); -defineSymbol(math, rel, ":", ":"); -defineSymbol(math, rel, "\u2248", "\\approx", true); -defineSymbol(math, rel, "\u2245", "\\cong", true); -defineSymbol(math, rel, "\u2265", "\\ge"); -defineSymbol(math, rel, "\u2265", "\\geq", true); -defineSymbol(math, rel, "\u2190", "\\gets"); -defineSymbol(math, rel, ">", "\\gt", true); -defineSymbol(math, rel, "\u2208", "\\in", true); -defineSymbol(math, rel, "\u2209", "\\notin", true); -defineSymbol(math, rel, "\ue020", "\\@not"); -defineSymbol(math, rel, "\u2282", "\\subset", true); -defineSymbol(math, rel, "\u2283", "\\supset", true); -defineSymbol(math, rel, "\u2286", "\\subseteq", true); -defineSymbol(math, rel, "\u2287", "\\supseteq", true); -defineSymbol(math, rel, "\u2288", "\\nsubseteq", true); -defineSymbol(math, rel, "\u2288", "\\nsubseteqq"); -defineSymbol(math, rel, "\u2289", "\\nsupseteq", true); -defineSymbol(math, rel, "\u2289", "\\nsupseteqq"); -defineSymbol(math, rel, "\u22a8", "\\models"); -defineSymbol(math, rel, "\u2190", "\\leftarrow", true); -defineSymbol(math, rel, "\u2264", "\\le"); -defineSymbol(math, rel, "\u2264", "\\leq", true); -defineSymbol(math, rel, "<", "\\lt", true); -defineSymbol(math, rel, "\u2192", "\\rightarrow", true); -defineSymbol(math, rel, "\u2192", "\\to"); -defineSymbol(math, rel, "\u2271", "\\ngeq", true); -defineSymbol(math, rel, "\u2271", "\\ngeqq"); -defineSymbol(math, rel, "\u2271", "\\ngeqslant"); -defineSymbol(math, rel, "\u2270", "\\nleq", true); -defineSymbol(math, rel, "\u2270", "\\nleqq"); -defineSymbol(math, rel, "\u2270", "\\nleqslant"); -defineSymbol(math, rel, "\u2aeb", "\\Perp", true); //cmll package -defineSymbol(math, spacing, "\u00a0", "\\ "); -defineSymbol(math, spacing, "\u00a0", "\\space"); -// Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{% -defineSymbol(math, spacing, "\u00a0", "\\nobreakspace"); -defineSymbol(text, spacing, "\u00a0", "\\ "); -defineSymbol(text, spacing, "\u00a0", " "); -defineSymbol(text, spacing, "\u00a0", "\\space"); -defineSymbol(text, spacing, "\u00a0", "\\nobreakspace"); -defineSymbol(math, spacing, null, "\\nobreak"); -defineSymbol(math, spacing, null, "\\allowbreak"); -defineSymbol(math, punct, ",", ","); -defineSymbol(text, punct, ":", ":"); -defineSymbol(math, punct, ";", ";"); -defineSymbol(math, bin, "\u22bc", "\\barwedge"); -defineSymbol(math, bin, "\u22bb", "\\veebar"); -defineSymbol(math, bin, "\u2299", "\\odot", true); -// Firefox turns ⊕ into an emoji. So append \uFE0E. Define Unicode character in macros, not here. -defineSymbol(math, bin, "\u2295\uFE0E", "\\oplus"); -defineSymbol(math, bin, "\u2297", "\\otimes", true); -defineSymbol(math, textord, "\u2202", "\\partial", true); -defineSymbol(math, bin, "\u2298", "\\oslash", true); -defineSymbol(math, bin, "\u229a", "\\circledcirc", true); -defineSymbol(math, bin, "\u22a1", "\\boxdot", true); -defineSymbol(math, bin, "\u25b3", "\\bigtriangleup"); -defineSymbol(math, bin, "\u25bd", "\\bigtriangledown"); -defineSymbol(math, bin, "\u2020", "\\dagger"); -defineSymbol(math, bin, "\u22c4", "\\diamond"); -defineSymbol(math, bin, "\u25c3", "\\triangleleft"); -defineSymbol(math, bin, "\u25b9", "\\triangleright"); -defineSymbol(math, open, "{", "\\{"); -defineSymbol(text, textord, "{", "\\{"); -defineSymbol(text, textord, "{", "\\textbraceleft"); -defineSymbol(math, close, "}", "\\}"); -defineSymbol(text, textord, "}", "\\}"); -defineSymbol(text, textord, "}", "\\textbraceright"); -defineSymbol(math, open, "{", "\\lbrace"); -defineSymbol(math, close, "}", "\\rbrace"); -defineSymbol(math, open, "[", "\\lbrack", true); -defineSymbol(text, textord, "[", "\\lbrack", true); -defineSymbol(math, close, "]", "\\rbrack", true); -defineSymbol(text, textord, "]", "\\rbrack", true); -defineSymbol(math, open, "(", "\\lparen", true); -defineSymbol(math, close, ")", "\\rparen", true); -defineSymbol(math, open, "⦇", "\\llparenthesis", true); -defineSymbol(math, close, "⦈", "\\rrparenthesis", true); -defineSymbol(text, textord, "<", "\\textless", true); // in T1 fontenc -defineSymbol(text, textord, ">", "\\textgreater", true); // in T1 fontenc -defineSymbol(math, open, "\u230a", "\\lfloor", true); -defineSymbol(math, close, "\u230b", "\\rfloor", true); -defineSymbol(math, open, "\u2308", "\\lceil", true); -defineSymbol(math, close, "\u2309", "\\rceil", true); -defineSymbol(math, textord, "\\", "\\backslash"); -defineSymbol(math, textord, "|", "|"); -defineSymbol(math, textord, "|", "\\vert"); -defineSymbol(text, textord, "|", "\\textbar", true); // in T1 fontenc -defineSymbol(math, textord, "\u2016", "\\|"); -defineSymbol(math, textord, "\u2016", "\\Vert"); -defineSymbol(text, textord, "\u2016", "\\textbardbl"); -defineSymbol(text, textord, "~", "\\textasciitilde"); -defineSymbol(text, textord, "\\", "\\textbackslash"); -defineSymbol(text, textord, "^", "\\textasciicircum"); -defineSymbol(math, rel, "\u2191", "\\uparrow", true); -defineSymbol(math, rel, "\u21d1", "\\Uparrow", true); -defineSymbol(math, rel, "\u2193", "\\downarrow", true); -defineSymbol(math, rel, "\u21d3", "\\Downarrow", true); -defineSymbol(math, rel, "\u2195", "\\updownarrow", true); -defineSymbol(math, rel, "\u21d5", "\\Updownarrow", true); -defineSymbol(math, op, "\u2210", "\\coprod"); -defineSymbol(math, op, "\u22c1", "\\bigvee"); -defineSymbol(math, op, "\u22c0", "\\bigwedge"); -defineSymbol(math, op, "\u2a04", "\\biguplus"); -defineSymbol(math, op, "\u2a04", "\\bigcupplus"); -defineSymbol(math, op, "\u2a03", "\\bigcupdot"); -defineSymbol(math, op, "\u2a07", "\\bigdoublevee"); -defineSymbol(math, op, "\u2a08", "\\bigdoublewedge"); -defineSymbol(math, op, "\u22c2", "\\bigcap"); -defineSymbol(math, op, "\u22c3", "\\bigcup"); -defineSymbol(math, op, "\u222b", "\\int"); -defineSymbol(math, op, "\u222b", "\\intop"); -defineSymbol(math, op, "\u222c", "\\iint"); -defineSymbol(math, op, "\u222d", "\\iiint"); -defineSymbol(math, op, "\u220f", "\\prod"); -defineSymbol(math, op, "\u2211", "\\sum"); -defineSymbol(math, op, "\u2a02", "\\bigotimes"); -defineSymbol(math, op, "\u2a01", "\\bigoplus"); -defineSymbol(math, op, "\u2a00", "\\bigodot"); -defineSymbol(math, op, "\u2a09", "\\bigtimes"); -defineSymbol(math, op, "\u222e", "\\oint"); -defineSymbol(math, op, "\u222f", "\\oiint"); -defineSymbol(math, op, "\u2230", "\\oiiint"); -defineSymbol(math, op, "\u2231", "\\intclockwise"); -defineSymbol(math, op, "\u2232", "\\varointclockwise"); -defineSymbol(math, op, "\u2a0c", "\\iiiint"); -defineSymbol(math, op, "\u2a0d", "\\intbar"); -defineSymbol(math, op, "\u2a0e", "\\intBar"); -defineSymbol(math, op, "\u2a0f", "\\fint"); -defineSymbol(math, op, "\u2a12", "\\rppolint"); -defineSymbol(math, op, "\u2a13", "\\scpolint"); -defineSymbol(math, op, "\u2a15", "\\pointint"); -defineSymbol(math, op, "\u2a16", "\\sqint"); -defineSymbol(math, op, "\u2a17", "\\intlarhk"); -defineSymbol(math, op, "\u2a18", "\\intx"); -defineSymbol(math, op, "\u2a19", "\\intcap"); -defineSymbol(math, op, "\u2a1a", "\\intcup"); -defineSymbol(math, op, "\u2a05", "\\bigsqcap"); -defineSymbol(math, op, "\u2a06", "\\bigsqcup"); -defineSymbol(math, op, "\u222b", "\\smallint"); -defineSymbol(text, inner, "\u2026", "\\textellipsis"); -defineSymbol(math, inner, "\u2026", "\\mathellipsis"); -defineSymbol(text, inner, "\u2026", "\\ldots", true); -defineSymbol(math, inner, "\u2026", "\\ldots", true); -defineSymbol(math, inner, "\u22f0", "\\iddots", true); -defineSymbol(math, inner, "\u22ef", "\\@cdots", true); -defineSymbol(math, inner, "\u22f1", "\\ddots", true); -defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro -defineSymbol(text, textord, "\u22ee", "\\varvdots"); -defineSymbol(math, accent, "\u00b4", "\\acute"); -defineSymbol(math, accent, "\u0060", "\\grave"); -defineSymbol(math, accent, "\u00a8", "\\ddot"); -defineSymbol(math, accent, "\u2026", "\\dddot"); -defineSymbol(math, accent, "\u2026\u002e", "\\ddddot"); -defineSymbol(math, accent, "\u007e", "\\tilde"); -defineSymbol(math, accent, "\u203e", "\\bar"); -defineSymbol(math, accent, "\u02d8", "\\breve"); -defineSymbol(math, accent, "\u02c7", "\\check"); -defineSymbol(math, accent, "\u005e", "\\hat"); -defineSymbol(math, accent, "\u2192", "\\vec"); -defineSymbol(math, accent, "\u02d9", "\\dot"); -defineSymbol(math, accent, "\u02da", "\\mathring"); -defineSymbol(math, mathord, "\u0131", "\\imath", true); -defineSymbol(math, mathord, "\u0237", "\\jmath", true); -defineSymbol(math, textord, "\u0131", "\u0131"); -defineSymbol(math, textord, "\u0237", "\u0237"); -defineSymbol(text, textord, "\u0131", "\\i", true); -defineSymbol(text, textord, "\u0237", "\\j", true); -defineSymbol(text, textord, "\u00f8", "\\o", true); -defineSymbol(math, mathord, "\u00f8", "\\o", true); -defineSymbol(text, textord, "\u00d8", "\\O", true); -defineSymbol(math, mathord, "\u00d8", "\\O", true); -defineSymbol(text, accent, "\u02ca", "\\'"); // acute -defineSymbol(text, accent, "\u02cb", "\\`"); // grave -defineSymbol(text, accent, "\u02c6", "\\^"); // circumflex -defineSymbol(text, accent, "\u007e", "\\~"); // tilde -defineSymbol(text, accent, "\u02c9", "\\="); // macron -defineSymbol(text, accent, "\u02d8", "\\u"); // breve -defineSymbol(text, accent, "\u02d9", "\\."); // dot above -defineSymbol(text, accent, "\u00b8", "\\c"); // cedilla -defineSymbol(text, accent, "\u02da", "\\r"); // ring above -defineSymbol(text, accent, "\u02c7", "\\v"); // caron -defineSymbol(text, accent, "\u00a8", '\\"'); // diaeresis -defineSymbol(text, accent, "\u02dd", "\\H"); // double acute -defineSymbol(math, accent, "\u02ca", "\\'"); // acute -defineSymbol(math, accent, "\u02cb", "\\`"); // grave -defineSymbol(math, accent, "\u02c6", "\\^"); // circumflex -defineSymbol(math, accent, "\u007e", "\\~"); // tilde -defineSymbol(math, accent, "\u02c9", "\\="); // macron -defineSymbol(math, accent, "\u02d8", "\\u"); // breve -defineSymbol(math, accent, "\u02d9", "\\."); // dot above -defineSymbol(math, accent, "\u00b8", "\\c"); // cedilla -defineSymbol(math, accent, "\u02da", "\\r"); // ring above -defineSymbol(math, accent, "\u02c7", "\\v"); // caron -defineSymbol(math, accent, "\u00a8", '\\"'); // diaeresis -defineSymbol(math, accent, "\u02dd", "\\H"); // double acute - -// These ligatures are detected and created in Parser.js's `formLigatures`. -const ligatures = { - "--": true, - "---": true, - "``": true, - "''": true -}; - -defineSymbol(text, textord, "\u2013", "--", true); -defineSymbol(text, textord, "\u2013", "\\textendash"); -defineSymbol(text, textord, "\u2014", "---", true); -defineSymbol(text, textord, "\u2014", "\\textemdash"); -defineSymbol(text, textord, "\u2018", "`", true); -defineSymbol(text, textord, "\u2018", "\\textquoteleft"); -defineSymbol(text, textord, "\u2019", "'", true); -defineSymbol(text, textord, "\u2019", "\\textquoteright"); -defineSymbol(text, textord, "\u201c", "``", true); -defineSymbol(text, textord, "\u201c", "\\textquotedblleft"); -defineSymbol(text, textord, "\u201d", "''", true); -defineSymbol(text, textord, "\u201d", "\\textquotedblright"); -// \degree from gensymb package -defineSymbol(math, textord, "\u00b0", "\\degree", true); -defineSymbol(text, textord, "\u00b0", "\\degree"); -// \textdegree from inputenc package -defineSymbol(text, textord, "\u00b0", "\\textdegree", true); -// TODO: In LaTeX, \pounds can generate a different character in text and math -// mode, but among our fonts, only Main-Regular defines this character "163". -defineSymbol(math, textord, "\u00a3", "\\pounds"); -defineSymbol(math, textord, "\u00a3", "\\mathsterling", true); -defineSymbol(text, textord, "\u00a3", "\\pounds"); -defineSymbol(text, textord, "\u00a3", "\\textsterling", true); -defineSymbol(math, textord, "\u2720", "\\maltese"); -defineSymbol(text, textord, "\u2720", "\\maltese"); -defineSymbol(math, textord, "\u20ac", "\\euro", true); -defineSymbol(text, textord, "\u20ac", "\\euro", true); -defineSymbol(text, textord, "\u20ac", "\\texteuro"); -defineSymbol(math, textord, "\u00a9", "\\copyright", true); -defineSymbol(text, textord, "\u00a9", "\\textcopyright"); -defineSymbol(math, textord, "\u2300", "\\diameter", true); -defineSymbol(text, textord, "\u2300", "\\diameter"); - -// Italic Greek -defineSymbol(math, textord, "𝛤", "\\varGamma"); -defineSymbol(math, textord, "𝛥", "\\varDelta"); -defineSymbol(math, textord, "𝛩", "\\varTheta"); -defineSymbol(math, textord, "𝛬", "\\varLambda"); -defineSymbol(math, textord, "𝛯", "\\varXi"); -defineSymbol(math, textord, "𝛱", "\\varPi"); -defineSymbol(math, textord, "𝛴", "\\varSigma"); -defineSymbol(math, textord, "𝛶", "\\varUpsilon"); -defineSymbol(math, textord, "𝛷", "\\varPhi"); -defineSymbol(math, textord, "𝛹", "\\varPsi"); -defineSymbol(math, textord, "𝛺", "\\varOmega"); -defineSymbol(text, textord, "𝛤", "\\varGamma"); -defineSymbol(text, textord, "𝛥", "\\varDelta"); -defineSymbol(text, textord, "𝛩", "\\varTheta"); -defineSymbol(text, textord, "𝛬", "\\varLambda"); -defineSymbol(text, textord, "𝛯", "\\varXi"); -defineSymbol(text, textord, "𝛱", "\\varPi"); -defineSymbol(text, textord, "𝛴", "\\varSigma"); -defineSymbol(text, textord, "𝛶", "\\varUpsilon"); -defineSymbol(text, textord, "𝛷", "\\varPhi"); -defineSymbol(text, textord, "𝛹", "\\varPsi"); -defineSymbol(text, textord, "𝛺", "\\varOmega"); - - -// There are lots of symbols which are the same, so we add them in afterwards. -// All of these are textords in math mode -const mathTextSymbols = '0123456789/@."'; -for (let i = 0; i < mathTextSymbols.length; i++) { - const ch = mathTextSymbols.charAt(i); - defineSymbol(math, textord, ch, ch); -} - -// All of these are textords in text mode -const textSymbols = '0123456789!@*()-=+";:?/.,'; -for (let i = 0; i < textSymbols.length; i++) { - const ch = textSymbols.charAt(i); - defineSymbol(text, textord, ch, ch); -} - -// All of these are textords in text mode, and mathords in math mode -const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -for (let i = 0; i < letters.length; i++) { - const ch = letters.charAt(i); - defineSymbol(math, mathord, ch, ch); - defineSymbol(text, textord, ch, ch); -} - -// Some more letters in Unicode Basic Multilingual Plane. -const narrow = "ÇÐÞçþℂℍℕℙℚℝℤℎℏℊℋℌℐℑℒℓ℘ℛℜℬℰℱℳℭℨ"; -for (let i = 0; i < narrow.length; i++) { - const ch = narrow.charAt(i); - defineSymbol(math, mathord, ch, ch); - defineSymbol(text, textord, ch, ch); -} - -// The next loop loads wide (surrogate pair) characters. -// We support some letters in the Unicode range U+1D400 to U+1D7FF, -// Mathematical Alphanumeric Symbols. -let wideChar = ""; -for (let i = 0; i < letters.length; i++) { - // The hex numbers in the next line are a surrogate pair. - // 0xD835 is the high surrogate for all letters in the range we support. - // 0xDC00 is the low surrogate for bold A. - wideChar = String.fromCharCode(0xd835, 0xdc00 + i); // A-Z a-z bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdc34 + i); // A-Z a-z italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdc68 + i); // A-Z a-z bold italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdd04 + i); // A-Z a-z Fractur - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdda0 + i); // A-Z a-z sans-serif - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xddd4 + i); // A-Z a-z sans bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xde08 + i); // A-Z a-z sans italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xde70 + i); // A-Z a-z monospace - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdd38 + i); // A-Z a-z double struck - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - const ch = letters.charAt(i); - wideChar = String.fromCharCode(0xd835, 0xdc9c + i); // A-Z a-z calligraphic - defineSymbol(math, mathord, ch, wideChar); - defineSymbol(text, textord, ch, wideChar); -} - -// Next, some wide character numerals -for (let i = 0; i < 10; i++) { - wideChar = String.fromCharCode(0xd835, 0xdfce + i); // 0-9 bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdfe2 + i); // 0-9 sans serif - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdfec + i); // 0-9 bold sans - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdff6 + i); // 0-9 monospace - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); -} - -/* - * Neither Firefox nor Chrome support hard line breaks or soft line breaks. - * (Despite https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs) - * So Temml has work-arounds for both hard and soft breaks. - * The work-arounds sadly do not work simultaneously. Any top-level hard - * break makes soft line breaks impossible. - * - * Hard breaks are simulated by creating a and putting each line in its own . - * - * To create soft line breaks, Temml avoids using the and tags. - * Then the top level of a element can be occupied by elements, and the browser - * will break after a if the expression extends beyond the container limit. - * - * The default is for soft line breaks after each top-level binary or - * relational operator, per TeXbook p. 173. So we gather the expression into s so that - * each ends in a binary or relational operator. - * - * An option is for soft line breaks before an "=" sign. That changes the s. - * - * Soft line breaks will not work in Chromium and Safari, only Firefox. - * - * Hopefully browsers will someday do their own linebreaking and we will be able to delete - * much of this module. - */ - -function setLineBreaks(expression, wrapMode, isDisplayMode) { - const mtrs = []; - let mrows = []; - let block = []; - let numTopLevelEquals = 0; - let i = 0; - while (i < expression.length) { - while (expression[i] instanceof DocumentFragment) { - expression.splice(i, 1, ...expression[i].children); // Expand the fragment. - } - const node = expression[i]; - if (node.attributes && node.attributes.linebreak && - node.attributes.linebreak === "newline") { - // A hard line break. Create a for the current block. - if (block.length > 0) { - mrows.push(new MathNode("mrow", block)); - } - mrows.push(node); - block = []; - const mtd = new MathNode("mtd", mrows); - mtd.style.textAlign = "left"; - mtrs.push(new MathNode("mtr", [mtd])); - mrows = []; - i += 1; - continue - } - block.push(node); - if (node.type && node.type === "mo" && node.children.length === 1 && - !(node.attributes.form && node.attributes.form === "prefix") && // unary operators - !Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) { - const ch = node.children[0].text; - if (wrapMode === "=" && ch === "=") { - numTopLevelEquals += 1; - if (numTopLevelEquals > 1) { - block.pop(); - // Start a new block. (Insert a soft linebreak.) - const element = new MathNode("mrow", block); - mrows.push(element); - block = [node]; - } - } else if (wrapMode === "tex") { - // Check if the following node is a \nobreak text node, e.g. "~"" - const next = i < expression.length - 1 ? expression[i + 1] : null; - let glueIsFreeOfNobreak = true; - if ( - !( - next && - next.type === "mtext" && - next.attributes.linebreak && - next.attributes.linebreak === "nobreak" - ) - ) { - // We may need to start a new block. - // First, put any post-operator glue on same line as operator. - for (let j = i + 1; j < expression.length; j++) { - const nd = expression[j]; - if ( - nd.type && - nd.type === "mspace" && - !(nd.attributes.linebreak && nd.attributes.linebreak === "newline") - ) { - block.push(nd); - i += 1; - if ( - nd.attributes && - nd.attributes.linebreak && - nd.attributes.linebreak === "nobreak" - ) { - glueIsFreeOfNobreak = false; - } - } else { - break; - } - } - } - if (glueIsFreeOfNobreak) { - // Start a new block. (Insert a soft linebreak.) - const element = new MathNode("mrow", block); - mrows.push(element); - block = []; - } - } - } - i += 1; - } - if (block.length > 0) { - const element = new MathNode("mrow", block); - mrows.push(element); - } - if (mtrs.length > 0) { - const mtd = new MathNode("mtd", mrows); - mtd.style.textAlign = "left"; - const mtr = new MathNode("mtr", [mtd]); - mtrs.push(mtr); - const mtable = new MathNode("mtable", mtrs); - if (!isDisplayMode) { - mtable.setAttribute("columnalign", "left"); - mtable.setAttribute("rowspacing", "0em"); - } - return mtable - } - return newDocumentFragment(mrows); -} - -/** - * This file converts a parse tree into a corresponding MathML tree. The main - * entry point is the `buildMathML` function, which takes a parse tree from the - * parser. - */ - - -/** - * Takes a symbol and converts it into a MathML text node after performing - * optional replacement from symbols.js. - */ -const makeText = function(text, mode, style) { - if ( - symbols[mode][text] && - symbols[mode][text].replace && - text.charCodeAt(0) !== 0xd835 && - !( - Object.prototype.hasOwnProperty.call(ligatures, text) && - style && - ((style.fontFamily && style.fontFamily.slice(4, 6) === "tt") || - (style.font && style.font.slice(4, 6) === "tt")) - ) - ) { - text = symbols[mode][text].replace; - } - - return new TextNode(text); -}; - -const copyChar = (newRow, child) => { - if (newRow.children.length === 0 || - newRow.children[newRow.children.length - 1].type !== "mtext") { - const mtext = new MathNode( - "mtext", - [new TextNode(child.children[0].text)] - ); - newRow.children.push(mtext); - } else { - newRow.children[newRow.children.length - 1].children[0].text += child.children[0].text; - } -}; - -const consolidateText = mrow => { - // If possible, consolidate adjacent elements into a single element. - if (mrow.type !== "mrow" && mrow.type !== "mstyle") { return mrow } - if (mrow.children.length === 0) { return mrow } // empty group, e.g., \text{} - const newRow = new MathNode("mrow"); - for (let i = 0; i < mrow.children.length; i++) { - const child = mrow.children[i]; - if (child.type === "mtext" && Object.keys(child.attributes).length === 0) { - copyChar(newRow, child); - } else if (child.type === "mrow") { - // We'll also check the children of an mrow. One level only. No recursion. - let canConsolidate = true; - for (let j = 0; j < child.children.length; j++) { - const grandChild = child.children[j]; - if (grandChild.type !== "mtext" || Object.keys(child.attributes).length !== 0) { - canConsolidate = false; - break - } - } - if (canConsolidate) { - for (let j = 0; j < child.children.length; j++) { - const grandChild = child.children[j]; - copyChar(newRow, grandChild); - } - } else { - newRow.children.push(child); - } - } else { - newRow.children.push(child); - } - } - for (let i = 0; i < newRow.children.length; i++) { - if (newRow.children[i].type === "mtext") { - const mtext = newRow.children[i]; - // Firefox does not render a space at either end of an string. - // To get proper rendering, we replace leading or trailing spaces with no-break spaces. - if (mtext.children[0].text.charAt(0) === " ") { - mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1); - } - const L = mtext.children[0].text.length; - if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") { - mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0"; - } - for (const [key, value] of Object.entries(mrow.attributes)) { - mtext.attributes[key] = value; - } - } - } - if (newRow.children.length === 1 && newRow.children[0].type === "mtext") { - return newRow.children[0]; // A consolidated - } else { - return newRow - } -}; - -/** - * Wrap the given array of nodes in an node if needed, i.e., - * unless the array has length 1. Always returns a single node. - */ -const makeRow = function(body, semisimple = false) { - if (body.length === 1 && !(body[0] instanceof DocumentFragment)) { - return body[0]; - } else if (!semisimple) { - // Suppress spacing on nodes at both ends of the row. - if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) { - body[0].attributes.lspace = "0em"; - body[0].attributes.rspace = "0em"; - } - const end = body.length - 1; - if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) { - body[end].attributes.lspace = "0em"; - body[end].attributes.rspace = "0em"; - } - } - return new MathNode("mrow", body); -}; - -/** - * Check for . which is how a dot renders in MathML, - * or , - * which is how a braced comma {,} renders in MathML - */ -function isNumberPunctuation(group) { - if (!group) { - return false - } - if (group.type === 'mi' && group.children.length === 1) { - const child = group.children[0]; - return child instanceof TextNode && child.text === '.' - } else if (group.type === "mtext" && group.children.length === 1) { - const child = group.children[0]; - return child instanceof TextNode && child.text === '\u2008' // punctuation space - } else if (group.type === 'mo' && group.children.length === 1 && - group.getAttribute('separator') === 'true' && - group.getAttribute('lspace') === '0em' && - group.getAttribute('rspace') === '0em') { - const child = group.children[0]; - return child instanceof TextNode && child.text === ',' - } else { - return false - } -} -const isComma = (expression, i) => { - const node = expression[i]; - const followingNode = expression[i + 1]; - return (node.type === "atom" && node.text === ",") && - // Don't consolidate if there is a space after the comma. - node.loc && followingNode.loc && node.loc.end === followingNode.loc.start -}; - -const isRel = item => { - return (item.type === "atom" && item.family === "rel") || - (item.type === "mclass" && item.mclass === "mrel") -}; - -/** - * Takes a list of nodes, builds them, and returns a list of the generated - * MathML nodes. Also do a couple chores along the way: - * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}. - * (2) Suppress spacing between two adjacent relations. - */ -const buildExpression = function(expression, style, semisimple = false) { - if (!semisimple && expression.length === 1) { - const group = buildGroup$1(expression[0], style); - if (group instanceof MathNode && group.type === "mo") { - // When TeX writers want to suppress spacing on an operator, - // they often put the operator by itself inside braces. - group.setAttribute("lspace", "0em"); - group.setAttribute("rspace", "0em"); - } - return [group]; - } - - const groups = []; - const groupArray = []; - let lastGroup; - for (let i = 0; i < expression.length; i++) { - groupArray.push(buildGroup$1(expression[i], style)); - } - - for (let i = 0; i < groupArray.length; i++) { - const group = groupArray[i]; - - // Suppress spacing between adjacent relations - if (i < expression.length - 1 && isRel(expression[i]) && isRel(expression[i + 1])) { - group.setAttribute("rspace", "0em"); - } - if (i > 0 && isRel(expression[i]) && isRel(expression[i - 1])) { - group.setAttribute("lspace", "0em"); - } - - // Concatenate numbers - if (group.type === 'mn' && lastGroup && lastGroup.type === 'mn') { - // Concatenate ... followed by . - lastGroup.children.push(...group.children); - continue - } else if (isNumberPunctuation(group) && lastGroup && lastGroup.type === 'mn') { - // Concatenate ... followed by . - lastGroup.children.push(...group.children); - continue - } else if (lastGroup && lastGroup.type === "mn" && i < groupArray.length - 1 && - groupArray[i + 1].type === "mn" && isComma(expression, i)) { - lastGroup.children.push(...group.children); - continue - } else if (group.type === 'mn' && isNumberPunctuation(lastGroup)) { - // Concatenate . followed by ... - group.children = [...lastGroup.children, ...group.children]; - groups.pop(); - } else if ((group.type === 'msup' || group.type === 'msub') && - group.children.length >= 1 && lastGroup && - (lastGroup.type === 'mn' || isNumberPunctuation(lastGroup))) { - // Put preceding ... or . inside base of - // ...base......exponent... (or ) - const base = group.children[0]; - if (base instanceof MathNode && base.type === 'mn' && lastGroup) { - base.children = [...lastGroup.children, ...base.children]; - groups.pop(); - } - } - groups.push(group); - lastGroup = group; - } - return groups -}; - -/** - * Equivalent to buildExpression, but wraps the elements in an - * if there's more than one. Returns a single node instead of an array. - */ -const buildExpressionRow = function(expression, style, semisimple = false) { - return makeRow(buildExpression(expression, style, semisimple), semisimple); -}; - -/** - * Takes a group from the parser and calls the appropriate groupBuilders function - * on it to produce a MathML node. - */ -const buildGroup$1 = function(group, style) { - if (!group) { - return new MathNode("mrow"); - } - - if (_mathmlGroupBuilders[group.type]) { - // Call the groupBuilders function - const result = _mathmlGroupBuilders[group.type](group, style); - return result; - } else { - throw new ParseError("Got group of unknown type: '" + group.type + "'"); - } -}; - -const glue$1 = _ => { - return new MathNode("mtd", [], [], { padding: "0", width: "50%" }) -}; - -const labelContainers = ["mrow", "mtd", "mtable", "mtr"]; -const getLabel = parent => { - for (const node of parent.children) { - if (node.type && labelContainers.includes(node.type)) { - if (node.classes && node.classes[0] === "tml-label") { - const label = node.label; - return label - } else { - const label = getLabel(node); - if (label) { return label } - } - } else if (!node.type) { - const label = getLabel(node); - if (label) { return label } - } - } -}; - -const taggedExpression = (expression, tag, style, leqno) => { - tag = buildExpressionRow(tag[0].body, style); - tag = consolidateText(tag); // tag is now an element - tag.classes.push("tml-tag"); // to be available for \ref - - const label = getLabel(expression); // from a \label{} function. - expression = new MathNode("mtd", [expression]); - const rowArray = [glue$1(), expression, glue$1()]; - rowArray[leqno ? 0 : 2].children.push(tag); - const mtr = new MathNode("mtr", rowArray, ["tml-tageqn"]); - if (label) { mtr.setAttribute("id", label); } - const table = new MathNode("mtable", [mtr]); - table.style.width = "100%"; - table.setAttribute("displaystyle", "true"); - return table -}; - -/** - * Takes a full parse tree and settings and builds a MathML representation of - * it. - */ -function buildMathML(tree, texExpression, style, settings) { - // Strip off outer tag wrapper for processing below. - let tag = null; - if (tree.length === 1 && tree[0].type === "tag") { - tag = tree[0].tag; - tree = tree[0].body; - } - - const expression = buildExpression(tree, style); - - if (expression.length === 1 && expression[0] instanceof AnchorNode) { - return expression[0] - } - - const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap; - - const n1 = expression.length === 0 ? null : expression[0]; - let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode) - ? expression[0] - : setLineBreaks(expression, wrap, settings.displayMode); - - if (tag) { - wrapper = taggedExpression(wrapper, tag, style, settings.leqno); - } - - if (settings.annotate) { - // Build a TeX annotation of the source - const annotation = new MathNode( - "annotation", [new TextNode(texExpression)]); - annotation.setAttribute("encoding", "application/x-tex"); - wrapper = new MathNode("semantics", [wrapper, annotation]); - } - - const math = new MathNode("math", [wrapper]); - - if (settings.xml) { - math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML"); - } - if (settings.displayMode) { - math.setAttribute("display", "block"); - math.style.display = "block math"; // necessary in Chromium. - // Firefox and Safari do not recognize display: "block math". - // Set a class so that the CSS file can set display: block. - math.classes = ["tml-display"]; - } - return math; -} - -// From the KaTeX font metrics, identify letters whose accents need a italic correction. -const smallNudge = "DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ"; -const mediumNudge = "BCEGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ"; -const largeNudge = "AFJdfΔΛ"; - -const mathmlBuilder$a = (group, style) => { - const accentNode$1 = group.isStretchy - ? accentNode(group) - : new MathNode("mo", [makeText(group.label, group.mode)]); - if (!group.isStretchy) { - accentNode$1.setAttribute("stretchy", "false"); // Keep Firefox from stretching \check - } - if (group.label !== "\\vec") { - accentNode$1.style.mathDepth = "0"; // not scriptstyle - // Don't use attribute accent="true" because MathML Core eliminates a needed space. - } - const tag = group.label === "\\c" ? "munder" : "mover"; - const needsWbkVertShift = needsWebkitVerticalShift.has(group.label); - if (tag === "mover" && group.mode === "math" && (!group.isStretchy) && group.base.text - && group.base.text.length === 1) { - const text = group.base.text; - const isVec = group.label === "\\vec"; - const vecPostfix = isVec === "\\vec" ? "-vec" : ""; - if (isVec) { - accentNode$1.classes.push("tml-vec"); // Firefox sizing of \vec arrow - } - const wbkPostfix = isVec ? "-vec" : needsWbkVertShift ? "-acc" : ""; - if (smallNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-sml${vecPostfix}`); - accentNode$1.classes.push(`wbk-sml${wbkPostfix}`); - } else if (mediumNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-med${vecPostfix}`); - accentNode$1.classes.push(`wbk-med${wbkPostfix}`); - } else if (largeNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-lrg${vecPostfix}`); - accentNode$1.classes.push(`wbk-lrg${wbkPostfix}`); - } else if (isVec) { - accentNode$1.classes.push(`wbk-vec`); - } else if (needsWbkVertShift) { - accentNode$1.classes.push(`wbk-acc`); - } - } else if (needsWbkVertShift) { - // text-mode accents - accentNode$1.classes.push("wbk-acc"); - } - const node = new MathNode(tag, [buildGroup$1(group.base, style), accentNode$1]); - return node; -}; - -const nonStretchyAccents = new Set([ - "\\acute", - "\\check", - "\\grave", - "\\ddot", - "\\dddot", - "\\ddddot", - "\\tilde", - "\\bar", - "\\breve", - "\\check", - "\\hat", - "\\vec", - "\\dot", - "\\mathring" -]); - -const needsWebkitVerticalShift = new Set([ - "\\acute", - "\\bar", - "\\breve", - "\\check", - "\\dot", - "\\ddot", - "\\grave", - "\\hat", - "\\mathring", - "\\`", "\\'", "\\^", "\\=", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v" -]); - -const combiningChar = { - "\\`": "\u0300", - "\\'": "\u0301", - "\\^": "\u0302", - "\\~": "\u0303", - "\\=": "\u0304", - "\\u": "\u0306", - "\\.": "\u0307", - '\\"': "\u0308", - "\\r": "\u030A", - "\\H": "\u030B", - "\\v": "\u030C", - "\\c": "\u0327" -}; - -// Accents -defineFunction({ - type: "accent", - names: [ - "\\acute", - "\\grave", - "\\ddot", - "\\dddot", - "\\ddddot", - "\\tilde", - "\\bar", - "\\breve", - "\\check", - "\\hat", - "\\vec", - "\\dot", - "\\mathring", - "\\overparen", - "\\widecheck", - "\\widehat", - "\\wideparen", - "\\widetilde", - "\\overrightarrow", - "\\overleftarrow", - "\\Overrightarrow", - "\\overleftrightarrow", - "\\overgroup", - "\\overleftharpoon", - "\\overrightharpoon" - ], - props: { - numArgs: 1 - }, - handler: (context, args) => { - const base = normalizeArgument(args[0]); - - const isStretchy = !nonStretchyAccents.has(context.funcName); - - return { - type: "accent", - mode: context.parser.mode, - label: context.funcName, - isStretchy, - base - }; - }, - mathmlBuilder: mathmlBuilder$a -}); - -// Text-mode accents -defineFunction({ - type: "accent", - names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\c", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"], - props: { - numArgs: 1, - allowedInText: true, - allowedInMath: true, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const base = normalizeArgument(args[0]); - const mode = context.parser.mode; - - if (mode === "math" && context.parser.settings.strict) { - // LaTeX only writes a warning. It doesn't stop. We'll issue the same warning. - // eslint-disable-next-line no-console - console.log(`Temml parse error: Command ${context.funcName} is invalid in math mode.`); - } - - if (mode === "text" && base.text && base.text.length === 1 - && context.funcName in combiningChar && smalls.indexOf(base.text) > -1) { - // Return a combining accent character - return { - type: "textord", - mode: "text", - text: base.text + combiningChar[context.funcName] - } - } else if (context.funcName === "\\c" && mode === "text" && base.text - && base.text.length === 1) { - // combining cedilla - return { type: "textord", mode: "text", text: base.text + "\u0327" } - } else { - // Build up the accent - return { - type: "accent", - mode, - label: context.funcName, - isStretchy: false, - base - } - } - }, - mathmlBuilder: mathmlBuilder$a -}); - -defineFunction({ - type: "accentUnder", - names: [ - "\\underleftarrow", - "\\underrightarrow", - "\\underleftrightarrow", - "\\undergroup", - "\\underparen", - "\\utilde" - ], - props: { - numArgs: 1 - }, - handler: ({ parser, funcName }, args) => { - const base = args[0]; - return { - type: "accentUnder", - mode: parser.mode, - label: funcName, - base: base - }; - }, - mathmlBuilder: (group, style) => { - const accentNode$1 = accentNode(group); - accentNode$1.style["math-depth"] = 0; - const node = new MathNode("munder", [ - buildGroup$1(group.base, style), - accentNode$1 - ]); - return node; - } -}); - -/** - * This file does conversion between units. In particular, it provides - * calculateSize to convert other units into CSS units. - */ - - -const ptPerUnit = { - // Convert to CSS (Postscipt) points, not TeX points - // https://en.wikibooks.org/wiki/LaTeX/Lengths and - // https://tex.stackexchange.com/a/8263 - pt: 800 / 803, // convert TeX point to CSS (Postscript) point - pc: (12 * 800) / 803, // pica - dd: ((1238 / 1157) * 800) / 803, // didot - cc: ((14856 / 1157) * 800) / 803, // cicero (12 didot) - nd: ((685 / 642) * 800) / 803, // new didot - nc: ((1370 / 107) * 800) / 803, // new cicero (12 new didot) - sp: ((1 / 65536) * 800) / 803, // scaled point (TeX's internal smallest unit) - mm: (25.4 / 72), - cm: (2.54 / 72), - in: (1 / 72), - px: (96 / 72) -}; - -/** - * Determine whether the specified unit (either a string defining the unit - * or a "size" parse node containing a unit field) is valid. - */ -const validUnits = [ - "em", - "ex", - "mu", - "pt", - "mm", - "cm", - "in", - "px", - "bp", - "pc", - "dd", - "cc", - "nd", - "nc", - "sp" -]; - -const validUnit = function(unit) { - if (typeof unit !== "string") { - unit = unit.unit; - } - return validUnits.indexOf(unit) > -1 -}; - -const emScale = styleLevel => { - const scriptLevel = Math.max(styleLevel - 1, 0); - return [1, 0.7, 0.5][scriptLevel] -}; - -/* - * Convert a "size" parse node (with numeric "number" and string "unit" fields, - * as parsed by functions.js argType "size") into a CSS value. - */ -const calculateSize = function(sizeValue, style) { - let number = sizeValue.number; - if (style.maxSize[0] < 0 && number > 0) { - return { number: 0, unit: "em" } - } - const unit = sizeValue.unit; - switch (unit) { - case "mm": - case "cm": - case "in": - case "px": { - const numInCssPts = number * ptPerUnit[unit]; - if (numInCssPts > style.maxSize[1]) { - return { number: style.maxSize[1], unit: "pt" } - } - return { number, unit }; // absolute CSS units. - } - case "em": - case "ex": { - // In TeX, em and ex do not change size in \scriptstyle. - if (unit === "ex") { number *= 0.431; } - number = Math.min(number / emScale(style.level), style.maxSize[0]); - return { number: round(number), unit: "em" }; - } - case "bp": { - if (number > style.maxSize[1]) { number = style.maxSize[1]; } - return { number, unit: "pt" }; // TeX bp is a CSS pt. (1/72 inch). - } - case "pt": - case "pc": - case "dd": - case "cc": - case "nd": - case "nc": - case "sp": { - number = Math.min(number * ptPerUnit[unit], style.maxSize[1]); - return { number: round(number), unit: "pt" } - } - case "mu": { - number = Math.min(number / 18, style.maxSize[0]); - return { number: round(number), unit: "em" } - } - default: - throw new ParseError("Invalid unit: '" + unit + "'") - } -}; - -// Helper functions - -const padding = width => { - const node = new MathNode("mspace"); - node.setAttribute("width", width + "em"); - return node -}; - -const paddedNode = (group, lspace = 0.3, rspace = 0, mustSmash = false) => { - if (group == null && rspace === 0) { return padding(lspace) } - const row = group ? [group] : []; - if (lspace !== 0) { row.unshift(padding(lspace)); } - if (rspace > 0) { row.push(padding(rspace)); } - if (mustSmash) { - // Used for the bottom arrow in a {CD} environment - const mpadded = new MathNode("mpadded", row); - mpadded.setAttribute("height", "0.1px"); // Don't use 0. WebKit would hide it. - return mpadded - } else { - return new MathNode("mrow", row) - } -}; - -const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel); - -const munderoverNode = (fName, body, below, style) => { - const arrowNode = mathMLnode(fName); - // Is this the short part of a mhchem equilibrium arrow? - const isEq = fName.slice(1, 3) === "eq"; - const minWidth = fName.charAt(1) === "x" - ? "1.75" // mathtools extensible arrows are ≥ 1.75em long - : fName.slice(2, 4) === "cd" - ? "3.0" // cd package arrows - : isEq - ? "1.0" // The shorter harpoon of a mhchem equilibrium arrow - : "2.0"; // other mhchem arrows - // TODO: When Firefox supports minsize, use the next line. - //arrowNode.setAttribute("minsize", String(minWidth) + "em") - arrowNode.setAttribute("lspace", "0"); - arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0")); - - // upper and lower labels are set to scriptlevel by MathML - // So we have to adjust our label dimensions accordingly. - const labelStyle = style.withLevel(style.level < 2 ? 2 : 3); - const minArrowWidth = labelSize(minWidth, labelStyle.level); - // The dummyNode will be inside a inside a - // So it will be at scriptlevel 3 - const dummyWidth = labelSize(minWidth, 3); - const emptyLabel = paddedNode(null, minArrowWidth.toFixed(4), 0); - const dummyNode = paddedNode(null, dummyWidth.toFixed(4), 0); - // The arrow is a little longer than the label. Set a spacer length. - const space = labelSize((isEq ? 0 : 0.3), labelStyle.level).toFixed(4); - let upperNode; - let lowerNode; - - const gotUpper = (body && body.body && - // \hphantom visible content - (body.body.body || body.body.length > 0)); - if (gotUpper) { - let label = buildGroup$1(body, labelStyle); - const mustSmash = (fName === "\\\\cdrightarrow" || fName === "\\\\cdleftarrow"); - label = paddedNode(label, space, space, mustSmash); - // Since Firefox does not support minsize, stack a invisible node - // on top of the label. Its width will serve as a min-width. - // TODO: Refactor this after Firefox supports minsize. - upperNode = new MathNode("mover", [label, dummyNode]); - } - const gotLower = (below && below.body && - (below.body.body || below.body.length > 0)); - if (gotLower) { - let label = buildGroup$1(below, labelStyle); - label = paddedNode(label, space, space); - lowerNode = new MathNode("munder", [label, dummyNode]); - } - - let node; - if (!gotUpper && !gotLower) { - node = new MathNode("mover", [arrowNode, emptyLabel]); - } else if (gotUpper && gotLower) { - node = new MathNode("munderover", [arrowNode, lowerNode, upperNode]); - } else if (gotUpper) { - node = new MathNode("mover", [arrowNode, upperNode]); - } else { - node = new MathNode("munder", [arrowNode, lowerNode]); - } - if (minWidth === "3.0") { node.style.height = "1em"; } // CD environment - node.setAttribute("accent", "false"); // Necessary for MS Word - return node -}; - -// Stretchy arrows with an optional argument -defineFunction({ - type: "xArrow", - names: [ - "\\xleftarrow", - "\\xrightarrow", - "\\xLeftarrow", - "\\xRightarrow", - "\\xleftrightarrow", - "\\xLeftrightarrow", - "\\xhookleftarrow", - "\\xhookrightarrow", - "\\xmapsto", - "\\xrightharpoondown", - "\\xrightharpoonup", - "\\xleftharpoondown", - "\\xleftharpoonup", - "\\xlongequal", - "\\xtwoheadrightarrow", - "\\xtwoheadleftarrow", - "\\xtofrom", // expfeil - "\\xleftrightharpoons", // mathtools - "\\xrightleftharpoons", // mathtools - // The next 7 functions are here only to support mhchem - "\\yields", - "\\yieldsLeft", - "\\mesomerism", - "\\longrightharpoonup", - "\\longleftharpoondown", - "\\yieldsLeftRight", - "\\chemequilibrium", - // The next 3 functions are here only to support the {CD} environment. - "\\\\cdrightarrow", - "\\\\cdleftarrow", - "\\\\cdlongequal" - ], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser, funcName }, args, optArgs) { - return { - type: "xArrow", - mode: parser.mode, - name: funcName, - body: args[0], - below: optArgs[0] - }; - }, - mathmlBuilder(group, style) { - // Build the arrow and its labels. - const node = munderoverNode(group.name, group.body, group.below, style); - // Create operator spacing for a relation. - const row = [node]; - row.unshift(padding(0.2778)); - row.push(padding(0.2778)); - return new MathNode("mrow", row) - } -}); - -const arrowComponent = { - "\\equilibriumRight": ["\\longrightharpoonup", "\\eqleftharpoondown"], - "\\equilibriumLeft": ["\\eqrightharpoonup", "\\longleftharpoondown"] -}; - -// Math fonts do not have a single glyph for these two mhchem functions. -// So we stack a pair of single harpoons. -defineFunction({ - type: "stackedArrow", - names: [ - "\\equilibriumRight", - "\\equilibriumLeft" - ], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser, funcName }, args, optArgs) { - const lowerArrowBody = args[0] - ? { - type: "hphantom", - mode: parser.mode, - body: args[0] - } - : null; - const upperArrowBelow = optArgs[0] - ? { - type: "hphantom", - mode: parser.mode, - body: optArgs[0] - } - : null; - return { - type: "stackedArrow", - mode: parser.mode, - name: funcName, - body: args[0], - upperArrowBelow, - lowerArrowBody, - below: optArgs[0] - }; - }, - mathmlBuilder(group, style) { - const topLabel = arrowComponent[group.name][0]; - const botLabel = arrowComponent[group.name][1]; - const topArrow = munderoverNode(topLabel, group.body, group.upperArrowBelow, style); - const botArrow = munderoverNode(botLabel, group.lowerArrowBody, group.below, style); - let wrapper; - - const raiseNode = new MathNode("mpadded", [topArrow]); - raiseNode.setAttribute("voffset", "0.3em"); - raiseNode.setAttribute("height", "+0.3em"); - raiseNode.setAttribute("depth", "-0.3em"); - // One of the arrows is given ~zero width. so the other has the same horzontal alignment. - if (group.name === "\\equilibriumLeft") { - const botNode = new MathNode("mpadded", [botArrow]); - botNode.setAttribute("width", "0.5em"); - wrapper = new MathNode( - "mpadded", - [padding(0.2778), botNode, raiseNode, padding(0.2778)] - ); - } else { - raiseNode.setAttribute("width", (group.name === "\\equilibriumRight" ? "0.5em" : "0")); - wrapper = new MathNode( - "mpadded", - [padding(0.2778), raiseNode, botArrow, padding(0.2778)] - ); - } - - wrapper.setAttribute("voffset", "-0.18em"); - wrapper.setAttribute("height", "-0.18em"); - wrapper.setAttribute("depth", "+0.18em"); - return wrapper - } -}); - -/** - * All registered environments. - * `environments.js` exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary via `environments.js`. - */ -const _environments = {}; - -function defineEnvironment({ type, names, props, handler, mathmlBuilder }) { - // Set default values of environments. - const data = { - type, - numArgs: props.numArgs || 0, - allowedInText: false, - numOptionalArgs: 0, - handler - }; - for (let i = 0; i < names.length; ++i) { - _environments[names[i]] = data; - } - if (mathmlBuilder) { - _mathmlGroupBuilders[type] = mathmlBuilder; - } -} - -/** - * Asserts that the node is of the given type and returns it with stricter - * typing. Throws if the node's type does not match. - */ -function assertNodeType(node, type) { - if (!node || node.type !== type) { - throw new Error( - `Expected node of type ${type}, but got ` + - (node ? `node of type ${node.type}` : String(node)) - ); - } - return node; -} - -/** - * Returns the node more strictly typed iff it is of the given type. Otherwise, - * returns null. - */ -function assertSymbolNodeType(node) { - const typedNode = checkSymbolNodeType(node); - if (!typedNode) { - throw new Error( - `Expected node of symbol group type, but got ` + - (node ? `node of type ${node.type}` : String(node)) - ); - } - return typedNode; -} - -/** - * Returns the node more strictly typed iff it is of the given type. Otherwise, - * returns null. - */ -function checkSymbolNodeType(node) { - if (node && (node.type === "atom" || node.type === "delimiter" || - Object.prototype.hasOwnProperty.call(NON_ATOMS, node.type))) { - return node; - } - return null; -} - -const cdArrowFunctionName = { - ">": "\\\\cdrightarrow", - "<": "\\\\cdleftarrow", - "=": "\\\\cdlongequal", - A: "\\uparrow", - V: "\\downarrow", - "|": "\\Vert", - ".": "no arrow" -}; - -const newCell = () => { - // Create an empty cell, to be filled below with parse nodes. - return { type: "styling", body: [], mode: "math", scriptLevel: "display" }; -}; - -const isStartOfArrow = (node) => { - return node.type === "textord" && node.text === "@"; -}; - -const isLabelEnd = (node, endChar) => { - return (node.type === "mathord" || node.type === "atom") && node.text === endChar; -}; - -function cdArrow(arrowChar, labels, parser) { - // Return a parse tree of an arrow and its labels. - // This acts in a way similar to a macro expansion. - const funcName = cdArrowFunctionName[arrowChar]; - switch (funcName) { - case "\\\\cdrightarrow": - case "\\\\cdleftarrow": - return parser.callFunction(funcName, [labels[0]], [labels[1]]); - case "\\uparrow": - case "\\downarrow": { - const leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []); - const bareArrow = { - type: "atom", - text: funcName, - mode: "math", - family: "rel" - }; - const sizedArrow = parser.callFunction("\\Big", [bareArrow], []); - const rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []); - const arrowGroup = { - type: "ordgroup", - mode: "math", - body: [leftLabel, sizedArrow, rightLabel], - semisimple: true - }; - return parser.callFunction("\\\\cdparent", [arrowGroup], []); - } - case "\\\\cdlongequal": - return parser.callFunction("\\\\cdlongequal", [], []); - case "\\Vert": { - const arrow = { type: "textord", text: "\\Vert", mode: "math" }; - return parser.callFunction("\\Big", [arrow], []); - } - default: - return { type: "textord", text: " ", mode: "math" }; - } -} - -function parseCD(parser) { - // Get the array's parse nodes with \\ temporarily mapped to \cr. - const parsedRows = []; - parser.gullet.beginGroup(); - parser.gullet.macros.set("\\cr", "\\\\\\relax"); - parser.gullet.beginGroup(); - while (true) { - // Get the parse nodes for the next row. - parsedRows.push(parser.parseExpression(false, "\\\\")); - parser.gullet.endGroup(); - parser.gullet.beginGroup(); - const next = parser.fetch().text; - if (next === "&" || next === "\\\\") { - parser.consume(); - } else if (next === "\\end") { - if (parsedRows[parsedRows.length - 1].length === 0) { - parsedRows.pop(); // final row ended in \\ - } - break; - } else { - throw new ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken); - } - } - - let row = []; - const body = [row]; - - // Loop thru the parse nodes. Collect them into cells and arrows. - for (let i = 0; i < parsedRows.length; i++) { - // Start a new row. - const rowNodes = parsedRows[i]; - // Create the first cell. - let cell = newCell(); - - for (let j = 0; j < rowNodes.length; j++) { - if (!isStartOfArrow(rowNodes[j])) { - // If a parseNode is not an arrow, it goes into a cell. - cell.body.push(rowNodes[j]); - } else { - // Parse node j is an "@", the start of an arrow. - // Before starting on the arrow, push the cell into `row`. - row.push(cell); - - // Now collect parseNodes into an arrow. - // The character after "@" defines the arrow type. - j += 1; - const arrowChar = assertSymbolNodeType(rowNodes[j]).text; - - // Create two empty label nodes. We may or may not use them. - const labels = new Array(2); - labels[0] = { type: "ordgroup", mode: "math", body: [] }; - labels[1] = { type: "ordgroup", mode: "math", body: [] }; - - // Process the arrow. - if ("=|.".indexOf(arrowChar) > -1) ; else if ("<>AV".indexOf(arrowChar) > -1) { - // Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take - // two optional labels. E.g. the right-point arrow syntax is - // really: @>{optional label}>{optional label}> - // Collect parseNodes into labels. - for (let labelNum = 0; labelNum < 2; labelNum++) { - let inLabel = true; - for (let k = j + 1; k < rowNodes.length; k++) { - if (isLabelEnd(rowNodes[k], arrowChar)) { - inLabel = false; - j = k; - break; - } - if (isStartOfArrow(rowNodes[k])) { - throw new ParseError( - "Missing a " + arrowChar + " character to complete a CD arrow.", - rowNodes[k] - ); - } - - labels[labelNum].body.push(rowNodes[k]); - } - if (inLabel) { - // isLabelEnd never returned a true. - throw new ParseError( - "Missing a " + arrowChar + " character to complete a CD arrow.", - rowNodes[j] - ); - } - } - } else { - throw new ParseError(`Expected one of "<>AV=|." after @.`); - } - - // Now join the arrow to its labels. - const arrow = cdArrow(arrowChar, labels, parser); - - // Wrap the arrow in a styling node - row.push(arrow); - // In CD's syntax, cells are implicit. That is, everything that - // is not an arrow gets collected into a cell. So create an empty - // cell now. It will collect upcoming parseNodes. - cell = newCell(); - } - } - if (i % 2 === 0) { - // Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell - // The last cell is not yet pushed into `row`, so: - row.push(cell); - } else { - // Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow - // Remove the empty cell that was placed at the beginning of `row`. - row.shift(); - } - row = []; - body.push(row); - } - body.pop(); - - // End row group - parser.gullet.endGroup(); - // End array group defining \\ - parser.gullet.endGroup(); - - return { - type: "array", - mode: "math", - body, - tags: null, - labels: new Array(body.length + 1).fill(""), - envClasses: ["jot", "cd"], - cols: [], - hLinesBeforeRow: new Array(body.length + 1).fill([]) - }; -} - -// The functions below are not available for general use. -// They are here only for internal use by the {CD} environment in placing labels -// next to vertical arrows. - -// We don't need any such functions for horizontal arrows because we can reuse -// the functionality that already exists for extensible arrows. - -defineFunction({ - type: "cdlabel", - names: ["\\\\cdleft", "\\\\cdright"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - return { - type: "cdlabel", - mode: parser.mode, - side: funcName.slice(4), - label: args[0] - }; - }, - mathmlBuilder(group, style) { - if (group.label.body.length === 0) { - return new MathNode("mrow", style) // empty label - } - // Abuse an to create vertically centered content. - const mrow = buildGroup$1(group.label, style); - if (group.side === "left") { - mrow.classes.push("tml-shift-left"); - } - const mtd = new MathNode("mtd", [mrow]); - mtd.style.padding = "0"; - const mtr = new MathNode("mtr", [mtd]); - const mtable = new MathNode("mtable", [mtr]); - const label = new MathNode("mpadded", [mtable]); - // Set the label width to zero so that the arrow will be centered under the corner cell. - label.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - label.setAttribute("displaystyle", "false"); - label.setAttribute("scriptlevel", "1"); - return label; - } -}); - -defineFunction({ - type: "cdlabelparent", - names: ["\\\\cdparent"], - props: { - numArgs: 1 - }, - handler({ parser }, args) { - return { - type: "cdlabelparent", - mode: parser.mode, - fragment: args[0] - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow", [buildGroup$1(group.fragment, style)]); - } -}); - -const ordGroup = (body) => { - return { - "type": "ordgroup", - "mode": "math", - "body": body, - "semisimple": true - } -}; - -const phantom = (body, type) => { - return { - "type": type, - "mode": "math", - "body": ordGroup(body) - } -}; - -/* - * A helper for \bordermatrix. - * parseArray() has parsed the tokens as if the environment - * was \begin{matrix}. That parse tree is this function’s input. - * Here, we rearrange the parse tree to get one that will - * result in TeX \bordermatrix. - * The final result includes a {pmatrix}, which is the bottom - * half of a element. The top of the contains - * the \bordermatrix headings. The top section also contains the - * contents of the bottom {pmatrix}. Those elements are hidden via - * \hphantom, but they ensure that column widths are the same top and - * bottom. - * - * We also create a left {matrix} with a single column that contains - * elements shifted out of the matrix. The left {matrix} also - * contains \vphantom copies of the other {pmatrix} elements. - * As before, this ensures consistent row heights of left and main. - */ - -const bordermatrixParseTree = (matrix, delimiters) => { - const body = matrix.body; - body[0].shift(); // dispose of top left cell - - // Create an array for the left column - const leftColumnBody = new Array(body.length - 1).fill().map(() => []); - for (let i = 1; i < body.length; i++) { - // The visible part of the cell - leftColumnBody[i - 1].push(body[i].shift()); - // A vphantom with contents from the pmatrix, to set minimum cell height - const phantomBody = []; - for (let j = 0; j < body[i].length; j++) { - phantomBody.push(body[i][j]); - } - leftColumnBody[i - 1].push(phantom(phantomBody, "vphantom")); - } - - // Create an array for the top row - const topRowBody = new Array(body.length).fill().map(() => []); - for (let j = 0; j < body[0].length; j++) { - topRowBody[0].push(body[0][j]); - } - // Copy the rest of the pmatrix, but squashed via \hphantom - for (let i = 1; i < body.length; i++) { - for (let j = 0; j < body[0].length; j++) { - topRowBody[i].push(phantom(body[i][j].body, "hphantom")); - } - } - - // Squash the top row of the main {pmatrix} - for (let j = 0; j < body[0].length; j++) { - body[0][j] = phantom(body[0][j].body, "hphantom"); - } - - // Now wrap the arrays in the proper parse nodes. - - const leftColumn = { - type: "array", - mode: "math", - body: leftColumnBody, - cols: [{ type: "align", align: "c" }], - rowGaps: new Array(leftColumnBody.length - 1).fill(null), - hLinesBeforeRow: new Array(leftColumnBody.length + 1).fill().map(() => []), - envClasses: [], - scriptLevel: "text", - arraystretch: 1, - labels: new Array(leftColumnBody.length).fill(""), - arraycolsep: { "number": 0.04, unit: "em" } - }; - - const topRow = { - type: "array", - mode: "math", - body: topRowBody, - cols: new Array(topRowBody.length).fill({ type: "align", align: "c" }), - rowGaps: new Array(topRowBody.length - 1).fill(null), - hLinesBeforeRow: new Array(topRowBody.length + 1).fill().map(() => []), - envClasses: [], - scriptLevel: "text", - arraystretch: 1, - labels: new Array(topRowBody.length).fill(""), - arraycolsep: null - }; - - const topWrapper = { - type: "styling", - mode: "math", - scriptLevel: "text", // Must set this explicitly. - body: [topRow] // Default level is "script". - }; - - const container = { - type: "leftright", - mode: "math", - body: [matrix], - left: delimiters ? delimiters[0] : "(", - right: delimiters ? delimiters[1] : ")", - rightColor: undefined - }; - - const base = { - type: "op", // The base of a TeX \overset - mode: "math", - limits: true, - alwaysHandleSupSub: true, - parentIsSupSub: true, - symbol: false, - suppressBaseShift: true, - body: [container] - }; - - const mover = { - type: "supsub", // We're using the MathML equivalent - mode: "math", // of TeX \overset. - stack: true, - base: base, // That keeps the {pmatrix} aligned with - sup: topWrapper, // the math centerline. - sub: null - }; - - return ordGroup([leftColumn, mover]) -}; - -/** - * Lexing or parsing positional information for error reporting. - * This object is immutable. - */ -class SourceLocation { - constructor(lexer, start, end) { - this.lexer = lexer; // Lexer holding the input string. - this.start = start; // Start offset, zero-based inclusive. - this.end = end; // End offset, zero-based exclusive. - } - - /** - * Merges two `SourceLocation`s from location providers, given they are - * provided in order of appearance. - * - Returns the first one's location if only the first is provided. - * - Returns a merged range of the first and the last if both are provided - * and their lexers match. - * - Otherwise, returns null. - */ - static range(first, second) { - if (!second) { - return first && first.loc; - } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) { - return null; - } else { - return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end); - } - } -} - -/** - * Interface required to break circular dependency between Token, Lexer, and - * ParseError. - */ - -/** - * The resulting token returned from `lex`. - * - * It consists of the token text plus some position information. - * The position information is essentially a range in an input string, - * but instead of referencing the bare input string, we refer to the lexer. - * That way it is possible to attach extra metadata to the input string, - * like for example a file name or similar. - * - * The position information is optional, so it is OK to construct synthetic - * tokens if appropriate. Not providing available position information may - * lead to degraded error reporting, though. - */ -class Token { - constructor( - text, // the text of this token - loc - ) { - this.text = text; - this.loc = loc; - } - - /** - * Given a pair of tokens (this and endToken), compute a `Token` encompassing - * the whole input range enclosed by these two. - */ - range( - endToken, // last token of the range, inclusive - text // the text of the newly constructed token - ) { - return new Token(text, SourceLocation.range(this, endToken)); - } -} - -// In TeX, there are actually three sets of dimensions, one for each of -// textstyle, scriptstyle, and scriptscriptstyle. These are -// provided in the the arrays below, in that order. -// - -// Math style is not quite the same thing as script level. -const StyleLevel = { - DISPLAY: 0, - TEXT: 1, - SCRIPT: 2, - SCRIPTSCRIPT: 3 -}; - -/** - * All registered global/built-in macros. - * `macros.js` exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary via `macros.js`. - */ -const _macros = {}; - -// This function might one day accept an additional argument and do more things. -function defineMacro(name, body) { - _macros[name] = body; -} - -/** - * Predefined macros for Temml. - * This can be used to define some commands in terms of others. - */ - -const macros = _macros; - -////////////////////////////////////////////////////////////////////// -// macro tools - -defineMacro("\\noexpand", function(context) { - // The expansion is the token itself; but that token is interpreted - // as if its meaning were ‘\relax’ if it is a control sequence that - // would ordinarily be expanded by TeX’s expansion rules. - const t = context.popToken(); - if (context.isExpandable(t.text)) { - t.noexpand = true; - t.treatAsRelax = true; - } - return { tokens: [t], numArgs: 0 }; -}); - -defineMacro("\\expandafter", function(context) { - // TeX first reads the token that comes immediately after \expandafter, - // without expanding it; let’s call this token t. Then TeX reads the - // token that comes after t (and possibly more tokens, if that token - // has an argument), replacing it by its expansion. Finally TeX puts - // t back in front of that expansion. - const t = context.popToken(); - context.expandOnce(true); // expand only an expandable token - return { tokens: [t], numArgs: 0 }; -}); - -// LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2 -// TeX source: \long\def\@firstoftwo#1#2{#1} -defineMacro("\\@firstoftwo", function(context) { - const args = context.consumeArgs(2); - return { tokens: args[0], numArgs: 0 }; -}); - -// LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1 -// TeX source: \long\def\@secondoftwo#1#2{#2} -defineMacro("\\@secondoftwo", function(context) { - const args = context.consumeArgs(2); - return { tokens: args[1], numArgs: 0 }; -}); - -// LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded) -// symbol that isn't a space, consuming any spaces but not consuming the -// first nonspace character. If that nonspace character matches #1, then -// the macro expands to #2; otherwise, it expands to #3. -defineMacro("\\@ifnextchar", function(context) { - const args = context.consumeArgs(3); // symbol, if, else - context.consumeSpaces(); - const nextToken = context.future(); - if (args[0].length === 1 && args[0][0].text === nextToken.text) { - return { tokens: args[1], numArgs: 0 }; - } else { - return { tokens: args[2], numArgs: 0 }; - } -}); - -// LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol. -// If it is `*`, then it consumes the symbol, and the macro expands to #1; -// otherwise, the macro expands to #2 (without consuming the symbol). -// TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}} -defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}"); - -// LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode -defineMacro("\\TextOrMath", function(context) { - const args = context.consumeArgs(2); - if (context.mode === "text") { - return { tokens: args[0], numArgs: 0 }; - } else { - return { tokens: args[1], numArgs: 0 }; - } -}); - -const stringFromArg = arg => { - // Reverse the order of the arg and return a string. - let str = ""; - for (let i = arg.length - 1; i > -1; i--) { - str += arg[i].text; - } - return str -}; - -// Lookup table for parsing numbers in base 8 through 16 -const digitToNumber = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - a: 10, - A: 10, - b: 11, - B: 11, - c: 12, - C: 12, - d: 13, - D: 13, - e: 14, - E: 14, - f: 15, - F: 15 -}; - -const nextCharNumber = context => { - const numStr = context.future().text; - if (numStr === "EOF") { return [null, ""] } - return [digitToNumber[numStr.charAt(0)], numStr] -}; - -const appendCharNumbers = (number, numStr, base) => { - for (let i = 1; i < numStr.length; i++) { - const digit = digitToNumber[numStr.charAt(i)]; - number *= base; - number += digit; - } - return number -}; - -// TeX \char makes a literal character (catcode 12) using the following forms: -// (see The TeXBook, p. 43) -// \char123 -- decimal -// \char'123 -- octal -// \char"123 -- hex -// \char`x -- character that can be written (i.e. isn't active) -// \char`\x -- character that cannot be written (e.g. %) -// These all refer to characters from the font, so we turn them into special -// calls to a function \@char dealt with in the Parser. -defineMacro("\\char", function(context) { - let token = context.popToken(); - let base; - let number = ""; - if (token.text === "'") { - base = 8; - token = context.popToken(); - } else if (token.text === '"') { - base = 16; - token = context.popToken(); - } else if (token.text === "`") { - token = context.popToken(); - if (token.text[0] === "\\") { - number = token.text.charCodeAt(1); - } else if (token.text === "EOF") { - throw new ParseError("\\char` missing argument"); - } else { - number = token.text.charCodeAt(0); - } - } else { - base = 10; - } - if (base) { - // Parse a number in the given base, starting with first `token`. - let numStr = token.text; - number = digitToNumber[numStr.charAt(0)]; - if (number == null || number >= base) { - throw new ParseError(`Invalid base-${base} digit ${token.text}`); - } - number = appendCharNumbers(number, numStr, base); - let digit; - [digit, numStr] = nextCharNumber(context); - while (digit != null && digit < base) { - number *= base; - number += digit; - number = appendCharNumbers(number, numStr, base); - context.popToken(); - [digit, numStr] = nextCharNumber(context); - } - } - return `\\@char{${number}}`; -}); - -function recreateArgStr(context) { - // Recreate the macro's original argument string from the array of parse tokens. - const tokens = context.consumeArgs(1)[0]; - let str = ""; - let expectedLoc = tokens[tokens.length - 1].loc.start; - for (let i = tokens.length - 1; i >= 0; i--) { - const actualLoc = tokens[i].loc.start; - if (actualLoc > expectedLoc) { - // context.consumeArgs has eaten a space. - str += " "; - expectedLoc = actualLoc; - } - str += tokens[i].text; - expectedLoc += tokens[i].text.length; - } - return str -} - -// The Latin Modern font renders at the wrong vertical alignment. -// This macro provides a better rendering. -defineMacro("\\surd", '\\sqrt{\\vphantom{|}}'); - -// See comment for \oplus in symbols.js. -defineMacro("\u2295", "\\oplus"); - -// Since Temml has no \par, ignore \long. -defineMacro("\\long", ""); - -////////////////////////////////////////////////////////////////////// -// Grouping -// \let\bgroup={ \let\egroup=} -defineMacro("\\bgroup", "{"); -defineMacro("\\egroup", "}"); - -// Symbols from latex.ltx: -// \def~{\nobreakspace{}} -// \def\lq{`} -// \def\rq{'} -// \def \aa {\r a} -defineMacro("~", "\\nobreakspace"); -defineMacro("\\lq", "`"); -defineMacro("\\rq", "'"); -defineMacro("\\aa", "\\r a"); - -defineMacro("\\Bbbk", "\\Bbb{k}"); - -// \mathstrut from the TeXbook, p 360 -defineMacro("\\mathstrut", "\\vphantom{(}"); - -// \underbar from TeXbook p 353 -defineMacro("\\underbar", "\\underline{\\text{#1}}"); - -////////////////////////////////////////////////////////////////////// -// LaTeX_2ε - -// \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@ -// \kern6\p@\hbox{.}\hbox{.}\hbox{.}}} -// We'll call \varvdots, which gets a glyph from symbols.js. -// The zero-width rule gets us an equivalent to the vertical 6pt kern. -defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}"); -defineMacro("\u22ee", "\\vdots"); - -// {array} environment gaps -defineMacro("\\arraystretch", "1"); // line spacing factor times 12pt -defineMacro("\\arraycolsep", "6pt"); // half the width separating columns - -////////////////////////////////////////////////////////////////////// -// amsmath.sty -// http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf - -//\newcommand{\substack}[1]{\subarray{c}#1\endsubarray} -defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"); - -// \def\iff{\DOTSB\;\Longleftrightarrow\;} -// \def\implies{\DOTSB\;\Longrightarrow\;} -// \def\impliedby{\DOTSB\;\Longleftarrow\;} -defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"); -defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"); -defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"); - -// AMSMath's automatic \dots, based on \mdots@@ macro. -const dotsByToken = { - ",": "\\dotsc", - "\\not": "\\dotsb", - // \keybin@ checks for the following: - "+": "\\dotsb", - "=": "\\dotsb", - "<": "\\dotsb", - ">": "\\dotsb", - "-": "\\dotsb", - "*": "\\dotsb", - ":": "\\dotsb", - // Symbols whose definition starts with \DOTSB: - "\\DOTSB": "\\dotsb", - "\\coprod": "\\dotsb", - "\\bigvee": "\\dotsb", - "\\bigwedge": "\\dotsb", - "\\biguplus": "\\dotsb", - "\\bigcap": "\\dotsb", - "\\bigcup": "\\dotsb", - "\\prod": "\\dotsb", - "\\sum": "\\dotsb", - "\\bigotimes": "\\dotsb", - "\\bigoplus": "\\dotsb", - "\\bigodot": "\\dotsb", - "\\bigsqcap": "\\dotsb", - "\\bigsqcup": "\\dotsb", - "\\bigtimes": "\\dotsb", - "\\And": "\\dotsb", - "\\longrightarrow": "\\dotsb", - "\\Longrightarrow": "\\dotsb", - "\\longleftarrow": "\\dotsb", - "\\Longleftarrow": "\\dotsb", - "\\longleftrightarrow": "\\dotsb", - "\\Longleftrightarrow": "\\dotsb", - "\\mapsto": "\\dotsb", - "\\longmapsto": "\\dotsb", - "\\hookrightarrow": "\\dotsb", - "\\doteq": "\\dotsb", - // Symbols whose definition starts with \mathbin: - "\\mathbin": "\\dotsb", - // Symbols whose definition starts with \mathrel: - "\\mathrel": "\\dotsb", - "\\relbar": "\\dotsb", - "\\Relbar": "\\dotsb", - "\\xrightarrow": "\\dotsb", - "\\xleftarrow": "\\dotsb", - // Symbols whose definition starts with \DOTSI: - "\\DOTSI": "\\dotsi", - "\\int": "\\dotsi", - "\\oint": "\\dotsi", - "\\iint": "\\dotsi", - "\\iiint": "\\dotsi", - "\\iiiint": "\\dotsi", - // Symbols whose definition starts with \DOTSX: - "\\DOTSX": "\\dotsx" -}; - -defineMacro("\\dots", function(context) { - // TODO: If used in text mode, should expand to \textellipsis. - // However, in Temml, \textellipsis and \ldots behave the same - // (in text mode), and it's unlikely we'd see any of the math commands - // that affect the behavior of \dots when in text mode. So fine for now - // (until we support \ifmmode ... \else ... \fi). - let thedots = "\\dotso"; - const next = context.expandAfterFuture().text; - if (next in dotsByToken) { - thedots = dotsByToken[next]; - } else if (next.slice(0, 4) === "\\not") { - thedots = "\\dotsb"; - } else if (next in symbols.math) { - if (["bin", "rel"].includes(symbols.math[next].group)) { - thedots = "\\dotsb"; - } - } - return thedots; -}); - -const spaceAfterDots = { - // \rightdelim@ checks for the following: - ")": true, - "]": true, - "\\rbrack": true, - "\\}": true, - "\\rbrace": true, - "\\rangle": true, - "\\rceil": true, - "\\rfloor": true, - "\\rgroup": true, - "\\rmoustache": true, - "\\right": true, - "\\bigr": true, - "\\biggr": true, - "\\Bigr": true, - "\\Biggr": true, - // \extra@ also tests for the following: - $: true, - // \extrap@ checks for the following: - ";": true, - ".": true, - ",": true -}; - -defineMacro("\\dotso", function(context) { - const next = context.future().text; - if (next in spaceAfterDots) { - return "\\ldots\\,"; - } else { - return "\\ldots"; - } -}); - -defineMacro("\\dotsc", function(context) { - const next = context.future().text; - // \dotsc uses \extra@ but not \extrap@, instead specially checking for - // ';' and '.', but doesn't check for ','. - if (next in spaceAfterDots && next !== ",") { - return "\\ldots\\,"; - } else { - return "\\ldots"; - } -}); - -defineMacro("\\cdots", function(context) { - const next = context.future().text; - if (next in spaceAfterDots) { - return "\\@cdots\\,"; - } else { - return "\\@cdots"; - } -}); - -defineMacro("\\dotsb", "\\cdots"); -defineMacro("\\dotsm", "\\cdots"); -defineMacro("\\dotsi", "\\!\\cdots"); -defineMacro("\\idotsint", "\\int\\!\\cdots\\!\\int"); -// amsmath doesn't actually define \dotsx, but \dots followed by a macro -// starting with \DOTSX implies \dotso, and then \extra@ detects this case -// and forces the added `\,`. -defineMacro("\\dotsx", "\\ldots\\,"); - -// \let\DOTSI\relax -// \let\DOTSB\relax -// \let\DOTSX\relax -defineMacro("\\DOTSI", "\\relax"); -defineMacro("\\DOTSB", "\\relax"); -defineMacro("\\DOTSX", "\\relax"); - -// Spacing, based on amsmath.sty's override of LaTeX defaults -// \DeclareRobustCommand{\tmspace}[3]{% -// \ifmmode\mskip#1#2\else\kern#1#3\fi\relax} -defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"); -// \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}} -// TODO: math mode should use \thinmuskip -defineMacro("\\,", "{\\tmspace+{3mu}{.1667em}}"); -// \let\thinspace\, -defineMacro("\\thinspace", "\\,"); -// \def\>{\mskip\medmuskip} -// \renewcommand{\:}{\tmspace+\medmuskip{.2222em}} -// TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu -defineMacro("\\>", "\\mskip{4mu}"); -defineMacro("\\:", "{\\tmspace+{4mu}{.2222em}}"); -// \let\medspace\: -defineMacro("\\medspace", "\\:"); -// \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}} -// TODO: math mode should use \thickmuskip = 5mu plus 5mu -defineMacro("\\;", "{\\tmspace+{5mu}{.2777em}}"); -// \let\thickspace\; -defineMacro("\\thickspace", "\\;"); -// \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}} -// TODO: math mode should use \thinmuskip -defineMacro("\\!", "{\\tmspace-{3mu}{.1667em}}"); -// \let\negthinspace\! -defineMacro("\\negthinspace", "\\!"); -// \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}} -// TODO: math mode should use \medmuskip -defineMacro("\\negmedspace", "{\\tmspace-{4mu}{.2222em}}"); -// \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}} -// TODO: math mode should use \thickmuskip -defineMacro("\\negthickspace", "{\\tmspace-{5mu}{.277em}}"); -// \def\enspace{\kern.5em } -defineMacro("\\enspace", "\\kern.5em "); -// \def\enskip{\hskip.5em\relax} -defineMacro("\\enskip", "\\hskip.5em\\relax"); -// \def\quad{\hskip1em\relax} -defineMacro("\\quad", "\\hskip1em\\relax"); -// \def\qquad{\hskip2em\relax} -defineMacro("\\qquad", "\\hskip2em\\relax"); - -defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax"); - -// \tag@in@display form of \tag -defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren"); -defineMacro("\\tag@paren", "\\tag@literal{({#1})}"); -defineMacro("\\tag@literal", (context) => { - if (context.macros.get("\\df@tag")) { - throw new ParseError("Multiple \\tag"); - } - return "\\gdef\\df@tag{\\text{#1}}"; -}); -defineMacro("\\notag", "\\nonumber"); -defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}"); - -// \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin -// {\operator@font mod}\penalty900 -// \mkern5mu\nonscript\mskip-\medmuskip} -// \newcommand{\pod}[1]{\allowbreak -// \if@display\mkern18mu\else\mkern8mu\fi(#1)} -// \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}} -// \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu -// \else\mkern12mu\fi{\operator@font mod}\,\,#1} -// TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu -defineMacro("\\bmod", "\\mathbin{\\text{mod}}"); -defineMacro( - "\\pod", - "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)" -); -defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"); -defineMacro( - "\\mod", - "\\allowbreak" + - "\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" + - "{\\rm mod}\\,\\,#1" -); - -////////////////////////////////////////////////////////////////////// -// LaTeX source2e - -// \expandafter\let\expandafter\@normalcr -// \csname\expandafter\@gobble\string\\ \endcsname -// \DeclareRobustCommand\newline{\@normalcr\relax} -defineMacro("\\newline", "\\\\\\relax"); - -// \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@} -// TODO: Doesn't normally work in math mode because \@ fails. -defineMacro("\\TeX", "\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}"); - -defineMacro( - "\\LaTeX", - "\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX" -); - -defineMacro( - "\\Temml", - // eslint-disable-next-line max-len - "\\textrm{T}\\kern-0.2em\\lower{0.2em}{\\textrm{E}}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}" -); - -// \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace} -// \def\@hspace#1{\hskip #1\relax} -// \def\@hspacer#1{\vrule \@width\z@\nobreak -// \hskip #1\hskip \z@skip} -defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace"); -defineMacro("\\@hspace", "\\hskip #1\\relax"); -defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"); - -defineMacro("\\colon", `\\mathpunct{\\char"3a}`); - -////////////////////////////////////////////////////////////////////// -// mathtools.sty - -defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}"); - -//\providecommand\ordinarycolon{:} -defineMacro("\\ordinarycolon", `\\char"3a`); -// Raise to center on the math axis, as closely as possible. -defineMacro("\\vcentcolon", "\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}"); -// \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}} -defineMacro("\\coloneq", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}'); -// \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}} -defineMacro("\\Coloneq", '\\mathrel{\\char"2237\\char"2212}'); -// \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon} -defineMacro("\\Eqqcolon", '\\mathrel{\\char"3d\\char"2237}'); -// \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon} -defineMacro("\\Eqcolon", '\\mathrel{\\char"2212\\char"2237}'); -// \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx} -defineMacro("\\colonapprox", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}'); -// \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx} -defineMacro("\\Colonapprox", '\\mathrel{\\char"2237\\char"2248}'); -// \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim} -defineMacro("\\colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); -// \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim} -defineMacro("\\Colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); - -////////////////////////////////////////////////////////////////////// -// colonequals.sty - -// Alternate names for mathtools's macros: -defineMacro("\\ratio", "\\vcentcolon"); -defineMacro("\\coloncolon", "\\dblcolon"); -defineMacro("\\colonequals", "\\coloneqq"); -defineMacro("\\coloncolonequals", "\\Coloneqq"); -defineMacro("\\equalscolon", "\\eqqcolon"); -defineMacro("\\equalscoloncolon", "\\Eqqcolon"); -defineMacro("\\colonminus", "\\coloneq"); -defineMacro("\\coloncolonminus", "\\Coloneq"); -defineMacro("\\minuscolon", "\\eqcolon"); -defineMacro("\\minuscoloncolon", "\\Eqcolon"); -// \colonapprox name is same in mathtools and colonequals. -defineMacro("\\coloncolonapprox", "\\Colonapprox"); -// \colonsim name is same in mathtools and colonequals. -defineMacro("\\coloncolonsim", "\\Colonsim"); - -// Present in newtxmath, pxfonts and txfonts -defineMacro("\\notni", "\\mathrel{\\char`\u220C}"); -defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"); -defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"); - -////////////////////////////////////////////////////////////////////// -// From amsopn.sty -defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"); -defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"); -defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\text{lim}}}"); -defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\text{lim}}}"); -defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}"); -defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}"); - -defineMacro("\\centerdot", "{\\medspace\\rule{0.167em}{0.189em}\\medspace}"); - -////////////////////////////////////////////////////////////////////// -// statmath.sty -// https://ctan.math.illinois.edu/macros/latex/contrib/statmath/statmath.pdf - -defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"); -defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"); -defineMacro("\\plim", "\\DOTSB\\operatorname*{plim}"); - -////////////////////////////////////////////////////////////////////// -// MnSymbol.sty - -defineMacro("\\leftmodels", "\\mathop{\\reflectbox{$\\models$}}"); - -////////////////////////////////////////////////////////////////////// -// braket.sty -// http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf - -defineMacro("\\bra", "\\mathinner{\\langle{#1}|}"); -defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}"); -defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}"); -defineMacro("\\Bra", "\\left\\langle#1\\right|"); -defineMacro("\\Ket", "\\left|#1\\right\\rangle"); -// A helper for \Braket and \Set -const replaceVert = (argStr, match) => { - const ch = match[0] === "|" ? "\\vert" : "\\Vert"; - const replaceStr = `}\\,\\middle${ch}\\,{`; - return argStr.slice(0, match.index) + replaceStr + argStr.slice(match.index + match[0].length) -}; -defineMacro("\\Braket", function(context) { - let argStr = recreateArgStr(context); - const regEx = /\|\||\||\\\|/g; - let match; - while ((match = regEx.exec(argStr)) !== null) { - argStr = replaceVert(argStr, match); - } - return "\\left\\langle{" + argStr + "}\\right\\rangle" -}); -defineMacro("\\Set", function(context) { - let argStr = recreateArgStr(context); - const match = /\|\||\||\\\|/.exec(argStr); - if (match) { - argStr = replaceVert(argStr, match); - } - return "\\left\\{\\:{" + argStr + "}\\:\\right\\}" -}); -defineMacro("\\set", function(context) { - const argStr = recreateArgStr(context); - return "\\{{" + argStr.replace(/\|/, "}\\mid{") + "}\\}" -}); - -////////////////////////////////////////////////////////////////////// -// actuarialangle.dtx -defineMacro("\\angln", "{\\angl n}"); - -////////////////////////////////////////////////////////////////////// -// derivative.sty -defineMacro("\\odv", "\\@ifstar\\odv@next\\odv@numerator"); -defineMacro("\\odv@numerator", "\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}"); -defineMacro("\\odv@next", "\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1"); -defineMacro("\\pdv", "\\@ifstar\\pdv@next\\pdv@numerator"); - -const pdvHelper = args => { - const numerator = args[0][0].text; - const denoms = stringFromArg(args[1]).split(","); - const power = String(denoms.length); - const numOp = power === "1" ? "\\partial" : `\\partial^${power}`; - let denominator = ""; - denoms.map(e => { denominator += "\\partial " + e.trim() + "\\,";}); - return [numerator, numOp, denominator.replace(/\\,$/, "")] -}; -defineMacro("\\pdv@numerator", function(context) { - const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); - return `\\frac{${numOp} ${numerator}}{${denominator}}` -}); -defineMacro("\\pdv@next", function(context) { - const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); - return `\\frac{${numOp}}{${denominator}} ${numerator}` -}); - -////////////////////////////////////////////////////////////////////// -// upgreek.dtx -defineMacro("\\upalpha", "\\up@greek{\\alpha}"); -defineMacro("\\upbeta", "\\up@greek{\\beta}"); -defineMacro("\\upgamma", "\\up@greek{\\gamma}"); -defineMacro("\\updelta", "\\up@greek{\\delta}"); -defineMacro("\\upepsilon", "\\up@greek{\\epsilon}"); -defineMacro("\\upzeta", "\\up@greek{\\zeta}"); -defineMacro("\\upeta", "\\up@greek{\\eta}"); -defineMacro("\\uptheta", "\\up@greek{\\theta}"); -defineMacro("\\upiota", "\\up@greek{\\iota}"); -defineMacro("\\upkappa", "\\up@greek{\\kappa}"); -defineMacro("\\uplambda", "\\up@greek{\\lambda}"); -defineMacro("\\upmu", "\\up@greek{\\mu}"); -defineMacro("\\upnu", "\\up@greek{\\nu}"); -defineMacro("\\upxi", "\\up@greek{\\xi}"); -defineMacro("\\upomicron", "\\up@greek{\\omicron}"); -defineMacro("\\uppi", "\\up@greek{\\pi}"); -defineMacro("\\upalpha", "\\up@greek{\\alpha}"); -defineMacro("\\uprho", "\\up@greek{\\rho}"); -defineMacro("\\upsigma", "\\up@greek{\\sigma}"); -defineMacro("\\uptau", "\\up@greek{\\tau}"); -defineMacro("\\upupsilon", "\\up@greek{\\upsilon}"); -defineMacro("\\upphi", "\\up@greek{\\phi}"); -defineMacro("\\upchi", "\\up@greek{\\chi}"); -defineMacro("\\uppsi", "\\up@greek{\\psi}"); -defineMacro("\\upomega", "\\up@greek{\\omega}"); - -////////////////////////////////////////////////////////////////////// -// cmll package -defineMacro("\\invamp", '\\mathbin{\\char"214b}'); -defineMacro("\\parr", '\\mathbin{\\char"214b}'); -defineMacro("\\upand", '\\mathbin{\\char"214b}'); // STIX package -defineMacro("\\with", '\\mathbin{\\char"26}'); -defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}'); -defineMacro("\\multimapboth", '\\mathrel{\\char"29df}'); -defineMacro("\\scoh", '{\\mkern5mu\\char"2322\\mkern5mu}'); -defineMacro("\\sincoh", '{\\mkern5mu\\char"2323\\mkern5mu}'); -defineMacro("\\coh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}} -{\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}`); -defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}} -{\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}`); - - -////////////////////////////////////////////////////////////////////// -// chemstyle package -defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}"); - -/* eslint-disable */ -/* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ - -/************************************************************* - * - * Temml mhchem.js - * - * This file implements a Temml version of mhchem version 3.3.0. - * It is adapted from MathJax/extensions/TeX/mhchem.js - * It differs from the MathJax version as follows: - * 1. The interface is changed so that it can be called from Temml, not MathJax. - * 2. \rlap and \llap are replaced with \mathrlap and \mathllap. - * 3. The reaction arrow code is simplified. All reaction arrows are rendered - * using Temml extensible arrows instead of building non-extensible arrows. - * 4. The ~bond forms are composed entirely of \rule elements. - * 5. Two dashes in _getBond are wrapped in braces to suppress spacing. i.e., {-} - * 6. The electron dot uses \textbullet instead of \bullet. - * 7. \smash[T] has been removed. (WebKit hides anything inside \smash{…}) - * - * This code, as other Temml code, is released under the MIT license. - * - * /************************************************************* - * - * MathJax/extensions/TeX/mhchem.js - * - * Implements the \ce command for handling chemical formulas - * from the mhchem LaTeX package. - * - * --------------------------------------------------------------------- - * - * Copyright (c) 2011-2015 The MathJax Consortium - * Copyright (c) 2015-2018 Martin Hensel - * - * 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. - */ - -// -// Coding Style -// - use '' for identifiers that can by minified/uglified -// - use "" for strings that need to stay untouched - -// version: "3.3.0" for MathJax and Temml - - -// Add \ce, \pu, and \tripleDash to the Temml macros. - -defineMacro("\\ce", function(context) { - return chemParse(context.consumeArgs(1)[0], "ce") -}); - -defineMacro("\\pu", function(context) { - return chemParse(context.consumeArgs(1)[0], "pu"); -}); - -// Math fonts do not include glyphs for the ~ form of bonds. So we'll send path geometry -// So we'll compose characters built from \rule elements. -defineMacro("\\uniDash", `{\\rule{0.672em}{0.06em}}`) -defineMacro("\\triDash", `{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}`) -defineMacro("\\tripleDash", `\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em`) -defineMacro("\\tripleDashOverLine", `\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em`) -defineMacro("\\tripleDashOverDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) -defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\uniDash}}\\raise0.27em{\\triDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) - - // - // This is the main function for handing the \ce and \pu commands. - // It takes the argument to \ce or \pu and returns the corresponding TeX string. - // - - var chemParse = function (tokens, stateMachine) { - // Recreate the argument string from Temml's array of tokens. - var str = ""; - var expectedLoc = tokens.length && tokens[tokens.length - 1].loc.start - for (var i = tokens.length - 1; i >= 0; i--) { - if(tokens[i].loc.start > expectedLoc) { - // context.consumeArgs has eaten a space. - str += " "; - expectedLoc = tokens[i].loc.start; - } - str += tokens[i].text; - expectedLoc += tokens[i].text.length; - } - // Call the mhchem core parser. - var tex = texify.go(mhchemParser.go(str, stateMachine)); - return tex; - }; - - // - // Core parser for mhchem syntax (recursive) - // - /** @type {MhchemParser} */ - var mhchemParser = { - // - // Parses mchem \ce syntax - // - // Call like - // go("H2O"); - // - go: function (input, stateMachine) { - if (!input) { return []; } - if (stateMachine === undefined) { stateMachine = 'ce'; } - var state = '0'; - - // - // String buffers for parsing: - // - // buffer.a == amount - // buffer.o == element - // buffer.b == left-side superscript - // buffer.p == left-side subscript - // buffer.q == right-side subscript - // buffer.d == right-side superscript - // - // buffer.r == arrow - // buffer.rdt == arrow, script above, type - // buffer.rd == arrow, script above, content - // buffer.rqt == arrow, script below, type - // buffer.rq == arrow, script below, content - // - // buffer.text_ - // buffer.rm - // etc. - // - // buffer.parenthesisLevel == int, starting at 0 - // buffer.sb == bool, space before - // buffer.beginsWithBond == bool - // - // These letters are also used as state names. - // - // Other states: - // 0 == begin of main part (arrow/operator unlikely) - // 1 == next entity - // 2 == next entity (arrow/operator unlikely) - // 3 == next atom - // c == macro - // - /** @type {Buffer} */ - var buffer = {}; - buffer['parenthesisLevel'] = 0; - - input = input.replace(/\n/g, " "); - input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-"); - input = input.replace(/[\u2026]/g, "..."); - - // - // Looks through mhchemParser.transitions, to execute a matching action - // (recursive) - // - var lastInput; - var watchdog = 10; - /** @type {ParserOutput[]} */ - var output = []; - while (true) { - if (lastInput !== input) { - watchdog = 10; - lastInput = input; - } else { - watchdog--; - } - // - // Find actions in transition table - // - var machine = mhchemParser.stateMachines[stateMachine]; - var t = machine.transitions[state] || machine.transitions['*']; - iterateTransitions: - for (var i=0; i 0) { - if (!task.revisit) { - input = matches.remainder; - } - if (!task.toContinue) { - break iterateTransitions; - } - } else { - return output; - } - } - } - // - // Prevent infinite loop - // - if (watchdog <= 0) { - throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character - } - } - }, - concatArray: function (a, b) { - if (b) { - if (Array.isArray(b)) { - for (var iB=0; iB': /^[=<>]/, - '#': /^[#\u2261]/, - '+': /^\+/, - '-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, // -space -, -; -] -/ -$ -state-of-aggregation - '-9': /^-(?=[0-9])/, - '- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/, - '-': /^-/, - 'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/, - 'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/, - 'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/, - '\\bond{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\bond{", "", "", "}"); }, - '->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/, - 'CMT': /^[CMT](?=\[)/, - '[(...)]': function (input) { return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]"); }, - '1st-level escape': /^(&|\\\\|\\hline)\s*/, - '\\,': /^(?:\\[,\ ;:])/, // \\x - but output no space before - '\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); }, - '\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", ""); }, - '\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/, - '\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/, - 'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, // only those with numbers in front, because the others will be formatted correctly anyway - 'others': /^[\/~|]/, - '\\frac{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\frac{", "", "", "}", "{", "", "", "}"); }, - '\\overset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\overset{", "", "", "}", "{", "", "", "}"); }, - '\\underset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underset{", "", "", "}", "{", "", "", "}"); }, - '\\underbrace{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underbrace{", "", "", "}_", "{", "", "", "}"); }, - '\\color{(...)}0': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}"); }, - '\\color{(...)}{(...)}1': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}", "{", "", "", "}"); }, - '\\color(...){(...)}2': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}"); }, - '\\ce{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\ce{", "", "", "}"); }, - 'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, - 'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, // 0 could be oxidation or charge - 'roman numeral': /^[IVX]+/, - '1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/, - 'amount': function (input) { - var match; - // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing - match = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/); - if (match) { - return { match_: match[0], remainder: input.substr(match[0].length) }; - } - var a = mhchemParser.patterns.findObserveGroups(input, "", "$", "$", ""); - if (a) { // e.g. $2n-1$, $-$ - match = a.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/); - if (match) { - return { match_: match[0], remainder: input.substr(match[0].length) }; - } - } - return null; - }, - 'amount2': function (input) { return this['amount'](input); }, - '(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/, - 'formula$': function (input) { - if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula - var match = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/); - if (match) { - return { match_: match[0], remainder: input.substr(match[0].length) }; - } - return null; - }, - 'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/, - '/': /^\s*(\/)\s*/, - '//': /^\s*(\/\/)\s*/, - '*': /^\s*[*.]\s*/ - }, - findObserveGroups: function (input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) { - /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */ - var _match = function (input, pattern) { - if (typeof pattern === "string") { - if (input.indexOf(pattern) !== 0) { return null; } - return pattern; - } else { - var match = input.match(pattern); - if (!match) { return null; } - return match[0]; - } - }; - /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */ - var _findObserveGroups = function (input, i, endChars) { - var braces = 0; - while (i < input.length) { - var a = input.charAt(i); - var match = _match(input.substr(i), endChars); - if (match !== null && braces === 0) { - return { endMatchBegin: i, endMatchEnd: i + match.length }; - } else if (a === "{") { - braces++; - } else if (a === "}") { - if (braces === 0) { - throw ["ExtraCloseMissingOpen", "Extra close brace or missing open brace"]; - } else { - braces--; - } - } - i++; - } - if (braces > 0) { - return null; - } - return null; - }; - var match = _match(input, begExcl); - if (match === null) { return null; } - input = input.substr(match.length); - match = _match(input, begIncl); - if (match === null) { return null; } - var e = _findObserveGroups(input, match.length, endIncl || endExcl); - if (e === null) { return null; } - var match1 = input.substring(0, (endIncl ? e.endMatchEnd : e.endMatchBegin)); - if (!(beg2Excl || beg2Incl)) { - return { - match_: match1, - remainder: input.substr(e.endMatchEnd) - }; - } else { - var group2 = this.findObserveGroups(input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl); - if (group2 === null) { return null; } - /** @type {string[]} */ - var matchRet = [match1, group2.match_]; - return { - match_: (combine ? matchRet.join("") : matchRet), - remainder: group2.remainder - }; - } - }, - - // - // Matching function - // e.g. match("a", input) will look for the regexp called "a" and see if it matches - // returns null or {match_:"a", remainder:"bc"} - // - match_: function (m, input) { - var pattern = mhchemParser.patterns.patterns[m]; - if (pattern === undefined) { - throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern - } else if (typeof pattern === "function") { - return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser - } else { // RegExp - var match = input.match(pattern); - if (match) { - var mm; - if (match[2]) { - mm = [ match[1], match[2] ]; - } else if (match[1]) { - mm = match[1]; - } else { - mm = match[0]; - } - return { match_: mm, remainder: input.substr(match[0].length) }; - } - return null; - } - } - }, - - // - // Generic state machine actions - // - actions: { - 'a=': function (buffer, m) { buffer.a = (buffer.a || "") + m; }, - 'b=': function (buffer, m) { buffer.b = (buffer.b || "") + m; }, - 'p=': function (buffer, m) { buffer.p = (buffer.p || "") + m; }, - 'o=': function (buffer, m) { buffer.o = (buffer.o || "") + m; }, - 'q=': function (buffer, m) { buffer.q = (buffer.q || "") + m; }, - 'd=': function (buffer, m) { buffer.d = (buffer.d || "") + m; }, - 'rm=': function (buffer, m) { buffer.rm = (buffer.rm || "") + m; }, - 'text=': function (buffer, m) { buffer.text_ = (buffer.text_ || "") + m; }, - 'insert': function (buffer, m, a) { return { type_: a }; }, - 'insert+p1': function (buffer, m, a) { return { type_: a, p1: m }; }, - 'insert+p1+p2': function (buffer, m, a) { return { type_: a, p1: m[0], p2: m[1] }; }, - 'copy': function (buffer, m) { return m; }, - 'rm': function (buffer, m) { return { type_: 'rm', p1: m || ""}; }, - 'text': function (buffer, m) { return mhchemParser.go(m, 'text'); }, - '{text}': function (buffer, m) { - var ret = [ "{" ]; - mhchemParser.concatArray(ret, mhchemParser.go(m, 'text')); - ret.push("}"); - return ret; - }, - 'tex-math': function (buffer, m) { return mhchemParser.go(m, 'tex-math'); }, - 'tex-math tight': function (buffer, m) { return mhchemParser.go(m, 'tex-math tight'); }, - 'bond': function (buffer, m, k) { return { type_: 'bond', kind_: k || m }; }, - 'color0-output': function (buffer, m) { return { type_: 'color0', color: m[0] }; }, - 'ce': function (buffer, m) { return mhchemParser.go(m); }, - '1/2': function (buffer, m) { - /** @type {ParserOutput[]} */ - var ret = []; - if (m.match(/^[+\-]/)) { - ret.push(m.substr(0, 1)); - m = m.substr(1); - } - var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/); - n[1] = n[1].replace(/\$/g, ""); - ret.push({ type_: 'frac', p1: n[1], p2: n[2] }); - if (n[3]) { - n[3] = n[3].replace(/\$/g, ""); - ret.push({ type_: 'tex-math', p1: n[3] }); - } - return ret; - }, - '9,9': function (buffer, m) { return mhchemParser.go(m, '9,9'); } - }, - // - // createTransitions - // convert { 'letter': { 'state': { action_: 'output' } } } to { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] } - // with expansion of 'a|b' to 'a' and 'b' (at 2 places) - // - createTransitions: function (o) { - var pattern, state; - /** @type {string[]} */ - var stateArray; - var i; - // - // 1. Collect all states - // - /** @type {Transitions} */ - var transitions = {}; - for (pattern in o) { - for (state in o[pattern]) { - stateArray = state.split("|"); - o[pattern][state].stateArray = stateArray; - for (i=0; i': { - '0|1|2|3': { action_: 'r=', nextState: 'r' }, - 'a|as': { action_: [ 'output', 'r=' ], nextState: 'r' }, - '*': { action_: [ 'output', 'r=' ], nextState: 'r' } }, - '+': { - 'o': { action_: 'd= kv', nextState: 'd' }, - 'd|D': { action_: 'd=', nextState: 'd' }, - 'q': { action_: 'd=', nextState: 'qd' }, - 'qd|qD': { action_: 'd=', nextState: 'qd' }, - 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' }, - '3': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } }, - 'amount': { - '0|2': { action_: 'a=', nextState: 'a' } }, - 'pm-operator': { - '0|1|2|a|as': { action_: [ 'sb=false', 'output', { type_: 'operator', option: '\\pm' } ], nextState: '0' } }, - 'operator': { - '0|1|2|a|as': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } }, - '-$': { - 'o|q': { action_: [ 'charge or bond', 'output' ], nextState: 'qd' }, - 'd': { action_: 'd=', nextState: 'd' }, - 'D': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' }, - 'q': { action_: 'd=', nextState: 'qd' }, - 'qd': { action_: 'd=', nextState: 'qd' }, - 'qD|dq': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } }, - '-9': { - '3|o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '3' } }, - '- orbital overlap': { - 'o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' }, - 'd': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' } }, - '-': { - '0|1|2': { action_: [ { type_: 'output', option: 1 }, 'beginsWithBond=true', { type_: 'bond', option: "-" } ], nextState: '3' }, - '3': { action_: { type_: 'bond', option: "-" } }, - 'a': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' }, - 'as': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "-" } ], nextState: '3' }, - 'b': { action_: 'b=' }, - 'o': { action_: { type_: '- after o/d', option: false }, nextState: '2' }, - 'q': { action_: { type_: '- after o/d', option: false }, nextState: '2' }, - 'd|qd|dq': { action_: { type_: '- after o/d', option: true }, nextState: '2' }, - 'D|qD|p': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } }, - 'amount2': { - '1|3': { action_: 'a=', nextState: 'a' } }, - 'letters': { - '0|1|2|3|a|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, - 'q|dq': { action_: ['output', 'o='], nextState: 'o' }, - 'd|D|qd|qD': { action_: 'o after d', nextState: 'o' } }, - 'digits': { - 'o': { action_: 'q=', nextState: 'q' }, - 'd|D': { action_: 'q=', nextState: 'dq' }, - 'q': { action_: [ 'output', 'o=' ], nextState: 'o' }, - 'a': { action_: 'o=', nextState: 'o' } }, - 'space A': { - 'b|p|bp': {} }, - 'space': { - 'a': { nextState: 'as' }, - '0': { action_: 'sb=false' }, - '1|2': { action_: 'sb=true' }, - 'r|rt|rd|rdt|rdq': { action_: 'output', nextState: '0' }, - '*': { action_: [ 'output', 'sb=true' ], nextState: '1'} }, - '1st-level escape': { - '1|2': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ] }, - '*': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ], nextState: '0' } }, - '[(...)]': { - 'r|rt': { action_: 'rd=', nextState: 'rd' }, - 'rd|rdt': { action_: 'rq=', nextState: 'rdq' } }, - '...': { - 'o|d|D|dq|qd|qD': { action_: [ 'output', { type_: 'bond', option: "..." } ], nextState: '3' }, - '*': { action_: [ { type_: 'output', option: 1 }, { type_: 'insert', option: 'ellipsis' } ], nextState: '1' } }, - '. |* ': { - '*': { action_: [ 'output', { type_: 'insert', option: 'addition compound' } ], nextState: '1' } }, - 'state of aggregation $': { - '*': { action_: [ 'output', 'state of aggregation' ], nextState: '1' } }, - '{[(': { - 'a|as|o': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' }, - '0|1|2|3': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' }, - '*': { action_: [ 'output', 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' } }, - ')]}': { - '0|1|2|3|b|p|bp|o': { action_: [ 'o=', 'parenthesisLevel--' ], nextState: 'o' }, - 'a|as|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=', 'parenthesisLevel--' ], nextState: 'o' } }, - ', ': { - '*': { action_: [ 'output', 'comma' ], nextState: '0' } }, - '^_': { // ^ and _ without a sensible argument - '*': { } }, - '^{(...)}|^($...$)': { - '0|1|2|as': { action_: 'b=', nextState: 'b' }, - 'p': { action_: 'b=', nextState: 'bp' }, - '3|o': { action_: 'd= kv', nextState: 'D' }, - 'q': { action_: 'd=', nextState: 'qD' }, - 'd|D|qd|qD|dq': { action_: [ 'output', 'd=' ], nextState: 'D' } }, - '^a|^\\x{}{}|^\\x{}|^\\x|\'': { - '0|1|2|as': { action_: 'b=', nextState: 'b' }, - 'p': { action_: 'b=', nextState: 'bp' }, - '3|o': { action_: 'd= kv', nextState: 'd' }, - 'q': { action_: 'd=', nextState: 'qd' }, - 'd|qd|D|qD': { action_: 'd=' }, - 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' } }, - '_{(state of aggregation)}$': { - 'd|D|q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } }, - '_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': { - '0|1|2|as': { action_: 'p=', nextState: 'p' }, - 'b': { action_: 'p=', nextState: 'bp' }, - '3|o': { action_: 'q=', nextState: 'q' }, - 'd|D': { action_: 'q=', nextState: 'dq' }, - 'q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } }, - '=<>': { - '0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: '3' } }, - '#': { - '0|1|2|3|a|as|o': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "#" } ], nextState: '3' } }, - '{}': { - '*': { action_: { type_: 'output', option: 1 }, nextState: '1' } }, - '{...}': { - '0|1|2|3|a|as|b|p|bp': { action_: 'o=', nextState: 'o' }, - 'o|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } }, - '$...$': { - 'a': { action_: 'a=' }, // 2$n$ - '0|1|2|3|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, // not 'amount' - 'as|o': { action_: 'o=' }, - 'q|d|D|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } }, - '\\bond{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: "3" } }, - '\\frac{(...)}': { - '*': { action_: [ { type_: 'output', option: 1 }, 'frac-output' ], nextState: '3' } }, - '\\overset{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'overset-output' ], nextState: '3' } }, - '\\underset{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'underset-output' ], nextState: '3' } }, - '\\underbrace{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'underbrace-output' ], nextState: '3' } }, - '\\color{(...)}{(...)}1|\\color(...){(...)}2': { - '*': { action_: [ { type_: 'output', option: 2 }, 'color-output' ], nextState: '3' } }, - '\\color{(...)}0': { - '*': { action_: [ { type_: 'output', option: 2 }, 'color0-output' ] } }, - '\\ce{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'ce' ], nextState: '3' } }, - '\\,': { - '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '1' } }, - '\\x{}{}|\\x{}|\\x': { - '0|1|2|3|a|as|b|p|bp|o|c0': { action_: [ 'o=', 'output' ], nextState: '3' }, - '*': { action_: ['output', 'o=', 'output' ], nextState: '3' } }, - 'others': { - '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '3' } }, - 'else2': { - 'a': { action_: 'a to o', nextState: 'o', revisit: true }, - 'as': { action_: [ 'output', 'sb=true' ], nextState: '1', revisit: true }, - 'r|rt|rd|rdt|rdq': { action_: [ 'output' ], nextState: '0', revisit: true }, - '*': { action_: [ 'output', 'copy' ], nextState: '3' } } - }), - actions: { - 'o after d': function (buffer, m) { - var ret; - if ((buffer.d || "").match(/^[0-9]+$/)) { - var tmp = buffer.d; - buffer.d = undefined; - ret = this['output'](buffer); - buffer.b = tmp; - } else { - ret = this['output'](buffer); - } - mhchemParser.actions['o='](buffer, m); - return ret; - }, - 'd= kv': function (buffer, m) { - buffer.d = m; - buffer.dType = 'kv'; - }, - 'charge or bond': function (buffer, m) { - if (buffer['beginsWithBond']) { - /** @type {ParserOutput[]} */ - var ret = []; - mhchemParser.concatArray(ret, this['output'](buffer)); - mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); - return ret; - } else { - buffer.d = m; - } - }, - '- after o/d': function (buffer, m, isAfterD) { - var c1 = mhchemParser.patterns.match_('orbital', buffer.o || ""); - var c2 = mhchemParser.patterns.match_('one lowercase greek letter $', buffer.o || ""); - var c3 = mhchemParser.patterns.match_('one lowercase latin letter $', buffer.o || ""); - var c4 = mhchemParser.patterns.match_('$one lowercase latin letter$ $', buffer.o || ""); - var hyphenFollows = m==="-" && ( c1 && c1.remainder==="" || c2 || c3 || c4 ); - if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) { - buffer.o = '$' + buffer.o + '$'; - } - /** @type {ParserOutput[]} */ - var ret = []; - if (hyphenFollows) { - mhchemParser.concatArray(ret, this['output'](buffer)); - ret.push({ type_: 'hyphen' }); - } else { - c1 = mhchemParser.patterns.match_('digits', buffer.d || ""); - if (isAfterD && c1 && c1.remainder==='') { - mhchemParser.concatArray(ret, mhchemParser.actions['d='](buffer, m)); - mhchemParser.concatArray(ret, this['output'](buffer)); - } else { - mhchemParser.concatArray(ret, this['output'](buffer)); - mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); - } - } - return ret; - }, - 'a to o': function (buffer) { - buffer.o = buffer.a; - buffer.a = undefined; - }, - 'sb=true': function (buffer) { buffer.sb = true; }, - 'sb=false': function (buffer) { buffer.sb = false; }, - 'beginsWithBond=true': function (buffer) { buffer['beginsWithBond'] = true; }, - 'beginsWithBond=false': function (buffer) { buffer['beginsWithBond'] = false; }, - 'parenthesisLevel++': function (buffer) { buffer['parenthesisLevel']++; }, - 'parenthesisLevel--': function (buffer) { buffer['parenthesisLevel']--; }, - 'state of aggregation': function (buffer, m) { - return { type_: 'state of aggregation', p1: mhchemParser.go(m, 'o') }; - }, - 'comma': function (buffer, m) { - var a = m.replace(/\s*$/, ''); - var withSpace = (a !== m); - if (withSpace && buffer['parenthesisLevel'] === 0) { - return { type_: 'comma enumeration L', p1: a }; - } else { - return { type_: 'comma enumeration M', p1: a }; - } - }, - 'output': function (buffer, m, entityFollows) { - // entityFollows: - // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb) - // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1) - // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as) - /** @type {ParserOutput | ParserOutput[]} */ - var ret; - if (!buffer.r) { - ret = []; - if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) { - //ret = []; - } else { - if (buffer.sb) { - ret.push({ type_: 'entitySkip' }); - } - if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows!==2) { - buffer.o = buffer.a; - buffer.a = undefined; - } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) { - buffer.o = buffer.a; - buffer.d = buffer.b; - buffer.q = buffer.p; - buffer.a = buffer.b = buffer.p = undefined; - } else { - if (buffer.o && buffer.dType==='kv' && mhchemParser.patterns.match_('d-oxidation$', buffer.d || "")) { - buffer.dType = 'oxidation'; - } else if (buffer.o && buffer.dType==='kv' && !buffer.q) { - buffer.dType = undefined; - } - } - ret.push({ - type_: 'chemfive', - a: mhchemParser.go(buffer.a, 'a'), - b: mhchemParser.go(buffer.b, 'bd'), - p: mhchemParser.go(buffer.p, 'pq'), - o: mhchemParser.go(buffer.o, 'o'), - q: mhchemParser.go(buffer.q, 'pq'), - d: mhchemParser.go(buffer.d, (buffer.dType === 'oxidation' ? 'oxidation' : 'bd')), - dType: buffer.dType - }); - } - } else { // r - /** @type {ParserOutput[]} */ - var rd; - if (buffer.rdt === 'M') { - rd = mhchemParser.go(buffer.rd, 'tex-math'); - } else if (buffer.rdt === 'T') { - rd = [ { type_: 'text', p1: buffer.rd || "" } ]; - } else { - rd = mhchemParser.go(buffer.rd); - } - /** @type {ParserOutput[]} */ - var rq; - if (buffer.rqt === 'M') { - rq = mhchemParser.go(buffer.rq, 'tex-math'); - } else if (buffer.rqt === 'T') { - rq = [ { type_: 'text', p1: buffer.rq || ""} ]; - } else { - rq = mhchemParser.go(buffer.rq); - } - ret = { - type_: 'arrow', - r: buffer.r, - rd: rd, - rq: rq - }; - } - for (var p in buffer) { - if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') { - delete buffer[p]; - } - } - return ret; - }, - 'oxidation-output': function (buffer, m) { - var ret = [ "{" ]; - mhchemParser.concatArray(ret, mhchemParser.go(m, 'oxidation')); - ret.push("}"); - return ret; - }, - 'frac-output': function (buffer, m) { - return { type_: 'frac-ce', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'overset-output': function (buffer, m) { - return { type_: 'overset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'underset-output': function (buffer, m) { - return { type_: 'underset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'underbrace-output': function (buffer, m) { - return { type_: 'underbrace', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'color-output': function (buffer, m) { - return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1]) }; - }, - 'r=': function (buffer, m) { buffer.r = m; }, - 'rdt=': function (buffer, m) { buffer.rdt = m; }, - 'rd=': function (buffer, m) { buffer.rd = m; }, - 'rqt=': function (buffer, m) { buffer.rqt = m; }, - 'rq=': function (buffer, m) { buffer.rq = m; }, - 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; } - } - }, - 'a': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - '1/2$': { - '0': { action_: '1/2' } }, - 'else': { - '0': { nextState: '1', revisit: true } }, - '$(...)$': { - '*': { action_: 'tex-math tight', nextState: '1' } }, - ',': { - '*': { action_: { type_: 'insert', option: 'commaDecimal' } } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: {} - }, - 'o': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - '1/2$': { - '0': { action_: '1/2' } }, - 'else': { - '0': { nextState: '1', revisit: true } }, - 'letters': { - '*': { action_: 'rm' } }, - '\\ca': { - '*': { action_: { type_: 'insert', option: 'circa' } } }, - '\\x{}{}|\\x{}|\\x': { - '*': { action_: 'copy' } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '{(...)}': { - '*': { action_: '{text}' } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: {} - }, - 'text': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '{...}': { - '*': { action_: 'text=' } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '\\greek': { - '*': { action_: [ 'output', 'rm' ] } }, - '\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: [ 'output', 'copy' ] } }, - 'else': { - '*': { action_: 'text=' } } - }), - actions: { - 'output': function (buffer) { - if (buffer.text_) { - /** @type {ParserOutput} */ - var ret = { type_: 'text', p1: buffer.text_ }; - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - } - }, - 'pq': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - 'state of aggregation $': { - '*': { action_: 'state of aggregation' } }, - 'i$': { - '0': { nextState: '!f', revisit: true } }, - '(KV letters),': { - '0': { action_: 'rm', nextState: '0' } }, - 'formula$': { - '0': { nextState: 'f', revisit: true } }, - '1/2$': { - '0': { action_: '1/2' } }, - 'else': { - '0': { nextState: '!f', revisit: true } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '{(...)}': { - '*': { action_: 'text' } }, - 'a-z': { - 'f': { action_: 'tex-math' } }, - 'letters': { - '*': { action_: 'rm' } }, - '-9.,9': { - '*': { action_: '9,9' } }, - ',': { - '*': { action_: { type_: 'insert+p1', option: 'comma enumeration S' } } }, - '\\color{(...)}{(...)}1|\\color(...){(...)}2': { - '*': { action_: 'color-output' } }, - '\\color{(...)}0': { - '*': { action_: 'color0-output' } }, - '\\ce{(...)}': { - '*': { action_: 'ce' } }, - '\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'copy' } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: { - 'state of aggregation': function (buffer, m) { - return { type_: 'state of aggregation subscript', p1: mhchemParser.go(m, 'o') }; - }, - 'color-output': function (buffer, m) { - return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'pq') }; - } - } - }, - 'bd': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - 'x$': { - '0': { nextState: '!f', revisit: true } }, - 'formula$': { - '0': { nextState: 'f', revisit: true } }, - 'else': { - '0': { nextState: '!f', revisit: true } }, - '-9.,9 no missing 0': { - '*': { action_: '9,9' } }, - '.': { - '*': { action_: { type_: 'insert', option: 'electron dot' } } }, - 'a-z': { - 'f': { action_: 'tex-math' } }, - 'x': { - '*': { action_: { type_: 'insert', option: 'KV x' } } }, - 'letters': { - '*': { action_: 'rm' } }, - '\'': { - '*': { action_: { type_: 'insert', option: 'prime' } } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '{(...)}': { - '*': { action_: 'text' } }, - '\\color{(...)}{(...)}1|\\color(...){(...)}2': { - '*': { action_: 'color-output' } }, - '\\color{(...)}0': { - '*': { action_: 'color0-output' } }, - '\\ce{(...)}': { - '*': { action_: 'ce' } }, - '\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'copy' } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: { - 'color-output': function (buffer, m) { - return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'bd') }; - } - } - }, - 'oxidation': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - 'roman numeral': { - '*': { action_: 'roman-numeral' } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - 'else': { - '*': { action_: 'copy' } } - }), - actions: { - 'roman-numeral': function (buffer, m) { return { type_: 'roman numeral', p1: m || "" }; } - } - }, - 'tex-math': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '\\ce{(...)}': { - '*': { action_: [ 'output', 'ce' ] } }, - '{...}|\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'o=' } }, - 'else': { - '*': { action_: 'o=' } } - }), - actions: { - 'output': function (buffer) { - if (buffer.o) { - /** @type {ParserOutput} */ - var ret = { type_: 'tex-math', p1: buffer.o }; - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - } - }, - 'tex-math tight': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '\\ce{(...)}': { - '*': { action_: [ 'output', 'ce' ] } }, - '{...}|\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'o=' } }, - '-|+': { - '*': { action_: 'tight operator' } }, - 'else': { - '*': { action_: 'o=' } } - }), - actions: { - 'tight operator': function (buffer, m) { buffer.o = (buffer.o || "") + "{"+m+"}"; }, - 'output': function (buffer) { - if (buffer.o) { - /** @type {ParserOutput} */ - var ret = { type_: 'tex-math', p1: buffer.o }; - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - } - }, - '9,9': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - ',': { - '*': { action_: 'comma' } }, - 'else': { - '*': { action_: 'copy' } } - }), - actions: { - 'comma': function () { return { type_: 'commaDecimal' }; } - } - }, - //#endregion - // - // \pu state machines - // - //#region pu - 'pu': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - 'space$': { - '*': { action_: [ 'output', 'space' ] } }, - '{[(|)]}': { - '0|a': { action_: 'copy' } }, - '(-)(9)^(-9)': { - '0': { action_: 'number^', nextState: 'a' } }, - '(-)(9.,9)(e)(99)': { - '0': { action_: 'enumber', nextState: 'a' } }, - 'space': { - '0|a': {} }, - 'pm-operator': { - '0|a': { action_: { type_: 'operator', option: '\\pm' }, nextState: '0' } }, - 'operator': { - '0|a': { action_: 'copy', nextState: '0' } }, - '//': { - 'd': { action_: 'o=', nextState: '/' } }, - '/': { - 'd': { action_: 'o=', nextState: '/' } }, - '{...}|else': { - '0|d': { action_: 'd=', nextState: 'd' }, - 'a': { action_: [ 'space', 'd=' ], nextState: 'd' }, - '/|q': { action_: 'q=', nextState: 'q' } } - }), - actions: { - 'enumber': function (buffer, m) { - /** @type {ParserOutput[]} */ - var ret = []; - if (m[0] === "+-" || m[0] === "+/-") { - ret.push("\\pm "); - } else if (m[0]) { - ret.push(m[0]); - } - if (m[1]) { - mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); - if (m[2]) { - if (m[2].match(/[,.]/)) { - mhchemParser.concatArray(ret, mhchemParser.go(m[2], 'pu-9,9')); - } else { - ret.push(m[2]); - } - } - m[3] = m[4] || m[3]; - if (m[3]) { - m[3] = m[3].trim(); - if (m[3] === "e" || m[3].substr(0, 1) === "*") { - ret.push({ type_: 'cdot' }); - } else { - ret.push({ type_: 'times' }); - } - } - } - if (m[3]) { - ret.push("10^{"+m[5]+"}"); - } - return ret; - }, - 'number^': function (buffer, m) { - /** @type {ParserOutput[]} */ - var ret = []; - if (m[0] === "+-" || m[0] === "+/-") { - ret.push("\\pm "); - } else if (m[0]) { - ret.push(m[0]); - } - mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); - ret.push("^{"+m[2]+"}"); - return ret; - }, - 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; }, - 'space': function () { return { type_: 'pu-space-1' }; }, - 'output': function (buffer) { - /** @type {ParserOutput | ParserOutput[]} */ - var ret; - var md = mhchemParser.patterns.match_('{(...)}', buffer.d || ""); - if (md && md.remainder === '') { buffer.d = md.match_; } - var mq = mhchemParser.patterns.match_('{(...)}', buffer.q || ""); - if (mq && mq.remainder === '') { buffer.q = mq.match_; } - if (buffer.d) { - buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); - buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); - } - if (buffer.q) { // fraction - buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); - buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); - var b5 = { - d: mhchemParser.go(buffer.d, 'pu'), - q: mhchemParser.go(buffer.q, 'pu') - }; - if (buffer.o === '//') { - ret = { type_: 'pu-frac', p1: b5.d, p2: b5.q }; - } else { - ret = b5.d; - if (b5.d.length > 1 || b5.q.length > 1) { - ret.push({ type_: ' / ' }); - } else { - ret.push({ type_: '/' }); - } - mhchemParser.concatArray(ret, b5.q); - } - } else { // no fraction - ret = mhchemParser.go(buffer.d, 'pu-2'); - } - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - }, - 'pu-2': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '*': { - '*': { action_: [ 'output', 'cdot' ], nextState: '0' } }, - '\\x': { - '*': { action_: 'rm=' } }, - 'space': { - '*': { action_: [ 'output', 'space' ], nextState: '0' } }, - '^{(...)}|^(-1)': { - '1': { action_: '^(-1)' } }, - '-9.,9': { - '0': { action_: 'rm=', nextState: '0' }, - '1': { action_: '^(-1)', nextState: '0' } }, - '{...}|else': { - '*': { action_: 'rm=', nextState: '1' } } - }), - actions: { - 'cdot': function () { return { type_: 'tight cdot' }; }, - '^(-1)': function (buffer, m) { buffer.rm += "^{"+m+"}"; }, - 'space': function () { return { type_: 'pu-space-2' }; }, - 'output': function (buffer) { - /** @type {ParserOutput | ParserOutput[]} */ - var ret = []; - if (buffer.rm) { - var mrm = mhchemParser.patterns.match_('{(...)}', buffer.rm || ""); - if (mrm && mrm.remainder === '') { - ret = mhchemParser.go(mrm.match_, 'pu'); - } else { - ret = { type_: 'rm', p1: buffer.rm }; - } - } - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - }, - 'pu-9,9': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '0': { action_: 'output-0' }, - 'o': { action_: 'output-o' } }, - ',': { - '0': { action_: [ 'output-0', 'comma' ], nextState: 'o' } }, - '.': { - '0': { action_: [ 'output-0', 'copy' ], nextState: 'o' } }, - 'else': { - '*': { action_: 'text=' } } - }), - actions: { - 'comma': function () { return { type_: 'commaDecimal' }; }, - 'output-0': function (buffer) { - /** @type {ParserOutput[]} */ - var ret = []; - buffer.text_ = buffer.text_ || ""; - if (buffer.text_.length > 4) { - var a = buffer.text_.length % 3; - if (a === 0) { a = 3; } - for (var i=buffer.text_.length-3; i>0; i-=3) { - ret.push(buffer.text_.substr(i, 3)); - ret.push({ type_: '1000 separator' }); - } - ret.push(buffer.text_.substr(0, a)); - ret.reverse(); - } else { - ret.push(buffer.text_); - } - for (var p in buffer) { delete buffer[p]; } - return ret; - }, - 'output-o': function (buffer) { - /** @type {ParserOutput[]} */ - var ret = []; - buffer.text_ = buffer.text_ || ""; - if (buffer.text_.length > 4) { - var a = buffer.text_.length - 3; - for (var i=0; i w/height="0" - //res += "^{\\smash[t]{\\vphantom{2}}\\mathllap{"+(b5.b||"")+"}}"; - res += "^{\\vphantom{2}\\mathllap{"+(b5.b||"")+"}}"; - //res += "_{\\vphantom{2}\\mathllap{\\smash[t]{"+(b5.p||"")+"}}}"; - res += "_{\\vphantom{2}\\mathllap{"+(b5.p||"")+"}}"; - } - // - // o - // - if (b5.o) { - if (b5.o.match(/^[+\-]/)) { b5.o = "{"+b5.o+"}"; } - res += b5.o; - } - // - // q and d - // - if (buf.dType === 'kv') { - if (b5.d || b5.q) { - res += "{\\vphantom{X}}"; - } - if (b5.d) { - res += "^{"+b5.d+"}"; - } - if (b5.q) { - // In the next line, I've removed \smash[t] (ron) - // TODO: Revert \smash[t] when WebKit properly renders w/height="0" - //res += "_{\\smash[t]{"+b5.q+"}}"; - res += "_{"+b5.q+"}"; - } - } else if (buf.dType === 'oxidation') { - if (b5.d) { - res += "{\\vphantom{X}}"; - res += "^{"+b5.d+"}"; - } - if (b5.q) { - // A Firefox bug adds a bogus depth to , so we change \vphantom{X} to {} - // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. -// res += "{\\vphantom{X}}"; - res += "{{}}"; - // In the next line, I've removed \smash[t] (ron) - // TODO: Revert \smash[t] when WebKit properly renders w/height="0" - //res += "_{\\smash[t]{"+b5.q+"}}"; - res += "_{"+b5.q+"}"; - } - } else { - if (b5.q) { - // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. -// res += "{\\vphantom{X}}"; - res += "{{}}"; - // In the next line, I've removed \smash[t] (ron) - // TODO: Revert \smash[t] when WebKit properly renders w/height="0" - //res += "_{\\smash[t]{"+b5.q+"}}"; - res += "_{"+b5.q+"}"; - } - if (b5.d) { - // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. -// res += "{\\vphantom{X}}"; - res += "{{}}"; - res += "^{"+b5.d+"}"; - } - } - break; - case 'rm': - res = "\\mathrm{"+buf.p1+"}"; - break; - case 'text': - if (buf.p1.match(/[\^_]/)) { - buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}"); - res = "\\mathrm{"+buf.p1+"}"; - } else { - res = "\\text{"+buf.p1+"}"; - } - break; - case 'roman numeral': - res = "\\mathrm{"+buf.p1+"}"; - break; - case 'state of aggregation': - res = "\\mskip2mu "+texify._goInner(buf.p1); - break; - case 'state of aggregation subscript': - res = "\\mskip1mu "+texify._goInner(buf.p1); - break; - case 'bond': - res = texify._getBond(buf.kind_); - if (!res) { - throw ["MhchemErrorBond", "mhchem Error. Unknown bond type (" + buf.kind_ + ")"]; - } - break; - case 'frac': - var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}"; - res = "\\mathchoice{\\textstyle"+c+"}{"+c+"}{"+c+"}{"+c+"}"; - break; - case 'pu-frac': - var d = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - res = "\\mathchoice{\\textstyle"+d+"}{"+d+"}{"+d+"}{"+d+"}"; - break; - case 'tex-math': - res = buf.p1 + " "; - break; - case 'frac-ce': - res = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - break; - case 'overset': - res = "\\overset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - break; - case 'underset': - res = "\\underset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - break; - case 'underbrace': - res = "\\underbrace{" + texify._goInner(buf.p1) + "}_{" + texify._goInner(buf.p2) + "}"; - break; - case 'color': - res = "{\\color{" + buf.color1 + "}{" + texify._goInner(buf.color2) + "}}"; - break; - case 'color0': - res = "\\color{" + buf.color + "}"; - break; - case 'arrow': - var b6 = { - rd: texify._goInner(buf.rd), - rq: texify._goInner(buf.rq) - }; - var arrow = texify._getArrow(buf.r); - if (b6.rq) { arrow += "[{\\rm " + b6.rq + "}]"; } - if (b6.rd) { - arrow += "{\\rm " + b6.rd + "}"; - } else { - arrow += "{}"; - } - res = arrow; - break; - case 'operator': - res = texify._getOperator(buf.kind_); - break; - case '1st-level escape': - res = buf.p1+" "; // &, \\\\, \\hlin - break; - case 'space': - res = " "; - break; - case 'entitySkip': - res = "~"; - break; - case 'pu-space-1': - res = "~"; - break; - case 'pu-space-2': - res = "\\mkern3mu "; - break; - case '1000 separator': - res = "\\mkern2mu "; - break; - case 'commaDecimal': - res = "{,}"; - break; - case 'comma enumeration L': - res = "{"+buf.p1+"}\\mkern6mu "; - break; - case 'comma enumeration M': - res = "{"+buf.p1+"}\\mkern3mu "; - break; - case 'comma enumeration S': - res = "{"+buf.p1+"}\\mkern1mu "; - break; - case 'hyphen': - res = "\\text{-}"; - break; - case 'addition compound': - res = "\\,{\\cdot}\\,"; - break; - case 'electron dot': - res = "\\mkern1mu \\text{\\textbullet}\\mkern1mu "; - break; - case 'KV x': - res = "{\\times}"; - break; - case 'prime': - res = "\\prime "; - break; - case 'cdot': - res = "\\cdot "; - break; - case 'tight cdot': - res = "\\mkern1mu{\\cdot}\\mkern1mu "; - break; - case 'times': - res = "\\times "; - break; - case 'circa': - res = "{\\sim}"; - break; - case '^': - res = "uparrow"; - break; - case 'v': - res = "downarrow"; - break; - case 'ellipsis': - res = "\\ldots "; - break; - case '/': - res = "/"; - break; - case ' / ': - res = "\\,/\\,"; - break; - default: - assertNever(buf); - throw ["MhchemBugT", "mhchem bug T. Please report."]; // Missing texify rule or unknown MhchemParser output - } - assertString(res); - return res; - }, - _getArrow: function (a) { - switch (a) { - case "->": return "\\yields"; - case "\u2192": return "\\yields"; - case "\u27F6": return "\\yields"; - case "<-": return "\\yieldsLeft"; - case "<->": return "\\mesomerism"; - case "<-->": return "\\yieldsLeftRight"; - case "<=>": return "\\chemequilibrium"; - case "\u21CC": return "\\chemequilibrium"; - case "<=>>": return "\\equilibriumRight"; - case "<<=>": return "\\equilibriumLeft"; - default: - assertNever(a); - throw ["MhchemBugT", "mhchem bug T. Please report."]; - } - }, - _getBond: function (a) { - switch (a) { - case "-": return "{-}"; - case "1": return "{-}"; - case "=": return "{=}"; - case "2": return "{=}"; - case "#": return "{\\equiv}"; - case "3": return "{\\equiv}"; - case "~": return "{\\tripleDash}"; - case "~-": return "{\\tripleDashOverLine}"; - case "~=": return "{\\tripleDashOverDoubleLine}"; - case "~--": return "{\\tripleDashOverDoubleLine}"; - case "-~-": return "{\\tripleDashBetweenDoubleLine}"; - case "...": return "{{\\cdot}{\\cdot}{\\cdot}}"; - case "....": return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}"; - case "->": return "{\\rightarrow}"; - case "<-": return "{\\leftarrow}"; - case "<": return "{<}"; - case ">": return "{>}"; - default: - assertNever(a); - throw ["MhchemBugT", "mhchem bug T. Please report."]; - } - }, - _getOperator: function (a) { - switch (a) { - case "+": return " {}+{} "; - case "-": return " {}-{} "; - case "=": return " {}={} "; - case "<": return " {}<{} "; - case ">": return " {}>{} "; - case "<<": return " {}\\ll{} "; - case ">>": return " {}\\gg{} "; - case "\\pm": return " {}\\pm{} "; - case "\\approx": return " {}\\approx{} "; - case "$\\approx$": return " {}\\approx{} "; - case "v": return " \\downarrow{} "; - case "(v)": return " \\downarrow{} "; - case "^": return " \\uparrow{} "; - case "(^)": return " \\uparrow{} "; - default: - assertNever(a); - throw ["MhchemBugT", "mhchem bug T. Please report."]; - } - } - }; - - // - // Helpers for code analysis - // Will show type error at calling position - // - /** @param {number} a */ - function assertNever(a) {} - /** @param {string} a */ - function assertString(a) {} - -/* eslint-disable no-undef */ - -////////////////////////////////////////////////////////////////////// -// texvc.sty - -// The texvc package contains macros available in mediawiki pages. -// We omit the functions deprecated at -// https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax - -// We also omit texvc's \O, which conflicts with \text{\O} - -defineMacro("\\darr", "\\downarrow"); -defineMacro("\\dArr", "\\Downarrow"); -defineMacro("\\Darr", "\\Downarrow"); -defineMacro("\\lang", "\\langle"); -defineMacro("\\rang", "\\rangle"); -defineMacro("\\uarr", "\\uparrow"); -defineMacro("\\uArr", "\\Uparrow"); -defineMacro("\\Uarr", "\\Uparrow"); -defineMacro("\\N", "\\mathbb{N}"); -defineMacro("\\R", "\\mathbb{R}"); -defineMacro("\\Z", "\\mathbb{Z}"); -defineMacro("\\alef", "\\aleph"); -defineMacro("\\alefsym", "\\aleph"); -defineMacro("\\bull", "\\bullet"); -defineMacro("\\clubs", "\\clubsuit"); -defineMacro("\\cnums", "\\mathbb{C}"); -defineMacro("\\Complex", "\\mathbb{C}"); -defineMacro("\\Dagger", "\\ddagger"); -defineMacro("\\diamonds", "\\diamondsuit"); -defineMacro("\\empty", "\\emptyset"); -defineMacro("\\exist", "\\exists"); -defineMacro("\\harr", "\\leftrightarrow"); -defineMacro("\\hArr", "\\Leftrightarrow"); -defineMacro("\\Harr", "\\Leftrightarrow"); -defineMacro("\\hearts", "\\heartsuit"); -defineMacro("\\image", "\\Im"); -defineMacro("\\infin", "\\infty"); -defineMacro("\\isin", "\\in"); -defineMacro("\\larr", "\\leftarrow"); -defineMacro("\\lArr", "\\Leftarrow"); -defineMacro("\\Larr", "\\Leftarrow"); -defineMacro("\\lrarr", "\\leftrightarrow"); -defineMacro("\\lrArr", "\\Leftrightarrow"); -defineMacro("\\Lrarr", "\\Leftrightarrow"); -defineMacro("\\natnums", "\\mathbb{N}"); -defineMacro("\\plusmn", "\\pm"); -defineMacro("\\rarr", "\\rightarrow"); -defineMacro("\\rArr", "\\Rightarrow"); -defineMacro("\\Rarr", "\\Rightarrow"); -defineMacro("\\real", "\\Re"); -defineMacro("\\reals", "\\mathbb{R}"); -defineMacro("\\Reals", "\\mathbb{R}"); -defineMacro("\\sdot", "\\cdot"); -defineMacro("\\sect", "\\S"); -defineMacro("\\spades", "\\spadesuit"); -defineMacro("\\sub", "\\subset"); -defineMacro("\\sube", "\\subseteq"); -defineMacro("\\supe", "\\supseteq"); -defineMacro("\\thetasym", "\\vartheta"); -defineMacro("\\weierp", "\\wp"); - -/* eslint-disable no-undef */ - -/**************************************************** - * - * physics.js - * - * Implements the Physics Package for LaTeX input. - * - * --------------------------------------------------------------------- - * - * The original version of this file is licensed as follows: - * Copyright (c) 2015-2016 Kolen Cheung . - * - * 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. - * - * --------------------------------------------------------------------- - * - * This file has been revised from the original in the following ways: - * 1. The interface is changed so that it can be called from Temml, not MathJax. - * 2. \Re and \Im are not used, to avoid conflict with existing LaTeX letters. - * - * This revision of the file is released under the MIT license. - * https://mit-license.org/ - */ -defineMacro("\\quantity", "{\\left\\{ #1 \\right\\}}"); -defineMacro("\\qty", "{\\left\\{ #1 \\right\\}}"); -defineMacro("\\pqty", "{\\left( #1 \\right)}"); -defineMacro("\\bqty", "{\\left[ #1 \\right]}"); -defineMacro("\\vqty", "{\\left\\vert #1 \\right\\vert}"); -defineMacro("\\Bqty", "{\\left\\{ #1 \\right\\}}"); -defineMacro("\\absolutevalue", "{\\left\\vert #1 \\right\\vert}"); -defineMacro("\\abs", "{\\left\\vert #1 \\right\\vert}"); -defineMacro("\\norm", "{\\left\\Vert #1 \\right\\Vert}"); -defineMacro("\\evaluated", "{\\left.#1 \\right\\vert}"); -defineMacro("\\eval", "{\\left.#1 \\right\\vert}"); -defineMacro("\\order", "{\\mathcal{O} \\left( #1 \\right)}"); -defineMacro("\\commutator", "{\\left[ #1 , #2 \\right]}"); -defineMacro("\\comm", "{\\left[ #1 , #2 \\right]}"); -defineMacro("\\anticommutator", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\acomm", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\poissonbracket", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\pb", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\vectorbold", "{\\boldsymbol{ #1 }}"); -defineMacro("\\vb", "{\\boldsymbol{ #1 }}"); -defineMacro("\\vectorarrow", "{\\vec{\\boldsymbol{ #1 }}}"); -defineMacro("\\va", "{\\vec{\\boldsymbol{ #1 }}}"); -defineMacro("\\vectorunit", "{{\\boldsymbol{\\hat{ #1 }}}}"); -defineMacro("\\vu", "{{\\boldsymbol{\\hat{ #1 }}}}"); -defineMacro("\\dotproduct", "\\mathbin{\\boldsymbol\\cdot}"); -defineMacro("\\vdot", "{\\boldsymbol\\cdot}"); -defineMacro("\\crossproduct", "\\mathbin{\\boldsymbol\\times}"); -defineMacro("\\cross", "\\mathbin{\\boldsymbol\\times}"); -defineMacro("\\cp", "\\mathbin{\\boldsymbol\\times}"); -defineMacro("\\gradient", "{\\boldsymbol\\nabla}"); -defineMacro("\\grad", "{\\boldsymbol\\nabla}"); -defineMacro("\\divergence", "{\\grad\\vdot}"); -//defineMacro("\\div", "{\\grad\\vdot}"); Not included in Temml. Conflicts w/LaTeX \div -defineMacro("\\curl", "{\\grad\\cross}"); -defineMacro("\\laplacian", "\\nabla^2"); -defineMacro("\\tr", "{\\operatorname{tr}}"); -defineMacro("\\Tr", "{\\operatorname{Tr}}"); -defineMacro("\\rank", "{\\operatorname{rank}}"); -defineMacro("\\erf", "{\\operatorname{erf}}"); -defineMacro("\\Res", "{\\operatorname{Res}}"); -defineMacro("\\principalvalue", "{\\mathcal{P}}"); -defineMacro("\\pv", "{\\mathcal{P}}"); -defineMacro("\\PV", "{\\operatorname{P.V.}}"); -// Temml does not use the next two lines. They conflict with LaTeX letters. -//defineMacro("\\Re", "{\\operatorname{Re} \\left\\{ #1 \\right\\}}"); -//defineMacro("\\Im", "{\\operatorname{Im} \\left\\{ #1 \\right\\}}"); -defineMacro("\\qqtext", "{\\quad\\text{ #1 }\\quad}"); -defineMacro("\\qq", "{\\quad\\text{ #1 }\\quad}"); -defineMacro("\\qcomma", "{\\text{,}\\quad}"); -defineMacro("\\qc", "{\\text{,}\\quad}"); -defineMacro("\\qcc", "{\\quad\\text{c.c.}\\quad}"); -defineMacro("\\qif", "{\\quad\\text{if}\\quad}"); -defineMacro("\\qthen", "{\\quad\\text{then}\\quad}"); -defineMacro("\\qelse", "{\\quad\\text{else}\\quad}"); -defineMacro("\\qotherwise", "{\\quad\\text{otherwise}\\quad}"); -defineMacro("\\qunless", "{\\quad\\text{unless}\\quad}"); -defineMacro("\\qgiven", "{\\quad\\text{given}\\quad}"); -defineMacro("\\qusing", "{\\quad\\text{using}\\quad}"); -defineMacro("\\qassume", "{\\quad\\text{assume}\\quad}"); -defineMacro("\\qsince", "{\\quad\\text{since}\\quad}"); -defineMacro("\\qlet", "{\\quad\\text{let}\\quad}"); -defineMacro("\\qfor", "{\\quad\\text{for}\\quad}"); -defineMacro("\\qall", "{\\quad\\text{all}\\quad}"); -defineMacro("\\qeven", "{\\quad\\text{even}\\quad}"); -defineMacro("\\qodd", "{\\quad\\text{odd}\\quad}"); -defineMacro("\\qinteger", "{\\quad\\text{integer}\\quad}"); -defineMacro("\\qand", "{\\quad\\text{and}\\quad}"); -defineMacro("\\qor", "{\\quad\\text{or}\\quad}"); -defineMacro("\\qas", "{\\quad\\text{as}\\quad}"); -defineMacro("\\qin", "{\\quad\\text{in}\\quad}"); -defineMacro("\\differential", "{\\text{d}}"); -defineMacro("\\dd", "{\\text{d}}"); -defineMacro("\\derivative", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}"); -defineMacro("\\dv", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}"); -defineMacro("\\partialderivative", "{\\frac{\\partial{ #1 }}{\\partial{ #2 }}}"); -defineMacro("\\variation", "{\\delta}"); -defineMacro("\\var", "{\\delta}"); -defineMacro("\\functionalderivative", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}"); -defineMacro("\\fdv", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}"); -defineMacro("\\innerproduct", "{\\left\\langle {#1} \\mid { #2} \\right\\rangle}"); -defineMacro("\\outerproduct", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\dyad", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\ketbra", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\op", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\expectationvalue", "{\\left\\langle {#1 } \\right\\rangle}"); -defineMacro("\\expval", "{\\left\\langle {#1 } \\right\\rangle}"); -defineMacro("\\ev", "{\\left\\langle {#1 } \\right\\rangle}"); -defineMacro("\\matrixelement", - "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); -defineMacro("\\matrixel", - "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); -defineMacro("\\mel", - "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); - -// Helper functions -function getHLines(parser) { - // Return an array. The array length = number of hlines. - // Each element in the array tells if the line is dashed. - const hlineInfo = []; - parser.consumeSpaces(); - let nxt = parser.fetch().text; - if (nxt === "\\relax") { - parser.consume(); - parser.consumeSpaces(); - nxt = parser.fetch().text; - } - while (nxt === "\\hline" || nxt === "\\hdashline") { - parser.consume(); - hlineInfo.push(nxt === "\\hdashline"); - parser.consumeSpaces(); - nxt = parser.fetch().text; - } - return hlineInfo; -} - -const validateAmsEnvironmentContext = context => { - const settings = context.parser.settings; - if (!settings.displayMode) { - throw new ParseError(`{${context.envName}} can be used only in display mode.`); - } -}; - -const sizeRegEx$1 = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; -const arrayGaps = macros => { - let arraystretch = macros.get("\\arraystretch"); - if (typeof arraystretch !== "string") { - arraystretch = stringFromArg(arraystretch.tokens); - } - arraystretch = isNaN(arraystretch) ? null : Number(arraystretch); - let arraycolsepStr = macros.get("\\arraycolsep"); - if (typeof arraycolsepStr !== "string") { - arraycolsepStr = stringFromArg(arraycolsepStr.tokens); - } - const match = sizeRegEx$1.exec(arraycolsepStr); - const arraycolsep = match - ? { number: +(match[1] + match[2]), unit: match[3] } - : null; - return [arraystretch, arraycolsep] -}; - -const checkCellForLabels = cell => { - // Check if the author wrote a \tag{} inside this cell. - let rowLabel = ""; - for (let i = 0; i < cell.length; i++) { - if (cell[i].type === "label") { - if (rowLabel) { throw new ParseError(("Multiple \\labels in one row")) } - rowLabel = cell[i].string; - } - } - return rowLabel -}; - -// autoTag (an argument to parseArray) can be one of three values: -// * undefined: Regular (not-top-level) array; no tags on each row -// * true: Automatic equation numbering, overridable by \tag -// * false: Tags allowed on each row, but no automatic numbering -// This function *doesn't* work with the "split" environment name. -function getAutoTag(name) { - if (name.indexOf("ed") === -1) { - return name.indexOf("*") === -1; - } - // return undefined; -} - -/** - * Parse the body of the environment, with rows delimited by \\ and - * columns delimited by &, and create a nested list in row-major order - * with one group per cell. If given an optional argument scriptLevel - * ("text", "display", etc.), then each cell is cast into that scriptLevel. - */ -function parseArray( - parser, - { - cols, // [{ type: string , align: l|c|r|null }] - envClasses, // align(ed|at|edat) | array | cases | cd | small | multline - autoTag, // boolean - singleRow, // boolean - emptySingleRow, // boolean - maxNumCols, // number - leqno, // boolean - arraystretch, // number | null - arraycolsep // size value | null -}, - scriptLevel -) { - const endToken = envClasses && envClasses.includes("bordermatrix") ? "}" : "\\end"; - parser.gullet.beginGroup(); - if (!singleRow) { - // \cr is equivalent to \\ without the optional size argument (see below) - // TODO: provide helpful error when \cr is used outside array environment - parser.gullet.macros.set("\\cr", "\\\\\\relax"); - } - - // Start group for first cell - parser.gullet.beginGroup(); - - let row = []; - const body = [row]; - const rowGaps = []; - const labels = []; - - const hLinesBeforeRow = []; - - const tags = (autoTag != null ? [] : undefined); - - // amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent - // whether this row should have an equation number. Simulate this with - // a \@eqnsw macro set to 1 or 0. - function beginRow() { - if (autoTag) { - parser.gullet.macros.set("\\@eqnsw", "1", true); - } - } - function endRow() { - if (tags) { - if (parser.gullet.macros.get("\\df@tag")) { - tags.push(parser.subparse([new Token("\\df@tag")])); - parser.gullet.macros.set("\\df@tag", undefined, true); - } else { - tags.push(Boolean(autoTag) && - parser.gullet.macros.get("\\@eqnsw") === "1"); - } - } - } - beginRow(); - - // Test for \hline at the top of the array. - hLinesBeforeRow.push(getHLines(parser)); - - while (true) { - // Parse each cell in its own group (namespace) - let cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\"); - parser.gullet.endGroup(); - parser.gullet.beginGroup(); - - cell = { - type: "ordgroup", - mode: parser.mode, - body: cell, - semisimple: true - }; - row.push(cell); - const next = parser.fetch().text; - if (next === "&") { - if (maxNumCols && row.length === maxNumCols) { - if (envClasses.includes("array")) { - if (parser.settings.strict) { - throw new ParseError("Too few columns " + "specified in the {array} column argument.", - parser.nextToken) - } - } else if (maxNumCols === 2) { - throw new ParseError("The split environment accepts no more than two columns", - parser.nextToken); - } else { - throw new ParseError("The equation environment accepts only one column", - parser.nextToken) - } - } - parser.consume(); - } else if (next === endToken) { - endRow(); - // Arrays terminate newlines with `\crcr` which consumes a `\cr` if - // the last line is empty. However, AMS environments keep the - // empty row if it's the only one. - // NOTE: Currently, `cell` is the last item added into `row`. - if (row.length === 1 && cell.body.length === 0 && (body.length > 1 || !emptySingleRow)) { - body.pop(); - } - labels.push(checkCellForLabels(cell.body)); - if (hLinesBeforeRow.length < body.length + 1) { - hLinesBeforeRow.push([]); - } - break; - } else if (next === "\\\\") { - parser.consume(); - let size; - // \def\Let@{\let\\\math@cr} - // \def\math@cr{...\math@cr@} - // \def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}} - // \def\math@cr@@[#1]{...\math@cr@@@...} - // \def\math@cr@@@{\cr} - if (parser.gullet.future().text !== " ") { - size = parser.parseSizeGroup(true); - } - rowGaps.push(size ? size.value : null); - endRow(); - - labels.push(checkCellForLabels(cell.body)); - - // check for \hline(s) following the row separator - hLinesBeforeRow.push(getHLines(parser)); - - row = []; - body.push(row); - beginRow(); - } else { - throw new ParseError("Expected & or \\\\ or \\cr or " + endToken, parser.nextToken); - } - } - - // End cell group - parser.gullet.endGroup(); - // End array group defining \cr - parser.gullet.endGroup(); - - return { - type: "array", - mode: parser.mode, - body, - cols, - rowGaps, - hLinesBeforeRow, - envClasses, - autoTag, - scriptLevel, - tags, - labels, - leqno, - arraystretch, - arraycolsep - }; -} - -// Decides on a scriptLevel for cells in an array according to whether the given -// environment name starts with the letter 'd'. -function dCellStyle(envName) { - return envName.slice(0, 1) === "d" ? "display" : "text" -} - -const alignMap = { - c: "center ", - l: "left ", - r: "right " -}; - -const glue = group => { - const glueNode = new MathNode("mtd", []); - glueNode.style = { padding: "0", width: "50%" }; - if (group.envClasses.includes("multline")) { - glueNode.style.width = "7.5%"; - } - return glueNode -}; - -const mathmlBuilder$9 = function(group, style) { - const tbl = []; - const numRows = group.body.length; - const hlines = group.hLinesBeforeRow; - const tagIsPresent = (group.tags && group.tags.some((tag) => tag)); - - for (let i = 0; i < numRows; i++) { - const rw = group.body[i]; - const row = []; - const cellLevel = group.scriptLevel === "text" - ? StyleLevel.TEXT - : group.scriptLevel === "script" - ? StyleLevel.SCRIPT - : StyleLevel.DISPLAY; - - for (let j = 0; j < rw.length; j++) { - const mtd = new MathNode( - "mtd", - [buildGroup$1(rw[j], style.withLevel(cellLevel))] - ); - - if (group.envClasses.includes("multline")) { - const align = i === 0 ? "left" : i === numRows - 1 ? "right" : "center"; - if (align !== "center") { - mtd.classes.push("tml-" + align); - } - } - row.push(mtd); - } - const numColumns = group.body[0].length; - // Fill out a short row with empty elements. - for (let k = 0; k < numColumns - rw.length; k++) { - row.push(new MathNode("mtd", [], [], style)); - } - if (tagIsPresent) { - const tag = group.tags[i]; - let tagElement; - if (tag === true) { // automatic numbering - tagElement = new MathNode("mtext", [new Span(["tml-eqn"])]); - } else if (tag === false) { - // \nonumber/\notag or starred environment - tagElement = new MathNode("mtext", [], []); - } else { // manual \tag - tagElement = buildExpressionRow(tag[0].body, style.withLevel(cellLevel), true); - tagElement = consolidateText(tagElement); - tagElement.classes = ["tml-tag"]; - } - if (tagElement) { - row.unshift(glue(group)); - row.push(glue(group)); - if (group.leqno) { - row[0].children.push(tagElement); - } else { - row[row.length - 1].children.push(tagElement); - } - } - } - const mtr = new MathNode("mtr", row, []); - const label = group.labels.shift(); - if (label && group.tags && group.tags[i]) { - mtr.setAttribute("id", label); - if (Array.isArray(group.tags[i])) { mtr.classes.push("tml-tageqn"); } - } - - // Write horizontal rules - if (i === 0 && hlines[0].length > 0) { - if (hlines[0].length === 2) { - mtr.children.forEach(cell => { cell.style.borderTop = "0.15em double"; }); - } else { - mtr.children.forEach(cell => { - cell.style.borderTop = hlines[0][0] ? "0.06em dashed" : "0.06em solid"; - }); - } - } - if (hlines[i + 1].length > 0) { - if (hlines[i + 1].length === 2) { - mtr.children.forEach(cell => { cell.style.borderBottom = "0.15em double"; }); - } else { - mtr.children.forEach(cell => { - cell.style.borderBottom = hlines[i + 1][0] ? "0.06em dashed" : "0.06em solid"; - }); - } - } - - // Check for \hphantom \from \bordermatrix - let mustSquashRow = true; - for (let j = 0; j < mtr.children.length; j++) { - const child = mtr.children[j].children[0]; - if (!(child && child.type === "mpadded" && child.attributes.height === "0px")) { - mustSquashRow = false; - break - } - } - if (mustSquashRow) { - // All the cell contents are \hphantom. Squash the cell. - for (let j = 0; j < mtr.children.length; j++) { - mtr.children[j].style.display = "block"; // necessary in Firefox only - mtr.children[j].style.height = "0"; // necessary in Firefox only - mtr.children[j].style.paddingTop = "0"; - mtr.children[j].style.paddingBottom = "0"; - } - } - - tbl.push(mtr); - } - - if (group.arraystretch && group.arraystretch !== 1) { - // In LaTeX, \arraystretch is a factor applied to a 12pt strut height. - // It defines a baseline to baseline distance. - // Here, we do an approximation of that approach. - const pad = String(1.4 * group.arraystretch - 0.8) + "ex"; - for (let i = 0; i < tbl.length; i++) { - for (let j = 0; j < tbl[i].children.length; j++) { - tbl[i].children[j].style.paddingTop = pad; - tbl[i].children[j].style.paddingBottom = pad; - } - } - } - - let sidePadding; - let sidePadUnit; - if (group.envClasses.length > 0) { - sidePadding = group.envClasses.includes("abut") - ? "0" - : group.envClasses.includes("cases") - ? "0" - : group.envClasses.includes("small") - ? "0.1389" - : group.envClasses.includes("cd") - ? "0.25" - : "0.4"; // default side padding - sidePadUnit = "em"; - } - if (group.arraycolsep) { - const arraySidePad = calculateSize(group.arraycolsep, style); - sidePadding = arraySidePad.number.toFixed(4); - sidePadUnit = arraySidePad.unit; - } - if (sidePadding) { - const numCols = tbl.length === 0 ? 0 : tbl[0].children.length; - - const sidePad = (j, hand) => { - if (j === 0 && hand === 0) { return "0" } - if (j === numCols - 1 && hand === 1) { return "0" } - if (group.envClasses[0] !== "align") { return sidePadding } - if (hand === 1) { return "0" } - if (tagIsPresent) { - return (j % 2) ? "1" : "0" - } else { - return (j % 2) ? "0" : "1" - } - }; - - // Side padding - for (let i = 0; i < tbl.length; i++) { - for (let j = 0; j < tbl[i].children.length; j++) { - tbl[i].children[j].style.paddingLeft = `${sidePad(j, 0)}${sidePadUnit}`; - tbl[i].children[j].style.paddingRight = `${sidePad(j, 1)}${sidePadUnit}`; - } - } - } - if (group.envClasses.length === 0) { - // Set zero padding on side of the matrix - for (let i = 0; i < tbl.length; i++) { - tbl[i].children[0].style.paddingLeft = "0em"; - if (tbl[i].children.length === tbl[0].children.length) { - tbl[i].children[tbl[i].children.length - 1].style.paddingRight = "0em"; - } - } - } - - if (group.envClasses.length > 0) { - // Justification - const align = group.envClasses.includes("align") || group.envClasses.includes("alignat"); - for (let i = 0; i < tbl.length; i++) { - const row = tbl[i]; - if (align) { - for (let j = 0; j < row.children.length; j++) { - // Chromium does not recognize text-align: left. Use -webkit- - // TODO: Remove -webkit- when Chromium no longer needs it. - row.children[j].classes = ["tml-" + (j % 2 ? "left" : "right")]; - } - if (tagIsPresent) { - const k = group.leqno ? 0 : row.children.length - 1; - row.children[k].classes = []; // Default is center. - } - } - if (row.children.length > 1 && group.envClasses.includes("cases")) { - row.children[1].style.paddingLeft = "1em"; - } - - if (group.envClasses.includes("cases") || group.envClasses.includes("subarray")) { - for (const cell of row.children) { - cell.classes.push("tml-left"); - } - } - } - } - - let table = new MathNode("mtable", tbl); - if (group.envClasses.length > 0) { - // Top & bottom padding - if (group.envClasses.includes("jot")) { - table.classes.push("tml-jot"); - } else if (group.envClasses.includes("small")) { - table.classes.push("tml-small"); - } - } - if (group.scriptLevel === "display") { table.setAttribute("displaystyle", "true"); } - - if (group.autoTag || group.envClasses.includes("multline")) { - table.style.width = "100%"; - } - - // Column separator lines and column alignment - - if (group.cols && group.cols.length > 0) { - const cols = group.cols; - let prevTypeWasAlign = false; - let iStart = 0; - let iEnd = cols.length; - - while (cols[iStart].type === "separator") { - iStart += 1; - } - while (cols[iEnd - 1].type === "separator") { - iEnd -= 1; - } - - if (cols[0].type === "separator") { - const sep = cols[1].type === "separator" - ? "0.15em double" - : cols[0].separator === "|" - ? "0.06em solid " - : "0.06em dashed "; - for (const row of table.children) { - row.children[0].style.borderLeft = sep; - } - } - let iCol = tagIsPresent ? 0 : -1; - for (let i = iStart; i < iEnd; i++) { - if (cols[i].type === "align") { - const colAlign = alignMap[cols[i].align]; - iCol += 1; - for (const row of table.children) { - if (colAlign.trim() !== "center" && iCol < row.children.length) { - row.children[iCol].classes = ["tml-" + colAlign.trim()]; - } - } - prevTypeWasAlign = true; - } else if (cols[i].type === "separator") { - // MathML accepts only single lines between cells. - // So we read only the first of consecutive separators. - if (prevTypeWasAlign) { - const sep = cols[i + 1].type === "separator" - ? "0.15em double" - : cols[i].separator === "|" - ? "0.06em solid" - : "0.06em dashed"; - for (const row of table.children) { - if (iCol < row.children.length) { - row.children[iCol].style.borderRight = sep; - } - } - } - prevTypeWasAlign = false; - } - } - if (cols[cols.length - 1].type === "separator") { - const sep = cols[cols.length - 2].type === "separator" - ? "0.15em double" - : cols[cols.length - 1].separator === "|" - ? "0.06em solid" - : "0.06em dashed"; - for (const row of table.children) { - row.children[row.children.length - 1].style.borderRight = sep; - row.children[row.children.length - 1].style.paddingRight = "0.4em"; - } - } - } - - if (group.envClasses.includes("small")) { - // A small array. Wrap in scriptstyle. - table = new MathNode("mstyle", [table]); - table.setAttribute("scriptlevel", "1"); - } - - return table -}; - -// Convenience function for align, align*, aligned, alignat, alignat*, alignedat, split. -const alignedHandler = function(context, args) { - if (context.envName.indexOf("ed") === -1) { - validateAmsEnvironmentContext(context); - } - const isSplit = context.envName === "split"; - const cols = []; - const res = parseArray( - context.parser, - { - cols, - emptySingleRow: true, - autoTag: isSplit ? undefined : getAutoTag(context.envName), - envClasses: ["abut", "jot"], // set row spacing & provisional column spacing - maxNumCols: context.envName === "split" ? 2 : undefined, - leqno: context.parser.settings.leqno - }, - "display" - ); - - // Determining number of columns. - // 1. If the first argument is given, we use it as a number of columns, - // and makes sure that each row doesn't exceed that number. - // 2. Otherwise, just count number of columns = maximum number - // of cells in each row ("aligned" mode -- isAligned will be true). - // - // At the same time, prepend empty group {} at beginning of every second - // cell in each row (starting with second cell) so that operators become - // binary. This behavior is implemented in amsmath's \start@aligned. - let numMaths; - let numCols = 0; - const isAlignedAt = context.envName.indexOf("at") > -1; - if (args[0] && isAlignedAt) { - // alignat environment takes an argument w/ number of columns - let arg0 = ""; - for (let i = 0; i < args[0].body.length; i++) { - const textord = assertNodeType(args[0].body[i], "textord"); - arg0 += textord.text; - } - if (isNaN(arg0)) { - throw new ParseError("The alignat enviroment requires a numeric first argument.") - } - numMaths = Number(arg0); - numCols = numMaths * 2; - } - res.body.forEach(function(row) { - if (isAlignedAt) { - // Case 1 - const curMaths = row.length / 2; - if (numMaths < curMaths) { - throw new ParseError( - "Too many math in a row: " + `expected ${numMaths}, but got ${curMaths}`, - row[0] - ); - } - } else if (numCols < row.length) { - // Case 2 - numCols = row.length; - } - }); - - // Adjusting alignment. - // In aligned mode, we add one \qquad between columns; - // otherwise we add nothing. - for (let i = 0; i < numCols; ++i) { - let align = "r"; - if (i % 2 === 1) { - align = "l"; - } - cols[i] = { - type: "align", - align: align - }; - } - if (context.envName === "split") ; else if (isAlignedAt) { - res.envClasses.push("alignat"); // Sets justification - } else { - res.envClasses[0] = "align"; // Sets column spacing & justification - } - return res; -}; - -// Arrays are part of LaTeX, defined in lttab.dtx so its documentation -// is part of the source2e.pdf file of LaTeX2e source documentation. -// {darray} is an {array} environment where cells are set in \displaystyle, -// as defined in nccmath.sty. -defineEnvironment({ - type: "array", - names: ["array", "darray"], - props: { - numArgs: 1 - }, - handler(context, args) { - // Since no types are specified above, the two possibilities are - // - The argument is wrapped in {} or [], in which case Parser's - // parseGroup() returns an "ordgroup" wrapping some symbol node. - // - The argument is a bare symbol node. - const symNode = checkSymbolNodeType(args[0]); - const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; - const cols = colalign.map(function(nde) { - const node = assertSymbolNodeType(nde); - const ca = node.text; - if ("lcr".indexOf(ca) !== -1) { - return { - type: "align", - align: ca - }; - } else if (ca === "|") { - return { - type: "separator", - separator: "|" - }; - } else if (ca === ":") { - return { - type: "separator", - separator: ":" - }; - } - throw new ParseError("Unknown column alignment: " + ca, nde); - }); - const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); - const res = { - cols, - envClasses: ["array"], - maxNumCols: cols.length, - arraystretch, - arraycolsep - }; - return parseArray(context.parser, res, dCellStyle(context.envName)); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// The matrix environments of amsmath build on the array environment -// of LaTeX, which is discussed above. -// The mathtools package adds starred versions of the same environments. -// These have an optional argument to choose left|center|right justification. -defineEnvironment({ - type: "array", - names: [ - "matrix", - "pmatrix", - "bmatrix", - "Bmatrix", - "vmatrix", - "Vmatrix", - "matrix*", - "pmatrix*", - "bmatrix*", - "Bmatrix*", - "vmatrix*", - "Vmatrix*" - ], - props: { - numArgs: 0 - }, - handler(context) { - const delimiters = { - matrix: null, - pmatrix: ["(", ")"], - bmatrix: ["[", "]"], - Bmatrix: ["\\{", "\\}"], - vmatrix: ["|", "|"], - Vmatrix: ["\\Vert", "\\Vert"] - }[context.envName.replace("*", "")]; - // \hskip -\arraycolsep in amsmath - let colAlign = "c"; - const payload = { - envClasses: [], - cols: [] - }; - if (context.envName.charAt(context.envName.length - 1) === "*") { - // It's one of the mathtools starred functions. - // Parse the optional alignment argument. - const parser = context.parser; - parser.consumeSpaces(); - if (parser.fetch().text === "[") { - parser.consume(); - parser.consumeSpaces(); - colAlign = parser.fetch().text; - if ("lcr".indexOf(colAlign) === -1) { - throw new ParseError("Expected l or c or r", parser.nextToken); - } - parser.consume(); - parser.consumeSpaces(); - parser.expect("]"); - parser.consume(); - payload.cols = []; - } - } - const res = parseArray(context.parser, payload, "text"); - res.cols = res.body.length > 0 - ? new Array(res.body[0].length).fill({ type: "align", align: colAlign }) - : []; - const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); - res.arraystretch = arraystretch; - if (arraycolsep && !(arraycolsep === 6 && arraycolsep === "pt")) { - res.arraycolsep = arraycolsep; - } - return delimiters - ? { - type: "leftright", - mode: context.mode, - body: [res], - left: delimiters[0], - right: delimiters[1], - rightColor: undefined // \right uninfluenced by \color in array - } - : res; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["bordermatrix"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { cols: [], envClasses: ["bordermatrix"] }; - const res = parseArray(context.parser, payload, "text"); - res.cols = res.body.length > 0 - ? new Array(res.body[0].length).fill({ type: "align", align: "c" }) - : []; - res.envClasses = []; - res.arraystretch = 1; - if (context.envName === "matrix") { return res} - return bordermatrixParseTree(res, context.delimiters) - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["smallmatrix"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { envClasses: ["small"] }; - const res = parseArray(context.parser, payload, "script"); - return res; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["subarray"], - props: { - numArgs: 1 - }, - handler(context, args) { - // Parsing of {subarray} is similar to {array} - const symNode = checkSymbolNodeType(args[0]); - const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; - const cols = colalign.map(function(nde) { - const node = assertSymbolNodeType(nde); - const ca = node.text; - // {subarray} only recognizes "l" & "c" - if ("lc".indexOf(ca) !== -1) { - return { - type: "align", - align: ca - }; - } - throw new ParseError("Unknown column alignment: " + ca, nde); - }); - if (cols.length > 1) { - throw new ParseError("{subarray} can contain only one column"); - } - let res = { - cols, - envClasses: ["small"] - }; - res = parseArray(context.parser, res, "script"); - if (res.body.length > 0 && res.body[0].length > 1) { - throw new ParseError("{subarray} can contain only one column"); - } - return res; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// A cases environment (in amsmath.sty) is almost equivalent to -// \def -// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. -// {dcases} is a {cases} environment where cells are set in \displaystyle, -// as defined in mathtools.sty. -// {rcases} is another mathtools environment. It's brace is on the right side. -defineEnvironment({ - type: "array", - names: ["cases", "dcases", "rcases", "drcases"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { - cols: [], - envClasses: ["cases"] - }; - const res = parseArray(context.parser, payload, dCellStyle(context.envName)); - return { - type: "leftright", - mode: context.mode, - body: [res], - left: context.envName.indexOf("r") > -1 ? "." : "\\{", - right: context.envName.indexOf("r") > -1 ? "\\}" : ".", - rightColor: undefined - }; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// In the align environment, one uses ampersands, &, to specify number of -// columns in each row, and to locate spacing between each column. -// align gets automatic numbering. align* and aligned do not. -// The alignedat environment can be used in math mode. -defineEnvironment({ - type: "array", - names: ["align", "align*", "aligned", "split"], - props: { - numArgs: 0 - }, - handler: alignedHandler, - mathmlBuilder: mathmlBuilder$9 -}); - -// alignat environment is like an align environment, but one must explicitly -// specify maximum number of columns in each row, and can adjust where spacing occurs. -defineEnvironment({ - type: "array", - names: ["alignat", "alignat*", "alignedat"], - props: { - numArgs: 1 - }, - handler: alignedHandler, - mathmlBuilder: mathmlBuilder$9 -}); - -// A gathered environment is like an array environment with one centered -// column, but where rows are considered lines so get \jot line spacing -// and contents are set in \displaystyle. -defineEnvironment({ - type: "array", - names: ["gathered", "gather", "gather*"], - props: { - numArgs: 0 - }, - handler(context) { - if (context.envName !== "gathered") { - validateAmsEnvironmentContext(context); - } - const res = { - cols: [], - envClasses: ["abut", "jot"], - autoTag: getAutoTag(context.envName), - emptySingleRow: true, - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["equation", "equation*"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - const res = { - autoTag: getAutoTag(context.envName), - emptySingleRow: true, - singleRow: true, - maxNumCols: 1, - envClasses: ["align"], - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["multline", "multline*"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - const res = { - autoTag: context.envName === "multline", - maxNumCols: 1, - envClasses: ["jot", "multline"], - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["CD"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - return parseCD(context.parser); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// Catch \hline outside array environment -defineFunction({ - type: "text", // Doesn't matter what this is. - names: ["\\hline", "\\hdashline"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: true - }, - handler(context, args) { - throw new ParseError(`${context.funcName} valid only within array environment`); - } -}); - -const environments = _environments; - -// \bordermatrix from TeXbook pp 177 & 361 -// Optional argument from Herbert Voß, Math mode, p 20 -// Ref: https://tug.ctan.org/obsolete/info/math/voss/mathmode/Mathmode.pdf - -defineFunction({ - type: "bordermatrix", - names: ["\\bordermatrix", "\\matrix"], - props: { - numArgs: 0, - numOptionalArgs: 1 - }, - handler: ({ parser, funcName }, args, optArgs) => { - // Find out if the author has defined custom delimiters - let delimiters = ["(", ")"]; - if (funcName === "\\bordermatrix" && optArgs[0] && optArgs[0].body) { - const body = optArgs[0].body; - if (body.length === 2 && body[0].type === "atom" && body[1].type === "atom") { - if (body[0].family === "open" && body[1].family === "close") { - delimiters = [body[0].text, body[1].text]; - } - } - } - // consume the opening brace - parser.consumeSpaces(); - parser.consume(); - - // Pass control to the environment handler in array.js. - const env = environments["bordermatrix"]; - const context = { - mode: parser.mode, - envName: funcName.slice(1), - delimiters, - parser - }; - const result = env.handler(context); - parser.expect("}", true); - return result - } -}); - -defineFunction({ - type: "cancelto", - names: ["\\cancelto"], - props: { - numArgs: 2 - }, - handler({ parser }, args) { - const to = args[0]; - const body = args[1]; - return { - type: "cancelto", - mode: parser.mode, - body, - to, - isCharacterBox: isCharacterBox(body) - }; - }, - mathmlBuilder(group, style) { - const fromNode = new MathNode( - "mrow", - [buildGroup$1(group.body, style)], - ["ff-narrow"] // A zero-width mrow. - ); - // Write the arrow in a node written after the content. - // That way, the arrow will be an overlay on the content. - const phantom = new MathNode("mphantom", [buildGroup$1(group.body, style)]); - const arrow = new MathNode("mrow", [phantom], ["tml-cancelto"]); - arrow.style.color = style.color; - if (group.isCharacterBox && smalls.indexOf(group.body.body[0].text) > -1) { - arrow.style.left = "0.1em"; - arrow.style.width = "90%"; - } - const node = new MathNode("mrow", [fromNode, arrow], ["menclose"]); - if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) { - // Add 0.2em space to right of content to make room for the arrowhead. - phantom.style.paddingRight = "0.2em"; - } else { - phantom.style.padding = "0.5ex 0.1em 0 0"; - const strut = new MathNode('mspace', []); - strut.setAttribute('height', "0.85em"); - fromNode.children.push(strut); - } - - // Create the "to" value above and to the right of the arrow. - // First, we want a dummy node with the same height as the `from` content. - // We'll place the `to` node above the dummy to get the correct vertical alignment. - let dummyNode; - if (group.isCharacterBox) { - dummyNode = new MathNode('mspace', []); - dummyNode.setAttribute('height', "1em"); - } else { - // Create a phantom node with the same content as the body. - const inner = buildGroup$1(group.body, style); - // The phantom node will be zero-width, so it won't affect horizontal spacing. - const zeroWidthNode = new MathNode("mpadded", [inner]); - zeroWidthNode.setAttribute("width", "0.1px"); // Don't use 0. WebKit would omit it. - dummyNode = new MathNode("mphantom", [zeroWidthNode]); // Hide it. - } - const toNode = buildGroup$1(group.to, style); - toNode.style.color = style.color; - const zeroWidthToNode = new MathNode("mpadded", [toNode]); - if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) { - const w = new MathNode("mspace", []); - w.setAttribute('width', "0.2em"); - zeroWidthToNode.children.unshift(w); - } - zeroWidthToNode.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - const mover = new MathNode("mover", [dummyNode, zeroWidthToNode]); - // Fix Firefox positioning. - const nudgeLeft = new MathNode('mrow', [], ["ff-nudge-left"]); - return newDocumentFragment([makeRow([node, mover]), nudgeLeft]) - } -}); - -// \@char is an internal function that takes a grouped decimal argument like -// {123} and converts into symbol with code 123. It is used by the *macro* -// \char defined in macros.js. -defineFunction({ - type: "textord", - names: ["\\@char"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser, token }, args) { - const arg = assertNodeType(args[0], "ordgroup"); - const group = arg.body; - let number = ""; - for (let i = 0; i < group.length; i++) { - const node = assertNodeType(group[i], "textord"); - number += node.text; - } - const code = parseInt(number); - if (isNaN(code)) { - throw new ParseError(`\\@char has non-numeric argument ${number}`, token) - } - return { - type: "textord", - mode: parser.mode, - text: String.fromCodePoint(code) - } - } -}); - -// Helpers -const htmlRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6})$/i; -const htmlOrNameRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i; -const RGBregEx = /^ *\d{1,3} *(?:, *\d{1,3} *){2}$/; -const rgbRegEx = /^ *[10](?:\.\d*)? *(?:, *[10](?:\.\d*)? *){2}$/; -const xcolorHtmlRegEx = /^[a-f0-9]{6}$/i; -const toHex = num => { - let str = num.toString(16); - if (str.length === 1) { str = "0" + str; } - return str -}; - -// Colors from Tables 4.1 and 4.2 of the xcolor package. -// Table 4.1 (lower case) RGB values are taken from chroma and xcolor.dtx. -// Table 4.2 (Capitalized) values were sampled, because Chroma contains a unreliable -// conversion from cmyk to RGB. See https://tex.stackexchange.com/a/537274. -const xcolors = JSON.parse(`{ - "Apricot": "#ffb484", - "Aquamarine": "#08b4bc", - "Bittersweet": "#c84c14", - "blue": "#0000FF", - "Blue": "#303494", - "BlueGreen": "#08b4bc", - "BlueViolet": "#503c94", - "BrickRed": "#b8341c", - "brown": "#BF8040", - "Brown": "#802404", - "BurntOrange": "#f8941c", - "CadetBlue": "#78749c", - "CarnationPink": "#f884b4", - "Cerulean": "#08a4e4", - "CornflowerBlue": "#40ace4", - "cyan": "#00FFFF", - "Cyan": "#08acec", - "Dandelion": "#ffbc44", - "darkgray": "#404040", - "DarkOrchid": "#a8548c", - "Emerald": "#08ac9c", - "ForestGreen": "#089c54", - "Fuchsia": "#90348c", - "Goldenrod": "#ffdc44", - "gray": "#808080", - "Gray": "#98949c", - "green": "#00FF00", - "Green": "#08a44c", - "GreenYellow": "#e0e474", - "JungleGreen": "#08ac9c", - "Lavender": "#f89cc4", - "lightgray": "#c0c0c0", - "lime": "#BFFF00", - "LimeGreen": "#90c43c", - "magenta": "#FF00FF", - "Magenta": "#f0048c", - "Mahogany": "#b0341c", - "Maroon": "#b03434", - "Melon": "#f89c7c", - "MidnightBlue": "#086494", - "Mulberry": "#b03c94", - "NavyBlue": "#086cbc", - "olive": "#7F7F00", - "OliveGreen": "#407c34", - "orange": "#FF8000", - "Orange": "#f8843c", - "OrangeRed": "#f0145c", - "Orchid": "#b074ac", - "Peach": "#f8945c", - "Periwinkle": "#8074bc", - "PineGreen": "#088c74", - "pink": "#ff7f7f", - "Plum": "#98248c", - "ProcessBlue": "#08b4ec", - "purple": "#BF0040", - "Purple": "#a0449c", - "RawSienna": "#983c04", - "red": "#ff0000", - "Red": "#f01c24", - "RedOrange": "#f86434", - "RedViolet": "#a0246c", - "Rhodamine": "#f0549c", - "Royallue": "#0874bc", - "RoyalPurple": "#683c9c", - "RubineRed": "#f0047c", - "Salmon": "#f8948c", - "SeaGreen": "#30bc9c", - "Sepia": "#701404", - "SkyBlue": "#48c4dc", - "SpringGreen": "#c8dc64", - "Tan": "#e09c74", - "teal": "#007F7F", - "TealBlue": "#08acb4", - "Thistle": "#d884b4", - "Turquoise": "#08b4cc", - "violet": "#800080", - "Violet": "#60449c", - "VioletRed": "#f054a4", - "WildStrawberry": "#f0246c", - "yellow": "#FFFF00", - "Yellow": "#fff404", - "YellowGreen": "#98cc6c", - "YellowOrange": "#ffa41c" -}`); - -const colorFromSpec = (model, spec) => { - let color = ""; - if (model === "HTML") { - if (!htmlRegEx.test(spec)) { - throw new ParseError("Invalid HTML input.") - } - color = spec; - } else if (model === "RGB") { - if (!RGBregEx.test(spec)) { - throw new ParseError("Invalid RGB input.") - } - spec.split(",").map(e => { color += toHex(Number(e.trim())); }); - } else { - if (!rgbRegEx.test(spec)) { - throw new ParseError("Invalid rbg input.") - } - spec.split(",").map(e => { - const num = Number(e.trim()); - if (num > 1) { throw new ParseError("Color rgb input must be < 1.") } - color += toHex(Number((num * 255).toFixed(0))); - }); - } - if (color.charAt(0) !== "#") { color = "#" + color; } - return color -}; - -const validateColor = (color, macros, token) => { - const macroName = `\\\\color@${color}`; // from \defineColor. - const match = htmlOrNameRegEx.exec(color); - if (!match) { throw new ParseError("Invalid color: '" + color + "'", token) } - // We allow a 6-digit HTML color spec without a leading "#". - // This follows the xcolor package's HTML color model. - // Predefined color names are all missed by this RegEx pattern. - if (xcolorHtmlRegEx.test(color)) { - return "#" + color - } else if (color.charAt(0) === "#") { - return color - } else if (macros.has(macroName)) { - color = macros.get(macroName).tokens[0].text; - } else if (xcolors[color]) { - color = xcolors[color]; - } - return color -}; - -const mathmlBuilder$8 = (group, style) => { - // In LaTeX, color is not supposed to change the spacing of any node. - // So instead of wrapping the group in an , we apply - // the color individually to each node and return a document fragment. - let expr = buildExpression(group.body, style.withColor(group.color)); - if (expr.length === 0) { - expr.push(new MathNode("mrow")); - } - expr = expr.map(e => { - e.style.color = group.color; - return e - }); - return newDocumentFragment(expr) -}; - -defineFunction({ - type: "color", - names: ["\\textcolor"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "original"] - }, - handler({ parser, token }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); - } - const body = args[1]; - return { - type: "color", - mode: parser.mode, - color, - isTextColor: true, - body: ordargument(body) - } - }, - mathmlBuilder: mathmlBuilder$8 -}); - -defineFunction({ - type: "color", - names: ["\\color"], - props: { - numArgs: 1, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw"] - }, - handler({ parser, breakOnTokenText, token }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); - } - - // Parse out the implicit body that should be colored. - const body = parser.parseExpression(true, breakOnTokenText, true); - - return { - type: "color", - mode: parser.mode, - color, - isTextColor: false, - body - } - }, - mathmlBuilder: mathmlBuilder$8 -}); - -defineFunction({ - type: "color", - names: ["\\definecolor"], - props: { - numArgs: 3, - allowedInText: true, - argTypes: ["raw", "raw", "raw"] - }, - handler({ parser, funcName, token }, args) { - const name = assertNodeType(args[0], "raw").string; - if (!/^[A-Za-z]+$/.test(name)) { - throw new ParseError("Color name must be latin letters.", token) - } - const model = assertNodeType(args[1], "raw").string; - if (!["HTML", "RGB", "rgb"].includes(model)) { - throw new ParseError("Color model must be HTML, RGB, or rgb.", token) - } - const spec = assertNodeType(args[2], "raw").string; - const color = colorFromSpec(model, spec); - parser.gullet.macros.set(`\\\\color@${name}`, { tokens: [{ text: color }], numArgs: 0 }); - return { type: "internal", mode: parser.mode } - } - // No mathmlBuilder. The point of \definecolor is to set a macro. -}); - -// Row breaks within tabular environments, and line breaks at top level - - -// \DeclareRobustCommand\\{...\@xnewline} -defineFunction({ - type: "cr", - names: ["\\\\"], - props: { - numArgs: 0, - numOptionalArgs: 0, - allowedInText: true - }, - - handler({ parser }, args, optArgs) { - const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null; - const newLine = !parser.settings.displayMode; - return { - type: "cr", - mode: parser.mode, - newLine, - size: size && assertNodeType(size, "size").value - } - }, - - // The following builder is called only at the top level, - // not within tabular/array environments. - - mathmlBuilder(group, style) { - // MathML 3.0 calls for newline to occur in an or an . - // Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.linebreaking - const node = new MathNode("mo"); - if (group.newLine) { - node.setAttribute("linebreak", "newline"); - if (group.size) { - const size = calculateSize(group.size, style); - node.setAttribute("height", size.number + size.unit); - } - } - return node - } -}); - -const globalMap = { - "\\global": "\\global", - "\\long": "\\\\globallong", - "\\\\globallong": "\\\\globallong", - "\\def": "\\gdef", - "\\gdef": "\\gdef", - "\\edef": "\\xdef", - "\\xdef": "\\xdef", - "\\let": "\\\\globallet", - "\\futurelet": "\\\\globalfuture" -}; - -const checkControlSequence = (tok) => { - const name = tok.text; - if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { - throw new ParseError("Expected a control sequence", tok); - } - return name; -}; - -const getRHS = (parser) => { - let tok = parser.gullet.popToken(); - if (tok.text === "=") { - // consume optional equals - tok = parser.gullet.popToken(); - if (tok.text === " ") { - // consume one optional space - tok = parser.gullet.popToken(); - } - } - return tok; -}; - -const letCommand = (parser, name, tok, global) => { - let macro = parser.gullet.macros.get(tok.text); - if (macro == null) { - // don't expand it later even if a macro with the same name is defined - // e.g., \let\foo=\frac \def\frac{\relax} \frac12 - tok.noexpand = true; - macro = { - tokens: [tok], - numArgs: 0, - // reproduce the same behavior in expansion - unexpandable: !parser.gullet.isExpandable(tok.text) - }; - } - parser.gullet.macros.set(name, macro, global); -}; - -// -> | -// -> |\global -// -> | -// -> \global|\long|\outer -defineFunction({ - type: "internal", - names: [ - "\\global", - "\\long", - "\\\\globallong" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true - }, - handler({ parser, funcName }) { - parser.consumeSpaces(); - const token = parser.fetch(); - if (globalMap[token.text]) { - // Temml doesn't have \par, so ignore \long - if (funcName === "\\global" || funcName === "\\\\globallong") { - token.text = globalMap[token.text]; - } - return assertNodeType(parser.parseFunction(), "internal"); - } - throw new ParseError(`Invalid token after macro prefix`, token); - } -}); - -// Basic support for macro definitions: \def, \gdef, \edef, \xdef -// -> -// -> \def|\gdef|\edef|\xdef -// -> -defineFunction({ - type: "internal", - names: ["\\def", "\\gdef", "\\edef", "\\xdef"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - let tok = parser.gullet.popToken(); - const name = tok.text; - if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { - throw new ParseError("Expected a control sequence", tok); - } - - let numArgs = 0; - let insert; - const delimiters = [[]]; - // contains no braces - while (parser.gullet.future().text !== "{") { - tok = parser.gullet.popToken(); - if (tok.text === "#") { - // If the very last character of the is #, so that - // this # is immediately followed by {, TeX will behave as if the { - // had been inserted at the right end of both the parameter text - // and the replacement text. - if (parser.gullet.future().text === "{") { - insert = parser.gullet.future(); - delimiters[numArgs].push("{"); - break; - } - - // A parameter, the first appearance of # must be followed by 1, - // the next by 2, and so on; up to nine #’s are allowed - tok = parser.gullet.popToken(); - if (!/^[1-9]$/.test(tok.text)) { - throw new ParseError(`Invalid argument number "${tok.text}"`); - } - if (parseInt(tok.text) !== numArgs + 1) { - throw new ParseError(`Argument number "${tok.text}" out of order`); - } - numArgs++; - delimiters.push([]); - } else if (tok.text === "EOF") { - throw new ParseError("Expected a macro definition"); - } else { - delimiters[numArgs].push(tok.text); - } - } - // replacement text, enclosed in '{' and '}' and properly nested - let { tokens } = parser.gullet.consumeArg(); - if (insert) { - tokens.unshift(insert); - } - - if (funcName === "\\edef" || funcName === "\\xdef") { - tokens = parser.gullet.expandTokens(tokens); - if (tokens.length > parser.gullet.settings.maxExpand) { - throw new ParseError("Too many expansions in an " + funcName); - } - tokens.reverse(); // to fit in with stack order - } - // Final arg is the expansion of the macro - parser.gullet.macros.set( - name, - { tokens, numArgs, delimiters }, - funcName === globalMap[funcName] - ); - return { type: "internal", mode: parser.mode }; - } -}); - -// -> -// -> \futurelet -// | \let -// -> |= -defineFunction({ - type: "internal", - names: [ - "\\let", - "\\\\globallet" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - const name = checkControlSequence(parser.gullet.popToken()); - parser.gullet.consumeSpaces(); - const tok = getRHS(parser); - letCommand(parser, name, tok, funcName === "\\\\globallet"); - return { type: "internal", mode: parser.mode }; - } -}); - -// ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf -defineFunction({ - type: "internal", - names: [ - "\\futurelet", - "\\\\globalfuture" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - const name = checkControlSequence(parser.gullet.popToken()); - const middle = parser.gullet.popToken(); - const tok = parser.gullet.popToken(); - letCommand(parser, name, tok, funcName === "\\\\globalfuture"); - parser.gullet.pushToken(tok); - parser.gullet.pushToken(middle); - return { type: "internal", mode: parser.mode }; - } -}); - -defineFunction({ - type: "internal", - names: ["\\newcommand", "\\renewcommand", "\\providecommand"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - let name = ""; - const tok = parser.gullet.popToken(); - if (tok.text === "{") { - name = checkControlSequence(parser.gullet.popToken()); - parser.gullet.popToken(); - } else { - name = checkControlSequence(tok); - } - - const exists = parser.gullet.isDefined(name); - if (exists && funcName === "\\newcommand") { - throw new ParseError( - `\\newcommand{${name}} attempting to redefine ${name}; use \\renewcommand` - ); - } - if (!exists && funcName === "\\renewcommand") { - throw new ParseError( - `\\renewcommand{${name}} when command ${name} does not yet exist; use \\newcommand` - ); - } - - let numArgs = 0; - if (parser.gullet.future().text === "[") { - let tok = parser.gullet.popToken(); - tok = parser.gullet.popToken(); - if (!/^[0-9]$/.test(tok.text)) { - throw new ParseError(`Invalid number of arguments: "${tok.text}"`); - } - numArgs = parseInt(tok.text); - tok = parser.gullet.popToken(); - if (tok.text !== "]") { - throw new ParseError(`Invalid argument "${tok.text}"`); - } - } - - // replacement text, enclosed in '{' and '}' and properly nested - const { tokens } = parser.gullet.consumeArg(); - - if (!(funcName === "\\providecommand" && parser.gullet.macros.has(name))) { - // Ignore \providecommand - parser.gullet.macros.set( - name, - { tokens, numArgs } - ); - } - - return { type: "internal", mode: parser.mode }; - - } -}); - -// Extra data needed for the delimiter handler down below -const delimiterSizes = { - "\\bigl": { mclass: "mopen", size: 1 }, - "\\Bigl": { mclass: "mopen", size: 2 }, - "\\biggl": { mclass: "mopen", size: 3 }, - "\\Biggl": { mclass: "mopen", size: 4 }, - "\\bigr": { mclass: "mclose", size: 1 }, - "\\Bigr": { mclass: "mclose", size: 2 }, - "\\biggr": { mclass: "mclose", size: 3 }, - "\\Biggr": { mclass: "mclose", size: 4 }, - "\\bigm": { mclass: "mrel", size: 1 }, - "\\Bigm": { mclass: "mrel", size: 2 }, - "\\biggm": { mclass: "mrel", size: 3 }, - "\\Biggm": { mclass: "mrel", size: 4 }, - "\\big": { mclass: "mord", size: 1 }, - "\\Big": { mclass: "mord", size: 2 }, - "\\bigg": { mclass: "mord", size: 3 }, - "\\Bigg": { mclass: "mord", size: 4 } -}; - -const leftToRight = { - "(": ")", - "\\lparen": "\\rparen", - "[": "]", - "\\lbrack": "\\rbrack", - "\\{": "\\}", - "\\lbrace": "\\rbrace", - "⦇": "⦈", - "\\llparenthesis": "\\rrparenthesis", - "\\lfloor": "\\rfloor", - "\u230a": "\u230b", - "\\lceil": "\\rceil", - "\u2308": "\u2309", - "\\langle": "\\rangle", - "\u27e8": "\u27e9", - "\\lAngle": "\\rAngle", - "\u27ea": "\u27eb", - "\\llangle": "\\rrangle", - "⦉": "⦊", - "\\lvert": "\\rvert", - "\\lVert": "\\rVert", - "\\lgroup": "\\rgroup", - "\u27ee": "\u27ef", - "\\lmoustache": "\\rmoustache", - "\u23b0": "\u23b1", - "\\llbracket": "\\rrbracket", - "\u27e6": "\u27e7", - "\\lBrace": "\\rBrace", - "\u2983": "\u2984" -}; - -const leftDelimiterNames = new Set(Object.keys(leftToRight)); -new Set(Object.values(leftToRight)); - -const delimiters = new Set([ - "(", - "\\lparen", - ")", - "\\rparen", - "[", - "\\lbrack", - "]", - "\\rbrack", - "\\{", - "\\lbrace", - "\\}", - "\\rbrace", - "⦇", - "\\llparenthesis", - "⦈", - "\\rrparenthesis", - "\\lfloor", - "\\rfloor", - "\u230a", - "\u230b", - "\\lceil", - "\\rceil", - "\u2308", - "\u2309", - "<", - ">", - "\\langle", - "\u27e8", - "\\rangle", - "\u27e9", - "\\lAngle", - "\u27ea", - "\\rAngle", - "\u27eb", - "\\llangle", - "⦉", - "\\rrangle", - "⦊", - "\\lt", - "\\gt", - "\\lvert", - "\\rvert", - "\\lVert", - "\\rVert", - "\\lgroup", - "\\rgroup", - "\u27ee", - "\u27ef", - "\\lmoustache", - "\\rmoustache", - "\u23b0", - "\u23b1", - "\\llbracket", - "\\rrbracket", - "\u27e6", - "\u27e7", - "\\lBrace", - "\\rBrace", - "\u2983", - "\u2984", - "/", - "\\backslash", - "|", - "\\vert", - "\\|", - "\\Vert", - "\u2016", - "\\uparrow", - "\\Uparrow", - "\\downarrow", - "\\Downarrow", - "\\updownarrow", - "\\Updownarrow", - "." -]); - -// Export isDelimiter for benefit of parser. -const dels = new Set(["}", "\\left", "\\middle", "\\right"]); -const isDelimiter = str => str.length > 0 && - (delimiters.has(str) || delimiterSizes[str] || dels.has(str)); - -// Metrics of the different sizes. Found by looking at TeX's output of -// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$ -// Used to create stacked delimiters of appropriate sizes in makeSizedDelim. -const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; - -// Delimiter functions -function checkDelimiter(delim, context) { - if (delim.type === "ordgroup" && delim.body.length === 1) { - delim = delim.body[0]; // Unwrap the braces - } - const symDelim = checkSymbolNodeType(delim); - if (symDelim && delimiters.has(symDelim.text)) { - // If a character is not in the MathML operator dictionary, it will not stretch. - // Replace such characters w/characters that will stretch. - if (symDelim.text === "<" || symDelim.text === "\\lt") { symDelim.text = "⟨"; } - if (symDelim.text === ">" || symDelim.text === "\\gt") { symDelim.text = "⟩"; } - return symDelim; - } else if (symDelim) { - throw new ParseError(`Invalid delimiter '${symDelim.text}' after '${context.funcName}'`, delim); - } else { - throw new ParseError(`Invalid delimiter type '${delim.type}'`, delim); - } -} - -// / \ -const needExplicitStretch = new Set(["\u002F", "\u005C", "\\backslash", "\u2216", "\\vert", "|"]); - -const makeFenceMo = (delim, mode, form, isStretchy) => { - const text = delim === "." ? "" : delim; - const node = new MathNode("mo", [makeText(text, mode)]); - node.setAttribute("fence", "true"); - node.setAttribute("form", form); - node.setAttribute("stretchy", isStretchy ? "true" : "false"); - return node; -}; - -defineFunction({ - type: "delimsizing", - names: [ - "\\bigl", - "\\Bigl", - "\\biggl", - "\\Biggl", - "\\bigr", - "\\Bigr", - "\\biggr", - "\\Biggr", - "\\bigm", - "\\Bigm", - "\\biggm", - "\\Biggm", - "\\big", - "\\Big", - "\\bigg", - "\\Bigg" - ], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - - const delimNode = { - type: "delimsizing", - mode: context.parser.mode, - size: delimiterSizes[context.funcName].size, - mclass: delimiterSizes[context.funcName].mclass, - delim: delim.text - }; - const nextToken = context.parser.fetch().text; - if (nextToken !== "^" && nextToken !== "_") { - return delimNode - } else { - // Chromium mis-renders a sized delim if it is the base of a supsub. - // So wrap it in a ordgroup. - return { - type: "ordgroup", - mode: "math", - body: [delimNode, { type: "ordgroup", mode: "math", body: [] }] - } - } - }, - mathmlBuilder: (group) => { - const children = []; - const delim = group.delim === "." ? "" : group.delim; - - children.push(makeText(delim, group.mode)); - - const node = new MathNode("mo", children); - - if (group.mclass === "mopen" || group.mclass === "mclose") { - // Only some of the delimsizing functions act as fences, and they - // return "mopen" or "mclose" mclass. - node.setAttribute("fence", "true"); - } else { - // Explicitly disable fencing if it's not a fence, to override the - // defaults. - node.setAttribute("fence", "false"); - } - if (needExplicitStretch.has(delim) || delim.indexOf("arrow") > -1) { - // We have to explicitly set stretchy to true. - node.setAttribute("stretchy", "true"); - } - node.setAttribute("symmetric", "true"); // Needed for tall arrows in Firefox. - node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em"); - node.setAttribute("maxsize", sizeToMaxHeight[group.size] + "em"); - return node; - } -}); - -function assertParsed(group) { - if (!group.body) { - throw new Error("Bug: The delim ParseNode wasn't fully parsed."); - } -} - -defineFunction({ - type: "leftright-right", - names: ["\\right"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - return { - type: "leftright-right", - mode: context.parser.mode, - delim: checkDelimiter(args[0], context).text - }; - } -}); - -defineFunction({ - type: "leftright", - names: ["\\left"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - - const parser = context.parser; - ++parser.leftrightDepth; - let body = parser.parseExpression(false, "\\right", true); - let nextToken = parser.fetch(); - while (nextToken.text === "\\middle") { - parser.consume(); - const middle = parser.fetch().text; - if (!symbols.math[middle]) { - throw new ParseError(`Invalid delimiter '${middle}' after '\\middle'`); - } - checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" }); - body.push({ type: "middle", mode: "math", delim: middle }); - parser.consume(); - body = body.concat(parser.parseExpression(false, "\\right", true)); - nextToken = parser.fetch(); - } - --parser.leftrightDepth; - parser.expect("\\right", false); - const right = assertNodeType(parser.parseFunction(), "leftright-right"); - return { - type: "leftright", - mode: parser.mode, - body, - left: delim.text, - right: right.delim, - isStretchy: true - }; - }, - mathmlBuilder: (group, style) => { - assertParsed(group); - const inner = buildExpression(group.body, style); - - const leftNode = makeFenceMo(group.left, group.mode, "prefix", true); - inner.unshift(leftNode); - - const rightNode = makeFenceMo(group.right, group.mode, "postfix", true); - if (group.body.length > 0) { - const lastElement = group.body[group.body.length - 1]; - if (lastElement.type === "color" && !lastElement.isTextColor) { - rightNode.setAttribute("mathcolor", lastElement.color); - } - } - inner.push(rightNode); - - return makeRow(inner); - } -}); - -defineFunction({ - type: "delimiter", - names: Array.from(leftDelimiterNames), - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: true, - allowedInArgument: true - }, - handler: ({ parser, funcName, token }) => { - if (parser.mode === "text") { - return { - type: "textord", - mode: "text", - text: funcName, - loc: token.loc - } - } else if (!parser.settings.wrapDelimiterPairs) { - // Treat this token as an ordinary symbol. - return { - type: "atom", - mode: "math", - family: "open", - loc: token.loc, - text: funcName - }; - } - // Otherwise, try to wrap a pair of delimiters with an . - const rightDelim = leftToRight[funcName]; - // Parse the inner expression, looking for the corresponding right delimiter. - const body = parser.parseExpression(false, rightDelim, false); - const nextToken = parser.fetch().text; - - if (nextToken !== rightDelim) { - // We were unable to find a matching right delimiter. - // Throw control back to renderToMathMLTree. - // It will reparse the entire expression with wrapDelimiterPairs set to false. - throw new ParseError("Unmatched delimiter"); - } - parser.consume(); - - return { - type: "delimiter", - mode: parser.mode, - body, - left: funcName, - right: rightDelim - }; - }, - mathmlBuilder: (group, style) => { - assertParsed(group); - const inner = buildExpression(group.body, style); - - const leftNode = makeFenceMo(group.left, group.mode, "prefix", false); - inner.unshift(leftNode); - - const rightNode = makeFenceMo(group.right, group.mode, "postfix", false); - if (group.body.length > 0) { - const lastElement = group.body[group.body.length - 1]; - if (lastElement.type === "color" && !lastElement.isTextColor) { - rightNode.setAttribute("mathcolor", lastElement.color); - } - } - inner.push(rightNode); - - return makeRow(inner); - } -}); - -defineFunction({ - type: "middle", - names: ["\\middle"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - if (!context.parser.leftrightDepth) { - throw new ParseError("\\middle without preceding \\left", delim); - } - - return { - type: "middle", - mode: context.parser.mode, - delim: delim.text - }; - }, - mathmlBuilder: (group) => { - const textNode = makeText(group.delim, group.mode); - const middleNode = new MathNode("mo", [textNode]); - middleNode.setAttribute("fence", "true"); - if (group.delim.indexOf("arrow") > -1) { - middleNode.setAttribute("stretchy", "true"); - } - // The next line is not semantically correct, but - // Chromium fails to stretch if it is not there. - middleNode.setAttribute("form", "prefix"); - // MathML gives 5/18em spacing to each element. - // \middle should get delimiter spacing instead. - middleNode.setAttribute("lspace", "0.05em"); - middleNode.setAttribute("rspace", "0.05em"); - return middleNode; - } -}); - -const boxTags = ["\\boxed", "\\fcolorbox", "\\colorbox"]; - -const mathmlBuilder$7 = (group, style) => { - const tag = boxTags.includes(group.label) ? "mrow" : "menclose"; - const node = new MathNode(tag, [buildGroup$1(group.body, style)]); - switch (group.label) { - case "\\overline": - node.setAttribute("notation", "top"); // for Firefox & WebKit - node.classes.push("tml-overline"); // for Chromium - break - case "\\underline": - node.setAttribute("notation", "bottom"); - node.classes.push("tml-underline"); - break - case "\\cancel": - node.setAttribute("notation", "updiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "upstrike"])); - break - case "\\bcancel": - node.setAttribute("notation", "downdiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "downstrike"])); - break - case "\\sout": - node.setAttribute("notation", "horizontalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "sout"])); - break - case "\\xcancel": - node.setAttribute("notation", "updiagonalstrike downdiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "tml-xcancel"])); - break - // cancelto is handled in cancelto.js - case "\\longdiv": - node.setAttribute("notation", "longdiv"); - node.classes.push("longdiv-top"); - node.children.push(new MathNode("mrow", [], ["longdiv-arc"])); - break - case "\\phase": - node.setAttribute("notation", "phasorangle"); - node.classes.push("phasor-bottom"); - node.children.push(new MathNode("mrow", [], ["phasor-angle"])); - break - case "\\textcircled": - node.setAttribute("notation", "circle"); - node.classes.push("circle-pad"); - node.children.push(new MathNode("mrow", [], ["textcircle"])); - break - case "\\angl": - node.setAttribute("notation", "actuarial"); - node.classes.push("actuarial"); - break - case "\\boxed": - // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty - node.style.padding = "3pt"; - node.style.border = "1px solid"; - node.setAttribute("scriptlevel", "0"); - node.setAttribute("displaystyle", "true"); - break - case "\\fbox": - node.setAttribute("notation", "box"); - node.classes.push("tml-fbox"); - break - case "\\fcolorbox": - case "\\colorbox": { - // Don't use . WebKit would show a radical. - node.style.padding = "0.3em"; // 3 pt from LaTeX source2e for a 10pt font - if (group.label === "\\fcolorbox") { - node.style.border = "0.0667em solid " + String(group.borderColor); - } - break - } - } - if (group.backgroundColor) { - node.setAttribute("mathbackground", group.backgroundColor); - } - return node; -}; - -defineFunction({ - type: "enclose", - names: ["\\colorbox"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "text"] - }, - handler({ parser, funcName }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); - } - const body = args[1]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - backgroundColor: color, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -defineFunction({ - type: "enclose", - names: ["\\fcolorbox"], - props: { - numArgs: 3, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "raw", "text"] - }, - handler({ parser, funcName }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let borderColor = ""; - let backgroundColor; - if (model) { - const borderSpec = assertNodeType(args[0], "raw").string; - const backgroundSpec = assertNodeType(args[0], "raw").string; - borderColor = colorFromSpec(model, borderSpec); - backgroundColor = colorFromSpec(model, backgroundSpec); - } else { - borderColor = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); - backgroundColor = validateColor(assertNodeType(args[1], "raw").string, parser.gullet.macros); - } - const body = args[2]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - backgroundColor, - borderColor, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -defineFunction({ - type: "enclose", - names: ["\\fbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "enclose", - mode: parser.mode, - label: "\\fbox", - body: args[0] - }; - } -}); - -defineFunction({ - type: "enclose", - names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", - "\\boxed", "\\longdiv", "\\phase"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -defineFunction({ - type: "enclose", - names: ["\\underline"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - - -defineFunction({ - type: "enclose", - names: ["\\textcircled"], - props: { - numArgs: 1, - argTypes: ["text"], - allowedInArgument: true, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -// Environment delimiters. HTML/MathML rendering is defined in the corresponding -// defineEnvironment definitions. -defineFunction({ - type: "environment", - names: ["\\begin", "\\end"], - props: { - numArgs: 1, - argTypes: ["text"] - }, - handler({ parser, funcName }, args) { - const nameGroup = args[0]; - if (nameGroup.type !== "ordgroup") { - throw new ParseError("Invalid environment name", nameGroup); - } - let envName = ""; - for (let i = 0; i < nameGroup.body.length; ++i) { - envName += assertNodeType(nameGroup.body[i], "textord").text; - } - - if (funcName === "\\begin") { - // begin...end is similar to left...right - if (!Object.prototype.hasOwnProperty.call(environments, envName )) { - throw new ParseError("No such environment: " + envName, nameGroup); - } - // Build the environment object. Arguments and other information will - // be made available to the begin and end methods using properties. - const env = environments[envName]; - const { args, optArgs } = parser.parseArguments("\\begin{" + envName + "}", env); - const context = { - mode: parser.mode, - envName, - parser - }; - const result = env.handler(context, args, optArgs); - parser.expect("\\end", false); - const endNameToken = parser.nextToken; - const end = assertNodeType(parser.parseFunction(), "environment"); - if (end.name !== envName) { - throw new ParseError( - `Mismatch: \\begin{${envName}} matched by \\end{${end.name}}`, - endNameToken - ); - } - return result; - } - - return { - type: "environment", - mode: parser.mode, - name: envName, - nameGroup - }; - } -}); - -defineFunction({ - type: "envTag", - names: ["\\env@tag"], - props: { - numArgs: 1, - argTypes: ["math"] - }, - handler({ parser }, args) { - return { - type: "envTag", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow"); - } -}); - -defineFunction({ - type: "noTag", - names: ["\\env@notag"], - props: { - numArgs: 0 - }, - handler({ parser }) { - return { - type: "noTag", - mode: parser.mode - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow"); - } -}); - -const isLongVariableName = (group, font) => { - if (font !== "mathrm" || group.body.type !== "ordgroup" || group.body.body.length === 1) { - return false - } - if (group.body.body[0].type !== "mathord") { return false } - for (let i = 1; i < group.body.body.length; i++) { - const parseNodeType = group.body.body[i].type; - if (!(parseNodeType === "mathord" || - (parseNodeType === "textord" && !isNaN(group.body.body[i].text)))) { - return false - } - } - return true -}; - -const mathmlBuilder$6 = (group, style) => { - const font = group.font; - const newStyle = style.withFont(font); - const mathGroup = buildGroup$1(group.body, newStyle); - - if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{} - if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) { - mathGroup.style.fontWeight = "bold"; - return mathGroup - } - // Check if it is possible to consolidate elements into a single element. - if (isLongVariableName(group, font)) { - // This is a \mathrm{…} group. It gets special treatment because symbolsOrd.js - // wraps elements with s to work around a Firefox bug. - const mi = mathGroup.children[0].children[0].children - ? mathGroup.children[0].children[0] - : mathGroup.children[0]; - delete mi.attributes.mathvariant; - for (let i = 1; i < mathGroup.children.length; i++) { - mi.children[0].text += mathGroup.children[i].children[0].children - ? mathGroup.children[i].children[0].children[0].text - : mathGroup.children[i].children[0].text; - } - // Wrap in a to prevent the same Firefox bug. - const mpadded = new MathNode("mpadded", [mi]); - mpadded.setAttribute("lspace", "0"); - return mpadded - } - let canConsolidate = mathGroup.children[0].type === "mo"; - for (let i = 1; i < mathGroup.children.length; i++) { - if (mathGroup.children[i].type === "mo" && font === "boldsymbol") { - mathGroup.children[i].style.fontWeight = "bold"; - } - if (mathGroup.children[i].type !== "mi") { canConsolidate = false; } - const localVariant = mathGroup.children[i].attributes && - mathGroup.children[i].attributes.mathvariant || ""; - if (localVariant !== "normal") { canConsolidate = false; } - } - if (!canConsolidate) { return mathGroup } - // Consolidate the elements. - const mi = mathGroup.children[0]; - for (let i = 1; i < mathGroup.children.length; i++) { - mi.children.push(mathGroup.children[i].children[0]); - } - if (mi.attributes.mathvariant && mi.attributes.mathvariant === "normal") { - // Workaround for a Firefox bug that renders spurious space around - // a - // Ref: https://bugs.webkit.org/show_bug.cgi?id=129097 - // We insert a text node that contains a zero-width space and wrap in an mrow. - // TODO: Get rid of this workaround when the Firefox bug is fixed. - const bogus = new MathNode("mtext", new TextNode("\u200b")); - return new MathNode("mrow", [bogus, mi]) - } - return mi -}; - -const fontAliases = { - "\\Bbb": "\\mathbb", - "\\bold": "\\mathbf", - "\\frak": "\\mathfrak", - "\\bm": "\\boldsymbol" -}; - -defineFunction({ - type: "font", - names: [ - // styles - "\\mathrm", - "\\mathit", - "\\mathbf", - "\\mathnormal", - "\\up@greek", - "\\boldsymbol", - - // families - "\\mathbb", - "\\mathcal", - "\\mathfrak", - "\\mathscr", - "\\mathsf", - "\\mathsfit", - "\\mathtt", - - // aliases - "\\Bbb", - "\\bm", - "\\bold", - "\\frak" - ], - props: { - numArgs: 1, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const body = normalizeArgument(args[0]); - let func = funcName; - if (func in fontAliases) { - func = fontAliases[func]; - } - return { - type: "font", - mode: parser.mode, - font: func.slice(1), - body - }; - }, - mathmlBuilder: mathmlBuilder$6 -}); - -// Old font changing functions -defineFunction({ - type: "font", - names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"], - props: { - numArgs: 0, - allowedInText: true - }, - handler: ({ parser, funcName, breakOnTokenText }, args) => { - const { mode } = parser; - const body = parser.parseExpression(true, breakOnTokenText, true); - const fontStyle = `math${funcName.slice(1)}`; - - return { - type: "font", - mode: mode, - font: fontStyle, - body: { - type: "ordgroup", - mode: parser.mode, - body - } - }; - }, - mathmlBuilder: mathmlBuilder$6 -}); - -const stylArray = ["display", "text", "script", "scriptscript"]; -const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 }; - -const adjustStyle = (functionSize, originalStyle) => { - // Figure out what style this fraction should be in based on the - // function used - let style = originalStyle; - if (functionSize === "display") { //\tfrac or \cfrac - // Get display style as a default. - // If incoming style is sub/sup, use style.text() to get correct size. - const newSize = style.level >= StyleLevel.SCRIPT ? StyleLevel.TEXT : StyleLevel.DISPLAY; - style = style.withLevel(newSize); - } else if (functionSize === "text" && - style.level === StyleLevel.DISPLAY) { - // We're in a \tfrac but incoming style is displaystyle, so: - style = style.withLevel(StyleLevel.TEXT); - } else if (functionSize === "auto") { - style = style.incrementLevel(); - } else if (functionSize === "script") { - style = style.withLevel(StyleLevel.SCRIPT); - } else if (functionSize === "scriptscript") { - style = style.withLevel(StyleLevel.SCRIPTSCRIPT); - } - return style; -}; - -const mathmlBuilder$5 = (group, style) => { - style = adjustStyle(group.scriptLevel, style); - - // Chromium (wrongly) continues to shrink fractions beyond scriptscriptlevel. - // So we check for levels that Chromium shrinks too small. - // If necessary, set an explicit fraction depth. - const numer = buildGroup$1(group.numer, style); - const denom = buildGroup$1(group.denom, style); - if (style.level === 3) { - numer.style.mathDepth = "2"; - numer.setAttribute("scriptlevel", "2"); - denom.style.mathDepth = "2"; - denom.setAttribute("scriptlevel", "2"); - } - - let node = new MathNode("mfrac", [numer, denom]); - - if (!group.hasBarLine) { - node.setAttribute("linethickness", "0px"); - } else if (group.barSize) { - const ruleWidth = calculateSize(group.barSize, style); - node.setAttribute("linethickness", ruleWidth.number + ruleWidth.unit); - } - - if (group.leftDelim != null || group.rightDelim != null) { - const withDelims = []; - - if (group.leftDelim != null) { - const leftOp = new MathNode("mo", [ - new TextNode(group.leftDelim.replace("\\", "")) - ]); - leftOp.setAttribute("fence", "true"); - withDelims.push(leftOp); - } - - withDelims.push(node); - - if (group.rightDelim != null) { - const rightOp = new MathNode("mo", [ - new TextNode(group.rightDelim.replace("\\", "")) - ]); - rightOp.setAttribute("fence", "true"); - withDelims.push(rightOp); - } - - node = makeRow(withDelims); - } - - if (group.scriptLevel !== "auto") { - node = new MathNode("mstyle", [node]); - node.setAttribute("displaystyle", String(group.scriptLevel === "display")); - node.setAttribute("scriptlevel", scriptLevel[group.scriptLevel]); - } - - return node; -}; - -defineFunction({ - type: "genfrac", - names: [ - "\\cfrac", - "\\dfrac", - "\\frac", - "\\tfrac", - "\\dbinom", - "\\binom", - "\\tbinom", - "\\\\atopfrac", // can’t be entered directly - "\\\\bracefrac", - "\\\\brackfrac" // ditto - ], - props: { - numArgs: 2, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const numer = args[0]; - const denom = args[1]; - let hasBarLine = false; - let leftDelim = null; - let rightDelim = null; - let scriptLevel = "auto"; - - switch (funcName) { - case "\\cfrac": - case "\\dfrac": - case "\\frac": - case "\\tfrac": - hasBarLine = true; - break; - case "\\\\atopfrac": - hasBarLine = false; - break; - case "\\dbinom": - case "\\binom": - case "\\tbinom": - leftDelim = "("; - rightDelim = ")"; - break; - case "\\\\bracefrac": - leftDelim = "\\{"; - rightDelim = "\\}"; - break; - case "\\\\brackfrac": - leftDelim = "["; - rightDelim = "]"; - break; - default: - throw new Error("Unrecognized genfrac command"); - } - - if (funcName === "\\cfrac" || funcName.startsWith("\\d")) { - scriptLevel = "display"; - } else if (funcName.startsWith("\\t")) { - scriptLevel = "text"; - } - - return { - type: "genfrac", - mode: parser.mode, - continued: false, - numer, - denom, - hasBarLine, - leftDelim, - rightDelim, - scriptLevel, - barSize: null - }; - }, - mathmlBuilder: mathmlBuilder$5 -}); - -// Infix generalized fractions -- these are not rendered directly, but replaced -// immediately by one of the variants above. -defineFunction({ - type: "infix", - names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"], - props: { - numArgs: 0, - infix: true - }, - handler({ parser, funcName, token }) { - let replaceWith; - switch (funcName) { - case "\\over": - replaceWith = "\\frac"; - break; - case "\\choose": - replaceWith = "\\binom"; - break; - case "\\atop": - replaceWith = "\\\\atopfrac"; - break; - case "\\brace": - replaceWith = "\\\\bracefrac"; - break; - case "\\brack": - replaceWith = "\\\\brackfrac"; - break; - default: - throw new Error("Unrecognized infix genfrac command"); - } - return { - type: "infix", - mode: parser.mode, - replaceWith, - token - }; - } -}); - -const delimFromValue = function(delimString) { - let delim = null; - if (delimString.length > 0) { - delim = delimString; - delim = delim === "." ? null : delim; - } - return delim; -}; - -defineFunction({ - type: "genfrac", - names: ["\\genfrac"], - props: { - numArgs: 6, - allowedInArgument: true, - argTypes: ["math", "math", "size", "text", "math", "math"] - }, - handler({ parser }, args) { - const numer = args[4]; - const denom = args[5]; - - // Look into the parse nodes to get the desired delimiters. - const leftNode = normalizeArgument(args[0]); - const leftDelim = leftNode.type === "atom" && leftNode.family === "open" - ? delimFromValue(leftNode.text) - : null; - const rightNode = normalizeArgument(args[1]); - const rightDelim = - rightNode.type === "atom" && rightNode.family === "close" - ? delimFromValue(rightNode.text) - : null; - - const barNode = assertNodeType(args[2], "size"); - let hasBarLine; - let barSize = null; - if (barNode.isBlank) { - // \genfrac acts differently than \above. - // \genfrac treats an empty size group as a signal to use a - // standard bar size. \above would see size = 0 and omit the bar. - hasBarLine = true; - } else { - barSize = barNode.value; - hasBarLine = barSize.number > 0; - } - - // Find out if we want displaystyle, textstyle, etc. - let scriptLevel = "auto"; - let styl = args[3]; - if (styl.type === "ordgroup") { - if (styl.body.length > 0) { - const textOrd = assertNodeType(styl.body[0], "textord"); - scriptLevel = stylArray[Number(textOrd.text)]; - } - } else { - styl = assertNodeType(styl, "textord"); - scriptLevel = stylArray[Number(styl.text)]; - } - - return { - type: "genfrac", - mode: parser.mode, - numer, - denom, - continued: false, - hasBarLine, - barSize, - leftDelim, - rightDelim, - scriptLevel - }; - }, - mathmlBuilder: mathmlBuilder$5 -}); - -// \above is an infix fraction that also defines a fraction bar size. -defineFunction({ - type: "infix", - names: ["\\above"], - props: { - numArgs: 1, - argTypes: ["size"], - infix: true - }, - handler({ parser, funcName, token }, args) { - return { - type: "infix", - mode: parser.mode, - replaceWith: "\\\\abovefrac", - barSize: assertNodeType(args[0], "size").value, - token - }; - } -}); - -defineFunction({ - type: "genfrac", - names: ["\\\\abovefrac"], - props: { - numArgs: 3, - argTypes: ["math", "size", "math"] - }, - handler: ({ parser, funcName }, args) => { - const numer = args[0]; - const barSize = assert(assertNodeType(args[1], "infix").barSize); - const denom = args[2]; - - const hasBarLine = barSize.number > 0; - return { - type: "genfrac", - mode: parser.mode, - numer, - denom, - continued: false, - hasBarLine, - barSize, - leftDelim: null, - rightDelim: null, - scriptLevel: "auto" - }; - }, - - mathmlBuilder: mathmlBuilder$5 -}); - -// \hbox is provided for compatibility with LaTeX functions that act on a box. -// This function by itself doesn't do anything but set scriptlevel to \textstyle -// and prevent a soft line break. - -defineFunction({ - type: "hbox", - names: ["\\hbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInArgument: true, - allowedInText: false - }, - handler({ parser }, args) { - return { - type: "hbox", - mode: parser.mode, - body: ordargument(args[0]) - }; - }, - mathmlBuilder(group, style) { - const newStyle = style.withLevel(StyleLevel.TEXT); - const mrow = buildExpressionRow(group.body, newStyle); - return consolidateText(mrow) - } -}); - -const mathmlBuilder$4 = (group, style) => { - const accentNode = mathMLnode(group.label); - accentNode.style["math-depth"] = 0; - return new MathNode(group.isOver ? "mover" : "munder", [ - buildGroup$1(group.base, style), - accentNode - ]); -}; - -// Horizontal stretchy brackets -defineFunction({ - type: "horizBracket", - names: ["\\overbrace", "\\underbrace", "\\overbracket", "\\underbracket"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - return { - type: "horizBracket", - mode: parser.mode, - label: funcName, - isOver: /^\\over/.test(funcName), - base: args[0] - }; - }, - mathmlBuilder: mathmlBuilder$4 -}); - -defineFunction({ - type: "html", - names: ["\\class", "\\id", "\\style", "\\data"], - props: { - numArgs: 2, - argTypes: ["raw", "original"], - allowedInText: true - }, - handler: ({ parser, funcName, token }, args) => { - const value = assertNodeType(args[0], "raw").string; - const body = args[1]; - - if (parser.settings.strict) { - throw new ParseError(`Function "${funcName}" is disabled in strict mode`, token) - } - - let trustContext; - const attributes = {}; - - switch (funcName) { - case "\\class": - attributes.class = value; - trustContext = { - command: "\\class", - class: value - }; - break; - case "\\id": - attributes.id = value; - trustContext = { - command: "\\id", - id: value - }; - break; - case "\\style": - attributes.style = value; - trustContext = { - command: "\\style", - style: value - }; - break; - case "\\data": { - const data = value.split(","); - for (let i = 0; i < data.length; i++) { - const keyVal = data[i].split("="); - if (keyVal.length !== 2) { - throw new ParseError("Error parsing key-value for \\data"); - } - attributes["data-" + keyVal[0].trim()] = keyVal[1].trim(); - } - - trustContext = { - command: "\\data", - attributes - }; - break; - } - default: - throw new Error("Unrecognized html command"); - } - - if (!parser.settings.isTrusted(trustContext)) { - throw new ParseError(`Function "${funcName}" is not trusted`, token) - } - return { - type: "html", - mode: parser.mode, - attributes, - body: ordargument(body) - }; - }, - mathmlBuilder: (group, style) => { - const element = buildExpressionRow(group.body, style); - - const classes = []; - if (group.attributes.class) { - classes.push(...group.attributes.class.trim().split(/\s+/)); - } - element.classes = classes; - - for (const attr in group.attributes) { - if (attr !== "class" && Object.prototype.hasOwnProperty.call(group.attributes, attr)) { - element.setAttribute(attr, group.attributes[attr]); - } - } - - return element; - } -}); - -const sizeData = function(str) { - if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) { - // str is a number with no unit specified. - // default unit is bp, per graphix package. - return { number: +str, unit: "bp" } - } else { - const match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(str); - if (!match) { - throw new ParseError("Invalid size: '" + str + "' in \\includegraphics"); - } - const data = { - number: +(match[1] + match[2]), // sign + magnitude, cast to number - unit: match[3] - }; - if (!validUnit(data)) { - throw new ParseError("Invalid unit: '" + data.unit + "' in \\includegraphics."); - } - return data - } -}; - -defineFunction({ - type: "includegraphics", - names: ["\\includegraphics"], - props: { - numArgs: 1, - numOptionalArgs: 1, - argTypes: ["raw", "url"], - allowedInText: false - }, - handler: ({ parser, token }, args, optArgs) => { - let width = { number: 0, unit: "em" }; - let height = { number: 0.9, unit: "em" }; // sorta character sized. - let totalheight = { number: 0, unit: "em" }; - let alt = ""; - - if (optArgs[0]) { - const attributeStr = assertNodeType(optArgs[0], "raw").string; - - // Parser.js does not parse key/value pairs. We get a string. - const attributes = attributeStr.split(","); - for (let i = 0; i < attributes.length; i++) { - const keyVal = attributes[i].split("="); - if (keyVal.length === 2) { - const str = keyVal[1].trim(); - switch (keyVal[0].trim()) { - case "alt": - alt = str; - break - case "width": - width = sizeData(str); - break - case "height": - height = sizeData(str); - break - case "totalheight": - totalheight = sizeData(str); - break - default: - throw new ParseError("Invalid key: '" + keyVal[0] + "' in \\includegraphics.") - } - } - } - } - - const src = assertNodeType(args[0], "url").url; - - if (alt === "") { - // No alt given. Use the file name. Strip away the path. - alt = src; - alt = alt.replace(/^.*[\\/]/, ""); - alt = alt.substring(0, alt.lastIndexOf(".")); - } - - if ( - !parser.settings.isTrusted({ - command: "\\includegraphics", - url: src - }) - ) { - throw new ParseError(`Function "\\includegraphics" is not trusted`, token) - } - - return { - type: "includegraphics", - mode: parser.mode, - alt: alt, - width: width, - height: height, - totalheight: totalheight, - src: src - } - }, - mathmlBuilder: (group, style) => { - const height = calculateSize(group.height, style); - const depth = { number: 0, unit: "em" }; - - if (group.totalheight.number > 0) { - if (group.totalheight.unit === height.unit && - group.totalheight.number > height.number) { - depth.number = group.totalheight.number - height.number; - depth.unit = height.unit; - } - } - - let width = 0; - if (group.width.number > 0) { - width = calculateSize(group.width, style); - } - - const graphicStyle = { height: height.number + depth.number + "em" }; - if (width.number > 0) { - graphicStyle.width = width.number + width.unit; - } - if (depth.number > 0) { - graphicStyle.verticalAlign = -depth.number + depth.unit; - } - - const node = new Img(group.src, group.alt, graphicStyle); - node.height = height; - node.depth = depth; - return new MathNode("mtext", [node]) - } -}); - -// Horizontal spacing commands - - -// TODO: \hskip and \mskip should support plus and minus in lengths - -defineFunction({ - type: "kern", - names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"], - props: { - numArgs: 1, - argTypes: ["size"], - primitive: true, - allowedInText: true - }, - handler({ parser, funcName, token }, args) { - const size = assertNodeType(args[0], "size"); - if (parser.settings.strict) { - const mathFunction = funcName[1] === "m"; // \mkern, \mskip - const muUnit = size.value.unit === "mu"; - if (mathFunction) { - if (!muUnit) { - throw new ParseError(`LaTeX's ${funcName} supports only mu units, ` + - `not ${size.value.unit} units`, token) - } - if (parser.mode !== "math") { - throw new ParseError(`LaTeX's ${funcName} works only in math mode`, token) - } - } else { - // !mathFunction - if (muUnit) { - throw new ParseError(`LaTeX's ${funcName} doesn't support mu units`, token) - } - } - } - return { - type: "kern", - mode: parser.mode, - dimension: size.value - }; - }, - mathmlBuilder(group, style) { - const dimension = calculateSize(group.dimension, style); - const ch = dimension.number > 0 && dimension.unit === "em" - ? spaceCharacter(dimension.number) - : ""; - if (group.mode === "text" && ch.length > 0) { - const character = new TextNode(ch); - return new MathNode("mtext", [character]); - } else { - if (dimension.number >= 0) { - const node = new MathNode("mspace"); - node.setAttribute("width", dimension.number + dimension.unit); - return node - } else { - // Don't use or because - // WebKit recognizes negative left margin only on a element - const node = new MathNode("mrow"); - node.style.marginLeft = dimension.number + dimension.unit; - return node - } - } - } -}); - -const spaceCharacter = function(width) { - if (width >= 0.05555 && width <= 0.05556) { - return "\u200a"; //   - } else if (width >= 0.1666 && width <= 0.1667) { - return "\u2009"; //   - } else if (width >= 0.2222 && width <= 0.2223) { - return "\u2005"; //   - } else if (width >= 0.2777 && width <= 0.2778) { - return "\u2005\u200a"; //    - } else { - return ""; - } -}; - -// Limit valid characters to a small set, for safety. -const invalidIdRegEx = /[^A-Za-z_0-9-]/g; - -defineFunction({ - type: "label", - names: ["\\label"], - props: { - numArgs: 1, - argTypes: ["raw"] - }, - handler({ parser }, args) { - return { - type: "label", - mode: parser.mode, - string: args[0].string.replace(invalidIdRegEx, "") - }; - }, - mathmlBuilder(group, style) { - // Return a no-width, no-ink element with an HTML id. - const node = new MathNode("mrow", [], ["tml-label"]); - if (group.string.length > 0) { - node.setLabel(group.string); - } - return node - } -}); - -// Horizontal overlap functions - -const textModeLap = ["\\clap", "\\llap", "\\rlap"]; - -defineFunction({ - type: "lap", - names: ["\\mathllap", "\\mathrlap", "\\mathclap", "\\clap", "\\llap", "\\rlap"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser, funcName, token }, args) => { - if (textModeLap.includes(funcName)) { - if (parser.settings.strict && parser.mode !== "text") { - throw new ParseError(`{${funcName}} can be used only in text mode. - Try \\math${funcName.slice(1)}`, token) - } - funcName = funcName.slice(1); - } else { - funcName = funcName.slice(5); - } - const body = args[0]; - return { - type: "lap", - mode: parser.mode, - alignment: funcName, - body - } - }, - mathmlBuilder: (group, style) => { - // mathllap, mathrlap, mathclap - let strut; - if (group.alignment === "llap") { - // We need an invisible strut with the same depth as the group. - // We can't just read the depth, so we use \vphantom methods. - const phantomInner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", phantomInner); - strut = new MathNode("mpadded", [phantom]); - strut.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - } - - const inner = buildGroup$1(group.body, style); - let node; - if (group.alignment === "llap") { - inner.style.position = "absolute"; - inner.style.right = "0"; - inner.style.bottom = `0`; // If we could have read the ink depth, it would go here. - node = new MathNode("mpadded", [strut, inner]); - } else { - node = new MathNode("mpadded", [inner]); - } - - if (group.alignment === "rlap") { - if (group.body.body.length > 0 && group.body.body[0].type === "genfrac") { - // In Firefox, a squashes the 3/18em padding of a child \frac. Put it back. - node.setAttribute("lspace", "0.16667em"); - } - } else { - const offset = group.alignment === "llap" ? "-1" : "-0.5"; - node.setAttribute("lspace", offset + "width"); - if (group.alignment === "llap") { - node.style.position = "relative"; - } else { - node.style.display = "flex"; - node.style.justifyContent = "center"; - } - } - node.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - return node - } -}); - -// Switching from text mode back to math mode -defineFunction({ - type: "ordgroup", - names: ["\\(", "$"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: false - }, - handler({ funcName, parser }, args) { - const outerMode = parser.mode; - parser.switchMode("math"); - const close = funcName === "\\(" ? "\\)" : "$"; - const body = parser.parseExpression(false, close); - parser.expect(close); - parser.switchMode(outerMode); - return { - type: "ordgroup", - mode: parser.mode, - body - }; - } -}); - -// Check for extra closing math delimiters -defineFunction({ - type: "text", // Doesn't matter what this is. - names: ["\\)", "\\]"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: false - }, - handler(context, token) { - throw new ParseError(`Mismatched ${context.funcName}`, token); - } -}); - -const chooseStyle = (group, style) => { - switch (style.level) { - case StyleLevel.DISPLAY: // 0 - return group.display; - case StyleLevel.TEXT: // 1 - return group.text; - case StyleLevel.SCRIPT: // 2 - return group.script; - case StyleLevel.SCRIPTSCRIPT: // 3 - return group.scriptscript; - default: - return group.text; - } -}; - -defineFunction({ - type: "mathchoice", - names: ["\\mathchoice"], - props: { - numArgs: 4, - primitive: true - }, - handler: ({ parser }, args) => { - return { - type: "mathchoice", - mode: parser.mode, - display: ordargument(args[0]), - text: ordargument(args[1]), - script: ordargument(args[2]), - scriptscript: ordargument(args[3]) - }; - }, - mathmlBuilder: (group, style) => { - const body = chooseStyle(group, style); - return buildExpressionRow(body, style); - } -}); - -const textAtomTypes = ["text", "textord", "mathord", "atom"]; - -function mathmlBuilder$3(group, style) { - let node; - const inner = buildExpression(group.body, style); - - if (group.mclass === "minner") { - node = new MathNode("mpadded", inner); - } else if (group.mclass === "mord") { - if (group.isCharacterBox || inner[0].type === "mathord") { - node = inner[0]; - node.type = "mi"; - if (node.children.length === 1 && node.children[0].text && node.children[0].text === "∇") { - node.setAttribute("mathvariant", "normal"); - } - } else { - node = new MathNode("mi", inner); - } - } else { - node = new MathNode("mrow", inner); - if (group.mustPromote) { - node = inner[0]; - node.type = "mo"; - if (group.isCharacterBox && group.body[0].text && /[A-Za-z]/.test(group.body[0].text)) { - node.setAttribute("mathvariant", "italic"); - } - } else { - node = new MathNode("mrow", inner); - } - - // Set spacing based on what is the most likely adjacent atom type. - // See TeXbook p170. - const doSpacing = style.level < 2; // Operator spacing is zero inside a (sub|super)script. - if (node.type === "mrow") { - if (doSpacing ) { - if (group.mclass === "mbin") { - // medium space - node.children.unshift(padding(0.2222)); - node.children.push(padding(0.2222)); - } else if (group.mclass === "mrel") { - // thickspace - node.children.unshift(padding(0.2778)); - node.children.push(padding(0.2778)); - } else if (group.mclass === "mpunct") { - node.children.push(padding(0.1667)); - } else if (group.mclass === "minner") { - node.children.unshift(padding(0.0556)); // 1 mu is the most likely option - node.children.push(padding(0.0556)); - } - } - } else { - if (group.mclass === "mbin") { - // medium space - node.attributes.lspace = (doSpacing ? "0.2222em" : "0"); - node.attributes.rspace = (doSpacing ? "0.2222em" : "0"); - } else if (group.mclass === "mrel") { - // thickspace - node.attributes.lspace = (doSpacing ? "0.2778em" : "0"); - node.attributes.rspace = (doSpacing ? "0.2778em" : "0"); - } else if (group.mclass === "mpunct") { - node.attributes.lspace = "0em"; - node.attributes.rspace = (doSpacing ? "0.1667em" : "0"); - } else if (group.mclass === "mopen" || group.mclass === "mclose") { - node.attributes.lspace = "0em"; - node.attributes.rspace = "0em"; - } else if (group.mclass === "minner" && doSpacing) { - node.attributes.lspace = "0.0556em"; // 1 mu is the most likely option - node.attributes.width = "+0.1111em"; - } - } - - if (!(group.mclass === "mopen" || group.mclass === "mclose")) { - delete node.attributes.stretchy; - delete node.attributes.form; - } - } - return node; -} - -// Math class commands except \mathop -defineFunction({ - type: "mclass", - names: [ - "\\mathord", - "\\mathbin", - "\\mathrel", - "\\mathopen", - "\\mathclose", - "\\mathpunct", - "\\mathinner" - ], - props: { - numArgs: 1, - primitive: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - const isCharacterBox$1 = isCharacterBox(body); - // We should not wrap a around a or . That would be invalid MathML. - // In that case, we instead promote the text contents of the body to the parent. - let mustPromote = true; - const mord = { type: "mathord", text: "", mode: parser.mode }; - const arr = (body.body) ? body.body : [body]; - for (const arg of arr) { - if (textAtomTypes.includes(arg.type)) { - if (symbols[parser.mode][arg.text]) { - mord.text += symbols[parser.mode][arg.text].replace; - } else if (arg.text) { - mord.text += arg.text; - } else if (arg.body) { - arg.body.map(e => { mord.text += e.text; }); - } - } else { - mustPromote = false; - break - } - } - if (mustPromote && funcName === "\\mathord" && mord.type === "mathord" - && mord.text.length > 1) { - return mord - } else { - return { - type: "mclass", - mode: parser.mode, - mclass: "m" + funcName.slice(5), - body: ordargument(mustPromote ? mord : body), - isCharacterBox: isCharacterBox$1, - mustPromote - }; - } - }, - mathmlBuilder: mathmlBuilder$3 -}); - -const binrelClass = (arg) => { - // \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument. - // (by rendering separately and with {}s before and after, and measuring - // the change in spacing). We'll do roughly the same by detecting the - // atom type directly. - const atom = arg.type === "ordgroup" && arg.body.length && arg.body.length === 1 - ? arg.body[0] - : arg; - if (atom.type === "atom") { - // BIN args are sometimes changed to OPEN, so check the original family. - const family = arg.body.length > 0 && arg.body[0].text && symbols.math[arg.body[0].text] - ? symbols.math[arg.body[0].text].group - : atom.family; - if (family === "bin" || family === "rel") { - return "m" + family; - } else { - return "mord"; - } - } else { - return "mord"; - } -}; - -// \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord. -// This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX. -defineFunction({ - type: "mclass", - names: ["\\@binrel"], - props: { - numArgs: 2 - }, - handler({ parser }, args) { - return { - type: "mclass", - mode: parser.mode, - mclass: binrelClass(args[0]), - body: ordargument(args[1]), - isCharacterBox: isCharacterBox(args[1]) - }; - } -}); - -// Build a relation or stacked op by placing one symbol on top of another -defineFunction({ - type: "mclass", - names: ["\\stackrel", "\\overset", "\\underset"], - props: { - numArgs: 2 - }, - handler({ parser, funcName }, args) { - const baseArg = args[1]; - const shiftedArg = args[0]; - - let mclass; - if (funcName !== "\\stackrel") { - // LaTeX applies \binrel spacing to \overset and \underset. - mclass = binrelClass(baseArg); - } else { - mclass = "mrel"; // for \stackrel - } - - const baseType = mclass === "mrel" || mclass === "mbin" - ? "op" - : "ordgroup"; - - const baseOp = { - type: baseType, - mode: baseArg.mode, - limits: true, - alwaysHandleSupSub: true, - parentIsSupSub: false, - symbol: false, - suppressBaseShift: funcName !== "\\stackrel", - body: ordargument(baseArg) - }; - - return { - type: "supsub", - mode: shiftedArg.mode, - stack: true, - base: baseOp, - sup: funcName === "\\underset" ? null : shiftedArg, - sub: funcName === "\\underset" ? shiftedArg : null - }; - }, - mathmlBuilder: mathmlBuilder$3 -}); - -// Helper function -const buildGroup = (el, style, noneNode) => { - if (!el) { return noneNode } - const node = buildGroup$1(el, style); - if (node.type === "mrow" && node.children.length === 0) { return noneNode } - return node -}; - -defineFunction({ - type: "multiscript", - names: ["\\sideset", "\\pres@cript"], // See macros.js for \prescript - props: { - numArgs: 3 - }, - handler({ parser, funcName, token }, args) { - if (args[2].body.length === 0) { - throw new ParseError(funcName + `cannot parse an empty base.`) - } - const base = args[2].body[0]; - if (parser.settings.strict && funcName === "\\sideset" && !base.symbol) { - throw new ParseError(`The base of \\sideset must be a big operator. Try \\prescript.`) - } - - if ((args[0].body.length > 0 && args[0].body[0].type !== "supsub") || - (args[1].body.length > 0 && args[1].body[0].type !== "supsub")) { - throw new ParseError("\\sideset can parse only subscripts and " + - "superscripts in its first two arguments", token) - } - - // The prescripts and postscripts come wrapped in a supsub. - const prescripts = args[0].body.length > 0 ? args[0].body[0] : null; - const postscripts = args[1].body.length > 0 ? args[1].body[0] : null; - - if (!prescripts && !postscripts) { - return base - } else if (!prescripts) { - // It's not a multi-script. Get a \textstyle supsub. - return { - type: "styling", - mode: parser.mode, - scriptLevel: "text", - body: [{ - type: "supsub", - mode: parser.mode, - base, - sup: postscripts.sup, - sub: postscripts.sub - }] - } - } else { - return { - type: "multiscript", - mode: parser.mode, - isSideset: funcName === "\\sideset", - prescripts, - postscripts, - base - } - } - }, - mathmlBuilder(group, style) { - const base = buildGroup$1(group.base, style); - - const prescriptsNode = new MathNode("mprescripts"); - const noneNode = new MathNode("none"); - let children = []; - - const preSub = buildGroup(group.prescripts.sub, style, noneNode); - const preSup = buildGroup(group.prescripts.sup, style, noneNode); - if (group.isSideset) { - // This seems silly, but LaTeX does this. Firefox ignores it, which does not make me sad. - preSub.setAttribute("style", "text-align: left;"); - preSup.setAttribute("style", "text-align: left;"); - } - - if (group.postscripts) { - const postSub = buildGroup(group.postscripts.sub, style, noneNode); - const postSup = buildGroup(group.postscripts.sup, style, noneNode); - children = [base, postSub, postSup, prescriptsNode, preSub, preSup]; - } else { - children = [base, prescriptsNode, preSub, preSup]; - } - - return new MathNode("mmultiscripts", children); - } -}); - -defineFunction({ - type: "not", - names: ["\\not"], - props: { - numArgs: 1, - primitive: true, - allowedInText: false - }, - handler({ parser }, args) { - const isCharacterBox$1 = isCharacterBox(args[0]); - let body; - if (isCharacterBox$1) { - body = ordargument(args[0]); - if (body[0].text.charAt(0) === "\\") { - body[0].text = symbols.math[body[0].text].replace; - } - // \u0338 is the Unicode Combining Long Solidus Overlay - body[0].text = body[0].text.slice(0, 1) + "\u0338" + body[0].text.slice(1); - } else { - // When the argument is not a character box, TeX does an awkward, poorly placed overlay. - // We'll do the same. - const notNode = { type: "textord", mode: "math", text: "\u0338" }; - const kernNode = { type: "kern", mode: "math", dimension: { number: -0.6, unit: "em" } }; - body = [notNode, kernNode, args[0]]; - } - return { - type: "not", - mode: parser.mode, - body, - isCharacterBox: isCharacterBox$1 - }; - }, - mathmlBuilder(group, style) { - if (group.isCharacterBox) { - const inner = buildExpression(group.body, style, true); - return inner[0] - } else { - return buildExpressionRow(group.body, style) - } - } -}); - -// Limits, symbols - -// Some helpers - -const ordAtomTypes = ["textord", "mathord", "atom"]; - -// Most operators have a large successor symbol, but these don't. -const noSuccessor = ["\\smallint"]; - -// Math operators (e.g. \sin) need a space between these types and themselves: -const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright", "font"]; - -// NOTE: Unlike most `builders`s, this one handles not only "op", but also -// "supsub" since some of them (like \int) can affect super/subscripting. - -const setSpacing = node => { - // The user wrote a \mathop{…} function. Change spacing from default to OP spacing. - // The most likely spacing for an OP is a thin space per TeXbook p170. - node.attributes.lspace = "0.1667em"; - node.attributes.rspace = "0.1667em"; -}; - -const mathmlBuilder$2 = (group, style) => { - let node; - - if (group.symbol) { - // This is a symbol. Just add the symbol. - node = new MathNode("mo", [makeText(group.name, group.mode)]); - if (noSuccessor.includes(group.name)) { - node.setAttribute("largeop", "false"); - } else { - node.setAttribute("movablelimits", "false"); - } - if (group.fromMathOp) { setSpacing(node); } - } else if (group.body) { - // This is an operator with children. Add them. - node = new MathNode("mo", buildExpression(group.body, style)); - if (group.fromMathOp) { setSpacing(node); } - } else { - // This is a text operator. Add all of the characters from the operator's name. - node = new MathNode("mi", [new TextNode(group.name.slice(1))]); - - if (!group.parentIsSupSub) { - // Append an invisible . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - const row = [node, operator]; - // Set spacing - if (group.needsLeadingSpace) { - const lead = new MathNode("mspace"); - lead.setAttribute("width", "0.1667em"); // thin space. - row.unshift(lead); - } - if (!group.isFollowedByDelimiter) { - const trail = new MathNode("mspace"); - trail.setAttribute("width", "0.1667em"); // thin space. - row.push(trail); - } - node = new MathNode("mrow", row); - } - } - - return node; -}; - -const singleCharBigOps = { - "\u220F": "\\prod", - "\u2210": "\\coprod", - "\u2211": "\\sum", - "\u22c0": "\\bigwedge", - "\u22c1": "\\bigvee", - "\u22c2": "\\bigcap", - "\u22c3": "\\bigcup", - "\u2a00": "\\bigodot", - "\u2a01": "\\bigoplus", - "\u2a02": "\\bigotimes", - "\u2a04": "\\biguplus", - "\u2a05": "\\bigsqcap", - "\u2a06": "\\bigsqcup", - "\u2a03": "\\bigcupdot", - "\u2a07": "\\bigdoublevee", - "\u2a08": "\\bigdoublewedge", - "\u2a09": "\\bigtimes" -}; - -defineFunction({ - type: "op", - names: [ - "\\coprod", - "\\bigvee", - "\\bigwedge", - "\\biguplus", - "\\bigcupplus", - "\\bigcupdot", - "\\bigcap", - "\\bigcup", - "\\bigdoublevee", - "\\bigdoublewedge", - "\\intop", - "\\prod", - "\\sum", - "\\bigotimes", - "\\bigoplus", - "\\bigodot", - "\\bigsqcap", - "\\bigsqcup", - "\\bigtimes", - "\\smallint", - "\u220F", - "\u2210", - "\u2211", - "\u22c0", - "\u22c1", - "\u22c2", - "\u22c3", - "\u2a00", - "\u2a01", - "\u2a02", - "\u2a03", - "\u2a04", - "\u2a05", - "\u2a06", - "\u2a07", - "\u2a08", - "\u2a09" - ], - props: { - numArgs: 0 - }, - handler: ({ parser, funcName }, args) => { - let fName = funcName; - if (fName.length === 1) { - fName = singleCharBigOps[fName]; - } - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: true, - stack: false, // This is true for \stackrel{}, not here. - name: fName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// Note: calling defineFunction with a type that's already been defined only -// works because the same mathmlBuilder is being used. -defineFunction({ - type: "op", - names: ["\\mathop"], - props: { - numArgs: 1, - primitive: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - // It would be convienient to just wrap a around the argument. - // But if the argument is a or , that would be invalid MathML. - // In that case, we instead promote the text contents of the body to the parent. - const arr = (body.body) ? body.body : [body]; - const isSymbol = arr.length === 1 && ordAtomTypes.includes(arr[0].type); - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: isSymbol, - fromMathOp: true, - stack: false, - name: isSymbol ? arr[0].text : null, - body: isSymbol ? null : ordargument(body) - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// There are 2 flags for operators; whether they produce limits in -// displaystyle, and whether they are symbols and should grow in -// displaystyle. These four groups cover the four possible choices. - -const singleCharIntegrals = { - "\u222b": "\\int", - "\u222c": "\\iint", - "\u222d": "\\iiint", - "\u222e": "\\oint", - "\u222f": "\\oiint", - "\u2230": "\\oiiint", - "\u2231": "\\intclockwise", - "\u2232": "\\varointclockwise", - "\u2a0c": "\\iiiint", - "\u2a0d": "\\intbar", - "\u2a0e": "\\intBar", - "\u2a0f": "\\fint", - "\u2a12": "\\rppolint", - "\u2a13": "\\scpolint", - "\u2a15": "\\pointint", - "\u2a16": "\\sqint", - "\u2a17": "\\intlarhk", - "\u2a18": "\\intx", - "\u2a19": "\\intcap", - "\u2a1a": "\\intcup" -}; - -// No limits, not symbols -defineFunction({ - type: "op", - names: [ - "\\arcsin", - "\\arccos", - "\\arctan", - "\\arctg", - "\\arcctg", - "\\arg", - "\\ch", - "\\cos", - "\\cosec", - "\\cosh", - "\\cot", - "\\cotg", - "\\coth", - "\\csc", - "\\ctg", - "\\cth", - "\\deg", - "\\dim", - "\\exp", - "\\hom", - "\\ker", - "\\lg", - "\\ln", - "\\log", - "\\sec", - "\\sin", - "\\sinh", - "\\sh", - "\\sgn", - "\\tan", - "\\tanh", - "\\tg", - "\\th" - ], - props: { - numArgs: 0 - }, - handler({ parser, funcName }) { - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "op", - mode: parser.mode, - limits: false, - parentIsSupSub: false, - symbol: false, - stack: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), - name: funcName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// Limits, not symbols -defineFunction({ - type: "op", - names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"], - props: { - numArgs: 0 - }, - handler({ parser, funcName }) { - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: false, - stack: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), - name: funcName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// No limits, symbols -defineFunction({ - type: "op", - names: [ - "\\int", - "\\iint", - "\\iiint", - "\\iiiint", - "\\oint", - "\\oiint", - "\\oiiint", - "\\intclockwise", - "\\varointclockwise", - "\\intbar", - "\\intBar", - "\\fint", - "\\rppolint", - "\\scpolint", - "\\pointint", - "\\sqint", - "\\intlarhk", - "\\intx", - "\\intcap", - "\\intcup", - "\u222b", - "\u222c", - "\u222d", - "\u222e", - "\u222f", - "\u2230", - "\u2231", - "\u2232", - "\u2a0c", - "\u2a0d", - "\u2a0e", - "\u2a0f", - "\u2a12", - "\u2a13", - "\u2a15", - "\u2a16", - "\u2a17", - "\u2a18", - "\u2a19", - "\u2a1a" - ], - props: { - numArgs: 0, - allowedInArgument: true - }, - handler({ parser, funcName }) { - let fName = funcName; - if (fName.length === 1) { - fName = singleCharIntegrals[fName]; - } - return { - type: "op", - mode: parser.mode, - limits: false, - parentIsSupSub: false, - symbol: true, - stack: false, - name: fName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// NOTE: Unlike most builders, this one handles not only -// "operatorname", but also "supsub" since \operatorname* can -// affect super/subscripting. - -const mathmlBuilder$1 = (group, style) => { - let expression = buildExpression(group.body, style.withFont("mathrm")); - - // Is expression a string or has it something like a fraction? - let isAllString = true; // default - for (let i = 0; i < expression.length; i++) { - let node = expression[i]; - if (node instanceof MathNode) { - if ((node.type === "mrow" || node.type === "mpadded") && node.children.length === 1 && - node.children[0] instanceof MathNode) { - node = node.children[0]; - } else if (node.type === "mrow" && node.children.length === 2 && - node.children[0] instanceof MathNode && - node.children[1] instanceof MathNode && - node.children[1].type === "mspace" && !node.children[1].attributes.width && - node.children[1].children.length === 0) { - // This is a workaround for a Firefox bug that applies spacing to - // an with mathvariant="normal". - node = node.children[0]; - } - switch (node.type) { - case "mi": - case "mn": - case "ms": - case "mtext": - break; // Do nothing yet. - case "mspace": - { - if (node.attributes.width) { - const width = node.attributes.width.replace("em", ""); - const ch = spaceCharacter(Number(width)); - if (ch === "") { - isAllString = false; - } else { - expression[i] = new MathNode("mtext", [new TextNode(ch)]); - } - } - } - break - case "mo": { - const child = node.children[0]; - if (node.children.length === 1 && child instanceof TextNode) { - child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); - } else { - isAllString = false; - } - break - } - default: - isAllString = false; - } - } else { - isAllString = false; - } - } - - if (isAllString) { - // Write a single TextNode instead of multiple nested tags. - const word = expression.map((node) => node.toText()).join(""); - expression = [new TextNode(word)]; - } else if ( - expression.length === 1 - && ["mover", "munder"].includes(expression[0].type) && - (expression[0].children[0].type === "mi" || expression[0].children[0].type === "mtext") - ) { - expression[0].children[0].type = "mi"; - if (group.parentIsSupSub) { - return new MathNode("mrow", expression) - } else { - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - return newDocumentFragment([expression[0], operator]) - } - } - - let wrapper; - if (isAllString) { - wrapper = new MathNode("mi", expression); - if (expression[0].text.length === 1) { - wrapper.setAttribute("mathvariant", "normal"); - } - } else { - wrapper = new MathNode("mrow", expression); - } - - if (!group.parentIsSupSub) { - // Append an . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - const fragment = [wrapper, operator]; - if (group.needsLeadingSpace) { - // LaTeX gives operator spacing, but a gets ord spacing. - // So add a leading space. - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - fragment.unshift(space); - } - if (!group.isFollowedByDelimiter) { - const trail = new MathNode("mspace"); - trail.setAttribute("width", "0.1667em"); // thin space. - fragment.push(trail); - } - return newDocumentFragment(fragment) - } - - return wrapper -}; - -// \operatorname -// amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@ -defineFunction({ - type: "operatorname", - names: ["\\operatorname@", "\\operatornamewithlimits"], - props: { - numArgs: 1, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const body = args[0]; - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "operatorname", - mode: parser.mode, - body: ordargument(body), - alwaysHandleSupSub: (funcName === "\\operatornamewithlimits"), - limits: false, - parentIsSupSub: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType) - }; - }, - mathmlBuilder: mathmlBuilder$1 -}); - -defineMacro("\\operatorname", - "\\@ifstar\\operatornamewithlimits\\operatorname@"); - -defineFunctionBuilders({ - type: "ordgroup", - mathmlBuilder(group, style) { - return buildExpressionRow(group.body, style, group.semisimple); - } -}); - -defineFunction({ - type: "phantom", - names: ["\\phantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "phantom", - mode: parser.mode, - body: ordargument(body) - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(group.body, style); - return new MathNode("mphantom", inner); - } -}); - -defineFunction({ - type: "hphantom", - names: ["\\hphantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "hphantom", - mode: parser.mode, - body - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", inner); - const node = new MathNode("mpadded", [phantom]); - node.setAttribute("height", "0px"); - node.setAttribute("depth", "0px"); - return node; - } -}); - -defineFunction({ - type: "vphantom", - names: ["\\vphantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "vphantom", - mode: parser.mode, - body - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", inner); - const node = new MathNode("mpadded", [phantom]); - node.setAttribute("width", "0px"); - return node; - } -}); - -// In LaTeX, \pmb is a simulation of bold font. -// The version of \pmb in ambsy.sty works by typesetting three copies of the argument -// with small offsets. We use CSS font-weight:bold. - -defineFunction({ - type: "pmb", - names: ["\\pmb"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "pmb", - mode: parser.mode, - body: ordargument(args[0]) - } - }, - mathmlBuilder(group, style) { - const inner = buildExpression(group.body, style); - // Wrap with an element. - const node = wrapWithMstyle(inner); - node.setAttribute("style", "font-weight:bold"); - return node - } -}); - -// \raise, \lower, and \raisebox - -const mathmlBuilder = (group, style) => { - const newStyle = style.withLevel(StyleLevel.TEXT); - const node = new MathNode("mpadded", [buildGroup$1(group.body, newStyle)]); - const dy = calculateSize(group.dy, style); - node.setAttribute("voffset", dy.number + dy.unit); - // Add padding, which acts to increase height in Chromium. - // TODO: Figure out some way to change height in Firefox w/o breaking Chromium. - if (dy.number > 0) { - node.style.padding = dy.number + dy.unit + " 0 0 0"; - } else { - node.style.padding = "0 0 " + Math.abs(dy.number) + dy.unit + " 0"; - } - return node -}; - -defineFunction({ - type: "raise", - names: ["\\raise", "\\lower"], - props: { - numArgs: 2, - argTypes: ["size", "primitive"], - primitive: true - }, - handler({ parser, funcName }, args) { - const amount = assertNodeType(args[0], "size").value; - if (funcName === "\\lower") { amount.number *= -1; } - const body = args[1]; - return { - type: "raise", - mode: parser.mode, - dy: amount, - body - }; - }, - mathmlBuilder -}); - - -defineFunction({ - type: "raise", - names: ["\\raisebox"], - props: { - numArgs: 2, - argTypes: ["size", "hbox"], - allowedInText: true - }, - handler({ parser, funcName }, args) { - const amount = assertNodeType(args[0], "size").value; - const body = args[1]; - return { - type: "raise", - mode: parser.mode, - dy: amount, - body - }; - }, - mathmlBuilder -}); - -defineFunction({ - type: "ref", - names: ["\\ref", "\\eqref"], - props: { - numArgs: 1, - argTypes: ["raw"] - }, - handler({ parser, funcName }, args) { - return { - type: "ref", - mode: parser.mode, - funcName, - string: args[0].string.replace(invalidIdRegEx, "") - }; - }, - mathmlBuilder(group, style) { - // Create an empty node. Set a class and an href attribute. - // The post-processor will populate with the target's tag or equation number. - const classes = group.funcName === "\\ref" ? ["tml-ref"] : ["tml-ref", "tml-eqref"]; - return new AnchorNode("#" + group.string, classes, null) - } -}); - -defineFunction({ - type: "reflect", - names: ["\\reflectbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "reflect", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - const node = buildGroup$1(group.body, style); - node.style.transform = "scaleX(-1)"; - return node - } -}); - -defineFunction({ - type: "internal", - names: ["\\relax"], - props: { - numArgs: 0, - allowedInText: true, - allowedInArgument: true - }, - handler({ parser }) { - return { - type: "internal", - mode: parser.mode - }; - } -}); - -defineFunction({ - type: "rule", - names: ["\\rule"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - allowedInMath: true, - argTypes: ["size", "size", "size"] - }, - handler({ parser }, args, optArgs) { - const shift = optArgs[0]; - const width = assertNodeType(args[0], "size"); - const height = assertNodeType(args[1], "size"); - return { - type: "rule", - mode: parser.mode, - shift: shift && assertNodeType(shift, "size").value, - width: width.value, - height: height.value - }; - }, - mathmlBuilder(group, style) { - const width = calculateSize(group.width, style); - const height = calculateSize(group.height, style); - const shift = group.shift - ? calculateSize(group.shift, style) - : { number: 0, unit: "em" }; - const color = (style.color && style.getColor()) || "black"; - - const rule = new MathNode("mspace"); - if (width.number > 0 && height.number > 0) { - rule.setAttribute("mathbackground", color); - } - rule.setAttribute("width", width.number + width.unit); - rule.setAttribute("height", height.number + height.unit); - if (shift.number === 0) { return rule } - - const wrapper = new MathNode("mpadded", [rule]); - if (shift.number >= 0) { - wrapper.setAttribute("height", "+" + shift.number + shift.unit); - } else { - wrapper.setAttribute("height", shift.number + shift.unit); - wrapper.setAttribute("depth", "+" + -shift.number + shift.unit); - } - wrapper.setAttribute("voffset", shift.number + shift.unit); - return wrapper; - } -}); - -const numRegEx = /^[0-9]$/; -const unicodeNumSubs = { - '0': '₀', - '1': '₁', - '2': '₂', - '3': '₃', - '4': '₄', - '5': '₅', - '6': '₆', - '7': '₇', - '8': '₈', - '9': '₉' -}; -const unicodeNumSups = { - '0': '⁰', - '1': '¹', - '2': '²', - '3': '³', - '4': '⁴', - '5': '⁵', - '6': '⁶', - '7': '⁷', - '8': '⁸', - '9': '⁹' -}; - -defineFunction({ - type: "sfrac", - names: ["\\sfrac"], - props: { - numArgs: 2, - allowedInText: true, - allowedInMath: true - }, - handler({ parser }, args) { - let numerator = ""; - for (const node of args[0].body) { - if (node.type !== "textord" || !numRegEx.test(node.text)) { - throw new ParseError("Numerator must be an integer.", node) - } - numerator += node.text; - } - let denominator = ""; - for (const node of args[1].body) { - if (node.type !== "textord" || !numRegEx.test(node.text)) { - throw new ParseError("Denominator must be an integer.", node) - } - denominator += node.text; - } - return { - type: "sfrac", - mode: parser.mode, - numerator, - denominator - }; - }, - mathmlBuilder(group, style) { - const numerator = group.numerator.split('').map(c => unicodeNumSups[c]).join(''); - const denominator = group.denominator.split('').map(c => unicodeNumSubs[c]).join(''); - // Use a fraction slash. - const text = new TextNode(numerator + "\u2044" + denominator, group.mode, style); - return new MathNode("mn", [text], ["special-fraction"]) - } -}); - -// The size mappings are taken from TeX with \normalsize=10pt. -// We don't have to track script level. MathML does that. -const sizeMap = { - "\\tiny": 0.5, - "\\sixptsize": 0.6, - "\\Tiny": 0.6, - "\\scriptsize": 0.7, - "\\footnotesize": 0.8, - "\\small": 0.9, - "\\normalsize": 1.0, - "\\large": 1.2, - "\\Large": 1.44, - "\\LARGE": 1.728, - "\\huge": 2.074, - "\\Huge": 2.488 -}; - -defineFunction({ - type: "sizing", - names: [ - "\\tiny", - "\\sixptsize", - "\\Tiny", - "\\scriptsize", - "\\footnotesize", - "\\small", - "\\normalsize", - "\\large", - "\\Large", - "\\LARGE", - "\\huge", - "\\Huge" - ], - props: { - numArgs: 0, - allowedInText: true - }, - handler: ({ breakOnTokenText, funcName, parser }, args) => { - if (parser.settings.strict && parser.mode === "math") { - // eslint-disable-next-line no-console - console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`); - } - const body = parser.parseExpression(false, breakOnTokenText, true); - return { - type: "sizing", - mode: parser.mode, - funcName, - body - }; - }, - mathmlBuilder: (group, style) => { - const newStyle = style.withFontSize(sizeMap[group.funcName]); - const inner = buildExpression(group.body, newStyle); - // Wrap with an element. - const node = wrapWithMstyle(inner); - const factor = (sizeMap[group.funcName] / style.fontSize).toFixed(4); - node.setAttribute("mathsize", factor + "em"); - return node; - } -}); - -// smash, with optional [tb], as in AMS - -defineFunction({ - type: "smash", - names: ["\\smash"], - props: { - numArgs: 1, - numOptionalArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args, optArgs) => { - let smashHeight = false; - let smashDepth = false; - const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup"); - if (tbArg) { - // Optional [tb] argument is engaged. - // ref: amsmath: \renewcommand{\smash}[1][tb]{% - // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}% - let letter = ""; - for (let i = 0; i < tbArg.body.length; ++i) { - const node = tbArg.body[i]; - // TODO: Write an AssertSymbolNode - letter = node.text; - if (letter === "t") { - smashHeight = true; - } else if (letter === "b") { - smashDepth = true; - } else { - smashHeight = false; - smashDepth = false; - break; - } - } - } else { - smashHeight = true; - smashDepth = true; - } - - const body = args[0]; - return { - type: "smash", - mode: parser.mode, - body, - smashHeight, - smashDepth - }; - }, - mathmlBuilder: (group, style) => { - const node = new MathNode("mpadded", [buildGroup$1(group.body, style)]); - - if (group.smashHeight) { - node.setAttribute("height", "0px"); - } - - if (group.smashDepth) { - node.setAttribute("depth", "0px"); - } - - return node; - } -}); - -// Letters that are x-height w/o a descender. -const xHeights = ['a', 'c', 'e', 'ı', 'm', 'n', 'o', 'r', 's', 'u', 'v', 'w', 'x', 'z', 'α', - 'ε', 'ι', 'κ', 'ν', 'ο', 'π', 'σ', 'τ', 'υ', 'ω', '\\alpha', '\\epsilon', "\\iota", - '\\kappa', '\\nu', '\\omega', '\\pi', '\\tau', '\\omega']; - -defineFunction({ - type: "sqrt", - names: ["\\sqrt"], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser }, args, optArgs) { - const index = optArgs[0]; - const body = args[0]; - // Check if the body consists entirely of an x-height letter. - // TODO: Remove this check after Chromium is fixed. - if (body.body && body.body.length === 1 && body.body[0].text && - xHeights.includes(body.body[0].text)) { - // Chromium does not put enough space above an x-height letter. - // Insert a strut. - body.body.push({ - "type": "rule", - "mode": "math", - "shift": null, - "width": { "number": 0, "unit": "pt" }, - "height": { "number": 0.5, "unit": "em" } - }); - } - return { - type: "sqrt", - mode: parser.mode, - body, - index - }; - }, - mathmlBuilder(group, style) { - const { body, index } = group; - return index - ? new MathNode("mroot", [ - buildGroup$1(body, style), - buildGroup$1(index, style.incrementLevel()) - ]) - : new MathNode("msqrt", [buildGroup$1(body, style)]); - } -}); - -const styleMap = { - display: 0, - text: 1, - script: 2, - scriptscript: 3 -}; - -const styleAttributes = { - display: ["0", "true"], - text: ["0", "false"], - script: ["1", "false"], - scriptscript: ["2", "false"] -}; - -defineFunction({ - type: "styling", - names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ breakOnTokenText, funcName, parser }, args) { - // parse out the implicit body - const body = parser.parseExpression(true, breakOnTokenText, true); - - const scriptLevel = funcName.slice(1, funcName.length - 5); - return { - type: "styling", - mode: parser.mode, - // Figure out what scriptLevel to use by pulling out the scriptLevel from - // the function name - scriptLevel, - body - }; - }, - mathmlBuilder(group, style) { - // Figure out what scriptLevel we're changing to. - const newStyle = style.withLevel(styleMap[group.scriptLevel]); - // The style argument in the next line does NOT directly set a MathML script level. - // It just tracks the style level, in case we need to know it for supsub or mathchoice. - const inner = buildExpression(group.body, newStyle); - // Wrap with an element. - const node = wrapWithMstyle(inner); - - const attr = styleAttributes[group.scriptLevel]; - - // Here is where we set the MathML script level. - node.setAttribute("scriptlevel", attr[0]); - node.setAttribute("displaystyle", attr[1]); - - return node; - } -}); - -/** - * Sometimes, groups perform special rules when they have superscripts or - * subscripts attached to them. This function lets the `supsub` group know that - * Sometimes, groups perform special rules when they have superscripts or - * its inner element should handle the superscripts and subscripts instead of - * handling them itself. - */ - -// Helpers -const symbolRegEx = /^m(over|under|underover)$/; - -// From the KaTeX font metrics, identify letters that encroach on a superscript. -const smallPad = "DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ"; -const mediumPad = "BCEFGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ"; -const largePad = "AJdfΔΛ"; - -// Super scripts and subscripts, whose precise placement can depend on other -// functions that precede them. -defineFunctionBuilders({ - type: "supsub", - mathmlBuilder(group, style) { - // Is the inner group a relevant horizontal brace or bracket? - let isBracket = false; - let isOver; - let isSup; - let appendApplyFunction = false; - let appendSpace = false; - let needsLeadingSpace = false; - - if (group.base && group.base.type === "horizBracket") { - isSup = !!group.sup; - if (isSup === group.base.isOver) { - isBracket = true; - isOver = group.base.isOver; - } - } - - if (group.base && !group.stack && - (group.base.type === "op" || group.base.type === "operatorname")) { - group.base.parentIsSupSub = true; - appendApplyFunction = !group.base.symbol; - appendSpace = appendApplyFunction && !group.isFollowedByDelimiter; - needsLeadingSpace = group.base.needsLeadingSpace; - } - - const children = group.stack && group.base.body.length === 1 - ? [buildGroup$1(group.base.body[0], style)] - : [buildGroup$1(group.base, style)]; - - // Note regarding scriptstyle level. - // (Sub|super)scripts should not shrink beyond MathML scriptlevel 2 aka \scriptscriptstyle - // Ref: https://w3c.github.io/mathml-core/#the-displaystyle-and-scriptlevel-attributes - // (BTW, MathML scriptlevel 2 is equal to Temml level 3.) - // But Chromium continues to shrink the (sub|super)scripts. So we explicitly set scriptlevel 2. - - const childStyle = style.inSubOrSup(); - if (group.sub) { - const sub = buildGroup$1(group.sub, childStyle); - if (style.level === 3) { sub.setAttribute("scriptlevel", "2"); } - children.push(sub); - } - - if (group.sup) { - const sup = buildGroup$1(group.sup, childStyle); - if (style.level === 3) { sup.setAttribute("scriptlevel", "2"); } - if (group.base && group.base.text && group.base.text.length === 1) { - // Make an italic correction on the superscript. - const text = group.base.text; - if (smallPad.indexOf(text) > -1) { - sup.classes.push("tml-sml-pad"); - } else if (mediumPad.indexOf(text) > -1) { - sup.classes.push("tml-med-pad"); - } else if (largePad.indexOf(text) > -1) { - sup.classes.push("tml-lrg-pad"); - } - } - children.push(sup); - } - - let nodeType; - if (isBracket) { - nodeType = isOver ? "mover" : "munder"; - } else if (!group.sub) { - const base = group.base; - if ( - base && - base.type === "op" && - base.limits && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "mover"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (base.limits || style.level === StyleLevel.DISPLAY) - ) { - nodeType = "mover"; - } else { - nodeType = "msup"; - } - } else if (!group.sup) { - const base = group.base; - if (group.stack) { - nodeType = "munder"; - } else if ( - base && - base.type === "op" && - base.limits && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "munder"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (base.limits || style.level === StyleLevel.DISPLAY) - ) { - nodeType = "munder"; - } else { - nodeType = "msub"; - } - } else { - const base = group.base; - if (base && ((base.type === "op" && base.limits) || base.type === "multiscript") && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "munderover"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (style.level === StyleLevel.DISPLAY || base.limits) - ) { - nodeType = "munderover"; - } else { - nodeType = "msubsup"; - } - } - - let node = new MathNode(nodeType, children); - if (appendApplyFunction) { - // Append an . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - if (needsLeadingSpace) { - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - node = newDocumentFragment([space, node, operator]); - } else { - node = newDocumentFragment([node, operator]); - } - if (appendSpace) { - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - node.children.push(space); - } - } else if (symbolRegEx.test(nodeType)) { - // Wrap in a . Otherwise Firefox stretchy parens will not stretch to include limits. - node = new MathNode("mrow", [node]); - } - - return node - } -}); - -// Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js. - -const short = ["\\shortmid", "\\nshortmid", "\\shortparallel", - "\\nshortparallel", "\\smallsetminus"]; - -const arrows = ["\\Rsh", "\\Lsh", "\\restriction"]; - -const isArrow = str => { - if (str.length === 1) { - const codePoint = str.codePointAt(0); - return (0x218f < codePoint && codePoint < 0x2200) - } - return str.indexOf("arrow") > -1 || str.indexOf("harpoon") > -1 || arrows.includes(str) -}; - -defineFunctionBuilders({ - type: "atom", - mathmlBuilder(group, style) { - const node = new MathNode("mo", [makeText(group.text, group.mode)]); - if (group.family === "punct") { - node.setAttribute("separator", "true"); - } else if (group.family === "open" || group.family === "close") { - // Delims built here should not stretch vertically. - // See delimsizing.js for stretchy delims. - if (group.family === "open") { - node.setAttribute("form", "prefix"); - // Set an explicit attribute for stretch. Otherwise Firefox may do it wrong. - node.setAttribute("stretchy", "false"); - } else if (group.family === "close") { - node.setAttribute("form", "postfix"); - node.setAttribute("stretchy", "false"); - } - } else if (group.text === "\\mid") { - // Firefox messes up this spacing if at the end of an . See it explicitly. - node.setAttribute("lspace", "0.22em"); // medium space - node.setAttribute("rspace", "0.22em"); - node.setAttribute("stretchy", "false"); - } else if (group.family === "rel" && isArrow(group.text)) { - node.setAttribute("stretchy", "false"); - } else if (short.includes(group.text)) { - node.setAttribute("mathsize", "70%"); - } else if (group.text === ":") { - // ":" is not in the MathML operator dictionary. Give it BIN spacing. - node.attributes.lspace = "0.2222em"; - node.attributes.rspace = "0.2222em"; - } else if (group.needsSpacing) { - // Fix a MathML bug that occurs when a is between two elements. - if (group.family === "bin") { - return new MathNode("mrow", [padding(0.222), node, padding(0.222)]) - } else { - // REL spacing - return new MathNode("mrow", [padding(0.2778), node, padding(0.2778)]) - } - } - return node; - } -}); - -/** - * Maps TeX font commands to "mathvariant" attribute in buildMathML.js - */ -const fontMap = { - // styles - mathbf: "bold", - mathrm: "normal", - textit: "italic", - mathit: "italic", - mathnormal: "italic", - - // families - mathbb: "double-struck", - mathcal: "script", - mathfrak: "fraktur", - mathscr: "script", - mathsf: "sans-serif", - mathtt: "monospace" -}; - -/** - * Returns the math variant as a string or null if none is required. - */ -const getVariant = function(group, style) { - // Handle font specifiers as best we can. - // Chromium does not support the MathML mathvariant attribute. - // So we'll use Unicode replacement characters instead. - // But first, determine the math variant. - - // Deal with the \textit, \textbf, etc., functions. - if (style.fontFamily === "texttt") { - return "monospace" - } else if (style.fontFamily === "textsc") { - return "normal"; // handled via character substitution in symbolsOrd.js. - } else if (style.fontFamily === "textsf") { - if (style.fontShape === "textit" && style.fontWeight === "textbf") { - return "sans-serif-bold-italic" - } else if (style.fontShape === "textit") { - return "sans-serif-italic" - } else if (style.fontWeight === "textbf") { - return "sans-serif-bold" - } else { - return "sans-serif" - } - } else if (style.fontShape === "textit" && style.fontWeight === "textbf") { - return "bold-italic" - } else if (style.fontShape === "textit") { - return "italic" - } else if (style.fontWeight === "textbf") { - return "bold" - } - - // Deal with the \mathit, mathbf, etc, functions. - const font = style.font; - if (!font || font === "mathnormal") { - return null - } - - const mode = group.mode; - switch (font) { - case "mathit": - return "italic" - case "mathrm": { - const codePoint = group.text.codePointAt(0); - // LaTeX \mathrm returns italic for Greek characters. - return (0x03ab < codePoint && codePoint < 0x03cf) ? "italic" : "normal" - } - case "greekItalic": - return "italic" - case "up@greek": - return "normal" - case "boldsymbol": - case "mathboldsymbol": - return "bold-italic" - case "mathbf": - return "bold" - case "mathbb": - return "double-struck" - case "mathfrak": - return "fraktur" - case "mathscr": - case "mathcal": - return "script" - case "mathsf": - return "sans-serif" - case "mathsfit": - return "sans-serif-italic" - case "mathtt": - return "monospace" - } - - let text = group.text; - if (symbols[mode][text] && symbols[mode][text].replace) { - text = symbols[mode][text].replace; - } - - return Object.prototype.hasOwnProperty.call(fontMap, font) ? fontMap[font] : null -}; - -// Chromium does not support the MathML `mathvariant` attribute. -// Instead, we replace ASCII characters with Unicode characters that -// are defined in the font as bold, italic, double-struck, etc. -// This module identifies those Unicode code points. - -// First, a few helpers. -const script = Object.freeze({ - B: 0x20EA, // Offset from ASCII B to Unicode script B - E: 0x20EB, - F: 0x20EB, - H: 0x20C3, - I: 0x20C7, - L: 0x20C6, - M: 0x20E6, - R: 0x20C9, - e: 0x20CA, - g: 0x20A3, - o: 0x20C5 -}); - -const frak = Object.freeze({ - C: 0x20EA, - H: 0x20C4, - I: 0x20C8, - R: 0x20CA, - Z: 0x20CE -}); - -const bbb = Object.freeze({ - C: 0x20BF, // blackboard bold - H: 0x20C5, - N: 0x20C7, - P: 0x20C9, - Q: 0x20C9, - R: 0x20CB, - Z: 0x20CA -}); - -const bold = Object.freeze({ - "\u03f5": 0x1D2E7, // lunate epsilon - "\u03d1": 0x1D30C, // vartheta - "\u03f0": 0x1D2EE, // varkappa - "\u03c6": 0x1D319, // varphi - "\u03f1": 0x1D2EF, // varrho - "\u03d6": 0x1D30B // varpi -}); - -const boldItalic = Object.freeze({ - "\u03f5": 0x1D35B, // lunate epsilon - "\u03d1": 0x1D380, // vartheta - "\u03f0": 0x1D362, // varkappa - "\u03c6": 0x1D38D, // varphi - "\u03f1": 0x1D363, // varrho - "\u03d6": 0x1D37F // varpi -}); - -const boldsf = Object.freeze({ - "\u03f5": 0x1D395, // lunate epsilon - "\u03d1": 0x1D3BA, // vartheta - "\u03f0": 0x1D39C, // varkappa - "\u03c6": 0x1D3C7, // varphi - "\u03f1": 0x1D39D, // varrho - "\u03d6": 0x1D3B9 // varpi -}); - -const bisf = Object.freeze({ - "\u03f5": 0x1D3CF, // lunate epsilon - "\u03d1": 0x1D3F4, // vartheta - "\u03f0": 0x1D3D6, // varkappa - "\u03c6": 0x1D401, // varphi - "\u03f1": 0x1D3D7, // varrho - "\u03d6": 0x1D3F3 // varpi -}); - -// Code point offsets below are derived from https://www.unicode.org/charts/PDF/U1D400.pdf -const offset = Object.freeze({ - upperCaseLatin: { // A-Z - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D3BF }, - "italic": ch => { return 0x1D3F3 }, - "bold-italic": ch => { return 0x1D427 }, - "script": ch => { return script[ch] || 0x1D45B }, - "script-bold": ch => { return 0x1D48F }, - "fraktur": ch => { return frak[ch] || 0x1D4C3 }, - "fraktur-bold": ch => { return 0x1D52B }, - "double-struck": ch => { return bbb[ch] || 0x1D4F7 }, - "sans-serif": ch => { return 0x1D55F }, - "sans-serif-bold": ch => { return 0x1D593 }, - "sans-serif-italic": ch => { return 0x1D5C7 }, - "sans-serif-bold-italic": ch => { return 0x1D63C }, - "monospace": ch => { return 0x1D62F } - }, - lowerCaseLatin: { // a-z - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D3B9 }, - "italic": ch => { return ch === "h" ? 0x20A6 : 0x1D3ED }, - "bold-italic": ch => { return 0x1D421 }, - "script": ch => { return script[ch] || 0x1D455 }, - "script-bold": ch => { return 0x1D489 }, - "fraktur": ch => { return 0x1D4BD }, - "fraktur-bold": ch => { return 0x1D525 }, - "double-struck": ch => { return 0x1D4F1 }, - "sans-serif": ch => { return 0x1D559 }, - "sans-serif-bold": ch => { return 0x1D58D }, - "sans-serif-italic": ch => { return 0x1D5C1 }, - "sans-serif-bold-italic": ch => { return 0x1D5F5 }, - "monospace": ch => { return 0x1D629 } - }, - upperCaseGreek: { // A-Ω - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D317 }, - "italic": ch => { return 0x1D351 }, - // \boldsymbol actually returns upright bold for upperCaseGreek - "bold-italic": ch => { return 0x1D317 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - // Unicode has no code points for regular-weight san-serif Greek. Use bold. - "sans-serif": ch => { return 0x1D3C5 }, - "sans-serif-bold": ch => { return 0x1D3C5 }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0x1D3FF }, - "monospace": ch => { return 0 } - }, - lowerCaseGreek: { // α-ω - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D311 }, - "italic": ch => { return 0x1D34B }, - "bold-italic": ch => { return ch === "\u03d5" ? 0x1D37E : 0x1D385 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - // Unicode has no code points for regular-weight san-serif Greek. Use bold. - "sans-serif": ch => { return 0x1D3BF }, - "sans-serif-bold": ch => { return 0x1D3BF }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0x1D3F9 }, - "monospace": ch => { return 0 } - }, - varGreek: { // \varGamma, etc - "normal": ch => { return 0 }, - "bold": ch => { return bold[ch] || -51 }, - "italic": ch => { return 0 }, - "bold-italic": ch => { return boldItalic[ch] || 0x3A }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - "sans-serif": ch => { return boldsf[ch] || 0x74 }, - "sans-serif-bold": ch => { return boldsf[ch] || 0x74 }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return bisf[ch] || 0xAE }, - "monospace": ch => { return 0 } - }, - numeral: { // 0-9 - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D79E }, - "italic": ch => { return 0 }, - "bold-italic": ch => { return 0 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0x1D7A8 }, - "sans-serif": ch => { return 0x1D7B2 }, - "sans-serif-bold": ch => { return 0x1D7BC }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0 }, - "monospace": ch => { return 0x1D7C6 } - } -}); - -const variantChar = (ch, variant) => { - const codePoint = ch.codePointAt(0); - const block = 0x40 < codePoint && codePoint < 0x5b - ? "upperCaseLatin" - : 0x60 < codePoint && codePoint < 0x7b - ? "lowerCaseLatin" - : (0x390 < codePoint && codePoint < 0x3AA) - ? "upperCaseGreek" - : 0x3B0 < codePoint && codePoint < 0x3CA || ch === "\u03d5" - ? "lowerCaseGreek" - : 0x1D6E1 < codePoint && codePoint < 0x1D6FC || bold[ch] - ? "varGreek" - : (0x2F < codePoint && codePoint < 0x3A) - ? "numeral" - : "other"; - return block === "other" - ? ch - : String.fromCodePoint(codePoint + offset[block][variant](ch)) -}; - -const smallCaps = Object.freeze({ - a: "ᴀ", - b: "ʙ", - c: "ᴄ", - d: "ᴅ", - e: "ᴇ", - f: "ꜰ", - g: "ɢ", - h: "ʜ", - i: "ɪ", - j: "ᴊ", - k: "ᴋ", - l: "ʟ", - m: "ᴍ", - n: "ɴ", - o: "ᴏ", - p: "ᴘ", - q: "ǫ", - r: "ʀ", - s: "s", - t: "ᴛ", - u: "ᴜ", - v: "ᴠ", - w: "ᴡ", - x: "x", - y: "ʏ", - z: "ᴢ" -}); - -// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in -// src/symbols.js. - -const numberRegEx = /^\d(?:[\d,.]*\d)?$/; -const latinRegEx = /[A-Ba-z]/; -const primes = new Set(["\\prime", "\\dprime", "\\trprime", "\\qprime", - "\\backprime", "\\backdprime", "\\backtrprime"]); - -const italicNumber = (text, variant, tag) => { - const mn = new MathNode(tag, [text]); - const wrapper = new MathNode("mstyle", [mn]); - wrapper.style["font-style"] = "italic"; - wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif"; - if (variant === "bold-italic") { wrapper.style["font-weight"] = "bold"; } - return wrapper -}; - -defineFunctionBuilders({ - type: "mathord", - mathmlBuilder(group, style) { - const text = makeText(group.text, group.mode, style); - const codePoint = text.text.codePointAt(0); - // Test for upper-case Greek - const defaultVariant = (0x0390 < codePoint && codePoint < 0x03aa) ? "normal" : "italic"; - const variant = getVariant(group, style) || defaultVariant; - if (variant === "script") { - text.text = variantChar(text.text, variant); - return new MathNode("mi", [text], [style.font]) - } else if (variant !== "italic") { - text.text = variantChar(text.text, variant); - } - let node = new MathNode("mi", [text]); - // TODO: Handle U+1D49C - U+1D4CF per https://www.unicode.org/charts/PDF/U1D400.pdf - if (variant === "normal") { - node.setAttribute("mathvariant", "normal"); - if (text.text.length === 1) { - // A Firefox bug will apply spacing here, but there should be none. Fix it. - const mspace = new MathNode("mspace", []); - node = new MathNode("mrow", [node, mspace]); - } - } - return node - } -}); - -defineFunctionBuilders({ - type: "textord", - mathmlBuilder(group, style) { - let ch = group.text; - const codePoint = ch.codePointAt(0); - if (style.fontFamily === "textsc") { - // Convert small latin letters to small caps. - if (96 < codePoint && codePoint < 123) { - ch = smallCaps[ch]; - } - } - const text = makeText(ch, group.mode, style); - const variant = getVariant(group, style) || "normal"; - - let node; - if (numberRegEx.test(group.text)) { - const tag = group.mode === "text" ? "mtext" : "mn"; - if (variant === "italic" || variant === "bold-italic") { - return italicNumber(text, variant, tag) - } else { - if (variant !== "normal") { - text.text = text.text.split("").map(c => variantChar(c, variant)).join(""); - } - node = new MathNode(tag, [text]); - } - } else if (group.mode === "text") { - if (variant !== "normal") { - text.text = variantChar(text.text, variant); - } - node = new MathNode("mtext", [text]); - } else if (primes.has(group.text)) { - node = new MathNode("mo", [text]); - // TODO: If/when Chromium uses ssty variant for prime, remove the next line. - node.classes.push("tml-prime"); - } else { - const origText = text.text; - if (variant !== "italic") { - text.text = variantChar(text.text, variant); - } - node = new MathNode("mi", [text]); - if (text.text === origText && latinRegEx.test(origText)) { - node.setAttribute("mathvariant", "italic"); - } - } - return node - } -}); - -// A map of CSS-based spacing functions to their CSS class. -const cssSpace = { - "\\nobreak": "nobreak", - "\\allowbreak": "allowbreak" -}; - -// A lookup table to determine whether a spacing function/symbol should be -// treated like a regular space character. If a symbol or command is a key -// in this table, then it should be a regular space character. Furthermore, -// the associated value may have a `className` specifying an extra CSS class -// to add to the created `span`. -const regularSpace = { - " ": {}, - "\\ ": {}, - "~": { - className: "nobreak" - }, - "\\space": {}, - "\\nobreakspace": { - className: "nobreak" - } -}; - -// ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in -// src/symbols.js. -defineFunctionBuilders({ - type: "spacing", - mathmlBuilder(group, style) { - let node; - - if (Object.prototype.hasOwnProperty.call(regularSpace, group.text)) { - // Firefox does not render a space in a . So write a no-break space. - // TODO: If Firefox fixes that bug, uncomment the next line and write ch into the node. - //const ch = (regularSpace[group.text].className === "nobreak") ? "\u00a0" : " " - node = new MathNode("mtext", [new TextNode("\u00a0")]); - } else if (Object.prototype.hasOwnProperty.call(cssSpace, group.text)) { - // MathML 3.0 calls for nobreak to occur in an , not an - // Ref: https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs - node = new MathNode("mo"); - if (group.text === "\\nobreak") { - node.setAttribute("linebreak", "nobreak"); - } - } else { - throw new ParseError(`Unknown type of space "${group.text}"`) - } - - return node - } -}); - -defineFunctionBuilders({ - type: "tag" -}); - -// For a \tag, the work usually done in a mathmlBuilder is instead done in buildMathML.js. -// That way, a \tag can be pulled out of the parse tree and wrapped around the outer node. - -// Non-mathy text, possibly in a font -const textFontFamilies = { - "\\text": undefined, - "\\textrm": "textrm", - "\\textsf": "textsf", - "\\texttt": "texttt", - "\\textnormal": "textrm", - "\\textsc": "textsc" // small caps -}; - -const textFontWeights = { - "\\textbf": "textbf", - "\\textmd": "textmd" -}; - -const textFontShapes = { - "\\textit": "textit", - "\\textup": "textup" -}; - -const styleWithFont = (group, style) => { - const font = group.font; - // Checks if the argument is a font family or a font style. - if (!font) { - return style; - } else if (textFontFamilies[font]) { - return style.withTextFontFamily(textFontFamilies[font]); - } else if (textFontWeights[font]) { - return style.withTextFontWeight(textFontWeights[font]); - } else if (font === "\\emph") { - return style.fontShape === "textit" - ? style.withTextFontShape("textup") - : style.withTextFontShape("textit") - } - return style.withTextFontShape(textFontShapes[font]) -}; - -defineFunction({ - type: "text", - names: [ - // Font families - "\\text", - "\\textrm", - "\\textsf", - "\\texttt", - "\\textnormal", - "\\textsc", - // Font weights - "\\textbf", - "\\textmd", - // Font Shapes - "\\textit", - "\\textup", - "\\emph" - ], - props: { - numArgs: 1, - argTypes: ["text"], - allowedInArgument: true, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "text", - mode: parser.mode, - body: ordargument(body), - font: funcName - }; - }, - mathmlBuilder(group, style) { - const newStyle = styleWithFont(group, style); - const mrow = buildExpressionRow(group.body, newStyle); - return consolidateText(mrow) - } -}); - -// \vcenter: Vertically center the argument group on the math axis. - -defineFunction({ - type: "vcenter", - names: ["\\vcenter"], - props: { - numArgs: 1, - argTypes: ["original"], - allowedInText: false - }, - handler({ parser }, args) { - return { - type: "vcenter", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - // Use a math table to create vertically centered content. - const mtd = new MathNode("mtd", [buildGroup$1(group.body, style)]); - mtd.style.padding = "0"; - const mtr = new MathNode("mtr", [mtd]); - return new MathNode("mtable", [mtr]) - } -}); - -defineFunction({ - type: "verb", - names: ["\\verb"], - props: { - numArgs: 0, - allowedInText: true - }, - handler(context, args, optArgs) { - // \verb and \verb* are dealt with directly in Parser.js. - // If we end up here, it's because of a failure to match the two delimiters - // in the regex in Lexer.js. LaTeX raises the following error when \verb is - // terminated by end of line (or file). - throw new ParseError("\\verb ended by end of line instead of matching delimiter"); - }, - mathmlBuilder(group, style) { - const text = new TextNode(makeVerb(group)); - const node = new MathNode("mtext", [text]); - node.setAttribute("mathvariant", "monospace"); - return node; - } -}); - -/** - * Converts verb group into body string. - * - * \verb* replaces each space with an open box \u2423 - * \verb replaces each space with a no-break space \xA0 - */ -const makeVerb = (group) => group.body.replace(/ /g, group.star ? "\u2423" : "\xA0"); - -/** Include this to ensure that all functions are defined. */ - -const functions = _functions; - -/** - * The Lexer class handles tokenizing the input in various ways. Since our - * parser expects us to be able to backtrack, the lexer allows lexing from any - * given starting point. - * - * Its main exposed function is the `lex` function, which takes a position to - * lex from and a type of token to lex. It defers to the appropriate `_innerLex` - * function. - * - * The various `_innerLex` functions perform the actual lexing of different - * kinds. - */ - - -/* The following tokenRegex - * - matches typical whitespace (but not NBSP etc.) using its first two groups - * - does not match any control character \x00-\x1f except whitespace - * - does not match a bare backslash - * - matches any ASCII character except those just mentioned - * - does not match the BMP private use area \uE000-\uF8FF - * - does not match bare surrogate code units - * - matches any BMP character except for those just described - * - matches any valid Unicode surrogate pair - * - mathches numerals - * - matches a backslash followed by one or more whitespace characters - * - matches a backslash followed by one or more letters then whitespace - * - matches a backslash followed by any BMP character - * Capturing groups: - * [1] regular whitespace - * [2] backslash followed by whitespace - * [3] anything else, which may include: - * [4] left character of \verb* - * [5] left character of \verb - * [6] backslash followed by word, excluding any trailing whitespace - * Just because the Lexer matches something doesn't mean it's valid input: - * If there is no matching function or symbol definition, the Parser will - * still reject the input. - */ -const spaceRegexString = "[ \r\n\t]"; -const controlWordRegexString = "\\\\[a-zA-Z@]+"; -const controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]"; -const controlWordWhitespaceRegexString = `(${controlWordRegexString})${spaceRegexString}*`; -const controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*"; -const combiningDiacriticalMarkString = "[\u0300-\u036f]"; -const combiningDiacriticalMarksEndRegex = new RegExp(`${combiningDiacriticalMarkString}+$`); -const tokenRegexString = - `(${spaceRegexString}+)|` + // whitespace - `${controlSpaceRegexString}|` + // whitespace - "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint - `${combiningDiacriticalMarkString}*` + // ...plus accents - "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair - `${combiningDiacriticalMarkString}*` + // ...plus accents - "|\\\\verb\\*([^]).*?\\4" + // \verb* - "|\\\\verb([^*a-zA-Z]).*?\\5" + // \verb unstarred - `|${controlWordWhitespaceRegexString}` + // \macroName + spaces - `|${controlSymbolRegexString})`; // \\, \', etc. - -/** Main Lexer class */ -class Lexer { - constructor(input, settings) { - // Separate accents from characters - this.input = input; - this.settings = settings; - this.tokenRegex = new RegExp(tokenRegexString, 'g'); - // Category codes. The lexer only supports comment characters (14) for now. - // MacroExpander additionally distinguishes active (13). - this.catcodes = { - "%": 14, // comment character - "~": 13 // active character - }; - } - - setCatcode(char, code) { - this.catcodes[char] = code; - } - - /** - * This function lexes a single token. - */ - lex() { - const input = this.input; - const pos = this.tokenRegex.lastIndex; - if (pos === input.length) { - return new Token("EOF", new SourceLocation(this, pos, pos)); - } - const match = this.tokenRegex.exec(input); - if (match === null || match.index !== pos) { - throw new ParseError( - `Unexpected character: '${input[pos]}'`, - new Token(input[pos], new SourceLocation(this, pos, pos + 1)) - ); - } - const text = match[6] || match[3] || (match[2] ? "\\ " : " "); - - if (this.catcodes[text] === 14) { - // comment character - const nlIndex = input.indexOf("\n", this.tokenRegex.lastIndex); - if (nlIndex === -1) { - this.tokenRegex.lastIndex = input.length; // EOF - if (this.settings.strict) { - throw new ParseError("% comment has no terminating newline; LaTeX would " + - "fail because of commenting the end of math mode") - } - } else { - this.tokenRegex.lastIndex = nlIndex + 1; - } - return this.lex(); - } - - return new Token(text, new SourceLocation(this, pos, this.tokenRegex.lastIndex)); - } -} - -/** - * A `Namespace` refers to a space of nameable things like macros or lengths, - * which can be `set` either globally or local to a nested group, using an - * undo stack similar to how TeX implements this functionality. - * Performance-wise, `get` and local `set` take constant time, while global - * `set` takes time proportional to the depth of group nesting. - */ - - -class Namespace { - /** - * Both arguments are optional. The first argument is an object of - * built-in mappings which never change. The second argument is an object - * of initial (global-level) mappings, which will constantly change - * according to any global/top-level `set`s done. - */ - constructor(builtins = {}, globalMacros = {}) { - this.current = globalMacros; - this.builtins = builtins; - this.undefStack = []; - } - - /** - * Start a new nested group, affecting future local `set`s. - */ - beginGroup() { - this.undefStack.push({}); - } - - /** - * End current nested group, restoring values before the group began. - */ - endGroup() { - if (this.undefStack.length === 0) { - throw new ParseError( - "Unbalanced namespace destruction: attempt " + - "to pop global namespace; please report this as a bug" - ); - } - const undefs = this.undefStack.pop(); - for (const undef in undefs) { - if (Object.prototype.hasOwnProperty.call(undefs, undef )) { - if (undefs[undef] === undefined) { - delete this.current[undef]; - } else { - this.current[undef] = undefs[undef]; - } - } - } - } - - /** - * Detect whether `name` has a definition. Equivalent to - * `get(name) != null`. - */ - has(name) { - return Object.prototype.hasOwnProperty.call(this.current, name ) || - Object.prototype.hasOwnProperty.call(this.builtins, name ); - } - - /** - * Get the current value of a name, or `undefined` if there is no value. - * - * Note: Do not use `if (namespace.get(...))` to detect whether a macro - * is defined, as the definition may be the empty string which evaluates - * to `false` in JavaScript. Use `if (namespace.get(...) != null)` or - * `if (namespace.has(...))`. - */ - get(name) { - if (Object.prototype.hasOwnProperty.call(this.current, name )) { - return this.current[name]; - } else { - return this.builtins[name]; - } - } - - /** - * Set the current value of a name, and optionally set it globally too. - * Local set() sets the current value and (when appropriate) adds an undo - * operation to the undo stack. Global set() may change the undo - * operation at every level, so takes time linear in their number. - */ - set(name, value, global = false) { - if (global) { - // Global set is equivalent to setting in all groups. Simulate this - // by destroying any undos currently scheduled for this name, - // and adding an undo with the *new* value (in case it later gets - // locally reset within this environment). - for (let i = 0; i < this.undefStack.length; i++) { - delete this.undefStack[i][name]; - } - if (this.undefStack.length > 0) { - this.undefStack[this.undefStack.length - 1][name] = value; - } - } else { - // Undo this set at end of this group (possibly to `undefined`), - // unless an undo is already in place, in which case that older - // value is the correct one. - const top = this.undefStack[this.undefStack.length - 1]; - if (top && !Object.prototype.hasOwnProperty.call(top, name )) { - top[name] = this.current[name]; - } - } - this.current[name] = value; - } -} - -/** - * This file contains the “gullet” where macros are expanded - * until only non-macro tokens remain. - */ - - -// List of commands that act like macros but aren't defined as a macro, -// function, or symbol. Used in `isDefined`. -const implicitCommands = { - "^": true, // Parser.js - _: true, // Parser.js - "\\limits": true, // Parser.js - "\\nolimits": true // Parser.js -}; - -class MacroExpander { - constructor(input, settings, mode) { - this.settings = settings; - this.expansionCount = 0; - this.feed(input); - // Make new global namespace - this.macros = new Namespace(macros, settings.macros); - this.mode = mode; - this.stack = []; // contains tokens in REVERSE order - } - - /** - * Feed a new input string to the same MacroExpander - * (with existing macros etc.). - */ - feed(input) { - this.lexer = new Lexer(input, this.settings); - } - - /** - * Switches between "text" and "math" modes. - */ - switchMode(newMode) { - this.mode = newMode; - } - - /** - * Start a new group nesting within all namespaces. - */ - beginGroup() { - this.macros.beginGroup(); - } - - /** - * End current group nesting within all namespaces. - */ - endGroup() { - this.macros.endGroup(); - } - - /** - * Returns the topmost token on the stack, without expanding it. - * Similar in behavior to TeX's `\futurelet`. - */ - future() { - if (this.stack.length === 0) { - this.pushToken(this.lexer.lex()); - } - return this.stack[this.stack.length - 1] - } - - /** - * Remove and return the next unexpanded token. - */ - popToken() { - this.future(); // ensure non-empty stack - return this.stack.pop(); - } - - /** - * Add a given token to the token stack. In particular, this get be used - * to put back a token returned from one of the other methods. - */ - pushToken(token) { - this.stack.push(token); - } - - /** - * Append an array of tokens to the token stack. - */ - pushTokens(tokens) { - this.stack.push(...tokens); - } - - /** - * Find an macro argument without expanding tokens and append the array of - * tokens to the token stack. Uses Token as a container for the result. - */ - scanArgument(isOptional) { - let start; - let end; - let tokens; - if (isOptional) { - this.consumeSpaces(); // \@ifnextchar gobbles any space following it - if (this.future().text !== "[") { - return null; - } - start = this.popToken(); // don't include [ in tokens - ({ tokens, end } = this.consumeArg(["]"])); - } else { - ({ tokens, start, end } = this.consumeArg()); - } - - // indicate the end of an argument - this.pushToken(new Token("EOF", end.loc)); - - this.pushTokens(tokens); - return start.range(end, ""); - } - - /** - * Consume all following space tokens, without expansion. - */ - consumeSpaces() { - for (;;) { - const token = this.future(); - if (token.text === " ") { - this.stack.pop(); - } else { - break; - } - } - } - - /** - * Consume an argument from the token stream, and return the resulting array - * of tokens and start/end token. - */ - consumeArg(delims) { - // The argument for a delimited parameter is the shortest (possibly - // empty) sequence of tokens with properly nested {...} groups that is - // followed ... by this particular list of non-parameter tokens. - // The argument for an undelimited parameter is the next nonblank - // token, unless that token is ‘{’, when the argument will be the - // entire {...} group that follows. - const tokens = []; - const isDelimited = delims && delims.length > 0; - if (!isDelimited) { - // Ignore spaces between arguments. As the TeXbook says: - // "After you have said ‘\def\row#1#2{...}’, you are allowed to - // put spaces between the arguments (e.g., ‘\row x n’), because - // TeX doesn’t use single spaces as undelimited arguments." - this.consumeSpaces(); - } - const start = this.future(); - let tok; - let depth = 0; - let match = 0; - do { - tok = this.popToken(); - tokens.push(tok); - if (tok.text === "{") { - ++depth; - } else if (tok.text === "}") { - --depth; - if (depth === -1) { - throw new ParseError("Extra }", tok); - } - } else if (tok.text === "EOF") { - throw new ParseError( - "Unexpected end of input in a macro argument" + - ", expected '" + - (delims && isDelimited ? delims[match] : "}") + - "'", - tok - ); - } - if (delims && isDelimited) { - if ((depth === 0 || (depth === 1 && delims[match] === "{")) && tok.text === delims[match]) { - ++match; - if (match === delims.length) { - // don't include delims in tokens - tokens.splice(-match, match); - break; - } - } else { - match = 0; - } - } - } while (depth !== 0 || isDelimited); - // If the argument found ... has the form ‘{}’, - // ... the outermost braces enclosing the argument are removed - if (start.text === "{" && tokens[tokens.length - 1].text === "}") { - tokens.pop(); - tokens.shift(); - } - tokens.reverse(); // to fit in with stack order - return { tokens, start, end: tok }; - } - - /** - * Consume the specified number of (delimited) arguments from the token - * stream and return the resulting array of arguments. - */ - consumeArgs(numArgs, delimiters) { - if (delimiters) { - if (delimiters.length !== numArgs + 1) { - throw new ParseError("The length of delimiters doesn't match the number of args!"); - } - const delims = delimiters[0]; - for (let i = 0; i < delims.length; i++) { - const tok = this.popToken(); - if (delims[i] !== tok.text) { - throw new ParseError("Use of the macro doesn't match its definition", tok); - } - } - } - - const args = []; - for (let i = 0; i < numArgs; i++) { - args.push(this.consumeArg(delimiters && delimiters[i + 1]).tokens); - } - return args; - } - - /** - * Expand the next token only once if possible. - * - * If the token is expanded, the resulting tokens will be pushed onto - * the stack in reverse order, and the number of such tokens will be - * returned. This number might be zero or positive. - * - * If not, the return value is `false`, and the next token remains at the - * top of the stack. - * - * In either case, the next token will be on the top of the stack, - * or the stack will be empty (in case of empty expansion - * and no other tokens). - * - * Used to implement `expandAfterFuture` and `expandNextToken`. - * - * If expandableOnly, only expandable tokens are expanded and - * an undefined control sequence results in an error. - */ - expandOnce(expandableOnly) { - const topToken = this.popToken(); - const name = topToken.text; - const expansion = !topToken.noexpand ? this._getExpansion(name) : null; - if (expansion == null || (expandableOnly && expansion.unexpandable)) { - if (expandableOnly && expansion == null && name[0] === "\\" && !this.isDefined(name)) { - throw new ParseError("Undefined control sequence: " + name); - } - this.pushToken(topToken); - return false; - } - this.expansionCount++; - if (this.expansionCount > this.settings.maxExpand) { - throw new ParseError( - "Too many expansions: infinite loop or " + "need to increase maxExpand setting" - ); - } - let tokens = expansion.tokens; - const args = this.consumeArgs(expansion.numArgs, expansion.delimiters); - if (expansion.numArgs) { - // paste arguments in place of the placeholders - tokens = tokens.slice(); // make a shallow copy - for (let i = tokens.length - 1; i >= 0; --i) { - let tok = tokens[i]; - if (tok.text === "#") { - if (i === 0) { - throw new ParseError("Incomplete placeholder at end of macro body", tok); - } - tok = tokens[--i]; // next token on stack - if (tok.text === "#") { - // ## → # - tokens.splice(i + 1, 1); // drop first # - } else if (/^[1-9]$/.test(tok.text)) { - // replace the placeholder with the indicated argument - tokens.splice(i, 2, ...args[+tok.text - 1]); - } else { - throw new ParseError("Not a valid argument number", tok); - } - } - } - } - // Concatenate expansion onto top of stack. - this.pushTokens(tokens); - return tokens.length; - } - - /** - * Expand the next token only once (if possible), and return the resulting - * top token on the stack (without removing anything from the stack). - * Similar in behavior to TeX's `\expandafter\futurelet`. - * Equivalent to expandOnce() followed by future(). - */ - expandAfterFuture() { - this.expandOnce(); - return this.future(); - } - - /** - * Recursively expand first token, then return first non-expandable token. - */ - expandNextToken() { - for (;;) { - if (this.expandOnce() === false) { // fully expanded - const token = this.stack.pop(); - // The token after \noexpand is interpreted as if its meaning were ‘\relax’ - if (token.treatAsRelax) { - token.text = "\\relax"; - } - return token - } - } - - // This pathway is impossible. - throw new Error(); // eslint-disable-line no-unreachable - } - - /** - * Fully expand the given macro name and return the resulting list of - * tokens, or return `undefined` if no such macro is defined. - */ - expandMacro(name) { - return this.macros.has(name) ? this.expandTokens([new Token(name)]) : undefined; - } - - /** - * Fully expand the given token stream and return the resulting list of - * tokens. Note that the input tokens are in reverse order, but the - * output tokens are in forward order. - */ - expandTokens(tokens) { - const output = []; - const oldStackLength = this.stack.length; - this.pushTokens(tokens); - while (this.stack.length > oldStackLength) { - // Expand only expandable tokens - if (this.expandOnce(true) === false) { // fully expanded - const token = this.stack.pop(); - if (token.treatAsRelax) { - // the expansion of \noexpand is the token itself - token.noexpand = false; - token.treatAsRelax = false; - } - output.push(token); - } - } - return output; - } - - /** - * Fully expand the given macro name and return the result as a string, - * or return `undefined` if no such macro is defined. - */ - expandMacroAsText(name) { - const tokens = this.expandMacro(name); - if (tokens) { - return tokens.map((token) => token.text).join(""); - } else { - return tokens; - } - } - - /** - * Returns the expanded macro as a reversed array of tokens and a macro - * argument count. Or returns `null` if no such macro. - */ - _getExpansion(name) { - const definition = this.macros.get(name); - if (definition == null) { - // mainly checking for undefined here - return definition; - } - // If a single character has an associated catcode other than 13 - // (active character), then don't expand it. - if (name.length === 1) { - const catcode = this.lexer.catcodes[name]; - if (catcode != null && catcode !== 13) { - return - } - } - const expansion = typeof definition === "function" ? definition(this) : definition; - if (typeof expansion === "string") { - let numArgs = 0; - if (expansion.indexOf("#") !== -1) { - const stripped = expansion.replace(/##/g, ""); - while (stripped.indexOf("#" + (numArgs + 1)) !== -1) { - ++numArgs; - } - } - const bodyLexer = new Lexer(expansion, this.settings); - const tokens = []; - let tok = bodyLexer.lex(); - while (tok.text !== "EOF") { - tokens.push(tok); - tok = bodyLexer.lex(); - } - tokens.reverse(); // to fit in with stack using push and pop - const expanded = { tokens, numArgs }; - return expanded; - } - - return expansion; - } - - /** - * Determine whether a command is currently "defined" (has some - * functionality), meaning that it's a macro (in the current group), - * a function, a symbol, or one of the special commands listed in - * `implicitCommands`. - */ - isDefined(name) { - return ( - this.macros.has(name) || - Object.prototype.hasOwnProperty.call(functions, name ) || - Object.prototype.hasOwnProperty.call(symbols.math, name ) || - Object.prototype.hasOwnProperty.call(symbols.text, name ) || - Object.prototype.hasOwnProperty.call(implicitCommands, name ) - ); - } - - /** - * Determine whether a command is expandable. - */ - isExpandable(name) { - const macro = this.macros.get(name); - return macro != null - ? typeof macro === "string" || typeof macro === "function" || !macro.unexpandable - : Object.prototype.hasOwnProperty.call(functions, name ) && !functions[name].primitive; - } -} - -// Helpers for Parser.js handling of Unicode (sub|super)script characters. - -const unicodeSubRegEx = /^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/; - -const uSubsAndSups = Object.freeze({ - '₊': '+', - '₋': '-', - '₌': '=', - '₍': '(', - '₎': ')', - '₀': '0', - '₁': '1', - '₂': '2', - '₃': '3', - '₄': '4', - '₅': '5', - '₆': '6', - '₇': '7', - '₈': '8', - '₉': '9', - '\u2090': 'a', - '\u2091': 'e', - '\u2095': 'h', - '\u1D62': 'i', - '\u2C7C': 'j', - '\u2096': 'k', - '\u2097': 'l', - '\u2098': 'm', - '\u2099': 'n', - '\u2092': 'o', - '\u209A': 'p', - '\u1D63': 'r', - '\u209B': 's', - '\u209C': 't', - '\u1D64': 'u', - '\u1D65': 'v', - '\u2093': 'x', - '\u1D66': 'β', - '\u1D67': 'γ', - '\u1D68': 'ρ', - '\u1D69': '\u03d5', - '\u1D6A': 'χ', - '⁺': '+', - '⁻': '-', - '⁼': '=', - '⁽': '(', - '⁾': ')', - '⁰': '0', - '¹': '1', - '²': '2', - '³': '3', - '⁴': '4', - '⁵': '5', - '⁶': '6', - '⁷': '7', - '⁸': '8', - '⁹': '9', - '\u1D2C': 'A', - '\u1D2E': 'B', - '\u1D30': 'D', - '\u1D31': 'E', - '\u1D33': 'G', - '\u1D34': 'H', - '\u1D35': 'I', - '\u1D36': 'J', - '\u1D37': 'K', - '\u1D38': 'L', - '\u1D39': 'M', - '\u1D3A': 'N', - '\u1D3C': 'O', - '\u1D3E': 'P', - '\u1D3F': 'R', - '\u1D40': 'T', - '\u1D41': 'U', - '\u2C7D': 'V', - '\u1D42': 'W', - '\u1D43': 'a', - '\u1D47': 'b', - '\u1D9C': 'c', - '\u1D48': 'd', - '\u1D49': 'e', - '\u1DA0': 'f', - '\u1D4D': 'g', - '\u02B0': 'h', - '\u2071': 'i', - '\u02B2': 'j', - '\u1D4F': 'k', - '\u02E1': 'l', - '\u1D50': 'm', - '\u207F': 'n', - '\u1D52': 'o', - '\u1D56': 'p', - '\u02B3': 'r', - '\u02E2': 's', - '\u1D57': 't', - '\u1D58': 'u', - '\u1D5B': 'v', - '\u02B7': 'w', - '\u02E3': 'x', - '\u02B8': 'y', - '\u1DBB': 'z', - '\u1D5D': 'β', - '\u1D5E': 'γ', - '\u1D5F': 'δ', - '\u1D60': '\u03d5', - '\u1D61': 'χ', - '\u1DBF': 'θ' -}); - -// Used for Unicode input of calligraphic and script letters -const asciiFromScript = Object.freeze({ - "\ud835\udc9c": "A", - "\u212c": "B", - "\ud835\udc9e": "C", - "\ud835\udc9f": "D", - "\u2130": "E", - "\u2131": "F", - "\ud835\udca2": "G", - "\u210B": "H", - "\u2110": "I", - "\ud835\udca5": "J", - "\ud835\udca6": "K", - "\u2112": "L", - "\u2133": "M", - "\ud835\udca9": "N", - "\ud835\udcaa": "O", - "\ud835\udcab": "P", - "\ud835\udcac": "Q", - "\u211B": "R", - "\ud835\udcae": "S", - "\ud835\udcaf": "T", - "\ud835\udcb0": "U", - "\ud835\udcb1": "V", - "\ud835\udcb2": "W", - "\ud835\udcb3": "X", - "\ud835\udcb4": "Y", - "\ud835\udcb5": "Z" -}); - -// Mapping of Unicode accent characters to their LaTeX equivalent in text and -// math mode (when they exist). -var unicodeAccents = { - "\u0301": { text: "\\'", math: "\\acute" }, - "\u0300": { text: "\\`", math: "\\grave" }, - "\u0308": { text: '\\"', math: "\\ddot" }, - "\u0303": { text: "\\~", math: "\\tilde" }, - "\u0304": { text: "\\=", math: "\\bar" }, - "\u0306": { text: "\\u", math: "\\breve" }, - "\u030c": { text: "\\v", math: "\\check" }, - "\u0302": { text: "\\^", math: "\\hat" }, - "\u0307": { text: "\\.", math: "\\dot" }, - "\u030a": { text: "\\r", math: "\\mathring" }, - "\u030b": { text: "\\H" }, - '\u0327': { text: '\\c' } -}; - -var unicodeSymbols = { - "á": "á", - "à": "à", - "ä": "ä", - "ǟ": "ǟ", - "ã": "ã", - "ā": "ā", - "ă": "ă", - "ắ": "ắ", - "ằ": "ằ", - "ẵ": "ẵ", - "ǎ": "ǎ", - "â": "â", - "ấ": "ấ", - "ầ": "ầ", - "ẫ": "ẫ", - "ȧ": "ȧ", - "ǡ": "ǡ", - "å": "å", - "ǻ": "ǻ", - "ḃ": "ḃ", - "ć": "ć", - "č": "č", - "ĉ": "ĉ", - "ċ": "ċ", - "ď": "ď", - "ḋ": "ḋ", - "é": "é", - "è": "è", - "ë": "ë", - "ẽ": "ẽ", - "ē": "ē", - "ḗ": "ḗ", - "ḕ": "ḕ", - "ĕ": "ĕ", - "ě": "ě", - "ê": "ê", - "ế": "ế", - "ề": "ề", - "ễ": "ễ", - "ė": "ė", - "ḟ": "ḟ", - "ǵ": "ǵ", - "ḡ": "ḡ", - "ğ": "ğ", - "ǧ": "ǧ", - "ĝ": "ĝ", - "ġ": "ġ", - "ḧ": "ḧ", - "ȟ": "ȟ", - "ĥ": "ĥ", - "ḣ": "ḣ", - "í": "í", - "ì": "ì", - "ï": "ï", - "ḯ": "ḯ", - "ĩ": "ĩ", - "ī": "ī", - "ĭ": "ĭ", - "ǐ": "ǐ", - "î": "î", - "ǰ": "ǰ", - "ĵ": "ĵ", - "ḱ": "ḱ", - "ǩ": "ǩ", - "ĺ": "ĺ", - "ľ": "ľ", - "ḿ": "ḿ", - "ṁ": "ṁ", - "ń": "ń", - "ǹ": "ǹ", - "ñ": "ñ", - "ň": "ň", - "ṅ": "ṅ", - "ó": "ó", - "ò": "ò", - "ö": "ö", - "ȫ": "ȫ", - "õ": "õ", - "ṍ": "ṍ", - "ṏ": "ṏ", - "ȭ": "ȭ", - "ō": "ō", - "ṓ": "ṓ", - "ṑ": "ṑ", - "ŏ": "ŏ", - "ǒ": "ǒ", - "ô": "ô", - "ố": "ố", - "ồ": "ồ", - "ỗ": "ỗ", - "ȯ": "ȯ", - "ȱ": "ȱ", - "ő": "ő", - "ṕ": "ṕ", - "ṗ": "ṗ", - "ŕ": "ŕ", - "ř": "ř", - "ṙ": "ṙ", - "ś": "ś", - "ṥ": "ṥ", - "š": "š", - "ṧ": "ṧ", - "ŝ": "ŝ", - "ṡ": "ṡ", - "ẗ": "ẗ", - "ť": "ť", - "ṫ": "ṫ", - "ú": "ú", - "ù": "ù", - "ü": "ü", - "ǘ": "ǘ", - "ǜ": "ǜ", - "ǖ": "ǖ", - "ǚ": "ǚ", - "ũ": "ũ", - "ṹ": "ṹ", - "ū": "ū", - "ṻ": "ṻ", - "ŭ": "ŭ", - "ǔ": "ǔ", - "û": "û", - "ů": "ů", - "ű": "ű", - "ṽ": "ṽ", - "ẃ": "ẃ", - "ẁ": "ẁ", - "ẅ": "ẅ", - "ŵ": "ŵ", - "ẇ": "ẇ", - "ẘ": "ẘ", - "ẍ": "ẍ", - "ẋ": "ẋ", - "ý": "ý", - "ỳ": "ỳ", - "ÿ": "ÿ", - "ỹ": "ỹ", - "ȳ": "ȳ", - "ŷ": "ŷ", - "ẏ": "ẏ", - "ẙ": "ẙ", - "ź": "ź", - "ž": "ž", - "ẑ": "ẑ", - "ż": "ż", - "Á": "Á", - "À": "À", - "Ä": "Ä", - "Ǟ": "Ǟ", - "Ã": "Ã", - "Ā": "Ā", - "Ă": "Ă", - "Ắ": "Ắ", - "Ằ": "Ằ", - "Ẵ": "Ẵ", - "Ǎ": "Ǎ", - "Â": "Â", - "Ấ": "Ấ", - "Ầ": "Ầ", - "Ẫ": "Ẫ", - "Ȧ": "Ȧ", - "Ǡ": "Ǡ", - "Å": "Å", - "Ǻ": "Ǻ", - "Ḃ": "Ḃ", - "Ć": "Ć", - "Č": "Č", - "Ĉ": "Ĉ", - "Ċ": "Ċ", - "Ď": "Ď", - "Ḋ": "Ḋ", - "É": "É", - "È": "È", - "Ë": "Ë", - "Ẽ": "Ẽ", - "Ē": "Ē", - "Ḗ": "Ḗ", - "Ḕ": "Ḕ", - "Ĕ": "Ĕ", - "Ě": "Ě", - "Ê": "Ê", - "Ế": "Ế", - "Ề": "Ề", - "Ễ": "Ễ", - "Ė": "Ė", - "Ḟ": "Ḟ", - "Ǵ": "Ǵ", - "Ḡ": "Ḡ", - "Ğ": "Ğ", - "Ǧ": "Ǧ", - "Ĝ": "Ĝ", - "Ġ": "Ġ", - "Ḧ": "Ḧ", - "Ȟ": "Ȟ", - "Ĥ": "Ĥ", - "Ḣ": "Ḣ", - "Í": "Í", - "Ì": "Ì", - "Ï": "Ï", - "Ḯ": "Ḯ", - "Ĩ": "Ĩ", - "Ī": "Ī", - "Ĭ": "Ĭ", - "Ǐ": "Ǐ", - "Î": "Î", - "İ": "İ", - "Ĵ": "Ĵ", - "Ḱ": "Ḱ", - "Ǩ": "Ǩ", - "Ĺ": "Ĺ", - "Ľ": "Ľ", - "Ḿ": "Ḿ", - "Ṁ": "Ṁ", - "Ń": "Ń", - "Ǹ": "Ǹ", - "Ñ": "Ñ", - "Ň": "Ň", - "Ṅ": "Ṅ", - "Ó": "Ó", - "Ò": "Ò", - "Ö": "Ö", - "Ȫ": "Ȫ", - "Õ": "Õ", - "Ṍ": "Ṍ", - "Ṏ": "Ṏ", - "Ȭ": "Ȭ", - "Ō": "Ō", - "Ṓ": "Ṓ", - "Ṑ": "Ṑ", - "Ŏ": "Ŏ", - "Ǒ": "Ǒ", - "Ô": "Ô", - "Ố": "Ố", - "Ồ": "Ồ", - "Ỗ": "Ỗ", - "Ȯ": "Ȯ", - "Ȱ": "Ȱ", - "Ő": "Ő", - "Ṕ": "Ṕ", - "Ṗ": "Ṗ", - "Ŕ": "Ŕ", - "Ř": "Ř", - "Ṙ": "Ṙ", - "Ś": "Ś", - "Ṥ": "Ṥ", - "Š": "Š", - "Ṧ": "Ṧ", - "Ŝ": "Ŝ", - "Ṡ": "Ṡ", - "Ť": "Ť", - "Ṫ": "Ṫ", - "Ú": "Ú", - "Ù": "Ù", - "Ü": "Ü", - "Ǘ": "Ǘ", - "Ǜ": "Ǜ", - "Ǖ": "Ǖ", - "Ǚ": "Ǚ", - "Ũ": "Ũ", - "Ṹ": "Ṹ", - "Ū": "Ū", - "Ṻ": "Ṻ", - "Ŭ": "Ŭ", - "Ǔ": "Ǔ", - "Û": "Û", - "Ů": "Ů", - "Ű": "Ű", - "Ṽ": "Ṽ", - "Ẃ": "Ẃ", - "Ẁ": "Ẁ", - "Ẅ": "Ẅ", - "Ŵ": "Ŵ", - "Ẇ": "Ẇ", - "Ẍ": "Ẍ", - "Ẋ": "Ẋ", - "Ý": "Ý", - "Ỳ": "Ỳ", - "Ÿ": "Ÿ", - "Ỹ": "Ỹ", - "Ȳ": "Ȳ", - "Ŷ": "Ŷ", - "Ẏ": "Ẏ", - "Ź": "Ź", - "Ž": "Ž", - "Ẑ": "Ẑ", - "Ż": "Ż", - "ά": "ά", - "ὰ": "ὰ", - "ᾱ": "ᾱ", - "ᾰ": "ᾰ", - "έ": "έ", - "ὲ": "ὲ", - "ή": "ή", - "ὴ": "ὴ", - "ί": "ί", - "ὶ": "ὶ", - "ϊ": "ϊ", - "ΐ": "ΐ", - "ῒ": "ῒ", - "ῑ": "ῑ", - "ῐ": "ῐ", - "ό": "ό", - "ὸ": "ὸ", - "ύ": "ύ", - "ὺ": "ὺ", - "ϋ": "ϋ", - "ΰ": "ΰ", - "ῢ": "ῢ", - "ῡ": "ῡ", - "ῠ": "ῠ", - "ώ": "ώ", - "ὼ": "ὼ", - "Ύ": "Ύ", - "Ὺ": "Ὺ", - "Ϋ": "Ϋ", - "Ῡ": "Ῡ", - "Ῠ": "Ῠ", - "Ώ": "Ώ", - "Ὼ": "Ὼ" -}; - -/* eslint no-constant-condition:0 */ - -const binLeftCancellers = ["bin", "op", "open", "punct", "rel"]; -const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; -const textRegEx = /^ *\\text/; - -/** - * This file contains the parser used to parse out a TeX expression from the - * input. Since TeX isn't context-free, standard parsers don't work particularly - * well. - * - * The strategy of this parser is as such: - * - * The main functions (the `.parse...` ones) take a position in the current - * parse string to parse tokens from. The lexer (found in Lexer.js, stored at - * this.gullet.lexer) also supports pulling out tokens at arbitrary places. When - * individual tokens are needed at a position, the lexer is called to pull out a - * token, which is then used. - * - * The parser has a property called "mode" indicating the mode that - * the parser is currently in. Currently it has to be one of "math" or - * "text", which denotes whether the current environment is a math-y - * one or a text-y one (e.g. inside \text). Currently, this serves to - * limit the functions which can be used in text mode. - * - * The main functions then return an object which contains the useful data that - * was parsed at its given point, and a new position at the end of the parsed - * data. The main functions can call each other and continue the parsing by - * using the returned position as a new starting point. - * - * There are also extra `.handle...` functions, which pull out some reused - * functionality into self-contained functions. - * - * The functions return ParseNodes. - */ - -class Parser { - constructor(input, settings, isPreamble = false) { - // Start in math mode - this.mode = "math"; - // Create a new macro expander (gullet) and (indirectly via that) also a - // new lexer (mouth) for this parser (stomach, in the language of TeX) - this.gullet = new MacroExpander(input, settings, this.mode); - // Store the settings for use in parsing - this.settings = settings; - // Are we defining a preamble? - this.isPreamble = isPreamble; - // Count leftright depth (for \middle errors) - this.leftrightDepth = 0; - this.prevAtomType = ""; - } - - /** - * Checks a result to make sure it has the right type, and throws an - * appropriate error otherwise. - */ - expect(text, consume = true) { - if (this.fetch().text !== text) { - throw new ParseError(`Expected '${text}', got '${this.fetch().text}'`, this.fetch()); - } - if (consume) { - this.consume(); - } - } - - /** - * Discards the current lookahead token, considering it consumed. - */ - consume() { - this.nextToken = null; - } - - /** - * Return the current lookahead token, or if there isn't one (at the - * beginning, or if the previous lookahead token was consume()d), - * fetch the next token as the new lookahead token and return it. - */ - fetch() { - if (this.nextToken == null) { - this.nextToken = this.gullet.expandNextToken(); - } - return this.nextToken; - } - - /** - * Switches between "text" and "math" modes. - */ - switchMode(newMode) { - this.mode = newMode; - this.gullet.switchMode(newMode); - } - - /** - * Main parsing function, which parses an entire input. - */ - parse() { - // Create a group namespace for every $...$, $$...$$, \[...\].) - // A \def is then valid only within that pair of delimiters. - this.gullet.beginGroup(); - - if (this.settings.colorIsTextColor) { - // Use old \color behavior (same as LaTeX's \textcolor) if requested. - // We do this within the group for the math expression, so it doesn't - // pollute settings.macros. - this.gullet.macros.set("\\color", "\\textcolor"); - } - - // Try to parse the input - const parse = this.parseExpression(false); - - // If we succeeded, make sure there's an EOF at the end - this.expect("EOF"); - - if (this.isPreamble) { - const macros = Object.create(null); - Object.entries(this.gullet.macros.current).forEach(([key, value]) => { - macros[key] = value; - }); - this.gullet.endGroup(); - return macros - } - - // The only local macro that we want to save is from \tag. - const tag = this.gullet.macros.get("\\df@tag"); - - // End the group namespace for the expression - this.gullet.endGroup(); - - if (tag) { this.gullet.macros.current["\\df@tag"] = tag; } - - return parse; - } - - static get endOfExpression() { - return ["}", "\\endgroup", "\\end", "\\right", "\\endtoggle", "&"]; - } - - /** - * Fully parse a separate sequence of tokens as a separate job. - * Tokens should be specified in reverse order, as in a MacroDefinition. - */ - subparse(tokens) { - // Save the next token from the current job. - const oldToken = this.nextToken; - this.consume(); - - // Run the new job, terminating it with an excess '}' - this.gullet.pushToken(new Token("}")); - this.gullet.pushTokens(tokens); - const parse = this.parseExpression(false); - this.expect("}"); - - // Restore the next token from the current job. - this.nextToken = oldToken; - - return parse; - } - -/** - * Parses an "expression", which is a list of atoms. - * - * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This - * happens when functions have higher precedence than infix - * nodes in implicit parses. - * - * `breakOnTokenText`: The text of the token that the expression should end - * with, or `null` if something else should end the - * expression. - * - * `breakOnMiddle`: \color, \over, and old styling functions work on an implicit group. - * These groups end just before the usual tokens, but they also - * end just before `\middle`. - */ - parseExpression(breakOnInfix, breakOnTokenText, breakOnMiddle) { - const body = []; - this.prevAtomType = ""; - // Keep adding atoms to the body until we can't parse any more atoms (either - // we reached the end, a }, or a \right) - while (true) { - // Ignore spaces in math mode - if (this.mode === "math") { - this.consumeSpaces(); - } - const lex = this.fetch(); - if (Parser.endOfExpression.indexOf(lex.text) !== -1) { - break; - } - if (breakOnTokenText && lex.text === breakOnTokenText) { - break; - } - if (breakOnMiddle && lex.text === "\\middle") { - break - } - if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) { - break; - } - const atom = this.parseAtom(breakOnTokenText); - if (!atom) { - break; - } else if (atom.type === "internal") { - // Internal nodes do not appear in parse tree - continue; - } - body.push(atom); - // Keep a record of the atom type, so that op.js can set correct spacing. - this.prevAtomType = atom.type === "atom" ? atom.family : atom.type; - } - if (this.mode === "text") { - this.formLigatures(body); - } - return this.handleInfixNodes(body); - } - - /** - * Rewrites infix operators such as \over with corresponding commands such - * as \frac. - * - * There can only be one infix operator per group. If there's more than one - * then the expression is ambiguous. This can be resolved by adding {}. - */ - handleInfixNodes(body) { - let overIndex = -1; - let funcName; - - for (let i = 0; i < body.length; i++) { - if (body[i].type === "infix") { - if (overIndex !== -1) { - throw new ParseError("only one infix operator per group", body[i].token); - } - overIndex = i; - funcName = body[i].replaceWith; - } - } - - if (overIndex !== -1 && funcName) { - let numerNode; - let denomNode; - - const numerBody = body.slice(0, overIndex); - const denomBody = body.slice(overIndex + 1); - - if (numerBody.length === 1 && numerBody[0].type === "ordgroup") { - numerNode = numerBody[0]; - } else { - numerNode = { type: "ordgroup", mode: this.mode, body: numerBody }; - } - - if (denomBody.length === 1 && denomBody[0].type === "ordgroup") { - denomNode = denomBody[0]; - } else { - denomNode = { type: "ordgroup", mode: this.mode, body: denomBody }; - } - - let node; - if (funcName === "\\\\abovefrac") { - node = this.callFunction(funcName, [numerNode, body[overIndex], denomNode], []); - } else { - node = this.callFunction(funcName, [numerNode, denomNode], []); - } - return [node]; - } else { - return body; - } - } - - /** - * Handle a subscript or superscript with nice errors. - */ - handleSupSubscript( - name // For error reporting. - ) { - const symbolToken = this.fetch(); - const symbol = symbolToken.text; - this.consume(); - this.consumeSpaces(); // ignore spaces before sup/subscript argument - // Skip over allowed internal nodes such as \relax - let group; - do { - group = this.parseGroup(name); - } while (group.type && group.type === "internal") - - if (!group) { - throw new ParseError("Expected group after '" + symbol + "'", symbolToken); - } - - return group; - } - - /** - * Converts the textual input of an unsupported command into a text node - * contained within a color node whose color is determined by errorColor - */ - formatUnsupportedCmd(text) { - const textordArray = []; - - for (let i = 0; i < text.length; i++) { - textordArray.push({ type: "textord", mode: "text", text: text[i] }); - } - - const textNode = { - type: "text", - mode: this.mode, - body: textordArray - }; - - const colorNode = { - type: "color", - mode: this.mode, - color: this.settings.errorColor, - body: [textNode] - }; - - return colorNode; - } - - /** - * Parses a group with optional super/subscripts. - */ - parseAtom(breakOnTokenText) { - // The body of an atom is an implicit group, so that things like - // \left(x\right)^2 work correctly. - const base = this.parseGroup("atom", breakOnTokenText); - - // Internal nodes (e.g. \relax) cannot support super/subscripts. - // Instead we will pick up super/subscripts with blank base next round. - if (base && base.type === "internal") { - return base - } - - // In text mode, we don't have superscripts or subscripts - if (this.mode === "text") { - return base - } - - // Note that base may be empty (i.e. null) at this point. - - let superscript; - let subscript; - while (true) { - // Guaranteed in math mode, so eat any spaces first. - this.consumeSpaces(); - - // Lex the first token - const lex = this.fetch(); - - if (lex.text === "\\limits" || lex.text === "\\nolimits") { - // We got a limit control - if (base && base.type === "op") { - const limits = lex.text === "\\limits"; - base.limits = limits; - base.alwaysHandleSupSub = true; - } else if (base && base.type === "operatorname") { - if (base.alwaysHandleSupSub) { - base.limits = lex.text === "\\limits"; - } - } else { - throw new ParseError("Limit controls must follow a math operator", lex); - } - this.consume(); - } else if (lex.text === "^") { - // We got a superscript start - if (superscript) { - throw new ParseError("Double superscript", lex); - } - superscript = this.handleSupSubscript("superscript"); - } else if (lex.text === "_") { - // We got a subscript start - if (subscript) { - throw new ParseError("Double subscript", lex); - } - subscript = this.handleSupSubscript("subscript"); - } else if (lex.text === "'") { - // We got a prime - if (superscript) { - throw new ParseError("Double superscript", lex); - } - const prime = { type: "textord", mode: this.mode, text: "\\prime" }; - - // Many primes can be grouped together, so we handle this here - const primes = [prime]; - this.consume(); - // Keep lexing tokens until we get something that's not a prime - while (this.fetch().text === "'") { - // For each one, add another prime to the list - primes.push(prime); - this.consume(); - } - // If there's a superscript following the primes, combine that - // superscript in with the primes. - if (this.fetch().text === "^") { - primes.push(this.handleSupSubscript("superscript")); - } - // Put everything into an ordgroup as the superscript - superscript = { type: "ordgroup", mode: this.mode, body: primes }; - } else if (uSubsAndSups[lex.text]) { - // A Unicode subscript or superscript character. - // We treat these similarly to the unicode-math package. - // So we render a string of Unicode (sub|super)scripts the - // same as a (sub|super)script of regular characters. - const isSub = unicodeSubRegEx.test(lex.text); - const subsupTokens = []; - subsupTokens.push(new Token(uSubsAndSups[lex.text])); - this.consume(); - // Continue fetching tokens to fill out the group. - while (true) { - const token = this.fetch().text; - if (!(uSubsAndSups[token])) { break } - if (unicodeSubRegEx.test(token) !== isSub) { break } - subsupTokens.unshift(new Token(uSubsAndSups[token])); - this.consume(); - } - // Now create a (sub|super)script. - const body = this.subparse(subsupTokens); - if (isSub) { - subscript = { type: "ordgroup", mode: "math", body }; - } else { - superscript = { type: "ordgroup", mode: "math", body }; - } - } else { - // If it wasn't ^, _, a Unicode (sub|super)script, or ', stop parsing super/subscripts - break; - } - } - - if (superscript || subscript) { - if (base && base.type === "multiscript" && !base.postscripts) { - // base is the result of a \prescript function. - // Write the sub- & superscripts into the multiscript element. - base.postscripts = { sup: superscript, sub: subscript }; - return base - } else { - // We got either a superscript or subscript, create a supsub - const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname") - ? undefined - : isDelimiter(this.nextToken.text); - return { - type: "supsub", - mode: this.mode, - base: base, - sup: superscript, - sub: subscript, - isFollowedByDelimiter - } - } - } else { - // Otherwise return the original body - return base; - } - } - - /** - * Parses an entire function, including its base and all of its arguments. - */ - parseFunction( - breakOnTokenText, - name // For determining its context - ) { - const token = this.fetch(); - const func = token.text; - const funcData = functions[func]; - if (!funcData) { - return null; - } - this.consume(); // consume command token - - if (name && name !== "atom" && !funcData.allowedInArgument) { - throw new ParseError( - "Got function '" + func + "' with no arguments" + (name ? " as " + name : ""), - token - ); - } else if (this.mode === "text" && !funcData.allowedInText) { - throw new ParseError("Can't use function '" + func + "' in text mode", token); - } else if (this.mode === "math" && funcData.allowedInMath === false) { - throw new ParseError("Can't use function '" + func + "' in math mode", token); - } - - const prevAtomType = this.prevAtomType; - const { args, optArgs } = this.parseArguments(func, funcData); - this.prevAtomType = prevAtomType; - return this.callFunction(func, args, optArgs, token, breakOnTokenText); - } - - /** - * Call a function handler with a suitable context and arguments. - */ - callFunction(name, args, optArgs, token, breakOnTokenText) { - const context = { - funcName: name, - parser: this, - token, - breakOnTokenText - }; - const func = functions[name]; - if (func && func.handler) { - return func.handler(context, args, optArgs); - } else { - throw new ParseError(`No function handler for ${name}`); - } - } - - /** - * Parses the arguments of a function or environment - */ - parseArguments( - func, // Should look like "\name" or "\begin{name}". - funcData - ) { - const totalArgs = funcData.numArgs + funcData.numOptionalArgs; - if (totalArgs === 0) { - return { args: [], optArgs: [] }; - } - - const args = []; - const optArgs = []; - - for (let i = 0; i < totalArgs; i++) { - let argType = funcData.argTypes && funcData.argTypes[i]; - const isOptional = i < funcData.numOptionalArgs; - - if ( - (funcData.primitive && argType == null) || - // \sqrt expands into primitive if optional argument doesn't exist - (funcData.type === "sqrt" && i === 1 && optArgs[0] == null) - ) { - argType = "primitive"; - } - - const arg = this.parseGroupOfType(`argument to '${func}'`, argType, isOptional); - if (isOptional) { - optArgs.push(arg); - } else if (arg != null) { - args.push(arg); - } else { - // should be unreachable - throw new ParseError("Null argument, please report this as a bug"); - } - } - - return { args, optArgs }; - } - - /** - * Parses a group when the mode is changing. - */ - parseGroupOfType(name, type, optional) { - switch (type) { - case "size": - return this.parseSizeGroup(optional); - case "url": - return this.parseUrlGroup(optional); - case "math": - case "text": - return this.parseArgumentGroup(optional, type); - case "hbox": { - // hbox argument type wraps the argument in the equivalent of - // \hbox, which is like \text but switching to \textstyle size. - const group = this.parseArgumentGroup(optional, "text"); - return group != null - ? { - type: "styling", - mode: group.mode, - body: [group], - scriptLevel: "text" // simulate \textstyle - } - : null; - } - case "raw": { - const token = this.parseStringGroup("raw", optional); - return token != null - ? { - type: "raw", - mode: "text", - string: token.text - } - : null; - } - case "primitive": { - if (optional) { - throw new ParseError("A primitive argument cannot be optional"); - } - const group = this.parseGroup(name); - if (group == null) { - throw new ParseError("Expected group as " + name, this.fetch()); - } - return group; - } - case "original": - case null: - case undefined: - return this.parseArgumentGroup(optional); - default: - throw new ParseError("Unknown group type as " + name, this.fetch()); - } - } - - /** - * Discard any space tokens, fetching the next non-space token. - */ - consumeSpaces() { - while (true) { - const ch = this.fetch().text; - // \ufe0e is the Unicode variation selector to supress emoji. Ignore it. - if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") { - this.consume(); - } else { - break - } - } - } - - /** - * Parses a group, essentially returning the string formed by the - * brace-enclosed tokens plus some position information. - */ - parseStringGroup( - modeName, // Used to describe the mode in error messages. - optional - ) { - const argToken = this.gullet.scanArgument(optional); - if (argToken == null) { - return null; - } - let str = ""; - let nextToken; - while ((nextToken = this.fetch()).text !== "EOF") { - str += nextToken.text; - this.consume(); - } - this.consume(); // consume the end of the argument - argToken.text = str; - return argToken; - } - - /** - * Parses a regex-delimited group: the largest sequence of tokens - * whose concatenated strings match `regex`. Returns the string - * formed by the tokens plus some position information. - */ - parseRegexGroup( - regex, - modeName // Used to describe the mode in error messages. - ) { - const firstToken = this.fetch(); - let lastToken = firstToken; - let str = ""; - let nextToken; - while ((nextToken = this.fetch()).text !== "EOF" && regex.test(str + nextToken.text)) { - lastToken = nextToken; - str += lastToken.text; - this.consume(); - } - if (str === "") { - throw new ParseError("Invalid " + modeName + ": '" + firstToken.text + "'", firstToken); - } - return firstToken.range(lastToken, str); - } - - /** - * Parses a size specification, consisting of magnitude and unit. - */ - parseSizeGroup(optional) { - let res; - let isBlank = false; - // don't expand before parseStringGroup - this.gullet.consumeSpaces(); - if (!optional && this.gullet.future().text !== "{") { - res = this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size"); - } else { - res = this.parseStringGroup("size", optional); - } - if (!res) { - return null; - } - if (!optional && res.text.length === 0) { - // Because we've tested for what is !optional, this block won't - // affect \kern, \hspace, etc. It will capture the mandatory arguments - // to \genfrac and \above. - res.text = "0pt"; // Enable \above{} - isBlank = true; // This is here specifically for \genfrac - } - const match = sizeRegEx.exec(res.text); - if (!match) { - throw new ParseError("Invalid size: '" + res.text + "'", res); - } - const data = { - number: +(match[1] + match[2]), // sign + magnitude, cast to number - unit: match[3] - }; - if (!validUnit(data)) { - throw new ParseError("Invalid unit: '" + data.unit + "'", res); - } - return { - type: "size", - mode: this.mode, - value: data, - isBlank - }; - } - - /** - * Parses an URL, checking escaped letters and allowed protocols, - * and setting the catcode of % as an active character (as in \hyperref). - */ - parseUrlGroup(optional) { - this.gullet.lexer.setCatcode("%", 13); // active character - this.gullet.lexer.setCatcode("~", 12); // other character - const res = this.parseStringGroup("url", optional); - this.gullet.lexer.setCatcode("%", 14); // comment character - this.gullet.lexer.setCatcode("~", 13); // active character - if (res == null) { - return null; - } - // hyperref package allows backslashes alone in href, but doesn't - // generate valid links in such cases; we interpret this as - // "undefined" behaviour, and keep them as-is. Some browser will - // replace backslashes with forward slashes. - let url = res.text.replace(/\\([#$%&~_^{}])/g, "$1"); - url = res.text.replace(/{\u2044}/g, "/"); - return { - type: "url", - mode: this.mode, - url - }; - } - - /** - * Parses an argument with the mode specified. - */ - parseArgumentGroup(optional, mode) { - const argToken = this.gullet.scanArgument(optional); - if (argToken == null) { - return null; - } - const outerMode = this.mode; - if (mode) { - // Switch to specified mode - this.switchMode(mode); - } - - this.gullet.beginGroup(); - const expression = this.parseExpression(false, "EOF"); - // TODO: find an alternative way to denote the end - this.expect("EOF"); // expect the end of the argument - this.gullet.endGroup(); - const result = { - type: "ordgroup", - mode: this.mode, - loc: argToken.loc, - body: expression - }; - - if (mode) { - // Switch mode back - this.switchMode(outerMode); - } - return result; - } - - /** - * Parses an ordinary group, which is either a single nucleus (like "x") - * or an expression in braces (like "{x+y}") or an implicit group, a group - * that starts at the current position, and ends right before a higher explicit - * group ends, or at EOF. - */ - parseGroup( - name, // For error reporting. - breakOnTokenText - ) { - const firstToken = this.fetch(); - const text = firstToken.text; - if (name === "argument to '\\left'") { return this.parseSymbol() } - let result; - // Try to parse an open brace or \begingroup - if (text === "{" || text === "\\begingroup" || text === "\\toggle") { - this.consume(); - const groupEnd = text === "{" - ? "}" - : text === "\\begingroup" - ? "\\endgroup" - : "\\endtoggle"; - - this.gullet.beginGroup(); - // If we get a brace, parse an expression - const expression = this.parseExpression(false, groupEnd); - const lastToken = this.fetch(); - this.expect(groupEnd); // Check that we got a matching closing brace - this.gullet.endGroup(); - result = { - type: (lastToken.text === "\\endtoggle" ? "toggle" : "ordgroup"), - mode: this.mode, - loc: SourceLocation.range(firstToken, lastToken), - body: expression, - // A group formed by \begingroup...\endgroup is a semi-simple group - // which doesn't affect spacing in math mode, i.e., is transparent. - // https://tex.stackexchange.com/questions/1930/ - semisimple: text === "\\begingroup" || undefined - }; - } else { - // If there exists a function with this name, parse the function. - // Otherwise, just return a nucleus - result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol(); - if (result == null && text[0] === "\\" && - !Object.prototype.hasOwnProperty.call(implicitCommands, text )) { - if (this.settings.throwOnError) { - throw new ParseError("Unsupported function name: " + text, firstToken); - } - // For people getting dyanamically rendered math, it's better to - // show the unsupported command in red rather than panicking for every - // partially written expression. - result = this.formatUnsupportedCmd(text); - this.consume(); - } - } - return result; - } - - /** - * Form ligature-like combinations of characters for text mode. - * This includes inputs like "--", "---", "``" and "''". - * The result will simply replace multiple textord nodes with a single - * character in each value by a single textord node having multiple - * characters in its value. The representation is still ASCII source. - * The group will be modified in place. - */ - formLigatures(group) { - let n = group.length - 1; - for (let i = 0; i < n; ++i) { - const a = group[i]; - const v = a.text; - if (v === "-" && group[i + 1].text === "-") { - if (i + 1 < n && group[i + 2].text === "-") { - group.splice(i, 3, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 2]), - text: "---" - }); - n -= 2; - } else { - group.splice(i, 2, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 1]), - text: "--" - }); - n -= 1; - } - } - if ((v === "'" || v === "`") && group[i + 1].text === v) { - group.splice(i, 2, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 1]), - text: v + v - }); - n -= 1; - } - } - } - - /** - * Parse a single symbol out of the string. Here, we handle single character - * symbols and special functions like \verb. - */ - parseSymbol() { - const nucleus = this.fetch(); - let text = nucleus.text; - - if (/^\\verb[^a-zA-Z]/.test(text)) { - this.consume(); - let arg = text.slice(5); - const star = arg.charAt(0) === "*"; - if (star) { - arg = arg.slice(1); - } - // Lexer's tokenRegex is constructed to always have matching - // first/last characters. - if (arg.length < 2 || arg.charAt(0) !== arg.slice(-1)) { - throw new ParseError(`\\verb assertion failed -- - please report what input caused this bug`); - } - arg = arg.slice(1, -1); // remove first and last char - return { - type: "verb", - mode: "text", - body: arg, - star - }; - } - // At this point, we should have a symbol, possibly with accents. - // First expand any accented base symbol according to unicodeSymbols. - if (Object.prototype.hasOwnProperty.call(unicodeSymbols, text[0]) && - this.mode === "math" && !symbols[this.mode][text[0]]) { - // This behavior is not strict (XeTeX-compatible) in math mode. - if (this.settings.strict && this.mode === "math") { - throw new ParseError(`Accented Unicode text character "${text[0]}" used in ` + `math mode`, - nucleus - ); - } - text = unicodeSymbols[text[0]] + text.slice(1); - } - // Strip off any combining characters - const match = this.mode === "math" - ? combiningDiacriticalMarksEndRegex.exec(text) - : null; - if (match) { - text = text.substring(0, match.index); - if (text === "i") { - text = "\u0131"; // dotless i, in math and text mode - } else if (text === "j") { - text = "\u0237"; // dotless j, in math and text mode - } - } - // Recognize base symbol - let symbol; - if (symbols[this.mode][text]) { - let group = symbols[this.mode][text].group; - if (group === "bin" && - (binLeftCancellers.includes(this.prevAtomType) || this.prevAtomType === "")) { - // Change from a binary operator to a unary (prefix) operator - group = "open"; - } - const loc = SourceLocation.range(nucleus); - let s; - if (Object.prototype.hasOwnProperty.call(ATOMS, group )) { - const family = group; - s = { - type: "atom", - mode: this.mode, - family, - loc, - text - }; - if ((family === "rel" || family === "bin") && this.prevAtomType === "text") { - if (textRegEx.test(loc.lexer.input.slice(loc.end))) { - s.needsSpacing = true; // Fix a MathML bug. - } - } - } else { - if (asciiFromScript[text]) { - // Unicode 14 disambiguates chancery from roundhand. - // See https://www.unicode.org/charts/PDF/U1D400.pdf - this.consume(); - const nextCode = this.fetch().text.charCodeAt(0); - // mathcal is Temml default. Use mathscript if called for. - const font = nextCode === 0xfe01 ? "mathscr" : "mathcal"; - if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume(); } - return { - type: "font", - mode: "math", - font, - body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] } - } - } - // Default ord character. No disambiguation necessary. - s = { - type: group, - mode: this.mode, - loc, - text - }; - } - symbol = s; - } else if (text.charCodeAt(0) >= 0x80 || combiningDiacriticalMarksEndRegex.exec(text)) { - // no symbol for e.g. ^ - if (this.settings.strict && this.mode === "math") { - throw new ParseError(`Unicode text character "${text[0]}" used in math mode`, nucleus) - } - // All nonmathematical Unicode characters are rendered as if they - // are in text mode (wrapped in \text) because that's what it - // takes to render them in LaTeX. - symbol = { - type: "textord", - mode: "text", - loc: SourceLocation.range(nucleus), - text - }; - } else { - return null; // EOF, ^, _, {, }, etc. - } - this.consume(); - // Transform combining characters into accents - if (match) { - for (let i = 0; i < match[0].length; i++) { - const accent = match[0][i]; - if (!unicodeAccents[accent]) { - throw new ParseError(`Unknown accent ' ${accent}'`, nucleus); - } - const command = unicodeAccents[accent][this.mode] || - unicodeAccents[accent].text; - if (!command) { - throw new ParseError(`Accent ${accent} unsupported in ${this.mode} mode`, nucleus); - } - symbol = { - type: "accent", - mode: this.mode, - loc: SourceLocation.range(nucleus), - label: command, - isStretchy: false, - base: symbol - }; - } - } - return symbol; - } -} - -/** - * Parses an expression using a Parser, then returns the parsed result. - */ -const parseTree = function(toParse, settings) { - if (!(typeof toParse === "string" || toParse instanceof String)) { - throw new TypeError("Temml can only parse string typed expression") - } - let tree; - let parser; - try { - parser = new Parser(toParse, settings); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - - tree = parser.parse(); - } catch (error) { - if (error.toString() === "ParseError: Unmatched delimiter") { - // Abandon the attempt to wrap delimiter pairs in an . - // Try again, and put each delimiter into an element. - settings.wrapDelimiterPairs = false; - parser = new Parser(toParse, settings); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - tree = parser.parse(); - } else { - throw error; - } - } - - // LaTeX ignores a \tag placed outside an AMS environment. - if (!(tree.length > 0 && tree[0].type && tree[0].type === "array" && tree[0].addEqnNum)) { - // If the input used \tag, it will set the \df@tag macro to the tag. - // In this case, we separately parse the tag and wrap the tree. - if (parser.gullet.macros.get("\\df@tag")) { - if (!settings.displayMode) { - throw new ParseError("\\tag works only in display mode") - } - parser.gullet.feed("\\df@tag"); - tree = [ - { - type: "tag", - mode: "text", - body: tree, - tag: parser.parse() - } - ]; - } - } - - return tree -}; - -/** - * This file contains information about the style that the mathmlBuilder carries - * around with it. Data is held in an `Style` object, and when - * recursing, a new `Style` object can be created with the `.with*` functions. - */ - -const subOrSupLevel = [2, 2, 3, 3]; - -/** - * This is the main Style class. It contains the current style.level, color, and font. - * - * Style objects should not be modified. To create a new Style with - * different properties, call a `.with*` method. - */ -class Style { - constructor(data) { - // Style.level can be 0 | 1 | 2 | 3, which correspond to - // displaystyle, textstyle, scriptstyle, and scriptscriptstyle. - // style.level usually does not directly set MathML's script level. MathML does that itself. - // However, Chromium does not stop shrinking after scriptscriptstyle, so we do explicitly - // set a scriptlevel attribute in those conditions. - // We also use style.level to track math style so that we can get the correct - // scriptlevel when needed in supsub.js, mathchoice.js, or for dimensions in em. - this.level = data.level; - this.color = data.color; // string | void - // A font family applies to a group of fonts (i.e. SansSerif), while a font - // represents a specific font (i.e. SansSerif Bold). - // See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm - this.font = data.font || ""; // string - this.fontFamily = data.fontFamily || ""; // string - this.fontSize = data.fontSize || 1.0; // number - this.fontWeight = data.fontWeight || ""; - this.fontShape = data.fontShape || ""; - this.maxSize = data.maxSize; // [number, number] - } - - /** - * Returns a new style object with the same properties as "this". Properties - * from "extension" will be copied to the new style object. - */ - extend(extension) { - const data = { - level: this.level, - color: this.color, - font: this.font, - fontFamily: this.fontFamily, - fontSize: this.fontSize, - fontWeight: this.fontWeight, - fontShape: this.fontShape, - maxSize: this.maxSize - }; - - for (const key in extension) { - if (Object.prototype.hasOwnProperty.call(extension, key)) { - data[key] = extension[key]; - } - } - - return new Style(data); - } - - withLevel(n) { - return this.extend({ - level: n - }); - } - - incrementLevel() { - return this.extend({ - level: Math.min(this.level + 1, 3) - }); - } - - inSubOrSup() { - return this.extend({ - level: subOrSupLevel[this.level] - }) - } - - /** - * Create a new style object with the given color. - */ - withColor(color) { - return this.extend({ - color: color - }); - } - - /** - * Creates a new style object with the given math font or old text font. - * @type {[type]} - */ - withFont(font) { - return this.extend({ - font - }); - } - - /** - * Create a new style objects with the given fontFamily. - */ - withTextFontFamily(fontFamily) { - return this.extend({ - fontFamily, - font: "" - }); - } - - /** - * Creates a new style object with the given font size - */ - withFontSize(num) { - return this.extend({ - fontSize: num - }); - } - - /** - * Creates a new style object with the given font weight - */ - withTextFontWeight(fontWeight) { - return this.extend({ - fontWeight, - font: "" - }); - } - - /** - * Creates a new style object with the given font weight - */ - withTextFontShape(fontShape) { - return this.extend({ - fontShape, - font: "" - }); - } - - /** - * Gets the CSS color of the current style object - */ - getColor() { - return this.color; - } -} - -/* Temml Post Process - * Populate the text contents of each \ref & \eqref - * - * As with other Temml code, this file is released under terms of the MIT license. - * https://mit-license.org/ - */ - -const version = "0.13.02"; - -function postProcess(block) { - const labelMap = {}; - let i = 0; - - // Get a collection of the parents of each \tag & auto-numbered equation - const amsEqns = document.getElementsByClassName('tml-eqn'); - for (let parent of amsEqns) { - // AMS automatically numbered equation. - // Assign an id. - i += 1; - parent.setAttribute("id", "tml-eqn-" + String(i)); - // No need to write a number into the text content of the element. - // A CSS counter has done that even if this postProcess() function is not used. - - // Find any \label that refers to an AMS automatic eqn number. - while (true) { - if (parent.tagName === "mtable") { break } - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = String(i); - break - } else { - parent = parent.parentElement; - } - } - } - - // Find \labels associated with \tag - const taggedEqns = document.getElementsByClassName('tml-tageqn'); - for (const parent of taggedEqns) { - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const tags = parent.getElementsByClassName("tml-tag"); - if (tags.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = tags[0].textContent; - } - } - } - - // Populate \ref & \eqref text content - const refs = block.getElementsByClassName("tml-ref"); - [...refs].forEach(ref => { - const attr = ref.getAttribute("href"); - let str = labelMap[attr.slice(1)]; - if (ref.className.indexOf("tml-eqref") === -1) { - // \ref. Omit parens. - str = str.replace(/^\(/, ""); - str = str.replace(/\)$/, ""); - } else { - // \eqref. Include parens - if (str.charAt(0) !== "(") { str = "(" + str; } - if (str.slice(-1) !== ")") { str = str + ")"; } - } - const mtext = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mtext"); - mtext.appendChild(document.createTextNode(str)); - const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"); - math.appendChild(mtext); - ref.textContent = ''; - ref.appendChild(math); - }); -} - -const findEndOfMath = function(delimiter, text, startIndex) { - // Adapted from - // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx - let index = startIndex; - let braceLevel = 0; - - const delimLength = delimiter.length; - - while (index < text.length) { - const character = text[index]; - - if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) { - return index; - } else if (character === "\\") { - index++; - } else if (character === "{") { - braceLevel++; - } else if (character === "}") { - braceLevel--; - } - - index++; - } - - return -1; -}; - -const escapeRegex = function(string) { - return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); -}; - -const amsRegex = /^\\(?:begin|(?:eq)?ref){/; - -const splitAtDelimiters = function(text, delimiters) { - let index; - const data = []; - - const regexLeft = new RegExp( - "(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")" - ); - - while (true) { - index = text.search(regexLeft); - if (index === -1) { - break; - } - if (index > 0) { - data.push({ - type: "text", - data: text.slice(0, index) - }); - text = text.slice(index); // now text starts with delimiter - } - // ... so this always succeeds: - const i = delimiters.findIndex((delim) => text.startsWith(delim.left)); - index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length); - if (index === -1) { - break; - } - const rawData = text.slice(0, index + delimiters[i].right.length); - const math = amsRegex.test(rawData) - ? rawData - : text.slice(delimiters[i].left.length, index); - data.push({ - type: "math", - data: math, - rawData, - display: delimiters[i].display - }); - text = text.slice(index + delimiters[i].right.length); - } - - if (text !== "") { - data.push({ - type: "text", - data: text - }); - } - - return data; -}; - -const defaultDelimiters = [ - { left: "$$", right: "$$", display: true }, - { left: "\\(", right: "\\)", display: false }, - // LaTeX uses $…$, but it ruins the display of normal `$` in text: - // {left: "$", right: "$", display: false}, - // $ must come after $$ - - // Render AMS environments even if outside $$…$$ delimiters. - { left: "\\begin{equation}", right: "\\end{equation}", display: true }, - { left: "\\begin{equation*}", right: "\\end{equation*}", display: true }, - { left: "\\begin{align}", right: "\\end{align}", display: true }, - { left: "\\begin{align*}", right: "\\end{align*}", display: true }, - { left: "\\begin{alignat}", right: "\\end{alignat}", display: true }, - { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true }, - { left: "\\begin{gather}", right: "\\end{gather}", display: true }, - { left: "\\begin{gather*}", right: "\\end{gather*}", display: true }, - { left: "\\begin{CD}", right: "\\end{CD}", display: true }, - // Ditto \ref & \eqref - { left: "\\ref{", right: "}", display: false }, - { left: "\\eqref{", right: "}", display: false }, - - { left: "\\[", right: "\\]", display: true } -]; - -const firstDraftDelimiters = { - "$": [ - { left: "$$", right: "$$", display: true }, - { left: "$`", right: "`$", display: false }, - { left: "$", right: "$", display: false } - ], - "(": [ - { left: "\\[", right: "\\]", display: true }, - { left: "\\(", right: "\\)", display: false } - ] -}; - -const amsDelimiters = [ - { left: "\\begin{equation}", right: "\\end{equation}", display: true }, - { left: "\\begin{equation*}", right: "\\end{equation*}", display: true }, - { left: "\\begin{align}", right: "\\end{align}", display: true }, - { left: "\\begin{align*}", right: "\\end{align*}", display: true }, - { left: "\\begin{alignat}", right: "\\end{alignat}", display: true }, - { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true }, - { left: "\\begin{gather}", right: "\\end{gather}", display: true }, - { left: "\\begin{gather*}", right: "\\end{gather*}", display: true }, - { left: "\\begin{CD}", right: "\\end{CD}", display: true }, - { left: "\\ref{", right: "}", display: false }, - { left: "\\eqref{", right: "}", display: false } -]; - -const delimitersFromKey = key => { - if (key === "$" || key === "(") { - return firstDraftDelimiters[key]; - } else if (key === "$+" || key === "(+") { - const firstDraft = firstDraftDelimiters[key.slice(0, 1)]; - return firstDraft.concat(amsDelimiters) - } else if (key === "ams") { - return amsDelimiters - } else if (key === "all") { - return (firstDraftDelimiters["("]).concat(firstDraftDelimiters["$"]).concat(amsDelimiters) - } else { - return defaultDelimiters - } -}; - -/* Note: optionsCopy is mutated by this method. If it is ever exposed in the - * API, we should copy it before mutating. - */ -const renderMathInText = function(text, optionsCopy) { - const data = splitAtDelimiters(text, optionsCopy.delimiters); - if (data.length === 1 && data[0].type === "text") { - // There is no formula in the text. - // Let's return null which means there is no need to replace - // the current text node with a new one. - return null; - } - - const fragment = document.createDocumentFragment(); - - for (let i = 0; i < data.length; i++) { - if (data[i].type === "text") { - fragment.appendChild(document.createTextNode(data[i].data)); - } else { - const span = document.createElement("span"); - let math = data[i].data; - // Override any display mode defined in the settings with that - // defined by the text itself - optionsCopy.displayMode = data[i].display; - try { - if (optionsCopy.preProcess) { - math = optionsCopy.preProcess(math); - } - // Importing render() from temml.js would be a circular dependency. - // So call the global version. - // eslint-disable-next-line no-undef - temml.render(math, span, optionsCopy); - } catch (e) { - if (!(e instanceof ParseError)) { - throw e; - } - optionsCopy.errorCallback( - "Temml auto-render: Failed to parse `" + data[i].data + "` with ", - e - ); - fragment.appendChild(document.createTextNode(data[i].rawData)); - continue; - } - fragment.appendChild(span); - } - } - - return fragment; -}; - -const renderElem = function(elem, optionsCopy) { - for (let i = 0; i < elem.childNodes.length; i++) { - const childNode = elem.childNodes[i]; - if (childNode.nodeType === 3) { - // Text node - const frag = renderMathInText(childNode.textContent, optionsCopy); - if (frag) { - i += frag.childNodes.length - 1; - elem.replaceChild(frag, childNode); - } - } else if (childNode.nodeType === 1) { - // Element node - const className = " " + childNode.className + " "; - const shouldRender = - optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && - optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1); - - if (shouldRender) { - renderElem(childNode, optionsCopy); - } - } - // Otherwise, it's something else, and ignore it. - } -}; - -const renderMathInElement = function(elem, options) { - if (!elem) { - throw new Error("No element provided to render"); - } - - const optionsCopy = {}; - - // Object.assign(optionsCopy, option) - for (const option in options) { - if (Object.prototype.hasOwnProperty.call(options, option)) { - optionsCopy[option] = options[option]; - } - } - - if (optionsCopy.fences) { - optionsCopy.delimiters = delimitersFromKey(optionsCopy.fences); - } else { - optionsCopy.delimiters = optionsCopy.delimiters || defaultDelimiters; - } - optionsCopy.ignoredTags = optionsCopy.ignoredTags || [ - "script", - "noscript", - "style", - "textarea", - "pre", - "code", - "option" - ]; - optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || []; - // eslint-disable-next-line no-console - optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; - - // Enable sharing of global macros defined via `\gdef` between different - // math elements within a single call to `renderMathInElement`. - optionsCopy.macros = optionsCopy.macros || {}; - - renderElem(elem, optionsCopy); - postProcess(elem); -}; - -/* eslint no-console:0 */ -/** - * This is the main entry point for Temml. Here, we expose functions for - * rendering expressions either to DOM nodes or to markup strings. - * - * We also expose the ParseError class to check if errors thrown from Temml are - * errors in the expression, or errors in javascript handling. - */ - - -/** - * @type {import('./temml').render} - * Parse and build an expression, and place that expression in the DOM node - * given. - */ -let render = function(expression, baseNode, options = {}) { - baseNode.textContent = ""; - const alreadyInMathElement = baseNode.tagName.toLowerCase() === "math"; - if (alreadyInMathElement) { options.wrap = "none"; } - const math = renderToMathMLTree(expression, options); - if (alreadyInMathElement) { - // The element already exists. Populate it. - baseNode.textContent = ""; - math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); - } else if (math.children.length > 1) { - baseNode.textContent = ""; - math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); - } else { - baseNode.appendChild(math.toNode()); - } -}; - -// Temml's styles don't work properly in quirks mode. Print out an error, and -// disable rendering. -if (typeof document !== "undefined") { - if (document.compatMode !== "CSS1Compat") { - typeof console !== "undefined" && - console.warn( - "Warning: Temml doesn't work in quirks mode. Make sure your " + - "website has a suitable doctype." - ); - - render = function() { - throw new ParseError("Temml doesn't work in quirks mode."); - }; - } -} - -/** - * @type {import('./temml').renderToString} - * Parse and build an expression, and return the markup for that. - */ -const renderToString = function(expression, options) { - const markup = renderToMathMLTree(expression, options).toMarkup(); - return markup; -}; - -/** - * @type {import('./temml').generateParseTree} - * Parse an expression and return the parse tree. - */ -const generateParseTree = function(expression, options) { - const settings = new Settings(options); - return parseTree(expression, settings); -}; - -/** - * @type {import('./temml').definePreamble} - * Take an expression which contains a preamble. - * Parse it and return the macros. - */ -const definePreamble = function(expression, options) { - const settings = new Settings(options); - settings.macros = {}; - if (!(typeof expression === "string" || expression instanceof String)) { - throw new TypeError("Temml can only parse string typed expression") - } - const parser = new Parser(expression, settings, true); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - const macros = parser.parse(); - return macros -}; - -/** - * If the given error is a Temml ParseError, - * renders the invalid LaTeX as a span with hover title giving the Temml - * error message. Otherwise, simply throws the error. - */ -const renderError = function(error, expression, options) { - if (options.throwOnError || !(error instanceof ParseError)) { - throw error; - } - const node = new Span(["temml-error"], [new TextNode$1(expression + "\n\n" + error.toString())]); - node.style.color = options.errorColor; - node.style.whiteSpace = "pre-line"; - return node; -}; - -/** - * @type {import('./temml').renderToMathMLTree} - * Generates and returns the Temml build tree. This is used for advanced - * use cases (like rendering to custom output). - */ -const renderToMathMLTree = function(expression, options) { - const settings = new Settings(options); - try { - const tree = parseTree(expression, settings); - const style = new Style({ - level: settings.displayMode ? StyleLevel.DISPLAY : StyleLevel.TEXT, - maxSize: settings.maxSize - }); - return buildMathML(tree, expression, style, settings); - } catch (error) { - return renderError(error, expression, settings); - } -}; - -/** @type {import('./temml').default} */ -var temml$1 = { - /** - * Current Temml version - */ - version: version, - /** - * Renders the given LaTeX into MathML, and adds - * it as a child to the specified DOM node. - */ - render, - /** - * Renders the given LaTeX into MathML string, - * for sending to the client. - */ - renderToString, - /** - * Finds all the math delimiters in a given element of a running HTML document - * and converts the contents of each instance into a element. - */ - renderMathInElement, - /** - * Post-process an entire HTML block. - * Writes AMS auto-numbers and implements \ref{}. - * Typcally called once, after a loop has rendered many individual spans. - */ - postProcess, - /** - * Temml error, usually during parsing. - */ - ParseError, - /** - * Creates a set of macros with document-wide scope. - */ - definePreamble, - /** - * Parses the given LaTeX into Temml's internal parse tree structure, - * without rendering to HTML or MathML. - * - * NOTE: This method is not currently recommended for public use. - * The internal tree representation is unstable and is very likely - * to change. Use at your own risk. - */ - __parse: generateParseTree, - /** - * Renders the given LaTeX into a MathML internal DOM tree - * representation, without flattening that representation to a string. - * - * NOTE: This method is not currently recommended for public use. - * The internal tree representation is unstable and is very likely - * to change. Use at your own risk. - */ - __renderToMathMLTree: renderToMathMLTree, - /** - * adds a new symbol to builtin symbols table - */ - __defineSymbol: defineSymbol, - /** - * adds a new macro to builtin macro list - */ - __defineMacro: defineMacro -}; - -module.exports = temml$1; diff --git a/dist/temml.d.ts b/dist/temml.d.ts deleted file mode 100644 index 7ed1f014..00000000 --- a/dist/temml.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -export interface Options { - displayMode?: boolean; - annotate?: boolean; - leqno?: boolean; - throwOnError?: boolean; - errorColor?: string; - macros?: Record; - wrap?: "none" | "tex" | "="; - xml?: boolean; - colorIsTextColor?: boolean; - strict?: boolean; - trust?: boolean | ((context: any) => boolean); - maxSize?: [number, number]; - maxExpand?: number; -} - -export function render( - expression: string, - baseNode: HTMLElement | MathMLElement, - options?: Options, -): void; - -export function renderToString(expression: string, options?: Options): string; - -export function renderMathInElement(block: any, options?: Options): void; - -export function generateParseTree(expression: string, options?: Options): any; - -export function definePreamble(expression: string, options?: Options): any; - -export function renderToMathMLTree(expression: string, options?: Options): any; - -declare function postProcess(block: any): void; -declare function defineMacro(name: string, body: any): void; -declare function defineSymbol( - mode: string, - group: string, - replace: string, - name: string, - acceptUnicodeChar: boolean, -): void; -declare class ParseError { - constructor( - message: string, // The error message - token: any, // An object providing position information - ); -} - -declare const Temml: { - version: string; - render: typeof render; - renderToString: typeof renderToString; - renderMathInElement: typeof renderMathInElement; - postProcess: typeof postProcess; - ParseError: typeof ParseError; - definePreamble: typeof definePreamble; - __parse: typeof generateParseTree; - __renderToMathMLTree: typeof renderToMathMLTree; - __defineSymbol: typeof defineSymbol; - __defineMacro: typeof defineMacro; -}; - -export default Temml; diff --git a/dist/temml.js b/dist/temml.js deleted file mode 100644 index 94b2154b..00000000 --- a/dist/temml.js +++ /dev/null @@ -1,12774 +0,0 @@ -var temml = (function () { - 'use strict'; - - /** - * This is the ParseError class, which is the main error thrown by Temml - * functions when something has gone wrong. This is used to distinguish internal - * errors from errors in the expression that the user provided. - * - * If possible, a caller should provide a Token or ParseNode with information - * about where in the source string the problem occurred. - */ - class ParseError { - constructor( - message, // The error message - token // An object providing position information - ) { - let error = " " + message; - let start; - - const loc = token && token.loc; - if (loc && loc.start <= loc.end) { - // If we have the input and a position, make the error a bit fancier - - // Get the input - const input = loc.lexer.input; - - // Prepend some information - start = loc.start; - const end = loc.end; - if (start === input.length) { - error += " at end of input: "; - } else { - error += " at position " + (start + 1) + ": \n"; - } - - // Underline token in question using combining underscores - const underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332"); - - // Extract some context from the input and add it to the error - let left; - if (start > 15) { - left = "…" + input.slice(start - 15, start); - } else { - left = input.slice(0, start); - } - let right; - if (end + 15 < input.length) { - right = input.slice(end, end + 15) + "…"; - } else { - right = input.slice(end); - } - error += left + underlined + right; - } - - // Some hackery to make ParseError a prototype of Error - // See http://stackoverflow.com/a/8460753 - const self = new Error(error); - self.name = "ParseError"; - self.__proto__ = ParseError.prototype; - self.position = start; - return self; - } - } - - ParseError.prototype.__proto__ = Error.prototype; - - // - /** - * This file contains a list of utility functions which are useful in other - * files. - */ - - /** - * Provide a default value if a setting is undefined - */ - const deflt = function(setting, defaultIfUndefined) { - return setting === undefined ? defaultIfUndefined : setting; - }; - - // hyphenate and escape adapted from Facebook's React under Apache 2 license - - const uppercase = /([A-Z])/g; - const hyphenate = function(str) { - return str.replace(uppercase, "-$1").toLowerCase(); - }; - - const ESCAPE_LOOKUP = { - "&": "&", - ">": ">", - "<": "<", - '"': """, - "'": "'" - }; - - const ESCAPE_REGEX = /[&><"']/g; - - /** - * Escapes text to prevent scripting attacks. - */ - function escape(text) { - return String(text).replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]); - } - - /** - * Sometimes we want to pull out the innermost element of a group. In most - * cases, this will just be the group itself, but when ordgroups and colors have - * a single element, we want to pull that out. - */ - const getBaseElem = function(group) { - if (group.type === "ordgroup") { - if (group.body.length === 1) { - return getBaseElem(group.body[0]); - } else { - return group; - } - } else if (group.type === "color") { - if (group.body.length === 1) { - return getBaseElem(group.body[0]); - } else { - return group; - } - } else if (group.type === "font") { - return getBaseElem(group.body); - } else { - return group; - } - }; - - /** - * TeXbook algorithms often reference "character boxes", which are simply groups - * with a single character in them. To decide if something is a character box, - * we find its innermost group, and see if it is a single character. - */ - const isCharacterBox = function(group) { - const baseElem = getBaseElem(group); - - // These are all the types of groups which hold single characters - return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom" - }; - - const assert = function(value) { - if (!value) { - throw new Error("Expected non-null, but got " + String(value)); - } - return value; - }; - - /** - * Return the protocol of a URL, or "_relative" if the URL does not specify a - * protocol (and thus is relative), or `null` if URL has invalid protocol - * (so should be outright rejected). - */ - const protocolFromUrl = function(url) { - // Check for possible leading protocol. - // https://url.spec.whatwg.org/#url-parsing strips leading whitespace - // (\x00) or C0 control (\x00-\x1F) characters. - // eslint-disable-next-line no-control-regex - const protocol = /^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(url); - if (!protocol) { - return "_relative"; - } - // Reject weird colons - if (protocol[2] !== ":") { - return null; - } - // Reject invalid characters in scheme according to - // https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 - if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(protocol[1])) { - return null; - } - // Lowercase the protocol - return protocol[1].toLowerCase(); - }; - - /** - * Round `n` to 4 decimal places, or to the nearest 1/10,000th em. The TeXbook - * gives an acceptable rounding error of 100sp (which would be the nearest - * 1/6551.6em with our ptPerEm = 10): - * http://www.ctex.org/documents/shredder/src/texbook.pdf#page=69 - */ - const round = function(n) { - return +n.toFixed(4); - }; - - // Identify short letters. Used for accents and \cancelto. - const smalls = "acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚𝐜𝐞𝐠𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐮𝐯𝐰𝐱𝐲𝐳"; - - /** - * This is a module for storing settings passed into Temml. It correctly handles - * default settings. - */ - - - /** - * The main Settings object - */ - class Settings { - constructor(options) { - // allow null options - options = options || {}; - this.displayMode = deflt(options.displayMode, false); // boolean - this.annotate = deflt(options.annotate, false); // boolean - this.leqno = deflt(options.leqno, false); // boolean - this.throwOnError = deflt(options.throwOnError, false); // boolean - this.errorColor = deflt(options.errorColor, "#b22222"); // string - this.macros = options.macros || {}; - this.wrap = deflt(options.wrap, "none"); // "none" | "tex" | "=" - this.xml = deflt(options.xml, false); // boolean - this.colorIsTextColor = deflt(options.colorIsTextColor, false); // boolean - this.strict = deflt(options.strict, false); // boolean - this.trust = deflt(options.trust, false); // trust context. See html.js. - this.maxSize = (options.maxSize === undefined - ? [Infinity, Infinity] - : Array.isArray(options.maxSize) - ? options.maxSize - : [Infinity, Infinity] - ); - this.maxExpand = Math.max(0, deflt(options.maxExpand, 1000)); // number - this.wrapDelimiterPairs = true; // boolean - } - - /** - * Check whether to test potentially dangerous input, and return - * `true` (trusted) or `false` (untrusted). The sole argument `context` - * should be an object with `command` field specifying the relevant LaTeX - * command (as a string starting with `\`), and any other arguments, etc. - * If `context` has a `url` field, a `protocol` field will automatically - * get added by this function (changing the specified object). - */ - isTrusted(context) { - if (context.url && !context.protocol) { - const protocol = protocolFromUrl(context.url); - if (protocol == null) { - return false - } - context.protocol = protocol; - } - const trust = typeof this.trust === "function" ? this.trust(context) : this.trust; - return Boolean(trust); - } - } - - /** - * All registered functions. - * `functions.js` just exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary. - */ - const _functions = {}; - - /** - * All MathML builders. Should be only used in the `define*` and the `build*ML` - * functions. - */ - const _mathmlGroupBuilders = {}; - - function defineFunction({ - type, - names, - props, - handler, - mathmlBuilder - }) { - // Set default values of functions - const data = { - type, - numArgs: props.numArgs, - argTypes: props.argTypes, - allowedInArgument: !!props.allowedInArgument, - allowedInText: !!props.allowedInText, - allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath, - numOptionalArgs: props.numOptionalArgs || 0, - infix: !!props.infix, - primitive: !!props.primitive, - handler: handler - }; - for (let i = 0; i < names.length; ++i) { - _functions[names[i]] = data; - } - if (type) { - if (mathmlBuilder) { - _mathmlGroupBuilders[type] = mathmlBuilder; - } - } - } - - /** - * Use this to register only the MathML builder for a function(e.g. - * if the function's ParseNode is generated in Parser.js rather than via a - * stand-alone handler provided to `defineFunction`). - */ - function defineFunctionBuilders({ type, mathmlBuilder }) { - defineFunction({ - type, - names: [], - props: { numArgs: 0 }, - handler() { - throw new Error("Should never be called.") - }, - mathmlBuilder - }); - } - - const normalizeArgument = function(arg) { - return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg - }; - - // Since the corresponding buildMathML function expects a - // list of elements, we normalize for different kinds of arguments - const ordargument = function(arg) { - return arg.type === "ordgroup" ? arg.body : [arg] - }; - - /** - * This node represents a document fragment, which contains elements, but when - * placed into the DOM doesn't have any representation itself. It only contains - * children and doesn't have any DOM node properties. - */ - class DocumentFragment { - constructor(children) { - this.children = children; - this.classes = []; - this.style = {}; - } - - hasClass(className) { - return this.classes.includes(className); - } - - /** Convert the fragment into a node. */ - toNode() { - const frag = document.createDocumentFragment(); - - for (let i = 0; i < this.children.length; i++) { - frag.appendChild(this.children[i].toNode()); - } - - return frag; - } - - /** Convert the fragment into HTML markup. */ - toMarkup() { - let markup = ""; - - // Simply concatenate the markup for the children together. - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - return markup; - } - - /** - * Converts the math node into a string, similar to innerText. Applies to - * MathDomNode's only. - */ - toText() { - // To avoid this, we would subclass documentFragment separately for - // MathML, but polyfills for subclassing is expensive per PR 1469. - const toText = (child) => child.toText(); - return this.children.map(toText).join(""); - } - } - - /** - * These objects store the data about the DOM nodes we create, as well as some - * extra data. They can then be transformed into real DOM nodes with the - * `toNode` function or HTML markup using `toMarkup`. They are useful for both - * storing extra properties on the nodes, as well as providing a way to easily - * work with the DOM. - * - * Similar functions for working with MathML nodes exist in mathMLTree.js. - * - */ - - /** - * Create an HTML className based on a list of classes. In addition to joining - * with spaces, we also remove empty classes. - */ - const createClass = function(classes) { - return classes.filter((cls) => cls).join(" "); - }; - - const initNode = function(classes, style) { - this.classes = classes || []; - this.attributes = {}; - this.style = style || {}; - }; - - /** - * Convert into an HTML node - */ - const toNode = function(tagName) { - const node = document.createElement(tagName); - - // Apply the class - node.className = createClass(this.classes); - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - // Apply attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { - node.setAttribute(attr, this.attributes[attr]); - } - } - - // Append the children, also as HTML nodes - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - - return node; - }; - - /** - * Convert into an HTML markup string - */ - const toMarkup = function(tagName) { - let markup = `<${tagName}`; - - // Add the class - if (this.classes.length) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - - let styles = ""; - - // Add the styles, after hyphenation - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - styles += `${hyphenate(style)}:${this.style[style]};`; - } - } - - if (styles) { - markup += ` style="${styles}"`; - } - - // Add the attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { - markup += ` ${attr}="${escape(this.attributes[attr])}"`; - } - } - - markup += ">"; - - // Add the markup of the children, also as markup - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - markup += ``; - - return markup; - }; - - /** - * This node represents a span node, with a className, a list of children, and - * an inline style. - * - */ - class Span { - constructor(classes, children, style) { - initNode.call(this, classes, style); - this.children = children || []; - } - - setAttribute(attribute, value) { - this.attributes[attribute] = value; - } - - toNode() { - return toNode.call(this, "span"); - } - - toMarkup() { - return toMarkup.call(this, "span"); - } - } - - let TextNode$1 = class TextNode { - constructor(text) { - this.text = text; - } - toNode() { - return document.createTextNode(this.text); - } - toMarkup() { - return escape(this.text); - } - }; - - // Create an node. - class AnchorNode { - constructor(href, classes, children) { - this.href = href; - this.classes = classes; - this.children = children || []; - } - - toNode() { - const node = document.createElement("a"); - node.setAttribute("href", this.href); - if (this.classes.length > 0) { - node.className = createClass(this.classes); - } - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - return node - } - - toMarkup() { - let markup = ` 0) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - markup += ">"; - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - markup += ""; - return markup - } - } - - /* - * This node represents an image embed () element. - */ - class Img { - constructor(src, alt, style) { - this.alt = alt; - this.src = src; - this.classes = ["mord"]; - this.style = style; - } - - hasClass(className) { - return this.classes.includes(className); - } - - toNode() { - const node = document.createElement("img"); - node.src = this.src; - node.alt = this.alt; - node.className = "mord"; - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - return node; - } - - toMarkup() { - let markup = `${this.alt}` and - * `` tags). - */ - class MathNode { - constructor(type, children, classes, style) { - this.type = type; - this.attributes = {}; - this.children = children || []; - this.classes = classes || []; - this.style = style || {}; // Used for elements - this.label = ""; - } - - /** - * Sets an attribute on a MathML node. MathML depends on attributes to convey a - * semantic content, so this is used heavily. - */ - setAttribute(name, value) { - this.attributes[name] = value; - } - - /** - * Gets an attribute on a MathML node. - */ - getAttribute(name) { - return this.attributes[name]; - } - - setLabel(value) { - this.label = value; - } - - /** - * Converts the math node into a MathML-namespaced DOM element. - */ - toNode() { - const node = document.createElementNS("http://www.w3.org/1998/Math/MathML", this.type); - - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { - node.setAttribute(attr, this.attributes[attr]); - } - } - - if (this.classes.length > 0) { - node.className = createClass(this.classes); - } - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - - return node; - } - - /** - * Converts the math node into an HTML markup string. - */ - toMarkup() { - let markup = "<" + this.type; - - // Add the attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { - markup += " " + attr + '="'; - markup += escape(this.attributes[attr]); - markup += '"'; - } - } - - if (this.classes.length > 0) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - - let styles = ""; - - // Add the styles, after hyphenation - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - styles += `${hyphenate(style)}:${this.style[style]};`; - } - } - - if (styles) { - markup += ` style="${styles}"`; - } - - markup += ">"; - - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - markup += ""; - - return markup; - } - - /** - * Converts the math node into a string, similar to innerText, but escaped. - */ - toText() { - return this.children.map((child) => child.toText()).join(""); - } - } - - /** - * This node represents a piece of text. - */ - class TextNode { - constructor(text) { - this.text = text; - } - - /** - * Converts the text node into a DOM text node. - */ - toNode() { - return document.createTextNode(this.text); - } - - /** - * Converts the text node into escaped HTML markup - * (representing the text itself). - */ - toMarkup() { - return escape(this.toText()); - } - - /** - * Converts the text node into a string - * (representing the text itself). - */ - toText() { - return this.text; - } - } - - // Do not make an the only child of a . - // An acts as its own implicit . - const wrapWithMstyle = expression => { - let node; - if (expression.length === 1 && expression[0].type === "mrow") { - node = expression.pop(); - node.type = "mstyle"; - } else { - node = new MathNode("mstyle", expression); - } - return node - }; - - /** - * This file provides support for building horizontal stretchy elements. - */ - - - // TODO: Remove when Chromium stretches \widetilde & \widehat - const estimatedWidth = node => { - let width = 0; - if (node.body && Array.isArray(node.body)) { - for (const item of node.body) { - width += estimatedWidth(item); - } - } else if (node.body) { - width += estimatedWidth(node.body); - } else if (node.type === "supsub") { - width += estimatedWidth(node.base); - if (node.sub) { width += 0.7 * estimatedWidth(node.sub); } - if (node.sup) { width += 0.7 * estimatedWidth(node.sup); } - } else if (node.type === "mathord" || node.type === "textord") { - for (const ch of node.text.split('')) { - const codePoint = ch.codePointAt(0); - if ((0x60 < codePoint && codePoint < 0x7B) || (0x03B0 < codePoint && codePoint < 0x3CA)) { - width += 0.56; // lower case latin or greek. Use advance width of letter n - } else if (0x2F < codePoint && codePoint < 0x3A) { - width += 0.50; // numerals. - } else { - width += 0.92; // advance width of letter M - } - } - } else { - width += 1.0; - } - return width - }; - - const stretchyCodePoint = { - widehat: "^", - widecheck: "ˇ", - widetilde: "~", - wideparen: "⏜", // \u23dc - utilde: "~", - overleftarrow: "\u2190", - underleftarrow: "\u2190", - xleftarrow: "\u2190", - overrightarrow: "\u2192", - underrightarrow: "\u2192", - xrightarrow: "\u2192", - underbrace: "\u23df", - overbrace: "\u23de", - overbracket: "\u23b4", - underbracket: "\u23b5", - overgroup: "\u23e0", - overparen: "⏜", - undergroup: "\u23e1", - underparen: "\u23dd", - overleftrightarrow: "\u2194", - underleftrightarrow: "\u2194", - xleftrightarrow: "\u2194", - Overrightarrow: "\u21d2", - xRightarrow: "\u21d2", - overleftharpoon: "\u21bc", - xleftharpoonup: "\u21bc", - overrightharpoon: "\u21c0", - xrightharpoonup: "\u21c0", - xLeftarrow: "\u21d0", - xLeftrightarrow: "\u21d4", - xhookleftarrow: "\u21a9", - xhookrightarrow: "\u21aa", - xmapsto: "\u21a6", - xrightharpoondown: "\u21c1", - xleftharpoondown: "\u21bd", - xtwoheadleftarrow: "\u219e", - xtwoheadrightarrow: "\u21a0", - xlongequal: "=", - xrightleftarrows: "\u21c4", - xtofrom: "\u21c4", - xleftrightharpoons: "\u21cb", - xrightleftharpoons: "\u21cc", - yields: "\u2192", - yieldsLeft: "\u2190", - mesomerism: "\u2194", - longrightharpoonup: "\u21c0", - longleftharpoondown: "\u21bd", - eqrightharpoonup: "\u21c0", - eqleftharpoondown: "\u21bd", - "\\cdrightarrow": "\u2192", - "\\cdleftarrow": "\u2190", - "\\cdlongequal": "=", - yieldsLeftRight: "\u21c4", - chemequilibrium: "\u21cc" - }; - - const mathMLnode = function(label) { - const child = new TextNode(stretchyCodePoint[label.slice(1)]); - const node = new MathNode("mo", [child]); - node.setAttribute("stretchy", "true"); - return node - }; - - const crookedWides = ["\\widetilde", "\\widehat", "\\widecheck", "\\utilde"]; - - // TODO: Remove when Chromium stretches \widetilde & \widehat - const accentNode = (group) => { - const mo = mathMLnode(group.label); - if (crookedWides.includes(group.label)) { - const width = estimatedWidth(group.base); - if (1 < width && width < 1.6) { - mo.classes.push("tml-crooked-2"); - } else if (1.6 <= width && width < 2.5) { - mo.classes.push("tml-crooked-3"); - } else if (2.5 <= width) { - mo.classes.push("tml-crooked-4"); - } - } - return mo - }; - - /** - * This file holds a list of all no-argument functions and single-character - * symbols (like 'a' or ';'). - * - * For each of the symbols, there are two properties they can have: - * - group (required): the ParseNode group type the symbol should have (i.e. - "textord", "mathord", etc). - * - replace: the character that this symbol or function should be - * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi - * character in the main font). - * - * The outermost map in the table indicates what mode the symbols should be - * accepted in (e.g. "math" or "text"). - */ - - // Some of these have a "-token" suffix since these are also used as `ParseNode` - // types for raw text tokens, and we want to avoid conflicts with higher-level - // `ParseNode` types. These `ParseNode`s are constructed within `Parser` by - // looking up the `symbols` map. - const ATOMS = { - bin: 1, - close: 1, - inner: 1, - open: 1, - punct: 1, - rel: 1 - }; - const NON_ATOMS = { - "accent-token": 1, - mathord: 1, - "op-token": 1, - spacing: 1, - textord: 1 - }; - - const symbols = { - math: {}, - text: {} - }; - - /** `acceptUnicodeChar = true` is only applicable if `replace` is set. */ - function defineSymbol(mode, group, replace, name, acceptUnicodeChar) { - symbols[mode][name] = { group, replace }; - - if (acceptUnicodeChar && replace) { - symbols[mode][replace] = symbols[mode][name]; - } - } - - // Some abbreviations for commonly used strings. - // This helps minify the code, and also spotting typos using jshint. - - // modes: - const math = "math"; - const text = "text"; - - // groups: - const accent = "accent-token"; - const bin = "bin"; - const close = "close"; - const inner = "inner"; - const mathord = "mathord"; - const op = "op-token"; - const open = "open"; - const punct = "punct"; - const rel = "rel"; - const spacing = "spacing"; - const textord = "textord"; - - // Now comes the symbol table - - // Relation Symbols - defineSymbol(math, rel, "\u2261", "\\equiv", true); - defineSymbol(math, rel, "\u227a", "\\prec", true); - defineSymbol(math, rel, "\u227b", "\\succ", true); - defineSymbol(math, rel, "\u223c", "\\sim", true); - defineSymbol(math, rel, "\u27c2", "\\perp", true); - defineSymbol(math, rel, "\u2aaf", "\\preceq", true); - defineSymbol(math, rel, "\u2ab0", "\\succeq", true); - defineSymbol(math, rel, "\u2243", "\\simeq", true); - defineSymbol(math, rel, "\u224c", "\\backcong", true); - defineSymbol(math, rel, "|", "\\mid", true); - defineSymbol(math, rel, "\u226a", "\\ll", true); - defineSymbol(math, rel, "\u226b", "\\gg", true); - defineSymbol(math, rel, "\u224d", "\\asymp", true); - defineSymbol(math, rel, "\u2225", "\\parallel"); - defineSymbol(math, rel, "\u2323", "\\smile", true); - defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true); - defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true); - defineSymbol(math, rel, "\u2250", "\\doteq", true); - defineSymbol(math, rel, "\u2322", "\\frown", true); - defineSymbol(math, rel, "\u220b", "\\ni", true); - defineSymbol(math, rel, "\u220c", "\\notni", true); - defineSymbol(math, rel, "\u221d", "\\propto", true); - defineSymbol(math, rel, "\u22a2", "\\vdash", true); - defineSymbol(math, rel, "\u22a3", "\\dashv", true); - defineSymbol(math, rel, "\u220b", "\\owns"); - defineSymbol(math, rel, "\u2258", "\\arceq", true); - defineSymbol(math, rel, "\u2259", "\\wedgeq", true); - defineSymbol(math, rel, "\u225a", "\\veeeq", true); - defineSymbol(math, rel, "\u225b", "\\stareq", true); - defineSymbol(math, rel, "\u225d", "\\eqdef", true); - defineSymbol(math, rel, "\u225e", "\\measeq", true); - defineSymbol(math, rel, "\u225f", "\\questeq", true); - defineSymbol(math, rel, "\u2260", "\\ne", true); - defineSymbol(math, rel, "\u2260", "\\neq"); - // unicodemath - defineSymbol(math, rel, "\u2a75", "\\eqeq", true); - defineSymbol(math, rel, "\u2a76", "\\eqeqeq", true); - // mathtools.sty - defineSymbol(math, rel, "\u2237", "\\dblcolon", true); - defineSymbol(math, rel, "\u2254", "\\coloneqq", true); - defineSymbol(math, rel, "\u2255", "\\eqqcolon", true); - defineSymbol(math, rel, "\u2239", "\\eqcolon", true); - defineSymbol(math, rel, "\u2A74", "\\Coloneqq", true); - - // Punctuation - defineSymbol(math, punct, "\u002e", "\\ldotp"); - defineSymbol(math, punct, "\u00b7", "\\cdotp"); - - // Misc Symbols - defineSymbol(math, textord, "\u0023", "\\#"); - defineSymbol(text, textord, "\u0023", "\\#"); - defineSymbol(math, textord, "\u0026", "\\&"); - defineSymbol(text, textord, "\u0026", "\\&"); - defineSymbol(math, textord, "\u2135", "\\aleph", true); - defineSymbol(math, textord, "\u2200", "\\forall", true); - defineSymbol(math, textord, "\u210f", "\\hbar", true); - defineSymbol(math, textord, "\u2203", "\\exists", true); - defineSymbol(math, open, "\u2207", "\\nabla", true); - defineSymbol(math, textord, "\u266d", "\\flat", true); - defineSymbol(math, textord, "\u2113", "\\ell", true); - defineSymbol(math, textord, "\u266e", "\\natural", true); - defineSymbol(math, textord, "Å", "\\Angstrom", true); - defineSymbol(text, textord, "Å", "\\Angstrom", true); - defineSymbol(math, textord, "\u2663", "\\clubsuit", true); - defineSymbol(math, textord, "\u2667", "\\varclubsuit", true); - defineSymbol(math, textord, "\u2118", "\\wp", true); - defineSymbol(math, textord, "\u266f", "\\sharp", true); - defineSymbol(math, textord, "\u2662", "\\diamondsuit", true); - defineSymbol(math, textord, "\u2666", "\\vardiamondsuit", true); - defineSymbol(math, textord, "\u211c", "\\Re", true); - defineSymbol(math, textord, "\u2661", "\\heartsuit", true); - defineSymbol(math, textord, "\u2665", "\\varheartsuit", true); - defineSymbol(math, textord, "\u2111", "\\Im", true); - defineSymbol(math, textord, "\u2660", "\\spadesuit", true); - defineSymbol(math, textord, "\u2664", "\\varspadesuit", true); - defineSymbol(math, textord, "\u2640", "\\female", true); - defineSymbol(math, textord, "\u2642", "\\male", true); - defineSymbol(math, textord, "\u00a7", "\\S", true); - defineSymbol(text, textord, "\u00a7", "\\S"); - defineSymbol(math, textord, "\u00b6", "\\P", true); - defineSymbol(text, textord, "\u00b6", "\\P"); - defineSymbol(text, textord, "\u263a", "\\smiley", true); - defineSymbol(math, textord, "\u263a", "\\smiley", true); - - // Math and Text - defineSymbol(math, textord, "\u2020", "\\dag"); - defineSymbol(text, textord, "\u2020", "\\dag"); - defineSymbol(text, textord, "\u2020", "\\textdagger"); - defineSymbol(math, textord, "\u2021", "\\ddag"); - defineSymbol(text, textord, "\u2021", "\\ddag"); - defineSymbol(text, textord, "\u2021", "\\textdaggerdbl"); - - // Large Delimiters - defineSymbol(math, close, "\u23b1", "\\rmoustache", true); - defineSymbol(math, open, "\u23b0", "\\lmoustache", true); - defineSymbol(math, close, "\u27ef", "\\rgroup", true); - defineSymbol(math, open, "\u27ee", "\\lgroup", true); - - // Binary Operators - defineSymbol(math, bin, "\u2213", "\\mp", true); - defineSymbol(math, bin, "\u2296", "\\ominus", true); - defineSymbol(math, bin, "\u228e", "\\uplus", true); - defineSymbol(math, bin, "\u2293", "\\sqcap", true); - defineSymbol(math, bin, "\u2217", "\\ast"); - defineSymbol(math, bin, "\u2294", "\\sqcup", true); - defineSymbol(math, bin, "\u25ef", "\\bigcirc", true); - defineSymbol(math, bin, "\u2219", "\\bullet", true); - defineSymbol(math, bin, "\u2021", "\\ddagger"); - defineSymbol(math, bin, "\u2240", "\\wr", true); - defineSymbol(math, bin, "\u2a3f", "\\amalg"); - defineSymbol(math, bin, "\u0026", "\\And"); // from amsmath - defineSymbol(math, bin, "\u2AFD", "\\sslash", true); // from stmaryrd - - // Arrow Symbols - defineSymbol(math, rel, "\u27f5", "\\longleftarrow", true); - defineSymbol(math, rel, "\u21d0", "\\Leftarrow", true); - defineSymbol(math, rel, "\u27f8", "\\Longleftarrow", true); - defineSymbol(math, rel, "\u27f6", "\\longrightarrow", true); - defineSymbol(math, rel, "\u21d2", "\\Rightarrow", true); - defineSymbol(math, rel, "\u27f9", "\\Longrightarrow", true); - defineSymbol(math, rel, "\u2194", "\\leftrightarrow", true); - defineSymbol(math, rel, "\u27f7", "\\longleftrightarrow", true); - defineSymbol(math, rel, "\u21d4", "\\Leftrightarrow", true); - defineSymbol(math, rel, "\u27fa", "\\Longleftrightarrow", true); - defineSymbol(math, rel, "\u21a4", "\\mapsfrom", true); - defineSymbol(math, rel, "\u21a6", "\\mapsto", true); - defineSymbol(math, rel, "\u27fc", "\\longmapsto", true); - defineSymbol(math, rel, "\u2197", "\\nearrow", true); - defineSymbol(math, rel, "\u21a9", "\\hookleftarrow", true); - defineSymbol(math, rel, "\u21aa", "\\hookrightarrow", true); - defineSymbol(math, rel, "\u2198", "\\searrow", true); - defineSymbol(math, rel, "\u21bc", "\\leftharpoonup", true); - defineSymbol(math, rel, "\u21c0", "\\rightharpoonup", true); - defineSymbol(math, rel, "\u2199", "\\swarrow", true); - defineSymbol(math, rel, "\u21bd", "\\leftharpoondown", true); - defineSymbol(math, rel, "\u21c1", "\\rightharpoondown", true); - defineSymbol(math, rel, "\u2196", "\\nwarrow", true); - defineSymbol(math, rel, "\u21cc", "\\rightleftharpoons", true); - defineSymbol(math, mathord, "\u21af", "\\lightning", true); - defineSymbol(math, mathord, "\u220E", "\\QED", true); - defineSymbol(math, mathord, "\u2030", "\\permil", true); - defineSymbol(text, textord, "\u2030", "\\permil"); - defineSymbol(math, mathord, "\u2609", "\\astrosun", true); - defineSymbol(math, mathord, "\u263c", "\\sun", true); - defineSymbol(math, mathord, "\u263e", "\\leftmoon", true); - defineSymbol(math, mathord, "\u263d", "\\rightmoon", true); - defineSymbol(math, mathord, "\u2295", "\\Earth"); - - // AMS Negated Binary Relations - defineSymbol(math, rel, "\u226e", "\\nless", true); - // Symbol names preceded by "@" each have a corresponding macro. - defineSymbol(math, rel, "\u2a87", "\\lneq", true); - defineSymbol(math, rel, "\u2268", "\\lneqq", true); - defineSymbol(math, rel, "\u2268\ufe00", "\\lvertneqq"); - defineSymbol(math, rel, "\u22e6", "\\lnsim", true); - defineSymbol(math, rel, "\u2a89", "\\lnapprox", true); - defineSymbol(math, rel, "\u2280", "\\nprec", true); - // unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym. - defineSymbol(math, rel, "\u22e0", "\\npreceq", true); - defineSymbol(math, rel, "\u22e8", "\\precnsim", true); - defineSymbol(math, rel, "\u2ab9", "\\precnapprox", true); - defineSymbol(math, rel, "\u2241", "\\nsim", true); - defineSymbol(math, rel, "\u2224", "\\nmid", true); - defineSymbol(math, rel, "\u2224", "\\nshortmid"); - defineSymbol(math, rel, "\u22ac", "\\nvdash", true); - defineSymbol(math, rel, "\u22ad", "\\nvDash", true); - defineSymbol(math, rel, "\u22ea", "\\ntriangleleft"); - defineSymbol(math, rel, "\u22ec", "\\ntrianglelefteq", true); - defineSymbol(math, rel, "\u2284", "\\nsubset", true); - defineSymbol(math, rel, "\u2285", "\\nsupset", true); - defineSymbol(math, rel, "\u228a", "\\subsetneq", true); - defineSymbol(math, rel, "\u228a\ufe00", "\\varsubsetneq"); - defineSymbol(math, rel, "\u2acb", "\\subsetneqq", true); - defineSymbol(math, rel, "\u2acb\ufe00", "\\varsubsetneqq"); - defineSymbol(math, rel, "\u226f", "\\ngtr", true); - defineSymbol(math, rel, "\u2a88", "\\gneq", true); - defineSymbol(math, rel, "\u2269", "\\gneqq", true); - defineSymbol(math, rel, "\u2269\ufe00", "\\gvertneqq"); - defineSymbol(math, rel, "\u22e7", "\\gnsim", true); - defineSymbol(math, rel, "\u2a8a", "\\gnapprox", true); - defineSymbol(math, rel, "\u2281", "\\nsucc", true); - // unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym. - defineSymbol(math, rel, "\u22e1", "\\nsucceq", true); - defineSymbol(math, rel, "\u22e9", "\\succnsim", true); - defineSymbol(math, rel, "\u2aba", "\\succnapprox", true); - // unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym. - defineSymbol(math, rel, "\u2246", "\\ncong", true); - defineSymbol(math, rel, "\u2226", "\\nparallel", true); - defineSymbol(math, rel, "\u2226", "\\nshortparallel"); - defineSymbol(math, rel, "\u22af", "\\nVDash", true); - defineSymbol(math, rel, "\u22eb", "\\ntriangleright"); - defineSymbol(math, rel, "\u22ed", "\\ntrianglerighteq", true); - defineSymbol(math, rel, "\u228b", "\\supsetneq", true); - defineSymbol(math, rel, "\u228b", "\\varsupsetneq"); - defineSymbol(math, rel, "\u2acc", "\\supsetneqq", true); - defineSymbol(math, rel, "\u2acc\ufe00", "\\varsupsetneqq"); - defineSymbol(math, rel, "\u22ae", "\\nVdash", true); - defineSymbol(math, rel, "\u2ab5", "\\precneqq", true); - defineSymbol(math, rel, "\u2ab6", "\\succneqq", true); - defineSymbol(math, bin, "\u22b4", "\\unlhd"); - defineSymbol(math, bin, "\u22b5", "\\unrhd"); - - // AMS Negated Arrows - defineSymbol(math, rel, "\u219a", "\\nleftarrow", true); - defineSymbol(math, rel, "\u219b", "\\nrightarrow", true); - defineSymbol(math, rel, "\u21cd", "\\nLeftarrow", true); - defineSymbol(math, rel, "\u21cf", "\\nRightarrow", true); - defineSymbol(math, rel, "\u21ae", "\\nleftrightarrow", true); - defineSymbol(math, rel, "\u21ce", "\\nLeftrightarrow", true); - - // AMS Misc - defineSymbol(math, rel, "\u25b3", "\\vartriangle"); - defineSymbol(math, textord, "\u210f", "\\hslash"); - defineSymbol(math, textord, "\u25bd", "\\triangledown"); - defineSymbol(math, textord, "\u25ca", "\\lozenge"); - defineSymbol(math, textord, "\u24c8", "\\circledS"); - defineSymbol(math, textord, "\u00ae", "\\circledR", true); - defineSymbol(text, textord, "\u00ae", "\\circledR"); - defineSymbol(text, textord, "\u00ae", "\\textregistered"); - defineSymbol(math, textord, "\u2221", "\\measuredangle", true); - defineSymbol(math, textord, "\u2204", "\\nexists"); - defineSymbol(math, textord, "\u2127", "\\mho"); - defineSymbol(math, textord, "\u2132", "\\Finv", true); - defineSymbol(math, textord, "\u2141", "\\Game", true); - defineSymbol(math, textord, "\u2035", "\\backprime"); - defineSymbol(math, textord, "\u2036", "\\backdprime"); - defineSymbol(math, textord, "\u2037", "\\backtrprime"); - defineSymbol(math, textord, "\u25b2", "\\blacktriangle"); - defineSymbol(math, textord, "\u25bc", "\\blacktriangledown"); - defineSymbol(math, textord, "\u25a0", "\\blacksquare"); - defineSymbol(math, textord, "\u29eb", "\\blacklozenge"); - defineSymbol(math, textord, "\u2605", "\\bigstar"); - defineSymbol(math, textord, "\u2222", "\\sphericalangle", true); - defineSymbol(math, textord, "\u2201", "\\complement", true); - defineSymbol(math, textord, "\u2571", "\\diagup"); - defineSymbol(math, textord, "\u2572", "\\diagdown"); - defineSymbol(math, textord, "\u25a1", "\\square"); - defineSymbol(math, textord, "\u25a1", "\\Box"); - defineSymbol(math, textord, "\u25ca", "\\Diamond"); - // unicode-math maps U+A5 to \mathyen. We map to AMS function \yen - defineSymbol(math, textord, "\u00a5", "\\yen", true); - defineSymbol(text, textord, "\u00a5", "\\yen", true); - defineSymbol(math, textord, "\u2713", "\\checkmark", true); - defineSymbol(text, textord, "\u2713", "\\checkmark"); - defineSymbol(math, textord, "\u2717", "\\ballotx", true); - defineSymbol(text, textord, "\u2717", "\\ballotx"); - defineSymbol(text, textord, "\u2022", "\\textbullet"); - - // AMS Hebrew - defineSymbol(math, textord, "\u2136", "\\beth", true); - defineSymbol(math, textord, "\u2138", "\\daleth", true); - defineSymbol(math, textord, "\u2137", "\\gimel", true); - - // AMS Greek - defineSymbol(math, textord, "\u03dd", "\\digamma", true); - defineSymbol(math, textord, "\u03f0", "\\varkappa"); - - // AMS Delimiters - defineSymbol(math, open, "\u231C", "\\ulcorner", true); - defineSymbol(math, close, "\u231D", "\\urcorner", true); - defineSymbol(math, open, "\u231E", "\\llcorner", true); - defineSymbol(math, close, "\u231F", "\\lrcorner", true); - - // AMS Binary Relations - defineSymbol(math, rel, "\u2266", "\\leqq", true); - defineSymbol(math, rel, "\u2a7d", "\\leqslant", true); - defineSymbol(math, rel, "\u2a95", "\\eqslantless", true); - defineSymbol(math, rel, "\u2272", "\\lesssim", true); - defineSymbol(math, rel, "\u2a85", "\\lessapprox", true); - defineSymbol(math, rel, "\u224a", "\\approxeq", true); - defineSymbol(math, bin, "\u22d6", "\\lessdot"); - defineSymbol(math, rel, "\u22d8", "\\lll", true); - defineSymbol(math, rel, "\u2276", "\\lessgtr", true); - defineSymbol(math, rel, "\u22da", "\\lesseqgtr", true); - defineSymbol(math, rel, "\u2a8b", "\\lesseqqgtr", true); - defineSymbol(math, rel, "\u2251", "\\doteqdot"); - defineSymbol(math, rel, "\u2253", "\\risingdotseq", true); - defineSymbol(math, rel, "\u2252", "\\fallingdotseq", true); - defineSymbol(math, rel, "\u223d", "\\backsim", true); - defineSymbol(math, rel, "\u22cd", "\\backsimeq", true); - defineSymbol(math, rel, "\u2ac5", "\\subseteqq", true); - defineSymbol(math, rel, "\u22d0", "\\Subset", true); - defineSymbol(math, rel, "\u228f", "\\sqsubset", true); - defineSymbol(math, rel, "\u227c", "\\preccurlyeq", true); - defineSymbol(math, rel, "\u22de", "\\curlyeqprec", true); - defineSymbol(math, rel, "\u227e", "\\precsim", true); - defineSymbol(math, rel, "\u2ab7", "\\precapprox", true); - defineSymbol(math, rel, "\u22b2", "\\vartriangleleft"); - defineSymbol(math, rel, "\u22b4", "\\trianglelefteq"); - defineSymbol(math, rel, "\u22a8", "\\vDash", true); - defineSymbol(math, rel, "\u22ab", "\\VDash", true); - defineSymbol(math, rel, "\u22aa", "\\Vvdash", true); - defineSymbol(math, rel, "\u2323", "\\smallsmile"); - defineSymbol(math, rel, "\u2322", "\\smallfrown"); - defineSymbol(math, rel, "\u224f", "\\bumpeq", true); - defineSymbol(math, rel, "\u224e", "\\Bumpeq", true); - defineSymbol(math, rel, "\u2267", "\\geqq", true); - defineSymbol(math, rel, "\u2a7e", "\\geqslant", true); - defineSymbol(math, rel, "\u2a96", "\\eqslantgtr", true); - defineSymbol(math, rel, "\u2273", "\\gtrsim", true); - defineSymbol(math, rel, "\u2a86", "\\gtrapprox", true); - defineSymbol(math, bin, "\u22d7", "\\gtrdot"); - defineSymbol(math, rel, "\u22d9", "\\ggg", true); - defineSymbol(math, rel, "\u2277", "\\gtrless", true); - defineSymbol(math, rel, "\u22db", "\\gtreqless", true); - defineSymbol(math, rel, "\u2a8c", "\\gtreqqless", true); - defineSymbol(math, rel, "\u2256", "\\eqcirc", true); - defineSymbol(math, rel, "\u2257", "\\circeq", true); - defineSymbol(math, rel, "\u225c", "\\triangleq", true); - defineSymbol(math, rel, "\u223c", "\\thicksim"); - defineSymbol(math, rel, "\u2248", "\\thickapprox"); - defineSymbol(math, rel, "\u2ac6", "\\supseteqq", true); - defineSymbol(math, rel, "\u22d1", "\\Supset", true); - defineSymbol(math, rel, "\u2290", "\\sqsupset", true); - defineSymbol(math, rel, "\u227d", "\\succcurlyeq", true); - defineSymbol(math, rel, "\u22df", "\\curlyeqsucc", true); - defineSymbol(math, rel, "\u227f", "\\succsim", true); - defineSymbol(math, rel, "\u2ab8", "\\succapprox", true); - defineSymbol(math, rel, "\u22b3", "\\vartriangleright"); - defineSymbol(math, rel, "\u22b5", "\\trianglerighteq"); - defineSymbol(math, rel, "\u22a9", "\\Vdash", true); - defineSymbol(math, rel, "\u2223", "\\shortmid"); - defineSymbol(math, rel, "\u2225", "\\shortparallel"); - defineSymbol(math, rel, "\u226c", "\\between", true); - defineSymbol(math, rel, "\u22d4", "\\pitchfork", true); - defineSymbol(math, rel, "\u221d", "\\varpropto"); - defineSymbol(math, rel, "\u25c0", "\\blacktriangleleft"); - // unicode-math says that \therefore is a mathord atom. - // We kept the amssymb atom type, which is rel. - defineSymbol(math, rel, "\u2234", "\\therefore", true); - defineSymbol(math, rel, "\u220d", "\\backepsilon"); - defineSymbol(math, rel, "\u25b6", "\\blacktriangleright"); - // unicode-math says that \because is a mathord atom. - // We kept the amssymb atom type, which is rel. - defineSymbol(math, rel, "\u2235", "\\because", true); - defineSymbol(math, rel, "\u22d8", "\\llless"); - defineSymbol(math, rel, "\u22d9", "\\gggtr"); - defineSymbol(math, bin, "\u22b2", "\\lhd"); - defineSymbol(math, bin, "\u22b3", "\\rhd"); - defineSymbol(math, rel, "\u2242", "\\eqsim", true); - defineSymbol(math, rel, "\u2251", "\\Doteq", true); - defineSymbol(math, rel, "\u297d", "\\strictif", true); - defineSymbol(math, rel, "\u297c", "\\strictfi", true); - - // AMS Binary Operators - defineSymbol(math, bin, "\u2214", "\\dotplus", true); - defineSymbol(math, bin, "\u2216", "\\smallsetminus"); - defineSymbol(math, bin, "\u22d2", "\\Cap", true); - defineSymbol(math, bin, "\u22d3", "\\Cup", true); - defineSymbol(math, bin, "\u2a5e", "\\doublebarwedge", true); - defineSymbol(math, bin, "\u229f", "\\boxminus", true); - defineSymbol(math, bin, "\u229e", "\\boxplus", true); - defineSymbol(math, bin, "\u29C4", "\\boxslash", true); - defineSymbol(math, bin, "\u22c7", "\\divideontimes", true); - defineSymbol(math, bin, "\u22c9", "\\ltimes", true); - defineSymbol(math, bin, "\u22ca", "\\rtimes", true); - defineSymbol(math, bin, "\u22cb", "\\leftthreetimes", true); - defineSymbol(math, bin, "\u22cc", "\\rightthreetimes", true); - defineSymbol(math, bin, "\u22cf", "\\curlywedge", true); - defineSymbol(math, bin, "\u22ce", "\\curlyvee", true); - defineSymbol(math, bin, "\u229d", "\\circleddash", true); - defineSymbol(math, bin, "\u229b", "\\circledast", true); - defineSymbol(math, bin, "\u22ba", "\\intercal", true); - defineSymbol(math, bin, "\u22d2", "\\doublecap"); - defineSymbol(math, bin, "\u22d3", "\\doublecup"); - defineSymbol(math, bin, "\u22a0", "\\boxtimes", true); - defineSymbol(math, bin, "\u22c8", "\\bowtie", true); - defineSymbol(math, bin, "\u22c8", "\\Join"); - defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true); - defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true); - defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true); - - // stix Binary Operators - defineSymbol(math, bin, "\u2238", "\\dotminus", true); - defineSymbol(math, bin, "\u27D1", "\\wedgedot", true); - defineSymbol(math, bin, "\u27C7", "\\veedot", true); - defineSymbol(math, bin, "\u2A62", "\\doublebarvee", true); - defineSymbol(math, bin, "\u2A63", "\\veedoublebar", true); - defineSymbol(math, bin, "\u2A5F", "\\wedgebar", true); - defineSymbol(math, bin, "\u2A60", "\\wedgedoublebar", true); - defineSymbol(math, bin, "\u2A54", "\\Vee", true); - defineSymbol(math, bin, "\u2A53", "\\Wedge", true); - defineSymbol(math, bin, "\u2A43", "\\barcap", true); - defineSymbol(math, bin, "\u2A42", "\\barcup", true); - defineSymbol(math, bin, "\u2A48", "\\capbarcup", true); - defineSymbol(math, bin, "\u2A40", "\\capdot", true); - defineSymbol(math, bin, "\u2A47", "\\capovercup", true); - defineSymbol(math, bin, "\u2A46", "\\cupovercap", true); - defineSymbol(math, bin, "\u2A4D", "\\closedvarcap", true); - defineSymbol(math, bin, "\u2A4C", "\\closedvarcup", true); - defineSymbol(math, bin, "\u2A2A", "\\minusdot", true); - defineSymbol(math, bin, "\u2A2B", "\\minusfdots", true); - defineSymbol(math, bin, "\u2A2C", "\\minusrdots", true); - defineSymbol(math, bin, "\u22BB", "\\Xor", true); - defineSymbol(math, bin, "\u22BC", "\\Nand", true); - defineSymbol(math, bin, "\u22BD", "\\Nor", true); - defineSymbol(math, bin, "\u22BD", "\\barvee"); - defineSymbol(math, bin, "\u2AF4", "\\interleave", true); - defineSymbol(math, bin, "\u29E2", "\\shuffle", true); - defineSymbol(math, bin, "\u2AF6", "\\threedotcolon", true); - defineSymbol(math, bin, "\u2982", "\\typecolon", true); - defineSymbol(math, bin, "\u223E", "\\invlazys", true); - defineSymbol(math, bin, "\u2A4B", "\\twocaps", true); - defineSymbol(math, bin, "\u2A4A", "\\twocups", true); - defineSymbol(math, bin, "\u2A4E", "\\Sqcap", true); - defineSymbol(math, bin, "\u2A4F", "\\Sqcup", true); - defineSymbol(math, bin, "\u2A56", "\\veeonvee", true); - defineSymbol(math, bin, "\u2A55", "\\wedgeonwedge", true); - defineSymbol(math, bin, "\u29D7", "\\blackhourglass", true); - defineSymbol(math, bin, "\u29C6", "\\boxast", true); - defineSymbol(math, bin, "\u29C8", "\\boxbox", true); - defineSymbol(math, bin, "\u29C7", "\\boxcircle", true); - defineSymbol(math, bin, "\u229C", "\\circledequal", true); - defineSymbol(math, bin, "\u29B7", "\\circledparallel", true); - defineSymbol(math, bin, "\u29B6", "\\circledvert", true); - defineSymbol(math, bin, "\u29B5", "\\circlehbar", true); - defineSymbol(math, bin, "\u27E1", "\\concavediamond", true); - defineSymbol(math, bin, "\u27E2", "\\concavediamondtickleft", true); - defineSymbol(math, bin, "\u27E3", "\\concavediamondtickright", true); - defineSymbol(math, bin, "\u22C4", "\\diamond", true); - defineSymbol(math, bin, "\u29D6", "\\hourglass", true); - defineSymbol(math, bin, "\u27E0", "\\lozengeminus", true); - defineSymbol(math, bin, "\u233D", "\\obar", true); - defineSymbol(math, bin, "\u29B8", "\\obslash", true); - defineSymbol(math, bin, "\u2A38", "\\odiv", true); - defineSymbol(math, bin, "\u29C1", "\\ogreaterthan", true); - defineSymbol(math, bin, "\u29C0", "\\olessthan", true); - defineSymbol(math, bin, "\u29B9", "\\operp", true); - defineSymbol(math, bin, "\u2A37", "\\Otimes", true); - defineSymbol(math, bin, "\u2A36", "\\otimeshat", true); - defineSymbol(math, bin, "\u22C6", "\\star", true); - defineSymbol(math, bin, "\u25B3", "\\triangle", true); - defineSymbol(math, bin, "\u2A3A", "\\triangleminus", true); - defineSymbol(math, bin, "\u2A39", "\\triangleplus", true); - defineSymbol(math, bin, "\u2A3B", "\\triangletimes", true); - defineSymbol(math, bin, "\u27E4", "\\whitesquaretickleft", true); - defineSymbol(math, bin, "\u27E5", "\\whitesquaretickright", true); - defineSymbol(math, bin, "\u2A33", "\\smashtimes", true); - - // AMS Arrows - // Note: unicode-math maps \u21e2 to their own function \rightdasharrow. - // We'll map it to AMS function \dashrightarrow. It produces the same atom. - defineSymbol(math, rel, "\u21e2", "\\dashrightarrow", true); - // unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym. - defineSymbol(math, rel, "\u21e0", "\\dashleftarrow", true); - defineSymbol(math, rel, "\u21c7", "\\leftleftarrows", true); - defineSymbol(math, rel, "\u21c6", "\\leftrightarrows", true); - defineSymbol(math, rel, "\u21da", "\\Lleftarrow", true); - defineSymbol(math, rel, "\u219e", "\\twoheadleftarrow", true); - defineSymbol(math, rel, "\u21a2", "\\leftarrowtail", true); - defineSymbol(math, rel, "\u21ab", "\\looparrowleft", true); - defineSymbol(math, rel, "\u21cb", "\\leftrightharpoons", true); - defineSymbol(math, rel, "\u21b6", "\\curvearrowleft", true); - // unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym. - defineSymbol(math, rel, "\u21ba", "\\circlearrowleft", true); - defineSymbol(math, rel, "\u21b0", "\\Lsh", true); - defineSymbol(math, rel, "\u21c8", "\\upuparrows", true); - defineSymbol(math, rel, "\u21bf", "\\upharpoonleft", true); - defineSymbol(math, rel, "\u21c3", "\\downharpoonleft", true); - defineSymbol(math, rel, "\u22b6", "\\origof", true); - defineSymbol(math, rel, "\u22b7", "\\imageof", true); - defineSymbol(math, rel, "\u22b8", "\\multimap", true); - defineSymbol(math, rel, "\u21ad", "\\leftrightsquigarrow", true); - defineSymbol(math, rel, "\u21c9", "\\rightrightarrows", true); - defineSymbol(math, rel, "\u21c4", "\\rightleftarrows", true); - defineSymbol(math, rel, "\u21a0", "\\twoheadrightarrow", true); - defineSymbol(math, rel, "\u21a3", "\\rightarrowtail", true); - defineSymbol(math, rel, "\u21ac", "\\looparrowright", true); - defineSymbol(math, rel, "\u21b7", "\\curvearrowright", true); - // unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym. - defineSymbol(math, rel, "\u21bb", "\\circlearrowright", true); - defineSymbol(math, rel, "\u21b1", "\\Rsh", true); - defineSymbol(math, rel, "\u21ca", "\\downdownarrows", true); - defineSymbol(math, rel, "\u21be", "\\upharpoonright", true); - defineSymbol(math, rel, "\u21c2", "\\downharpoonright", true); - defineSymbol(math, rel, "\u21dd", "\\rightsquigarrow", true); - defineSymbol(math, rel, "\u21dd", "\\leadsto"); - defineSymbol(math, rel, "\u21db", "\\Rrightarrow", true); - defineSymbol(math, rel, "\u21be", "\\restriction"); - - defineSymbol(math, textord, "\u2018", "`"); - defineSymbol(math, textord, "$", "\\$"); - defineSymbol(text, textord, "$", "\\$"); - defineSymbol(text, textord, "$", "\\textdollar"); - defineSymbol(math, textord, "¢", "\\cent"); - defineSymbol(text, textord, "¢", "\\cent"); - defineSymbol(math, textord, "%", "\\%"); - defineSymbol(text, textord, "%", "\\%"); - defineSymbol(math, textord, "_", "\\_"); - defineSymbol(text, textord, "_", "\\_"); - defineSymbol(text, textord, "_", "\\textunderscore"); - defineSymbol(text, textord, "\u2423", "\\textvisiblespace", true); - defineSymbol(math, textord, "\u2220", "\\angle", true); - defineSymbol(math, textord, "\u221e", "\\infty", true); - defineSymbol(math, textord, "\u2032", "\\prime"); - defineSymbol(math, textord, "\u2033", "\\dprime"); - defineSymbol(math, textord, "\u2034", "\\trprime"); - defineSymbol(math, textord, "\u2057", "\\qprime"); - defineSymbol(math, textord, "\u25b3", "\\triangle"); - defineSymbol(text, textord, "\u0391", "\\Alpha", true); - defineSymbol(text, textord, "\u0392", "\\Beta", true); - defineSymbol(text, textord, "\u0393", "\\Gamma", true); - defineSymbol(text, textord, "\u0394", "\\Delta", true); - defineSymbol(text, textord, "\u0395", "\\Epsilon", true); - defineSymbol(text, textord, "\u0396", "\\Zeta", true); - defineSymbol(text, textord, "\u0397", "\\Eta", true); - defineSymbol(text, textord, "\u0398", "\\Theta", true); - defineSymbol(text, textord, "\u0399", "\\Iota", true); - defineSymbol(text, textord, "\u039a", "\\Kappa", true); - defineSymbol(text, textord, "\u039b", "\\Lambda", true); - defineSymbol(text, textord, "\u039c", "\\Mu", true); - defineSymbol(text, textord, "\u039d", "\\Nu", true); - defineSymbol(text, textord, "\u039e", "\\Xi", true); - defineSymbol(text, textord, "\u039f", "\\Omicron", true); - defineSymbol(text, textord, "\u03a0", "\\Pi", true); - defineSymbol(text, textord, "\u03a1", "\\Rho", true); - defineSymbol(text, textord, "\u03a3", "\\Sigma", true); - defineSymbol(text, textord, "\u03a4", "\\Tau", true); - defineSymbol(text, textord, "\u03a5", "\\Upsilon", true); - defineSymbol(text, textord, "\u03a6", "\\Phi", true); - defineSymbol(text, textord, "\u03a7", "\\Chi", true); - defineSymbol(text, textord, "\u03a8", "\\Psi", true); - defineSymbol(text, textord, "\u03a9", "\\Omega", true); - defineSymbol(math, mathord, "\u0391", "\\Alpha", true); - defineSymbol(math, mathord, "\u0392", "\\Beta", true); - defineSymbol(math, mathord, "\u0393", "\\Gamma", true); - defineSymbol(math, mathord, "\u0394", "\\Delta", true); - defineSymbol(math, mathord, "\u0395", "\\Epsilon", true); - defineSymbol(math, mathord, "\u0396", "\\Zeta", true); - defineSymbol(math, mathord, "\u0397", "\\Eta", true); - defineSymbol(math, mathord, "\u0398", "\\Theta", true); - defineSymbol(math, mathord, "\u0399", "\\Iota", true); - defineSymbol(math, mathord, "\u039a", "\\Kappa", true); - defineSymbol(math, mathord, "\u039b", "\\Lambda", true); - defineSymbol(math, mathord, "\u039c", "\\Mu", true); - defineSymbol(math, mathord, "\u039d", "\\Nu", true); - defineSymbol(math, mathord, "\u039e", "\\Xi", true); - defineSymbol(math, mathord, "\u039f", "\\Omicron", true); - defineSymbol(math, mathord, "\u03a0", "\\Pi", true); - defineSymbol(math, mathord, "\u03a1", "\\Rho", true); - defineSymbol(math, mathord, "\u03a3", "\\Sigma", true); - defineSymbol(math, mathord, "\u03a4", "\\Tau", true); - defineSymbol(math, mathord, "\u03a5", "\\Upsilon", true); - defineSymbol(math, mathord, "\u03a6", "\\Phi", true); - defineSymbol(math, mathord, "\u03a7", "\\Chi", true); - defineSymbol(math, mathord, "\u03a8", "\\Psi", true); - defineSymbol(math, mathord, "\u03a9", "\\Omega", true); - defineSymbol(math, open, "\u00ac", "\\neg", true); - defineSymbol(math, open, "\u00ac", "\\lnot"); - defineSymbol(math, textord, "\u22a4", "\\top"); - defineSymbol(math, textord, "\u22a5", "\\bot"); - defineSymbol(math, textord, "\u2205", "\\emptyset"); - defineSymbol(math, textord, "\u2300", "\\varnothing"); - defineSymbol(math, mathord, "\u03b1", "\\alpha", true); - defineSymbol(math, mathord, "\u03b2", "\\beta", true); - defineSymbol(math, mathord, "\u03b3", "\\gamma", true); - defineSymbol(math, mathord, "\u03b4", "\\delta", true); - defineSymbol(math, mathord, "\u03f5", "\\epsilon", true); - defineSymbol(math, mathord, "\u03b6", "\\zeta", true); - defineSymbol(math, mathord, "\u03b7", "\\eta", true); - defineSymbol(math, mathord, "\u03b8", "\\theta", true); - defineSymbol(math, mathord, "\u03b9", "\\iota", true); - defineSymbol(math, mathord, "\u03ba", "\\kappa", true); - defineSymbol(math, mathord, "\u03bb", "\\lambda", true); - defineSymbol(math, mathord, "\u03bc", "\\mu", true); - defineSymbol(math, mathord, "\u03bd", "\\nu", true); - defineSymbol(math, mathord, "\u03be", "\\xi", true); - defineSymbol(math, mathord, "\u03bf", "\\omicron", true); - defineSymbol(math, mathord, "\u03c0", "\\pi", true); - defineSymbol(math, mathord, "\u03c1", "\\rho", true); - defineSymbol(math, mathord, "\u03c3", "\\sigma", true); - defineSymbol(math, mathord, "\u03c4", "\\tau", true); - defineSymbol(math, mathord, "\u03c5", "\\upsilon", true); - defineSymbol(math, mathord, "\u03d5", "\\phi", true); - defineSymbol(math, mathord, "\u03c7", "\\chi", true); - defineSymbol(math, mathord, "\u03c8", "\\psi", true); - defineSymbol(math, mathord, "\u03c9", "\\omega", true); - defineSymbol(math, mathord, "\u03b5", "\\varepsilon", true); - defineSymbol(math, mathord, "\u03d1", "\\vartheta", true); - defineSymbol(math, mathord, "\u03d6", "\\varpi", true); - defineSymbol(math, mathord, "\u03f1", "\\varrho", true); - defineSymbol(math, mathord, "\u03c2", "\\varsigma", true); - defineSymbol(math, mathord, "\u03c6", "\\varphi", true); - defineSymbol(math, mathord, "\u03d8", "\\Coppa", true); - defineSymbol(math, mathord, "\u03d9", "\\coppa", true); - defineSymbol(math, mathord, "\u03d9", "\\varcoppa", true); - defineSymbol(math, mathord, "\u03de", "\\Koppa", true); - defineSymbol(math, mathord, "\u03df", "\\koppa", true); - defineSymbol(math, mathord, "\u03e0", "\\Sampi", true); - defineSymbol(math, mathord, "\u03e1", "\\sampi", true); - defineSymbol(math, mathord, "\u03da", "\\Stigma", true); - defineSymbol(math, mathord, "\u03db", "\\stigma", true); - defineSymbol(math, mathord, "\u2aeb", "\\Bot"); - - // unicode-math maps U+F0 to \matheth. We map to AMS function \eth - defineSymbol(math, textord, "\u00f0", "\\eth", true); // ð - defineSymbol(text, textord, "\u00f0", "\u00f0"); - // Extended ASCII and non-ASCII Letters - defineSymbol(math, textord, "\u00C5", "\\AA"); // Å - defineSymbol(text, textord, "\u00C5", "\\AA", true); - defineSymbol(math, textord, "\u00C6", "\\AE", true); // Æ - defineSymbol(text, textord, "\u00C6", "\\AE", true); - defineSymbol(math, textord, "\u00D0", "\\DH", true); // Ð - defineSymbol(text, textord, "\u00D0", "\\DH", true); - defineSymbol(math, textord, "\u00DE", "\\TH", true); // Þ - defineSymbol(text, textord, "\u00DE", "\\TH", true); - defineSymbol(math, textord, "\u00DF", "\\ss", true); // ß - defineSymbol(text, textord, "\u00DF", "\\ss", true); - defineSymbol(math, textord, "\u00E5", "\\aa"); // å - defineSymbol(text, textord, "\u00E5", "\\aa", true); - defineSymbol(math, textord, "\u00E6", "\\ae", true); // æ - defineSymbol(text, textord, "\u00E6", "\\ae", true); - defineSymbol(math, textord, "\u00F0", "\\dh"); // ð - defineSymbol(text, textord, "\u00F0", "\\dh", true); - defineSymbol(math, textord, "\u00FE", "\\th", true); // þ - defineSymbol(text, textord, "\u00FE", "\\th", true); - defineSymbol(math, textord, "\u0110", "\\DJ", true); // Đ - defineSymbol(text, textord, "\u0110", "\\DJ", true); - defineSymbol(math, textord, "\u0111", "\\dj", true); // đ - defineSymbol(text, textord, "\u0111", "\\dj", true); - defineSymbol(math, textord, "\u0141", "\\L", true); // Ł - defineSymbol(text, textord, "\u0141", "\\L", true); - defineSymbol(math, textord, "\u0141", "\\l", true); // ł - defineSymbol(text, textord, "\u0141", "\\l", true); - defineSymbol(math, textord, "\u014A", "\\NG", true); // Ŋ - defineSymbol(text, textord, "\u014A", "\\NG", true); - defineSymbol(math, textord, "\u014B", "\\ng", true); // ŋ - defineSymbol(text, textord, "\u014B", "\\ng", true); - defineSymbol(math, textord, "\u0152", "\\OE", true); // Œ - defineSymbol(text, textord, "\u0152", "\\OE", true); - defineSymbol(math, textord, "\u0153", "\\oe", true); // œ - defineSymbol(text, textord, "\u0153", "\\oe", true); - - defineSymbol(math, bin, "\u2217", "\u2217", true); - defineSymbol(math, bin, "+", "+"); - defineSymbol(math, bin, "\u2217", "*"); - defineSymbol(math, bin, "\u2044", "/", true); - defineSymbol(math, bin, "\u2044", "\u2044"); - defineSymbol(math, bin, "\u2212", "-", true); - defineSymbol(math, bin, "\u22c5", "\\cdot", true); - defineSymbol(math, bin, "\u2218", "\\circ", true); - defineSymbol(math, bin, "\u00f7", "\\div", true); - defineSymbol(math, bin, "\u00b1", "\\pm", true); - defineSymbol(math, bin, "\u00d7", "\\times", true); - defineSymbol(math, bin, "\u2229", "\\cap", true); - defineSymbol(math, bin, "\u222a", "\\cup", true); - defineSymbol(math, bin, "\u2216", "\\setminus", true); - defineSymbol(math, bin, "\u2227", "\\land"); - defineSymbol(math, bin, "\u2228", "\\lor"); - defineSymbol(math, bin, "\u2227", "\\wedge", true); - defineSymbol(math, bin, "\u2228", "\\vee", true); - defineSymbol(math, open, "\u27e6", "\\llbracket", true); // stmaryrd/semantic packages - defineSymbol(math, close, "\u27e7", "\\rrbracket", true); - defineSymbol(math, open, "\u27e8", "\\langle", true); - defineSymbol(math, open, "\u27ea", "\\lAngle", true); - defineSymbol(math, open, "\u2989", "\\llangle", true); - defineSymbol(math, open, "|", "\\lvert"); - defineSymbol(math, open, "\u2016", "\\lVert", true); - defineSymbol(math, textord, "!", "\\oc"); // cmll package - defineSymbol(math, textord, "?", "\\wn"); - defineSymbol(math, textord, "\u2193", "\\shpos"); - defineSymbol(math, textord, "\u2195", "\\shift"); - defineSymbol(math, textord, "\u2191", "\\shneg"); - defineSymbol(math, close, "?", "?"); - defineSymbol(math, close, "!", "!"); - defineSymbol(math, close, "‼", "‼"); - defineSymbol(math, close, "\u27e9", "\\rangle", true); - defineSymbol(math, close, "\u27eb", "\\rAngle", true); - defineSymbol(math, close, "\u298a", "\\rrangle", true); - defineSymbol(math, close, "|", "\\rvert"); - defineSymbol(math, close, "\u2016", "\\rVert"); - defineSymbol(math, open, "\u2983", "\\lBrace", true); // stmaryrd/semantic packages - defineSymbol(math, close, "\u2984", "\\rBrace", true); - defineSymbol(math, rel, "=", "\\equal", true); - defineSymbol(math, rel, ":", ":"); - defineSymbol(math, rel, "\u2248", "\\approx", true); - defineSymbol(math, rel, "\u2245", "\\cong", true); - defineSymbol(math, rel, "\u2265", "\\ge"); - defineSymbol(math, rel, "\u2265", "\\geq", true); - defineSymbol(math, rel, "\u2190", "\\gets"); - defineSymbol(math, rel, ">", "\\gt", true); - defineSymbol(math, rel, "\u2208", "\\in", true); - defineSymbol(math, rel, "\u2209", "\\notin", true); - defineSymbol(math, rel, "\ue020", "\\@not"); - defineSymbol(math, rel, "\u2282", "\\subset", true); - defineSymbol(math, rel, "\u2283", "\\supset", true); - defineSymbol(math, rel, "\u2286", "\\subseteq", true); - defineSymbol(math, rel, "\u2287", "\\supseteq", true); - defineSymbol(math, rel, "\u2288", "\\nsubseteq", true); - defineSymbol(math, rel, "\u2288", "\\nsubseteqq"); - defineSymbol(math, rel, "\u2289", "\\nsupseteq", true); - defineSymbol(math, rel, "\u2289", "\\nsupseteqq"); - defineSymbol(math, rel, "\u22a8", "\\models"); - defineSymbol(math, rel, "\u2190", "\\leftarrow", true); - defineSymbol(math, rel, "\u2264", "\\le"); - defineSymbol(math, rel, "\u2264", "\\leq", true); - defineSymbol(math, rel, "<", "\\lt", true); - defineSymbol(math, rel, "\u2192", "\\rightarrow", true); - defineSymbol(math, rel, "\u2192", "\\to"); - defineSymbol(math, rel, "\u2271", "\\ngeq", true); - defineSymbol(math, rel, "\u2271", "\\ngeqq"); - defineSymbol(math, rel, "\u2271", "\\ngeqslant"); - defineSymbol(math, rel, "\u2270", "\\nleq", true); - defineSymbol(math, rel, "\u2270", "\\nleqq"); - defineSymbol(math, rel, "\u2270", "\\nleqslant"); - defineSymbol(math, rel, "\u2aeb", "\\Perp", true); //cmll package - defineSymbol(math, spacing, "\u00a0", "\\ "); - defineSymbol(math, spacing, "\u00a0", "\\space"); - // Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{% - defineSymbol(math, spacing, "\u00a0", "\\nobreakspace"); - defineSymbol(text, spacing, "\u00a0", "\\ "); - defineSymbol(text, spacing, "\u00a0", " "); - defineSymbol(text, spacing, "\u00a0", "\\space"); - defineSymbol(text, spacing, "\u00a0", "\\nobreakspace"); - defineSymbol(math, spacing, null, "\\nobreak"); - defineSymbol(math, spacing, null, "\\allowbreak"); - defineSymbol(math, punct, ",", ","); - defineSymbol(text, punct, ":", ":"); - defineSymbol(math, punct, ";", ";"); - defineSymbol(math, bin, "\u22bc", "\\barwedge"); - defineSymbol(math, bin, "\u22bb", "\\veebar"); - defineSymbol(math, bin, "\u2299", "\\odot", true); - // Firefox turns ⊕ into an emoji. So append \uFE0E. Define Unicode character in macros, not here. - defineSymbol(math, bin, "\u2295\uFE0E", "\\oplus"); - defineSymbol(math, bin, "\u2297", "\\otimes", true); - defineSymbol(math, textord, "\u2202", "\\partial", true); - defineSymbol(math, bin, "\u2298", "\\oslash", true); - defineSymbol(math, bin, "\u229a", "\\circledcirc", true); - defineSymbol(math, bin, "\u22a1", "\\boxdot", true); - defineSymbol(math, bin, "\u25b3", "\\bigtriangleup"); - defineSymbol(math, bin, "\u25bd", "\\bigtriangledown"); - defineSymbol(math, bin, "\u2020", "\\dagger"); - defineSymbol(math, bin, "\u22c4", "\\diamond"); - defineSymbol(math, bin, "\u25c3", "\\triangleleft"); - defineSymbol(math, bin, "\u25b9", "\\triangleright"); - defineSymbol(math, open, "{", "\\{"); - defineSymbol(text, textord, "{", "\\{"); - defineSymbol(text, textord, "{", "\\textbraceleft"); - defineSymbol(math, close, "}", "\\}"); - defineSymbol(text, textord, "}", "\\}"); - defineSymbol(text, textord, "}", "\\textbraceright"); - defineSymbol(math, open, "{", "\\lbrace"); - defineSymbol(math, close, "}", "\\rbrace"); - defineSymbol(math, open, "[", "\\lbrack", true); - defineSymbol(text, textord, "[", "\\lbrack", true); - defineSymbol(math, close, "]", "\\rbrack", true); - defineSymbol(text, textord, "]", "\\rbrack", true); - defineSymbol(math, open, "(", "\\lparen", true); - defineSymbol(math, close, ")", "\\rparen", true); - defineSymbol(math, open, "⦇", "\\llparenthesis", true); - defineSymbol(math, close, "⦈", "\\rrparenthesis", true); - defineSymbol(text, textord, "<", "\\textless", true); // in T1 fontenc - defineSymbol(text, textord, ">", "\\textgreater", true); // in T1 fontenc - defineSymbol(math, open, "\u230a", "\\lfloor", true); - defineSymbol(math, close, "\u230b", "\\rfloor", true); - defineSymbol(math, open, "\u2308", "\\lceil", true); - defineSymbol(math, close, "\u2309", "\\rceil", true); - defineSymbol(math, textord, "\\", "\\backslash"); - defineSymbol(math, textord, "|", "|"); - defineSymbol(math, textord, "|", "\\vert"); - defineSymbol(text, textord, "|", "\\textbar", true); // in T1 fontenc - defineSymbol(math, textord, "\u2016", "\\|"); - defineSymbol(math, textord, "\u2016", "\\Vert"); - defineSymbol(text, textord, "\u2016", "\\textbardbl"); - defineSymbol(text, textord, "~", "\\textasciitilde"); - defineSymbol(text, textord, "\\", "\\textbackslash"); - defineSymbol(text, textord, "^", "\\textasciicircum"); - defineSymbol(math, rel, "\u2191", "\\uparrow", true); - defineSymbol(math, rel, "\u21d1", "\\Uparrow", true); - defineSymbol(math, rel, "\u2193", "\\downarrow", true); - defineSymbol(math, rel, "\u21d3", "\\Downarrow", true); - defineSymbol(math, rel, "\u2195", "\\updownarrow", true); - defineSymbol(math, rel, "\u21d5", "\\Updownarrow", true); - defineSymbol(math, op, "\u2210", "\\coprod"); - defineSymbol(math, op, "\u22c1", "\\bigvee"); - defineSymbol(math, op, "\u22c0", "\\bigwedge"); - defineSymbol(math, op, "\u2a04", "\\biguplus"); - defineSymbol(math, op, "\u2a04", "\\bigcupplus"); - defineSymbol(math, op, "\u2a03", "\\bigcupdot"); - defineSymbol(math, op, "\u2a07", "\\bigdoublevee"); - defineSymbol(math, op, "\u2a08", "\\bigdoublewedge"); - defineSymbol(math, op, "\u22c2", "\\bigcap"); - defineSymbol(math, op, "\u22c3", "\\bigcup"); - defineSymbol(math, op, "\u222b", "\\int"); - defineSymbol(math, op, "\u222b", "\\intop"); - defineSymbol(math, op, "\u222c", "\\iint"); - defineSymbol(math, op, "\u222d", "\\iiint"); - defineSymbol(math, op, "\u220f", "\\prod"); - defineSymbol(math, op, "\u2211", "\\sum"); - defineSymbol(math, op, "\u2a02", "\\bigotimes"); - defineSymbol(math, op, "\u2a01", "\\bigoplus"); - defineSymbol(math, op, "\u2a00", "\\bigodot"); - defineSymbol(math, op, "\u2a09", "\\bigtimes"); - defineSymbol(math, op, "\u222e", "\\oint"); - defineSymbol(math, op, "\u222f", "\\oiint"); - defineSymbol(math, op, "\u2230", "\\oiiint"); - defineSymbol(math, op, "\u2231", "\\intclockwise"); - defineSymbol(math, op, "\u2232", "\\varointclockwise"); - defineSymbol(math, op, "\u2a0c", "\\iiiint"); - defineSymbol(math, op, "\u2a0d", "\\intbar"); - defineSymbol(math, op, "\u2a0e", "\\intBar"); - defineSymbol(math, op, "\u2a0f", "\\fint"); - defineSymbol(math, op, "\u2a12", "\\rppolint"); - defineSymbol(math, op, "\u2a13", "\\scpolint"); - defineSymbol(math, op, "\u2a15", "\\pointint"); - defineSymbol(math, op, "\u2a16", "\\sqint"); - defineSymbol(math, op, "\u2a17", "\\intlarhk"); - defineSymbol(math, op, "\u2a18", "\\intx"); - defineSymbol(math, op, "\u2a19", "\\intcap"); - defineSymbol(math, op, "\u2a1a", "\\intcup"); - defineSymbol(math, op, "\u2a05", "\\bigsqcap"); - defineSymbol(math, op, "\u2a06", "\\bigsqcup"); - defineSymbol(math, op, "\u222b", "\\smallint"); - defineSymbol(text, inner, "\u2026", "\\textellipsis"); - defineSymbol(math, inner, "\u2026", "\\mathellipsis"); - defineSymbol(text, inner, "\u2026", "\\ldots", true); - defineSymbol(math, inner, "\u2026", "\\ldots", true); - defineSymbol(math, inner, "\u22f0", "\\iddots", true); - defineSymbol(math, inner, "\u22ef", "\\@cdots", true); - defineSymbol(math, inner, "\u22f1", "\\ddots", true); - defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro - defineSymbol(text, textord, "\u22ee", "\\varvdots"); - defineSymbol(math, accent, "\u00b4", "\\acute"); - defineSymbol(math, accent, "\u0060", "\\grave"); - defineSymbol(math, accent, "\u00a8", "\\ddot"); - defineSymbol(math, accent, "\u2026", "\\dddot"); - defineSymbol(math, accent, "\u2026\u002e", "\\ddddot"); - defineSymbol(math, accent, "\u007e", "\\tilde"); - defineSymbol(math, accent, "\u203e", "\\bar"); - defineSymbol(math, accent, "\u02d8", "\\breve"); - defineSymbol(math, accent, "\u02c7", "\\check"); - defineSymbol(math, accent, "\u005e", "\\hat"); - defineSymbol(math, accent, "\u2192", "\\vec"); - defineSymbol(math, accent, "\u02d9", "\\dot"); - defineSymbol(math, accent, "\u02da", "\\mathring"); - defineSymbol(math, mathord, "\u0131", "\\imath", true); - defineSymbol(math, mathord, "\u0237", "\\jmath", true); - defineSymbol(math, textord, "\u0131", "\u0131"); - defineSymbol(math, textord, "\u0237", "\u0237"); - defineSymbol(text, textord, "\u0131", "\\i", true); - defineSymbol(text, textord, "\u0237", "\\j", true); - defineSymbol(text, textord, "\u00f8", "\\o", true); - defineSymbol(math, mathord, "\u00f8", "\\o", true); - defineSymbol(text, textord, "\u00d8", "\\O", true); - defineSymbol(math, mathord, "\u00d8", "\\O", true); - defineSymbol(text, accent, "\u02ca", "\\'"); // acute - defineSymbol(text, accent, "\u02cb", "\\`"); // grave - defineSymbol(text, accent, "\u02c6", "\\^"); // circumflex - defineSymbol(text, accent, "\u007e", "\\~"); // tilde - defineSymbol(text, accent, "\u02c9", "\\="); // macron - defineSymbol(text, accent, "\u02d8", "\\u"); // breve - defineSymbol(text, accent, "\u02d9", "\\."); // dot above - defineSymbol(text, accent, "\u00b8", "\\c"); // cedilla - defineSymbol(text, accent, "\u02da", "\\r"); // ring above - defineSymbol(text, accent, "\u02c7", "\\v"); // caron - defineSymbol(text, accent, "\u00a8", '\\"'); // diaeresis - defineSymbol(text, accent, "\u02dd", "\\H"); // double acute - defineSymbol(math, accent, "\u02ca", "\\'"); // acute - defineSymbol(math, accent, "\u02cb", "\\`"); // grave - defineSymbol(math, accent, "\u02c6", "\\^"); // circumflex - defineSymbol(math, accent, "\u007e", "\\~"); // tilde - defineSymbol(math, accent, "\u02c9", "\\="); // macron - defineSymbol(math, accent, "\u02d8", "\\u"); // breve - defineSymbol(math, accent, "\u02d9", "\\."); // dot above - defineSymbol(math, accent, "\u00b8", "\\c"); // cedilla - defineSymbol(math, accent, "\u02da", "\\r"); // ring above - defineSymbol(math, accent, "\u02c7", "\\v"); // caron - defineSymbol(math, accent, "\u00a8", '\\"'); // diaeresis - defineSymbol(math, accent, "\u02dd", "\\H"); // double acute - - // These ligatures are detected and created in Parser.js's `formLigatures`. - const ligatures = { - "--": true, - "---": true, - "``": true, - "''": true - }; - - defineSymbol(text, textord, "\u2013", "--", true); - defineSymbol(text, textord, "\u2013", "\\textendash"); - defineSymbol(text, textord, "\u2014", "---", true); - defineSymbol(text, textord, "\u2014", "\\textemdash"); - defineSymbol(text, textord, "\u2018", "`", true); - defineSymbol(text, textord, "\u2018", "\\textquoteleft"); - defineSymbol(text, textord, "\u2019", "'", true); - defineSymbol(text, textord, "\u2019", "\\textquoteright"); - defineSymbol(text, textord, "\u201c", "``", true); - defineSymbol(text, textord, "\u201c", "\\textquotedblleft"); - defineSymbol(text, textord, "\u201d", "''", true); - defineSymbol(text, textord, "\u201d", "\\textquotedblright"); - // \degree from gensymb package - defineSymbol(math, textord, "\u00b0", "\\degree", true); - defineSymbol(text, textord, "\u00b0", "\\degree"); - // \textdegree from inputenc package - defineSymbol(text, textord, "\u00b0", "\\textdegree", true); - // TODO: In LaTeX, \pounds can generate a different character in text and math - // mode, but among our fonts, only Main-Regular defines this character "163". - defineSymbol(math, textord, "\u00a3", "\\pounds"); - defineSymbol(math, textord, "\u00a3", "\\mathsterling", true); - defineSymbol(text, textord, "\u00a3", "\\pounds"); - defineSymbol(text, textord, "\u00a3", "\\textsterling", true); - defineSymbol(math, textord, "\u2720", "\\maltese"); - defineSymbol(text, textord, "\u2720", "\\maltese"); - defineSymbol(math, textord, "\u20ac", "\\euro", true); - defineSymbol(text, textord, "\u20ac", "\\euro", true); - defineSymbol(text, textord, "\u20ac", "\\texteuro"); - defineSymbol(math, textord, "\u00a9", "\\copyright", true); - defineSymbol(text, textord, "\u00a9", "\\textcopyright"); - defineSymbol(math, textord, "\u2300", "\\diameter", true); - defineSymbol(text, textord, "\u2300", "\\diameter"); - - // Italic Greek - defineSymbol(math, textord, "𝛤", "\\varGamma"); - defineSymbol(math, textord, "𝛥", "\\varDelta"); - defineSymbol(math, textord, "𝛩", "\\varTheta"); - defineSymbol(math, textord, "𝛬", "\\varLambda"); - defineSymbol(math, textord, "𝛯", "\\varXi"); - defineSymbol(math, textord, "𝛱", "\\varPi"); - defineSymbol(math, textord, "𝛴", "\\varSigma"); - defineSymbol(math, textord, "𝛶", "\\varUpsilon"); - defineSymbol(math, textord, "𝛷", "\\varPhi"); - defineSymbol(math, textord, "𝛹", "\\varPsi"); - defineSymbol(math, textord, "𝛺", "\\varOmega"); - defineSymbol(text, textord, "𝛤", "\\varGamma"); - defineSymbol(text, textord, "𝛥", "\\varDelta"); - defineSymbol(text, textord, "𝛩", "\\varTheta"); - defineSymbol(text, textord, "𝛬", "\\varLambda"); - defineSymbol(text, textord, "𝛯", "\\varXi"); - defineSymbol(text, textord, "𝛱", "\\varPi"); - defineSymbol(text, textord, "𝛴", "\\varSigma"); - defineSymbol(text, textord, "𝛶", "\\varUpsilon"); - defineSymbol(text, textord, "𝛷", "\\varPhi"); - defineSymbol(text, textord, "𝛹", "\\varPsi"); - defineSymbol(text, textord, "𝛺", "\\varOmega"); - - - // There are lots of symbols which are the same, so we add them in afterwards. - // All of these are textords in math mode - const mathTextSymbols = '0123456789/@."'; - for (let i = 0; i < mathTextSymbols.length; i++) { - const ch = mathTextSymbols.charAt(i); - defineSymbol(math, textord, ch, ch); - } - - // All of these are textords in text mode - const textSymbols = '0123456789!@*()-=+";:?/.,'; - for (let i = 0; i < textSymbols.length; i++) { - const ch = textSymbols.charAt(i); - defineSymbol(text, textord, ch, ch); - } - - // All of these are textords in text mode, and mathords in math mode - const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - for (let i = 0; i < letters.length; i++) { - const ch = letters.charAt(i); - defineSymbol(math, mathord, ch, ch); - defineSymbol(text, textord, ch, ch); - } - - // Some more letters in Unicode Basic Multilingual Plane. - const narrow = "ÇÐÞçþℂℍℕℙℚℝℤℎℏℊℋℌℐℑℒℓ℘ℛℜℬℰℱℳℭℨ"; - for (let i = 0; i < narrow.length; i++) { - const ch = narrow.charAt(i); - defineSymbol(math, mathord, ch, ch); - defineSymbol(text, textord, ch, ch); - } - - // The next loop loads wide (surrogate pair) characters. - // We support some letters in the Unicode range U+1D400 to U+1D7FF, - // Mathematical Alphanumeric Symbols. - let wideChar = ""; - for (let i = 0; i < letters.length; i++) { - // The hex numbers in the next line are a surrogate pair. - // 0xD835 is the high surrogate for all letters in the range we support. - // 0xDC00 is the low surrogate for bold A. - wideChar = String.fromCharCode(0xd835, 0xdc00 + i); // A-Z a-z bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdc34 + i); // A-Z a-z italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdc68 + i); // A-Z a-z bold italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdd04 + i); // A-Z a-z Fractur - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdda0 + i); // A-Z a-z sans-serif - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xddd4 + i); // A-Z a-z sans bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xde08 + i); // A-Z a-z sans italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xde70 + i); // A-Z a-z monospace - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdd38 + i); // A-Z a-z double struck - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - const ch = letters.charAt(i); - wideChar = String.fromCharCode(0xd835, 0xdc9c + i); // A-Z a-z calligraphic - defineSymbol(math, mathord, ch, wideChar); - defineSymbol(text, textord, ch, wideChar); - } - - // Next, some wide character numerals - for (let i = 0; i < 10; i++) { - wideChar = String.fromCharCode(0xd835, 0xdfce + i); // 0-9 bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdfe2 + i); // 0-9 sans serif - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdfec + i); // 0-9 bold sans - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdff6 + i); // 0-9 monospace - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - } - - /* - * Neither Firefox nor Chrome support hard line breaks or soft line breaks. - * (Despite https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs) - * So Temml has work-arounds for both hard and soft breaks. - * The work-arounds sadly do not work simultaneously. Any top-level hard - * break makes soft line breaks impossible. - * - * Hard breaks are simulated by creating a and putting each line in its own . - * - * To create soft line breaks, Temml avoids using the and tags. - * Then the top level of a element can be occupied by elements, and the browser - * will break after a if the expression extends beyond the container limit. - * - * The default is for soft line breaks after each top-level binary or - * relational operator, per TeXbook p. 173. So we gather the expression into s so that - * each ends in a binary or relational operator. - * - * An option is for soft line breaks before an "=" sign. That changes the s. - * - * Soft line breaks will not work in Chromium and Safari, only Firefox. - * - * Hopefully browsers will someday do their own linebreaking and we will be able to delete - * much of this module. - */ - - function setLineBreaks(expression, wrapMode, isDisplayMode) { - const mtrs = []; - let mrows = []; - let block = []; - let numTopLevelEquals = 0; - let i = 0; - while (i < expression.length) { - while (expression[i] instanceof DocumentFragment) { - expression.splice(i, 1, ...expression[i].children); // Expand the fragment. - } - const node = expression[i]; - if (node.attributes && node.attributes.linebreak && - node.attributes.linebreak === "newline") { - // A hard line break. Create a for the current block. - if (block.length > 0) { - mrows.push(new MathNode("mrow", block)); - } - mrows.push(node); - block = []; - const mtd = new MathNode("mtd", mrows); - mtd.style.textAlign = "left"; - mtrs.push(new MathNode("mtr", [mtd])); - mrows = []; - i += 1; - continue - } - block.push(node); - if (node.type && node.type === "mo" && node.children.length === 1 && - !(node.attributes.form && node.attributes.form === "prefix") && // unary operators - !Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) { - const ch = node.children[0].text; - if (wrapMode === "=" && ch === "=") { - numTopLevelEquals += 1; - if (numTopLevelEquals > 1) { - block.pop(); - // Start a new block. (Insert a soft linebreak.) - const element = new MathNode("mrow", block); - mrows.push(element); - block = [node]; - } - } else if (wrapMode === "tex") { - // Check if the following node is a \nobreak text node, e.g. "~"" - const next = i < expression.length - 1 ? expression[i + 1] : null; - let glueIsFreeOfNobreak = true; - if ( - !( - next && - next.type === "mtext" && - next.attributes.linebreak && - next.attributes.linebreak === "nobreak" - ) - ) { - // We may need to start a new block. - // First, put any post-operator glue on same line as operator. - for (let j = i + 1; j < expression.length; j++) { - const nd = expression[j]; - if ( - nd.type && - nd.type === "mspace" && - !(nd.attributes.linebreak && nd.attributes.linebreak === "newline") - ) { - block.push(nd); - i += 1; - if ( - nd.attributes && - nd.attributes.linebreak && - nd.attributes.linebreak === "nobreak" - ) { - glueIsFreeOfNobreak = false; - } - } else { - break; - } - } - } - if (glueIsFreeOfNobreak) { - // Start a new block. (Insert a soft linebreak.) - const element = new MathNode("mrow", block); - mrows.push(element); - block = []; - } - } - } - i += 1; - } - if (block.length > 0) { - const element = new MathNode("mrow", block); - mrows.push(element); - } - if (mtrs.length > 0) { - const mtd = new MathNode("mtd", mrows); - mtd.style.textAlign = "left"; - const mtr = new MathNode("mtr", [mtd]); - mtrs.push(mtr); - const mtable = new MathNode("mtable", mtrs); - if (!isDisplayMode) { - mtable.setAttribute("columnalign", "left"); - mtable.setAttribute("rowspacing", "0em"); - } - return mtable - } - return newDocumentFragment(mrows); - } - - /** - * This file converts a parse tree into a corresponding MathML tree. The main - * entry point is the `buildMathML` function, which takes a parse tree from the - * parser. - */ - - - /** - * Takes a symbol and converts it into a MathML text node after performing - * optional replacement from symbols.js. - */ - const makeText = function(text, mode, style) { - if ( - symbols[mode][text] && - symbols[mode][text].replace && - text.charCodeAt(0) !== 0xd835 && - !( - Object.prototype.hasOwnProperty.call(ligatures, text) && - style && - ((style.fontFamily && style.fontFamily.slice(4, 6) === "tt") || - (style.font && style.font.slice(4, 6) === "tt")) - ) - ) { - text = symbols[mode][text].replace; - } - - return new TextNode(text); - }; - - const copyChar = (newRow, child) => { - if (newRow.children.length === 0 || - newRow.children[newRow.children.length - 1].type !== "mtext") { - const mtext = new MathNode( - "mtext", - [new TextNode(child.children[0].text)] - ); - newRow.children.push(mtext); - } else { - newRow.children[newRow.children.length - 1].children[0].text += child.children[0].text; - } - }; - - const consolidateText = mrow => { - // If possible, consolidate adjacent elements into a single element. - if (mrow.type !== "mrow" && mrow.type !== "mstyle") { return mrow } - if (mrow.children.length === 0) { return mrow } // empty group, e.g., \text{} - const newRow = new MathNode("mrow"); - for (let i = 0; i < mrow.children.length; i++) { - const child = mrow.children[i]; - if (child.type === "mtext" && Object.keys(child.attributes).length === 0) { - copyChar(newRow, child); - } else if (child.type === "mrow") { - // We'll also check the children of an mrow. One level only. No recursion. - let canConsolidate = true; - for (let j = 0; j < child.children.length; j++) { - const grandChild = child.children[j]; - if (grandChild.type !== "mtext" || Object.keys(child.attributes).length !== 0) { - canConsolidate = false; - break - } - } - if (canConsolidate) { - for (let j = 0; j < child.children.length; j++) { - const grandChild = child.children[j]; - copyChar(newRow, grandChild); - } - } else { - newRow.children.push(child); - } - } else { - newRow.children.push(child); - } - } - for (let i = 0; i < newRow.children.length; i++) { - if (newRow.children[i].type === "mtext") { - const mtext = newRow.children[i]; - // Firefox does not render a space at either end of an string. - // To get proper rendering, we replace leading or trailing spaces with no-break spaces. - if (mtext.children[0].text.charAt(0) === " ") { - mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1); - } - const L = mtext.children[0].text.length; - if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") { - mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0"; - } - for (const [key, value] of Object.entries(mrow.attributes)) { - mtext.attributes[key] = value; - } - } - } - if (newRow.children.length === 1 && newRow.children[0].type === "mtext") { - return newRow.children[0]; // A consolidated - } else { - return newRow - } - }; - - /** - * Wrap the given array of nodes in an node if needed, i.e., - * unless the array has length 1. Always returns a single node. - */ - const makeRow = function(body, semisimple = false) { - if (body.length === 1 && !(body[0] instanceof DocumentFragment)) { - return body[0]; - } else if (!semisimple) { - // Suppress spacing on nodes at both ends of the row. - if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) { - body[0].attributes.lspace = "0em"; - body[0].attributes.rspace = "0em"; - } - const end = body.length - 1; - if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) { - body[end].attributes.lspace = "0em"; - body[end].attributes.rspace = "0em"; - } - } - return new MathNode("mrow", body); - }; - - /** - * Check for . which is how a dot renders in MathML, - * or , - * which is how a braced comma {,} renders in MathML - */ - function isNumberPunctuation(group) { - if (!group) { - return false - } - if (group.type === 'mi' && group.children.length === 1) { - const child = group.children[0]; - return child instanceof TextNode && child.text === '.' - } else if (group.type === "mtext" && group.children.length === 1) { - const child = group.children[0]; - return child instanceof TextNode && child.text === '\u2008' // punctuation space - } else if (group.type === 'mo' && group.children.length === 1 && - group.getAttribute('separator') === 'true' && - group.getAttribute('lspace') === '0em' && - group.getAttribute('rspace') === '0em') { - const child = group.children[0]; - return child instanceof TextNode && child.text === ',' - } else { - return false - } - } - const isComma = (expression, i) => { - const node = expression[i]; - const followingNode = expression[i + 1]; - return (node.type === "atom" && node.text === ",") && - // Don't consolidate if there is a space after the comma. - node.loc && followingNode.loc && node.loc.end === followingNode.loc.start - }; - - const isRel = item => { - return (item.type === "atom" && item.family === "rel") || - (item.type === "mclass" && item.mclass === "mrel") - }; - - /** - * Takes a list of nodes, builds them, and returns a list of the generated - * MathML nodes. Also do a couple chores along the way: - * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}. - * (2) Suppress spacing between two adjacent relations. - */ - const buildExpression = function(expression, style, semisimple = false) { - if (!semisimple && expression.length === 1) { - const group = buildGroup$1(expression[0], style); - if (group instanceof MathNode && group.type === "mo") { - // When TeX writers want to suppress spacing on an operator, - // they often put the operator by itself inside braces. - group.setAttribute("lspace", "0em"); - group.setAttribute("rspace", "0em"); - } - return [group]; - } - - const groups = []; - const groupArray = []; - let lastGroup; - for (let i = 0; i < expression.length; i++) { - groupArray.push(buildGroup$1(expression[i], style)); - } - - for (let i = 0; i < groupArray.length; i++) { - const group = groupArray[i]; - - // Suppress spacing between adjacent relations - if (i < expression.length - 1 && isRel(expression[i]) && isRel(expression[i + 1])) { - group.setAttribute("rspace", "0em"); - } - if (i > 0 && isRel(expression[i]) && isRel(expression[i - 1])) { - group.setAttribute("lspace", "0em"); - } - - // Concatenate numbers - if (group.type === 'mn' && lastGroup && lastGroup.type === 'mn') { - // Concatenate ... followed by . - lastGroup.children.push(...group.children); - continue - } else if (isNumberPunctuation(group) && lastGroup && lastGroup.type === 'mn') { - // Concatenate ... followed by . - lastGroup.children.push(...group.children); - continue - } else if (lastGroup && lastGroup.type === "mn" && i < groupArray.length - 1 && - groupArray[i + 1].type === "mn" && isComma(expression, i)) { - lastGroup.children.push(...group.children); - continue - } else if (group.type === 'mn' && isNumberPunctuation(lastGroup)) { - // Concatenate . followed by ... - group.children = [...lastGroup.children, ...group.children]; - groups.pop(); - } else if ((group.type === 'msup' || group.type === 'msub') && - group.children.length >= 1 && lastGroup && - (lastGroup.type === 'mn' || isNumberPunctuation(lastGroup))) { - // Put preceding ... or . inside base of - // ...base......exponent... (or ) - const base = group.children[0]; - if (base instanceof MathNode && base.type === 'mn' && lastGroup) { - base.children = [...lastGroup.children, ...base.children]; - groups.pop(); - } - } - groups.push(group); - lastGroup = group; - } - return groups - }; - - /** - * Equivalent to buildExpression, but wraps the elements in an - * if there's more than one. Returns a single node instead of an array. - */ - const buildExpressionRow = function(expression, style, semisimple = false) { - return makeRow(buildExpression(expression, style, semisimple), semisimple); - }; - - /** - * Takes a group from the parser and calls the appropriate groupBuilders function - * on it to produce a MathML node. - */ - const buildGroup$1 = function(group, style) { - if (!group) { - return new MathNode("mrow"); - } - - if (_mathmlGroupBuilders[group.type]) { - // Call the groupBuilders function - const result = _mathmlGroupBuilders[group.type](group, style); - return result; - } else { - throw new ParseError("Got group of unknown type: '" + group.type + "'"); - } - }; - - const glue$1 = _ => { - return new MathNode("mtd", [], [], { padding: "0", width: "50%" }) - }; - - const labelContainers = ["mrow", "mtd", "mtable", "mtr"]; - const getLabel = parent => { - for (const node of parent.children) { - if (node.type && labelContainers.includes(node.type)) { - if (node.classes && node.classes[0] === "tml-label") { - const label = node.label; - return label - } else { - const label = getLabel(node); - if (label) { return label } - } - } else if (!node.type) { - const label = getLabel(node); - if (label) { return label } - } - } - }; - - const taggedExpression = (expression, tag, style, leqno) => { - tag = buildExpressionRow(tag[0].body, style); - tag = consolidateText(tag); // tag is now an element - tag.classes.push("tml-tag"); // to be available for \ref - - const label = getLabel(expression); // from a \label{} function. - expression = new MathNode("mtd", [expression]); - const rowArray = [glue$1(), expression, glue$1()]; - rowArray[leqno ? 0 : 2].children.push(tag); - const mtr = new MathNode("mtr", rowArray, ["tml-tageqn"]); - if (label) { mtr.setAttribute("id", label); } - const table = new MathNode("mtable", [mtr]); - table.style.width = "100%"; - table.setAttribute("displaystyle", "true"); - return table - }; - - /** - * Takes a full parse tree and settings and builds a MathML representation of - * it. - */ - function buildMathML(tree, texExpression, style, settings) { - // Strip off outer tag wrapper for processing below. - let tag = null; - if (tree.length === 1 && tree[0].type === "tag") { - tag = tree[0].tag; - tree = tree[0].body; - } - - const expression = buildExpression(tree, style); - - if (expression.length === 1 && expression[0] instanceof AnchorNode) { - return expression[0] - } - - const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap; - - const n1 = expression.length === 0 ? null : expression[0]; - let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode) - ? expression[0] - : setLineBreaks(expression, wrap, settings.displayMode); - - if (tag) { - wrapper = taggedExpression(wrapper, tag, style, settings.leqno); - } - - if (settings.annotate) { - // Build a TeX annotation of the source - const annotation = new MathNode( - "annotation", [new TextNode(texExpression)]); - annotation.setAttribute("encoding", "application/x-tex"); - wrapper = new MathNode("semantics", [wrapper, annotation]); - } - - const math = new MathNode("math", [wrapper]); - - if (settings.xml) { - math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML"); - } - if (settings.displayMode) { - math.setAttribute("display", "block"); - math.style.display = "block math"; // necessary in Chromium. - // Firefox and Safari do not recognize display: "block math". - // Set a class so that the CSS file can set display: block. - math.classes = ["tml-display"]; - } - return math; - } - - // From the KaTeX font metrics, identify letters whose accents need a italic correction. - const smallNudge = "DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ"; - const mediumNudge = "BCEGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ"; - const largeNudge = "AFJdfΔΛ"; - - const mathmlBuilder$a = (group, style) => { - const accentNode$1 = group.isStretchy - ? accentNode(group) - : new MathNode("mo", [makeText(group.label, group.mode)]); - if (!group.isStretchy) { - accentNode$1.setAttribute("stretchy", "false"); // Keep Firefox from stretching \check - } - if (group.label !== "\\vec") { - accentNode$1.style.mathDepth = "0"; // not scriptstyle - // Don't use attribute accent="true" because MathML Core eliminates a needed space. - } - const tag = group.label === "\\c" ? "munder" : "mover"; - const needsWbkVertShift = needsWebkitVerticalShift.has(group.label); - if (tag === "mover" && group.mode === "math" && (!group.isStretchy) && group.base.text - && group.base.text.length === 1) { - const text = group.base.text; - const isVec = group.label === "\\vec"; - const vecPostfix = isVec === "\\vec" ? "-vec" : ""; - if (isVec) { - accentNode$1.classes.push("tml-vec"); // Firefox sizing of \vec arrow - } - const wbkPostfix = isVec ? "-vec" : needsWbkVertShift ? "-acc" : ""; - if (smallNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-sml${vecPostfix}`); - accentNode$1.classes.push(`wbk-sml${wbkPostfix}`); - } else if (mediumNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-med${vecPostfix}`); - accentNode$1.classes.push(`wbk-med${wbkPostfix}`); - } else if (largeNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-lrg${vecPostfix}`); - accentNode$1.classes.push(`wbk-lrg${wbkPostfix}`); - } else if (isVec) { - accentNode$1.classes.push(`wbk-vec`); - } else if (needsWbkVertShift) { - accentNode$1.classes.push(`wbk-acc`); - } - } else if (needsWbkVertShift) { - // text-mode accents - accentNode$1.classes.push("wbk-acc"); - } - const node = new MathNode(tag, [buildGroup$1(group.base, style), accentNode$1]); - return node; - }; - - const nonStretchyAccents = new Set([ - "\\acute", - "\\check", - "\\grave", - "\\ddot", - "\\dddot", - "\\ddddot", - "\\tilde", - "\\bar", - "\\breve", - "\\check", - "\\hat", - "\\vec", - "\\dot", - "\\mathring" - ]); - - const needsWebkitVerticalShift = new Set([ - "\\acute", - "\\bar", - "\\breve", - "\\check", - "\\dot", - "\\ddot", - "\\grave", - "\\hat", - "\\mathring", - "\\`", "\\'", "\\^", "\\=", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v" - ]); - - const combiningChar = { - "\\`": "\u0300", - "\\'": "\u0301", - "\\^": "\u0302", - "\\~": "\u0303", - "\\=": "\u0304", - "\\u": "\u0306", - "\\.": "\u0307", - '\\"': "\u0308", - "\\r": "\u030A", - "\\H": "\u030B", - "\\v": "\u030C", - "\\c": "\u0327" - }; - - // Accents - defineFunction({ - type: "accent", - names: [ - "\\acute", - "\\grave", - "\\ddot", - "\\dddot", - "\\ddddot", - "\\tilde", - "\\bar", - "\\breve", - "\\check", - "\\hat", - "\\vec", - "\\dot", - "\\mathring", - "\\overparen", - "\\widecheck", - "\\widehat", - "\\wideparen", - "\\widetilde", - "\\overrightarrow", - "\\overleftarrow", - "\\Overrightarrow", - "\\overleftrightarrow", - "\\overgroup", - "\\overleftharpoon", - "\\overrightharpoon" - ], - props: { - numArgs: 1 - }, - handler: (context, args) => { - const base = normalizeArgument(args[0]); - - const isStretchy = !nonStretchyAccents.has(context.funcName); - - return { - type: "accent", - mode: context.parser.mode, - label: context.funcName, - isStretchy, - base - }; - }, - mathmlBuilder: mathmlBuilder$a - }); - - // Text-mode accents - defineFunction({ - type: "accent", - names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\c", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"], - props: { - numArgs: 1, - allowedInText: true, - allowedInMath: true, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const base = normalizeArgument(args[0]); - const mode = context.parser.mode; - - if (mode === "math" && context.parser.settings.strict) { - // LaTeX only writes a warning. It doesn't stop. We'll issue the same warning. - // eslint-disable-next-line no-console - console.log(`Temml parse error: Command ${context.funcName} is invalid in math mode.`); - } - - if (mode === "text" && base.text && base.text.length === 1 - && context.funcName in combiningChar && smalls.indexOf(base.text) > -1) { - // Return a combining accent character - return { - type: "textord", - mode: "text", - text: base.text + combiningChar[context.funcName] - } - } else if (context.funcName === "\\c" && mode === "text" && base.text - && base.text.length === 1) { - // combining cedilla - return { type: "textord", mode: "text", text: base.text + "\u0327" } - } else { - // Build up the accent - return { - type: "accent", - mode, - label: context.funcName, - isStretchy: false, - base - } - } - }, - mathmlBuilder: mathmlBuilder$a - }); - - defineFunction({ - type: "accentUnder", - names: [ - "\\underleftarrow", - "\\underrightarrow", - "\\underleftrightarrow", - "\\undergroup", - "\\underparen", - "\\utilde" - ], - props: { - numArgs: 1 - }, - handler: ({ parser, funcName }, args) => { - const base = args[0]; - return { - type: "accentUnder", - mode: parser.mode, - label: funcName, - base: base - }; - }, - mathmlBuilder: (group, style) => { - const accentNode$1 = accentNode(group); - accentNode$1.style["math-depth"] = 0; - const node = new MathNode("munder", [ - buildGroup$1(group.base, style), - accentNode$1 - ]); - return node; - } - }); - - /** - * This file does conversion between units. In particular, it provides - * calculateSize to convert other units into CSS units. - */ - - - const ptPerUnit = { - // Convert to CSS (Postscipt) points, not TeX points - // https://en.wikibooks.org/wiki/LaTeX/Lengths and - // https://tex.stackexchange.com/a/8263 - pt: 800 / 803, // convert TeX point to CSS (Postscript) point - pc: (12 * 800) / 803, // pica - dd: ((1238 / 1157) * 800) / 803, // didot - cc: ((14856 / 1157) * 800) / 803, // cicero (12 didot) - nd: ((685 / 642) * 800) / 803, // new didot - nc: ((1370 / 107) * 800) / 803, // new cicero (12 new didot) - sp: ((1 / 65536) * 800) / 803, // scaled point (TeX's internal smallest unit) - mm: (25.4 / 72), - cm: (2.54 / 72), - in: (1 / 72), - px: (96 / 72) - }; - - /** - * Determine whether the specified unit (either a string defining the unit - * or a "size" parse node containing a unit field) is valid. - */ - const validUnits = [ - "em", - "ex", - "mu", - "pt", - "mm", - "cm", - "in", - "px", - "bp", - "pc", - "dd", - "cc", - "nd", - "nc", - "sp" - ]; - - const validUnit = function(unit) { - if (typeof unit !== "string") { - unit = unit.unit; - } - return validUnits.indexOf(unit) > -1 - }; - - const emScale = styleLevel => { - const scriptLevel = Math.max(styleLevel - 1, 0); - return [1, 0.7, 0.5][scriptLevel] - }; - - /* - * Convert a "size" parse node (with numeric "number" and string "unit" fields, - * as parsed by functions.js argType "size") into a CSS value. - */ - const calculateSize = function(sizeValue, style) { - let number = sizeValue.number; - if (style.maxSize[0] < 0 && number > 0) { - return { number: 0, unit: "em" } - } - const unit = sizeValue.unit; - switch (unit) { - case "mm": - case "cm": - case "in": - case "px": { - const numInCssPts = number * ptPerUnit[unit]; - if (numInCssPts > style.maxSize[1]) { - return { number: style.maxSize[1], unit: "pt" } - } - return { number, unit }; // absolute CSS units. - } - case "em": - case "ex": { - // In TeX, em and ex do not change size in \scriptstyle. - if (unit === "ex") { number *= 0.431; } - number = Math.min(number / emScale(style.level), style.maxSize[0]); - return { number: round(number), unit: "em" }; - } - case "bp": { - if (number > style.maxSize[1]) { number = style.maxSize[1]; } - return { number, unit: "pt" }; // TeX bp is a CSS pt. (1/72 inch). - } - case "pt": - case "pc": - case "dd": - case "cc": - case "nd": - case "nc": - case "sp": { - number = Math.min(number * ptPerUnit[unit], style.maxSize[1]); - return { number: round(number), unit: "pt" } - } - case "mu": { - number = Math.min(number / 18, style.maxSize[0]); - return { number: round(number), unit: "em" } - } - default: - throw new ParseError("Invalid unit: '" + unit + "'") - } - }; - - // Helper functions - - const padding = width => { - const node = new MathNode("mspace"); - node.setAttribute("width", width + "em"); - return node - }; - - const paddedNode = (group, lspace = 0.3, rspace = 0, mustSmash = false) => { - if (group == null && rspace === 0) { return padding(lspace) } - const row = group ? [group] : []; - if (lspace !== 0) { row.unshift(padding(lspace)); } - if (rspace > 0) { row.push(padding(rspace)); } - if (mustSmash) { - // Used for the bottom arrow in a {CD} environment - const mpadded = new MathNode("mpadded", row); - mpadded.setAttribute("height", "0.1px"); // Don't use 0. WebKit would hide it. - return mpadded - } else { - return new MathNode("mrow", row) - } - }; - - const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel); - - const munderoverNode = (fName, body, below, style) => { - const arrowNode = mathMLnode(fName); - // Is this the short part of a mhchem equilibrium arrow? - const isEq = fName.slice(1, 3) === "eq"; - const minWidth = fName.charAt(1) === "x" - ? "1.75" // mathtools extensible arrows are ≥ 1.75em long - : fName.slice(2, 4) === "cd" - ? "3.0" // cd package arrows - : isEq - ? "1.0" // The shorter harpoon of a mhchem equilibrium arrow - : "2.0"; // other mhchem arrows - // TODO: When Firefox supports minsize, use the next line. - //arrowNode.setAttribute("minsize", String(minWidth) + "em") - arrowNode.setAttribute("lspace", "0"); - arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0")); - - // upper and lower labels are set to scriptlevel by MathML - // So we have to adjust our label dimensions accordingly. - const labelStyle = style.withLevel(style.level < 2 ? 2 : 3); - const minArrowWidth = labelSize(minWidth, labelStyle.level); - // The dummyNode will be inside a inside a - // So it will be at scriptlevel 3 - const dummyWidth = labelSize(minWidth, 3); - const emptyLabel = paddedNode(null, minArrowWidth.toFixed(4), 0); - const dummyNode = paddedNode(null, dummyWidth.toFixed(4), 0); - // The arrow is a little longer than the label. Set a spacer length. - const space = labelSize((isEq ? 0 : 0.3), labelStyle.level).toFixed(4); - let upperNode; - let lowerNode; - - const gotUpper = (body && body.body && - // \hphantom visible content - (body.body.body || body.body.length > 0)); - if (gotUpper) { - let label = buildGroup$1(body, labelStyle); - const mustSmash = (fName === "\\\\cdrightarrow" || fName === "\\\\cdleftarrow"); - label = paddedNode(label, space, space, mustSmash); - // Since Firefox does not support minsize, stack a invisible node - // on top of the label. Its width will serve as a min-width. - // TODO: Refactor this after Firefox supports minsize. - upperNode = new MathNode("mover", [label, dummyNode]); - } - const gotLower = (below && below.body && - (below.body.body || below.body.length > 0)); - if (gotLower) { - let label = buildGroup$1(below, labelStyle); - label = paddedNode(label, space, space); - lowerNode = new MathNode("munder", [label, dummyNode]); - } - - let node; - if (!gotUpper && !gotLower) { - node = new MathNode("mover", [arrowNode, emptyLabel]); - } else if (gotUpper && gotLower) { - node = new MathNode("munderover", [arrowNode, lowerNode, upperNode]); - } else if (gotUpper) { - node = new MathNode("mover", [arrowNode, upperNode]); - } else { - node = new MathNode("munder", [arrowNode, lowerNode]); - } - if (minWidth === "3.0") { node.style.height = "1em"; } // CD environment - node.setAttribute("accent", "false"); // Necessary for MS Word - return node - }; - - // Stretchy arrows with an optional argument - defineFunction({ - type: "xArrow", - names: [ - "\\xleftarrow", - "\\xrightarrow", - "\\xLeftarrow", - "\\xRightarrow", - "\\xleftrightarrow", - "\\xLeftrightarrow", - "\\xhookleftarrow", - "\\xhookrightarrow", - "\\xmapsto", - "\\xrightharpoondown", - "\\xrightharpoonup", - "\\xleftharpoondown", - "\\xleftharpoonup", - "\\xlongequal", - "\\xtwoheadrightarrow", - "\\xtwoheadleftarrow", - "\\xtofrom", // expfeil - "\\xleftrightharpoons", // mathtools - "\\xrightleftharpoons", // mathtools - // The next 7 functions are here only to support mhchem - "\\yields", - "\\yieldsLeft", - "\\mesomerism", - "\\longrightharpoonup", - "\\longleftharpoondown", - "\\yieldsLeftRight", - "\\chemequilibrium", - // The next 3 functions are here only to support the {CD} environment. - "\\\\cdrightarrow", - "\\\\cdleftarrow", - "\\\\cdlongequal" - ], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser, funcName }, args, optArgs) { - return { - type: "xArrow", - mode: parser.mode, - name: funcName, - body: args[0], - below: optArgs[0] - }; - }, - mathmlBuilder(group, style) { - // Build the arrow and its labels. - const node = munderoverNode(group.name, group.body, group.below, style); - // Create operator spacing for a relation. - const row = [node]; - row.unshift(padding(0.2778)); - row.push(padding(0.2778)); - return new MathNode("mrow", row) - } - }); - - const arrowComponent = { - "\\equilibriumRight": ["\\longrightharpoonup", "\\eqleftharpoondown"], - "\\equilibriumLeft": ["\\eqrightharpoonup", "\\longleftharpoondown"] - }; - - // Math fonts do not have a single glyph for these two mhchem functions. - // So we stack a pair of single harpoons. - defineFunction({ - type: "stackedArrow", - names: [ - "\\equilibriumRight", - "\\equilibriumLeft" - ], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser, funcName }, args, optArgs) { - const lowerArrowBody = args[0] - ? { - type: "hphantom", - mode: parser.mode, - body: args[0] - } - : null; - const upperArrowBelow = optArgs[0] - ? { - type: "hphantom", - mode: parser.mode, - body: optArgs[0] - } - : null; - return { - type: "stackedArrow", - mode: parser.mode, - name: funcName, - body: args[0], - upperArrowBelow, - lowerArrowBody, - below: optArgs[0] - }; - }, - mathmlBuilder(group, style) { - const topLabel = arrowComponent[group.name][0]; - const botLabel = arrowComponent[group.name][1]; - const topArrow = munderoverNode(topLabel, group.body, group.upperArrowBelow, style); - const botArrow = munderoverNode(botLabel, group.lowerArrowBody, group.below, style); - let wrapper; - - const raiseNode = new MathNode("mpadded", [topArrow]); - raiseNode.setAttribute("voffset", "0.3em"); - raiseNode.setAttribute("height", "+0.3em"); - raiseNode.setAttribute("depth", "-0.3em"); - // One of the arrows is given ~zero width. so the other has the same horzontal alignment. - if (group.name === "\\equilibriumLeft") { - const botNode = new MathNode("mpadded", [botArrow]); - botNode.setAttribute("width", "0.5em"); - wrapper = new MathNode( - "mpadded", - [padding(0.2778), botNode, raiseNode, padding(0.2778)] - ); - } else { - raiseNode.setAttribute("width", (group.name === "\\equilibriumRight" ? "0.5em" : "0")); - wrapper = new MathNode( - "mpadded", - [padding(0.2778), raiseNode, botArrow, padding(0.2778)] - ); - } - - wrapper.setAttribute("voffset", "-0.18em"); - wrapper.setAttribute("height", "-0.18em"); - wrapper.setAttribute("depth", "+0.18em"); - return wrapper - } - }); - - /** - * All registered environments. - * `environments.js` exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary via `environments.js`. - */ - const _environments = {}; - - function defineEnvironment({ type, names, props, handler, mathmlBuilder }) { - // Set default values of environments. - const data = { - type, - numArgs: props.numArgs || 0, - allowedInText: false, - numOptionalArgs: 0, - handler - }; - for (let i = 0; i < names.length; ++i) { - _environments[names[i]] = data; - } - if (mathmlBuilder) { - _mathmlGroupBuilders[type] = mathmlBuilder; - } - } - - /** - * Asserts that the node is of the given type and returns it with stricter - * typing. Throws if the node's type does not match. - */ - function assertNodeType(node, type) { - if (!node || node.type !== type) { - throw new Error( - `Expected node of type ${type}, but got ` + - (node ? `node of type ${node.type}` : String(node)) - ); - } - return node; - } - - /** - * Returns the node more strictly typed iff it is of the given type. Otherwise, - * returns null. - */ - function assertSymbolNodeType(node) { - const typedNode = checkSymbolNodeType(node); - if (!typedNode) { - throw new Error( - `Expected node of symbol group type, but got ` + - (node ? `node of type ${node.type}` : String(node)) - ); - } - return typedNode; - } - - /** - * Returns the node more strictly typed iff it is of the given type. Otherwise, - * returns null. - */ - function checkSymbolNodeType(node) { - if (node && (node.type === "atom" || node.type === "delimiter" || - Object.prototype.hasOwnProperty.call(NON_ATOMS, node.type))) { - return node; - } - return null; - } - - const cdArrowFunctionName = { - ">": "\\\\cdrightarrow", - "<": "\\\\cdleftarrow", - "=": "\\\\cdlongequal", - A: "\\uparrow", - V: "\\downarrow", - "|": "\\Vert", - ".": "no arrow" - }; - - const newCell = () => { - // Create an empty cell, to be filled below with parse nodes. - return { type: "styling", body: [], mode: "math", scriptLevel: "display" }; - }; - - const isStartOfArrow = (node) => { - return node.type === "textord" && node.text === "@"; - }; - - const isLabelEnd = (node, endChar) => { - return (node.type === "mathord" || node.type === "atom") && node.text === endChar; - }; - - function cdArrow(arrowChar, labels, parser) { - // Return a parse tree of an arrow and its labels. - // This acts in a way similar to a macro expansion. - const funcName = cdArrowFunctionName[arrowChar]; - switch (funcName) { - case "\\\\cdrightarrow": - case "\\\\cdleftarrow": - return parser.callFunction(funcName, [labels[0]], [labels[1]]); - case "\\uparrow": - case "\\downarrow": { - const leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []); - const bareArrow = { - type: "atom", - text: funcName, - mode: "math", - family: "rel" - }; - const sizedArrow = parser.callFunction("\\Big", [bareArrow], []); - const rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []); - const arrowGroup = { - type: "ordgroup", - mode: "math", - body: [leftLabel, sizedArrow, rightLabel], - semisimple: true - }; - return parser.callFunction("\\\\cdparent", [arrowGroup], []); - } - case "\\\\cdlongequal": - return parser.callFunction("\\\\cdlongequal", [], []); - case "\\Vert": { - const arrow = { type: "textord", text: "\\Vert", mode: "math" }; - return parser.callFunction("\\Big", [arrow], []); - } - default: - return { type: "textord", text: " ", mode: "math" }; - } - } - - function parseCD(parser) { - // Get the array's parse nodes with \\ temporarily mapped to \cr. - const parsedRows = []; - parser.gullet.beginGroup(); - parser.gullet.macros.set("\\cr", "\\\\\\relax"); - parser.gullet.beginGroup(); - while (true) { - // Get the parse nodes for the next row. - parsedRows.push(parser.parseExpression(false, "\\\\")); - parser.gullet.endGroup(); - parser.gullet.beginGroup(); - const next = parser.fetch().text; - if (next === "&" || next === "\\\\") { - parser.consume(); - } else if (next === "\\end") { - if (parsedRows[parsedRows.length - 1].length === 0) { - parsedRows.pop(); // final row ended in \\ - } - break; - } else { - throw new ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken); - } - } - - let row = []; - const body = [row]; - - // Loop thru the parse nodes. Collect them into cells and arrows. - for (let i = 0; i < parsedRows.length; i++) { - // Start a new row. - const rowNodes = parsedRows[i]; - // Create the first cell. - let cell = newCell(); - - for (let j = 0; j < rowNodes.length; j++) { - if (!isStartOfArrow(rowNodes[j])) { - // If a parseNode is not an arrow, it goes into a cell. - cell.body.push(rowNodes[j]); - } else { - // Parse node j is an "@", the start of an arrow. - // Before starting on the arrow, push the cell into `row`. - row.push(cell); - - // Now collect parseNodes into an arrow. - // The character after "@" defines the arrow type. - j += 1; - const arrowChar = assertSymbolNodeType(rowNodes[j]).text; - - // Create two empty label nodes. We may or may not use them. - const labels = new Array(2); - labels[0] = { type: "ordgroup", mode: "math", body: [] }; - labels[1] = { type: "ordgroup", mode: "math", body: [] }; - - // Process the arrow. - if ("=|.".indexOf(arrowChar) > -1) ; else if ("<>AV".indexOf(arrowChar) > -1) { - // Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take - // two optional labels. E.g. the right-point arrow syntax is - // really: @>{optional label}>{optional label}> - // Collect parseNodes into labels. - for (let labelNum = 0; labelNum < 2; labelNum++) { - let inLabel = true; - for (let k = j + 1; k < rowNodes.length; k++) { - if (isLabelEnd(rowNodes[k], arrowChar)) { - inLabel = false; - j = k; - break; - } - if (isStartOfArrow(rowNodes[k])) { - throw new ParseError( - "Missing a " + arrowChar + " character to complete a CD arrow.", - rowNodes[k] - ); - } - - labels[labelNum].body.push(rowNodes[k]); - } - if (inLabel) { - // isLabelEnd never returned a true. - throw new ParseError( - "Missing a " + arrowChar + " character to complete a CD arrow.", - rowNodes[j] - ); - } - } - } else { - throw new ParseError(`Expected one of "<>AV=|." after @.`); - } - - // Now join the arrow to its labels. - const arrow = cdArrow(arrowChar, labels, parser); - - // Wrap the arrow in a styling node - row.push(arrow); - // In CD's syntax, cells are implicit. That is, everything that - // is not an arrow gets collected into a cell. So create an empty - // cell now. It will collect upcoming parseNodes. - cell = newCell(); - } - } - if (i % 2 === 0) { - // Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell - // The last cell is not yet pushed into `row`, so: - row.push(cell); - } else { - // Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow - // Remove the empty cell that was placed at the beginning of `row`. - row.shift(); - } - row = []; - body.push(row); - } - body.pop(); - - // End row group - parser.gullet.endGroup(); - // End array group defining \\ - parser.gullet.endGroup(); - - return { - type: "array", - mode: "math", - body, - tags: null, - labels: new Array(body.length + 1).fill(""), - envClasses: ["jot", "cd"], - cols: [], - hLinesBeforeRow: new Array(body.length + 1).fill([]) - }; - } - - // The functions below are not available for general use. - // They are here only for internal use by the {CD} environment in placing labels - // next to vertical arrows. - - // We don't need any such functions for horizontal arrows because we can reuse - // the functionality that already exists for extensible arrows. - - defineFunction({ - type: "cdlabel", - names: ["\\\\cdleft", "\\\\cdright"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - return { - type: "cdlabel", - mode: parser.mode, - side: funcName.slice(4), - label: args[0] - }; - }, - mathmlBuilder(group, style) { - if (group.label.body.length === 0) { - return new MathNode("mrow", style) // empty label - } - // Abuse an to create vertically centered content. - const mrow = buildGroup$1(group.label, style); - if (group.side === "left") { - mrow.classes.push("tml-shift-left"); - } - const mtd = new MathNode("mtd", [mrow]); - mtd.style.padding = "0"; - const mtr = new MathNode("mtr", [mtd]); - const mtable = new MathNode("mtable", [mtr]); - const label = new MathNode("mpadded", [mtable]); - // Set the label width to zero so that the arrow will be centered under the corner cell. - label.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - label.setAttribute("displaystyle", "false"); - label.setAttribute("scriptlevel", "1"); - return label; - } - }); - - defineFunction({ - type: "cdlabelparent", - names: ["\\\\cdparent"], - props: { - numArgs: 1 - }, - handler({ parser }, args) { - return { - type: "cdlabelparent", - mode: parser.mode, - fragment: args[0] - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow", [buildGroup$1(group.fragment, style)]); - } - }); - - const ordGroup = (body) => { - return { - "type": "ordgroup", - "mode": "math", - "body": body, - "semisimple": true - } - }; - - const phantom = (body, type) => { - return { - "type": type, - "mode": "math", - "body": ordGroup(body) - } - }; - - /* - * A helper for \bordermatrix. - * parseArray() has parsed the tokens as if the environment - * was \begin{matrix}. That parse tree is this function’s input. - * Here, we rearrange the parse tree to get one that will - * result in TeX \bordermatrix. - * The final result includes a {pmatrix}, which is the bottom - * half of a element. The top of the contains - * the \bordermatrix headings. The top section also contains the - * contents of the bottom {pmatrix}. Those elements are hidden via - * \hphantom, but they ensure that column widths are the same top and - * bottom. - * - * We also create a left {matrix} with a single column that contains - * elements shifted out of the matrix. The left {matrix} also - * contains \vphantom copies of the other {pmatrix} elements. - * As before, this ensures consistent row heights of left and main. - */ - - const bordermatrixParseTree = (matrix, delimiters) => { - const body = matrix.body; - body[0].shift(); // dispose of top left cell - - // Create an array for the left column - const leftColumnBody = new Array(body.length - 1).fill().map(() => []); - for (let i = 1; i < body.length; i++) { - // The visible part of the cell - leftColumnBody[i - 1].push(body[i].shift()); - // A vphantom with contents from the pmatrix, to set minimum cell height - const phantomBody = []; - for (let j = 0; j < body[i].length; j++) { - phantomBody.push(body[i][j]); - } - leftColumnBody[i - 1].push(phantom(phantomBody, "vphantom")); - } - - // Create an array for the top row - const topRowBody = new Array(body.length).fill().map(() => []); - for (let j = 0; j < body[0].length; j++) { - topRowBody[0].push(body[0][j]); - } - // Copy the rest of the pmatrix, but squashed via \hphantom - for (let i = 1; i < body.length; i++) { - for (let j = 0; j < body[0].length; j++) { - topRowBody[i].push(phantom(body[i][j].body, "hphantom")); - } - } - - // Squash the top row of the main {pmatrix} - for (let j = 0; j < body[0].length; j++) { - body[0][j] = phantom(body[0][j].body, "hphantom"); - } - - // Now wrap the arrays in the proper parse nodes. - - const leftColumn = { - type: "array", - mode: "math", - body: leftColumnBody, - cols: [{ type: "align", align: "c" }], - rowGaps: new Array(leftColumnBody.length - 1).fill(null), - hLinesBeforeRow: new Array(leftColumnBody.length + 1).fill().map(() => []), - envClasses: [], - scriptLevel: "text", - arraystretch: 1, - labels: new Array(leftColumnBody.length).fill(""), - arraycolsep: { "number": 0.04, unit: "em" } - }; - - const topRow = { - type: "array", - mode: "math", - body: topRowBody, - cols: new Array(topRowBody.length).fill({ type: "align", align: "c" }), - rowGaps: new Array(topRowBody.length - 1).fill(null), - hLinesBeforeRow: new Array(topRowBody.length + 1).fill().map(() => []), - envClasses: [], - scriptLevel: "text", - arraystretch: 1, - labels: new Array(topRowBody.length).fill(""), - arraycolsep: null - }; - - const topWrapper = { - type: "styling", - mode: "math", - scriptLevel: "text", // Must set this explicitly. - body: [topRow] // Default level is "script". - }; - - const container = { - type: "leftright", - mode: "math", - body: [matrix], - left: delimiters ? delimiters[0] : "(", - right: delimiters ? delimiters[1] : ")", - rightColor: undefined - }; - - const base = { - type: "op", // The base of a TeX \overset - mode: "math", - limits: true, - alwaysHandleSupSub: true, - parentIsSupSub: true, - symbol: false, - suppressBaseShift: true, - body: [container] - }; - - const mover = { - type: "supsub", // We're using the MathML equivalent - mode: "math", // of TeX \overset. - stack: true, - base: base, // That keeps the {pmatrix} aligned with - sup: topWrapper, // the math centerline. - sub: null - }; - - return ordGroup([leftColumn, mover]) - }; - - /** - * Lexing or parsing positional information for error reporting. - * This object is immutable. - */ - class SourceLocation { - constructor(lexer, start, end) { - this.lexer = lexer; // Lexer holding the input string. - this.start = start; // Start offset, zero-based inclusive. - this.end = end; // End offset, zero-based exclusive. - } - - /** - * Merges two `SourceLocation`s from location providers, given they are - * provided in order of appearance. - * - Returns the first one's location if only the first is provided. - * - Returns a merged range of the first and the last if both are provided - * and their lexers match. - * - Otherwise, returns null. - */ - static range(first, second) { - if (!second) { - return first && first.loc; - } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) { - return null; - } else { - return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end); - } - } - } - - /** - * Interface required to break circular dependency between Token, Lexer, and - * ParseError. - */ - - /** - * The resulting token returned from `lex`. - * - * It consists of the token text plus some position information. - * The position information is essentially a range in an input string, - * but instead of referencing the bare input string, we refer to the lexer. - * That way it is possible to attach extra metadata to the input string, - * like for example a file name or similar. - * - * The position information is optional, so it is OK to construct synthetic - * tokens if appropriate. Not providing available position information may - * lead to degraded error reporting, though. - */ - class Token { - constructor( - text, // the text of this token - loc - ) { - this.text = text; - this.loc = loc; - } - - /** - * Given a pair of tokens (this and endToken), compute a `Token` encompassing - * the whole input range enclosed by these two. - */ - range( - endToken, // last token of the range, inclusive - text // the text of the newly constructed token - ) { - return new Token(text, SourceLocation.range(this, endToken)); - } - } - - // In TeX, there are actually three sets of dimensions, one for each of - // textstyle, scriptstyle, and scriptscriptstyle. These are - // provided in the the arrays below, in that order. - // - - // Math style is not quite the same thing as script level. - const StyleLevel = { - DISPLAY: 0, - TEXT: 1, - SCRIPT: 2, - SCRIPTSCRIPT: 3 - }; - - /** - * All registered global/built-in macros. - * `macros.js` exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary via `macros.js`. - */ - const _macros = {}; - - // This function might one day accept an additional argument and do more things. - function defineMacro(name, body) { - _macros[name] = body; - } - - /** - * Predefined macros for Temml. - * This can be used to define some commands in terms of others. - */ - - const macros = _macros; - - ////////////////////////////////////////////////////////////////////// - // macro tools - - defineMacro("\\noexpand", function(context) { - // The expansion is the token itself; but that token is interpreted - // as if its meaning were ‘\relax’ if it is a control sequence that - // would ordinarily be expanded by TeX’s expansion rules. - const t = context.popToken(); - if (context.isExpandable(t.text)) { - t.noexpand = true; - t.treatAsRelax = true; - } - return { tokens: [t], numArgs: 0 }; - }); - - defineMacro("\\expandafter", function(context) { - // TeX first reads the token that comes immediately after \expandafter, - // without expanding it; let’s call this token t. Then TeX reads the - // token that comes after t (and possibly more tokens, if that token - // has an argument), replacing it by its expansion. Finally TeX puts - // t back in front of that expansion. - const t = context.popToken(); - context.expandOnce(true); // expand only an expandable token - return { tokens: [t], numArgs: 0 }; - }); - - // LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2 - // TeX source: \long\def\@firstoftwo#1#2{#1} - defineMacro("\\@firstoftwo", function(context) { - const args = context.consumeArgs(2); - return { tokens: args[0], numArgs: 0 }; - }); - - // LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1 - // TeX source: \long\def\@secondoftwo#1#2{#2} - defineMacro("\\@secondoftwo", function(context) { - const args = context.consumeArgs(2); - return { tokens: args[1], numArgs: 0 }; - }); - - // LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded) - // symbol that isn't a space, consuming any spaces but not consuming the - // first nonspace character. If that nonspace character matches #1, then - // the macro expands to #2; otherwise, it expands to #3. - defineMacro("\\@ifnextchar", function(context) { - const args = context.consumeArgs(3); // symbol, if, else - context.consumeSpaces(); - const nextToken = context.future(); - if (args[0].length === 1 && args[0][0].text === nextToken.text) { - return { tokens: args[1], numArgs: 0 }; - } else { - return { tokens: args[2], numArgs: 0 }; - } - }); - - // LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol. - // If it is `*`, then it consumes the symbol, and the macro expands to #1; - // otherwise, the macro expands to #2 (without consuming the symbol). - // TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}} - defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}"); - - // LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode - defineMacro("\\TextOrMath", function(context) { - const args = context.consumeArgs(2); - if (context.mode === "text") { - return { tokens: args[0], numArgs: 0 }; - } else { - return { tokens: args[1], numArgs: 0 }; - } - }); - - const stringFromArg = arg => { - // Reverse the order of the arg and return a string. - let str = ""; - for (let i = arg.length - 1; i > -1; i--) { - str += arg[i].text; - } - return str - }; - - // Lookup table for parsing numbers in base 8 through 16 - const digitToNumber = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - a: 10, - A: 10, - b: 11, - B: 11, - c: 12, - C: 12, - d: 13, - D: 13, - e: 14, - E: 14, - f: 15, - F: 15 - }; - - const nextCharNumber = context => { - const numStr = context.future().text; - if (numStr === "EOF") { return [null, ""] } - return [digitToNumber[numStr.charAt(0)], numStr] - }; - - const appendCharNumbers = (number, numStr, base) => { - for (let i = 1; i < numStr.length; i++) { - const digit = digitToNumber[numStr.charAt(i)]; - number *= base; - number += digit; - } - return number - }; - - // TeX \char makes a literal character (catcode 12) using the following forms: - // (see The TeXBook, p. 43) - // \char123 -- decimal - // \char'123 -- octal - // \char"123 -- hex - // \char`x -- character that can be written (i.e. isn't active) - // \char`\x -- character that cannot be written (e.g. %) - // These all refer to characters from the font, so we turn them into special - // calls to a function \@char dealt with in the Parser. - defineMacro("\\char", function(context) { - let token = context.popToken(); - let base; - let number = ""; - if (token.text === "'") { - base = 8; - token = context.popToken(); - } else if (token.text === '"') { - base = 16; - token = context.popToken(); - } else if (token.text === "`") { - token = context.popToken(); - if (token.text[0] === "\\") { - number = token.text.charCodeAt(1); - } else if (token.text === "EOF") { - throw new ParseError("\\char` missing argument"); - } else { - number = token.text.charCodeAt(0); - } - } else { - base = 10; - } - if (base) { - // Parse a number in the given base, starting with first `token`. - let numStr = token.text; - number = digitToNumber[numStr.charAt(0)]; - if (number == null || number >= base) { - throw new ParseError(`Invalid base-${base} digit ${token.text}`); - } - number = appendCharNumbers(number, numStr, base); - let digit; - [digit, numStr] = nextCharNumber(context); - while (digit != null && digit < base) { - number *= base; - number += digit; - number = appendCharNumbers(number, numStr, base); - context.popToken(); - [digit, numStr] = nextCharNumber(context); - } - } - return `\\@char{${number}}`; - }); - - function recreateArgStr(context) { - // Recreate the macro's original argument string from the array of parse tokens. - const tokens = context.consumeArgs(1)[0]; - let str = ""; - let expectedLoc = tokens[tokens.length - 1].loc.start; - for (let i = tokens.length - 1; i >= 0; i--) { - const actualLoc = tokens[i].loc.start; - if (actualLoc > expectedLoc) { - // context.consumeArgs has eaten a space. - str += " "; - expectedLoc = actualLoc; - } - str += tokens[i].text; - expectedLoc += tokens[i].text.length; - } - return str - } - - // The Latin Modern font renders at the wrong vertical alignment. - // This macro provides a better rendering. - defineMacro("\\surd", '\\sqrt{\\vphantom{|}}'); - - // See comment for \oplus in symbols.js. - defineMacro("\u2295", "\\oplus"); - - // Since Temml has no \par, ignore \long. - defineMacro("\\long", ""); - - ////////////////////////////////////////////////////////////////////// - // Grouping - // \let\bgroup={ \let\egroup=} - defineMacro("\\bgroup", "{"); - defineMacro("\\egroup", "}"); - - // Symbols from latex.ltx: - // \def~{\nobreakspace{}} - // \def\lq{`} - // \def\rq{'} - // \def \aa {\r a} - defineMacro("~", "\\nobreakspace"); - defineMacro("\\lq", "`"); - defineMacro("\\rq", "'"); - defineMacro("\\aa", "\\r a"); - - defineMacro("\\Bbbk", "\\Bbb{k}"); - - // \mathstrut from the TeXbook, p 360 - defineMacro("\\mathstrut", "\\vphantom{(}"); - - // \underbar from TeXbook p 353 - defineMacro("\\underbar", "\\underline{\\text{#1}}"); - - ////////////////////////////////////////////////////////////////////// - // LaTeX_2ε - - // \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@ - // \kern6\p@\hbox{.}\hbox{.}\hbox{.}}} - // We'll call \varvdots, which gets a glyph from symbols.js. - // The zero-width rule gets us an equivalent to the vertical 6pt kern. - defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}"); - defineMacro("\u22ee", "\\vdots"); - - // {array} environment gaps - defineMacro("\\arraystretch", "1"); // line spacing factor times 12pt - defineMacro("\\arraycolsep", "6pt"); // half the width separating columns - - ////////////////////////////////////////////////////////////////////// - // amsmath.sty - // http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf - - //\newcommand{\substack}[1]{\subarray{c}#1\endsubarray} - defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"); - - // \def\iff{\DOTSB\;\Longleftrightarrow\;} - // \def\implies{\DOTSB\;\Longrightarrow\;} - // \def\impliedby{\DOTSB\;\Longleftarrow\;} - defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"); - defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"); - defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"); - - // AMSMath's automatic \dots, based on \mdots@@ macro. - const dotsByToken = { - ",": "\\dotsc", - "\\not": "\\dotsb", - // \keybin@ checks for the following: - "+": "\\dotsb", - "=": "\\dotsb", - "<": "\\dotsb", - ">": "\\dotsb", - "-": "\\dotsb", - "*": "\\dotsb", - ":": "\\dotsb", - // Symbols whose definition starts with \DOTSB: - "\\DOTSB": "\\dotsb", - "\\coprod": "\\dotsb", - "\\bigvee": "\\dotsb", - "\\bigwedge": "\\dotsb", - "\\biguplus": "\\dotsb", - "\\bigcap": "\\dotsb", - "\\bigcup": "\\dotsb", - "\\prod": "\\dotsb", - "\\sum": "\\dotsb", - "\\bigotimes": "\\dotsb", - "\\bigoplus": "\\dotsb", - "\\bigodot": "\\dotsb", - "\\bigsqcap": "\\dotsb", - "\\bigsqcup": "\\dotsb", - "\\bigtimes": "\\dotsb", - "\\And": "\\dotsb", - "\\longrightarrow": "\\dotsb", - "\\Longrightarrow": "\\dotsb", - "\\longleftarrow": "\\dotsb", - "\\Longleftarrow": "\\dotsb", - "\\longleftrightarrow": "\\dotsb", - "\\Longleftrightarrow": "\\dotsb", - "\\mapsto": "\\dotsb", - "\\longmapsto": "\\dotsb", - "\\hookrightarrow": "\\dotsb", - "\\doteq": "\\dotsb", - // Symbols whose definition starts with \mathbin: - "\\mathbin": "\\dotsb", - // Symbols whose definition starts with \mathrel: - "\\mathrel": "\\dotsb", - "\\relbar": "\\dotsb", - "\\Relbar": "\\dotsb", - "\\xrightarrow": "\\dotsb", - "\\xleftarrow": "\\dotsb", - // Symbols whose definition starts with \DOTSI: - "\\DOTSI": "\\dotsi", - "\\int": "\\dotsi", - "\\oint": "\\dotsi", - "\\iint": "\\dotsi", - "\\iiint": "\\dotsi", - "\\iiiint": "\\dotsi", - // Symbols whose definition starts with \DOTSX: - "\\DOTSX": "\\dotsx" - }; - - defineMacro("\\dots", function(context) { - // TODO: If used in text mode, should expand to \textellipsis. - // However, in Temml, \textellipsis and \ldots behave the same - // (in text mode), and it's unlikely we'd see any of the math commands - // that affect the behavior of \dots when in text mode. So fine for now - // (until we support \ifmmode ... \else ... \fi). - let thedots = "\\dotso"; - const next = context.expandAfterFuture().text; - if (next in dotsByToken) { - thedots = dotsByToken[next]; - } else if (next.slice(0, 4) === "\\not") { - thedots = "\\dotsb"; - } else if (next in symbols.math) { - if (["bin", "rel"].includes(symbols.math[next].group)) { - thedots = "\\dotsb"; - } - } - return thedots; - }); - - const spaceAfterDots = { - // \rightdelim@ checks for the following: - ")": true, - "]": true, - "\\rbrack": true, - "\\}": true, - "\\rbrace": true, - "\\rangle": true, - "\\rceil": true, - "\\rfloor": true, - "\\rgroup": true, - "\\rmoustache": true, - "\\right": true, - "\\bigr": true, - "\\biggr": true, - "\\Bigr": true, - "\\Biggr": true, - // \extra@ also tests for the following: - $: true, - // \extrap@ checks for the following: - ";": true, - ".": true, - ",": true - }; - - defineMacro("\\dotso", function(context) { - const next = context.future().text; - if (next in spaceAfterDots) { - return "\\ldots\\,"; - } else { - return "\\ldots"; - } - }); - - defineMacro("\\dotsc", function(context) { - const next = context.future().text; - // \dotsc uses \extra@ but not \extrap@, instead specially checking for - // ';' and '.', but doesn't check for ','. - if (next in spaceAfterDots && next !== ",") { - return "\\ldots\\,"; - } else { - return "\\ldots"; - } - }); - - defineMacro("\\cdots", function(context) { - const next = context.future().text; - if (next in spaceAfterDots) { - return "\\@cdots\\,"; - } else { - return "\\@cdots"; - } - }); - - defineMacro("\\dotsb", "\\cdots"); - defineMacro("\\dotsm", "\\cdots"); - defineMacro("\\dotsi", "\\!\\cdots"); - defineMacro("\\idotsint", "\\int\\!\\cdots\\!\\int"); - // amsmath doesn't actually define \dotsx, but \dots followed by a macro - // starting with \DOTSX implies \dotso, and then \extra@ detects this case - // and forces the added `\,`. - defineMacro("\\dotsx", "\\ldots\\,"); - - // \let\DOTSI\relax - // \let\DOTSB\relax - // \let\DOTSX\relax - defineMacro("\\DOTSI", "\\relax"); - defineMacro("\\DOTSB", "\\relax"); - defineMacro("\\DOTSX", "\\relax"); - - // Spacing, based on amsmath.sty's override of LaTeX defaults - // \DeclareRobustCommand{\tmspace}[3]{% - // \ifmmode\mskip#1#2\else\kern#1#3\fi\relax} - defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"); - // \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}} - // TODO: math mode should use \thinmuskip - defineMacro("\\,", "{\\tmspace+{3mu}{.1667em}}"); - // \let\thinspace\, - defineMacro("\\thinspace", "\\,"); - // \def\>{\mskip\medmuskip} - // \renewcommand{\:}{\tmspace+\medmuskip{.2222em}} - // TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu - defineMacro("\\>", "\\mskip{4mu}"); - defineMacro("\\:", "{\\tmspace+{4mu}{.2222em}}"); - // \let\medspace\: - defineMacro("\\medspace", "\\:"); - // \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}} - // TODO: math mode should use \thickmuskip = 5mu plus 5mu - defineMacro("\\;", "{\\tmspace+{5mu}{.2777em}}"); - // \let\thickspace\; - defineMacro("\\thickspace", "\\;"); - // \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}} - // TODO: math mode should use \thinmuskip - defineMacro("\\!", "{\\tmspace-{3mu}{.1667em}}"); - // \let\negthinspace\! - defineMacro("\\negthinspace", "\\!"); - // \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}} - // TODO: math mode should use \medmuskip - defineMacro("\\negmedspace", "{\\tmspace-{4mu}{.2222em}}"); - // \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}} - // TODO: math mode should use \thickmuskip - defineMacro("\\negthickspace", "{\\tmspace-{5mu}{.277em}}"); - // \def\enspace{\kern.5em } - defineMacro("\\enspace", "\\kern.5em "); - // \def\enskip{\hskip.5em\relax} - defineMacro("\\enskip", "\\hskip.5em\\relax"); - // \def\quad{\hskip1em\relax} - defineMacro("\\quad", "\\hskip1em\\relax"); - // \def\qquad{\hskip2em\relax} - defineMacro("\\qquad", "\\hskip2em\\relax"); - - defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax"); - - // \tag@in@display form of \tag - defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren"); - defineMacro("\\tag@paren", "\\tag@literal{({#1})}"); - defineMacro("\\tag@literal", (context) => { - if (context.macros.get("\\df@tag")) { - throw new ParseError("Multiple \\tag"); - } - return "\\gdef\\df@tag{\\text{#1}}"; - }); - defineMacro("\\notag", "\\nonumber"); - defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}"); - - // \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin - // {\operator@font mod}\penalty900 - // \mkern5mu\nonscript\mskip-\medmuskip} - // \newcommand{\pod}[1]{\allowbreak - // \if@display\mkern18mu\else\mkern8mu\fi(#1)} - // \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}} - // \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu - // \else\mkern12mu\fi{\operator@font mod}\,\,#1} - // TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu - defineMacro("\\bmod", "\\mathbin{\\text{mod}}"); - defineMacro( - "\\pod", - "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)" - ); - defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"); - defineMacro( - "\\mod", - "\\allowbreak" + - "\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" + - "{\\rm mod}\\,\\,#1" - ); - - ////////////////////////////////////////////////////////////////////// - // LaTeX source2e - - // \expandafter\let\expandafter\@normalcr - // \csname\expandafter\@gobble\string\\ \endcsname - // \DeclareRobustCommand\newline{\@normalcr\relax} - defineMacro("\\newline", "\\\\\\relax"); - - // \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@} - // TODO: Doesn't normally work in math mode because \@ fails. - defineMacro("\\TeX", "\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}"); - - defineMacro( - "\\LaTeX", - "\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX" - ); - - defineMacro( - "\\Temml", - // eslint-disable-next-line max-len - "\\textrm{T}\\kern-0.2em\\lower{0.2em}{\\textrm{E}}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}" - ); - - // \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace} - // \def\@hspace#1{\hskip #1\relax} - // \def\@hspacer#1{\vrule \@width\z@\nobreak - // \hskip #1\hskip \z@skip} - defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace"); - defineMacro("\\@hspace", "\\hskip #1\\relax"); - defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"); - - defineMacro("\\colon", `\\mathpunct{\\char"3a}`); - - ////////////////////////////////////////////////////////////////////// - // mathtools.sty - - defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}"); - - //\providecommand\ordinarycolon{:} - defineMacro("\\ordinarycolon", `\\char"3a`); - // Raise to center on the math axis, as closely as possible. - defineMacro("\\vcentcolon", "\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}"); - // \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}} - defineMacro("\\coloneq", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}'); - // \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}} - defineMacro("\\Coloneq", '\\mathrel{\\char"2237\\char"2212}'); - // \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon} - defineMacro("\\Eqqcolon", '\\mathrel{\\char"3d\\char"2237}'); - // \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon} - defineMacro("\\Eqcolon", '\\mathrel{\\char"2212\\char"2237}'); - // \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx} - defineMacro("\\colonapprox", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}'); - // \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx} - defineMacro("\\Colonapprox", '\\mathrel{\\char"2237\\char"2248}'); - // \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim} - defineMacro("\\colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); - // \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim} - defineMacro("\\Colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); - - ////////////////////////////////////////////////////////////////////// - // colonequals.sty - - // Alternate names for mathtools's macros: - defineMacro("\\ratio", "\\vcentcolon"); - defineMacro("\\coloncolon", "\\dblcolon"); - defineMacro("\\colonequals", "\\coloneqq"); - defineMacro("\\coloncolonequals", "\\Coloneqq"); - defineMacro("\\equalscolon", "\\eqqcolon"); - defineMacro("\\equalscoloncolon", "\\Eqqcolon"); - defineMacro("\\colonminus", "\\coloneq"); - defineMacro("\\coloncolonminus", "\\Coloneq"); - defineMacro("\\minuscolon", "\\eqcolon"); - defineMacro("\\minuscoloncolon", "\\Eqcolon"); - // \colonapprox name is same in mathtools and colonequals. - defineMacro("\\coloncolonapprox", "\\Colonapprox"); - // \colonsim name is same in mathtools and colonequals. - defineMacro("\\coloncolonsim", "\\Colonsim"); - - // Present in newtxmath, pxfonts and txfonts - defineMacro("\\notni", "\\mathrel{\\char`\u220C}"); - defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"); - defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"); - - ////////////////////////////////////////////////////////////////////// - // From amsopn.sty - defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"); - defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"); - defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\text{lim}}}"); - defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\text{lim}}}"); - defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}"); - defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}"); - - defineMacro("\\centerdot", "{\\medspace\\rule{0.167em}{0.189em}\\medspace}"); - - ////////////////////////////////////////////////////////////////////// - // statmath.sty - // https://ctan.math.illinois.edu/macros/latex/contrib/statmath/statmath.pdf - - defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"); - defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"); - defineMacro("\\plim", "\\DOTSB\\operatorname*{plim}"); - - ////////////////////////////////////////////////////////////////////// - // MnSymbol.sty - - defineMacro("\\leftmodels", "\\mathop{\\reflectbox{$\\models$}}"); - - ////////////////////////////////////////////////////////////////////// - // braket.sty - // http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf - - defineMacro("\\bra", "\\mathinner{\\langle{#1}|}"); - defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}"); - defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}"); - defineMacro("\\Bra", "\\left\\langle#1\\right|"); - defineMacro("\\Ket", "\\left|#1\\right\\rangle"); - // A helper for \Braket and \Set - const replaceVert = (argStr, match) => { - const ch = match[0] === "|" ? "\\vert" : "\\Vert"; - const replaceStr = `}\\,\\middle${ch}\\,{`; - return argStr.slice(0, match.index) + replaceStr + argStr.slice(match.index + match[0].length) - }; - defineMacro("\\Braket", function(context) { - let argStr = recreateArgStr(context); - const regEx = /\|\||\||\\\|/g; - let match; - while ((match = regEx.exec(argStr)) !== null) { - argStr = replaceVert(argStr, match); - } - return "\\left\\langle{" + argStr + "}\\right\\rangle" - }); - defineMacro("\\Set", function(context) { - let argStr = recreateArgStr(context); - const match = /\|\||\||\\\|/.exec(argStr); - if (match) { - argStr = replaceVert(argStr, match); - } - return "\\left\\{\\:{" + argStr + "}\\:\\right\\}" - }); - defineMacro("\\set", function(context) { - const argStr = recreateArgStr(context); - return "\\{{" + argStr.replace(/\|/, "}\\mid{") + "}\\}" - }); - - ////////////////////////////////////////////////////////////////////// - // actuarialangle.dtx - defineMacro("\\angln", "{\\angl n}"); - - ////////////////////////////////////////////////////////////////////// - // derivative.sty - defineMacro("\\odv", "\\@ifstar\\odv@next\\odv@numerator"); - defineMacro("\\odv@numerator", "\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}"); - defineMacro("\\odv@next", "\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1"); - defineMacro("\\pdv", "\\@ifstar\\pdv@next\\pdv@numerator"); - - const pdvHelper = args => { - const numerator = args[0][0].text; - const denoms = stringFromArg(args[1]).split(","); - const power = String(denoms.length); - const numOp = power === "1" ? "\\partial" : `\\partial^${power}`; - let denominator = ""; - denoms.map(e => { denominator += "\\partial " + e.trim() + "\\,";}); - return [numerator, numOp, denominator.replace(/\\,$/, "")] - }; - defineMacro("\\pdv@numerator", function(context) { - const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); - return `\\frac{${numOp} ${numerator}}{${denominator}}` - }); - defineMacro("\\pdv@next", function(context) { - const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); - return `\\frac{${numOp}}{${denominator}} ${numerator}` - }); - - ////////////////////////////////////////////////////////////////////// - // upgreek.dtx - defineMacro("\\upalpha", "\\up@greek{\\alpha}"); - defineMacro("\\upbeta", "\\up@greek{\\beta}"); - defineMacro("\\upgamma", "\\up@greek{\\gamma}"); - defineMacro("\\updelta", "\\up@greek{\\delta}"); - defineMacro("\\upepsilon", "\\up@greek{\\epsilon}"); - defineMacro("\\upzeta", "\\up@greek{\\zeta}"); - defineMacro("\\upeta", "\\up@greek{\\eta}"); - defineMacro("\\uptheta", "\\up@greek{\\theta}"); - defineMacro("\\upiota", "\\up@greek{\\iota}"); - defineMacro("\\upkappa", "\\up@greek{\\kappa}"); - defineMacro("\\uplambda", "\\up@greek{\\lambda}"); - defineMacro("\\upmu", "\\up@greek{\\mu}"); - defineMacro("\\upnu", "\\up@greek{\\nu}"); - defineMacro("\\upxi", "\\up@greek{\\xi}"); - defineMacro("\\upomicron", "\\up@greek{\\omicron}"); - defineMacro("\\uppi", "\\up@greek{\\pi}"); - defineMacro("\\upalpha", "\\up@greek{\\alpha}"); - defineMacro("\\uprho", "\\up@greek{\\rho}"); - defineMacro("\\upsigma", "\\up@greek{\\sigma}"); - defineMacro("\\uptau", "\\up@greek{\\tau}"); - defineMacro("\\upupsilon", "\\up@greek{\\upsilon}"); - defineMacro("\\upphi", "\\up@greek{\\phi}"); - defineMacro("\\upchi", "\\up@greek{\\chi}"); - defineMacro("\\uppsi", "\\up@greek{\\psi}"); - defineMacro("\\upomega", "\\up@greek{\\omega}"); - - ////////////////////////////////////////////////////////////////////// - // cmll package - defineMacro("\\invamp", '\\mathbin{\\char"214b}'); - defineMacro("\\parr", '\\mathbin{\\char"214b}'); - defineMacro("\\upand", '\\mathbin{\\char"214b}'); // STIX package - defineMacro("\\with", '\\mathbin{\\char"26}'); - defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}'); - defineMacro("\\multimapboth", '\\mathrel{\\char"29df}'); - defineMacro("\\scoh", '{\\mkern5mu\\char"2322\\mkern5mu}'); - defineMacro("\\sincoh", '{\\mkern5mu\\char"2323\\mkern5mu}'); - defineMacro("\\coh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}} -{\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}`); - defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}} -{\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}`); - - - ////////////////////////////////////////////////////////////////////// - // chemstyle package - defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}"); - - // Helper functions - function getHLines(parser) { - // Return an array. The array length = number of hlines. - // Each element in the array tells if the line is dashed. - const hlineInfo = []; - parser.consumeSpaces(); - let nxt = parser.fetch().text; - if (nxt === "\\relax") { - parser.consume(); - parser.consumeSpaces(); - nxt = parser.fetch().text; - } - while (nxt === "\\hline" || nxt === "\\hdashline") { - parser.consume(); - hlineInfo.push(nxt === "\\hdashline"); - parser.consumeSpaces(); - nxt = parser.fetch().text; - } - return hlineInfo; - } - - const validateAmsEnvironmentContext = context => { - const settings = context.parser.settings; - if (!settings.displayMode) { - throw new ParseError(`{${context.envName}} can be used only in display mode.`); - } - }; - - const sizeRegEx$1 = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; - const arrayGaps = macros => { - let arraystretch = macros.get("\\arraystretch"); - if (typeof arraystretch !== "string") { - arraystretch = stringFromArg(arraystretch.tokens); - } - arraystretch = isNaN(arraystretch) ? null : Number(arraystretch); - let arraycolsepStr = macros.get("\\arraycolsep"); - if (typeof arraycolsepStr !== "string") { - arraycolsepStr = stringFromArg(arraycolsepStr.tokens); - } - const match = sizeRegEx$1.exec(arraycolsepStr); - const arraycolsep = match - ? { number: +(match[1] + match[2]), unit: match[3] } - : null; - return [arraystretch, arraycolsep] - }; - - const checkCellForLabels = cell => { - // Check if the author wrote a \tag{} inside this cell. - let rowLabel = ""; - for (let i = 0; i < cell.length; i++) { - if (cell[i].type === "label") { - if (rowLabel) { throw new ParseError(("Multiple \\labels in one row")) } - rowLabel = cell[i].string; - } - } - return rowLabel - }; - - // autoTag (an argument to parseArray) can be one of three values: - // * undefined: Regular (not-top-level) array; no tags on each row - // * true: Automatic equation numbering, overridable by \tag - // * false: Tags allowed on each row, but no automatic numbering - // This function *doesn't* work with the "split" environment name. - function getAutoTag(name) { - if (name.indexOf("ed") === -1) { - return name.indexOf("*") === -1; - } - // return undefined; - } - - /** - * Parse the body of the environment, with rows delimited by \\ and - * columns delimited by &, and create a nested list in row-major order - * with one group per cell. If given an optional argument scriptLevel - * ("text", "display", etc.), then each cell is cast into that scriptLevel. - */ - function parseArray( - parser, - { - cols, // [{ type: string , align: l|c|r|null }] - envClasses, // align(ed|at|edat) | array | cases | cd | small | multline - autoTag, // boolean - singleRow, // boolean - emptySingleRow, // boolean - maxNumCols, // number - leqno, // boolean - arraystretch, // number | null - arraycolsep // size value | null - }, - scriptLevel - ) { - const endToken = envClasses && envClasses.includes("bordermatrix") ? "}" : "\\end"; - parser.gullet.beginGroup(); - if (!singleRow) { - // \cr is equivalent to \\ without the optional size argument (see below) - // TODO: provide helpful error when \cr is used outside array environment - parser.gullet.macros.set("\\cr", "\\\\\\relax"); - } - - // Start group for first cell - parser.gullet.beginGroup(); - - let row = []; - const body = [row]; - const rowGaps = []; - const labels = []; - - const hLinesBeforeRow = []; - - const tags = (autoTag != null ? [] : undefined); - - // amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent - // whether this row should have an equation number. Simulate this with - // a \@eqnsw macro set to 1 or 0. - function beginRow() { - if (autoTag) { - parser.gullet.macros.set("\\@eqnsw", "1", true); - } - } - function endRow() { - if (tags) { - if (parser.gullet.macros.get("\\df@tag")) { - tags.push(parser.subparse([new Token("\\df@tag")])); - parser.gullet.macros.set("\\df@tag", undefined, true); - } else { - tags.push(Boolean(autoTag) && - parser.gullet.macros.get("\\@eqnsw") === "1"); - } - } - } - beginRow(); - - // Test for \hline at the top of the array. - hLinesBeforeRow.push(getHLines(parser)); - - while (true) { - // Parse each cell in its own group (namespace) - let cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\"); - parser.gullet.endGroup(); - parser.gullet.beginGroup(); - - cell = { - type: "ordgroup", - mode: parser.mode, - body: cell, - semisimple: true - }; - row.push(cell); - const next = parser.fetch().text; - if (next === "&") { - if (maxNumCols && row.length === maxNumCols) { - if (envClasses.includes("array")) { - if (parser.settings.strict) { - throw new ParseError("Too few columns " + "specified in the {array} column argument.", - parser.nextToken) - } - } else if (maxNumCols === 2) { - throw new ParseError("The split environment accepts no more than two columns", - parser.nextToken); - } else { - throw new ParseError("The equation environment accepts only one column", - parser.nextToken) - } - } - parser.consume(); - } else if (next === endToken) { - endRow(); - // Arrays terminate newlines with `\crcr` which consumes a `\cr` if - // the last line is empty. However, AMS environments keep the - // empty row if it's the only one. - // NOTE: Currently, `cell` is the last item added into `row`. - if (row.length === 1 && cell.body.length === 0 && (body.length > 1 || !emptySingleRow)) { - body.pop(); - } - labels.push(checkCellForLabels(cell.body)); - if (hLinesBeforeRow.length < body.length + 1) { - hLinesBeforeRow.push([]); - } - break; - } else if (next === "\\\\") { - parser.consume(); - let size; - // \def\Let@{\let\\\math@cr} - // \def\math@cr{...\math@cr@} - // \def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}} - // \def\math@cr@@[#1]{...\math@cr@@@...} - // \def\math@cr@@@{\cr} - if (parser.gullet.future().text !== " ") { - size = parser.parseSizeGroup(true); - } - rowGaps.push(size ? size.value : null); - endRow(); - - labels.push(checkCellForLabels(cell.body)); - - // check for \hline(s) following the row separator - hLinesBeforeRow.push(getHLines(parser)); - - row = []; - body.push(row); - beginRow(); - } else { - throw new ParseError("Expected & or \\\\ or \\cr or " + endToken, parser.nextToken); - } - } - - // End cell group - parser.gullet.endGroup(); - // End array group defining \cr - parser.gullet.endGroup(); - - return { - type: "array", - mode: parser.mode, - body, - cols, - rowGaps, - hLinesBeforeRow, - envClasses, - autoTag, - scriptLevel, - tags, - labels, - leqno, - arraystretch, - arraycolsep - }; - } - - // Decides on a scriptLevel for cells in an array according to whether the given - // environment name starts with the letter 'd'. - function dCellStyle(envName) { - return envName.slice(0, 1) === "d" ? "display" : "text" - } - - const alignMap = { - c: "center ", - l: "left ", - r: "right " - }; - - const glue = group => { - const glueNode = new MathNode("mtd", []); - glueNode.style = { padding: "0", width: "50%" }; - if (group.envClasses.includes("multline")) { - glueNode.style.width = "7.5%"; - } - return glueNode - }; - - const mathmlBuilder$9 = function(group, style) { - const tbl = []; - const numRows = group.body.length; - const hlines = group.hLinesBeforeRow; - const tagIsPresent = (group.tags && group.tags.some((tag) => tag)); - - for (let i = 0; i < numRows; i++) { - const rw = group.body[i]; - const row = []; - const cellLevel = group.scriptLevel === "text" - ? StyleLevel.TEXT - : group.scriptLevel === "script" - ? StyleLevel.SCRIPT - : StyleLevel.DISPLAY; - - for (let j = 0; j < rw.length; j++) { - const mtd = new MathNode( - "mtd", - [buildGroup$1(rw[j], style.withLevel(cellLevel))] - ); - - if (group.envClasses.includes("multline")) { - const align = i === 0 ? "left" : i === numRows - 1 ? "right" : "center"; - if (align !== "center") { - mtd.classes.push("tml-" + align); - } - } - row.push(mtd); - } - const numColumns = group.body[0].length; - // Fill out a short row with empty elements. - for (let k = 0; k < numColumns - rw.length; k++) { - row.push(new MathNode("mtd", [], [], style)); - } - if (tagIsPresent) { - const tag = group.tags[i]; - let tagElement; - if (tag === true) { // automatic numbering - tagElement = new MathNode("mtext", [new Span(["tml-eqn"])]); - } else if (tag === false) { - // \nonumber/\notag or starred environment - tagElement = new MathNode("mtext", [], []); - } else { // manual \tag - tagElement = buildExpressionRow(tag[0].body, style.withLevel(cellLevel), true); - tagElement = consolidateText(tagElement); - tagElement.classes = ["tml-tag"]; - } - if (tagElement) { - row.unshift(glue(group)); - row.push(glue(group)); - if (group.leqno) { - row[0].children.push(tagElement); - } else { - row[row.length - 1].children.push(tagElement); - } - } - } - const mtr = new MathNode("mtr", row, []); - const label = group.labels.shift(); - if (label && group.tags && group.tags[i]) { - mtr.setAttribute("id", label); - if (Array.isArray(group.tags[i])) { mtr.classes.push("tml-tageqn"); } - } - - // Write horizontal rules - if (i === 0 && hlines[0].length > 0) { - if (hlines[0].length === 2) { - mtr.children.forEach(cell => { cell.style.borderTop = "0.15em double"; }); - } else { - mtr.children.forEach(cell => { - cell.style.borderTop = hlines[0][0] ? "0.06em dashed" : "0.06em solid"; - }); - } - } - if (hlines[i + 1].length > 0) { - if (hlines[i + 1].length === 2) { - mtr.children.forEach(cell => { cell.style.borderBottom = "0.15em double"; }); - } else { - mtr.children.forEach(cell => { - cell.style.borderBottom = hlines[i + 1][0] ? "0.06em dashed" : "0.06em solid"; - }); - } - } - - // Check for \hphantom \from \bordermatrix - let mustSquashRow = true; - for (let j = 0; j < mtr.children.length; j++) { - const child = mtr.children[j].children[0]; - if (!(child && child.type === "mpadded" && child.attributes.height === "0px")) { - mustSquashRow = false; - break - } - } - if (mustSquashRow) { - // All the cell contents are \hphantom. Squash the cell. - for (let j = 0; j < mtr.children.length; j++) { - mtr.children[j].style.display = "block"; // necessary in Firefox only - mtr.children[j].style.height = "0"; // necessary in Firefox only - mtr.children[j].style.paddingTop = "0"; - mtr.children[j].style.paddingBottom = "0"; - } - } - - tbl.push(mtr); - } - - if (group.arraystretch && group.arraystretch !== 1) { - // In LaTeX, \arraystretch is a factor applied to a 12pt strut height. - // It defines a baseline to baseline distance. - // Here, we do an approximation of that approach. - const pad = String(1.4 * group.arraystretch - 0.8) + "ex"; - for (let i = 0; i < tbl.length; i++) { - for (let j = 0; j < tbl[i].children.length; j++) { - tbl[i].children[j].style.paddingTop = pad; - tbl[i].children[j].style.paddingBottom = pad; - } - } - } - - let sidePadding; - let sidePadUnit; - if (group.envClasses.length > 0) { - sidePadding = group.envClasses.includes("abut") - ? "0" - : group.envClasses.includes("cases") - ? "0" - : group.envClasses.includes("small") - ? "0.1389" - : group.envClasses.includes("cd") - ? "0.25" - : "0.4"; // default side padding - sidePadUnit = "em"; - } - if (group.arraycolsep) { - const arraySidePad = calculateSize(group.arraycolsep, style); - sidePadding = arraySidePad.number.toFixed(4); - sidePadUnit = arraySidePad.unit; - } - if (sidePadding) { - const numCols = tbl.length === 0 ? 0 : tbl[0].children.length; - - const sidePad = (j, hand) => { - if (j === 0 && hand === 0) { return "0" } - if (j === numCols - 1 && hand === 1) { return "0" } - if (group.envClasses[0] !== "align") { return sidePadding } - if (hand === 1) { return "0" } - if (tagIsPresent) { - return (j % 2) ? "1" : "0" - } else { - return (j % 2) ? "0" : "1" - } - }; - - // Side padding - for (let i = 0; i < tbl.length; i++) { - for (let j = 0; j < tbl[i].children.length; j++) { - tbl[i].children[j].style.paddingLeft = `${sidePad(j, 0)}${sidePadUnit}`; - tbl[i].children[j].style.paddingRight = `${sidePad(j, 1)}${sidePadUnit}`; - } - } - } - if (group.envClasses.length === 0) { - // Set zero padding on side of the matrix - for (let i = 0; i < tbl.length; i++) { - tbl[i].children[0].style.paddingLeft = "0em"; - if (tbl[i].children.length === tbl[0].children.length) { - tbl[i].children[tbl[i].children.length - 1].style.paddingRight = "0em"; - } - } - } - - if (group.envClasses.length > 0) { - // Justification - const align = group.envClasses.includes("align") || group.envClasses.includes("alignat"); - for (let i = 0; i < tbl.length; i++) { - const row = tbl[i]; - if (align) { - for (let j = 0; j < row.children.length; j++) { - // Chromium does not recognize text-align: left. Use -webkit- - // TODO: Remove -webkit- when Chromium no longer needs it. - row.children[j].classes = ["tml-" + (j % 2 ? "left" : "right")]; - } - if (tagIsPresent) { - const k = group.leqno ? 0 : row.children.length - 1; - row.children[k].classes = []; // Default is center. - } - } - if (row.children.length > 1 && group.envClasses.includes("cases")) { - row.children[1].style.paddingLeft = "1em"; - } - - if (group.envClasses.includes("cases") || group.envClasses.includes("subarray")) { - for (const cell of row.children) { - cell.classes.push("tml-left"); - } - } - } - } - - let table = new MathNode("mtable", tbl); - if (group.envClasses.length > 0) { - // Top & bottom padding - if (group.envClasses.includes("jot")) { - table.classes.push("tml-jot"); - } else if (group.envClasses.includes("small")) { - table.classes.push("tml-small"); - } - } - if (group.scriptLevel === "display") { table.setAttribute("displaystyle", "true"); } - - if (group.autoTag || group.envClasses.includes("multline")) { - table.style.width = "100%"; - } - - // Column separator lines and column alignment - - if (group.cols && group.cols.length > 0) { - const cols = group.cols; - let prevTypeWasAlign = false; - let iStart = 0; - let iEnd = cols.length; - - while (cols[iStart].type === "separator") { - iStart += 1; - } - while (cols[iEnd - 1].type === "separator") { - iEnd -= 1; - } - - if (cols[0].type === "separator") { - const sep = cols[1].type === "separator" - ? "0.15em double" - : cols[0].separator === "|" - ? "0.06em solid " - : "0.06em dashed "; - for (const row of table.children) { - row.children[0].style.borderLeft = sep; - } - } - let iCol = tagIsPresent ? 0 : -1; - for (let i = iStart; i < iEnd; i++) { - if (cols[i].type === "align") { - const colAlign = alignMap[cols[i].align]; - iCol += 1; - for (const row of table.children) { - if (colAlign.trim() !== "center" && iCol < row.children.length) { - row.children[iCol].classes = ["tml-" + colAlign.trim()]; - } - } - prevTypeWasAlign = true; - } else if (cols[i].type === "separator") { - // MathML accepts only single lines between cells. - // So we read only the first of consecutive separators. - if (prevTypeWasAlign) { - const sep = cols[i + 1].type === "separator" - ? "0.15em double" - : cols[i].separator === "|" - ? "0.06em solid" - : "0.06em dashed"; - for (const row of table.children) { - if (iCol < row.children.length) { - row.children[iCol].style.borderRight = sep; - } - } - } - prevTypeWasAlign = false; - } - } - if (cols[cols.length - 1].type === "separator") { - const sep = cols[cols.length - 2].type === "separator" - ? "0.15em double" - : cols[cols.length - 1].separator === "|" - ? "0.06em solid" - : "0.06em dashed"; - for (const row of table.children) { - row.children[row.children.length - 1].style.borderRight = sep; - row.children[row.children.length - 1].style.paddingRight = "0.4em"; - } - } - } - - if (group.envClasses.includes("small")) { - // A small array. Wrap in scriptstyle. - table = new MathNode("mstyle", [table]); - table.setAttribute("scriptlevel", "1"); - } - - return table - }; - - // Convenience function for align, align*, aligned, alignat, alignat*, alignedat, split. - const alignedHandler = function(context, args) { - if (context.envName.indexOf("ed") === -1) { - validateAmsEnvironmentContext(context); - } - const isSplit = context.envName === "split"; - const cols = []; - const res = parseArray( - context.parser, - { - cols, - emptySingleRow: true, - autoTag: isSplit ? undefined : getAutoTag(context.envName), - envClasses: ["abut", "jot"], // set row spacing & provisional column spacing - maxNumCols: context.envName === "split" ? 2 : undefined, - leqno: context.parser.settings.leqno - }, - "display" - ); - - // Determining number of columns. - // 1. If the first argument is given, we use it as a number of columns, - // and makes sure that each row doesn't exceed that number. - // 2. Otherwise, just count number of columns = maximum number - // of cells in each row ("aligned" mode -- isAligned will be true). - // - // At the same time, prepend empty group {} at beginning of every second - // cell in each row (starting with second cell) so that operators become - // binary. This behavior is implemented in amsmath's \start@aligned. - let numMaths; - let numCols = 0; - const isAlignedAt = context.envName.indexOf("at") > -1; - if (args[0] && isAlignedAt) { - // alignat environment takes an argument w/ number of columns - let arg0 = ""; - for (let i = 0; i < args[0].body.length; i++) { - const textord = assertNodeType(args[0].body[i], "textord"); - arg0 += textord.text; - } - if (isNaN(arg0)) { - throw new ParseError("The alignat enviroment requires a numeric first argument.") - } - numMaths = Number(arg0); - numCols = numMaths * 2; - } - res.body.forEach(function(row) { - if (isAlignedAt) { - // Case 1 - const curMaths = row.length / 2; - if (numMaths < curMaths) { - throw new ParseError( - "Too many math in a row: " + `expected ${numMaths}, but got ${curMaths}`, - row[0] - ); - } - } else if (numCols < row.length) { - // Case 2 - numCols = row.length; - } - }); - - // Adjusting alignment. - // In aligned mode, we add one \qquad between columns; - // otherwise we add nothing. - for (let i = 0; i < numCols; ++i) { - let align = "r"; - if (i % 2 === 1) { - align = "l"; - } - cols[i] = { - type: "align", - align: align - }; - } - if (context.envName === "split") ; else if (isAlignedAt) { - res.envClasses.push("alignat"); // Sets justification - } else { - res.envClasses[0] = "align"; // Sets column spacing & justification - } - return res; - }; - - // Arrays are part of LaTeX, defined in lttab.dtx so its documentation - // is part of the source2e.pdf file of LaTeX2e source documentation. - // {darray} is an {array} environment where cells are set in \displaystyle, - // as defined in nccmath.sty. - defineEnvironment({ - type: "array", - names: ["array", "darray"], - props: { - numArgs: 1 - }, - handler(context, args) { - // Since no types are specified above, the two possibilities are - // - The argument is wrapped in {} or [], in which case Parser's - // parseGroup() returns an "ordgroup" wrapping some symbol node. - // - The argument is a bare symbol node. - const symNode = checkSymbolNodeType(args[0]); - const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; - const cols = colalign.map(function(nde) { - const node = assertSymbolNodeType(nde); - const ca = node.text; - if ("lcr".indexOf(ca) !== -1) { - return { - type: "align", - align: ca - }; - } else if (ca === "|") { - return { - type: "separator", - separator: "|" - }; - } else if (ca === ":") { - return { - type: "separator", - separator: ":" - }; - } - throw new ParseError("Unknown column alignment: " + ca, nde); - }); - const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); - const res = { - cols, - envClasses: ["array"], - maxNumCols: cols.length, - arraystretch, - arraycolsep - }; - return parseArray(context.parser, res, dCellStyle(context.envName)); - }, - mathmlBuilder: mathmlBuilder$9 - }); - - // The matrix environments of amsmath build on the array environment - // of LaTeX, which is discussed above. - // The mathtools package adds starred versions of the same environments. - // These have an optional argument to choose left|center|right justification. - defineEnvironment({ - type: "array", - names: [ - "matrix", - "pmatrix", - "bmatrix", - "Bmatrix", - "vmatrix", - "Vmatrix", - "matrix*", - "pmatrix*", - "bmatrix*", - "Bmatrix*", - "vmatrix*", - "Vmatrix*" - ], - props: { - numArgs: 0 - }, - handler(context) { - const delimiters = { - matrix: null, - pmatrix: ["(", ")"], - bmatrix: ["[", "]"], - Bmatrix: ["\\{", "\\}"], - vmatrix: ["|", "|"], - Vmatrix: ["\\Vert", "\\Vert"] - }[context.envName.replace("*", "")]; - // \hskip -\arraycolsep in amsmath - let colAlign = "c"; - const payload = { - envClasses: [], - cols: [] - }; - if (context.envName.charAt(context.envName.length - 1) === "*") { - // It's one of the mathtools starred functions. - // Parse the optional alignment argument. - const parser = context.parser; - parser.consumeSpaces(); - if (parser.fetch().text === "[") { - parser.consume(); - parser.consumeSpaces(); - colAlign = parser.fetch().text; - if ("lcr".indexOf(colAlign) === -1) { - throw new ParseError("Expected l or c or r", parser.nextToken); - } - parser.consume(); - parser.consumeSpaces(); - parser.expect("]"); - parser.consume(); - payload.cols = []; - } - } - const res = parseArray(context.parser, payload, "text"); - res.cols = res.body.length > 0 - ? new Array(res.body[0].length).fill({ type: "align", align: colAlign }) - : []; - const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); - res.arraystretch = arraystretch; - if (arraycolsep && !(arraycolsep === 6 && arraycolsep === "pt")) { - res.arraycolsep = arraycolsep; - } - return delimiters - ? { - type: "leftright", - mode: context.mode, - body: [res], - left: delimiters[0], - right: delimiters[1], - rightColor: undefined // \right uninfluenced by \color in array - } - : res; - }, - mathmlBuilder: mathmlBuilder$9 - }); - - defineEnvironment({ - type: "array", - names: ["bordermatrix"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { cols: [], envClasses: ["bordermatrix"] }; - const res = parseArray(context.parser, payload, "text"); - res.cols = res.body.length > 0 - ? new Array(res.body[0].length).fill({ type: "align", align: "c" }) - : []; - res.envClasses = []; - res.arraystretch = 1; - if (context.envName === "matrix") { return res} - return bordermatrixParseTree(res, context.delimiters) - }, - mathmlBuilder: mathmlBuilder$9 - }); - - defineEnvironment({ - type: "array", - names: ["smallmatrix"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { envClasses: ["small"] }; - const res = parseArray(context.parser, payload, "script"); - return res; - }, - mathmlBuilder: mathmlBuilder$9 - }); - - defineEnvironment({ - type: "array", - names: ["subarray"], - props: { - numArgs: 1 - }, - handler(context, args) { - // Parsing of {subarray} is similar to {array} - const symNode = checkSymbolNodeType(args[0]); - const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; - const cols = colalign.map(function(nde) { - const node = assertSymbolNodeType(nde); - const ca = node.text; - // {subarray} only recognizes "l" & "c" - if ("lc".indexOf(ca) !== -1) { - return { - type: "align", - align: ca - }; - } - throw new ParseError("Unknown column alignment: " + ca, nde); - }); - if (cols.length > 1) { - throw new ParseError("{subarray} can contain only one column"); - } - let res = { - cols, - envClasses: ["small"] - }; - res = parseArray(context.parser, res, "script"); - if (res.body.length > 0 && res.body[0].length > 1) { - throw new ParseError("{subarray} can contain only one column"); - } - return res; - }, - mathmlBuilder: mathmlBuilder$9 - }); - - // A cases environment (in amsmath.sty) is almost equivalent to - // \def - // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. - // {dcases} is a {cases} environment where cells are set in \displaystyle, - // as defined in mathtools.sty. - // {rcases} is another mathtools environment. It's brace is on the right side. - defineEnvironment({ - type: "array", - names: ["cases", "dcases", "rcases", "drcases"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { - cols: [], - envClasses: ["cases"] - }; - const res = parseArray(context.parser, payload, dCellStyle(context.envName)); - return { - type: "leftright", - mode: context.mode, - body: [res], - left: context.envName.indexOf("r") > -1 ? "." : "\\{", - right: context.envName.indexOf("r") > -1 ? "\\}" : ".", - rightColor: undefined - }; - }, - mathmlBuilder: mathmlBuilder$9 - }); - - // In the align environment, one uses ampersands, &, to specify number of - // columns in each row, and to locate spacing between each column. - // align gets automatic numbering. align* and aligned do not. - // The alignedat environment can be used in math mode. - defineEnvironment({ - type: "array", - names: ["align", "align*", "aligned", "split"], - props: { - numArgs: 0 - }, - handler: alignedHandler, - mathmlBuilder: mathmlBuilder$9 - }); - - // alignat environment is like an align environment, but one must explicitly - // specify maximum number of columns in each row, and can adjust where spacing occurs. - defineEnvironment({ - type: "array", - names: ["alignat", "alignat*", "alignedat"], - props: { - numArgs: 1 - }, - handler: alignedHandler, - mathmlBuilder: mathmlBuilder$9 - }); - - // A gathered environment is like an array environment with one centered - // column, but where rows are considered lines so get \jot line spacing - // and contents are set in \displaystyle. - defineEnvironment({ - type: "array", - names: ["gathered", "gather", "gather*"], - props: { - numArgs: 0 - }, - handler(context) { - if (context.envName !== "gathered") { - validateAmsEnvironmentContext(context); - } - const res = { - cols: [], - envClasses: ["abut", "jot"], - autoTag: getAutoTag(context.envName), - emptySingleRow: true, - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 - }); - - defineEnvironment({ - type: "array", - names: ["equation", "equation*"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - const res = { - autoTag: getAutoTag(context.envName), - emptySingleRow: true, - singleRow: true, - maxNumCols: 1, - envClasses: ["align"], - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 - }); - - defineEnvironment({ - type: "array", - names: ["multline", "multline*"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - const res = { - autoTag: context.envName === "multline", - maxNumCols: 1, - envClasses: ["jot", "multline"], - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 - }); - - defineEnvironment({ - type: "array", - names: ["CD"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - return parseCD(context.parser); - }, - mathmlBuilder: mathmlBuilder$9 - }); - - // Catch \hline outside array environment - defineFunction({ - type: "text", // Doesn't matter what this is. - names: ["\\hline", "\\hdashline"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: true - }, - handler(context, args) { - throw new ParseError(`${context.funcName} valid only within array environment`); - } - }); - - const environments = _environments; - - // \bordermatrix from TeXbook pp 177 & 361 - // Optional argument from Herbert Voß, Math mode, p 20 - // Ref: https://tug.ctan.org/obsolete/info/math/voss/mathmode/Mathmode.pdf - - defineFunction({ - type: "bordermatrix", - names: ["\\bordermatrix", "\\matrix"], - props: { - numArgs: 0, - numOptionalArgs: 1 - }, - handler: ({ parser, funcName }, args, optArgs) => { - // Find out if the author has defined custom delimiters - let delimiters = ["(", ")"]; - if (funcName === "\\bordermatrix" && optArgs[0] && optArgs[0].body) { - const body = optArgs[0].body; - if (body.length === 2 && body[0].type === "atom" && body[1].type === "atom") { - if (body[0].family === "open" && body[1].family === "close") { - delimiters = [body[0].text, body[1].text]; - } - } - } - // consume the opening brace - parser.consumeSpaces(); - parser.consume(); - - // Pass control to the environment handler in array.js. - const env = environments["bordermatrix"]; - const context = { - mode: parser.mode, - envName: funcName.slice(1), - delimiters, - parser - }; - const result = env.handler(context); - parser.expect("}", true); - return result - } - }); - - defineFunction({ - type: "cancelto", - names: ["\\cancelto"], - props: { - numArgs: 2 - }, - handler({ parser }, args) { - const to = args[0]; - const body = args[1]; - return { - type: "cancelto", - mode: parser.mode, - body, - to, - isCharacterBox: isCharacterBox(body) - }; - }, - mathmlBuilder(group, style) { - const fromNode = new MathNode( - "mrow", - [buildGroup$1(group.body, style)], - ["ff-narrow"] // A zero-width mrow. - ); - // Write the arrow in a node written after the content. - // That way, the arrow will be an overlay on the content. - const phantom = new MathNode("mphantom", [buildGroup$1(group.body, style)]); - const arrow = new MathNode("mrow", [phantom], ["tml-cancelto"]); - arrow.style.color = style.color; - if (group.isCharacterBox && smalls.indexOf(group.body.body[0].text) > -1) { - arrow.style.left = "0.1em"; - arrow.style.width = "90%"; - } - const node = new MathNode("mrow", [fromNode, arrow], ["menclose"]); - if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) { - // Add 0.2em space to right of content to make room for the arrowhead. - phantom.style.paddingRight = "0.2em"; - } else { - phantom.style.padding = "0.5ex 0.1em 0 0"; - const strut = new MathNode('mspace', []); - strut.setAttribute('height', "0.85em"); - fromNode.children.push(strut); - } - - // Create the "to" value above and to the right of the arrow. - // First, we want a dummy node with the same height as the `from` content. - // We'll place the `to` node above the dummy to get the correct vertical alignment. - let dummyNode; - if (group.isCharacterBox) { - dummyNode = new MathNode('mspace', []); - dummyNode.setAttribute('height', "1em"); - } else { - // Create a phantom node with the same content as the body. - const inner = buildGroup$1(group.body, style); - // The phantom node will be zero-width, so it won't affect horizontal spacing. - const zeroWidthNode = new MathNode("mpadded", [inner]); - zeroWidthNode.setAttribute("width", "0.1px"); // Don't use 0. WebKit would omit it. - dummyNode = new MathNode("mphantom", [zeroWidthNode]); // Hide it. - } - const toNode = buildGroup$1(group.to, style); - toNode.style.color = style.color; - const zeroWidthToNode = new MathNode("mpadded", [toNode]); - if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) { - const w = new MathNode("mspace", []); - w.setAttribute('width', "0.2em"); - zeroWidthToNode.children.unshift(w); - } - zeroWidthToNode.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - const mover = new MathNode("mover", [dummyNode, zeroWidthToNode]); - // Fix Firefox positioning. - const nudgeLeft = new MathNode('mrow', [], ["ff-nudge-left"]); - return newDocumentFragment([makeRow([node, mover]), nudgeLeft]) - } - }); - - // \@char is an internal function that takes a grouped decimal argument like - // {123} and converts into symbol with code 123. It is used by the *macro* - // \char defined in macros.js. - defineFunction({ - type: "textord", - names: ["\\@char"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser, token }, args) { - const arg = assertNodeType(args[0], "ordgroup"); - const group = arg.body; - let number = ""; - for (let i = 0; i < group.length; i++) { - const node = assertNodeType(group[i], "textord"); - number += node.text; - } - const code = parseInt(number); - if (isNaN(code)) { - throw new ParseError(`\\@char has non-numeric argument ${number}`, token) - } - return { - type: "textord", - mode: parser.mode, - text: String.fromCodePoint(code) - } - } - }); - - // Helpers - const htmlRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6})$/i; - const htmlOrNameRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i; - const RGBregEx = /^ *\d{1,3} *(?:, *\d{1,3} *){2}$/; - const rgbRegEx = /^ *[10](?:\.\d*)? *(?:, *[10](?:\.\d*)? *){2}$/; - const xcolorHtmlRegEx = /^[a-f0-9]{6}$/i; - const toHex = num => { - let str = num.toString(16); - if (str.length === 1) { str = "0" + str; } - return str - }; - - // Colors from Tables 4.1 and 4.2 of the xcolor package. - // Table 4.1 (lower case) RGB values are taken from chroma and xcolor.dtx. - // Table 4.2 (Capitalized) values were sampled, because Chroma contains a unreliable - // conversion from cmyk to RGB. See https://tex.stackexchange.com/a/537274. - const xcolors = JSON.parse(`{ - "Apricot": "#ffb484", - "Aquamarine": "#08b4bc", - "Bittersweet": "#c84c14", - "blue": "#0000FF", - "Blue": "#303494", - "BlueGreen": "#08b4bc", - "BlueViolet": "#503c94", - "BrickRed": "#b8341c", - "brown": "#BF8040", - "Brown": "#802404", - "BurntOrange": "#f8941c", - "CadetBlue": "#78749c", - "CarnationPink": "#f884b4", - "Cerulean": "#08a4e4", - "CornflowerBlue": "#40ace4", - "cyan": "#00FFFF", - "Cyan": "#08acec", - "Dandelion": "#ffbc44", - "darkgray": "#404040", - "DarkOrchid": "#a8548c", - "Emerald": "#08ac9c", - "ForestGreen": "#089c54", - "Fuchsia": "#90348c", - "Goldenrod": "#ffdc44", - "gray": "#808080", - "Gray": "#98949c", - "green": "#00FF00", - "Green": "#08a44c", - "GreenYellow": "#e0e474", - "JungleGreen": "#08ac9c", - "Lavender": "#f89cc4", - "lightgray": "#c0c0c0", - "lime": "#BFFF00", - "LimeGreen": "#90c43c", - "magenta": "#FF00FF", - "Magenta": "#f0048c", - "Mahogany": "#b0341c", - "Maroon": "#b03434", - "Melon": "#f89c7c", - "MidnightBlue": "#086494", - "Mulberry": "#b03c94", - "NavyBlue": "#086cbc", - "olive": "#7F7F00", - "OliveGreen": "#407c34", - "orange": "#FF8000", - "Orange": "#f8843c", - "OrangeRed": "#f0145c", - "Orchid": "#b074ac", - "Peach": "#f8945c", - "Periwinkle": "#8074bc", - "PineGreen": "#088c74", - "pink": "#ff7f7f", - "Plum": "#98248c", - "ProcessBlue": "#08b4ec", - "purple": "#BF0040", - "Purple": "#a0449c", - "RawSienna": "#983c04", - "red": "#ff0000", - "Red": "#f01c24", - "RedOrange": "#f86434", - "RedViolet": "#a0246c", - "Rhodamine": "#f0549c", - "Royallue": "#0874bc", - "RoyalPurple": "#683c9c", - "RubineRed": "#f0047c", - "Salmon": "#f8948c", - "SeaGreen": "#30bc9c", - "Sepia": "#701404", - "SkyBlue": "#48c4dc", - "SpringGreen": "#c8dc64", - "Tan": "#e09c74", - "teal": "#007F7F", - "TealBlue": "#08acb4", - "Thistle": "#d884b4", - "Turquoise": "#08b4cc", - "violet": "#800080", - "Violet": "#60449c", - "VioletRed": "#f054a4", - "WildStrawberry": "#f0246c", - "yellow": "#FFFF00", - "Yellow": "#fff404", - "YellowGreen": "#98cc6c", - "YellowOrange": "#ffa41c" -}`); - - const colorFromSpec = (model, spec) => { - let color = ""; - if (model === "HTML") { - if (!htmlRegEx.test(spec)) { - throw new ParseError("Invalid HTML input.") - } - color = spec; - } else if (model === "RGB") { - if (!RGBregEx.test(spec)) { - throw new ParseError("Invalid RGB input.") - } - spec.split(",").map(e => { color += toHex(Number(e.trim())); }); - } else { - if (!rgbRegEx.test(spec)) { - throw new ParseError("Invalid rbg input.") - } - spec.split(",").map(e => { - const num = Number(e.trim()); - if (num > 1) { throw new ParseError("Color rgb input must be < 1.") } - color += toHex(Number((num * 255).toFixed(0))); - }); - } - if (color.charAt(0) !== "#") { color = "#" + color; } - return color - }; - - const validateColor = (color, macros, token) => { - const macroName = `\\\\color@${color}`; // from \defineColor. - const match = htmlOrNameRegEx.exec(color); - if (!match) { throw new ParseError("Invalid color: '" + color + "'", token) } - // We allow a 6-digit HTML color spec without a leading "#". - // This follows the xcolor package's HTML color model. - // Predefined color names are all missed by this RegEx pattern. - if (xcolorHtmlRegEx.test(color)) { - return "#" + color - } else if (color.charAt(0) === "#") { - return color - } else if (macros.has(macroName)) { - color = macros.get(macroName).tokens[0].text; - } else if (xcolors[color]) { - color = xcolors[color]; - } - return color - }; - - const mathmlBuilder$8 = (group, style) => { - // In LaTeX, color is not supposed to change the spacing of any node. - // So instead of wrapping the group in an , we apply - // the color individually to each node and return a document fragment. - let expr = buildExpression(group.body, style.withColor(group.color)); - if (expr.length === 0) { - expr.push(new MathNode("mrow")); - } - expr = expr.map(e => { - e.style.color = group.color; - return e - }); - return newDocumentFragment(expr) - }; - - defineFunction({ - type: "color", - names: ["\\textcolor"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "original"] - }, - handler({ parser, token }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); - } - const body = args[1]; - return { - type: "color", - mode: parser.mode, - color, - isTextColor: true, - body: ordargument(body) - } - }, - mathmlBuilder: mathmlBuilder$8 - }); - - defineFunction({ - type: "color", - names: ["\\color"], - props: { - numArgs: 1, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw"] - }, - handler({ parser, breakOnTokenText, token }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); - } - - // Parse out the implicit body that should be colored. - const body = parser.parseExpression(true, breakOnTokenText, true); - - return { - type: "color", - mode: parser.mode, - color, - isTextColor: false, - body - } - }, - mathmlBuilder: mathmlBuilder$8 - }); - - defineFunction({ - type: "color", - names: ["\\definecolor"], - props: { - numArgs: 3, - allowedInText: true, - argTypes: ["raw", "raw", "raw"] - }, - handler({ parser, funcName, token }, args) { - const name = assertNodeType(args[0], "raw").string; - if (!/^[A-Za-z]+$/.test(name)) { - throw new ParseError("Color name must be latin letters.", token) - } - const model = assertNodeType(args[1], "raw").string; - if (!["HTML", "RGB", "rgb"].includes(model)) { - throw new ParseError("Color model must be HTML, RGB, or rgb.", token) - } - const spec = assertNodeType(args[2], "raw").string; - const color = colorFromSpec(model, spec); - parser.gullet.macros.set(`\\\\color@${name}`, { tokens: [{ text: color }], numArgs: 0 }); - return { type: "internal", mode: parser.mode } - } - // No mathmlBuilder. The point of \definecolor is to set a macro. - }); - - // Row breaks within tabular environments, and line breaks at top level - - - // \DeclareRobustCommand\\{...\@xnewline} - defineFunction({ - type: "cr", - names: ["\\\\"], - props: { - numArgs: 0, - numOptionalArgs: 0, - allowedInText: true - }, - - handler({ parser }, args, optArgs) { - const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null; - const newLine = !parser.settings.displayMode; - return { - type: "cr", - mode: parser.mode, - newLine, - size: size && assertNodeType(size, "size").value - } - }, - - // The following builder is called only at the top level, - // not within tabular/array environments. - - mathmlBuilder(group, style) { - // MathML 3.0 calls for newline to occur in an or an . - // Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.linebreaking - const node = new MathNode("mo"); - if (group.newLine) { - node.setAttribute("linebreak", "newline"); - if (group.size) { - const size = calculateSize(group.size, style); - node.setAttribute("height", size.number + size.unit); - } - } - return node - } - }); - - const globalMap = { - "\\global": "\\global", - "\\long": "\\\\globallong", - "\\\\globallong": "\\\\globallong", - "\\def": "\\gdef", - "\\gdef": "\\gdef", - "\\edef": "\\xdef", - "\\xdef": "\\xdef", - "\\let": "\\\\globallet", - "\\futurelet": "\\\\globalfuture" - }; - - const checkControlSequence = (tok) => { - const name = tok.text; - if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { - throw new ParseError("Expected a control sequence", tok); - } - return name; - }; - - const getRHS = (parser) => { - let tok = parser.gullet.popToken(); - if (tok.text === "=") { - // consume optional equals - tok = parser.gullet.popToken(); - if (tok.text === " ") { - // consume one optional space - tok = parser.gullet.popToken(); - } - } - return tok; - }; - - const letCommand = (parser, name, tok, global) => { - let macro = parser.gullet.macros.get(tok.text); - if (macro == null) { - // don't expand it later even if a macro with the same name is defined - // e.g., \let\foo=\frac \def\frac{\relax} \frac12 - tok.noexpand = true; - macro = { - tokens: [tok], - numArgs: 0, - // reproduce the same behavior in expansion - unexpandable: !parser.gullet.isExpandable(tok.text) - }; - } - parser.gullet.macros.set(name, macro, global); - }; - - // -> | - // -> |\global - // -> | - // -> \global|\long|\outer - defineFunction({ - type: "internal", - names: [ - "\\global", - "\\long", - "\\\\globallong" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true - }, - handler({ parser, funcName }) { - parser.consumeSpaces(); - const token = parser.fetch(); - if (globalMap[token.text]) { - // Temml doesn't have \par, so ignore \long - if (funcName === "\\global" || funcName === "\\\\globallong") { - token.text = globalMap[token.text]; - } - return assertNodeType(parser.parseFunction(), "internal"); - } - throw new ParseError(`Invalid token after macro prefix`, token); - } - }); - - // Basic support for macro definitions: \def, \gdef, \edef, \xdef - // -> - // -> \def|\gdef|\edef|\xdef - // -> - defineFunction({ - type: "internal", - names: ["\\def", "\\gdef", "\\edef", "\\xdef"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - let tok = parser.gullet.popToken(); - const name = tok.text; - if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { - throw new ParseError("Expected a control sequence", tok); - } - - let numArgs = 0; - let insert; - const delimiters = [[]]; - // contains no braces - while (parser.gullet.future().text !== "{") { - tok = parser.gullet.popToken(); - if (tok.text === "#") { - // If the very last character of the is #, so that - // this # is immediately followed by {, TeX will behave as if the { - // had been inserted at the right end of both the parameter text - // and the replacement text. - if (parser.gullet.future().text === "{") { - insert = parser.gullet.future(); - delimiters[numArgs].push("{"); - break; - } - - // A parameter, the first appearance of # must be followed by 1, - // the next by 2, and so on; up to nine #’s are allowed - tok = parser.gullet.popToken(); - if (!/^[1-9]$/.test(tok.text)) { - throw new ParseError(`Invalid argument number "${tok.text}"`); - } - if (parseInt(tok.text) !== numArgs + 1) { - throw new ParseError(`Argument number "${tok.text}" out of order`); - } - numArgs++; - delimiters.push([]); - } else if (tok.text === "EOF") { - throw new ParseError("Expected a macro definition"); - } else { - delimiters[numArgs].push(tok.text); - } - } - // replacement text, enclosed in '{' and '}' and properly nested - let { tokens } = parser.gullet.consumeArg(); - if (insert) { - tokens.unshift(insert); - } - - if (funcName === "\\edef" || funcName === "\\xdef") { - tokens = parser.gullet.expandTokens(tokens); - if (tokens.length > parser.gullet.settings.maxExpand) { - throw new ParseError("Too many expansions in an " + funcName); - } - tokens.reverse(); // to fit in with stack order - } - // Final arg is the expansion of the macro - parser.gullet.macros.set( - name, - { tokens, numArgs, delimiters }, - funcName === globalMap[funcName] - ); - return { type: "internal", mode: parser.mode }; - } - }); - - // -> - // -> \futurelet - // | \let - // -> |= - defineFunction({ - type: "internal", - names: [ - "\\let", - "\\\\globallet" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - const name = checkControlSequence(parser.gullet.popToken()); - parser.gullet.consumeSpaces(); - const tok = getRHS(parser); - letCommand(parser, name, tok, funcName === "\\\\globallet"); - return { type: "internal", mode: parser.mode }; - } - }); - - // ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf - defineFunction({ - type: "internal", - names: [ - "\\futurelet", - "\\\\globalfuture" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - const name = checkControlSequence(parser.gullet.popToken()); - const middle = parser.gullet.popToken(); - const tok = parser.gullet.popToken(); - letCommand(parser, name, tok, funcName === "\\\\globalfuture"); - parser.gullet.pushToken(tok); - parser.gullet.pushToken(middle); - return { type: "internal", mode: parser.mode }; - } - }); - - defineFunction({ - type: "internal", - names: ["\\newcommand", "\\renewcommand", "\\providecommand"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - let name = ""; - const tok = parser.gullet.popToken(); - if (tok.text === "{") { - name = checkControlSequence(parser.gullet.popToken()); - parser.gullet.popToken(); - } else { - name = checkControlSequence(tok); - } - - const exists = parser.gullet.isDefined(name); - if (exists && funcName === "\\newcommand") { - throw new ParseError( - `\\newcommand{${name}} attempting to redefine ${name}; use \\renewcommand` - ); - } - if (!exists && funcName === "\\renewcommand") { - throw new ParseError( - `\\renewcommand{${name}} when command ${name} does not yet exist; use \\newcommand` - ); - } - - let numArgs = 0; - if (parser.gullet.future().text === "[") { - let tok = parser.gullet.popToken(); - tok = parser.gullet.popToken(); - if (!/^[0-9]$/.test(tok.text)) { - throw new ParseError(`Invalid number of arguments: "${tok.text}"`); - } - numArgs = parseInt(tok.text); - tok = parser.gullet.popToken(); - if (tok.text !== "]") { - throw new ParseError(`Invalid argument "${tok.text}"`); - } - } - - // replacement text, enclosed in '{' and '}' and properly nested - const { tokens } = parser.gullet.consumeArg(); - - if (!(funcName === "\\providecommand" && parser.gullet.macros.has(name))) { - // Ignore \providecommand - parser.gullet.macros.set( - name, - { tokens, numArgs } - ); - } - - return { type: "internal", mode: parser.mode }; - - } - }); - - // Extra data needed for the delimiter handler down below - const delimiterSizes = { - "\\bigl": { mclass: "mopen", size: 1 }, - "\\Bigl": { mclass: "mopen", size: 2 }, - "\\biggl": { mclass: "mopen", size: 3 }, - "\\Biggl": { mclass: "mopen", size: 4 }, - "\\bigr": { mclass: "mclose", size: 1 }, - "\\Bigr": { mclass: "mclose", size: 2 }, - "\\biggr": { mclass: "mclose", size: 3 }, - "\\Biggr": { mclass: "mclose", size: 4 }, - "\\bigm": { mclass: "mrel", size: 1 }, - "\\Bigm": { mclass: "mrel", size: 2 }, - "\\biggm": { mclass: "mrel", size: 3 }, - "\\Biggm": { mclass: "mrel", size: 4 }, - "\\big": { mclass: "mord", size: 1 }, - "\\Big": { mclass: "mord", size: 2 }, - "\\bigg": { mclass: "mord", size: 3 }, - "\\Bigg": { mclass: "mord", size: 4 } - }; - - const leftToRight = { - "(": ")", - "\\lparen": "\\rparen", - "[": "]", - "\\lbrack": "\\rbrack", - "\\{": "\\}", - "\\lbrace": "\\rbrace", - "⦇": "⦈", - "\\llparenthesis": "\\rrparenthesis", - "\\lfloor": "\\rfloor", - "\u230a": "\u230b", - "\\lceil": "\\rceil", - "\u2308": "\u2309", - "\\langle": "\\rangle", - "\u27e8": "\u27e9", - "\\lAngle": "\\rAngle", - "\u27ea": "\u27eb", - "\\llangle": "\\rrangle", - "⦉": "⦊", - "\\lvert": "\\rvert", - "\\lVert": "\\rVert", - "\\lgroup": "\\rgroup", - "\u27ee": "\u27ef", - "\\lmoustache": "\\rmoustache", - "\u23b0": "\u23b1", - "\\llbracket": "\\rrbracket", - "\u27e6": "\u27e7", - "\\lBrace": "\\rBrace", - "\u2983": "\u2984" - }; - - const leftDelimiterNames = new Set(Object.keys(leftToRight)); - new Set(Object.values(leftToRight)); - - const delimiters = new Set([ - "(", - "\\lparen", - ")", - "\\rparen", - "[", - "\\lbrack", - "]", - "\\rbrack", - "\\{", - "\\lbrace", - "\\}", - "\\rbrace", - "⦇", - "\\llparenthesis", - "⦈", - "\\rrparenthesis", - "\\lfloor", - "\\rfloor", - "\u230a", - "\u230b", - "\\lceil", - "\\rceil", - "\u2308", - "\u2309", - "<", - ">", - "\\langle", - "\u27e8", - "\\rangle", - "\u27e9", - "\\lAngle", - "\u27ea", - "\\rAngle", - "\u27eb", - "\\llangle", - "⦉", - "\\rrangle", - "⦊", - "\\lt", - "\\gt", - "\\lvert", - "\\rvert", - "\\lVert", - "\\rVert", - "\\lgroup", - "\\rgroup", - "\u27ee", - "\u27ef", - "\\lmoustache", - "\\rmoustache", - "\u23b0", - "\u23b1", - "\\llbracket", - "\\rrbracket", - "\u27e6", - "\u27e7", - "\\lBrace", - "\\rBrace", - "\u2983", - "\u2984", - "/", - "\\backslash", - "|", - "\\vert", - "\\|", - "\\Vert", - "\u2016", - "\\uparrow", - "\\Uparrow", - "\\downarrow", - "\\Downarrow", - "\\updownarrow", - "\\Updownarrow", - "." - ]); - - // Export isDelimiter for benefit of parser. - const dels = new Set(["}", "\\left", "\\middle", "\\right"]); - const isDelimiter = str => str.length > 0 && - (delimiters.has(str) || delimiterSizes[str] || dels.has(str)); - - // Metrics of the different sizes. Found by looking at TeX's output of - // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$ - // Used to create stacked delimiters of appropriate sizes in makeSizedDelim. - const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; - - // Delimiter functions - function checkDelimiter(delim, context) { - if (delim.type === "ordgroup" && delim.body.length === 1) { - delim = delim.body[0]; // Unwrap the braces - } - const symDelim = checkSymbolNodeType(delim); - if (symDelim && delimiters.has(symDelim.text)) { - // If a character is not in the MathML operator dictionary, it will not stretch. - // Replace such characters w/characters that will stretch. - if (symDelim.text === "<" || symDelim.text === "\\lt") { symDelim.text = "⟨"; } - if (symDelim.text === ">" || symDelim.text === "\\gt") { symDelim.text = "⟩"; } - return symDelim; - } else if (symDelim) { - throw new ParseError(`Invalid delimiter '${symDelim.text}' after '${context.funcName}'`, delim); - } else { - throw new ParseError(`Invalid delimiter type '${delim.type}'`, delim); - } - } - - // / \ - const needExplicitStretch = new Set(["\u002F", "\u005C", "\\backslash", "\u2216", "\\vert", "|"]); - - const makeFenceMo = (delim, mode, form, isStretchy) => { - const text = delim === "." ? "" : delim; - const node = new MathNode("mo", [makeText(text, mode)]); - node.setAttribute("fence", "true"); - node.setAttribute("form", form); - node.setAttribute("stretchy", isStretchy ? "true" : "false"); - return node; - }; - - defineFunction({ - type: "delimsizing", - names: [ - "\\bigl", - "\\Bigl", - "\\biggl", - "\\Biggl", - "\\bigr", - "\\Bigr", - "\\biggr", - "\\Biggr", - "\\bigm", - "\\Bigm", - "\\biggm", - "\\Biggm", - "\\big", - "\\Big", - "\\bigg", - "\\Bigg" - ], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - - const delimNode = { - type: "delimsizing", - mode: context.parser.mode, - size: delimiterSizes[context.funcName].size, - mclass: delimiterSizes[context.funcName].mclass, - delim: delim.text - }; - const nextToken = context.parser.fetch().text; - if (nextToken !== "^" && nextToken !== "_") { - return delimNode - } else { - // Chromium mis-renders a sized delim if it is the base of a supsub. - // So wrap it in a ordgroup. - return { - type: "ordgroup", - mode: "math", - body: [delimNode, { type: "ordgroup", mode: "math", body: [] }] - } - } - }, - mathmlBuilder: (group) => { - const children = []; - const delim = group.delim === "." ? "" : group.delim; - - children.push(makeText(delim, group.mode)); - - const node = new MathNode("mo", children); - - if (group.mclass === "mopen" || group.mclass === "mclose") { - // Only some of the delimsizing functions act as fences, and they - // return "mopen" or "mclose" mclass. - node.setAttribute("fence", "true"); - } else { - // Explicitly disable fencing if it's not a fence, to override the - // defaults. - node.setAttribute("fence", "false"); - } - if (needExplicitStretch.has(delim) || delim.indexOf("arrow") > -1) { - // We have to explicitly set stretchy to true. - node.setAttribute("stretchy", "true"); - } - node.setAttribute("symmetric", "true"); // Needed for tall arrows in Firefox. - node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em"); - node.setAttribute("maxsize", sizeToMaxHeight[group.size] + "em"); - return node; - } - }); - - function assertParsed(group) { - if (!group.body) { - throw new Error("Bug: The delim ParseNode wasn't fully parsed."); - } - } - - defineFunction({ - type: "leftright-right", - names: ["\\right"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - return { - type: "leftright-right", - mode: context.parser.mode, - delim: checkDelimiter(args[0], context).text - }; - } - }); - - defineFunction({ - type: "leftright", - names: ["\\left"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - - const parser = context.parser; - ++parser.leftrightDepth; - let body = parser.parseExpression(false, "\\right", true); - let nextToken = parser.fetch(); - while (nextToken.text === "\\middle") { - parser.consume(); - const middle = parser.fetch().text; - if (!symbols.math[middle]) { - throw new ParseError(`Invalid delimiter '${middle}' after '\\middle'`); - } - checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" }); - body.push({ type: "middle", mode: "math", delim: middle }); - parser.consume(); - body = body.concat(parser.parseExpression(false, "\\right", true)); - nextToken = parser.fetch(); - } - --parser.leftrightDepth; - parser.expect("\\right", false); - const right = assertNodeType(parser.parseFunction(), "leftright-right"); - return { - type: "leftright", - mode: parser.mode, - body, - left: delim.text, - right: right.delim, - isStretchy: true - }; - }, - mathmlBuilder: (group, style) => { - assertParsed(group); - const inner = buildExpression(group.body, style); - - const leftNode = makeFenceMo(group.left, group.mode, "prefix", true); - inner.unshift(leftNode); - - const rightNode = makeFenceMo(group.right, group.mode, "postfix", true); - if (group.body.length > 0) { - const lastElement = group.body[group.body.length - 1]; - if (lastElement.type === "color" && !lastElement.isTextColor) { - rightNode.setAttribute("mathcolor", lastElement.color); - } - } - inner.push(rightNode); - - return makeRow(inner); - } - }); - - defineFunction({ - type: "delimiter", - names: Array.from(leftDelimiterNames), - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: true, - allowedInArgument: true - }, - handler: ({ parser, funcName, token }) => { - if (parser.mode === "text") { - return { - type: "textord", - mode: "text", - text: funcName, - loc: token.loc - } - } else if (!parser.settings.wrapDelimiterPairs) { - // Treat this token as an ordinary symbol. - return { - type: "atom", - mode: "math", - family: "open", - loc: token.loc, - text: funcName - }; - } - // Otherwise, try to wrap a pair of delimiters with an . - const rightDelim = leftToRight[funcName]; - // Parse the inner expression, looking for the corresponding right delimiter. - const body = parser.parseExpression(false, rightDelim, false); - const nextToken = parser.fetch().text; - - if (nextToken !== rightDelim) { - // We were unable to find a matching right delimiter. - // Throw control back to renderToMathMLTree. - // It will reparse the entire expression with wrapDelimiterPairs set to false. - throw new ParseError("Unmatched delimiter"); - } - parser.consume(); - - return { - type: "delimiter", - mode: parser.mode, - body, - left: funcName, - right: rightDelim - }; - }, - mathmlBuilder: (group, style) => { - assertParsed(group); - const inner = buildExpression(group.body, style); - - const leftNode = makeFenceMo(group.left, group.mode, "prefix", false); - inner.unshift(leftNode); - - const rightNode = makeFenceMo(group.right, group.mode, "postfix", false); - if (group.body.length > 0) { - const lastElement = group.body[group.body.length - 1]; - if (lastElement.type === "color" && !lastElement.isTextColor) { - rightNode.setAttribute("mathcolor", lastElement.color); - } - } - inner.push(rightNode); - - return makeRow(inner); - } - }); - - defineFunction({ - type: "middle", - names: ["\\middle"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - if (!context.parser.leftrightDepth) { - throw new ParseError("\\middle without preceding \\left", delim); - } - - return { - type: "middle", - mode: context.parser.mode, - delim: delim.text - }; - }, - mathmlBuilder: (group) => { - const textNode = makeText(group.delim, group.mode); - const middleNode = new MathNode("mo", [textNode]); - middleNode.setAttribute("fence", "true"); - if (group.delim.indexOf("arrow") > -1) { - middleNode.setAttribute("stretchy", "true"); - } - // The next line is not semantically correct, but - // Chromium fails to stretch if it is not there. - middleNode.setAttribute("form", "prefix"); - // MathML gives 5/18em spacing to each element. - // \middle should get delimiter spacing instead. - middleNode.setAttribute("lspace", "0.05em"); - middleNode.setAttribute("rspace", "0.05em"); - return middleNode; - } - }); - - const boxTags = ["\\boxed", "\\fcolorbox", "\\colorbox"]; - - const mathmlBuilder$7 = (group, style) => { - const tag = boxTags.includes(group.label) ? "mrow" : "menclose"; - const node = new MathNode(tag, [buildGroup$1(group.body, style)]); - switch (group.label) { - case "\\overline": - node.setAttribute("notation", "top"); // for Firefox & WebKit - node.classes.push("tml-overline"); // for Chromium - break - case "\\underline": - node.setAttribute("notation", "bottom"); - node.classes.push("tml-underline"); - break - case "\\cancel": - node.setAttribute("notation", "updiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "upstrike"])); - break - case "\\bcancel": - node.setAttribute("notation", "downdiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "downstrike"])); - break - case "\\sout": - node.setAttribute("notation", "horizontalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "sout"])); - break - case "\\xcancel": - node.setAttribute("notation", "updiagonalstrike downdiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "tml-xcancel"])); - break - // cancelto is handled in cancelto.js - case "\\longdiv": - node.setAttribute("notation", "longdiv"); - node.classes.push("longdiv-top"); - node.children.push(new MathNode("mrow", [], ["longdiv-arc"])); - break - case "\\phase": - node.setAttribute("notation", "phasorangle"); - node.classes.push("phasor-bottom"); - node.children.push(new MathNode("mrow", [], ["phasor-angle"])); - break - case "\\textcircled": - node.setAttribute("notation", "circle"); - node.classes.push("circle-pad"); - node.children.push(new MathNode("mrow", [], ["textcircle"])); - break - case "\\angl": - node.setAttribute("notation", "actuarial"); - node.classes.push("actuarial"); - break - case "\\boxed": - // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty - node.style.padding = "3pt"; - node.style.border = "1px solid"; - node.setAttribute("scriptlevel", "0"); - node.setAttribute("displaystyle", "true"); - break - case "\\fbox": - node.setAttribute("notation", "box"); - node.classes.push("tml-fbox"); - break - case "\\fcolorbox": - case "\\colorbox": { - // Don't use . WebKit would show a radical. - node.style.padding = "0.3em"; // 3 pt from LaTeX source2e for a 10pt font - if (group.label === "\\fcolorbox") { - node.style.border = "0.0667em solid " + String(group.borderColor); - } - break - } - } - if (group.backgroundColor) { - node.setAttribute("mathbackground", group.backgroundColor); - } - return node; - }; - - defineFunction({ - type: "enclose", - names: ["\\colorbox"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "text"] - }, - handler({ parser, funcName }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); - } - const body = args[1]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - backgroundColor: color, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 - }); - - defineFunction({ - type: "enclose", - names: ["\\fcolorbox"], - props: { - numArgs: 3, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "raw", "text"] - }, - handler({ parser, funcName }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let borderColor = ""; - let backgroundColor; - if (model) { - const borderSpec = assertNodeType(args[0], "raw").string; - const backgroundSpec = assertNodeType(args[0], "raw").string; - borderColor = colorFromSpec(model, borderSpec); - backgroundColor = colorFromSpec(model, backgroundSpec); - } else { - borderColor = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); - backgroundColor = validateColor(assertNodeType(args[1], "raw").string, parser.gullet.macros); - } - const body = args[2]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - backgroundColor, - borderColor, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 - }); - - defineFunction({ - type: "enclose", - names: ["\\fbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "enclose", - mode: parser.mode, - label: "\\fbox", - body: args[0] - }; - } - }); - - defineFunction({ - type: "enclose", - names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", - "\\boxed", "\\longdiv", "\\phase"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 - }); - - defineFunction({ - type: "enclose", - names: ["\\underline"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 - }); - - - defineFunction({ - type: "enclose", - names: ["\\textcircled"], - props: { - numArgs: 1, - argTypes: ["text"], - allowedInArgument: true, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 - }); - - // Environment delimiters. HTML/MathML rendering is defined in the corresponding - // defineEnvironment definitions. - defineFunction({ - type: "environment", - names: ["\\begin", "\\end"], - props: { - numArgs: 1, - argTypes: ["text"] - }, - handler({ parser, funcName }, args) { - const nameGroup = args[0]; - if (nameGroup.type !== "ordgroup") { - throw new ParseError("Invalid environment name", nameGroup); - } - let envName = ""; - for (let i = 0; i < nameGroup.body.length; ++i) { - envName += assertNodeType(nameGroup.body[i], "textord").text; - } - - if (funcName === "\\begin") { - // begin...end is similar to left...right - if (!Object.prototype.hasOwnProperty.call(environments, envName )) { - throw new ParseError("No such environment: " + envName, nameGroup); - } - // Build the environment object. Arguments and other information will - // be made available to the begin and end methods using properties. - const env = environments[envName]; - const { args, optArgs } = parser.parseArguments("\\begin{" + envName + "}", env); - const context = { - mode: parser.mode, - envName, - parser - }; - const result = env.handler(context, args, optArgs); - parser.expect("\\end", false); - const endNameToken = parser.nextToken; - const end = assertNodeType(parser.parseFunction(), "environment"); - if (end.name !== envName) { - throw new ParseError( - `Mismatch: \\begin{${envName}} matched by \\end{${end.name}}`, - endNameToken - ); - } - return result; - } - - return { - type: "environment", - mode: parser.mode, - name: envName, - nameGroup - }; - } - }); - - defineFunction({ - type: "envTag", - names: ["\\env@tag"], - props: { - numArgs: 1, - argTypes: ["math"] - }, - handler({ parser }, args) { - return { - type: "envTag", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow"); - } - }); - - defineFunction({ - type: "noTag", - names: ["\\env@notag"], - props: { - numArgs: 0 - }, - handler({ parser }) { - return { - type: "noTag", - mode: parser.mode - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow"); - } - }); - - const isLongVariableName = (group, font) => { - if (font !== "mathrm" || group.body.type !== "ordgroup" || group.body.body.length === 1) { - return false - } - if (group.body.body[0].type !== "mathord") { return false } - for (let i = 1; i < group.body.body.length; i++) { - const parseNodeType = group.body.body[i].type; - if (!(parseNodeType === "mathord" || - (parseNodeType === "textord" && !isNaN(group.body.body[i].text)))) { - return false - } - } - return true - }; - - const mathmlBuilder$6 = (group, style) => { - const font = group.font; - const newStyle = style.withFont(font); - const mathGroup = buildGroup$1(group.body, newStyle); - - if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{} - if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) { - mathGroup.style.fontWeight = "bold"; - return mathGroup - } - // Check if it is possible to consolidate elements into a single element. - if (isLongVariableName(group, font)) { - // This is a \mathrm{…} group. It gets special treatment because symbolsOrd.js - // wraps elements with s to work around a Firefox bug. - const mi = mathGroup.children[0].children[0].children - ? mathGroup.children[0].children[0] - : mathGroup.children[0]; - delete mi.attributes.mathvariant; - for (let i = 1; i < mathGroup.children.length; i++) { - mi.children[0].text += mathGroup.children[i].children[0].children - ? mathGroup.children[i].children[0].children[0].text - : mathGroup.children[i].children[0].text; - } - // Wrap in a to prevent the same Firefox bug. - const mpadded = new MathNode("mpadded", [mi]); - mpadded.setAttribute("lspace", "0"); - return mpadded - } - let canConsolidate = mathGroup.children[0].type === "mo"; - for (let i = 1; i < mathGroup.children.length; i++) { - if (mathGroup.children[i].type === "mo" && font === "boldsymbol") { - mathGroup.children[i].style.fontWeight = "bold"; - } - if (mathGroup.children[i].type !== "mi") { canConsolidate = false; } - const localVariant = mathGroup.children[i].attributes && - mathGroup.children[i].attributes.mathvariant || ""; - if (localVariant !== "normal") { canConsolidate = false; } - } - if (!canConsolidate) { return mathGroup } - // Consolidate the elements. - const mi = mathGroup.children[0]; - for (let i = 1; i < mathGroup.children.length; i++) { - mi.children.push(mathGroup.children[i].children[0]); - } - if (mi.attributes.mathvariant && mi.attributes.mathvariant === "normal") { - // Workaround for a Firefox bug that renders spurious space around - // a - // Ref: https://bugs.webkit.org/show_bug.cgi?id=129097 - // We insert a text node that contains a zero-width space and wrap in an mrow. - // TODO: Get rid of this workaround when the Firefox bug is fixed. - const bogus = new MathNode("mtext", new TextNode("\u200b")); - return new MathNode("mrow", [bogus, mi]) - } - return mi - }; - - const fontAliases = { - "\\Bbb": "\\mathbb", - "\\bold": "\\mathbf", - "\\frak": "\\mathfrak", - "\\bm": "\\boldsymbol" - }; - - defineFunction({ - type: "font", - names: [ - // styles - "\\mathrm", - "\\mathit", - "\\mathbf", - "\\mathnormal", - "\\up@greek", - "\\boldsymbol", - - // families - "\\mathbb", - "\\mathcal", - "\\mathfrak", - "\\mathscr", - "\\mathsf", - "\\mathsfit", - "\\mathtt", - - // aliases - "\\Bbb", - "\\bm", - "\\bold", - "\\frak" - ], - props: { - numArgs: 1, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const body = normalizeArgument(args[0]); - let func = funcName; - if (func in fontAliases) { - func = fontAliases[func]; - } - return { - type: "font", - mode: parser.mode, - font: func.slice(1), - body - }; - }, - mathmlBuilder: mathmlBuilder$6 - }); - - // Old font changing functions - defineFunction({ - type: "font", - names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"], - props: { - numArgs: 0, - allowedInText: true - }, - handler: ({ parser, funcName, breakOnTokenText }, args) => { - const { mode } = parser; - const body = parser.parseExpression(true, breakOnTokenText, true); - const fontStyle = `math${funcName.slice(1)}`; - - return { - type: "font", - mode: mode, - font: fontStyle, - body: { - type: "ordgroup", - mode: parser.mode, - body - } - }; - }, - mathmlBuilder: mathmlBuilder$6 - }); - - const stylArray = ["display", "text", "script", "scriptscript"]; - const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 }; - - const adjustStyle = (functionSize, originalStyle) => { - // Figure out what style this fraction should be in based on the - // function used - let style = originalStyle; - if (functionSize === "display") { //\tfrac or \cfrac - // Get display style as a default. - // If incoming style is sub/sup, use style.text() to get correct size. - const newSize = style.level >= StyleLevel.SCRIPT ? StyleLevel.TEXT : StyleLevel.DISPLAY; - style = style.withLevel(newSize); - } else if (functionSize === "text" && - style.level === StyleLevel.DISPLAY) { - // We're in a \tfrac but incoming style is displaystyle, so: - style = style.withLevel(StyleLevel.TEXT); - } else if (functionSize === "auto") { - style = style.incrementLevel(); - } else if (functionSize === "script") { - style = style.withLevel(StyleLevel.SCRIPT); - } else if (functionSize === "scriptscript") { - style = style.withLevel(StyleLevel.SCRIPTSCRIPT); - } - return style; - }; - - const mathmlBuilder$5 = (group, style) => { - style = adjustStyle(group.scriptLevel, style); - - // Chromium (wrongly) continues to shrink fractions beyond scriptscriptlevel. - // So we check for levels that Chromium shrinks too small. - // If necessary, set an explicit fraction depth. - const numer = buildGroup$1(group.numer, style); - const denom = buildGroup$1(group.denom, style); - if (style.level === 3) { - numer.style.mathDepth = "2"; - numer.setAttribute("scriptlevel", "2"); - denom.style.mathDepth = "2"; - denom.setAttribute("scriptlevel", "2"); - } - - let node = new MathNode("mfrac", [numer, denom]); - - if (!group.hasBarLine) { - node.setAttribute("linethickness", "0px"); - } else if (group.barSize) { - const ruleWidth = calculateSize(group.barSize, style); - node.setAttribute("linethickness", ruleWidth.number + ruleWidth.unit); - } - - if (group.leftDelim != null || group.rightDelim != null) { - const withDelims = []; - - if (group.leftDelim != null) { - const leftOp = new MathNode("mo", [ - new TextNode(group.leftDelim.replace("\\", "")) - ]); - leftOp.setAttribute("fence", "true"); - withDelims.push(leftOp); - } - - withDelims.push(node); - - if (group.rightDelim != null) { - const rightOp = new MathNode("mo", [ - new TextNode(group.rightDelim.replace("\\", "")) - ]); - rightOp.setAttribute("fence", "true"); - withDelims.push(rightOp); - } - - node = makeRow(withDelims); - } - - if (group.scriptLevel !== "auto") { - node = new MathNode("mstyle", [node]); - node.setAttribute("displaystyle", String(group.scriptLevel === "display")); - node.setAttribute("scriptlevel", scriptLevel[group.scriptLevel]); - } - - return node; - }; - - defineFunction({ - type: "genfrac", - names: [ - "\\cfrac", - "\\dfrac", - "\\frac", - "\\tfrac", - "\\dbinom", - "\\binom", - "\\tbinom", - "\\\\atopfrac", // can’t be entered directly - "\\\\bracefrac", - "\\\\brackfrac" // ditto - ], - props: { - numArgs: 2, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const numer = args[0]; - const denom = args[1]; - let hasBarLine = false; - let leftDelim = null; - let rightDelim = null; - let scriptLevel = "auto"; - - switch (funcName) { - case "\\cfrac": - case "\\dfrac": - case "\\frac": - case "\\tfrac": - hasBarLine = true; - break; - case "\\\\atopfrac": - hasBarLine = false; - break; - case "\\dbinom": - case "\\binom": - case "\\tbinom": - leftDelim = "("; - rightDelim = ")"; - break; - case "\\\\bracefrac": - leftDelim = "\\{"; - rightDelim = "\\}"; - break; - case "\\\\brackfrac": - leftDelim = "["; - rightDelim = "]"; - break; - default: - throw new Error("Unrecognized genfrac command"); - } - - if (funcName === "\\cfrac" || funcName.startsWith("\\d")) { - scriptLevel = "display"; - } else if (funcName.startsWith("\\t")) { - scriptLevel = "text"; - } - - return { - type: "genfrac", - mode: parser.mode, - continued: false, - numer, - denom, - hasBarLine, - leftDelim, - rightDelim, - scriptLevel, - barSize: null - }; - }, - mathmlBuilder: mathmlBuilder$5 - }); - - // Infix generalized fractions -- these are not rendered directly, but replaced - // immediately by one of the variants above. - defineFunction({ - type: "infix", - names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"], - props: { - numArgs: 0, - infix: true - }, - handler({ parser, funcName, token }) { - let replaceWith; - switch (funcName) { - case "\\over": - replaceWith = "\\frac"; - break; - case "\\choose": - replaceWith = "\\binom"; - break; - case "\\atop": - replaceWith = "\\\\atopfrac"; - break; - case "\\brace": - replaceWith = "\\\\bracefrac"; - break; - case "\\brack": - replaceWith = "\\\\brackfrac"; - break; - default: - throw new Error("Unrecognized infix genfrac command"); - } - return { - type: "infix", - mode: parser.mode, - replaceWith, - token - }; - } - }); - - const delimFromValue = function(delimString) { - let delim = null; - if (delimString.length > 0) { - delim = delimString; - delim = delim === "." ? null : delim; - } - return delim; - }; - - defineFunction({ - type: "genfrac", - names: ["\\genfrac"], - props: { - numArgs: 6, - allowedInArgument: true, - argTypes: ["math", "math", "size", "text", "math", "math"] - }, - handler({ parser }, args) { - const numer = args[4]; - const denom = args[5]; - - // Look into the parse nodes to get the desired delimiters. - const leftNode = normalizeArgument(args[0]); - const leftDelim = leftNode.type === "atom" && leftNode.family === "open" - ? delimFromValue(leftNode.text) - : null; - const rightNode = normalizeArgument(args[1]); - const rightDelim = - rightNode.type === "atom" && rightNode.family === "close" - ? delimFromValue(rightNode.text) - : null; - - const barNode = assertNodeType(args[2], "size"); - let hasBarLine; - let barSize = null; - if (barNode.isBlank) { - // \genfrac acts differently than \above. - // \genfrac treats an empty size group as a signal to use a - // standard bar size. \above would see size = 0 and omit the bar. - hasBarLine = true; - } else { - barSize = barNode.value; - hasBarLine = barSize.number > 0; - } - - // Find out if we want displaystyle, textstyle, etc. - let scriptLevel = "auto"; - let styl = args[3]; - if (styl.type === "ordgroup") { - if (styl.body.length > 0) { - const textOrd = assertNodeType(styl.body[0], "textord"); - scriptLevel = stylArray[Number(textOrd.text)]; - } - } else { - styl = assertNodeType(styl, "textord"); - scriptLevel = stylArray[Number(styl.text)]; - } - - return { - type: "genfrac", - mode: parser.mode, - numer, - denom, - continued: false, - hasBarLine, - barSize, - leftDelim, - rightDelim, - scriptLevel - }; - }, - mathmlBuilder: mathmlBuilder$5 - }); - - // \above is an infix fraction that also defines a fraction bar size. - defineFunction({ - type: "infix", - names: ["\\above"], - props: { - numArgs: 1, - argTypes: ["size"], - infix: true - }, - handler({ parser, funcName, token }, args) { - return { - type: "infix", - mode: parser.mode, - replaceWith: "\\\\abovefrac", - barSize: assertNodeType(args[0], "size").value, - token - }; - } - }); - - defineFunction({ - type: "genfrac", - names: ["\\\\abovefrac"], - props: { - numArgs: 3, - argTypes: ["math", "size", "math"] - }, - handler: ({ parser, funcName }, args) => { - const numer = args[0]; - const barSize = assert(assertNodeType(args[1], "infix").barSize); - const denom = args[2]; - - const hasBarLine = barSize.number > 0; - return { - type: "genfrac", - mode: parser.mode, - numer, - denom, - continued: false, - hasBarLine, - barSize, - leftDelim: null, - rightDelim: null, - scriptLevel: "auto" - }; - }, - - mathmlBuilder: mathmlBuilder$5 - }); - - // \hbox is provided for compatibility with LaTeX functions that act on a box. - // This function by itself doesn't do anything but set scriptlevel to \textstyle - // and prevent a soft line break. - - defineFunction({ - type: "hbox", - names: ["\\hbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInArgument: true, - allowedInText: false - }, - handler({ parser }, args) { - return { - type: "hbox", - mode: parser.mode, - body: ordargument(args[0]) - }; - }, - mathmlBuilder(group, style) { - const newStyle = style.withLevel(StyleLevel.TEXT); - const mrow = buildExpressionRow(group.body, newStyle); - return consolidateText(mrow) - } - }); - - const mathmlBuilder$4 = (group, style) => { - const accentNode = mathMLnode(group.label); - accentNode.style["math-depth"] = 0; - return new MathNode(group.isOver ? "mover" : "munder", [ - buildGroup$1(group.base, style), - accentNode - ]); - }; - - // Horizontal stretchy brackets - defineFunction({ - type: "horizBracket", - names: ["\\overbrace", "\\underbrace", "\\overbracket", "\\underbracket"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - return { - type: "horizBracket", - mode: parser.mode, - label: funcName, - isOver: /^\\over/.test(funcName), - base: args[0] - }; - }, - mathmlBuilder: mathmlBuilder$4 - }); - - defineFunction({ - type: "html", - names: ["\\class", "\\id", "\\style", "\\data"], - props: { - numArgs: 2, - argTypes: ["raw", "original"], - allowedInText: true - }, - handler: ({ parser, funcName, token }, args) => { - const value = assertNodeType(args[0], "raw").string; - const body = args[1]; - - if (parser.settings.strict) { - throw new ParseError(`Function "${funcName}" is disabled in strict mode`, token) - } - - let trustContext; - const attributes = {}; - - switch (funcName) { - case "\\class": - attributes.class = value; - trustContext = { - command: "\\class", - class: value - }; - break; - case "\\id": - attributes.id = value; - trustContext = { - command: "\\id", - id: value - }; - break; - case "\\style": - attributes.style = value; - trustContext = { - command: "\\style", - style: value - }; - break; - case "\\data": { - const data = value.split(","); - for (let i = 0; i < data.length; i++) { - const keyVal = data[i].split("="); - if (keyVal.length !== 2) { - throw new ParseError("Error parsing key-value for \\data"); - } - attributes["data-" + keyVal[0].trim()] = keyVal[1].trim(); - } - - trustContext = { - command: "\\data", - attributes - }; - break; - } - default: - throw new Error("Unrecognized html command"); - } - - if (!parser.settings.isTrusted(trustContext)) { - throw new ParseError(`Function "${funcName}" is not trusted`, token) - } - return { - type: "html", - mode: parser.mode, - attributes, - body: ordargument(body) - }; - }, - mathmlBuilder: (group, style) => { - const element = buildExpressionRow(group.body, style); - - const classes = []; - if (group.attributes.class) { - classes.push(...group.attributes.class.trim().split(/\s+/)); - } - element.classes = classes; - - for (const attr in group.attributes) { - if (attr !== "class" && Object.prototype.hasOwnProperty.call(group.attributes, attr)) { - element.setAttribute(attr, group.attributes[attr]); - } - } - - return element; - } - }); - - const sizeData = function(str) { - if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) { - // str is a number with no unit specified. - // default unit is bp, per graphix package. - return { number: +str, unit: "bp" } - } else { - const match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(str); - if (!match) { - throw new ParseError("Invalid size: '" + str + "' in \\includegraphics"); - } - const data = { - number: +(match[1] + match[2]), // sign + magnitude, cast to number - unit: match[3] - }; - if (!validUnit(data)) { - throw new ParseError("Invalid unit: '" + data.unit + "' in \\includegraphics."); - } - return data - } - }; - - defineFunction({ - type: "includegraphics", - names: ["\\includegraphics"], - props: { - numArgs: 1, - numOptionalArgs: 1, - argTypes: ["raw", "url"], - allowedInText: false - }, - handler: ({ parser, token }, args, optArgs) => { - let width = { number: 0, unit: "em" }; - let height = { number: 0.9, unit: "em" }; // sorta character sized. - let totalheight = { number: 0, unit: "em" }; - let alt = ""; - - if (optArgs[0]) { - const attributeStr = assertNodeType(optArgs[0], "raw").string; - - // Parser.js does not parse key/value pairs. We get a string. - const attributes = attributeStr.split(","); - for (let i = 0; i < attributes.length; i++) { - const keyVal = attributes[i].split("="); - if (keyVal.length === 2) { - const str = keyVal[1].trim(); - switch (keyVal[0].trim()) { - case "alt": - alt = str; - break - case "width": - width = sizeData(str); - break - case "height": - height = sizeData(str); - break - case "totalheight": - totalheight = sizeData(str); - break - default: - throw new ParseError("Invalid key: '" + keyVal[0] + "' in \\includegraphics.") - } - } - } - } - - const src = assertNodeType(args[0], "url").url; - - if (alt === "") { - // No alt given. Use the file name. Strip away the path. - alt = src; - alt = alt.replace(/^.*[\\/]/, ""); - alt = alt.substring(0, alt.lastIndexOf(".")); - } - - if ( - !parser.settings.isTrusted({ - command: "\\includegraphics", - url: src - }) - ) { - throw new ParseError(`Function "\\includegraphics" is not trusted`, token) - } - - return { - type: "includegraphics", - mode: parser.mode, - alt: alt, - width: width, - height: height, - totalheight: totalheight, - src: src - } - }, - mathmlBuilder: (group, style) => { - const height = calculateSize(group.height, style); - const depth = { number: 0, unit: "em" }; - - if (group.totalheight.number > 0) { - if (group.totalheight.unit === height.unit && - group.totalheight.number > height.number) { - depth.number = group.totalheight.number - height.number; - depth.unit = height.unit; - } - } - - let width = 0; - if (group.width.number > 0) { - width = calculateSize(group.width, style); - } - - const graphicStyle = { height: height.number + depth.number + "em" }; - if (width.number > 0) { - graphicStyle.width = width.number + width.unit; - } - if (depth.number > 0) { - graphicStyle.verticalAlign = -depth.number + depth.unit; - } - - const node = new Img(group.src, group.alt, graphicStyle); - node.height = height; - node.depth = depth; - return new MathNode("mtext", [node]) - } - }); - - // Horizontal spacing commands - - - // TODO: \hskip and \mskip should support plus and minus in lengths - - defineFunction({ - type: "kern", - names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"], - props: { - numArgs: 1, - argTypes: ["size"], - primitive: true, - allowedInText: true - }, - handler({ parser, funcName, token }, args) { - const size = assertNodeType(args[0], "size"); - if (parser.settings.strict) { - const mathFunction = funcName[1] === "m"; // \mkern, \mskip - const muUnit = size.value.unit === "mu"; - if (mathFunction) { - if (!muUnit) { - throw new ParseError(`LaTeX's ${funcName} supports only mu units, ` + - `not ${size.value.unit} units`, token) - } - if (parser.mode !== "math") { - throw new ParseError(`LaTeX's ${funcName} works only in math mode`, token) - } - } else { - // !mathFunction - if (muUnit) { - throw new ParseError(`LaTeX's ${funcName} doesn't support mu units`, token) - } - } - } - return { - type: "kern", - mode: parser.mode, - dimension: size.value - }; - }, - mathmlBuilder(group, style) { - const dimension = calculateSize(group.dimension, style); - const ch = dimension.number > 0 && dimension.unit === "em" - ? spaceCharacter(dimension.number) - : ""; - if (group.mode === "text" && ch.length > 0) { - const character = new TextNode(ch); - return new MathNode("mtext", [character]); - } else { - if (dimension.number >= 0) { - const node = new MathNode("mspace"); - node.setAttribute("width", dimension.number + dimension.unit); - return node - } else { - // Don't use or because - // WebKit recognizes negative left margin only on a element - const node = new MathNode("mrow"); - node.style.marginLeft = dimension.number + dimension.unit; - return node - } - } - } - }); - - const spaceCharacter = function(width) { - if (width >= 0.05555 && width <= 0.05556) { - return "\u200a"; //   - } else if (width >= 0.1666 && width <= 0.1667) { - return "\u2009"; //   - } else if (width >= 0.2222 && width <= 0.2223) { - return "\u2005"; //   - } else if (width >= 0.2777 && width <= 0.2778) { - return "\u2005\u200a"; //    - } else { - return ""; - } - }; - - // Limit valid characters to a small set, for safety. - const invalidIdRegEx = /[^A-Za-z_0-9-]/g; - - defineFunction({ - type: "label", - names: ["\\label"], - props: { - numArgs: 1, - argTypes: ["raw"] - }, - handler({ parser }, args) { - return { - type: "label", - mode: parser.mode, - string: args[0].string.replace(invalidIdRegEx, "") - }; - }, - mathmlBuilder(group, style) { - // Return a no-width, no-ink element with an HTML id. - const node = new MathNode("mrow", [], ["tml-label"]); - if (group.string.length > 0) { - node.setLabel(group.string); - } - return node - } - }); - - // Horizontal overlap functions - - const textModeLap = ["\\clap", "\\llap", "\\rlap"]; - - defineFunction({ - type: "lap", - names: ["\\mathllap", "\\mathrlap", "\\mathclap", "\\clap", "\\llap", "\\rlap"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser, funcName, token }, args) => { - if (textModeLap.includes(funcName)) { - if (parser.settings.strict && parser.mode !== "text") { - throw new ParseError(`{${funcName}} can be used only in text mode. - Try \\math${funcName.slice(1)}`, token) - } - funcName = funcName.slice(1); - } else { - funcName = funcName.slice(5); - } - const body = args[0]; - return { - type: "lap", - mode: parser.mode, - alignment: funcName, - body - } - }, - mathmlBuilder: (group, style) => { - // mathllap, mathrlap, mathclap - let strut; - if (group.alignment === "llap") { - // We need an invisible strut with the same depth as the group. - // We can't just read the depth, so we use \vphantom methods. - const phantomInner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", phantomInner); - strut = new MathNode("mpadded", [phantom]); - strut.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - } - - const inner = buildGroup$1(group.body, style); - let node; - if (group.alignment === "llap") { - inner.style.position = "absolute"; - inner.style.right = "0"; - inner.style.bottom = `0`; // If we could have read the ink depth, it would go here. - node = new MathNode("mpadded", [strut, inner]); - } else { - node = new MathNode("mpadded", [inner]); - } - - if (group.alignment === "rlap") { - if (group.body.body.length > 0 && group.body.body[0].type === "genfrac") { - // In Firefox, a squashes the 3/18em padding of a child \frac. Put it back. - node.setAttribute("lspace", "0.16667em"); - } - } else { - const offset = group.alignment === "llap" ? "-1" : "-0.5"; - node.setAttribute("lspace", offset + "width"); - if (group.alignment === "llap") { - node.style.position = "relative"; - } else { - node.style.display = "flex"; - node.style.justifyContent = "center"; - } - } - node.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - return node - } - }); - - // Switching from text mode back to math mode - defineFunction({ - type: "ordgroup", - names: ["\\(", "$"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: false - }, - handler({ funcName, parser }, args) { - const outerMode = parser.mode; - parser.switchMode("math"); - const close = funcName === "\\(" ? "\\)" : "$"; - const body = parser.parseExpression(false, close); - parser.expect(close); - parser.switchMode(outerMode); - return { - type: "ordgroup", - mode: parser.mode, - body - }; - } - }); - - // Check for extra closing math delimiters - defineFunction({ - type: "text", // Doesn't matter what this is. - names: ["\\)", "\\]"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: false - }, - handler(context, token) { - throw new ParseError(`Mismatched ${context.funcName}`, token); - } - }); - - const chooseStyle = (group, style) => { - switch (style.level) { - case StyleLevel.DISPLAY: // 0 - return group.display; - case StyleLevel.TEXT: // 1 - return group.text; - case StyleLevel.SCRIPT: // 2 - return group.script; - case StyleLevel.SCRIPTSCRIPT: // 3 - return group.scriptscript; - default: - return group.text; - } - }; - - defineFunction({ - type: "mathchoice", - names: ["\\mathchoice"], - props: { - numArgs: 4, - primitive: true - }, - handler: ({ parser }, args) => { - return { - type: "mathchoice", - mode: parser.mode, - display: ordargument(args[0]), - text: ordargument(args[1]), - script: ordargument(args[2]), - scriptscript: ordargument(args[3]) - }; - }, - mathmlBuilder: (group, style) => { - const body = chooseStyle(group, style); - return buildExpressionRow(body, style); - } - }); - - const textAtomTypes = ["text", "textord", "mathord", "atom"]; - - function mathmlBuilder$3(group, style) { - let node; - const inner = buildExpression(group.body, style); - - if (group.mclass === "minner") { - node = new MathNode("mpadded", inner); - } else if (group.mclass === "mord") { - if (group.isCharacterBox || inner[0].type === "mathord") { - node = inner[0]; - node.type = "mi"; - if (node.children.length === 1 && node.children[0].text && node.children[0].text === "∇") { - node.setAttribute("mathvariant", "normal"); - } - } else { - node = new MathNode("mi", inner); - } - } else { - node = new MathNode("mrow", inner); - if (group.mustPromote) { - node = inner[0]; - node.type = "mo"; - if (group.isCharacterBox && group.body[0].text && /[A-Za-z]/.test(group.body[0].text)) { - node.setAttribute("mathvariant", "italic"); - } - } else { - node = new MathNode("mrow", inner); - } - - // Set spacing based on what is the most likely adjacent atom type. - // See TeXbook p170. - const doSpacing = style.level < 2; // Operator spacing is zero inside a (sub|super)script. - if (node.type === "mrow") { - if (doSpacing ) { - if (group.mclass === "mbin") { - // medium space - node.children.unshift(padding(0.2222)); - node.children.push(padding(0.2222)); - } else if (group.mclass === "mrel") { - // thickspace - node.children.unshift(padding(0.2778)); - node.children.push(padding(0.2778)); - } else if (group.mclass === "mpunct") { - node.children.push(padding(0.1667)); - } else if (group.mclass === "minner") { - node.children.unshift(padding(0.0556)); // 1 mu is the most likely option - node.children.push(padding(0.0556)); - } - } - } else { - if (group.mclass === "mbin") { - // medium space - node.attributes.lspace = (doSpacing ? "0.2222em" : "0"); - node.attributes.rspace = (doSpacing ? "0.2222em" : "0"); - } else if (group.mclass === "mrel") { - // thickspace - node.attributes.lspace = (doSpacing ? "0.2778em" : "0"); - node.attributes.rspace = (doSpacing ? "0.2778em" : "0"); - } else if (group.mclass === "mpunct") { - node.attributes.lspace = "0em"; - node.attributes.rspace = (doSpacing ? "0.1667em" : "0"); - } else if (group.mclass === "mopen" || group.mclass === "mclose") { - node.attributes.lspace = "0em"; - node.attributes.rspace = "0em"; - } else if (group.mclass === "minner" && doSpacing) { - node.attributes.lspace = "0.0556em"; // 1 mu is the most likely option - node.attributes.width = "+0.1111em"; - } - } - - if (!(group.mclass === "mopen" || group.mclass === "mclose")) { - delete node.attributes.stretchy; - delete node.attributes.form; - } - } - return node; - } - - // Math class commands except \mathop - defineFunction({ - type: "mclass", - names: [ - "\\mathord", - "\\mathbin", - "\\mathrel", - "\\mathopen", - "\\mathclose", - "\\mathpunct", - "\\mathinner" - ], - props: { - numArgs: 1, - primitive: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - const isCharacterBox$1 = isCharacterBox(body); - // We should not wrap a around a or . That would be invalid MathML. - // In that case, we instead promote the text contents of the body to the parent. - let mustPromote = true; - const mord = { type: "mathord", text: "", mode: parser.mode }; - const arr = (body.body) ? body.body : [body]; - for (const arg of arr) { - if (textAtomTypes.includes(arg.type)) { - if (symbols[parser.mode][arg.text]) { - mord.text += symbols[parser.mode][arg.text].replace; - } else if (arg.text) { - mord.text += arg.text; - } else if (arg.body) { - arg.body.map(e => { mord.text += e.text; }); - } - } else { - mustPromote = false; - break - } - } - if (mustPromote && funcName === "\\mathord" && mord.type === "mathord" - && mord.text.length > 1) { - return mord - } else { - return { - type: "mclass", - mode: parser.mode, - mclass: "m" + funcName.slice(5), - body: ordargument(mustPromote ? mord : body), - isCharacterBox: isCharacterBox$1, - mustPromote - }; - } - }, - mathmlBuilder: mathmlBuilder$3 - }); - - const binrelClass = (arg) => { - // \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument. - // (by rendering separately and with {}s before and after, and measuring - // the change in spacing). We'll do roughly the same by detecting the - // atom type directly. - const atom = arg.type === "ordgroup" && arg.body.length && arg.body.length === 1 - ? arg.body[0] - : arg; - if (atom.type === "atom") { - // BIN args are sometimes changed to OPEN, so check the original family. - const family = arg.body.length > 0 && arg.body[0].text && symbols.math[arg.body[0].text] - ? symbols.math[arg.body[0].text].group - : atom.family; - if (family === "bin" || family === "rel") { - return "m" + family; - } else { - return "mord"; - } - } else { - return "mord"; - } - }; - - // \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord. - // This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX. - defineFunction({ - type: "mclass", - names: ["\\@binrel"], - props: { - numArgs: 2 - }, - handler({ parser }, args) { - return { - type: "mclass", - mode: parser.mode, - mclass: binrelClass(args[0]), - body: ordargument(args[1]), - isCharacterBox: isCharacterBox(args[1]) - }; - } - }); - - // Build a relation or stacked op by placing one symbol on top of another - defineFunction({ - type: "mclass", - names: ["\\stackrel", "\\overset", "\\underset"], - props: { - numArgs: 2 - }, - handler({ parser, funcName }, args) { - const baseArg = args[1]; - const shiftedArg = args[0]; - - let mclass; - if (funcName !== "\\stackrel") { - // LaTeX applies \binrel spacing to \overset and \underset. - mclass = binrelClass(baseArg); - } else { - mclass = "mrel"; // for \stackrel - } - - const baseType = mclass === "mrel" || mclass === "mbin" - ? "op" - : "ordgroup"; - - const baseOp = { - type: baseType, - mode: baseArg.mode, - limits: true, - alwaysHandleSupSub: true, - parentIsSupSub: false, - symbol: false, - suppressBaseShift: funcName !== "\\stackrel", - body: ordargument(baseArg) - }; - - return { - type: "supsub", - mode: shiftedArg.mode, - stack: true, - base: baseOp, - sup: funcName === "\\underset" ? null : shiftedArg, - sub: funcName === "\\underset" ? shiftedArg : null - }; - }, - mathmlBuilder: mathmlBuilder$3 - }); - - // Helper function - const buildGroup = (el, style, noneNode) => { - if (!el) { return noneNode } - const node = buildGroup$1(el, style); - if (node.type === "mrow" && node.children.length === 0) { return noneNode } - return node - }; - - defineFunction({ - type: "multiscript", - names: ["\\sideset", "\\pres@cript"], // See macros.js for \prescript - props: { - numArgs: 3 - }, - handler({ parser, funcName, token }, args) { - if (args[2].body.length === 0) { - throw new ParseError(funcName + `cannot parse an empty base.`) - } - const base = args[2].body[0]; - if (parser.settings.strict && funcName === "\\sideset" && !base.symbol) { - throw new ParseError(`The base of \\sideset must be a big operator. Try \\prescript.`) - } - - if ((args[0].body.length > 0 && args[0].body[0].type !== "supsub") || - (args[1].body.length > 0 && args[1].body[0].type !== "supsub")) { - throw new ParseError("\\sideset can parse only subscripts and " + - "superscripts in its first two arguments", token) - } - - // The prescripts and postscripts come wrapped in a supsub. - const prescripts = args[0].body.length > 0 ? args[0].body[0] : null; - const postscripts = args[1].body.length > 0 ? args[1].body[0] : null; - - if (!prescripts && !postscripts) { - return base - } else if (!prescripts) { - // It's not a multi-script. Get a \textstyle supsub. - return { - type: "styling", - mode: parser.mode, - scriptLevel: "text", - body: [{ - type: "supsub", - mode: parser.mode, - base, - sup: postscripts.sup, - sub: postscripts.sub - }] - } - } else { - return { - type: "multiscript", - mode: parser.mode, - isSideset: funcName === "\\sideset", - prescripts, - postscripts, - base - } - } - }, - mathmlBuilder(group, style) { - const base = buildGroup$1(group.base, style); - - const prescriptsNode = new MathNode("mprescripts"); - const noneNode = new MathNode("none"); - let children = []; - - const preSub = buildGroup(group.prescripts.sub, style, noneNode); - const preSup = buildGroup(group.prescripts.sup, style, noneNode); - if (group.isSideset) { - // This seems silly, but LaTeX does this. Firefox ignores it, which does not make me sad. - preSub.setAttribute("style", "text-align: left;"); - preSup.setAttribute("style", "text-align: left;"); - } - - if (group.postscripts) { - const postSub = buildGroup(group.postscripts.sub, style, noneNode); - const postSup = buildGroup(group.postscripts.sup, style, noneNode); - children = [base, postSub, postSup, prescriptsNode, preSub, preSup]; - } else { - children = [base, prescriptsNode, preSub, preSup]; - } - - return new MathNode("mmultiscripts", children); - } - }); - - defineFunction({ - type: "not", - names: ["\\not"], - props: { - numArgs: 1, - primitive: true, - allowedInText: false - }, - handler({ parser }, args) { - const isCharacterBox$1 = isCharacterBox(args[0]); - let body; - if (isCharacterBox$1) { - body = ordargument(args[0]); - if (body[0].text.charAt(0) === "\\") { - body[0].text = symbols.math[body[0].text].replace; - } - // \u0338 is the Unicode Combining Long Solidus Overlay - body[0].text = body[0].text.slice(0, 1) + "\u0338" + body[0].text.slice(1); - } else { - // When the argument is not a character box, TeX does an awkward, poorly placed overlay. - // We'll do the same. - const notNode = { type: "textord", mode: "math", text: "\u0338" }; - const kernNode = { type: "kern", mode: "math", dimension: { number: -0.6, unit: "em" } }; - body = [notNode, kernNode, args[0]]; - } - return { - type: "not", - mode: parser.mode, - body, - isCharacterBox: isCharacterBox$1 - }; - }, - mathmlBuilder(group, style) { - if (group.isCharacterBox) { - const inner = buildExpression(group.body, style, true); - return inner[0] - } else { - return buildExpressionRow(group.body, style) - } - } - }); - - // Limits, symbols - - // Some helpers - - const ordAtomTypes = ["textord", "mathord", "atom"]; - - // Most operators have a large successor symbol, but these don't. - const noSuccessor = ["\\smallint"]; - - // Math operators (e.g. \sin) need a space between these types and themselves: - const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright", "font"]; - - // NOTE: Unlike most `builders`s, this one handles not only "op", but also - // "supsub" since some of them (like \int) can affect super/subscripting. - - const setSpacing = node => { - // The user wrote a \mathop{…} function. Change spacing from default to OP spacing. - // The most likely spacing for an OP is a thin space per TeXbook p170. - node.attributes.lspace = "0.1667em"; - node.attributes.rspace = "0.1667em"; - }; - - const mathmlBuilder$2 = (group, style) => { - let node; - - if (group.symbol) { - // This is a symbol. Just add the symbol. - node = new MathNode("mo", [makeText(group.name, group.mode)]); - if (noSuccessor.includes(group.name)) { - node.setAttribute("largeop", "false"); - } else { - node.setAttribute("movablelimits", "false"); - } - if (group.fromMathOp) { setSpacing(node); } - } else if (group.body) { - // This is an operator with children. Add them. - node = new MathNode("mo", buildExpression(group.body, style)); - if (group.fromMathOp) { setSpacing(node); } - } else { - // This is a text operator. Add all of the characters from the operator's name. - node = new MathNode("mi", [new TextNode(group.name.slice(1))]); - - if (!group.parentIsSupSub) { - // Append an invisible . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - const row = [node, operator]; - // Set spacing - if (group.needsLeadingSpace) { - const lead = new MathNode("mspace"); - lead.setAttribute("width", "0.1667em"); // thin space. - row.unshift(lead); - } - if (!group.isFollowedByDelimiter) { - const trail = new MathNode("mspace"); - trail.setAttribute("width", "0.1667em"); // thin space. - row.push(trail); - } - node = new MathNode("mrow", row); - } - } - - return node; - }; - - const singleCharBigOps = { - "\u220F": "\\prod", - "\u2210": "\\coprod", - "\u2211": "\\sum", - "\u22c0": "\\bigwedge", - "\u22c1": "\\bigvee", - "\u22c2": "\\bigcap", - "\u22c3": "\\bigcup", - "\u2a00": "\\bigodot", - "\u2a01": "\\bigoplus", - "\u2a02": "\\bigotimes", - "\u2a04": "\\biguplus", - "\u2a05": "\\bigsqcap", - "\u2a06": "\\bigsqcup", - "\u2a03": "\\bigcupdot", - "\u2a07": "\\bigdoublevee", - "\u2a08": "\\bigdoublewedge", - "\u2a09": "\\bigtimes" - }; - - defineFunction({ - type: "op", - names: [ - "\\coprod", - "\\bigvee", - "\\bigwedge", - "\\biguplus", - "\\bigcupplus", - "\\bigcupdot", - "\\bigcap", - "\\bigcup", - "\\bigdoublevee", - "\\bigdoublewedge", - "\\intop", - "\\prod", - "\\sum", - "\\bigotimes", - "\\bigoplus", - "\\bigodot", - "\\bigsqcap", - "\\bigsqcup", - "\\bigtimes", - "\\smallint", - "\u220F", - "\u2210", - "\u2211", - "\u22c0", - "\u22c1", - "\u22c2", - "\u22c3", - "\u2a00", - "\u2a01", - "\u2a02", - "\u2a03", - "\u2a04", - "\u2a05", - "\u2a06", - "\u2a07", - "\u2a08", - "\u2a09" - ], - props: { - numArgs: 0 - }, - handler: ({ parser, funcName }, args) => { - let fName = funcName; - if (fName.length === 1) { - fName = singleCharBigOps[fName]; - } - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: true, - stack: false, // This is true for \stackrel{}, not here. - name: fName - }; - }, - mathmlBuilder: mathmlBuilder$2 - }); - - // Note: calling defineFunction with a type that's already been defined only - // works because the same mathmlBuilder is being used. - defineFunction({ - type: "op", - names: ["\\mathop"], - props: { - numArgs: 1, - primitive: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - // It would be convienient to just wrap a around the argument. - // But if the argument is a or , that would be invalid MathML. - // In that case, we instead promote the text contents of the body to the parent. - const arr = (body.body) ? body.body : [body]; - const isSymbol = arr.length === 1 && ordAtomTypes.includes(arr[0].type); - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: isSymbol, - fromMathOp: true, - stack: false, - name: isSymbol ? arr[0].text : null, - body: isSymbol ? null : ordargument(body) - }; - }, - mathmlBuilder: mathmlBuilder$2 - }); - - // There are 2 flags for operators; whether they produce limits in - // displaystyle, and whether they are symbols and should grow in - // displaystyle. These four groups cover the four possible choices. - - const singleCharIntegrals = { - "\u222b": "\\int", - "\u222c": "\\iint", - "\u222d": "\\iiint", - "\u222e": "\\oint", - "\u222f": "\\oiint", - "\u2230": "\\oiiint", - "\u2231": "\\intclockwise", - "\u2232": "\\varointclockwise", - "\u2a0c": "\\iiiint", - "\u2a0d": "\\intbar", - "\u2a0e": "\\intBar", - "\u2a0f": "\\fint", - "\u2a12": "\\rppolint", - "\u2a13": "\\scpolint", - "\u2a15": "\\pointint", - "\u2a16": "\\sqint", - "\u2a17": "\\intlarhk", - "\u2a18": "\\intx", - "\u2a19": "\\intcap", - "\u2a1a": "\\intcup" - }; - - // No limits, not symbols - defineFunction({ - type: "op", - names: [ - "\\arcsin", - "\\arccos", - "\\arctan", - "\\arctg", - "\\arcctg", - "\\arg", - "\\ch", - "\\cos", - "\\cosec", - "\\cosh", - "\\cot", - "\\cotg", - "\\coth", - "\\csc", - "\\ctg", - "\\cth", - "\\deg", - "\\dim", - "\\exp", - "\\hom", - "\\ker", - "\\lg", - "\\ln", - "\\log", - "\\sec", - "\\sin", - "\\sinh", - "\\sh", - "\\sgn", - "\\tan", - "\\tanh", - "\\tg", - "\\th" - ], - props: { - numArgs: 0 - }, - handler({ parser, funcName }) { - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "op", - mode: parser.mode, - limits: false, - parentIsSupSub: false, - symbol: false, - stack: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), - name: funcName - }; - }, - mathmlBuilder: mathmlBuilder$2 - }); - - // Limits, not symbols - defineFunction({ - type: "op", - names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"], - props: { - numArgs: 0 - }, - handler({ parser, funcName }) { - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: false, - stack: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), - name: funcName - }; - }, - mathmlBuilder: mathmlBuilder$2 - }); - - // No limits, symbols - defineFunction({ - type: "op", - names: [ - "\\int", - "\\iint", - "\\iiint", - "\\iiiint", - "\\oint", - "\\oiint", - "\\oiiint", - "\\intclockwise", - "\\varointclockwise", - "\\intbar", - "\\intBar", - "\\fint", - "\\rppolint", - "\\scpolint", - "\\pointint", - "\\sqint", - "\\intlarhk", - "\\intx", - "\\intcap", - "\\intcup", - "\u222b", - "\u222c", - "\u222d", - "\u222e", - "\u222f", - "\u2230", - "\u2231", - "\u2232", - "\u2a0c", - "\u2a0d", - "\u2a0e", - "\u2a0f", - "\u2a12", - "\u2a13", - "\u2a15", - "\u2a16", - "\u2a17", - "\u2a18", - "\u2a19", - "\u2a1a" - ], - props: { - numArgs: 0, - allowedInArgument: true - }, - handler({ parser, funcName }) { - let fName = funcName; - if (fName.length === 1) { - fName = singleCharIntegrals[fName]; - } - return { - type: "op", - mode: parser.mode, - limits: false, - parentIsSupSub: false, - symbol: true, - stack: false, - name: fName - }; - }, - mathmlBuilder: mathmlBuilder$2 - }); - - // NOTE: Unlike most builders, this one handles not only - // "operatorname", but also "supsub" since \operatorname* can - // affect super/subscripting. - - const mathmlBuilder$1 = (group, style) => { - let expression = buildExpression(group.body, style.withFont("mathrm")); - - // Is expression a string or has it something like a fraction? - let isAllString = true; // default - for (let i = 0; i < expression.length; i++) { - let node = expression[i]; - if (node instanceof MathNode) { - if ((node.type === "mrow" || node.type === "mpadded") && node.children.length === 1 && - node.children[0] instanceof MathNode) { - node = node.children[0]; - } else if (node.type === "mrow" && node.children.length === 2 && - node.children[0] instanceof MathNode && - node.children[1] instanceof MathNode && - node.children[1].type === "mspace" && !node.children[1].attributes.width && - node.children[1].children.length === 0) { - // This is a workaround for a Firefox bug that applies spacing to - // an with mathvariant="normal". - node = node.children[0]; - } - switch (node.type) { - case "mi": - case "mn": - case "ms": - case "mtext": - break; // Do nothing yet. - case "mspace": - { - if (node.attributes.width) { - const width = node.attributes.width.replace("em", ""); - const ch = spaceCharacter(Number(width)); - if (ch === "") { - isAllString = false; - } else { - expression[i] = new MathNode("mtext", [new TextNode(ch)]); - } - } - } - break - case "mo": { - const child = node.children[0]; - if (node.children.length === 1 && child instanceof TextNode) { - child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); - } else { - isAllString = false; - } - break - } - default: - isAllString = false; - } - } else { - isAllString = false; - } - } - - if (isAllString) { - // Write a single TextNode instead of multiple nested tags. - const word = expression.map((node) => node.toText()).join(""); - expression = [new TextNode(word)]; - } else if ( - expression.length === 1 - && ["mover", "munder"].includes(expression[0].type) && - (expression[0].children[0].type === "mi" || expression[0].children[0].type === "mtext") - ) { - expression[0].children[0].type = "mi"; - if (group.parentIsSupSub) { - return new MathNode("mrow", expression) - } else { - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - return newDocumentFragment([expression[0], operator]) - } - } - - let wrapper; - if (isAllString) { - wrapper = new MathNode("mi", expression); - if (expression[0].text.length === 1) { - wrapper.setAttribute("mathvariant", "normal"); - } - } else { - wrapper = new MathNode("mrow", expression); - } - - if (!group.parentIsSupSub) { - // Append an . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - const fragment = [wrapper, operator]; - if (group.needsLeadingSpace) { - // LaTeX gives operator spacing, but a gets ord spacing. - // So add a leading space. - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - fragment.unshift(space); - } - if (!group.isFollowedByDelimiter) { - const trail = new MathNode("mspace"); - trail.setAttribute("width", "0.1667em"); // thin space. - fragment.push(trail); - } - return newDocumentFragment(fragment) - } - - return wrapper - }; - - // \operatorname - // amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@ - defineFunction({ - type: "operatorname", - names: ["\\operatorname@", "\\operatornamewithlimits"], - props: { - numArgs: 1, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const body = args[0]; - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "operatorname", - mode: parser.mode, - body: ordargument(body), - alwaysHandleSupSub: (funcName === "\\operatornamewithlimits"), - limits: false, - parentIsSupSub: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType) - }; - }, - mathmlBuilder: mathmlBuilder$1 - }); - - defineMacro("\\operatorname", - "\\@ifstar\\operatornamewithlimits\\operatorname@"); - - defineFunctionBuilders({ - type: "ordgroup", - mathmlBuilder(group, style) { - return buildExpressionRow(group.body, style, group.semisimple); - } - }); - - defineFunction({ - type: "phantom", - names: ["\\phantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "phantom", - mode: parser.mode, - body: ordargument(body) - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(group.body, style); - return new MathNode("mphantom", inner); - } - }); - - defineFunction({ - type: "hphantom", - names: ["\\hphantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "hphantom", - mode: parser.mode, - body - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", inner); - const node = new MathNode("mpadded", [phantom]); - node.setAttribute("height", "0px"); - node.setAttribute("depth", "0px"); - return node; - } - }); - - defineFunction({ - type: "vphantom", - names: ["\\vphantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "vphantom", - mode: parser.mode, - body - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", inner); - const node = new MathNode("mpadded", [phantom]); - node.setAttribute("width", "0px"); - return node; - } - }); - - // In LaTeX, \pmb is a simulation of bold font. - // The version of \pmb in ambsy.sty works by typesetting three copies of the argument - // with small offsets. We use CSS font-weight:bold. - - defineFunction({ - type: "pmb", - names: ["\\pmb"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "pmb", - mode: parser.mode, - body: ordargument(args[0]) - } - }, - mathmlBuilder(group, style) { - const inner = buildExpression(group.body, style); - // Wrap with an element. - const node = wrapWithMstyle(inner); - node.setAttribute("style", "font-weight:bold"); - return node - } - }); - - // \raise, \lower, and \raisebox - - const mathmlBuilder = (group, style) => { - const newStyle = style.withLevel(StyleLevel.TEXT); - const node = new MathNode("mpadded", [buildGroup$1(group.body, newStyle)]); - const dy = calculateSize(group.dy, style); - node.setAttribute("voffset", dy.number + dy.unit); - // Add padding, which acts to increase height in Chromium. - // TODO: Figure out some way to change height in Firefox w/o breaking Chromium. - if (dy.number > 0) { - node.style.padding = dy.number + dy.unit + " 0 0 0"; - } else { - node.style.padding = "0 0 " + Math.abs(dy.number) + dy.unit + " 0"; - } - return node - }; - - defineFunction({ - type: "raise", - names: ["\\raise", "\\lower"], - props: { - numArgs: 2, - argTypes: ["size", "primitive"], - primitive: true - }, - handler({ parser, funcName }, args) { - const amount = assertNodeType(args[0], "size").value; - if (funcName === "\\lower") { amount.number *= -1; } - const body = args[1]; - return { - type: "raise", - mode: parser.mode, - dy: amount, - body - }; - }, - mathmlBuilder - }); - - - defineFunction({ - type: "raise", - names: ["\\raisebox"], - props: { - numArgs: 2, - argTypes: ["size", "hbox"], - allowedInText: true - }, - handler({ parser, funcName }, args) { - const amount = assertNodeType(args[0], "size").value; - const body = args[1]; - return { - type: "raise", - mode: parser.mode, - dy: amount, - body - }; - }, - mathmlBuilder - }); - - defineFunction({ - type: "ref", - names: ["\\ref", "\\eqref"], - props: { - numArgs: 1, - argTypes: ["raw"] - }, - handler({ parser, funcName }, args) { - return { - type: "ref", - mode: parser.mode, - funcName, - string: args[0].string.replace(invalidIdRegEx, "") - }; - }, - mathmlBuilder(group, style) { - // Create an empty node. Set a class and an href attribute. - // The post-processor will populate with the target's tag or equation number. - const classes = group.funcName === "\\ref" ? ["tml-ref"] : ["tml-ref", "tml-eqref"]; - return new AnchorNode("#" + group.string, classes, null) - } - }); - - defineFunction({ - type: "reflect", - names: ["\\reflectbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "reflect", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - const node = buildGroup$1(group.body, style); - node.style.transform = "scaleX(-1)"; - return node - } - }); - - defineFunction({ - type: "internal", - names: ["\\relax"], - props: { - numArgs: 0, - allowedInText: true, - allowedInArgument: true - }, - handler({ parser }) { - return { - type: "internal", - mode: parser.mode - }; - } - }); - - defineFunction({ - type: "rule", - names: ["\\rule"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - allowedInMath: true, - argTypes: ["size", "size", "size"] - }, - handler({ parser }, args, optArgs) { - const shift = optArgs[0]; - const width = assertNodeType(args[0], "size"); - const height = assertNodeType(args[1], "size"); - return { - type: "rule", - mode: parser.mode, - shift: shift && assertNodeType(shift, "size").value, - width: width.value, - height: height.value - }; - }, - mathmlBuilder(group, style) { - const width = calculateSize(group.width, style); - const height = calculateSize(group.height, style); - const shift = group.shift - ? calculateSize(group.shift, style) - : { number: 0, unit: "em" }; - const color = (style.color && style.getColor()) || "black"; - - const rule = new MathNode("mspace"); - if (width.number > 0 && height.number > 0) { - rule.setAttribute("mathbackground", color); - } - rule.setAttribute("width", width.number + width.unit); - rule.setAttribute("height", height.number + height.unit); - if (shift.number === 0) { return rule } - - const wrapper = new MathNode("mpadded", [rule]); - if (shift.number >= 0) { - wrapper.setAttribute("height", "+" + shift.number + shift.unit); - } else { - wrapper.setAttribute("height", shift.number + shift.unit); - wrapper.setAttribute("depth", "+" + -shift.number + shift.unit); - } - wrapper.setAttribute("voffset", shift.number + shift.unit); - return wrapper; - } - }); - - const numRegEx = /^[0-9]$/; - const unicodeNumSubs = { - '0': '₀', - '1': '₁', - '2': '₂', - '3': '₃', - '4': '₄', - '5': '₅', - '6': '₆', - '7': '₇', - '8': '₈', - '9': '₉' - }; - const unicodeNumSups = { - '0': '⁰', - '1': '¹', - '2': '²', - '3': '³', - '4': '⁴', - '5': '⁵', - '6': '⁶', - '7': '⁷', - '8': '⁸', - '9': '⁹' - }; - - defineFunction({ - type: "sfrac", - names: ["\\sfrac"], - props: { - numArgs: 2, - allowedInText: true, - allowedInMath: true - }, - handler({ parser }, args) { - let numerator = ""; - for (const node of args[0].body) { - if (node.type !== "textord" || !numRegEx.test(node.text)) { - throw new ParseError("Numerator must be an integer.", node) - } - numerator += node.text; - } - let denominator = ""; - for (const node of args[1].body) { - if (node.type !== "textord" || !numRegEx.test(node.text)) { - throw new ParseError("Denominator must be an integer.", node) - } - denominator += node.text; - } - return { - type: "sfrac", - mode: parser.mode, - numerator, - denominator - }; - }, - mathmlBuilder(group, style) { - const numerator = group.numerator.split('').map(c => unicodeNumSups[c]).join(''); - const denominator = group.denominator.split('').map(c => unicodeNumSubs[c]).join(''); - // Use a fraction slash. - const text = new TextNode(numerator + "\u2044" + denominator, group.mode, style); - return new MathNode("mn", [text], ["special-fraction"]) - } - }); - - // The size mappings are taken from TeX with \normalsize=10pt. - // We don't have to track script level. MathML does that. - const sizeMap = { - "\\tiny": 0.5, - "\\sixptsize": 0.6, - "\\Tiny": 0.6, - "\\scriptsize": 0.7, - "\\footnotesize": 0.8, - "\\small": 0.9, - "\\normalsize": 1.0, - "\\large": 1.2, - "\\Large": 1.44, - "\\LARGE": 1.728, - "\\huge": 2.074, - "\\Huge": 2.488 - }; - - defineFunction({ - type: "sizing", - names: [ - "\\tiny", - "\\sixptsize", - "\\Tiny", - "\\scriptsize", - "\\footnotesize", - "\\small", - "\\normalsize", - "\\large", - "\\Large", - "\\LARGE", - "\\huge", - "\\Huge" - ], - props: { - numArgs: 0, - allowedInText: true - }, - handler: ({ breakOnTokenText, funcName, parser }, args) => { - if (parser.settings.strict && parser.mode === "math") { - // eslint-disable-next-line no-console - console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`); - } - const body = parser.parseExpression(false, breakOnTokenText, true); - return { - type: "sizing", - mode: parser.mode, - funcName, - body - }; - }, - mathmlBuilder: (group, style) => { - const newStyle = style.withFontSize(sizeMap[group.funcName]); - const inner = buildExpression(group.body, newStyle); - // Wrap with an element. - const node = wrapWithMstyle(inner); - const factor = (sizeMap[group.funcName] / style.fontSize).toFixed(4); - node.setAttribute("mathsize", factor + "em"); - return node; - } - }); - - // smash, with optional [tb], as in AMS - - defineFunction({ - type: "smash", - names: ["\\smash"], - props: { - numArgs: 1, - numOptionalArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args, optArgs) => { - let smashHeight = false; - let smashDepth = false; - const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup"); - if (tbArg) { - // Optional [tb] argument is engaged. - // ref: amsmath: \renewcommand{\smash}[1][tb]{% - // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}% - let letter = ""; - for (let i = 0; i < tbArg.body.length; ++i) { - const node = tbArg.body[i]; - // TODO: Write an AssertSymbolNode - letter = node.text; - if (letter === "t") { - smashHeight = true; - } else if (letter === "b") { - smashDepth = true; - } else { - smashHeight = false; - smashDepth = false; - break; - } - } - } else { - smashHeight = true; - smashDepth = true; - } - - const body = args[0]; - return { - type: "smash", - mode: parser.mode, - body, - smashHeight, - smashDepth - }; - }, - mathmlBuilder: (group, style) => { - const node = new MathNode("mpadded", [buildGroup$1(group.body, style)]); - - if (group.smashHeight) { - node.setAttribute("height", "0px"); - } - - if (group.smashDepth) { - node.setAttribute("depth", "0px"); - } - - return node; - } - }); - - // Letters that are x-height w/o a descender. - const xHeights = ['a', 'c', 'e', 'ı', 'm', 'n', 'o', 'r', 's', 'u', 'v', 'w', 'x', 'z', 'α', - 'ε', 'ι', 'κ', 'ν', 'ο', 'π', 'σ', 'τ', 'υ', 'ω', '\\alpha', '\\epsilon', "\\iota", - '\\kappa', '\\nu', '\\omega', '\\pi', '\\tau', '\\omega']; - - defineFunction({ - type: "sqrt", - names: ["\\sqrt"], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser }, args, optArgs) { - const index = optArgs[0]; - const body = args[0]; - // Check if the body consists entirely of an x-height letter. - // TODO: Remove this check after Chromium is fixed. - if (body.body && body.body.length === 1 && body.body[0].text && - xHeights.includes(body.body[0].text)) { - // Chromium does not put enough space above an x-height letter. - // Insert a strut. - body.body.push({ - "type": "rule", - "mode": "math", - "shift": null, - "width": { "number": 0, "unit": "pt" }, - "height": { "number": 0.5, "unit": "em" } - }); - } - return { - type: "sqrt", - mode: parser.mode, - body, - index - }; - }, - mathmlBuilder(group, style) { - const { body, index } = group; - return index - ? new MathNode("mroot", [ - buildGroup$1(body, style), - buildGroup$1(index, style.incrementLevel()) - ]) - : new MathNode("msqrt", [buildGroup$1(body, style)]); - } - }); - - const styleMap = { - display: 0, - text: 1, - script: 2, - scriptscript: 3 - }; - - const styleAttributes = { - display: ["0", "true"], - text: ["0", "false"], - script: ["1", "false"], - scriptscript: ["2", "false"] - }; - - defineFunction({ - type: "styling", - names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ breakOnTokenText, funcName, parser }, args) { - // parse out the implicit body - const body = parser.parseExpression(true, breakOnTokenText, true); - - const scriptLevel = funcName.slice(1, funcName.length - 5); - return { - type: "styling", - mode: parser.mode, - // Figure out what scriptLevel to use by pulling out the scriptLevel from - // the function name - scriptLevel, - body - }; - }, - mathmlBuilder(group, style) { - // Figure out what scriptLevel we're changing to. - const newStyle = style.withLevel(styleMap[group.scriptLevel]); - // The style argument in the next line does NOT directly set a MathML script level. - // It just tracks the style level, in case we need to know it for supsub or mathchoice. - const inner = buildExpression(group.body, newStyle); - // Wrap with an element. - const node = wrapWithMstyle(inner); - - const attr = styleAttributes[group.scriptLevel]; - - // Here is where we set the MathML script level. - node.setAttribute("scriptlevel", attr[0]); - node.setAttribute("displaystyle", attr[1]); - - return node; - } - }); - - /** - * Sometimes, groups perform special rules when they have superscripts or - * subscripts attached to them. This function lets the `supsub` group know that - * Sometimes, groups perform special rules when they have superscripts or - * its inner element should handle the superscripts and subscripts instead of - * handling them itself. - */ - - // Helpers - const symbolRegEx = /^m(over|under|underover)$/; - - // From the KaTeX font metrics, identify letters that encroach on a superscript. - const smallPad = "DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ"; - const mediumPad = "BCEFGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ"; - const largePad = "AJdfΔΛ"; - - // Super scripts and subscripts, whose precise placement can depend on other - // functions that precede them. - defineFunctionBuilders({ - type: "supsub", - mathmlBuilder(group, style) { - // Is the inner group a relevant horizontal brace or bracket? - let isBracket = false; - let isOver; - let isSup; - let appendApplyFunction = false; - let appendSpace = false; - let needsLeadingSpace = false; - - if (group.base && group.base.type === "horizBracket") { - isSup = !!group.sup; - if (isSup === group.base.isOver) { - isBracket = true; - isOver = group.base.isOver; - } - } - - if (group.base && !group.stack && - (group.base.type === "op" || group.base.type === "operatorname")) { - group.base.parentIsSupSub = true; - appendApplyFunction = !group.base.symbol; - appendSpace = appendApplyFunction && !group.isFollowedByDelimiter; - needsLeadingSpace = group.base.needsLeadingSpace; - } - - const children = group.stack && group.base.body.length === 1 - ? [buildGroup$1(group.base.body[0], style)] - : [buildGroup$1(group.base, style)]; - - // Note regarding scriptstyle level. - // (Sub|super)scripts should not shrink beyond MathML scriptlevel 2 aka \scriptscriptstyle - // Ref: https://w3c.github.io/mathml-core/#the-displaystyle-and-scriptlevel-attributes - // (BTW, MathML scriptlevel 2 is equal to Temml level 3.) - // But Chromium continues to shrink the (sub|super)scripts. So we explicitly set scriptlevel 2. - - const childStyle = style.inSubOrSup(); - if (group.sub) { - const sub = buildGroup$1(group.sub, childStyle); - if (style.level === 3) { sub.setAttribute("scriptlevel", "2"); } - children.push(sub); - } - - if (group.sup) { - const sup = buildGroup$1(group.sup, childStyle); - if (style.level === 3) { sup.setAttribute("scriptlevel", "2"); } - if (group.base && group.base.text && group.base.text.length === 1) { - // Make an italic correction on the superscript. - const text = group.base.text; - if (smallPad.indexOf(text) > -1) { - sup.classes.push("tml-sml-pad"); - } else if (mediumPad.indexOf(text) > -1) { - sup.classes.push("tml-med-pad"); - } else if (largePad.indexOf(text) > -1) { - sup.classes.push("tml-lrg-pad"); - } - } - children.push(sup); - } - - let nodeType; - if (isBracket) { - nodeType = isOver ? "mover" : "munder"; - } else if (!group.sub) { - const base = group.base; - if ( - base && - base.type === "op" && - base.limits && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "mover"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (base.limits || style.level === StyleLevel.DISPLAY) - ) { - nodeType = "mover"; - } else { - nodeType = "msup"; - } - } else if (!group.sup) { - const base = group.base; - if (group.stack) { - nodeType = "munder"; - } else if ( - base && - base.type === "op" && - base.limits && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "munder"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (base.limits || style.level === StyleLevel.DISPLAY) - ) { - nodeType = "munder"; - } else { - nodeType = "msub"; - } - } else { - const base = group.base; - if (base && ((base.type === "op" && base.limits) || base.type === "multiscript") && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "munderover"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (style.level === StyleLevel.DISPLAY || base.limits) - ) { - nodeType = "munderover"; - } else { - nodeType = "msubsup"; - } - } - - let node = new MathNode(nodeType, children); - if (appendApplyFunction) { - // Append an . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - if (needsLeadingSpace) { - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - node = newDocumentFragment([space, node, operator]); - } else { - node = newDocumentFragment([node, operator]); - } - if (appendSpace) { - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - node.children.push(space); - } - } else if (symbolRegEx.test(nodeType)) { - // Wrap in a . Otherwise Firefox stretchy parens will not stretch to include limits. - node = new MathNode("mrow", [node]); - } - - return node - } - }); - - // Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js. - - const short = ["\\shortmid", "\\nshortmid", "\\shortparallel", - "\\nshortparallel", "\\smallsetminus"]; - - const arrows = ["\\Rsh", "\\Lsh", "\\restriction"]; - - const isArrow = str => { - if (str.length === 1) { - const codePoint = str.codePointAt(0); - return (0x218f < codePoint && codePoint < 0x2200) - } - return str.indexOf("arrow") > -1 || str.indexOf("harpoon") > -1 || arrows.includes(str) - }; - - defineFunctionBuilders({ - type: "atom", - mathmlBuilder(group, style) { - const node = new MathNode("mo", [makeText(group.text, group.mode)]); - if (group.family === "punct") { - node.setAttribute("separator", "true"); - } else if (group.family === "open" || group.family === "close") { - // Delims built here should not stretch vertically. - // See delimsizing.js for stretchy delims. - if (group.family === "open") { - node.setAttribute("form", "prefix"); - // Set an explicit attribute for stretch. Otherwise Firefox may do it wrong. - node.setAttribute("stretchy", "false"); - } else if (group.family === "close") { - node.setAttribute("form", "postfix"); - node.setAttribute("stretchy", "false"); - } - } else if (group.text === "\\mid") { - // Firefox messes up this spacing if at the end of an . See it explicitly. - node.setAttribute("lspace", "0.22em"); // medium space - node.setAttribute("rspace", "0.22em"); - node.setAttribute("stretchy", "false"); - } else if (group.family === "rel" && isArrow(group.text)) { - node.setAttribute("stretchy", "false"); - } else if (short.includes(group.text)) { - node.setAttribute("mathsize", "70%"); - } else if (group.text === ":") { - // ":" is not in the MathML operator dictionary. Give it BIN spacing. - node.attributes.lspace = "0.2222em"; - node.attributes.rspace = "0.2222em"; - } else if (group.needsSpacing) { - // Fix a MathML bug that occurs when a is between two elements. - if (group.family === "bin") { - return new MathNode("mrow", [padding(0.222), node, padding(0.222)]) - } else { - // REL spacing - return new MathNode("mrow", [padding(0.2778), node, padding(0.2778)]) - } - } - return node; - } - }); - - /** - * Maps TeX font commands to "mathvariant" attribute in buildMathML.js - */ - const fontMap = { - // styles - mathbf: "bold", - mathrm: "normal", - textit: "italic", - mathit: "italic", - mathnormal: "italic", - - // families - mathbb: "double-struck", - mathcal: "script", - mathfrak: "fraktur", - mathscr: "script", - mathsf: "sans-serif", - mathtt: "monospace" - }; - - /** - * Returns the math variant as a string or null if none is required. - */ - const getVariant = function(group, style) { - // Handle font specifiers as best we can. - // Chromium does not support the MathML mathvariant attribute. - // So we'll use Unicode replacement characters instead. - // But first, determine the math variant. - - // Deal with the \textit, \textbf, etc., functions. - if (style.fontFamily === "texttt") { - return "monospace" - } else if (style.fontFamily === "textsc") { - return "normal"; // handled via character substitution in symbolsOrd.js. - } else if (style.fontFamily === "textsf") { - if (style.fontShape === "textit" && style.fontWeight === "textbf") { - return "sans-serif-bold-italic" - } else if (style.fontShape === "textit") { - return "sans-serif-italic" - } else if (style.fontWeight === "textbf") { - return "sans-serif-bold" - } else { - return "sans-serif" - } - } else if (style.fontShape === "textit" && style.fontWeight === "textbf") { - return "bold-italic" - } else if (style.fontShape === "textit") { - return "italic" - } else if (style.fontWeight === "textbf") { - return "bold" - } - - // Deal with the \mathit, mathbf, etc, functions. - const font = style.font; - if (!font || font === "mathnormal") { - return null - } - - const mode = group.mode; - switch (font) { - case "mathit": - return "italic" - case "mathrm": { - const codePoint = group.text.codePointAt(0); - // LaTeX \mathrm returns italic for Greek characters. - return (0x03ab < codePoint && codePoint < 0x03cf) ? "italic" : "normal" - } - case "greekItalic": - return "italic" - case "up@greek": - return "normal" - case "boldsymbol": - case "mathboldsymbol": - return "bold-italic" - case "mathbf": - return "bold" - case "mathbb": - return "double-struck" - case "mathfrak": - return "fraktur" - case "mathscr": - case "mathcal": - return "script" - case "mathsf": - return "sans-serif" - case "mathsfit": - return "sans-serif-italic" - case "mathtt": - return "monospace" - } - - let text = group.text; - if (symbols[mode][text] && symbols[mode][text].replace) { - text = symbols[mode][text].replace; - } - - return Object.prototype.hasOwnProperty.call(fontMap, font) ? fontMap[font] : null - }; - - // Chromium does not support the MathML `mathvariant` attribute. - // Instead, we replace ASCII characters with Unicode characters that - // are defined in the font as bold, italic, double-struck, etc. - // This module identifies those Unicode code points. - - // First, a few helpers. - const script = Object.freeze({ - B: 0x20EA, // Offset from ASCII B to Unicode script B - E: 0x20EB, - F: 0x20EB, - H: 0x20C3, - I: 0x20C7, - L: 0x20C6, - M: 0x20E6, - R: 0x20C9, - e: 0x20CA, - g: 0x20A3, - o: 0x20C5 - }); - - const frak = Object.freeze({ - C: 0x20EA, - H: 0x20C4, - I: 0x20C8, - R: 0x20CA, - Z: 0x20CE - }); - - const bbb = Object.freeze({ - C: 0x20BF, // blackboard bold - H: 0x20C5, - N: 0x20C7, - P: 0x20C9, - Q: 0x20C9, - R: 0x20CB, - Z: 0x20CA - }); - - const bold = Object.freeze({ - "\u03f5": 0x1D2E7, // lunate epsilon - "\u03d1": 0x1D30C, // vartheta - "\u03f0": 0x1D2EE, // varkappa - "\u03c6": 0x1D319, // varphi - "\u03f1": 0x1D2EF, // varrho - "\u03d6": 0x1D30B // varpi - }); - - const boldItalic = Object.freeze({ - "\u03f5": 0x1D35B, // lunate epsilon - "\u03d1": 0x1D380, // vartheta - "\u03f0": 0x1D362, // varkappa - "\u03c6": 0x1D38D, // varphi - "\u03f1": 0x1D363, // varrho - "\u03d6": 0x1D37F // varpi - }); - - const boldsf = Object.freeze({ - "\u03f5": 0x1D395, // lunate epsilon - "\u03d1": 0x1D3BA, // vartheta - "\u03f0": 0x1D39C, // varkappa - "\u03c6": 0x1D3C7, // varphi - "\u03f1": 0x1D39D, // varrho - "\u03d6": 0x1D3B9 // varpi - }); - - const bisf = Object.freeze({ - "\u03f5": 0x1D3CF, // lunate epsilon - "\u03d1": 0x1D3F4, // vartheta - "\u03f0": 0x1D3D6, // varkappa - "\u03c6": 0x1D401, // varphi - "\u03f1": 0x1D3D7, // varrho - "\u03d6": 0x1D3F3 // varpi - }); - - // Code point offsets below are derived from https://www.unicode.org/charts/PDF/U1D400.pdf - const offset = Object.freeze({ - upperCaseLatin: { // A-Z - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D3BF }, - "italic": ch => { return 0x1D3F3 }, - "bold-italic": ch => { return 0x1D427 }, - "script": ch => { return script[ch] || 0x1D45B }, - "script-bold": ch => { return 0x1D48F }, - "fraktur": ch => { return frak[ch] || 0x1D4C3 }, - "fraktur-bold": ch => { return 0x1D52B }, - "double-struck": ch => { return bbb[ch] || 0x1D4F7 }, - "sans-serif": ch => { return 0x1D55F }, - "sans-serif-bold": ch => { return 0x1D593 }, - "sans-serif-italic": ch => { return 0x1D5C7 }, - "sans-serif-bold-italic": ch => { return 0x1D63C }, - "monospace": ch => { return 0x1D62F } - }, - lowerCaseLatin: { // a-z - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D3B9 }, - "italic": ch => { return ch === "h" ? 0x20A6 : 0x1D3ED }, - "bold-italic": ch => { return 0x1D421 }, - "script": ch => { return script[ch] || 0x1D455 }, - "script-bold": ch => { return 0x1D489 }, - "fraktur": ch => { return 0x1D4BD }, - "fraktur-bold": ch => { return 0x1D525 }, - "double-struck": ch => { return 0x1D4F1 }, - "sans-serif": ch => { return 0x1D559 }, - "sans-serif-bold": ch => { return 0x1D58D }, - "sans-serif-italic": ch => { return 0x1D5C1 }, - "sans-serif-bold-italic": ch => { return 0x1D5F5 }, - "monospace": ch => { return 0x1D629 } - }, - upperCaseGreek: { // A-Ω - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D317 }, - "italic": ch => { return 0x1D351 }, - // \boldsymbol actually returns upright bold for upperCaseGreek - "bold-italic": ch => { return 0x1D317 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - // Unicode has no code points for regular-weight san-serif Greek. Use bold. - "sans-serif": ch => { return 0x1D3C5 }, - "sans-serif-bold": ch => { return 0x1D3C5 }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0x1D3FF }, - "monospace": ch => { return 0 } - }, - lowerCaseGreek: { // α-ω - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D311 }, - "italic": ch => { return 0x1D34B }, - "bold-italic": ch => { return ch === "\u03d5" ? 0x1D37E : 0x1D385 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - // Unicode has no code points for regular-weight san-serif Greek. Use bold. - "sans-serif": ch => { return 0x1D3BF }, - "sans-serif-bold": ch => { return 0x1D3BF }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0x1D3F9 }, - "monospace": ch => { return 0 } - }, - varGreek: { // \varGamma, etc - "normal": ch => { return 0 }, - "bold": ch => { return bold[ch] || -51 }, - "italic": ch => { return 0 }, - "bold-italic": ch => { return boldItalic[ch] || 0x3A }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - "sans-serif": ch => { return boldsf[ch] || 0x74 }, - "sans-serif-bold": ch => { return boldsf[ch] || 0x74 }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return bisf[ch] || 0xAE }, - "monospace": ch => { return 0 } - }, - numeral: { // 0-9 - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D79E }, - "italic": ch => { return 0 }, - "bold-italic": ch => { return 0 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0x1D7A8 }, - "sans-serif": ch => { return 0x1D7B2 }, - "sans-serif-bold": ch => { return 0x1D7BC }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0 }, - "monospace": ch => { return 0x1D7C6 } - } - }); - - const variantChar = (ch, variant) => { - const codePoint = ch.codePointAt(0); - const block = 0x40 < codePoint && codePoint < 0x5b - ? "upperCaseLatin" - : 0x60 < codePoint && codePoint < 0x7b - ? "lowerCaseLatin" - : (0x390 < codePoint && codePoint < 0x3AA) - ? "upperCaseGreek" - : 0x3B0 < codePoint && codePoint < 0x3CA || ch === "\u03d5" - ? "lowerCaseGreek" - : 0x1D6E1 < codePoint && codePoint < 0x1D6FC || bold[ch] - ? "varGreek" - : (0x2F < codePoint && codePoint < 0x3A) - ? "numeral" - : "other"; - return block === "other" - ? ch - : String.fromCodePoint(codePoint + offset[block][variant](ch)) - }; - - const smallCaps = Object.freeze({ - a: "ᴀ", - b: "ʙ", - c: "ᴄ", - d: "ᴅ", - e: "ᴇ", - f: "ꜰ", - g: "ɢ", - h: "ʜ", - i: "ɪ", - j: "ᴊ", - k: "ᴋ", - l: "ʟ", - m: "ᴍ", - n: "ɴ", - o: "ᴏ", - p: "ᴘ", - q: "ǫ", - r: "ʀ", - s: "s", - t: "ᴛ", - u: "ᴜ", - v: "ᴠ", - w: "ᴡ", - x: "x", - y: "ʏ", - z: "ᴢ" - }); - - // "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in - // src/symbols.js. - - const numberRegEx = /^\d(?:[\d,.]*\d)?$/; - const latinRegEx = /[A-Ba-z]/; - const primes = new Set(["\\prime", "\\dprime", "\\trprime", "\\qprime", - "\\backprime", "\\backdprime", "\\backtrprime"]); - - const italicNumber = (text, variant, tag) => { - const mn = new MathNode(tag, [text]); - const wrapper = new MathNode("mstyle", [mn]); - wrapper.style["font-style"] = "italic"; - wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif"; - if (variant === "bold-italic") { wrapper.style["font-weight"] = "bold"; } - return wrapper - }; - - defineFunctionBuilders({ - type: "mathord", - mathmlBuilder(group, style) { - const text = makeText(group.text, group.mode, style); - const codePoint = text.text.codePointAt(0); - // Test for upper-case Greek - const defaultVariant = (0x0390 < codePoint && codePoint < 0x03aa) ? "normal" : "italic"; - const variant = getVariant(group, style) || defaultVariant; - if (variant === "script") { - text.text = variantChar(text.text, variant); - return new MathNode("mi", [text], [style.font]) - } else if (variant !== "italic") { - text.text = variantChar(text.text, variant); - } - let node = new MathNode("mi", [text]); - // TODO: Handle U+1D49C - U+1D4CF per https://www.unicode.org/charts/PDF/U1D400.pdf - if (variant === "normal") { - node.setAttribute("mathvariant", "normal"); - if (text.text.length === 1) { - // A Firefox bug will apply spacing here, but there should be none. Fix it. - const mspace = new MathNode("mspace", []); - node = new MathNode("mrow", [node, mspace]); - } - } - return node - } - }); - - defineFunctionBuilders({ - type: "textord", - mathmlBuilder(group, style) { - let ch = group.text; - const codePoint = ch.codePointAt(0); - if (style.fontFamily === "textsc") { - // Convert small latin letters to small caps. - if (96 < codePoint && codePoint < 123) { - ch = smallCaps[ch]; - } - } - const text = makeText(ch, group.mode, style); - const variant = getVariant(group, style) || "normal"; - - let node; - if (numberRegEx.test(group.text)) { - const tag = group.mode === "text" ? "mtext" : "mn"; - if (variant === "italic" || variant === "bold-italic") { - return italicNumber(text, variant, tag) - } else { - if (variant !== "normal") { - text.text = text.text.split("").map(c => variantChar(c, variant)).join(""); - } - node = new MathNode(tag, [text]); - } - } else if (group.mode === "text") { - if (variant !== "normal") { - text.text = variantChar(text.text, variant); - } - node = new MathNode("mtext", [text]); - } else if (primes.has(group.text)) { - node = new MathNode("mo", [text]); - // TODO: If/when Chromium uses ssty variant for prime, remove the next line. - node.classes.push("tml-prime"); - } else { - const origText = text.text; - if (variant !== "italic") { - text.text = variantChar(text.text, variant); - } - node = new MathNode("mi", [text]); - if (text.text === origText && latinRegEx.test(origText)) { - node.setAttribute("mathvariant", "italic"); - } - } - return node - } - }); - - // A map of CSS-based spacing functions to their CSS class. - const cssSpace = { - "\\nobreak": "nobreak", - "\\allowbreak": "allowbreak" - }; - - // A lookup table to determine whether a spacing function/symbol should be - // treated like a regular space character. If a symbol or command is a key - // in this table, then it should be a regular space character. Furthermore, - // the associated value may have a `className` specifying an extra CSS class - // to add to the created `span`. - const regularSpace = { - " ": {}, - "\\ ": {}, - "~": { - className: "nobreak" - }, - "\\space": {}, - "\\nobreakspace": { - className: "nobreak" - } - }; - - // ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in - // src/symbols.js. - defineFunctionBuilders({ - type: "spacing", - mathmlBuilder(group, style) { - let node; - - if (Object.prototype.hasOwnProperty.call(regularSpace, group.text)) { - // Firefox does not render a space in a . So write a no-break space. - // TODO: If Firefox fixes that bug, uncomment the next line and write ch into the node. - //const ch = (regularSpace[group.text].className === "nobreak") ? "\u00a0" : " " - node = new MathNode("mtext", [new TextNode("\u00a0")]); - } else if (Object.prototype.hasOwnProperty.call(cssSpace, group.text)) { - // MathML 3.0 calls for nobreak to occur in an , not an - // Ref: https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs - node = new MathNode("mo"); - if (group.text === "\\nobreak") { - node.setAttribute("linebreak", "nobreak"); - } - } else { - throw new ParseError(`Unknown type of space "${group.text}"`) - } - - return node - } - }); - - defineFunctionBuilders({ - type: "tag" - }); - - // For a \tag, the work usually done in a mathmlBuilder is instead done in buildMathML.js. - // That way, a \tag can be pulled out of the parse tree and wrapped around the outer node. - - // Non-mathy text, possibly in a font - const textFontFamilies = { - "\\text": undefined, - "\\textrm": "textrm", - "\\textsf": "textsf", - "\\texttt": "texttt", - "\\textnormal": "textrm", - "\\textsc": "textsc" // small caps - }; - - const textFontWeights = { - "\\textbf": "textbf", - "\\textmd": "textmd" - }; - - const textFontShapes = { - "\\textit": "textit", - "\\textup": "textup" - }; - - const styleWithFont = (group, style) => { - const font = group.font; - // Checks if the argument is a font family or a font style. - if (!font) { - return style; - } else if (textFontFamilies[font]) { - return style.withTextFontFamily(textFontFamilies[font]); - } else if (textFontWeights[font]) { - return style.withTextFontWeight(textFontWeights[font]); - } else if (font === "\\emph") { - return style.fontShape === "textit" - ? style.withTextFontShape("textup") - : style.withTextFontShape("textit") - } - return style.withTextFontShape(textFontShapes[font]) - }; - - defineFunction({ - type: "text", - names: [ - // Font families - "\\text", - "\\textrm", - "\\textsf", - "\\texttt", - "\\textnormal", - "\\textsc", - // Font weights - "\\textbf", - "\\textmd", - // Font Shapes - "\\textit", - "\\textup", - "\\emph" - ], - props: { - numArgs: 1, - argTypes: ["text"], - allowedInArgument: true, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "text", - mode: parser.mode, - body: ordargument(body), - font: funcName - }; - }, - mathmlBuilder(group, style) { - const newStyle = styleWithFont(group, style); - const mrow = buildExpressionRow(group.body, newStyle); - return consolidateText(mrow) - } - }); - - // \vcenter: Vertically center the argument group on the math axis. - - defineFunction({ - type: "vcenter", - names: ["\\vcenter"], - props: { - numArgs: 1, - argTypes: ["original"], - allowedInText: false - }, - handler({ parser }, args) { - return { - type: "vcenter", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - // Use a math table to create vertically centered content. - const mtd = new MathNode("mtd", [buildGroup$1(group.body, style)]); - mtd.style.padding = "0"; - const mtr = new MathNode("mtr", [mtd]); - return new MathNode("mtable", [mtr]) - } - }); - - defineFunction({ - type: "verb", - names: ["\\verb"], - props: { - numArgs: 0, - allowedInText: true - }, - handler(context, args, optArgs) { - // \verb and \verb* are dealt with directly in Parser.js. - // If we end up here, it's because of a failure to match the two delimiters - // in the regex in Lexer.js. LaTeX raises the following error when \verb is - // terminated by end of line (or file). - throw new ParseError("\\verb ended by end of line instead of matching delimiter"); - }, - mathmlBuilder(group, style) { - const text = new TextNode(makeVerb(group)); - const node = new MathNode("mtext", [text]); - node.setAttribute("mathvariant", "monospace"); - return node; - } - }); - - /** - * Converts verb group into body string. - * - * \verb* replaces each space with an open box \u2423 - * \verb replaces each space with a no-break space \xA0 - */ - const makeVerb = (group) => group.body.replace(/ /g, group.star ? "\u2423" : "\xA0"); - - /** Include this to ensure that all functions are defined. */ - - const functions = _functions; - - /** - * The Lexer class handles tokenizing the input in various ways. Since our - * parser expects us to be able to backtrack, the lexer allows lexing from any - * given starting point. - * - * Its main exposed function is the `lex` function, which takes a position to - * lex from and a type of token to lex. It defers to the appropriate `_innerLex` - * function. - * - * The various `_innerLex` functions perform the actual lexing of different - * kinds. - */ - - - /* The following tokenRegex - * - matches typical whitespace (but not NBSP etc.) using its first two groups - * - does not match any control character \x00-\x1f except whitespace - * - does not match a bare backslash - * - matches any ASCII character except those just mentioned - * - does not match the BMP private use area \uE000-\uF8FF - * - does not match bare surrogate code units - * - matches any BMP character except for those just described - * - matches any valid Unicode surrogate pair - * - mathches numerals - * - matches a backslash followed by one or more whitespace characters - * - matches a backslash followed by one or more letters then whitespace - * - matches a backslash followed by any BMP character - * Capturing groups: - * [1] regular whitespace - * [2] backslash followed by whitespace - * [3] anything else, which may include: - * [4] left character of \verb* - * [5] left character of \verb - * [6] backslash followed by word, excluding any trailing whitespace - * Just because the Lexer matches something doesn't mean it's valid input: - * If there is no matching function or symbol definition, the Parser will - * still reject the input. - */ - const spaceRegexString = "[ \r\n\t]"; - const controlWordRegexString = "\\\\[a-zA-Z@]+"; - const controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]"; - const controlWordWhitespaceRegexString = `(${controlWordRegexString})${spaceRegexString}*`; - const controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*"; - const combiningDiacriticalMarkString = "[\u0300-\u036f]"; - const combiningDiacriticalMarksEndRegex = new RegExp(`${combiningDiacriticalMarkString}+$`); - const tokenRegexString = - `(${spaceRegexString}+)|` + // whitespace - `${controlSpaceRegexString}|` + // whitespace - "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint - `${combiningDiacriticalMarkString}*` + // ...plus accents - "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair - `${combiningDiacriticalMarkString}*` + // ...plus accents - "|\\\\verb\\*([^]).*?\\4" + // \verb* - "|\\\\verb([^*a-zA-Z]).*?\\5" + // \verb unstarred - `|${controlWordWhitespaceRegexString}` + // \macroName + spaces - `|${controlSymbolRegexString})`; // \\, \', etc. - - /** Main Lexer class */ - class Lexer { - constructor(input, settings) { - // Separate accents from characters - this.input = input; - this.settings = settings; - this.tokenRegex = new RegExp(tokenRegexString, 'g'); - // Category codes. The lexer only supports comment characters (14) for now. - // MacroExpander additionally distinguishes active (13). - this.catcodes = { - "%": 14, // comment character - "~": 13 // active character - }; - } - - setCatcode(char, code) { - this.catcodes[char] = code; - } - - /** - * This function lexes a single token. - */ - lex() { - const input = this.input; - const pos = this.tokenRegex.lastIndex; - if (pos === input.length) { - return new Token("EOF", new SourceLocation(this, pos, pos)); - } - const match = this.tokenRegex.exec(input); - if (match === null || match.index !== pos) { - throw new ParseError( - `Unexpected character: '${input[pos]}'`, - new Token(input[pos], new SourceLocation(this, pos, pos + 1)) - ); - } - const text = match[6] || match[3] || (match[2] ? "\\ " : " "); - - if (this.catcodes[text] === 14) { - // comment character - const nlIndex = input.indexOf("\n", this.tokenRegex.lastIndex); - if (nlIndex === -1) { - this.tokenRegex.lastIndex = input.length; // EOF - if (this.settings.strict) { - throw new ParseError("% comment has no terminating newline; LaTeX would " + - "fail because of commenting the end of math mode") - } - } else { - this.tokenRegex.lastIndex = nlIndex + 1; - } - return this.lex(); - } - - return new Token(text, new SourceLocation(this, pos, this.tokenRegex.lastIndex)); - } - } - - /** - * A `Namespace` refers to a space of nameable things like macros or lengths, - * which can be `set` either globally or local to a nested group, using an - * undo stack similar to how TeX implements this functionality. - * Performance-wise, `get` and local `set` take constant time, while global - * `set` takes time proportional to the depth of group nesting. - */ - - - class Namespace { - /** - * Both arguments are optional. The first argument is an object of - * built-in mappings which never change. The second argument is an object - * of initial (global-level) mappings, which will constantly change - * according to any global/top-level `set`s done. - */ - constructor(builtins = {}, globalMacros = {}) { - this.current = globalMacros; - this.builtins = builtins; - this.undefStack = []; - } - - /** - * Start a new nested group, affecting future local `set`s. - */ - beginGroup() { - this.undefStack.push({}); - } - - /** - * End current nested group, restoring values before the group began. - */ - endGroup() { - if (this.undefStack.length === 0) { - throw new ParseError( - "Unbalanced namespace destruction: attempt " + - "to pop global namespace; please report this as a bug" - ); - } - const undefs = this.undefStack.pop(); - for (const undef in undefs) { - if (Object.prototype.hasOwnProperty.call(undefs, undef )) { - if (undefs[undef] === undefined) { - delete this.current[undef]; - } else { - this.current[undef] = undefs[undef]; - } - } - } - } - - /** - * Detect whether `name` has a definition. Equivalent to - * `get(name) != null`. - */ - has(name) { - return Object.prototype.hasOwnProperty.call(this.current, name ) || - Object.prototype.hasOwnProperty.call(this.builtins, name ); - } - - /** - * Get the current value of a name, or `undefined` if there is no value. - * - * Note: Do not use `if (namespace.get(...))` to detect whether a macro - * is defined, as the definition may be the empty string which evaluates - * to `false` in JavaScript. Use `if (namespace.get(...) != null)` or - * `if (namespace.has(...))`. - */ - get(name) { - if (Object.prototype.hasOwnProperty.call(this.current, name )) { - return this.current[name]; - } else { - return this.builtins[name]; - } - } - - /** - * Set the current value of a name, and optionally set it globally too. - * Local set() sets the current value and (when appropriate) adds an undo - * operation to the undo stack. Global set() may change the undo - * operation at every level, so takes time linear in their number. - */ - set(name, value, global = false) { - if (global) { - // Global set is equivalent to setting in all groups. Simulate this - // by destroying any undos currently scheduled for this name, - // and adding an undo with the *new* value (in case it later gets - // locally reset within this environment). - for (let i = 0; i < this.undefStack.length; i++) { - delete this.undefStack[i][name]; - } - if (this.undefStack.length > 0) { - this.undefStack[this.undefStack.length - 1][name] = value; - } - } else { - // Undo this set at end of this group (possibly to `undefined`), - // unless an undo is already in place, in which case that older - // value is the correct one. - const top = this.undefStack[this.undefStack.length - 1]; - if (top && !Object.prototype.hasOwnProperty.call(top, name )) { - top[name] = this.current[name]; - } - } - this.current[name] = value; - } - } - - /** - * This file contains the “gullet” where macros are expanded - * until only non-macro tokens remain. - */ - - - // List of commands that act like macros but aren't defined as a macro, - // function, or symbol. Used in `isDefined`. - const implicitCommands = { - "^": true, // Parser.js - _: true, // Parser.js - "\\limits": true, // Parser.js - "\\nolimits": true // Parser.js - }; - - class MacroExpander { - constructor(input, settings, mode) { - this.settings = settings; - this.expansionCount = 0; - this.feed(input); - // Make new global namespace - this.macros = new Namespace(macros, settings.macros); - this.mode = mode; - this.stack = []; // contains tokens in REVERSE order - } - - /** - * Feed a new input string to the same MacroExpander - * (with existing macros etc.). - */ - feed(input) { - this.lexer = new Lexer(input, this.settings); - } - - /** - * Switches between "text" and "math" modes. - */ - switchMode(newMode) { - this.mode = newMode; - } - - /** - * Start a new group nesting within all namespaces. - */ - beginGroup() { - this.macros.beginGroup(); - } - - /** - * End current group nesting within all namespaces. - */ - endGroup() { - this.macros.endGroup(); - } - - /** - * Returns the topmost token on the stack, without expanding it. - * Similar in behavior to TeX's `\futurelet`. - */ - future() { - if (this.stack.length === 0) { - this.pushToken(this.lexer.lex()); - } - return this.stack[this.stack.length - 1] - } - - /** - * Remove and return the next unexpanded token. - */ - popToken() { - this.future(); // ensure non-empty stack - return this.stack.pop(); - } - - /** - * Add a given token to the token stack. In particular, this get be used - * to put back a token returned from one of the other methods. - */ - pushToken(token) { - this.stack.push(token); - } - - /** - * Append an array of tokens to the token stack. - */ - pushTokens(tokens) { - this.stack.push(...tokens); - } - - /** - * Find an macro argument without expanding tokens and append the array of - * tokens to the token stack. Uses Token as a container for the result. - */ - scanArgument(isOptional) { - let start; - let end; - let tokens; - if (isOptional) { - this.consumeSpaces(); // \@ifnextchar gobbles any space following it - if (this.future().text !== "[") { - return null; - } - start = this.popToken(); // don't include [ in tokens - ({ tokens, end } = this.consumeArg(["]"])); - } else { - ({ tokens, start, end } = this.consumeArg()); - } - - // indicate the end of an argument - this.pushToken(new Token("EOF", end.loc)); - - this.pushTokens(tokens); - return start.range(end, ""); - } - - /** - * Consume all following space tokens, without expansion. - */ - consumeSpaces() { - for (;;) { - const token = this.future(); - if (token.text === " ") { - this.stack.pop(); - } else { - break; - } - } - } - - /** - * Consume an argument from the token stream, and return the resulting array - * of tokens and start/end token. - */ - consumeArg(delims) { - // The argument for a delimited parameter is the shortest (possibly - // empty) sequence of tokens with properly nested {...} groups that is - // followed ... by this particular list of non-parameter tokens. - // The argument for an undelimited parameter is the next nonblank - // token, unless that token is ‘{’, when the argument will be the - // entire {...} group that follows. - const tokens = []; - const isDelimited = delims && delims.length > 0; - if (!isDelimited) { - // Ignore spaces between arguments. As the TeXbook says: - // "After you have said ‘\def\row#1#2{...}’, you are allowed to - // put spaces between the arguments (e.g., ‘\row x n’), because - // TeX doesn’t use single spaces as undelimited arguments." - this.consumeSpaces(); - } - const start = this.future(); - let tok; - let depth = 0; - let match = 0; - do { - tok = this.popToken(); - tokens.push(tok); - if (tok.text === "{") { - ++depth; - } else if (tok.text === "}") { - --depth; - if (depth === -1) { - throw new ParseError("Extra }", tok); - } - } else if (tok.text === "EOF") { - throw new ParseError( - "Unexpected end of input in a macro argument" + - ", expected '" + - (delims && isDelimited ? delims[match] : "}") + - "'", - tok - ); - } - if (delims && isDelimited) { - if ((depth === 0 || (depth === 1 && delims[match] === "{")) && tok.text === delims[match]) { - ++match; - if (match === delims.length) { - // don't include delims in tokens - tokens.splice(-match, match); - break; - } - } else { - match = 0; - } - } - } while (depth !== 0 || isDelimited); - // If the argument found ... has the form ‘{}’, - // ... the outermost braces enclosing the argument are removed - if (start.text === "{" && tokens[tokens.length - 1].text === "}") { - tokens.pop(); - tokens.shift(); - } - tokens.reverse(); // to fit in with stack order - return { tokens, start, end: tok }; - } - - /** - * Consume the specified number of (delimited) arguments from the token - * stream and return the resulting array of arguments. - */ - consumeArgs(numArgs, delimiters) { - if (delimiters) { - if (delimiters.length !== numArgs + 1) { - throw new ParseError("The length of delimiters doesn't match the number of args!"); - } - const delims = delimiters[0]; - for (let i = 0; i < delims.length; i++) { - const tok = this.popToken(); - if (delims[i] !== tok.text) { - throw new ParseError("Use of the macro doesn't match its definition", tok); - } - } - } - - const args = []; - for (let i = 0; i < numArgs; i++) { - args.push(this.consumeArg(delimiters && delimiters[i + 1]).tokens); - } - return args; - } - - /** - * Expand the next token only once if possible. - * - * If the token is expanded, the resulting tokens will be pushed onto - * the stack in reverse order, and the number of such tokens will be - * returned. This number might be zero or positive. - * - * If not, the return value is `false`, and the next token remains at the - * top of the stack. - * - * In either case, the next token will be on the top of the stack, - * or the stack will be empty (in case of empty expansion - * and no other tokens). - * - * Used to implement `expandAfterFuture` and `expandNextToken`. - * - * If expandableOnly, only expandable tokens are expanded and - * an undefined control sequence results in an error. - */ - expandOnce(expandableOnly) { - const topToken = this.popToken(); - const name = topToken.text; - const expansion = !topToken.noexpand ? this._getExpansion(name) : null; - if (expansion == null || (expandableOnly && expansion.unexpandable)) { - if (expandableOnly && expansion == null && name[0] === "\\" && !this.isDefined(name)) { - throw new ParseError("Undefined control sequence: " + name); - } - this.pushToken(topToken); - return false; - } - this.expansionCount++; - if (this.expansionCount > this.settings.maxExpand) { - throw new ParseError( - "Too many expansions: infinite loop or " + "need to increase maxExpand setting" - ); - } - let tokens = expansion.tokens; - const args = this.consumeArgs(expansion.numArgs, expansion.delimiters); - if (expansion.numArgs) { - // paste arguments in place of the placeholders - tokens = tokens.slice(); // make a shallow copy - for (let i = tokens.length - 1; i >= 0; --i) { - let tok = tokens[i]; - if (tok.text === "#") { - if (i === 0) { - throw new ParseError("Incomplete placeholder at end of macro body", tok); - } - tok = tokens[--i]; // next token on stack - if (tok.text === "#") { - // ## → # - tokens.splice(i + 1, 1); // drop first # - } else if (/^[1-9]$/.test(tok.text)) { - // replace the placeholder with the indicated argument - tokens.splice(i, 2, ...args[+tok.text - 1]); - } else { - throw new ParseError("Not a valid argument number", tok); - } - } - } - } - // Concatenate expansion onto top of stack. - this.pushTokens(tokens); - return tokens.length; - } - - /** - * Expand the next token only once (if possible), and return the resulting - * top token on the stack (without removing anything from the stack). - * Similar in behavior to TeX's `\expandafter\futurelet`. - * Equivalent to expandOnce() followed by future(). - */ - expandAfterFuture() { - this.expandOnce(); - return this.future(); - } - - /** - * Recursively expand first token, then return first non-expandable token. - */ - expandNextToken() { - for (;;) { - if (this.expandOnce() === false) { // fully expanded - const token = this.stack.pop(); - // The token after \noexpand is interpreted as if its meaning were ‘\relax’ - if (token.treatAsRelax) { - token.text = "\\relax"; - } - return token - } - } - - // This pathway is impossible. - throw new Error(); // eslint-disable-line no-unreachable - } - - /** - * Fully expand the given macro name and return the resulting list of - * tokens, or return `undefined` if no such macro is defined. - */ - expandMacro(name) { - return this.macros.has(name) ? this.expandTokens([new Token(name)]) : undefined; - } - - /** - * Fully expand the given token stream and return the resulting list of - * tokens. Note that the input tokens are in reverse order, but the - * output tokens are in forward order. - */ - expandTokens(tokens) { - const output = []; - const oldStackLength = this.stack.length; - this.pushTokens(tokens); - while (this.stack.length > oldStackLength) { - // Expand only expandable tokens - if (this.expandOnce(true) === false) { // fully expanded - const token = this.stack.pop(); - if (token.treatAsRelax) { - // the expansion of \noexpand is the token itself - token.noexpand = false; - token.treatAsRelax = false; - } - output.push(token); - } - } - return output; - } - - /** - * Fully expand the given macro name and return the result as a string, - * or return `undefined` if no such macro is defined. - */ - expandMacroAsText(name) { - const tokens = this.expandMacro(name); - if (tokens) { - return tokens.map((token) => token.text).join(""); - } else { - return tokens; - } - } - - /** - * Returns the expanded macro as a reversed array of tokens and a macro - * argument count. Or returns `null` if no such macro. - */ - _getExpansion(name) { - const definition = this.macros.get(name); - if (definition == null) { - // mainly checking for undefined here - return definition; - } - // If a single character has an associated catcode other than 13 - // (active character), then don't expand it. - if (name.length === 1) { - const catcode = this.lexer.catcodes[name]; - if (catcode != null && catcode !== 13) { - return - } - } - const expansion = typeof definition === "function" ? definition(this) : definition; - if (typeof expansion === "string") { - let numArgs = 0; - if (expansion.indexOf("#") !== -1) { - const stripped = expansion.replace(/##/g, ""); - while (stripped.indexOf("#" + (numArgs + 1)) !== -1) { - ++numArgs; - } - } - const bodyLexer = new Lexer(expansion, this.settings); - const tokens = []; - let tok = bodyLexer.lex(); - while (tok.text !== "EOF") { - tokens.push(tok); - tok = bodyLexer.lex(); - } - tokens.reverse(); // to fit in with stack using push and pop - const expanded = { tokens, numArgs }; - return expanded; - } - - return expansion; - } - - /** - * Determine whether a command is currently "defined" (has some - * functionality), meaning that it's a macro (in the current group), - * a function, a symbol, or one of the special commands listed in - * `implicitCommands`. - */ - isDefined(name) { - return ( - this.macros.has(name) || - Object.prototype.hasOwnProperty.call(functions, name ) || - Object.prototype.hasOwnProperty.call(symbols.math, name ) || - Object.prototype.hasOwnProperty.call(symbols.text, name ) || - Object.prototype.hasOwnProperty.call(implicitCommands, name ) - ); - } - - /** - * Determine whether a command is expandable. - */ - isExpandable(name) { - const macro = this.macros.get(name); - return macro != null - ? typeof macro === "string" || typeof macro === "function" || !macro.unexpandable - : Object.prototype.hasOwnProperty.call(functions, name ) && !functions[name].primitive; - } - } - - // Helpers for Parser.js handling of Unicode (sub|super)script characters. - - const unicodeSubRegEx = /^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/; - - const uSubsAndSups = Object.freeze({ - '₊': '+', - '₋': '-', - '₌': '=', - '₍': '(', - '₎': ')', - '₀': '0', - '₁': '1', - '₂': '2', - '₃': '3', - '₄': '4', - '₅': '5', - '₆': '6', - '₇': '7', - '₈': '8', - '₉': '9', - '\u2090': 'a', - '\u2091': 'e', - '\u2095': 'h', - '\u1D62': 'i', - '\u2C7C': 'j', - '\u2096': 'k', - '\u2097': 'l', - '\u2098': 'm', - '\u2099': 'n', - '\u2092': 'o', - '\u209A': 'p', - '\u1D63': 'r', - '\u209B': 's', - '\u209C': 't', - '\u1D64': 'u', - '\u1D65': 'v', - '\u2093': 'x', - '\u1D66': 'β', - '\u1D67': 'γ', - '\u1D68': 'ρ', - '\u1D69': '\u03d5', - '\u1D6A': 'χ', - '⁺': '+', - '⁻': '-', - '⁼': '=', - '⁽': '(', - '⁾': ')', - '⁰': '0', - '¹': '1', - '²': '2', - '³': '3', - '⁴': '4', - '⁵': '5', - '⁶': '6', - '⁷': '7', - '⁸': '8', - '⁹': '9', - '\u1D2C': 'A', - '\u1D2E': 'B', - '\u1D30': 'D', - '\u1D31': 'E', - '\u1D33': 'G', - '\u1D34': 'H', - '\u1D35': 'I', - '\u1D36': 'J', - '\u1D37': 'K', - '\u1D38': 'L', - '\u1D39': 'M', - '\u1D3A': 'N', - '\u1D3C': 'O', - '\u1D3E': 'P', - '\u1D3F': 'R', - '\u1D40': 'T', - '\u1D41': 'U', - '\u2C7D': 'V', - '\u1D42': 'W', - '\u1D43': 'a', - '\u1D47': 'b', - '\u1D9C': 'c', - '\u1D48': 'd', - '\u1D49': 'e', - '\u1DA0': 'f', - '\u1D4D': 'g', - '\u02B0': 'h', - '\u2071': 'i', - '\u02B2': 'j', - '\u1D4F': 'k', - '\u02E1': 'l', - '\u1D50': 'm', - '\u207F': 'n', - '\u1D52': 'o', - '\u1D56': 'p', - '\u02B3': 'r', - '\u02E2': 's', - '\u1D57': 't', - '\u1D58': 'u', - '\u1D5B': 'v', - '\u02B7': 'w', - '\u02E3': 'x', - '\u02B8': 'y', - '\u1DBB': 'z', - '\u1D5D': 'β', - '\u1D5E': 'γ', - '\u1D5F': 'δ', - '\u1D60': '\u03d5', - '\u1D61': 'χ', - '\u1DBF': 'θ' - }); - - // Used for Unicode input of calligraphic and script letters - const asciiFromScript = Object.freeze({ - "\ud835\udc9c": "A", - "\u212c": "B", - "\ud835\udc9e": "C", - "\ud835\udc9f": "D", - "\u2130": "E", - "\u2131": "F", - "\ud835\udca2": "G", - "\u210B": "H", - "\u2110": "I", - "\ud835\udca5": "J", - "\ud835\udca6": "K", - "\u2112": "L", - "\u2133": "M", - "\ud835\udca9": "N", - "\ud835\udcaa": "O", - "\ud835\udcab": "P", - "\ud835\udcac": "Q", - "\u211B": "R", - "\ud835\udcae": "S", - "\ud835\udcaf": "T", - "\ud835\udcb0": "U", - "\ud835\udcb1": "V", - "\ud835\udcb2": "W", - "\ud835\udcb3": "X", - "\ud835\udcb4": "Y", - "\ud835\udcb5": "Z" - }); - - // Mapping of Unicode accent characters to their LaTeX equivalent in text and - // math mode (when they exist). - var unicodeAccents = { - "\u0301": { text: "\\'", math: "\\acute" }, - "\u0300": { text: "\\`", math: "\\grave" }, - "\u0308": { text: '\\"', math: "\\ddot" }, - "\u0303": { text: "\\~", math: "\\tilde" }, - "\u0304": { text: "\\=", math: "\\bar" }, - "\u0306": { text: "\\u", math: "\\breve" }, - "\u030c": { text: "\\v", math: "\\check" }, - "\u0302": { text: "\\^", math: "\\hat" }, - "\u0307": { text: "\\.", math: "\\dot" }, - "\u030a": { text: "\\r", math: "\\mathring" }, - "\u030b": { text: "\\H" }, - '\u0327': { text: '\\c' } - }; - - var unicodeSymbols = { - "á": "á", - "à": "à", - "ä": "ä", - "ǟ": "ǟ", - "ã": "ã", - "ā": "ā", - "ă": "ă", - "ắ": "ắ", - "ằ": "ằ", - "ẵ": "ẵ", - "ǎ": "ǎ", - "â": "â", - "ấ": "ấ", - "ầ": "ầ", - "ẫ": "ẫ", - "ȧ": "ȧ", - "ǡ": "ǡ", - "å": "å", - "ǻ": "ǻ", - "ḃ": "ḃ", - "ć": "ć", - "č": "č", - "ĉ": "ĉ", - "ċ": "ċ", - "ď": "ď", - "ḋ": "ḋ", - "é": "é", - "è": "è", - "ë": "ë", - "ẽ": "ẽ", - "ē": "ē", - "ḗ": "ḗ", - "ḕ": "ḕ", - "ĕ": "ĕ", - "ě": "ě", - "ê": "ê", - "ế": "ế", - "ề": "ề", - "ễ": "ễ", - "ė": "ė", - "ḟ": "ḟ", - "ǵ": "ǵ", - "ḡ": "ḡ", - "ğ": "ğ", - "ǧ": "ǧ", - "ĝ": "ĝ", - "ġ": "ġ", - "ḧ": "ḧ", - "ȟ": "ȟ", - "ĥ": "ĥ", - "ḣ": "ḣ", - "í": "í", - "ì": "ì", - "ï": "ï", - "ḯ": "ḯ", - "ĩ": "ĩ", - "ī": "ī", - "ĭ": "ĭ", - "ǐ": "ǐ", - "î": "î", - "ǰ": "ǰ", - "ĵ": "ĵ", - "ḱ": "ḱ", - "ǩ": "ǩ", - "ĺ": "ĺ", - "ľ": "ľ", - "ḿ": "ḿ", - "ṁ": "ṁ", - "ń": "ń", - "ǹ": "ǹ", - "ñ": "ñ", - "ň": "ň", - "ṅ": "ṅ", - "ó": "ó", - "ò": "ò", - "ö": "ö", - "ȫ": "ȫ", - "õ": "õ", - "ṍ": "ṍ", - "ṏ": "ṏ", - "ȭ": "ȭ", - "ō": "ō", - "ṓ": "ṓ", - "ṑ": "ṑ", - "ŏ": "ŏ", - "ǒ": "ǒ", - "ô": "ô", - "ố": "ố", - "ồ": "ồ", - "ỗ": "ỗ", - "ȯ": "ȯ", - "ȱ": "ȱ", - "ő": "ő", - "ṕ": "ṕ", - "ṗ": "ṗ", - "ŕ": "ŕ", - "ř": "ř", - "ṙ": "ṙ", - "ś": "ś", - "ṥ": "ṥ", - "š": "š", - "ṧ": "ṧ", - "ŝ": "ŝ", - "ṡ": "ṡ", - "ẗ": "ẗ", - "ť": "ť", - "ṫ": "ṫ", - "ú": "ú", - "ù": "ù", - "ü": "ü", - "ǘ": "ǘ", - "ǜ": "ǜ", - "ǖ": "ǖ", - "ǚ": "ǚ", - "ũ": "ũ", - "ṹ": "ṹ", - "ū": "ū", - "ṻ": "ṻ", - "ŭ": "ŭ", - "ǔ": "ǔ", - "û": "û", - "ů": "ů", - "ű": "ű", - "ṽ": "ṽ", - "ẃ": "ẃ", - "ẁ": "ẁ", - "ẅ": "ẅ", - "ŵ": "ŵ", - "ẇ": "ẇ", - "ẘ": "ẘ", - "ẍ": "ẍ", - "ẋ": "ẋ", - "ý": "ý", - "ỳ": "ỳ", - "ÿ": "ÿ", - "ỹ": "ỹ", - "ȳ": "ȳ", - "ŷ": "ŷ", - "ẏ": "ẏ", - "ẙ": "ẙ", - "ź": "ź", - "ž": "ž", - "ẑ": "ẑ", - "ż": "ż", - "Á": "Á", - "À": "À", - "Ä": "Ä", - "Ǟ": "Ǟ", - "Ã": "Ã", - "Ā": "Ā", - "Ă": "Ă", - "Ắ": "Ắ", - "Ằ": "Ằ", - "Ẵ": "Ẵ", - "Ǎ": "Ǎ", - "Â": "Â", - "Ấ": "Ấ", - "Ầ": "Ầ", - "Ẫ": "Ẫ", - "Ȧ": "Ȧ", - "Ǡ": "Ǡ", - "Å": "Å", - "Ǻ": "Ǻ", - "Ḃ": "Ḃ", - "Ć": "Ć", - "Č": "Č", - "Ĉ": "Ĉ", - "Ċ": "Ċ", - "Ď": "Ď", - "Ḋ": "Ḋ", - "É": "É", - "È": "È", - "Ë": "Ë", - "Ẽ": "Ẽ", - "Ē": "Ē", - "Ḗ": "Ḗ", - "Ḕ": "Ḕ", - "Ĕ": "Ĕ", - "Ě": "Ě", - "Ê": "Ê", - "Ế": "Ế", - "Ề": "Ề", - "Ễ": "Ễ", - "Ė": "Ė", - "Ḟ": "Ḟ", - "Ǵ": "Ǵ", - "Ḡ": "Ḡ", - "Ğ": "Ğ", - "Ǧ": "Ǧ", - "Ĝ": "Ĝ", - "Ġ": "Ġ", - "Ḧ": "Ḧ", - "Ȟ": "Ȟ", - "Ĥ": "Ĥ", - "Ḣ": "Ḣ", - "Í": "Í", - "Ì": "Ì", - "Ï": "Ï", - "Ḯ": "Ḯ", - "Ĩ": "Ĩ", - "Ī": "Ī", - "Ĭ": "Ĭ", - "Ǐ": "Ǐ", - "Î": "Î", - "İ": "İ", - "Ĵ": "Ĵ", - "Ḱ": "Ḱ", - "Ǩ": "Ǩ", - "Ĺ": "Ĺ", - "Ľ": "Ľ", - "Ḿ": "Ḿ", - "Ṁ": "Ṁ", - "Ń": "Ń", - "Ǹ": "Ǹ", - "Ñ": "Ñ", - "Ň": "Ň", - "Ṅ": "Ṅ", - "Ó": "Ó", - "Ò": "Ò", - "Ö": "Ö", - "Ȫ": "Ȫ", - "Õ": "Õ", - "Ṍ": "Ṍ", - "Ṏ": "Ṏ", - "Ȭ": "Ȭ", - "Ō": "Ō", - "Ṓ": "Ṓ", - "Ṑ": "Ṑ", - "Ŏ": "Ŏ", - "Ǒ": "Ǒ", - "Ô": "Ô", - "Ố": "Ố", - "Ồ": "Ồ", - "Ỗ": "Ỗ", - "Ȯ": "Ȯ", - "Ȱ": "Ȱ", - "Ő": "Ő", - "Ṕ": "Ṕ", - "Ṗ": "Ṗ", - "Ŕ": "Ŕ", - "Ř": "Ř", - "Ṙ": "Ṙ", - "Ś": "Ś", - "Ṥ": "Ṥ", - "Š": "Š", - "Ṧ": "Ṧ", - "Ŝ": "Ŝ", - "Ṡ": "Ṡ", - "Ť": "Ť", - "Ṫ": "Ṫ", - "Ú": "Ú", - "Ù": "Ù", - "Ü": "Ü", - "Ǘ": "Ǘ", - "Ǜ": "Ǜ", - "Ǖ": "Ǖ", - "Ǚ": "Ǚ", - "Ũ": "Ũ", - "Ṹ": "Ṹ", - "Ū": "Ū", - "Ṻ": "Ṻ", - "Ŭ": "Ŭ", - "Ǔ": "Ǔ", - "Û": "Û", - "Ů": "Ů", - "Ű": "Ű", - "Ṽ": "Ṽ", - "Ẃ": "Ẃ", - "Ẁ": "Ẁ", - "Ẅ": "Ẅ", - "Ŵ": "Ŵ", - "Ẇ": "Ẇ", - "Ẍ": "Ẍ", - "Ẋ": "Ẋ", - "Ý": "Ý", - "Ỳ": "Ỳ", - "Ÿ": "Ÿ", - "Ỹ": "Ỹ", - "Ȳ": "Ȳ", - "Ŷ": "Ŷ", - "Ẏ": "Ẏ", - "Ź": "Ź", - "Ž": "Ž", - "Ẑ": "Ẑ", - "Ż": "Ż", - "ά": "ά", - "ὰ": "ὰ", - "ᾱ": "ᾱ", - "ᾰ": "ᾰ", - "έ": "έ", - "ὲ": "ὲ", - "ή": "ή", - "ὴ": "ὴ", - "ί": "ί", - "ὶ": "ὶ", - "ϊ": "ϊ", - "ΐ": "ΐ", - "ῒ": "ῒ", - "ῑ": "ῑ", - "ῐ": "ῐ", - "ό": "ό", - "ὸ": "ὸ", - "ύ": "ύ", - "ὺ": "ὺ", - "ϋ": "ϋ", - "ΰ": "ΰ", - "ῢ": "ῢ", - "ῡ": "ῡ", - "ῠ": "ῠ", - "ώ": "ώ", - "ὼ": "ὼ", - "Ύ": "Ύ", - "Ὺ": "Ὺ", - "Ϋ": "Ϋ", - "Ῡ": "Ῡ", - "Ῠ": "Ῠ", - "Ώ": "Ώ", - "Ὼ": "Ὼ" - }; - - /* eslint no-constant-condition:0 */ - - const binLeftCancellers = ["bin", "op", "open", "punct", "rel"]; - const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; - const textRegEx = /^ *\\text/; - - /** - * This file contains the parser used to parse out a TeX expression from the - * input. Since TeX isn't context-free, standard parsers don't work particularly - * well. - * - * The strategy of this parser is as such: - * - * The main functions (the `.parse...` ones) take a position in the current - * parse string to parse tokens from. The lexer (found in Lexer.js, stored at - * this.gullet.lexer) also supports pulling out tokens at arbitrary places. When - * individual tokens are needed at a position, the lexer is called to pull out a - * token, which is then used. - * - * The parser has a property called "mode" indicating the mode that - * the parser is currently in. Currently it has to be one of "math" or - * "text", which denotes whether the current environment is a math-y - * one or a text-y one (e.g. inside \text). Currently, this serves to - * limit the functions which can be used in text mode. - * - * The main functions then return an object which contains the useful data that - * was parsed at its given point, and a new position at the end of the parsed - * data. The main functions can call each other and continue the parsing by - * using the returned position as a new starting point. - * - * There are also extra `.handle...` functions, which pull out some reused - * functionality into self-contained functions. - * - * The functions return ParseNodes. - */ - - class Parser { - constructor(input, settings, isPreamble = false) { - // Start in math mode - this.mode = "math"; - // Create a new macro expander (gullet) and (indirectly via that) also a - // new lexer (mouth) for this parser (stomach, in the language of TeX) - this.gullet = new MacroExpander(input, settings, this.mode); - // Store the settings for use in parsing - this.settings = settings; - // Are we defining a preamble? - this.isPreamble = isPreamble; - // Count leftright depth (for \middle errors) - this.leftrightDepth = 0; - this.prevAtomType = ""; - } - - /** - * Checks a result to make sure it has the right type, and throws an - * appropriate error otherwise. - */ - expect(text, consume = true) { - if (this.fetch().text !== text) { - throw new ParseError(`Expected '${text}', got '${this.fetch().text}'`, this.fetch()); - } - if (consume) { - this.consume(); - } - } - - /** - * Discards the current lookahead token, considering it consumed. - */ - consume() { - this.nextToken = null; - } - - /** - * Return the current lookahead token, or if there isn't one (at the - * beginning, or if the previous lookahead token was consume()d), - * fetch the next token as the new lookahead token and return it. - */ - fetch() { - if (this.nextToken == null) { - this.nextToken = this.gullet.expandNextToken(); - } - return this.nextToken; - } - - /** - * Switches between "text" and "math" modes. - */ - switchMode(newMode) { - this.mode = newMode; - this.gullet.switchMode(newMode); - } - - /** - * Main parsing function, which parses an entire input. - */ - parse() { - // Create a group namespace for every $...$, $$...$$, \[...\].) - // A \def is then valid only within that pair of delimiters. - this.gullet.beginGroup(); - - if (this.settings.colorIsTextColor) { - // Use old \color behavior (same as LaTeX's \textcolor) if requested. - // We do this within the group for the math expression, so it doesn't - // pollute settings.macros. - this.gullet.macros.set("\\color", "\\textcolor"); - } - - // Try to parse the input - const parse = this.parseExpression(false); - - // If we succeeded, make sure there's an EOF at the end - this.expect("EOF"); - - if (this.isPreamble) { - const macros = Object.create(null); - Object.entries(this.gullet.macros.current).forEach(([key, value]) => { - macros[key] = value; - }); - this.gullet.endGroup(); - return macros - } - - // The only local macro that we want to save is from \tag. - const tag = this.gullet.macros.get("\\df@tag"); - - // End the group namespace for the expression - this.gullet.endGroup(); - - if (tag) { this.gullet.macros.current["\\df@tag"] = tag; } - - return parse; - } - - static get endOfExpression() { - return ["}", "\\endgroup", "\\end", "\\right", "\\endtoggle", "&"]; - } - - /** - * Fully parse a separate sequence of tokens as a separate job. - * Tokens should be specified in reverse order, as in a MacroDefinition. - */ - subparse(tokens) { - // Save the next token from the current job. - const oldToken = this.nextToken; - this.consume(); - - // Run the new job, terminating it with an excess '}' - this.gullet.pushToken(new Token("}")); - this.gullet.pushTokens(tokens); - const parse = this.parseExpression(false); - this.expect("}"); - - // Restore the next token from the current job. - this.nextToken = oldToken; - - return parse; - } - - /** - * Parses an "expression", which is a list of atoms. - * - * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This - * happens when functions have higher precedence than infix - * nodes in implicit parses. - * - * `breakOnTokenText`: The text of the token that the expression should end - * with, or `null` if something else should end the - * expression. - * - * `breakOnMiddle`: \color, \over, and old styling functions work on an implicit group. - * These groups end just before the usual tokens, but they also - * end just before `\middle`. - */ - parseExpression(breakOnInfix, breakOnTokenText, breakOnMiddle) { - const body = []; - this.prevAtomType = ""; - // Keep adding atoms to the body until we can't parse any more atoms (either - // we reached the end, a }, or a \right) - while (true) { - // Ignore spaces in math mode - if (this.mode === "math") { - this.consumeSpaces(); - } - const lex = this.fetch(); - if (Parser.endOfExpression.indexOf(lex.text) !== -1) { - break; - } - if (breakOnTokenText && lex.text === breakOnTokenText) { - break; - } - if (breakOnMiddle && lex.text === "\\middle") { - break - } - if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) { - break; - } - const atom = this.parseAtom(breakOnTokenText); - if (!atom) { - break; - } else if (atom.type === "internal") { - // Internal nodes do not appear in parse tree - continue; - } - body.push(atom); - // Keep a record of the atom type, so that op.js can set correct spacing. - this.prevAtomType = atom.type === "atom" ? atom.family : atom.type; - } - if (this.mode === "text") { - this.formLigatures(body); - } - return this.handleInfixNodes(body); - } - - /** - * Rewrites infix operators such as \over with corresponding commands such - * as \frac. - * - * There can only be one infix operator per group. If there's more than one - * then the expression is ambiguous. This can be resolved by adding {}. - */ - handleInfixNodes(body) { - let overIndex = -1; - let funcName; - - for (let i = 0; i < body.length; i++) { - if (body[i].type === "infix") { - if (overIndex !== -1) { - throw new ParseError("only one infix operator per group", body[i].token); - } - overIndex = i; - funcName = body[i].replaceWith; - } - } - - if (overIndex !== -1 && funcName) { - let numerNode; - let denomNode; - - const numerBody = body.slice(0, overIndex); - const denomBody = body.slice(overIndex + 1); - - if (numerBody.length === 1 && numerBody[0].type === "ordgroup") { - numerNode = numerBody[0]; - } else { - numerNode = { type: "ordgroup", mode: this.mode, body: numerBody }; - } - - if (denomBody.length === 1 && denomBody[0].type === "ordgroup") { - denomNode = denomBody[0]; - } else { - denomNode = { type: "ordgroup", mode: this.mode, body: denomBody }; - } - - let node; - if (funcName === "\\\\abovefrac") { - node = this.callFunction(funcName, [numerNode, body[overIndex], denomNode], []); - } else { - node = this.callFunction(funcName, [numerNode, denomNode], []); - } - return [node]; - } else { - return body; - } - } - - /** - * Handle a subscript or superscript with nice errors. - */ - handleSupSubscript( - name // For error reporting. - ) { - const symbolToken = this.fetch(); - const symbol = symbolToken.text; - this.consume(); - this.consumeSpaces(); // ignore spaces before sup/subscript argument - // Skip over allowed internal nodes such as \relax - let group; - do { - group = this.parseGroup(name); - } while (group.type && group.type === "internal") - - if (!group) { - throw new ParseError("Expected group after '" + symbol + "'", symbolToken); - } - - return group; - } - - /** - * Converts the textual input of an unsupported command into a text node - * contained within a color node whose color is determined by errorColor - */ - formatUnsupportedCmd(text) { - const textordArray = []; - - for (let i = 0; i < text.length; i++) { - textordArray.push({ type: "textord", mode: "text", text: text[i] }); - } - - const textNode = { - type: "text", - mode: this.mode, - body: textordArray - }; - - const colorNode = { - type: "color", - mode: this.mode, - color: this.settings.errorColor, - body: [textNode] - }; - - return colorNode; - } - - /** - * Parses a group with optional super/subscripts. - */ - parseAtom(breakOnTokenText) { - // The body of an atom is an implicit group, so that things like - // \left(x\right)^2 work correctly. - const base = this.parseGroup("atom", breakOnTokenText); - - // Internal nodes (e.g. \relax) cannot support super/subscripts. - // Instead we will pick up super/subscripts with blank base next round. - if (base && base.type === "internal") { - return base - } - - // In text mode, we don't have superscripts or subscripts - if (this.mode === "text") { - return base - } - - // Note that base may be empty (i.e. null) at this point. - - let superscript; - let subscript; - while (true) { - // Guaranteed in math mode, so eat any spaces first. - this.consumeSpaces(); - - // Lex the first token - const lex = this.fetch(); - - if (lex.text === "\\limits" || lex.text === "\\nolimits") { - // We got a limit control - if (base && base.type === "op") { - const limits = lex.text === "\\limits"; - base.limits = limits; - base.alwaysHandleSupSub = true; - } else if (base && base.type === "operatorname") { - if (base.alwaysHandleSupSub) { - base.limits = lex.text === "\\limits"; - } - } else { - throw new ParseError("Limit controls must follow a math operator", lex); - } - this.consume(); - } else if (lex.text === "^") { - // We got a superscript start - if (superscript) { - throw new ParseError("Double superscript", lex); - } - superscript = this.handleSupSubscript("superscript"); - } else if (lex.text === "_") { - // We got a subscript start - if (subscript) { - throw new ParseError("Double subscript", lex); - } - subscript = this.handleSupSubscript("subscript"); - } else if (lex.text === "'") { - // We got a prime - if (superscript) { - throw new ParseError("Double superscript", lex); - } - const prime = { type: "textord", mode: this.mode, text: "\\prime" }; - - // Many primes can be grouped together, so we handle this here - const primes = [prime]; - this.consume(); - // Keep lexing tokens until we get something that's not a prime - while (this.fetch().text === "'") { - // For each one, add another prime to the list - primes.push(prime); - this.consume(); - } - // If there's a superscript following the primes, combine that - // superscript in with the primes. - if (this.fetch().text === "^") { - primes.push(this.handleSupSubscript("superscript")); - } - // Put everything into an ordgroup as the superscript - superscript = { type: "ordgroup", mode: this.mode, body: primes }; - } else if (uSubsAndSups[lex.text]) { - // A Unicode subscript or superscript character. - // We treat these similarly to the unicode-math package. - // So we render a string of Unicode (sub|super)scripts the - // same as a (sub|super)script of regular characters. - const isSub = unicodeSubRegEx.test(lex.text); - const subsupTokens = []; - subsupTokens.push(new Token(uSubsAndSups[lex.text])); - this.consume(); - // Continue fetching tokens to fill out the group. - while (true) { - const token = this.fetch().text; - if (!(uSubsAndSups[token])) { break } - if (unicodeSubRegEx.test(token) !== isSub) { break } - subsupTokens.unshift(new Token(uSubsAndSups[token])); - this.consume(); - } - // Now create a (sub|super)script. - const body = this.subparse(subsupTokens); - if (isSub) { - subscript = { type: "ordgroup", mode: "math", body }; - } else { - superscript = { type: "ordgroup", mode: "math", body }; - } - } else { - // If it wasn't ^, _, a Unicode (sub|super)script, or ', stop parsing super/subscripts - break; - } - } - - if (superscript || subscript) { - if (base && base.type === "multiscript" && !base.postscripts) { - // base is the result of a \prescript function. - // Write the sub- & superscripts into the multiscript element. - base.postscripts = { sup: superscript, sub: subscript }; - return base - } else { - // We got either a superscript or subscript, create a supsub - const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname") - ? undefined - : isDelimiter(this.nextToken.text); - return { - type: "supsub", - mode: this.mode, - base: base, - sup: superscript, - sub: subscript, - isFollowedByDelimiter - } - } - } else { - // Otherwise return the original body - return base; - } - } - - /** - * Parses an entire function, including its base and all of its arguments. - */ - parseFunction( - breakOnTokenText, - name // For determining its context - ) { - const token = this.fetch(); - const func = token.text; - const funcData = functions[func]; - if (!funcData) { - return null; - } - this.consume(); // consume command token - - if (name && name !== "atom" && !funcData.allowedInArgument) { - throw new ParseError( - "Got function '" + func + "' with no arguments" + (name ? " as " + name : ""), - token - ); - } else if (this.mode === "text" && !funcData.allowedInText) { - throw new ParseError("Can't use function '" + func + "' in text mode", token); - } else if (this.mode === "math" && funcData.allowedInMath === false) { - throw new ParseError("Can't use function '" + func + "' in math mode", token); - } - - const prevAtomType = this.prevAtomType; - const { args, optArgs } = this.parseArguments(func, funcData); - this.prevAtomType = prevAtomType; - return this.callFunction(func, args, optArgs, token, breakOnTokenText); - } - - /** - * Call a function handler with a suitable context and arguments. - */ - callFunction(name, args, optArgs, token, breakOnTokenText) { - const context = { - funcName: name, - parser: this, - token, - breakOnTokenText - }; - const func = functions[name]; - if (func && func.handler) { - return func.handler(context, args, optArgs); - } else { - throw new ParseError(`No function handler for ${name}`); - } - } - - /** - * Parses the arguments of a function or environment - */ - parseArguments( - func, // Should look like "\name" or "\begin{name}". - funcData - ) { - const totalArgs = funcData.numArgs + funcData.numOptionalArgs; - if (totalArgs === 0) { - return { args: [], optArgs: [] }; - } - - const args = []; - const optArgs = []; - - for (let i = 0; i < totalArgs; i++) { - let argType = funcData.argTypes && funcData.argTypes[i]; - const isOptional = i < funcData.numOptionalArgs; - - if ( - (funcData.primitive && argType == null) || - // \sqrt expands into primitive if optional argument doesn't exist - (funcData.type === "sqrt" && i === 1 && optArgs[0] == null) - ) { - argType = "primitive"; - } - - const arg = this.parseGroupOfType(`argument to '${func}'`, argType, isOptional); - if (isOptional) { - optArgs.push(arg); - } else if (arg != null) { - args.push(arg); - } else { - // should be unreachable - throw new ParseError("Null argument, please report this as a bug"); - } - } - - return { args, optArgs }; - } - - /** - * Parses a group when the mode is changing. - */ - parseGroupOfType(name, type, optional) { - switch (type) { - case "size": - return this.parseSizeGroup(optional); - case "url": - return this.parseUrlGroup(optional); - case "math": - case "text": - return this.parseArgumentGroup(optional, type); - case "hbox": { - // hbox argument type wraps the argument in the equivalent of - // \hbox, which is like \text but switching to \textstyle size. - const group = this.parseArgumentGroup(optional, "text"); - return group != null - ? { - type: "styling", - mode: group.mode, - body: [group], - scriptLevel: "text" // simulate \textstyle - } - : null; - } - case "raw": { - const token = this.parseStringGroup("raw", optional); - return token != null - ? { - type: "raw", - mode: "text", - string: token.text - } - : null; - } - case "primitive": { - if (optional) { - throw new ParseError("A primitive argument cannot be optional"); - } - const group = this.parseGroup(name); - if (group == null) { - throw new ParseError("Expected group as " + name, this.fetch()); - } - return group; - } - case "original": - case null: - case undefined: - return this.parseArgumentGroup(optional); - default: - throw new ParseError("Unknown group type as " + name, this.fetch()); - } - } - - /** - * Discard any space tokens, fetching the next non-space token. - */ - consumeSpaces() { - while (true) { - const ch = this.fetch().text; - // \ufe0e is the Unicode variation selector to supress emoji. Ignore it. - if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") { - this.consume(); - } else { - break - } - } - } - - /** - * Parses a group, essentially returning the string formed by the - * brace-enclosed tokens plus some position information. - */ - parseStringGroup( - modeName, // Used to describe the mode in error messages. - optional - ) { - const argToken = this.gullet.scanArgument(optional); - if (argToken == null) { - return null; - } - let str = ""; - let nextToken; - while ((nextToken = this.fetch()).text !== "EOF") { - str += nextToken.text; - this.consume(); - } - this.consume(); // consume the end of the argument - argToken.text = str; - return argToken; - } - - /** - * Parses a regex-delimited group: the largest sequence of tokens - * whose concatenated strings match `regex`. Returns the string - * formed by the tokens plus some position information. - */ - parseRegexGroup( - regex, - modeName // Used to describe the mode in error messages. - ) { - const firstToken = this.fetch(); - let lastToken = firstToken; - let str = ""; - let nextToken; - while ((nextToken = this.fetch()).text !== "EOF" && regex.test(str + nextToken.text)) { - lastToken = nextToken; - str += lastToken.text; - this.consume(); - } - if (str === "") { - throw new ParseError("Invalid " + modeName + ": '" + firstToken.text + "'", firstToken); - } - return firstToken.range(lastToken, str); - } - - /** - * Parses a size specification, consisting of magnitude and unit. - */ - parseSizeGroup(optional) { - let res; - let isBlank = false; - // don't expand before parseStringGroup - this.gullet.consumeSpaces(); - if (!optional && this.gullet.future().text !== "{") { - res = this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size"); - } else { - res = this.parseStringGroup("size", optional); - } - if (!res) { - return null; - } - if (!optional && res.text.length === 0) { - // Because we've tested for what is !optional, this block won't - // affect \kern, \hspace, etc. It will capture the mandatory arguments - // to \genfrac and \above. - res.text = "0pt"; // Enable \above{} - isBlank = true; // This is here specifically for \genfrac - } - const match = sizeRegEx.exec(res.text); - if (!match) { - throw new ParseError("Invalid size: '" + res.text + "'", res); - } - const data = { - number: +(match[1] + match[2]), // sign + magnitude, cast to number - unit: match[3] - }; - if (!validUnit(data)) { - throw new ParseError("Invalid unit: '" + data.unit + "'", res); - } - return { - type: "size", - mode: this.mode, - value: data, - isBlank - }; - } - - /** - * Parses an URL, checking escaped letters and allowed protocols, - * and setting the catcode of % as an active character (as in \hyperref). - */ - parseUrlGroup(optional) { - this.gullet.lexer.setCatcode("%", 13); // active character - this.gullet.lexer.setCatcode("~", 12); // other character - const res = this.parseStringGroup("url", optional); - this.gullet.lexer.setCatcode("%", 14); // comment character - this.gullet.lexer.setCatcode("~", 13); // active character - if (res == null) { - return null; - } - // hyperref package allows backslashes alone in href, but doesn't - // generate valid links in such cases; we interpret this as - // "undefined" behaviour, and keep them as-is. Some browser will - // replace backslashes with forward slashes. - let url = res.text.replace(/\\([#$%&~_^{}])/g, "$1"); - url = res.text.replace(/{\u2044}/g, "/"); - return { - type: "url", - mode: this.mode, - url - }; - } - - /** - * Parses an argument with the mode specified. - */ - parseArgumentGroup(optional, mode) { - const argToken = this.gullet.scanArgument(optional); - if (argToken == null) { - return null; - } - const outerMode = this.mode; - if (mode) { - // Switch to specified mode - this.switchMode(mode); - } - - this.gullet.beginGroup(); - const expression = this.parseExpression(false, "EOF"); - // TODO: find an alternative way to denote the end - this.expect("EOF"); // expect the end of the argument - this.gullet.endGroup(); - const result = { - type: "ordgroup", - mode: this.mode, - loc: argToken.loc, - body: expression - }; - - if (mode) { - // Switch mode back - this.switchMode(outerMode); - } - return result; - } - - /** - * Parses an ordinary group, which is either a single nucleus (like "x") - * or an expression in braces (like "{x+y}") or an implicit group, a group - * that starts at the current position, and ends right before a higher explicit - * group ends, or at EOF. - */ - parseGroup( - name, // For error reporting. - breakOnTokenText - ) { - const firstToken = this.fetch(); - const text = firstToken.text; - if (name === "argument to '\\left'") { return this.parseSymbol() } - let result; - // Try to parse an open brace or \begingroup - if (text === "{" || text === "\\begingroup" || text === "\\toggle") { - this.consume(); - const groupEnd = text === "{" - ? "}" - : text === "\\begingroup" - ? "\\endgroup" - : "\\endtoggle"; - - this.gullet.beginGroup(); - // If we get a brace, parse an expression - const expression = this.parseExpression(false, groupEnd); - const lastToken = this.fetch(); - this.expect(groupEnd); // Check that we got a matching closing brace - this.gullet.endGroup(); - result = { - type: (lastToken.text === "\\endtoggle" ? "toggle" : "ordgroup"), - mode: this.mode, - loc: SourceLocation.range(firstToken, lastToken), - body: expression, - // A group formed by \begingroup...\endgroup is a semi-simple group - // which doesn't affect spacing in math mode, i.e., is transparent. - // https://tex.stackexchange.com/questions/1930/ - semisimple: text === "\\begingroup" || undefined - }; - } else { - // If there exists a function with this name, parse the function. - // Otherwise, just return a nucleus - result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol(); - if (result == null && text[0] === "\\" && - !Object.prototype.hasOwnProperty.call(implicitCommands, text )) { - if (this.settings.throwOnError) { - throw new ParseError("Unsupported function name: " + text, firstToken); - } - // For people getting dyanamically rendered math, it's better to - // show the unsupported command in red rather than panicking for every - // partially written expression. - result = this.formatUnsupportedCmd(text); - this.consume(); - } - } - return result; - } - - /** - * Form ligature-like combinations of characters for text mode. - * This includes inputs like "--", "---", "``" and "''". - * The result will simply replace multiple textord nodes with a single - * character in each value by a single textord node having multiple - * characters in its value. The representation is still ASCII source. - * The group will be modified in place. - */ - formLigatures(group) { - let n = group.length - 1; - for (let i = 0; i < n; ++i) { - const a = group[i]; - const v = a.text; - if (v === "-" && group[i + 1].text === "-") { - if (i + 1 < n && group[i + 2].text === "-") { - group.splice(i, 3, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 2]), - text: "---" - }); - n -= 2; - } else { - group.splice(i, 2, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 1]), - text: "--" - }); - n -= 1; - } - } - if ((v === "'" || v === "`") && group[i + 1].text === v) { - group.splice(i, 2, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 1]), - text: v + v - }); - n -= 1; - } - } - } - - /** - * Parse a single symbol out of the string. Here, we handle single character - * symbols and special functions like \verb. - */ - parseSymbol() { - const nucleus = this.fetch(); - let text = nucleus.text; - - if (/^\\verb[^a-zA-Z]/.test(text)) { - this.consume(); - let arg = text.slice(5); - const star = arg.charAt(0) === "*"; - if (star) { - arg = arg.slice(1); - } - // Lexer's tokenRegex is constructed to always have matching - // first/last characters. - if (arg.length < 2 || arg.charAt(0) !== arg.slice(-1)) { - throw new ParseError(`\\verb assertion failed -- - please report what input caused this bug`); - } - arg = arg.slice(1, -1); // remove first and last char - return { - type: "verb", - mode: "text", - body: arg, - star - }; - } - // At this point, we should have a symbol, possibly with accents. - // First expand any accented base symbol according to unicodeSymbols. - if (Object.prototype.hasOwnProperty.call(unicodeSymbols, text[0]) && - this.mode === "math" && !symbols[this.mode][text[0]]) { - // This behavior is not strict (XeTeX-compatible) in math mode. - if (this.settings.strict && this.mode === "math") { - throw new ParseError(`Accented Unicode text character "${text[0]}" used in ` + `math mode`, - nucleus - ); - } - text = unicodeSymbols[text[0]] + text.slice(1); - } - // Strip off any combining characters - const match = this.mode === "math" - ? combiningDiacriticalMarksEndRegex.exec(text) - : null; - if (match) { - text = text.substring(0, match.index); - if (text === "i") { - text = "\u0131"; // dotless i, in math and text mode - } else if (text === "j") { - text = "\u0237"; // dotless j, in math and text mode - } - } - // Recognize base symbol - let symbol; - if (symbols[this.mode][text]) { - let group = symbols[this.mode][text].group; - if (group === "bin" && - (binLeftCancellers.includes(this.prevAtomType) || this.prevAtomType === "")) { - // Change from a binary operator to a unary (prefix) operator - group = "open"; - } - const loc = SourceLocation.range(nucleus); - let s; - if (Object.prototype.hasOwnProperty.call(ATOMS, group )) { - const family = group; - s = { - type: "atom", - mode: this.mode, - family, - loc, - text - }; - if ((family === "rel" || family === "bin") && this.prevAtomType === "text") { - if (textRegEx.test(loc.lexer.input.slice(loc.end))) { - s.needsSpacing = true; // Fix a MathML bug. - } - } - } else { - if (asciiFromScript[text]) { - // Unicode 14 disambiguates chancery from roundhand. - // See https://www.unicode.org/charts/PDF/U1D400.pdf - this.consume(); - const nextCode = this.fetch().text.charCodeAt(0); - // mathcal is Temml default. Use mathscript if called for. - const font = nextCode === 0xfe01 ? "mathscr" : "mathcal"; - if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume(); } - return { - type: "font", - mode: "math", - font, - body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] } - } - } - // Default ord character. No disambiguation necessary. - s = { - type: group, - mode: this.mode, - loc, - text - }; - } - symbol = s; - } else if (text.charCodeAt(0) >= 0x80 || combiningDiacriticalMarksEndRegex.exec(text)) { - // no symbol for e.g. ^ - if (this.settings.strict && this.mode === "math") { - throw new ParseError(`Unicode text character "${text[0]}" used in math mode`, nucleus) - } - // All nonmathematical Unicode characters are rendered as if they - // are in text mode (wrapped in \text) because that's what it - // takes to render them in LaTeX. - symbol = { - type: "textord", - mode: "text", - loc: SourceLocation.range(nucleus), - text - }; - } else { - return null; // EOF, ^, _, {, }, etc. - } - this.consume(); - // Transform combining characters into accents - if (match) { - for (let i = 0; i < match[0].length; i++) { - const accent = match[0][i]; - if (!unicodeAccents[accent]) { - throw new ParseError(`Unknown accent ' ${accent}'`, nucleus); - } - const command = unicodeAccents[accent][this.mode] || - unicodeAccents[accent].text; - if (!command) { - throw new ParseError(`Accent ${accent} unsupported in ${this.mode} mode`, nucleus); - } - symbol = { - type: "accent", - mode: this.mode, - loc: SourceLocation.range(nucleus), - label: command, - isStretchy: false, - base: symbol - }; - } - } - return symbol; - } - } - - /** - * Parses an expression using a Parser, then returns the parsed result. - */ - const parseTree = function(toParse, settings) { - if (!(typeof toParse === "string" || toParse instanceof String)) { - throw new TypeError("Temml can only parse string typed expression") - } - let tree; - let parser; - try { - parser = new Parser(toParse, settings); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - - tree = parser.parse(); - } catch (error) { - if (error.toString() === "ParseError: Unmatched delimiter") { - // Abandon the attempt to wrap delimiter pairs in an . - // Try again, and put each delimiter into an element. - settings.wrapDelimiterPairs = false; - parser = new Parser(toParse, settings); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - tree = parser.parse(); - } else { - throw error; - } - } - - // LaTeX ignores a \tag placed outside an AMS environment. - if (!(tree.length > 0 && tree[0].type && tree[0].type === "array" && tree[0].addEqnNum)) { - // If the input used \tag, it will set the \df@tag macro to the tag. - // In this case, we separately parse the tag and wrap the tree. - if (parser.gullet.macros.get("\\df@tag")) { - if (!settings.displayMode) { - throw new ParseError("\\tag works only in display mode") - } - parser.gullet.feed("\\df@tag"); - tree = [ - { - type: "tag", - mode: "text", - body: tree, - tag: parser.parse() - } - ]; - } - } - - return tree - }; - - /** - * This file contains information about the style that the mathmlBuilder carries - * around with it. Data is held in an `Style` object, and when - * recursing, a new `Style` object can be created with the `.with*` functions. - */ - - const subOrSupLevel = [2, 2, 3, 3]; - - /** - * This is the main Style class. It contains the current style.level, color, and font. - * - * Style objects should not be modified. To create a new Style with - * different properties, call a `.with*` method. - */ - class Style { - constructor(data) { - // Style.level can be 0 | 1 | 2 | 3, which correspond to - // displaystyle, textstyle, scriptstyle, and scriptscriptstyle. - // style.level usually does not directly set MathML's script level. MathML does that itself. - // However, Chromium does not stop shrinking after scriptscriptstyle, so we do explicitly - // set a scriptlevel attribute in those conditions. - // We also use style.level to track math style so that we can get the correct - // scriptlevel when needed in supsub.js, mathchoice.js, or for dimensions in em. - this.level = data.level; - this.color = data.color; // string | void - // A font family applies to a group of fonts (i.e. SansSerif), while a font - // represents a specific font (i.e. SansSerif Bold). - // See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm - this.font = data.font || ""; // string - this.fontFamily = data.fontFamily || ""; // string - this.fontSize = data.fontSize || 1.0; // number - this.fontWeight = data.fontWeight || ""; - this.fontShape = data.fontShape || ""; - this.maxSize = data.maxSize; // [number, number] - } - - /** - * Returns a new style object with the same properties as "this". Properties - * from "extension" will be copied to the new style object. - */ - extend(extension) { - const data = { - level: this.level, - color: this.color, - font: this.font, - fontFamily: this.fontFamily, - fontSize: this.fontSize, - fontWeight: this.fontWeight, - fontShape: this.fontShape, - maxSize: this.maxSize - }; - - for (const key in extension) { - if (Object.prototype.hasOwnProperty.call(extension, key)) { - data[key] = extension[key]; - } - } - - return new Style(data); - } - - withLevel(n) { - return this.extend({ - level: n - }); - } - - incrementLevel() { - return this.extend({ - level: Math.min(this.level + 1, 3) - }); - } - - inSubOrSup() { - return this.extend({ - level: subOrSupLevel[this.level] - }) - } - - /** - * Create a new style object with the given color. - */ - withColor(color) { - return this.extend({ - color: color - }); - } - - /** - * Creates a new style object with the given math font or old text font. - * @type {[type]} - */ - withFont(font) { - return this.extend({ - font - }); - } - - /** - * Create a new style objects with the given fontFamily. - */ - withTextFontFamily(fontFamily) { - return this.extend({ - fontFamily, - font: "" - }); - } - - /** - * Creates a new style object with the given font size - */ - withFontSize(num) { - return this.extend({ - fontSize: num - }); - } - - /** - * Creates a new style object with the given font weight - */ - withTextFontWeight(fontWeight) { - return this.extend({ - fontWeight, - font: "" - }); - } - - /** - * Creates a new style object with the given font weight - */ - withTextFontShape(fontShape) { - return this.extend({ - fontShape, - font: "" - }); - } - - /** - * Gets the CSS color of the current style object - */ - getColor() { - return this.color; - } - } - - /* Temml Post Process - * Populate the text contents of each \ref & \eqref - * - * As with other Temml code, this file is released under terms of the MIT license. - * https://mit-license.org/ - */ - - const version = "0.13.02"; - - function postProcess(block) { - const labelMap = {}; - let i = 0; - - // Get a collection of the parents of each \tag & auto-numbered equation - const amsEqns = document.getElementsByClassName('tml-eqn'); - for (let parent of amsEqns) { - // AMS automatically numbered equation. - // Assign an id. - i += 1; - parent.setAttribute("id", "tml-eqn-" + String(i)); - // No need to write a number into the text content of the element. - // A CSS counter has done that even if this postProcess() function is not used. - - // Find any \label that refers to an AMS automatic eqn number. - while (true) { - if (parent.tagName === "mtable") { break } - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = String(i); - break - } else { - parent = parent.parentElement; - } - } - } - - // Find \labels associated with \tag - const taggedEqns = document.getElementsByClassName('tml-tageqn'); - for (const parent of taggedEqns) { - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const tags = parent.getElementsByClassName("tml-tag"); - if (tags.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = tags[0].textContent; - } - } - } - - // Populate \ref & \eqref text content - const refs = block.getElementsByClassName("tml-ref"); - [...refs].forEach(ref => { - const attr = ref.getAttribute("href"); - let str = labelMap[attr.slice(1)]; - if (ref.className.indexOf("tml-eqref") === -1) { - // \ref. Omit parens. - str = str.replace(/^\(/, ""); - str = str.replace(/\)$/, ""); - } else { - // \eqref. Include parens - if (str.charAt(0) !== "(") { str = "(" + str; } - if (str.slice(-1) !== ")") { str = str + ")"; } - } - const mtext = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mtext"); - mtext.appendChild(document.createTextNode(str)); - const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"); - math.appendChild(mtext); - ref.textContent = ''; - ref.appendChild(math); - }); - } - - const findEndOfMath = function(delimiter, text, startIndex) { - // Adapted from - // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx - let index = startIndex; - let braceLevel = 0; - - const delimLength = delimiter.length; - - while (index < text.length) { - const character = text[index]; - - if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) { - return index; - } else if (character === "\\") { - index++; - } else if (character === "{") { - braceLevel++; - } else if (character === "}") { - braceLevel--; - } - - index++; - } - - return -1; - }; - - const escapeRegex = function(string) { - return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); - }; - - const amsRegex = /^\\(?:begin|(?:eq)?ref){/; - - const splitAtDelimiters = function(text, delimiters) { - let index; - const data = []; - - const regexLeft = new RegExp( - "(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")" - ); - - while (true) { - index = text.search(regexLeft); - if (index === -1) { - break; - } - if (index > 0) { - data.push({ - type: "text", - data: text.slice(0, index) - }); - text = text.slice(index); // now text starts with delimiter - } - // ... so this always succeeds: - const i = delimiters.findIndex((delim) => text.startsWith(delim.left)); - index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length); - if (index === -1) { - break; - } - const rawData = text.slice(0, index + delimiters[i].right.length); - const math = amsRegex.test(rawData) - ? rawData - : text.slice(delimiters[i].left.length, index); - data.push({ - type: "math", - data: math, - rawData, - display: delimiters[i].display - }); - text = text.slice(index + delimiters[i].right.length); - } - - if (text !== "") { - data.push({ - type: "text", - data: text - }); - } - - return data; - }; - - const defaultDelimiters = [ - { left: "$$", right: "$$", display: true }, - { left: "\\(", right: "\\)", display: false }, - // LaTeX uses $…$, but it ruins the display of normal `$` in text: - // {left: "$", right: "$", display: false}, - // $ must come after $$ - - // Render AMS environments even if outside $$…$$ delimiters. - { left: "\\begin{equation}", right: "\\end{equation}", display: true }, - { left: "\\begin{equation*}", right: "\\end{equation*}", display: true }, - { left: "\\begin{align}", right: "\\end{align}", display: true }, - { left: "\\begin{align*}", right: "\\end{align*}", display: true }, - { left: "\\begin{alignat}", right: "\\end{alignat}", display: true }, - { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true }, - { left: "\\begin{gather}", right: "\\end{gather}", display: true }, - { left: "\\begin{gather*}", right: "\\end{gather*}", display: true }, - { left: "\\begin{CD}", right: "\\end{CD}", display: true }, - // Ditto \ref & \eqref - { left: "\\ref{", right: "}", display: false }, - { left: "\\eqref{", right: "}", display: false }, - - { left: "\\[", right: "\\]", display: true } - ]; - - const firstDraftDelimiters = { - "$": [ - { left: "$$", right: "$$", display: true }, - { left: "$`", right: "`$", display: false }, - { left: "$", right: "$", display: false } - ], - "(": [ - { left: "\\[", right: "\\]", display: true }, - { left: "\\(", right: "\\)", display: false } - ] - }; - - const amsDelimiters = [ - { left: "\\begin{equation}", right: "\\end{equation}", display: true }, - { left: "\\begin{equation*}", right: "\\end{equation*}", display: true }, - { left: "\\begin{align}", right: "\\end{align}", display: true }, - { left: "\\begin{align*}", right: "\\end{align*}", display: true }, - { left: "\\begin{alignat}", right: "\\end{alignat}", display: true }, - { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true }, - { left: "\\begin{gather}", right: "\\end{gather}", display: true }, - { left: "\\begin{gather*}", right: "\\end{gather*}", display: true }, - { left: "\\begin{CD}", right: "\\end{CD}", display: true }, - { left: "\\ref{", right: "}", display: false }, - { left: "\\eqref{", right: "}", display: false } - ]; - - const delimitersFromKey = key => { - if (key === "$" || key === "(") { - return firstDraftDelimiters[key]; - } else if (key === "$+" || key === "(+") { - const firstDraft = firstDraftDelimiters[key.slice(0, 1)]; - return firstDraft.concat(amsDelimiters) - } else if (key === "ams") { - return amsDelimiters - } else if (key === "all") { - return (firstDraftDelimiters["("]).concat(firstDraftDelimiters["$"]).concat(amsDelimiters) - } else { - return defaultDelimiters - } - }; - - /* Note: optionsCopy is mutated by this method. If it is ever exposed in the - * API, we should copy it before mutating. - */ - const renderMathInText = function(text, optionsCopy) { - const data = splitAtDelimiters(text, optionsCopy.delimiters); - if (data.length === 1 && data[0].type === "text") { - // There is no formula in the text. - // Let's return null which means there is no need to replace - // the current text node with a new one. - return null; - } - - const fragment = document.createDocumentFragment(); - - for (let i = 0; i < data.length; i++) { - if (data[i].type === "text") { - fragment.appendChild(document.createTextNode(data[i].data)); - } else { - const span = document.createElement("span"); - let math = data[i].data; - // Override any display mode defined in the settings with that - // defined by the text itself - optionsCopy.displayMode = data[i].display; - try { - if (optionsCopy.preProcess) { - math = optionsCopy.preProcess(math); - } - // Importing render() from temml.js would be a circular dependency. - // So call the global version. - // eslint-disable-next-line no-undef - temml.render(math, span, optionsCopy); - } catch (e) { - if (!(e instanceof ParseError)) { - throw e; - } - optionsCopy.errorCallback( - "Temml auto-render: Failed to parse `" + data[i].data + "` with ", - e - ); - fragment.appendChild(document.createTextNode(data[i].rawData)); - continue; - } - fragment.appendChild(span); - } - } - - return fragment; - }; - - const renderElem = function(elem, optionsCopy) { - for (let i = 0; i < elem.childNodes.length; i++) { - const childNode = elem.childNodes[i]; - if (childNode.nodeType === 3) { - // Text node - const frag = renderMathInText(childNode.textContent, optionsCopy); - if (frag) { - i += frag.childNodes.length - 1; - elem.replaceChild(frag, childNode); - } - } else if (childNode.nodeType === 1) { - // Element node - const className = " " + childNode.className + " "; - const shouldRender = - optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && - optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1); - - if (shouldRender) { - renderElem(childNode, optionsCopy); - } - } - // Otherwise, it's something else, and ignore it. - } - }; - - const renderMathInElement = function(elem, options) { - if (!elem) { - throw new Error("No element provided to render"); - } - - const optionsCopy = {}; - - // Object.assign(optionsCopy, option) - for (const option in options) { - if (Object.prototype.hasOwnProperty.call(options, option)) { - optionsCopy[option] = options[option]; - } - } - - if (optionsCopy.fences) { - optionsCopy.delimiters = delimitersFromKey(optionsCopy.fences); - } else { - optionsCopy.delimiters = optionsCopy.delimiters || defaultDelimiters; - } - optionsCopy.ignoredTags = optionsCopy.ignoredTags || [ - "script", - "noscript", - "style", - "textarea", - "pre", - "code", - "option" - ]; - optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || []; - // eslint-disable-next-line no-console - optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; - - // Enable sharing of global macros defined via `\gdef` between different - // math elements within a single call to `renderMathInElement`. - optionsCopy.macros = optionsCopy.macros || {}; - - renderElem(elem, optionsCopy); - postProcess(elem); - }; - - /* eslint no-console:0 */ - /** - * This is the main entry point for Temml. Here, we expose functions for - * rendering expressions either to DOM nodes or to markup strings. - * - * We also expose the ParseError class to check if errors thrown from Temml are - * errors in the expression, or errors in javascript handling. - */ - - - /** - * @type {import('./temml').render} - * Parse and build an expression, and place that expression in the DOM node - * given. - */ - let render = function(expression, baseNode, options = {}) { - baseNode.textContent = ""; - const alreadyInMathElement = baseNode.tagName.toLowerCase() === "math"; - if (alreadyInMathElement) { options.wrap = "none"; } - const math = renderToMathMLTree(expression, options); - if (alreadyInMathElement) { - // The element already exists. Populate it. - baseNode.textContent = ""; - math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); - } else if (math.children.length > 1) { - baseNode.textContent = ""; - math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); - } else { - baseNode.appendChild(math.toNode()); - } - }; - - // Temml's styles don't work properly in quirks mode. Print out an error, and - // disable rendering. - if (typeof document !== "undefined") { - if (document.compatMode !== "CSS1Compat") { - typeof console !== "undefined" && - console.warn( - "Warning: Temml doesn't work in quirks mode. Make sure your " + - "website has a suitable doctype." - ); - - render = function() { - throw new ParseError("Temml doesn't work in quirks mode."); - }; - } - } - - /** - * @type {import('./temml').renderToString} - * Parse and build an expression, and return the markup for that. - */ - const renderToString = function(expression, options) { - const markup = renderToMathMLTree(expression, options).toMarkup(); - return markup; - }; - - /** - * @type {import('./temml').generateParseTree} - * Parse an expression and return the parse tree. - */ - const generateParseTree = function(expression, options) { - const settings = new Settings(options); - return parseTree(expression, settings); - }; - - /** - * @type {import('./temml').definePreamble} - * Take an expression which contains a preamble. - * Parse it and return the macros. - */ - const definePreamble = function(expression, options) { - const settings = new Settings(options); - settings.macros = {}; - if (!(typeof expression === "string" || expression instanceof String)) { - throw new TypeError("Temml can only parse string typed expression") - } - const parser = new Parser(expression, settings, true); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - const macros = parser.parse(); - return macros - }; - - /** - * If the given error is a Temml ParseError, - * renders the invalid LaTeX as a span with hover title giving the Temml - * error message. Otherwise, simply throws the error. - */ - const renderError = function(error, expression, options) { - if (options.throwOnError || !(error instanceof ParseError)) { - throw error; - } - const node = new Span(["temml-error"], [new TextNode$1(expression + "\n\n" + error.toString())]); - node.style.color = options.errorColor; - node.style.whiteSpace = "pre-line"; - return node; - }; - - /** - * @type {import('./temml').renderToMathMLTree} - * Generates and returns the Temml build tree. This is used for advanced - * use cases (like rendering to custom output). - */ - const renderToMathMLTree = function(expression, options) { - const settings = new Settings(options); - try { - const tree = parseTree(expression, settings); - const style = new Style({ - level: settings.displayMode ? StyleLevel.DISPLAY : StyleLevel.TEXT, - maxSize: settings.maxSize - }); - return buildMathML(tree, expression, style, settings); - } catch (error) { - return renderError(error, expression, settings); - } - }; - - /** @type {import('./temml').default} */ - var temml$1 = { - /** - * Current Temml version - */ - version: version, - /** - * Renders the given LaTeX into MathML, and adds - * it as a child to the specified DOM node. - */ - render, - /** - * Renders the given LaTeX into MathML string, - * for sending to the client. - */ - renderToString, - /** - * Finds all the math delimiters in a given element of a running HTML document - * and converts the contents of each instance into a element. - */ - renderMathInElement, - /** - * Post-process an entire HTML block. - * Writes AMS auto-numbers and implements \ref{}. - * Typcally called once, after a loop has rendered many individual spans. - */ - postProcess, - /** - * Temml error, usually during parsing. - */ - ParseError, - /** - * Creates a set of macros with document-wide scope. - */ - definePreamble, - /** - * Parses the given LaTeX into Temml's internal parse tree structure, - * without rendering to HTML or MathML. - * - * NOTE: This method is not currently recommended for public use. - * The internal tree representation is unstable and is very likely - * to change. Use at your own risk. - */ - __parse: generateParseTree, - /** - * Renders the given LaTeX into a MathML internal DOM tree - * representation, without flattening that representation to a string. - * - * NOTE: This method is not currently recommended for public use. - * The internal tree representation is unstable and is very likely - * to change. Use at your own risk. - */ - __renderToMathMLTree: renderToMathMLTree, - /** - * adds a new symbol to builtin symbols table - */ - __defineSymbol: defineSymbol, - /** - * adds a new macro to builtin macro list - */ - __defineMacro: defineMacro - }; - - return temml$1; - -})(); diff --git a/dist/temml.min.js b/dist/temml.min.js deleted file mode 100644 index d41cf781..00000000 --- a/dist/temml.min.js +++ /dev/null @@ -1 +0,0 @@ -var temml=function(){"use strict";class e{constructor(t,r){let n,s=" "+t;const o=r&&r.loc;if(o&&o.start<=o.end){const e=o.lexer.input;n=o.start;const t=o.end;n===e.length?s+=" at end of input: ":s+=" at position "+(n+1)+": \n";const r=e.slice(n,t).replace(/[^]/g,"$&̲");let a,l;a=n>15?"…"+e.slice(n-15,n):e.slice(0,n),l=t+15":">","<":"<",'"':""","'":"'"},o=/[&><"']/g;function a(e){return String(e).replace(o,e=>s[e])}const l=function(e){return"ordgroup"===e.type||"color"===e.type?1===e.body.length?l(e.body[0]):e:"font"===e.type?l(e.body):e},i=function(e){const t=l(e);return"mathord"===t.type||"textord"===t.type||"atom"===t.type},c=function(e){return+e.toFixed(4)},m="acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚𝐜𝐞𝐠𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐮𝐯𝐰𝐱𝐲𝐳";class p{constructor(e){e=e||{},this.displayMode=t(e.displayMode,!1),this.annotate=t(e.annotate,!1),this.leqno=t(e.leqno,!1),this.throwOnError=t(e.throwOnError,!1),this.errorColor=t(e.errorColor,"#b22222"),this.macros=e.macros||{},this.wrap=t(e.wrap,"none"),this.xml=t(e.xml,!1),this.colorIsTextColor=t(e.colorIsTextColor,!1),this.strict=t(e.strict,!1),this.trust=t(e.trust,!1),this.maxSize=void 0===e.maxSize?[1/0,1/0]:Array.isArray(e.maxSize)?e.maxSize:[1/0,1/0],this.maxExpand=Math.max(0,t(e.maxExpand,1e3)),this.wrapDelimiterPairs=!0}isTrusted(e){if(e.url&&!e.protocol){const t=function(e){const t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?":"!==t[2]?null:/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?t[1].toLowerCase():null:"_relative"}(e.url);if(null==t)return!1;e.protocol=t}const t="function"==typeof this.trust?this.trust(e):this.trust;return Boolean(t)}}const u={},d={};function h({type:e,names:t,props:r,handler:n,mathmlBuilder:s}){const o={type:e,numArgs:r.numArgs,argTypes:r.argTypes,allowedInArgument:!!r.allowedInArgument,allowedInText:!!r.allowedInText,allowedInMath:void 0===r.allowedInMath||r.allowedInMath,numOptionalArgs:r.numOptionalArgs||0,infix:!!r.infix,primitive:!!r.primitive,handler:n};for(let e=0;ee.toText()).join("")}}const w=function(e){return e.filter(e=>e).join(" ")},x=function(e,t){this.classes=e||[],this.attributes={},this.style=t||{}},k=function(e){const t=document.createElement(e);t.className=w(this.classes);for(const e in this.style)Object.prototype.hasOwnProperty.call(this.style,e)&&(t.style[e]=this.style[e]);for(const e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);for(let e=0;e`,t};class A{constructor(e,t,r){x.call(this,e,r),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}toNode(){return k.call(this,"span")}toMarkup(){return v.call(this,"span")}}let T=class{constructor(e){this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return a(this.text)}};class S{constructor(e,t,r){this.href=e,this.classes=t,this.children=r||[]}toNode(){const e=document.createElement("a");e.setAttribute("href",this.href),this.classes.length>0&&(e.className=w(this.classes));for(let t=0;t0&&(e+=` class="${a(w(this.classes))}"`),e+=">";for(let t=0;t0&&(e.className=w(this.classes));for(const t in this.style)Object.prototype.hasOwnProperty.call(this.style,t)&&(e.style[t]=this.style[t]);for(let t=0;t0&&(e+=` class="${a(w(this.classes))}"`);let t="";for(const e in this.style)Object.prototype.hasOwnProperty.call(this.style,e)&&(t+=`${n(e)}:${this.style[e]};`);t&&(e+=` style="${t}"`),e+=">";for(let t=0;t",e}toText(){return this.children.map(e=>e.toText()).join("")}}class C{constructor(e){this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return a(this.toText())}toText(){return this.text}}const N=e=>{let t;return 1===e.length&&"mrow"===e[0].type?(t=e.pop(),t.type="mstyle"):t=new B("mstyle",e),t},z=e=>{let t=0;if(e.body&&Array.isArray(e.body))for(const r of e.body)t+=z(r);else if(e.body)t+=z(e.body);else if("supsub"===e.type)t+=z(e.base),e.sub&&(t+=.7*z(e.sub)),e.sup&&(t+=.7*z(e.sup));else if("mathord"===e.type||"textord"===e.type)for(const r of e.text.split("")){const e=r.codePointAt(0);t+=96{const t=E(e.label);if(I.includes(e.label)){const r=z(e.base);1","\\gt",!0),G(P,Y,"∈","\\in",!0),G(P,Y,"∉","\\notin",!0),G(P,Y,"","\\@not"),G(P,Y,"⊂","\\subset",!0),G(P,Y,"⊃","\\supset",!0),G(P,Y,"⊆","\\subseteq",!0),G(P,Y,"⊇","\\supseteq",!0),G(P,Y,"⊈","\\nsubseteq",!0),G(P,Y,"⊈","\\nsubseteqq"),G(P,Y,"⊉","\\nsupseteq",!0),G(P,Y,"⊉","\\nsupseteqq"),G(P,Y,"⊨","\\models"),G(P,Y,"←","\\leftarrow",!0),G(P,Y,"≤","\\le"),G(P,Y,"≤","\\leq",!0),G(P,Y,"<","\\lt",!0),G(P,Y,"→","\\rightarrow",!0),G(P,Y,"→","\\to"),G(P,Y,"≱","\\ngeq",!0),G(P,Y,"≱","\\ngeqq"),G(P,Y,"≱","\\ngeqslant"),G(P,Y,"≰","\\nleq",!0),G(P,Y,"≰","\\nleqq"),G(P,Y,"≰","\\nleqslant"),G(P,Y,"⫫","\\Perp",!0),G(P,J," ","\\ "),G(P,J," ","\\space"),G(P,J," ","\\nobreakspace"),G(j,J," ","\\ "),G(j,J," "," "),G(j,J," ","\\space"),G(j,J," ","\\nobreakspace"),G(P,J,null,"\\nobreak"),G(P,J,null,"\\allowbreak"),G(P,Z,",",","),G(j,Z,":",":"),G(P,Z,";",";"),G(P,U,"⊼","\\barwedge"),G(P,U,"⊻","\\veebar"),G(P,U,"⊙","\\odot",!0),G(P,U,"⊕︎","\\oplus"),G(P,U,"⊗","\\otimes",!0),G(P,K,"∂","\\partial",!0),G(P,U,"⊘","\\oslash",!0),G(P,U,"⊚","\\circledcirc",!0),G(P,U,"⊡","\\boxdot",!0),G(P,U,"△","\\bigtriangleup"),G(P,U,"▽","\\bigtriangledown"),G(P,U,"†","\\dagger"),G(P,U,"⋄","\\diamond"),G(P,U,"◃","\\triangleleft"),G(P,U,"▹","\\triangleright"),G(P,X,"{","\\{"),G(j,K,"{","\\{"),G(j,K,"{","\\textbraceleft"),G(P,H,"}","\\}"),G(j,K,"}","\\}"),G(j,K,"}","\\textbraceright"),G(P,X,"{","\\lbrace"),G(P,H,"}","\\rbrace"),G(P,X,"[","\\lbrack",!0),G(j,K,"[","\\lbrack",!0),G(P,H,"]","\\rbrack",!0),G(j,K,"]","\\rbrack",!0),G(P,X,"(","\\lparen",!0),G(P,H,")","\\rparen",!0),G(P,X,"⦇","\\llparenthesis",!0),G(P,H,"⦈","\\rrparenthesis",!0),G(j,K,"<","\\textless",!0),G(j,K,">","\\textgreater",!0),G(P,X,"⌊","\\lfloor",!0),G(P,H,"⌋","\\rfloor",!0),G(P,X,"⌈","\\lceil",!0),G(P,H,"⌉","\\rceil",!0),G(P,K,"\\","\\backslash"),G(P,K,"|","|"),G(P,K,"|","\\vert"),G(j,K,"|","\\textbar",!0),G(P,K,"‖","\\|"),G(P,K,"‖","\\Vert"),G(j,K,"‖","\\textbardbl"),G(j,K,"~","\\textasciitilde"),G(j,K,"\\","\\textbackslash"),G(j,K,"^","\\textasciicircum"),G(P,Y,"↑","\\uparrow",!0),G(P,Y,"⇑","\\Uparrow",!0),G(P,Y,"↓","\\downarrow",!0),G(P,Y,"⇓","\\Downarrow",!0),G(P,Y,"↕","\\updownarrow",!0),G(P,Y,"⇕","\\Updownarrow",!0),G(P,W,"∐","\\coprod"),G(P,W,"⋁","\\bigvee"),G(P,W,"⋀","\\bigwedge"),G(P,W,"⨄","\\biguplus"),G(P,W,"⨄","\\bigcupplus"),G(P,W,"⨃","\\bigcupdot"),G(P,W,"⨇","\\bigdoublevee"),G(P,W,"⨈","\\bigdoublewedge"),G(P,W,"⋂","\\bigcap"),G(P,W,"⋃","\\bigcup"),G(P,W,"∫","\\int"),G(P,W,"∫","\\intop"),G(P,W,"∬","\\iint"),G(P,W,"∭","\\iiint"),G(P,W,"∏","\\prod"),G(P,W,"∑","\\sum"),G(P,W,"⨂","\\bigotimes"),G(P,W,"⨁","\\bigoplus"),G(P,W,"⨀","\\bigodot"),G(P,W,"⨉","\\bigtimes"),G(P,W,"∮","\\oint"),G(P,W,"∯","\\oiint"),G(P,W,"∰","\\oiiint"),G(P,W,"∱","\\intclockwise"),G(P,W,"∲","\\varointclockwise"),G(P,W,"⨌","\\iiiint"),G(P,W,"⨍","\\intbar"),G(P,W,"⨎","\\intBar"),G(P,W,"⨏","\\fint"),G(P,W,"⨒","\\rppolint"),G(P,W,"⨓","\\scpolint"),G(P,W,"⨕","\\pointint"),G(P,W,"⨖","\\sqint"),G(P,W,"⨗","\\intlarhk"),G(P,W,"⨘","\\intx"),G(P,W,"⨙","\\intcap"),G(P,W,"⨚","\\intcup"),G(P,W,"⨅","\\bigsqcap"),G(P,W,"⨆","\\bigsqcup"),G(P,W,"∫","\\smallint"),G(j,V,"…","\\textellipsis"),G(P,V,"…","\\mathellipsis"),G(j,V,"…","\\ldots",!0),G(P,V,"…","\\ldots",!0),G(P,V,"⋰","\\iddots",!0),G(P,V,"⋯","\\@cdots",!0),G(P,V,"⋱","\\ddots",!0),G(P,K,"⋮","\\varvdots"),G(j,K,"⋮","\\varvdots"),G(P,R,"´","\\acute"),G(P,R,"`","\\grave"),G(P,R,"¨","\\ddot"),G(P,R,"…","\\dddot"),G(P,R,"….","\\ddddot"),G(P,R,"~","\\tilde"),G(P,R,"‾","\\bar"),G(P,R,"˘","\\breve"),G(P,R,"ˇ","\\check"),G(P,R,"^","\\hat"),G(P,R,"→","\\vec"),G(P,R,"˙","\\dot"),G(P,R,"˚","\\mathring"),G(P,_,"ı","\\imath",!0),G(P,_,"ȷ","\\jmath",!0),G(P,K,"ı","ı"),G(P,K,"ȷ","ȷ"),G(j,K,"ı","\\i",!0),G(j,K,"ȷ","\\j",!0),G(j,K,"ø","\\o",!0),G(P,_,"ø","\\o",!0),G(j,K,"Ø","\\O",!0),G(P,_,"Ø","\\O",!0),G(j,R,"ˊ","\\'"),G(j,R,"ˋ","\\`"),G(j,R,"ˆ","\\^"),G(j,R,"~","\\~"),G(j,R,"ˉ","\\="),G(j,R,"˘","\\u"),G(j,R,"˙","\\."),G(j,R,"¸","\\c"),G(j,R,"˚","\\r"),G(j,R,"ˇ","\\v");G(j,R,"¨",'\\"'),G(j,R,"˝","\\H"),G(P,R,"ˊ","\\'"),G(P,R,"ˋ","\\`"),G(P,R,"ˆ","\\^"),G(P,R,"~","\\~"),G(P,R,"ˉ","\\="),G(P,R,"˘","\\u"),G(P,R,"˙","\\."),G(P,R,"¸","\\c"),G(P,R,"˚","\\r"),G(P,R,"ˇ","\\v"),G(P,R,"¨",'\\"'),G(P,R,"˝","\\H");const Q={"--":!0,"---":!0,"``":!0,"''":!0};G(j,K,"–","--",!0),G(j,K,"–","\\textendash"),G(j,K,"—","---",!0),G(j,K,"—","\\textemdash"),G(j,K,"‘","`",!0),G(j,K,"‘","\\textquoteleft"),G(j,K,"’","'",!0),G(j,K,"’","\\textquoteright"),G(j,K,"“","``",!0),G(j,K,"“","\\textquotedblleft"),G(j,K,"”","''",!0),G(j,K,"”","\\textquotedblright"),G(P,K,"°","\\degree",!0),G(j,K,"°","\\degree"),G(j,K,"°","\\textdegree",!0),G(P,K,"£","\\pounds"),G(P,K,"£","\\mathsterling",!0),G(j,K,"£","\\pounds"),G(j,K,"£","\\textsterling",!0),G(P,K,"✠","\\maltese"),G(j,K,"✠","\\maltese"),G(P,K,"€","\\euro",!0),G(j,K,"€","\\euro",!0),G(j,K,"€","\\texteuro"),G(P,K,"©","\\copyright",!0),G(j,K,"©","\\textcopyright"),G(P,K,"⌀","\\diameter",!0),G(j,K,"⌀","\\diameter"),G(P,K,"𝛤","\\varGamma"),G(P,K,"𝛥","\\varDelta"),G(P,K,"𝛩","\\varTheta"),G(P,K,"𝛬","\\varLambda"),G(P,K,"𝛯","\\varXi"),G(P,K,"𝛱","\\varPi"),G(P,K,"𝛴","\\varSigma"),G(P,K,"𝛶","\\varUpsilon"),G(P,K,"𝛷","\\varPhi"),G(P,K,"𝛹","\\varPsi"),G(P,K,"𝛺","\\varOmega"),G(j,K,"𝛤","\\varGamma"),G(j,K,"𝛥","\\varDelta"),G(j,K,"𝛩","\\varTheta"),G(j,K,"𝛬","\\varLambda"),G(j,K,"𝛯","\\varXi"),G(j,K,"𝛱","\\varPi"),G(j,K,"𝛴","\\varSigma"),G(j,K,"𝛶","\\varUpsilon"),G(j,K,"𝛷","\\varPhi"),G(j,K,"𝛹","\\varPsi"),G(j,K,"𝛺","\\varOmega");const ee='0123456789/@."';for(let e=0;e<14;e++){const t=ee.charAt(e);G(P,K,t,t)}const te='0123456789!@*()-=+";:?/.,';for(let e=0;e<25;e++){const t=te.charAt(e);G(j,K,t,t)}const re="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";for(let e=0;e<52;e++){const t=re.charAt(e);G(P,_,t,t),G(j,K,t,t)}const ne="ÇÐÞçþℂℍℕℙℚℝℤℎℏℊℋℌℐℑℒℓ℘ℛℜℬℰℱℳℭℨ";for(let e=0;e<30;e++){const t=ne.charAt(e);G(P,_,t,t),G(j,K,t,t)}let se="";for(let e=0;e<52;e++){se=String.fromCharCode(55349,56320+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56372+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56424+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56580+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56736+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56788+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56840+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56944+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,56632+e),G(P,_,se,se),G(j,K,se,se);const t=re.charAt(e);se=String.fromCharCode(55349,56476+e),G(P,_,t,se),G(j,K,t,se)}for(let e=0;e<10;e++)se=String.fromCharCode(55349,57294+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,57314+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,57324+e),G(P,_,se,se),G(j,K,se,se),se=String.fromCharCode(55349,57334+e),G(P,_,se,se),G(j,K,se,se);const oe=function(e,t,r){return!D[t][e]||!D[t][e].replace||55349===e.charCodeAt(0)||Object.prototype.hasOwnProperty.call(Q,e)&&r&&(r.fontFamily&&"tt"===r.fontFamily.slice(4,6)||r.font&&"tt"===r.font.slice(4,6))||(e=D[t][e].replace),new C(e)},ae=(e,t)=>{if(0===e.children.length||"mtext"!==e.children[e.children.length-1].type){const r=new B("mtext",[new C(t.children[0].text)]);e.children.push(r)}else e.children[e.children.length-1].children[0].text+=t.children[0].text},le=e=>{if("mrow"!==e.type&&"mstyle"!==e.type)return e;if(0===e.children.length)return e;const t=new B("mrow");for(let r=0;r0&&" "===n.children[0].text.charAt(s-1)&&(n.children[0].text=n.children[0].text.slice(0,-1)+" ");for(const[t,r]of Object.entries(e.attributes))n.attributes[t]=r}return 1===t.children.length&&"mtext"===t.children[0].type?t.children[0]:t},ie=function(e,t=!1){if(!(1!==e.length||e[0]instanceof y))return e[0];if(!t){e[0]instanceof B&&"mo"===e[0].type&&!e[0].attributes.fence&&(e[0].attributes.lspace="0em",e[0].attributes.rspace="0em");const t=e.length-1;e[t]instanceof B&&"mo"===e[t].type&&!e[t].attributes.fence&&(e[t].attributes.lspace="0em",e[t].attributes.rspace="0em")}return new B("mrow",e)};function ce(e){if(!e)return!1;if("mi"===e.type&&1===e.children.length){const t=e.children[0];return t instanceof C&&"."===t.text}if("mtext"===e.type&&1===e.children.length){const t=e.children[0];return t instanceof C&&" "===t.text}if("mo"===e.type&&1===e.children.length&&"true"===e.getAttribute("separator")&&"0em"===e.getAttribute("lspace")&&"0em"===e.getAttribute("rspace")){const t=e.children[0];return t instanceof C&&","===t.text}return!1}const me=(e,t)=>{const r=e[t],n=e[t+1];return"atom"===r.type&&","===r.text&&r.loc&&n.loc&&r.loc.end===n.loc.start},pe=e=>"atom"===e.type&&"rel"===e.family||"mclass"===e.type&&"mrel"===e.mclass,ue=function(e,t,r=!1){if(!r&&1===e.length){const r=he(e[0],t);return r instanceof B&&"mo"===r.type&&(r.setAttribute("lspace","0em"),r.setAttribute("rspace","0em")),[r]}const n=[],s=[];let o;for(let r=0;r0&&pe(e[t])&&pe(e[t-1])&&r.setAttribute("lspace","0em"),"mn"===r.type&&o&&"mn"===o.type)o.children.push(...r.children);else if(ce(r)&&o&&"mn"===o.type)o.children.push(...r.children);else if(o&&"mn"===o.type&&t=1&&o&&("mn"===o.type||ce(o))){const e=r.children[0];e instanceof B&&"mn"===e.type&&o&&(e.children=[...o.children,...e.children],n.pop())}n.push(r),o=r}}return n},de=function(e,t,r=!1){return ie(ue(e,t,r),r)},he=function(t,r){if(!t)return new B("mrow");if(d[t.type]){return d[t.type](t,r)}throw new e("Got group of unknown type: '"+t.type+"'")},ge=e=>new B("mtd",[],[],{padding:"0",width:"50%"}),fe=["mrow","mtd","mtable","mtr"],be=e=>{for(const t of e.children)if(t.type&&fe.includes(t.type)){if(t.classes&&"tml-label"===t.classes[0]){return t.label}{const e=be(t);if(e)return e}}else if(!t.type){const e=be(t);if(e)return e}};function ye(e,t,r,n){let s=null;1===e.length&&"tag"===e[0].type&&(s=e[0].tag,e=e[0].body);const o=ue(e,r);if(1===o.length&&o[0]instanceof S)return o[0];const a=n.displayMode||n.annotate?"none":n.wrap,l=0===o.length?null:o[0];let i=1===o.length&&null===s&&l instanceof B?o[0]:function(e,t,r){const n=[];let s=[],o=[],a=0,l=0;for(;l0&&s.push(new B("mrow",o)),s.push(r),o=[];const e=new B("mtd",s);e.style.textAlign="left",n.push(new B("mtr",[e])),s=[],l+=1;continue}if(o.push(r),r.type&&"mo"===r.type&&1===r.children.length&&(!r.attributes.form||"prefix"!==r.attributes.form)&&!Object.prototype.hasOwnProperty.call(r.attributes,"movablelimits")){const n=r.children[0].text;if("="===t&&"="===n){if(a+=1,a>1){o.pop();const e=new B("mrow",o);s.push(e),o=[r]}}else if("tex"===t){const t=l0){const e=new B("mrow",o);s.push(e)}if(n.length>0){const e=new B("mtd",s);e.style.textAlign="left";const t=new B("mtr",[e]);n.push(t);const o=new B("mtable",n);return r||(o.setAttribute("columnalign","left"),o.setAttribute("rowspacing","0em")),o}return O(s)}(o,a,n.displayMode);if(s&&(i=((e,t,r,n)=>{t=de(t[0].body,r),(t=le(t)).classes.push("tml-tag");const s=be(e);e=new B("mtd",[e]);const o=[ge(),e,ge()];o[n?0:2].children.push(t);const a=new B("mtr",o,["tml-tageqn"]);s&&a.setAttribute("id",s);const l=new B("mtable",[a]);return l.style.width="100%",l.setAttribute("displaystyle","true"),l})(i,s,r,n.leqno)),n.annotate){const e=new B("annotation",[new C(t)]);e.setAttribute("encoding","application/x-tex"),i=new B("semantics",[i,e])}const c=new B("math",[i]);return n.xml&&c.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n.displayMode&&(c.setAttribute("display","block"),c.style.display="block math",c.classes=["tml-display"]),c}const we=(e,t)=>{const r=e.isStretchy?L(e):new B("mo",[oe(e.label,e.mode)]);e.isStretchy||r.setAttribute("stretchy","false"),"\\vec"!==e.label&&(r.style.mathDepth="0");const n="\\c"===e.label?"munder":"mover",s=ke.has(e.label);if("mover"===n&&"math"===e.mode&&!e.isStretchy&&e.base.text&&1===e.base.text.length){const t=e.base.text,n="\\vec"===e.label,o="\\vec"===n?"-vec":"";n&&r.classes.push("tml-vec");const a=n?"-vec":s?"-acc":"";"DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ".indexOf(t)>-1?(r.classes.push(`chr-sml${o}`),r.classes.push(`wbk-sml${a}`)):"BCEGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ".indexOf(t)>-1?(r.classes.push(`chr-med${o}`),r.classes.push(`wbk-med${a}`)):"AFJdfΔΛ".indexOf(t)>-1?(r.classes.push(`chr-lrg${o}`),r.classes.push(`wbk-lrg${a}`)):n?r.classes.push("wbk-vec"):s&&r.classes.push("wbk-acc")}else s&&r.classes.push("wbk-acc");return new B(n,[he(e.base,t),r])},xe=new Set(["\\acute","\\check","\\grave","\\ddot","\\dddot","\\ddddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"]),ke=new Set(["\\acute","\\bar","\\breve","\\check","\\dot","\\ddot","\\grave","\\hat","\\mathring","\\`","\\'","\\^","\\=","\\u","\\.",'\\"',"\\r","\\H","\\v"]),ve={"\\`":"̀","\\'":"́","\\^":"̂","\\~":"̃","\\=":"̄","\\u":"̆","\\.":"̇",'\\"':"̈","\\r":"̊","\\H":"̋","\\v":"̌","\\c":"̧"};h({type:"accent",names:["\\acute","\\grave","\\ddot","\\dddot","\\ddddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\overparen","\\widecheck","\\widehat","\\wideparen","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(e,t)=>{const r=f(t[0]),n=!xe.has(e.funcName);return{type:"accent",mode:e.parser.mode,label:e.funcName,isStretchy:n,base:r}},mathmlBuilder:we}),h({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\c","\\u","\\.",'\\"',"\\r","\\H","\\v"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(e,t)=>{const r=f(t[0]),n=e.parser.mode;return"math"===n&&e.parser.settings.strict&&console.log(`Temml parse error: Command ${e.funcName} is invalid in math mode.`),"text"===n&&r.text&&1===r.text.length&&e.funcName in ve&&m.indexOf(r.text)>-1?{type:"textord",mode:"text",text:r.text+ve[e.funcName]}:"\\c"===e.funcName&&"text"===n&&r.text&&1===r.text.length?{type:"textord",mode:"text",text:r.text+"̧"}:{type:"accent",mode:n,label:e.funcName,isStretchy:!1,base:r}},mathmlBuilder:we}),h({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underparen","\\utilde"],props:{numArgs:1},handler:({parser:e,funcName:t},r)=>{const n=r[0];return{type:"accentUnder",mode:e.mode,label:t,base:n}},mathmlBuilder:(e,t)=>{const r=L(e);r.style["math-depth"]=0;return new B("munder",[he(e.base,t),r])}});const Ae={pt:800/803,pc:9600/803,dd:1238/1157*800/803,cc:12.792133216944668,nd:685/642*800/803,nc:1370/107*800/803,sp:1/65536*800/803,mm:25.4/72,cm:2.54/72,in:1/72,px:96/72},Te=["em","ex","mu","pt","mm","cm","in","px","bp","pc","dd","cc","nd","nc","sp"],Se=function(e){return"string"!=typeof e&&(e=e.unit),Te.indexOf(e)>-1},qe=e=>[1,.7,.5][Math.max(e-1,0)],Oe=function(t,r){let n=t.number;if(r.maxSize[0]<0&&n>0)return{number:0,unit:"em"};const s=t.unit;switch(s){case"mm":case"cm":case"in":case"px":return n*Ae[s]>r.maxSize[1]?{number:r.maxSize[1],unit:"pt"}:{number:n,unit:s};case"em":case"ex":return"ex"===s&&(n*=.431),n=Math.min(n/qe(r.level),r.maxSize[0]),{number:c(n),unit:"em"};case"bp":return n>r.maxSize[1]&&(n=r.maxSize[1]),{number:n,unit:"pt"};case"pt":case"pc":case"dd":case"cc":case"nd":case"nc":case"sp":return n=Math.min(n*Ae[s],r.maxSize[1]),{number:c(n),unit:"pt"};case"mu":return n=Math.min(n/18,r.maxSize[0]),{number:c(n),unit:"em"};default:throw new e("Invalid unit: '"+s+"'")}},Be=e=>{const t=new B("mspace");return t.setAttribute("width",e+"em"),t},Ce=(e,t=.3,r=0,n=!1)=>{if(null==e&&0===r)return Be(t);const s=e?[e]:[];if(0!==t&&s.unshift(Be(t)),r>0&&s.push(Be(r)),n){const e=new B("mpadded",s);return e.setAttribute("height","0.1px"),e}return new B("mrow",s)},Ne=(e,t)=>Number(e)/qe(t),ze=(e,t,r,n)=>{const s=E(e),o="eq"===e.slice(1,3),a="x"===e.charAt(1)?"1.75":"cd"===e.slice(2,4)?"3.0":o?"1.0":"2.0";s.setAttribute("lspace","0"),s.setAttribute("rspace",o?"0.5em":"0");const l=n.withLevel(n.level<2?2:3),i=Ne(a,l.level),c=Ne(a,3),m=Ce(null,i.toFixed(4),0),p=Ce(null,c.toFixed(4),0),u=Ne(o?0:.3,l.level).toFixed(4);let d,h;const g=t&&t.body&&(t.body.body||t.body.length>0);if(g){let r=he(t,l);r=Ce(r,u,u,"\\\\cdrightarrow"===e||"\\\\cdleftarrow"===e),d=new B("mover",[r,p])}const f=r&&r.body&&(r.body.body||r.body.length>0);if(f){let e=he(r,l);e=Ce(e,u,u),h=new B("munder",[e,p])}let b;return b=g||f?g&&f?new B("munderover",[s,h,d]):g?new B("mover",[s,d]):new B("munder",[s,h]):new B("mover",[s,m]),"3.0"===a&&(b.style.height="1em"),b.setAttribute("accent","false"),b};h({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xleftrightharpoons","\\xrightleftharpoons","\\yields","\\yieldsLeft","\\mesomerism","\\longrightharpoonup","\\longleftharpoondown","\\yieldsLeftRight","\\chemequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler:({parser:e,funcName:t},r,n)=>({type:"xArrow",mode:e.mode,name:t,body:r[0],below:n[0]}),mathmlBuilder(e,t){const r=[ze(e.name,e.body,e.below,t)];return r.unshift(Be(.2778)),r.push(Be(.2778)),new B("mrow",r)}});const $e={"\\equilibriumRight":["\\longrightharpoonup","\\eqleftharpoondown"],"\\equilibriumLeft":["\\eqrightharpoonup","\\longleftharpoondown"]};h({type:"stackedArrow",names:["\\equilibriumRight","\\equilibriumLeft"],props:{numArgs:1,numOptionalArgs:1},handler({parser:e,funcName:t},r,n){const s=r[0]?{type:"hphantom",mode:e.mode,body:r[0]}:null,o=n[0]?{type:"hphantom",mode:e.mode,body:n[0]}:null;return{type:"stackedArrow",mode:e.mode,name:t,body:r[0],upperArrowBelow:o,lowerArrowBody:s,below:n[0]}},mathmlBuilder(e,t){const r=$e[e.name][0],n=$e[e.name][1],s=ze(r,e.body,e.upperArrowBelow,t),o=ze(n,e.lowerArrowBody,e.below,t);let a;const l=new B("mpadded",[s]);if(l.setAttribute("voffset","0.3em"),l.setAttribute("height","+0.3em"),l.setAttribute("depth","-0.3em"),"\\equilibriumLeft"===e.name){const e=new B("mpadded",[o]);e.setAttribute("width","0.5em"),a=new B("mpadded",[Be(.2778),e,l,Be(.2778)])}else l.setAttribute("width","\\equilibriumRight"===e.name?"0.5em":"0"),a=new B("mpadded",[Be(.2778),l,o,Be(.2778)]);return a.setAttribute("voffset","-0.18em"),a.setAttribute("height","-0.18em"),a.setAttribute("depth","+0.18em"),a}});const Ee={};function Ie({type:e,names:t,props:r,handler:n,mathmlBuilder:s}){const o={type:e,numArgs:r.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:n};for(let e=0;e":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},Ge=()=>({type:"styling",body:[],mode:"math",scriptLevel:"display"}),Pe=e=>"textord"===e.type&&"@"===e.text,je=(e,t)=>("mathord"===e.type||"atom"===e.type)&&e.text===t;function Re(e,t,r){const n=De[e];switch(n){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(n,[t[0]],[t[1]]);case"\\uparrow":case"\\downarrow":{const e={type:"atom",text:n,mode:"math",family:"rel"},s={type:"ordgroup",mode:"math",body:[r.callFunction("\\\\cdleft",[t[0]],[]),r.callFunction("\\Big",[e],[]),r.callFunction("\\\\cdright",[t[1]],[])],semisimple:!0};return r.callFunction("\\\\cdparent",[s],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{const e={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[e],[])}default:return{type:"textord",text:" ",mode:"math"}}}h({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler:({parser:e,funcName:t},r)=>({type:"cdlabel",mode:e.mode,side:t.slice(4),label:r[0]}),mathmlBuilder(e,t){if(0===e.label.body.length)return new B("mrow",t);const r=he(e.label,t);"left"===e.side&&r.classes.push("tml-shift-left");const n=new B("mtd",[r]);n.style.padding="0";const s=new B("mtr",[n]),o=new B("mtable",[s]),a=new B("mpadded",[o]);return a.setAttribute("width","0.1px"),a.setAttribute("displaystyle","false"),a.setAttribute("scriptlevel","1"),a}}),h({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler:({parser:e},t)=>({type:"cdlabelparent",mode:e.mode,fragment:t[0]}),mathmlBuilder:(e,t)=>new B("mrow",[he(e.fragment,t)])});const Ue=e=>({type:"ordgroup",mode:"math",body:e,semisimple:!0}),He=(e,t)=>({type:t,mode:"math",body:Ue(e)});class Ve{constructor(e,t,r){this.lexer=e,this.start=t,this.end=r}static range(e,t){return t?e&&e.loc&&t.loc&&e.loc.lexer===t.loc.lexer?new Ve(e.loc.lexer,e.loc.start,t.loc.end):null:e&&e.loc}}class _e{constructor(e,t){this.text=e,this.loc=t}range(e,t){return new _e(t,Ve.range(this,e))}}const We=0,Xe=1,Ze=2,Ye=3,Je={};function Ke(e,t){Je[e]=t}const Qe=Je;Ke("\\noexpand",function(e){const t=e.popToken();return e.isExpandable(t.text)&&(t.noexpand=!0,t.treatAsRelax=!0),{tokens:[t],numArgs:0}}),Ke("\\expandafter",function(e){const t=e.popToken();return e.expandOnce(!0),{tokens:[t],numArgs:0}}),Ke("\\@firstoftwo",function(e){return{tokens:e.consumeArgs(2)[0],numArgs:0}}),Ke("\\@secondoftwo",function(e){return{tokens:e.consumeArgs(2)[1],numArgs:0}}),Ke("\\@ifnextchar",function(e){const t=e.consumeArgs(3);e.consumeSpaces();const r=e.future();return 1===t[0].length&&t[0][0].text===r.text?{tokens:t[1],numArgs:0}:{tokens:t[2],numArgs:0}}),Ke("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Ke("\\TextOrMath",function(e){const t=e.consumeArgs(2);return"text"===e.mode?{tokens:t[0],numArgs:0}:{tokens:t[1],numArgs:0}});const et=e=>{let t="";for(let r=e.length-1;r>-1;r--)t+=e[r].text;return t},tt={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15},rt=e=>{const t=e.future().text;return"EOF"===t?[null,""]:[tt[t.charAt(0)],t]},nt=(e,t,r)=>{for(let n=1;n=0;e--){const s=t[e].loc.start;s>n&&(r+=" ",n=s),r+=t[e].text,n+=t[e].text.length}return r}Ke("\\char",function(t){let r,n=t.popToken(),s="";if("'"===n.text)r=8,n=t.popToken();else if('"'===n.text)r=16,n=t.popToken();else if("`"===n.text)if(n=t.popToken(),"\\"===n.text[0])s=n.text.charCodeAt(1);else{if("EOF"===n.text)throw new e("\\char` missing argument");s=n.text.charCodeAt(0)}else r=10;if(r){let o,a=n.text;if(s=tt[a.charAt(0)],null==s||s>=r)throw new e(`Invalid base-${r} digit ${n.text}`);for(s=nt(s,a,r),[o,a]=rt(t);null!=o&&o":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcap":"\\dotsb","\\bigsqcup":"\\dotsb","\\bigtimes":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\DOTSX":"\\dotsx"};Ke("\\dots",function(e){let t="\\dotso";const r=e.expandAfterFuture().text;return r in ot?t=ot[r]:("\\not"===r.slice(0,4)||r in D.math&&["bin","rel"].includes(D.math[r].group))&&(t="\\dotsb"),t});const at={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};Ke("\\dotso",function(e){return e.future().text in at?"\\ldots\\,":"\\ldots"}),Ke("\\dotsc",function(e){const t=e.future().text;return t in at&&","!==t?"\\ldots\\,":"\\ldots"}),Ke("\\cdots",function(e){return e.future().text in at?"\\@cdots\\,":"\\@cdots"}),Ke("\\dotsb","\\cdots"),Ke("\\dotsm","\\cdots"),Ke("\\dotsi","\\!\\cdots"),Ke("\\idotsint","\\int\\!\\cdots\\!\\int"),Ke("\\dotsx","\\ldots\\,"),Ke("\\DOTSI","\\relax"),Ke("\\DOTSB","\\relax"),Ke("\\DOTSX","\\relax"),Ke("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Ke("\\,","{\\tmspace+{3mu}{.1667em}}"),Ke("\\thinspace","\\,"),Ke("\\>","\\mskip{4mu}"),Ke("\\:","{\\tmspace+{4mu}{.2222em}}"),Ke("\\medspace","\\:"),Ke("\\;","{\\tmspace+{5mu}{.2777em}}"),Ke("\\thickspace","\\;"),Ke("\\!","{\\tmspace-{3mu}{.1667em}}"),Ke("\\negthinspace","\\!"),Ke("\\negmedspace","{\\tmspace-{4mu}{.2222em}}"),Ke("\\negthickspace","{\\tmspace-{5mu}{.277em}}"),Ke("\\enspace","\\kern.5em "),Ke("\\enskip","\\hskip.5em\\relax"),Ke("\\quad","\\hskip1em\\relax"),Ke("\\qquad","\\hskip2em\\relax"),Ke("\\AA","\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax"),Ke("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Ke("\\tag@paren","\\tag@literal{({#1})}"),Ke("\\tag@literal",t=>{if(t.macros.get("\\df@tag"))throw new e("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"}),Ke("\\notag","\\nonumber"),Ke("\\nonumber","\\gdef\\@eqnsw{0}"),Ke("\\bmod","\\mathbin{\\text{mod}}"),Ke("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Ke("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Ke("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Ke("\\newline","\\\\\\relax"),Ke("\\TeX","\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}"),Ke("\\LaTeX","\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX"),Ke("\\Temml","\\textrm{T}\\kern-0.2em\\lower{0.2em}{\\textrm{E}}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}"),Ke("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Ke("\\@hspace","\\hskip #1\\relax"),Ke("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Ke("\\colon",'\\mathpunct{\\char"3a}'),Ke("\\prescript","\\pres@cript{_{#1}^{#2}}{}{#3}"),Ke("\\ordinarycolon",'\\char"3a'),Ke("\\vcentcolon","\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}"),Ke("\\coloneq",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}'),Ke("\\Coloneq",'\\mathrel{\\char"2237\\char"2212}'),Ke("\\Eqqcolon",'\\mathrel{\\char"3d\\char"2237}'),Ke("\\Eqcolon",'\\mathrel{\\char"2212\\char"2237}'),Ke("\\colonapprox",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}'),Ke("\\Colonapprox",'\\mathrel{\\char"2237\\char"2248}'),Ke("\\colonsim",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'),Ke("\\Colonsim",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'),Ke("\\ratio","\\vcentcolon"),Ke("\\coloncolon","\\dblcolon"),Ke("\\colonequals","\\coloneqq"),Ke("\\coloncolonequals","\\Coloneqq"),Ke("\\equalscolon","\\eqqcolon"),Ke("\\equalscoloncolon","\\Eqqcolon"),Ke("\\colonminus","\\coloneq"),Ke("\\coloncolonminus","\\Coloneq"),Ke("\\minuscolon","\\eqcolon"),Ke("\\minuscoloncolon","\\Eqcolon"),Ke("\\coloncolonapprox","\\Colonapprox"),Ke("\\coloncolonsim","\\Colonsim"),Ke("\\notni","\\mathrel{\\char`∌}"),Ke("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Ke("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Ke("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Ke("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Ke("\\varlimsup","\\DOTSB\\operatorname*{\\overline{\\text{lim}}}"),Ke("\\varliminf","\\DOTSB\\operatorname*{\\underline{\\text{lim}}}"),Ke("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}"),Ke("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}"),Ke("\\centerdot","{\\medspace\\rule{0.167em}{0.189em}\\medspace}"),Ke("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Ke("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Ke("\\plim","\\DOTSB\\operatorname*{plim}"),Ke("\\leftmodels","\\mathop{\\reflectbox{$\\models$}}"),Ke("\\bra","\\mathinner{\\langle{#1}|}"),Ke("\\ket","\\mathinner{|{#1}\\rangle}"),Ke("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Ke("\\Bra","\\left\\langle#1\\right|"),Ke("\\Ket","\\left|#1\\right\\rangle");const lt=(e,t)=>{const r=`}\\,\\middle${"|"===t[0]?"\\vert":"\\Vert"}\\,{`;return e.slice(0,t.index)+r+e.slice(t.index+t[0].length)};Ke("\\Braket",function(e){let t=st(e);const r=/\|\||\||\\\|/g;let n;for(;null!==(n=r.exec(t));)t=lt(t,n);return"\\left\\langle{"+t+"}\\right\\rangle"}),Ke("\\Set",function(e){let t=st(e);const r=/\|\||\||\\\|/.exec(t);return r&&(t=lt(t,r)),"\\left\\{\\:{"+t+"}\\:\\right\\}"}),Ke("\\set",function(e){return"\\{{"+st(e).replace(/\|/,"}\\mid{")+"}\\}"}),Ke("\\angln","{\\angl n}"),Ke("\\odv","\\@ifstar\\odv@next\\odv@numerator"),Ke("\\odv@numerator","\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}"),Ke("\\odv@next","\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1"),Ke("\\pdv","\\@ifstar\\pdv@next\\pdv@numerator");const it=e=>{const t=e[0][0].text,r=et(e[1]).split(","),n=String(r.length),s="1"===n?"\\partial":`\\partial^${n}`;let o="";return r.map(e=>{o+="\\partial "+e.trim()+"\\,"}),[t,s,o.replace(/\\,$/,"")]};function ct(e){const t=[];e.consumeSpaces();let r=e.fetch().text;for("\\relax"===r&&(e.consume(),e.consumeSpaces(),r=e.fetch().text);"\\hline"===r||"\\hdashline"===r;)e.consume(),t.push("\\hdashline"===r),e.consumeSpaces(),r=e.fetch().text;return t}Ke("\\pdv@numerator",function(e){const[t,r,n]=it(e.consumeArgs(2));return`\\frac{${r} ${t}}{${n}}`}),Ke("\\pdv@next",function(e){const[t,r,n]=it(e.consumeArgs(2));return`\\frac{${r}}{${n}} ${t}`}),Ke("\\upalpha","\\up@greek{\\alpha}"),Ke("\\upbeta","\\up@greek{\\beta}"),Ke("\\upgamma","\\up@greek{\\gamma}"),Ke("\\updelta","\\up@greek{\\delta}"),Ke("\\upepsilon","\\up@greek{\\epsilon}"),Ke("\\upzeta","\\up@greek{\\zeta}"),Ke("\\upeta","\\up@greek{\\eta}"),Ke("\\uptheta","\\up@greek{\\theta}"),Ke("\\upiota","\\up@greek{\\iota}"),Ke("\\upkappa","\\up@greek{\\kappa}"),Ke("\\uplambda","\\up@greek{\\lambda}"),Ke("\\upmu","\\up@greek{\\mu}"),Ke("\\upnu","\\up@greek{\\nu}"),Ke("\\upxi","\\up@greek{\\xi}"),Ke("\\upomicron","\\up@greek{\\omicron}"),Ke("\\uppi","\\up@greek{\\pi}"),Ke("\\upalpha","\\up@greek{\\alpha}"),Ke("\\uprho","\\up@greek{\\rho}"),Ke("\\upsigma","\\up@greek{\\sigma}"),Ke("\\uptau","\\up@greek{\\tau}"),Ke("\\upupsilon","\\up@greek{\\upsilon}"),Ke("\\upphi","\\up@greek{\\phi}"),Ke("\\upchi","\\up@greek{\\chi}"),Ke("\\uppsi","\\up@greek{\\psi}"),Ke("\\upomega","\\up@greek{\\omega}"),Ke("\\invamp",'\\mathbin{\\char"214b}'),Ke("\\parr",'\\mathbin{\\char"214b}'),Ke("\\upand",'\\mathbin{\\char"214b}'),Ke("\\with",'\\mathbin{\\char"26}'),Ke("\\multimapinv",'\\mathrel{\\char"27dc}'),Ke("\\multimapboth",'\\mathrel{\\char"29df}'),Ke("\\scoh",'{\\mkern5mu\\char"2322\\mkern5mu}'),Ke("\\sincoh",'{\\mkern5mu\\char"2323\\mkern5mu}'),Ke("\\coh",'{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}}\n{\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}'),Ke("\\incoh",'{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}}\n{\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}'),Ke("\\standardstate","\\text{\\tiny\\char`⦵}");const mt=t=>{if(!t.parser.settings.displayMode)throw new e(`{${t.envName}} can be used only in display mode.`)},pt=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/,ut=e=>{let t=e.get("\\arraystretch");"string"!=typeof t&&(t=et(t.tokens)),t=isNaN(t)?null:Number(t);let r=e.get("\\arraycolsep");"string"!=typeof r&&(r=et(r.tokens));const n=pt.exec(r);return[t,n?{number:+(n[1]+n[2]),unit:n[3]}:null]},dt=t=>{let r="";for(let n=0;n1||!a)&&h.pop(),f.push(dt(r.body)),b.length{const t=new B("mtd",[]);return t.style={padding:"0",width:"50%"},e.envClasses.includes("multline")&&(t.style.width="7.5%"),t},wt=function(e,t){const r=[],n=e.body.length,s=e.hLinesBeforeRow,o=e.tags&&e.tags.some(e=>e);for(let a=0;a0&&(2===s[0].length?p.children.forEach(e=>{e.style.borderTop="0.15em double"}):p.children.forEach(e=>{e.style.borderTop=s[0][0]?"0.06em dashed":"0.06em solid"})),s[a+1].length>0&&(2===s[a+1].length?p.children.forEach(e=>{e.style.borderBottom="0.15em double"}):p.children.forEach(e=>{e.style.borderBottom=s[a+1][0]?"0.06em dashed":"0.06em solid"}));let d=!0;for(let e=0;e0&&(a=e.envClasses.includes("abut")||e.envClasses.includes("cases")?"0":e.envClasses.includes("small")?"0.1389":e.envClasses.includes("cd")?"0.25":"0.4",l="em"),e.arraycolsep){const r=Oe(e.arraycolsep,t);a=r.number.toFixed(4),l=r.unit}if(a){const t=0===r.length?0:r[0].children.length,n=(r,n)=>0===r&&0===n||r===t-1&&1===n?"0":"align"!==e.envClasses[0]?a:1===n?"0":o?r%2?"1":"0":r%2?"0":"1";for(let e=0;e0){const t=e.envClasses.includes("align")||e.envClasses.includes("alignat");for(let n=0;n1&&e.envClasses.includes("cases")&&(s.children[1].style.paddingLeft="1em"),e.envClasses.includes("cases")||e.envClasses.includes("subarray"))for(const e of s.children)e.classes.push("tml-left")}}let i=new B("mtable",r);if(e.envClasses.length>0&&(e.envClasses.includes("jot")?i.classes.push("tml-jot"):e.envClasses.includes("small")&&i.classes.push("tml-small")),"display"===e.scriptLevel&&i.setAttribute("displaystyle","true"),(e.autoTag||e.envClasses.includes("multline"))&&(i.style.width="100%"),e.cols&&e.cols.length>0){const t=e.cols;let r=!1,n=0,s=t.length;for(;"separator"===t[n].type;)n+=1;for(;"separator"===t[s-1].type;)s-=1;if("separator"===t[0].type){const e="separator"===t[1].type?"0.15em double":"|"===t[0].separator?"0.06em solid ":"0.06em dashed ";for(const t of i.children)t.children[0].style.borderLeft=e}let a=o?0:-1;for(let e=n;e-1;if(r[0]&&i){let t="";for(let e=0;e0?new Array(o.body[0].length).fill({type:"align",align:n}):[];const[a,l]=ut(t.parser.gullet.macros);return o.arraystretch=a,!l||6===l&&"pt"===l||(o.arraycolsep=l),r?{type:"leftright",mode:t.mode,body:[o],left:r[0],right:r[1],rightColor:void 0}:o},mathmlBuilder:wt}),Ie({type:"array",names:["bordermatrix"],props:{numArgs:0},handler(e){const t=gt(e.parser,{cols:[],envClasses:["bordermatrix"]},"text");return t.cols=t.body.length>0?new Array(t.body[0].length).fill({type:"align",align:"c"}):[],t.envClasses=[],t.arraystretch=1,"matrix"===e.envName?t:((e,t)=>{const r=e.body;r[0].shift();const n=new Array(r.length-1).fill().map(()=>[]);for(let e=1;e[]);for(let e=0;e[]),envClasses:[],scriptLevel:"text",arraystretch:1,labels:new Array(n.length).fill(""),arraycolsep:{number:.04,unit:"em"}},a={type:"styling",mode:"math",scriptLevel:"text",body:[{type:"array",mode:"math",body:s,cols:new Array(s.length).fill({type:"align",align:"c"}),rowGaps:new Array(s.length-1).fill(null),hLinesBeforeRow:new Array(s.length+1).fill().map(()=>[]),envClasses:[],scriptLevel:"text",arraystretch:1,labels:new Array(s.length).fill(""),arraycolsep:null}]},l={type:"leftright",mode:"math",body:[e],left:t?t[0]:"(",right:t?t[1]:")",rightColor:void 0};return Ue([o,{type:"supsub",mode:"math",stack:!0,base:{type:"op",mode:"math",limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!0,symbol:!1,suppressBaseShift:!0,body:[l]},sup:a,sub:null}])})(t,e.delimiters)},mathmlBuilder:wt}),Ie({type:"array",names:["smallmatrix"],props:{numArgs:0},handler:e=>gt(e.parser,{envClasses:["small"]},"script"),mathmlBuilder:wt}),Ie({type:"array",names:["subarray"],props:{numArgs:1},handler(t,r){const n=(Me(r[0])?[r[0]]:Le(r[0],"ordgroup").body).map(function(t){const r=Fe(t).text;if(-1!=="lc".indexOf(r))return{type:"align",align:r};throw new e("Unknown column alignment: "+r,t)});if(n.length>1)throw new e("{subarray} can contain only one column");let s={cols:n,envClasses:["small"]};if(s=gt(t.parser,s,"script"),s.body.length>0&&s.body[0].length>1)throw new e("{subarray} can contain only one column");return s},mathmlBuilder:wt}),Ie({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(e){const t=gt(e.parser,{cols:[],envClasses:["cases"]},ft(e.envName));return{type:"leftright",mode:e.mode,body:[t],left:e.envName.indexOf("r")>-1?".":"\\{",right:e.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},mathmlBuilder:wt}),Ie({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:xt,mathmlBuilder:wt}),Ie({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:xt,mathmlBuilder:wt}),Ie({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(e){"gathered"!==e.envName&&mt(e);const t={cols:[],envClasses:["abut","jot"],autoTag:ht(e.envName),emptySingleRow:!0,leqno:e.parser.settings.leqno};return gt(e.parser,t,"display")},mathmlBuilder:wt}),Ie({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(e){mt(e);const t={autoTag:ht(e.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,envClasses:["align"],leqno:e.parser.settings.leqno};return gt(e.parser,t,"display")},mathmlBuilder:wt}),Ie({type:"array",names:["multline","multline*"],props:{numArgs:0},handler(e){mt(e);const t={autoTag:"multline"===e.envName,maxNumCols:1,envClasses:["jot","multline"],leqno:e.parser.settings.leqno};return gt(e.parser,t,"display")},mathmlBuilder:wt}),Ie({type:"array",names:["CD"],props:{numArgs:0},handler:t=>(mt(t),function(t){const r=[];for(t.gullet.beginGroup(),t.gullet.macros.set("\\cr","\\\\\\relax"),t.gullet.beginGroup();;){r.push(t.parseExpression(!1,"\\\\")),t.gullet.endGroup(),t.gullet.beginGroup();const n=t.fetch().text;if("&"!==n&&"\\\\"!==n){if("\\end"===n){0===r[r.length-1].length&&r.pop();break}throw new e("Expected \\\\ or \\cr or \\end",t.nextToken)}t.consume()}let n=[];const s=[n];for(let o=0;o-1);else{if(!("<>AV".indexOf(s)>-1))throw new e('Expected one of "<>AV=|." after @.');for(let t=0;t<2;t++){let n=!0;for(let l=r+1;l{let s=["(",")"];if("\\bordermatrix"===t&&n[0]&&n[0].body){const e=n[0].body;2===e.length&&"atom"===e[0].type&&"atom"===e[1].type&&"open"===e[0].family&&"close"===e[1].family&&(s=[e[0].text,e[1].text])}e.consumeSpaces(),e.consume();const o=kt.bordermatrix,a={mode:e.mode,envName:t.slice(1),delimiters:s,parser:e},l=o.handler(a);return e.expect("}",!0),l}}),h({type:"cancelto",names:["\\cancelto"],props:{numArgs:2},handler({parser:e},t){const r=t[0],n=t[1];return{type:"cancelto",mode:e.mode,body:n,to:r,isCharacterBox:i(n)}},mathmlBuilder(e,t){const r=new B("mrow",[he(e.body,t)],["ff-narrow"]),n=new B("mphantom",[he(e.body,t)]),s=new B("mrow",[n],["tml-cancelto"]);s.style.color=t.color,e.isCharacterBox&&m.indexOf(e.body.body[0].text)>-1&&(s.style.left="0.1em",s.style.width="90%");const o=new B("mrow",[r,s],["menclose"]);if(!e.isCharacterBox||/[f∫∑]/.test(e.body.body[0].text))n.style.paddingRight="0.2em";else{n.style.padding="0.5ex 0.1em 0 0";const e=new B("mspace",[]);e.setAttribute("height","0.85em"),r.children.push(e)}let a;if(e.isCharacterBox)a=new B("mspace",[]),a.setAttribute("height","1em");else{const r=he(e.body,t),n=new B("mpadded",[r]);n.setAttribute("width","0.1px"),a=new B("mphantom",[n])}const l=he(e.to,t);l.style.color=t.color;const i=new B("mpadded",[l]);if(!e.isCharacterBox||/[f∫∑]/.test(e.body.body[0].text)){const e=new B("mspace",[]);e.setAttribute("width","0.2em"),i.children.unshift(e)}i.setAttribute("width","0.1px");const c=new B("mover",[a,i]),p=new B("mrow",[],["ff-nudge-left"]);return O([ie([o,c]),p])}}),h({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler({parser:t,token:r},n){const s=Le(n[0],"ordgroup").body;let o="";for(let e=0;e{let t=e.toString(16);return 1===t.length&&(t="0"+t),t},Bt=JSON.parse('{\n "Apricot": "#ffb484",\n "Aquamarine": "#08b4bc",\n "Bittersweet": "#c84c14",\n "blue": "#0000FF",\n "Blue": "#303494",\n "BlueGreen": "#08b4bc",\n "BlueViolet": "#503c94",\n "BrickRed": "#b8341c",\n "brown": "#BF8040",\n "Brown": "#802404",\n "BurntOrange": "#f8941c",\n "CadetBlue": "#78749c",\n "CarnationPink": "#f884b4",\n "Cerulean": "#08a4e4",\n "CornflowerBlue": "#40ace4",\n "cyan": "#00FFFF",\n "Cyan": "#08acec",\n "Dandelion": "#ffbc44",\n "darkgray": "#404040",\n "DarkOrchid": "#a8548c",\n "Emerald": "#08ac9c",\n "ForestGreen": "#089c54",\n "Fuchsia": "#90348c",\n "Goldenrod": "#ffdc44",\n "gray": "#808080",\n "Gray": "#98949c",\n "green": "#00FF00",\n "Green": "#08a44c",\n "GreenYellow": "#e0e474",\n "JungleGreen": "#08ac9c",\n "Lavender": "#f89cc4",\n "lightgray": "#c0c0c0",\n "lime": "#BFFF00",\n "LimeGreen": "#90c43c",\n "magenta": "#FF00FF",\n "Magenta": "#f0048c",\n "Mahogany": "#b0341c",\n "Maroon": "#b03434",\n "Melon": "#f89c7c",\n "MidnightBlue": "#086494",\n "Mulberry": "#b03c94",\n "NavyBlue": "#086cbc",\n "olive": "#7F7F00",\n "OliveGreen": "#407c34",\n "orange": "#FF8000",\n "Orange": "#f8843c",\n "OrangeRed": "#f0145c",\n "Orchid": "#b074ac",\n "Peach": "#f8945c",\n "Periwinkle": "#8074bc",\n "PineGreen": "#088c74",\n "pink": "#ff7f7f",\n "Plum": "#98248c",\n "ProcessBlue": "#08b4ec",\n "purple": "#BF0040",\n "Purple": "#a0449c",\n "RawSienna": "#983c04",\n "red": "#ff0000",\n "Red": "#f01c24",\n "RedOrange": "#f86434",\n "RedViolet": "#a0246c",\n "Rhodamine": "#f0549c",\n "Royallue": "#0874bc",\n "RoyalPurple": "#683c9c",\n "RubineRed": "#f0047c",\n "Salmon": "#f8948c",\n "SeaGreen": "#30bc9c",\n "Sepia": "#701404",\n "SkyBlue": "#48c4dc",\n "SpringGreen": "#c8dc64",\n "Tan": "#e09c74",\n "teal": "#007F7F",\n "TealBlue": "#08acb4",\n "Thistle": "#d884b4",\n "Turquoise": "#08b4cc",\n "violet": "#800080",\n "Violet": "#60449c",\n "VioletRed": "#f054a4",\n "WildStrawberry": "#f0246c",\n "yellow": "#FFFF00",\n "Yellow": "#fff404",\n "YellowGreen": "#98cc6c",\n "YellowOrange": "#ffa41c"\n}'),Ct=(t,r)=>{let n="";if("HTML"===t){if(!vt.test(r))throw new e("Invalid HTML input.");n=r}else if("RGB"===t){if(!Tt.test(r))throw new e("Invalid RGB input.");r.split(",").map(e=>{n+=Ot(Number(e.trim()))})}else{if(!St.test(r))throw new e("Invalid rbg input.");r.split(",").map(t=>{const r=Number(t.trim());if(r>1)throw new e("Color rgb input must be < 1.");n+=Ot(Number((255*r).toFixed(0)))})}return"#"!==n.charAt(0)&&(n="#"+n),n},Nt=(t,r,n)=>{const s=`\\\\color@${t}`;if(!At.exec(t))throw new e("Invalid color: '"+t+"'",n);return qt.test(t)?"#"+t:("#"===t.charAt(0)||(r.has(s)?t=r.get(s).tokens[0].text:Bt[t]&&(t=Bt[t])),t)},zt=(e,t)=>{let r=ue(e.body,t.withColor(e.color));return 0===r.length&&r.push(new B("mrow")),r=r.map(t=>(t.style.color=e.color,t)),O(r)};h({type:"color",names:["\\textcolor"],props:{numArgs:2,numOptionalArgs:1,allowedInText:!0,argTypes:["raw","raw","original"]},handler({parser:e,token:t},r,n){const s=n[0]&&Le(n[0],"raw").string;let o="";if(s){const e=Le(r[0],"raw").string;o=Ct(s,e)}else o=Nt(Le(r[0],"raw").string,e.gullet.macros,t);const a=r[1];return{type:"color",mode:e.mode,color:o,isTextColor:!0,body:b(a)}},mathmlBuilder:zt}),h({type:"color",names:["\\color"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0,argTypes:["raw","raw"]},handler({parser:e,breakOnTokenText:t,token:r},n,s){const o=s[0]&&Le(s[0],"raw").string;let a="";if(o){const e=Le(n[0],"raw").string;a=Ct(o,e)}else a=Nt(Le(n[0],"raw").string,e.gullet.macros,r);const l=e.parseExpression(!0,t,!0);return{type:"color",mode:e.mode,color:a,isTextColor:!1,body:l}},mathmlBuilder:zt}),h({type:"color",names:["\\definecolor"],props:{numArgs:3,allowedInText:!0,argTypes:["raw","raw","raw"]},handler({parser:t,funcName:r,token:n},s){const o=Le(s[0],"raw").string;if(!/^[A-Za-z]+$/.test(o))throw new e("Color name must be latin letters.",n);const a=Le(s[1],"raw").string;if(!["HTML","RGB","rgb"].includes(a))throw new e("Color model must be HTML, RGB, or rgb.",n);const l=Le(s[2],"raw").string,i=Ct(a,l);return t.gullet.macros.set(`\\\\color@${o}`,{tokens:[{text:i}],numArgs:0}),{type:"internal",mode:t.mode}}}),h({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler({parser:e},t,r){const n="["===e.gullet.future().text?e.parseSizeGroup(!0):null,s=!e.settings.displayMode;return{type:"cr",mode:e.mode,newLine:s,size:n&&Le(n,"size").value}},mathmlBuilder(e,t){const r=new B("mo");if(e.newLine&&(r.setAttribute("linebreak","newline"),e.size)){const n=Oe(e.size,t);r.setAttribute("height",n.number+n.unit)}return r}});const $t={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},Et=t=>{const r=t.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(r))throw new e("Expected a control sequence",t);return r},It=(e,t,r,n)=>{let s=e.gullet.macros.get(r.text);null==s&&(r.noexpand=!0,s={tokens:[r],numArgs:0,unexpandable:!e.gullet.isExpandable(r.text)}),e.gullet.macros.set(t,s,n)};h({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler({parser:t,funcName:r}){t.consumeSpaces();const n=t.fetch();if($t[n.text])return"\\global"!==r&&"\\\\globallong"!==r||(n.text=$t[n.text]),Le(t.parseFunction(),"internal");throw new e("Invalid token after macro prefix",n)}}),h({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler({parser:t,funcName:r}){let n=t.gullet.popToken();const s=n.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(s))throw new e("Expected a control sequence",n);let o,a=0;const l=[[]];for(;"{"!==t.gullet.future().text;)if(n=t.gullet.popToken(),"#"===n.text){if("{"===t.gullet.future().text){o=t.gullet.future(),l[a].push("{");break}if(n=t.gullet.popToken(),!/^[1-9]$/.test(n.text))throw new e(`Invalid argument number "${n.text}"`);if(parseInt(n.text)!==a+1)throw new e(`Argument number "${n.text}" out of order`);a++,l.push([])}else{if("EOF"===n.text)throw new e("Expected a macro definition");l[a].push(n.text)}let{tokens:i}=t.gullet.consumeArg();if(o&&i.unshift(o),"\\edef"===r||"\\xdef"===r){if(i=t.gullet.expandTokens(i),i.length>t.gullet.settings.maxExpand)throw new e("Too many expansions in an "+r);i.reverse()}return t.gullet.macros.set(s,{tokens:i,numArgs:a,delimiters:l},r===$t[r]),{type:"internal",mode:t.mode}}}),h({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler({parser:e,funcName:t}){const r=Et(e.gullet.popToken());e.gullet.consumeSpaces();const n=(e=>{let t=e.gullet.popToken();return"="===t.text&&(t=e.gullet.popToken()," "===t.text&&(t=e.gullet.popToken())),t})(e);return It(e,r,n,"\\\\globallet"===t),{type:"internal",mode:e.mode}}}),h({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler({parser:e,funcName:t}){const r=Et(e.gullet.popToken()),n=e.gullet.popToken(),s=e.gullet.popToken();return It(e,r,s,"\\\\globalfuture"===t),e.gullet.pushToken(s),e.gullet.pushToken(n),{type:"internal",mode:e.mode}}}),h({type:"internal",names:["\\newcommand","\\renewcommand","\\providecommand"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler({parser:t,funcName:r}){let n="";const s=t.gullet.popToken();"{"===s.text?(n=Et(t.gullet.popToken()),t.gullet.popToken()):n=Et(s);const o=t.gullet.isDefined(n);if(o&&"\\newcommand"===r)throw new e(`\\newcommand{${n}} attempting to redefine ${n}; use \\renewcommand`);if(!o&&"\\renewcommand"===r)throw new e(`\\renewcommand{${n}} when command ${n} does not yet exist; use \\newcommand`);let a=0;if("["===t.gullet.future().text){let r=t.gullet.popToken();if(r=t.gullet.popToken(),!/^[0-9]$/.test(r.text))throw new e(`Invalid number of arguments: "${r.text}"`);if(a=parseInt(r.text),r=t.gullet.popToken(),"]"!==r.text)throw new e(`Invalid argument "${r.text}"`)}const{tokens:l}=t.gullet.consumeArg();return"\\providecommand"===r&&t.gullet.macros.has(n)||t.gullet.macros.set(n,{tokens:l,numArgs:a}),{type:"internal",mode:t.mode}}});const Lt={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},Ft={"(":")","\\lparen":"\\rparen","[":"]","\\lbrack":"\\rbrack","\\{":"\\}","\\lbrace":"\\rbrace","⦇":"⦈","\\llparenthesis":"\\rrparenthesis","\\lfloor":"\\rfloor","⌊":"⌋","\\lceil":"\\rceil","⌈":"⌉","\\langle":"\\rangle","⟨":"⟩","\\lAngle":"\\rAngle","⟪":"⟫","\\llangle":"\\rrangle","⦉":"⦊","\\lvert":"\\rvert","\\lVert":"\\rVert","\\lgroup":"\\rgroup","⟮":"⟯","\\lmoustache":"\\rmoustache","⎰":"⎱","\\llbracket":"\\rrbracket","⟦":"⟧","\\lBrace":"\\rBrace","⦃":"⦄"},Mt=new Set(Object.keys(Ft));new Set(Object.values(Ft));const Dt=new Set(["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","⦇","\\llparenthesis","⦈","\\rrparenthesis","\\lfloor","\\rfloor","⌊","⌋","\\lceil","\\rceil","⌈","⌉","<",">","\\langle","⟨","\\rangle","⟩","\\lAngle","⟪","\\rAngle","⟫","\\llangle","⦉","\\rrangle","⦊","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","⟮","⟯","\\lmoustache","\\rmoustache","⎰","⎱","\\llbracket","\\rrbracket","⟦","⟧","\\lBrace","\\rBrace","⦃","⦄","/","\\backslash","|","\\vert","\\|","\\Vert","‖","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."]),Gt=new Set(["}","\\left","\\middle","\\right"]),Pt=e=>e.length>0&&(Dt.has(e)||Lt[e]||Gt.has(e)),jt=[0,1.2,1.8,2.4,3];function Rt(t,r){"ordgroup"===t.type&&1===t.body.length&&(t=t.body[0]);const n=Me(t);if(n&&Dt.has(n.text))return"<"!==n.text&&"\\lt"!==n.text||(n.text="⟨"),">"!==n.text&&"\\gt"!==n.text||(n.text="⟩"),n;throw new e(n?`Invalid delimiter '${n.text}' after '${r.funcName}'`:`Invalid delimiter type '${t.type}'`,t)}const Ut=new Set(["/","\\","\\backslash","∖","\\vert","|"]),Ht=(e,t,r,n)=>{const s=new B("mo",[oe("."===e?"":e,t)]);return s.setAttribute("fence","true"),s.setAttribute("form",r),s.setAttribute("stretchy",n?"true":"false"),s};function Vt(e){if(!e.body)throw new Error("Bug: The delim ParseNode wasn't fully parsed.")}h({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:(e,t)=>{const r=Rt(t[0],e),n={type:"delimsizing",mode:e.parser.mode,size:Lt[e.funcName].size,mclass:Lt[e.funcName].mclass,delim:r.text},s=e.parser.fetch().text;return"^"!==s&&"_"!==s?n:{type:"ordgroup",mode:"math",body:[n,{type:"ordgroup",mode:"math",body:[]}]}},mathmlBuilder:e=>{const t=[],r="."===e.delim?"":e.delim;t.push(oe(r,e.mode));const n=new B("mo",t);return"mopen"===e.mclass||"mclose"===e.mclass?n.setAttribute("fence","true"):n.setAttribute("fence","false"),(Ut.has(r)||r.indexOf("arrow")>-1)&&n.setAttribute("stretchy","true"),n.setAttribute("symmetric","true"),n.setAttribute("minsize",jt[e.size]+"em"),n.setAttribute("maxsize",jt[e.size]+"em"),n}}),h({type:"leftright-right",names:["\\right"],props:{numArgs:1,argTypes:["primitive"]},handler:(e,t)=>({type:"leftright-right",mode:e.parser.mode,delim:Rt(t[0],e).text})}),h({type:"leftright",names:["\\left"],props:{numArgs:1,argTypes:["primitive"]},handler:(t,r)=>{const n=Rt(r[0],t),s=t.parser;++s.leftrightDepth;let o=s.parseExpression(!1,"\\right",!0),a=s.fetch();for(;"\\middle"===a.text;){s.consume();const t=s.fetch().text;if(!D.math[t])throw new e(`Invalid delimiter '${t}' after '\\middle'`);Rt({type:"atom",mode:"math",text:t},{funcName:"\\middle"}),o.push({type:"middle",mode:"math",delim:t}),s.consume(),o=o.concat(s.parseExpression(!1,"\\right",!0)),a=s.fetch()}--s.leftrightDepth,s.expect("\\right",!1);const l=Le(s.parseFunction(),"leftright-right");return{type:"leftright",mode:s.mode,body:o,left:n.text,right:l.delim,isStretchy:!0}},mathmlBuilder:(e,t)=>{Vt(e);const r=ue(e.body,t),n=Ht(e.left,e.mode,"prefix",!0);r.unshift(n);const s=Ht(e.right,e.mode,"postfix",!0);if(e.body.length>0){const t=e.body[e.body.length-1];"color"!==t.type||t.isTextColor||s.setAttribute("mathcolor",t.color)}return r.push(s),ie(r)}}),h({type:"delimiter",names:Array.from(Mt),props:{numArgs:0,allowedInText:!0,allowedInMath:!0,allowedInArgument:!0},handler:({parser:t,funcName:r,token:n})=>{if("text"===t.mode)return{type:"textord",mode:"text",text:r,loc:n.loc};if(!t.settings.wrapDelimiterPairs)return{type:"atom",mode:"math",family:"open",loc:n.loc,text:r};const s=Ft[r],o=t.parseExpression(!1,s,!1);if(t.fetch().text!==s)throw new e("Unmatched delimiter");return t.consume(),{type:"delimiter",mode:t.mode,body:o,left:r,right:s}},mathmlBuilder:(e,t)=>{Vt(e);const r=ue(e.body,t),n=Ht(e.left,e.mode,"prefix",!1);r.unshift(n);const s=Ht(e.right,e.mode,"postfix",!1);if(e.body.length>0){const t=e.body[e.body.length-1];"color"!==t.type||t.isTextColor||s.setAttribute("mathcolor",t.color)}return r.push(s),ie(r)}}),h({type:"middle",names:["\\middle"],props:{numArgs:1,argTypes:["primitive"]},handler:(t,r)=>{const n=Rt(r[0],t);if(!t.parser.leftrightDepth)throw new e("\\middle without preceding \\left",n);return{type:"middle",mode:t.parser.mode,delim:n.text}},mathmlBuilder:e=>{const t=oe(e.delim,e.mode),r=new B("mo",[t]);return r.setAttribute("fence","true"),e.delim.indexOf("arrow")>-1&&r.setAttribute("stretchy","true"),r.setAttribute("form","prefix"),r.setAttribute("lspace","0.05em"),r.setAttribute("rspace","0.05em"),r}});const _t=["\\boxed","\\fcolorbox","\\colorbox"],Wt=(e,t)=>{const r=_t.includes(e.label)?"mrow":"menclose",n=new B(r,[he(e.body,t)]);switch(e.label){case"\\overline":n.setAttribute("notation","top"),n.classes.push("tml-overline");break;case"\\underline":n.setAttribute("notation","bottom"),n.classes.push("tml-underline");break;case"\\cancel":n.setAttribute("notation","updiagonalstrike"),n.children.push(new B("mrow",[],["tml-cancel","upstrike"]));break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike"),n.children.push(new B("mrow",[],["tml-cancel","downstrike"]));break;case"\\sout":n.setAttribute("notation","horizontalstrike"),n.children.push(new B("mrow",[],["tml-cancel","sout"]));break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike"),n.children.push(new B("mrow",[],["tml-cancel","tml-xcancel"]));break;case"\\longdiv":n.setAttribute("notation","longdiv"),n.classes.push("longdiv-top"),n.children.push(new B("mrow",[],["longdiv-arc"]));break;case"\\phase":n.setAttribute("notation","phasorangle"),n.classes.push("phasor-bottom"),n.children.push(new B("mrow",[],["phasor-angle"]));break;case"\\textcircled":n.setAttribute("notation","circle"),n.classes.push("circle-pad"),n.children.push(new B("mrow",[],["textcircle"]));break;case"\\angl":n.setAttribute("notation","actuarial"),n.classes.push("actuarial");break;case"\\boxed":n.style.padding="3pt",n.style.border="1px solid",n.setAttribute("scriptlevel","0"),n.setAttribute("displaystyle","true");break;case"\\fbox":n.setAttribute("notation","box"),n.classes.push("tml-fbox");break;case"\\fcolorbox":case"\\colorbox":n.style.padding="0.3em","\\fcolorbox"===e.label&&(n.style.border="0.0667em solid "+String(e.borderColor))}return e.backgroundColor&&n.setAttribute("mathbackground",e.backgroundColor),n};h({type:"enclose",names:["\\colorbox"],props:{numArgs:2,numOptionalArgs:1,allowedInText:!0,argTypes:["raw","raw","text"]},handler({parser:e,funcName:t},r,n){const s=n[0]&&Le(n[0],"raw").string;let o="";if(s){const e=Le(r[0],"raw").string;o=Ct(s,e)}else o=Nt(Le(r[0],"raw").string,e.gullet.macros);const a=r[1];return{type:"enclose",mode:e.mode,label:t,backgroundColor:o,body:a}},mathmlBuilder:Wt}),h({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,numOptionalArgs:1,allowedInText:!0,argTypes:["raw","raw","raw","text"]},handler({parser:e,funcName:t},r,n){const s=n[0]&&Le(n[0],"raw").string;let o,a="";if(s){const e=Le(r[0],"raw").string,t=Le(r[0],"raw").string;a=Ct(s,e),o=Ct(s,t)}else a=Nt(Le(r[0],"raw").string,e.gullet.macros),o=Nt(Le(r[1],"raw").string,e.gullet.macros);const l=r[2];return{type:"enclose",mode:e.mode,label:t,backgroundColor:o,borderColor:a,body:l}},mathmlBuilder:Wt}),h({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler:({parser:e},t)=>({type:"enclose",mode:e.mode,label:"\\fbox",body:t[0]})}),h({type:"enclose",names:["\\angl","\\cancel","\\bcancel","\\xcancel","\\sout","\\overline","\\boxed","\\longdiv","\\phase"],props:{numArgs:1},handler({parser:e,funcName:t},r){const n=r[0];return{type:"enclose",mode:e.mode,label:t,body:n}},mathmlBuilder:Wt}),h({type:"enclose",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler({parser:e,funcName:t},r){const n=r[0];return{type:"enclose",mode:e.mode,label:t,body:n}},mathmlBuilder:Wt}),h({type:"enclose",names:["\\textcircled"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler({parser:e,funcName:t},r){const n=r[0];return{type:"enclose",mode:e.mode,label:t,body:n}},mathmlBuilder:Wt}),h({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler({parser:t,funcName:r},n){const s=n[0];if("ordgroup"!==s.type)throw new e("Invalid environment name",s);let o="";for(let e=0;e({type:"envTag",mode:e.mode,body:t[0]}),mathmlBuilder:(e,t)=>new B("mrow")}),h({type:"noTag",names:["\\env@notag"],props:{numArgs:0},handler:({parser:e})=>({type:"noTag",mode:e.mode}),mathmlBuilder:(e,t)=>new B("mrow")});const Xt=(e,t)=>{const r=e.font,n=t.withFont(r),s=he(e.body,n);if(0===s.children.length)return s;if("boldsymbol"===r&&["mo","mpadded","mrow"].includes(s.type))return s.style.fontWeight="bold",s;if(((e,t)=>{if("mathrm"!==t||"ordgroup"!==e.body.type||1===e.body.body.length)return!1;if("mathord"!==e.body.body[0].type)return!1;for(let t=1;t{const n=f(r[0]);let s=t;return s in Zt&&(s=Zt[s]),{type:"font",mode:e.mode,font:s.slice(1),body:n}},mathmlBuilder:Xt}),h({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:({parser:e,funcName:t,breakOnTokenText:r},n)=>{const{mode:s}=e,o=e.parseExpression(!0,r,!0);return{type:"font",mode:s,font:`math${t.slice(1)}`,body:{type:"ordgroup",mode:e.mode,body:o}}},mathmlBuilder:Xt});const Yt=["display","text","script","scriptscript"],Jt={auto:-1,display:0,text:0,script:1,scriptscript:2},Kt=(e,t)=>{t=((e,t)=>{let r=t;if("display"===e){const e=r.level>=Ze?Xe:We;r=r.withLevel(e)}else"text"===e&&r.level===We?r=r.withLevel(Xe):"auto"===e?r=r.incrementLevel():"script"===e?r=r.withLevel(Ze):"scriptscript"===e&&(r=r.withLevel(Ye));return r})(e.scriptLevel,t);const r=he(e.numer,t),n=he(e.denom,t);3===t.level&&(r.style.mathDepth="2",r.setAttribute("scriptlevel","2"),n.style.mathDepth="2",n.setAttribute("scriptlevel","2"));let s=new B("mfrac",[r,n]);if(e.hasBarLine){if(e.barSize){const r=Oe(e.barSize,t);s.setAttribute("linethickness",r.number+r.unit)}}else s.setAttribute("linethickness","0px");if(null!=e.leftDelim||null!=e.rightDelim){const t=[];if(null!=e.leftDelim){const r=new B("mo",[new C(e.leftDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}if(t.push(s),null!=e.rightDelim){const r=new B("mo",[new C(e.rightDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}s=ie(t)}return"auto"!==e.scriptLevel&&(s=new B("mstyle",[s]),s.setAttribute("displaystyle",String("display"===e.scriptLevel)),s.setAttribute("scriptlevel",Jt[e.scriptLevel])),s};h({type:"genfrac",names:["\\cfrac","\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:({parser:e,funcName:t},r)=>{const n=r[0],s=r[1];let o=!1,a=null,l=null,i="auto";switch(t){case"\\cfrac":case"\\dfrac":case"\\frac":case"\\tfrac":o=!0;break;case"\\\\atopfrac":o=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":a="(",l=")";break;case"\\\\bracefrac":a="\\{",l="\\}";break;case"\\\\brackfrac":a="[",l="]";break;default:throw new Error("Unrecognized genfrac command")}return"\\cfrac"===t||t.startsWith("\\d")?i="display":t.startsWith("\\t")&&(i="text"),{type:"genfrac",mode:e.mode,continued:!1,numer:n,denom:s,hasBarLine:o,leftDelim:a,rightDelim:l,scriptLevel:i,barSize:null}},mathmlBuilder:Kt}),h({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler({parser:e,funcName:t,token:r}){let n;switch(t){case"\\over":n="\\frac";break;case"\\choose":n="\\binom";break;case"\\atop":n="\\\\atopfrac";break;case"\\brace":n="\\\\bracefrac";break;case"\\brack":n="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:e.mode,replaceWith:n,token:r}}});const Qt=function(e){let t=null;return e.length>0&&(t=e,t="."===t?null:t),t};h({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler({parser:e},t){const r=t[4],n=t[5],s=f(t[0]),o="atom"===s.type&&"open"===s.family?Qt(s.text):null,a=f(t[1]),l="atom"===a.type&&"close"===a.family?Qt(a.text):null,i=Le(t[2],"size");let c,m=null;i.isBlank?c=!0:(m=i.value,c=m.number>0);let p="auto",u=t[3];if("ordgroup"===u.type){if(u.body.length>0){const e=Le(u.body[0],"textord");p=Yt[Number(e.text)]}}else u=Le(u,"textord"),p=Yt[Number(u.text)];return{type:"genfrac",mode:e.mode,numer:r,denom:n,continued:!1,hasBarLine:c,barSize:m,leftDelim:o,rightDelim:l,scriptLevel:p}},mathmlBuilder:Kt}),h({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler:({parser:e,funcName:t,token:r},n)=>({type:"infix",mode:e.mode,replaceWith:"\\\\abovefrac",barSize:Le(n[0],"size").value,token:r})}),h({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:({parser:e,funcName:t},r)=>{const n=r[0],s=function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e}(Le(r[1],"infix").barSize),o=r[2],a=s.number>0;return{type:"genfrac",mode:e.mode,numer:n,denom:o,continued:!1,hasBarLine:a,barSize:s,leftDelim:null,rightDelim:null,scriptLevel:"auto"}},mathmlBuilder:Kt}),h({type:"hbox",names:["\\hbox"],props:{numArgs:1,argTypes:["hbox"],allowedInArgument:!0,allowedInText:!1},handler:({parser:e},t)=>({type:"hbox",mode:e.mode,body:b(t[0])}),mathmlBuilder(e,t){const r=t.withLevel(Xe),n=de(e.body,r);return le(n)}});h({type:"horizBracket",names:["\\overbrace","\\underbrace","\\overbracket","\\underbracket"],props:{numArgs:1},handler:({parser:e,funcName:t},r)=>({type:"horizBracket",mode:e.mode,label:t,isOver:/^\\over/.test(t),base:r[0]}),mathmlBuilder:(e,t)=>{const r=E(e.label);return r.style["math-depth"]=0,new B(e.isOver?"mover":"munder",[he(e.base,t),r])}}),h({type:"html",names:["\\class","\\id","\\style","\\data"],props:{numArgs:2,argTypes:["raw","original"],allowedInText:!0},handler:({parser:t,funcName:r,token:n},s)=>{const o=Le(s[0],"raw").string,a=s[1];if(t.settings.strict)throw new e(`Function "${r}" is disabled in strict mode`,n);let l;const i={};switch(r){case"\\class":i.class=o,l={command:"\\class",class:o};break;case"\\id":i.id=o,l={command:"\\id",id:o};break;case"\\style":i.style=o,l={command:"\\style",style:o};break;case"\\data":{const t=o.split(",");for(let r=0;r{const r=de(e.body,t),n=[];e.attributes.class&&n.push(...e.attributes.class.trim().split(/\s+/)),r.classes=n;for(const t in e.attributes)"class"!==t&&Object.prototype.hasOwnProperty.call(e.attributes,t)&&r.setAttribute(t,e.attributes[t]);return r}});const er=function(t){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(t))return{number:+t,unit:"bp"};{const r=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(t);if(!r)throw new e("Invalid size: '"+t+"' in \\includegraphics");const n={number:+(r[1]+r[2]),unit:r[3]};if(!Se(n))throw new e("Invalid unit: '"+n.unit+"' in \\includegraphics.");return n}};h({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:({parser:t,token:r},n,s)=>{let o={number:0,unit:"em"},a={number:.9,unit:"em"},l={number:0,unit:"em"},i="";if(s[0]){const t=Le(s[0],"raw").string.split(",");for(let r=0;r{const r=Oe(e.height,t),n={number:0,unit:"em"};e.totalheight.number>0&&e.totalheight.unit===r.unit&&e.totalheight.number>r.number&&(n.number=e.totalheight.number-r.number,n.unit=r.unit);let s=0;e.width.number>0&&(s=Oe(e.width,t));const o={height:r.number+n.number+"em"};s.number>0&&(o.width=s.number+s.unit),n.number>0&&(o.verticalAlign=-n.number+n.unit);const a=new q(e.src,e.alt,o);return a.height=r,a.depth=n,new B("mtext",[a])}}),h({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler({parser:t,funcName:r,token:n},s){const o=Le(s[0],"size");if(t.settings.strict){const s="m"===r[1],a="mu"===o.value.unit;if(s){if(!a)throw new e(`LaTeX's ${r} supports only mu units, not ${o.value.unit} units`,n);if("math"!==t.mode)throw new e(`LaTeX's ${r} works only in math mode`,n)}else if(a)throw new e(`LaTeX's ${r} doesn't support mu units`,n)}return{type:"kern",mode:t.mode,dimension:o.value}},mathmlBuilder(e,t){const r=Oe(e.dimension,t),n=r.number>0&&"em"===r.unit?tr(r.number):"";if("text"===e.mode&&n.length>0){const e=new C(n);return new B("mtext",[e])}if(r.number>=0){const e=new B("mspace");return e.setAttribute("width",r.number+r.unit),e}{const e=new B("mrow");return e.style.marginLeft=r.number+r.unit,e}}});const tr=function(e){return e>=.05555&&e<=.05556?" ":e>=.1666&&e<=.1667?" ":e>=.2222&&e<=.2223?" ":e>=.2777&&e<=.2778?"  ":""},rr=/[^A-Za-z_0-9-]/g;h({type:"label",names:["\\label"],props:{numArgs:1,argTypes:["raw"]},handler:({parser:e},t)=>({type:"label",mode:e.mode,string:t[0].string.replace(rr,"")}),mathmlBuilder(e,t){const r=new B("mrow",[],["tml-label"]);return e.string.length>0&&r.setLabel(e.string),r}});const nr=["\\clap","\\llap","\\rlap"];h({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap","\\clap","\\llap","\\rlap"],props:{numArgs:1,allowedInText:!0},handler:({parser:t,funcName:r,token:n},s)=>{if(nr.includes(r)){if(t.settings.strict&&"text"!==t.mode)throw new e(`{${r}} can be used only in text mode.\n Try \\math${r.slice(1)}`,n);r=r.slice(1)}else r=r.slice(5);const o=s[0];return{type:"lap",mode:t.mode,alignment:r,body:o}},mathmlBuilder:(e,t)=>{let r;if("llap"===e.alignment){const n=ue(b(e.body),t),s=new B("mphantom",n);r=new B("mpadded",[s]),r.setAttribute("width","0.1px")}const n=he(e.body,t);let s;if("llap"===e.alignment?(n.style.position="absolute",n.style.right="0",n.style.bottom="0",s=new B("mpadded",[r,n])):s=new B("mpadded",[n]),"rlap"===e.alignment)e.body.body.length>0&&"genfrac"===e.body.body[0].type&&s.setAttribute("lspace","0.16667em");else{const t="llap"===e.alignment?"-1":"-0.5";s.setAttribute("lspace",t+"width"),"llap"===e.alignment?s.style.position="relative":(s.style.display="flex",s.style.justifyContent="center")}return s.setAttribute("width","0.1px"),s}}),h({type:"ordgroup",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler({funcName:e,parser:t},r){const n=t.mode;t.switchMode("math");const s="\\("===e?"\\)":"$",o=t.parseExpression(!1,s);return t.expect(s),t.switchMode(n),{type:"ordgroup",mode:t.mode,body:o}}}),h({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(t,r){throw new e(`Mismatched ${t.funcName}`,r)}});h({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:({parser:e},t)=>({type:"mathchoice",mode:e.mode,display:b(t[0]),text:b(t[1]),script:b(t[2]),scriptscript:b(t[3])}),mathmlBuilder:(e,t)=>{const r=((e,t)=>{switch(t.level){case We:return e.display;case Xe:return e.text;case Ze:return e.script;case Ye:return e.scriptscript;default:return e.text}})(e,t);return de(r,t)}});const sr=["text","textord","mathord","atom"];function or(e,t){let r;const n=ue(e.body,t);if("minner"===e.mclass)r=new B("mpadded",n);else if("mord"===e.mclass)e.isCharacterBox||"mathord"===n[0].type?(r=n[0],r.type="mi",1===r.children.length&&r.children[0].text&&"∇"===r.children[0].text&&r.setAttribute("mathvariant","normal")):r=new B("mi",n);else{r=new B("mrow",n),e.mustPromote?(r=n[0],r.type="mo",e.isCharacterBox&&e.body[0].text&&/[A-Za-z]/.test(e.body[0].text)&&r.setAttribute("mathvariant","italic")):r=new B("mrow",n);const s=t.level<2;"mrow"===r.type?s&&("mbin"===e.mclass?(r.children.unshift(Be(.2222)),r.children.push(Be(.2222))):"mrel"===e.mclass?(r.children.unshift(Be(.2778)),r.children.push(Be(.2778))):"mpunct"===e.mclass?r.children.push(Be(.1667)):"minner"===e.mclass&&(r.children.unshift(Be(.0556)),r.children.push(Be(.0556)))):"mbin"===e.mclass?(r.attributes.lspace=s?"0.2222em":"0",r.attributes.rspace=s?"0.2222em":"0"):"mrel"===e.mclass?(r.attributes.lspace=s?"0.2778em":"0",r.attributes.rspace=s?"0.2778em":"0"):"mpunct"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace=s?"0.1667em":"0"):"mopen"===e.mclass||"mclose"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0em"):"minner"===e.mclass&&s&&(r.attributes.lspace="0.0556em",r.attributes.width="+0.1111em"),"mopen"!==e.mclass&&"mclose"!==e.mclass&&(delete r.attributes.stretchy,delete r.attributes.form)}return r}h({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler({parser:e,funcName:t},r){const n=r[0],s=i(n);let o=!0;const a={type:"mathord",text:"",mode:e.mode},l=n.body?n.body:[n];for(const t of l){if(!sr.includes(t.type)){o=!1;break}D[e.mode][t.text]?a.text+=D[e.mode][t.text].replace:t.text?a.text+=t.text:t.body&&t.body.map(e=>{a.text+=e.text})}return o&&"\\mathord"===t&&"mathord"===a.type&&a.text.length>1?a:{type:"mclass",mode:e.mode,mclass:"m"+t.slice(5),body:b(o?a:n),isCharacterBox:s,mustPromote:o}},mathmlBuilder:or});const ar=e=>{const t="ordgroup"===e.type&&e.body.length&&1===e.body.length?e.body[0]:e;if("atom"===t.type){const r=e.body.length>0&&e.body[0].text&&D.math[e.body[0].text]?D.math[e.body[0].text].group:t.family;return"bin"===r||"rel"===r?"m"+r:"mord"}return"mord"};h({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler:({parser:e},t)=>({type:"mclass",mode:e.mode,mclass:ar(t[0]),body:b(t[1]),isCharacterBox:i(t[1])})}),h({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler({parser:e,funcName:t},r){const n=r[1],s=r[0];let o;o="\\stackrel"!==t?ar(n):"mrel";const a={type:"mrel"===o||"mbin"===o?"op":"ordgroup",mode:n.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==t,body:b(n)};return{type:"supsub",mode:s.mode,stack:!0,base:a,sup:"\\underset"===t?null:s,sub:"\\underset"===t?s:null}},mathmlBuilder:or});const lr=(e,t,r)=>{if(!e)return r;const n=he(e,t);return"mrow"===n.type&&0===n.children.length?r:n};h({type:"multiscript",names:["\\sideset","\\pres@cript"],props:{numArgs:3},handler({parser:t,funcName:r,token:n},s){if(0===s[2].body.length)throw new e(r+"cannot parse an empty base.");const o=s[2].body[0];if(t.settings.strict&&"\\sideset"===r&&!o.symbol)throw new e("The base of \\sideset must be a big operator. Try \\prescript.");if(s[0].body.length>0&&"supsub"!==s[0].body[0].type||s[1].body.length>0&&"supsub"!==s[1].body[0].type)throw new e("\\sideset can parse only subscripts and superscripts in its first two arguments",n);const a=s[0].body.length>0?s[0].body[0]:null,l=s[1].body.length>0?s[1].body[0]:null;return a||l?a?{type:"multiscript",mode:t.mode,isSideset:"\\sideset"===r,prescripts:a,postscripts:l,base:o}:{type:"styling",mode:t.mode,scriptLevel:"text",body:[{type:"supsub",mode:t.mode,base:o,sup:l.sup,sub:l.sub}]}:o},mathmlBuilder(e,t){const r=he(e.base,t),n=new B("mprescripts"),s=new B("none");let o=[];const a=lr(e.prescripts.sub,t,s),l=lr(e.prescripts.sup,t,s);if(e.isSideset&&(a.setAttribute("style","text-align: left;"),l.setAttribute("style","text-align: left;")),e.postscripts){o=[r,lr(e.postscripts.sub,t,s),lr(e.postscripts.sup,t,s),n,a,l]}else o=[r,n,a,l];return new B("mmultiscripts",o)}}),h({type:"not",names:["\\not"],props:{numArgs:1,primitive:!0,allowedInText:!1},handler({parser:e},t){const r=i(t[0]);let n;if(r)n=b(t[0]),"\\"===n[0].text.charAt(0)&&(n[0].text=D.math[n[0].text].replace),n[0].text=n[0].text.slice(0,1)+"̸"+n[0].text.slice(1);else{n=[{type:"textord",mode:"math",text:"̸"},{type:"kern",mode:"math",dimension:{number:-.6,unit:"em"}},t[0]]}return{type:"not",mode:e.mode,body:n,isCharacterBox:r}},mathmlBuilder(e,t){if(e.isCharacterBox){return ue(e.body,t,!0)[0]}return de(e.body,t)}});const ir=["textord","mathord","atom"],cr=["\\smallint"],mr=["textord","mathord","ordgroup","close","leftright","font"],pr=e=>{e.attributes.lspace="0.1667em",e.attributes.rspace="0.1667em"},ur=(e,t)=>{let r;if(e.symbol)r=new B("mo",[oe(e.name,e.mode)]),cr.includes(e.name)?r.setAttribute("largeop","false"):r.setAttribute("movablelimits","false"),e.fromMathOp&&pr(r);else if(e.body)r=new B("mo",ue(e.body,t)),e.fromMathOp&&pr(r);else if(r=new B("mi",[new C(e.name.slice(1))]),!e.parentIsSupSub){const t=[r,new B("mo",[oe("⁡","text")])];if(e.needsLeadingSpace){const e=new B("mspace");e.setAttribute("width","0.1667em"),t.unshift(e)}if(!e.isFollowedByDelimiter){const e=new B("mspace");e.setAttribute("width","0.1667em"),t.push(e)}r=new B("mrow",t)}return r},dr={"∏":"\\prod","∐":"\\coprod","∑":"\\sum","⋀":"\\bigwedge","⋁":"\\bigvee","⋂":"\\bigcap","⋃":"\\bigcup","⨀":"\\bigodot","⨁":"\\bigoplus","⨂":"\\bigotimes","⨄":"\\biguplus","⨅":"\\bigsqcap","⨆":"\\bigsqcup","⨃":"\\bigcupdot","⨇":"\\bigdoublevee","⨈":"\\bigdoublewedge","⨉":"\\bigtimes"};h({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcupplus","\\bigcupdot","\\bigcap","\\bigcup","\\bigdoublevee","\\bigdoublewedge","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcap","\\bigsqcup","\\bigtimes","\\smallint","∏","∐","∑","⋀","⋁","⋂","⋃","⨀","⨁","⨂","⨃","⨄","⨅","⨆","⨇","⨈","⨉"],props:{numArgs:0},handler:({parser:e,funcName:t},r)=>{let n=t;return 1===n.length&&(n=dr[n]),{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!0,stack:!1,name:n}},mathmlBuilder:ur}),h({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:({parser:e},t)=>{const r=t[0],n=r.body?r.body:[r],s=1===n.length&&ir.includes(n[0].type);return{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:s,fromMathOp:!0,stack:!1,name:s?n[0].text:null,body:s?null:b(r)}},mathmlBuilder:ur});const hr={"∫":"\\int","∬":"\\iint","∭":"\\iiint","∮":"\\oint","∯":"\\oiint","∰":"\\oiiint","∱":"\\intclockwise","∲":"\\varointclockwise","⨌":"\\iiiint","⨍":"\\intbar","⨎":"\\intBar","⨏":"\\fint","⨒":"\\rppolint","⨓":"\\scpolint","⨕":"\\pointint","⨖":"\\sqint","⨗":"\\intlarhk","⨘":"\\intx","⨙":"\\intcap","⨚":"\\intcup"};h({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\sgn","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler({parser:e,funcName:t}){const r=e.prevAtomType,n=e.gullet.future().text;return{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!1,stack:!1,isFollowedByDelimiter:Pt(n),needsLeadingSpace:r.length>0&&mr.includes(r),name:t}},mathmlBuilder:ur}),h({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler({parser:e,funcName:t}){const r=e.prevAtomType,n=e.gullet.future().text;return{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!1,stack:!1,isFollowedByDelimiter:Pt(n),needsLeadingSpace:r.length>0&&mr.includes(r),name:t}},mathmlBuilder:ur}),h({type:"op",names:["\\int","\\iint","\\iiint","\\iiiint","\\oint","\\oiint","\\oiiint","\\intclockwise","\\varointclockwise","\\intbar","\\intBar","\\fint","\\rppolint","\\scpolint","\\pointint","\\sqint","\\intlarhk","\\intx","\\intcap","\\intcup","∫","∬","∭","∮","∯","∰","∱","∲","⨌","⨍","⨎","⨏","⨒","⨓","⨕","⨖","⨗","⨘","⨙","⨚"],props:{numArgs:0,allowedInArgument:!0},handler({parser:e,funcName:t}){let r=t;return 1===r.length&&(r=hr[r]),{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!0,stack:!1,name:r}},mathmlBuilder:ur});h({type:"operatorname",names:["\\operatorname@","\\operatornamewithlimits"],props:{numArgs:1,allowedInArgument:!0},handler:({parser:e,funcName:t},r)=>{const n=r[0],s=e.prevAtomType,o=e.gullet.future().text;return{type:"operatorname",mode:e.mode,body:b(n),alwaysHandleSupSub:"\\operatornamewithlimits"===t,limits:!1,parentIsSupSub:!1,isFollowedByDelimiter:Pt(o),needsLeadingSpace:s.length>0&&mr.includes(s)}},mathmlBuilder:(e,t)=>{let r,n=ue(e.body,t.withFont("mathrm")),s=!0;for(let e=0;ee.toText()).join("");n=[new C(e)]}else if(1===n.length&&["mover","munder"].includes(n[0].type)&&("mi"===n[0].children[0].type||"mtext"===n[0].children[0].type)){if(n[0].children[0].type="mi",e.parentIsSupSub)return new B("mrow",n);{const e=new B("mo",[oe("⁡","text")]);return O([n[0],e])}}if(s?(r=new B("mi",n),1===n[0].text.length&&r.setAttribute("mathvariant","normal")):r=new B("mrow",n),!e.parentIsSupSub){const t=[r,new B("mo",[oe("⁡","text")])];if(e.needsLeadingSpace){const e=new B("mspace");e.setAttribute("width","0.1667em"),t.unshift(e)}if(!e.isFollowedByDelimiter){const e=new B("mspace");e.setAttribute("width","0.1667em"),t.push(e)}return O(t)}return r}}),Ke("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@"),g({type:"ordgroup",mathmlBuilder:(e,t)=>de(e.body,t,e.semisimple)}),h({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:({parser:e},t)=>{const r=t[0];return{type:"phantom",mode:e.mode,body:b(r)}},mathmlBuilder:(e,t)=>{const r=ue(e.body,t);return new B("mphantom",r)}}),h({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:({parser:e},t)=>{const r=t[0];return{type:"hphantom",mode:e.mode,body:r}},mathmlBuilder:(e,t)=>{const r=ue(b(e.body),t),n=new B("mphantom",r),s=new B("mpadded",[n]);return s.setAttribute("height","0px"),s.setAttribute("depth","0px"),s}}),h({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:({parser:e},t)=>{const r=t[0];return{type:"vphantom",mode:e.mode,body:r}},mathmlBuilder:(e,t)=>{const r=ue(b(e.body),t),n=new B("mphantom",r),s=new B("mpadded",[n]);return s.setAttribute("width","0px"),s}}),h({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler:({parser:e},t)=>({type:"pmb",mode:e.mode,body:b(t[0])}),mathmlBuilder(e,t){const r=ue(e.body,t),n=N(r);return n.setAttribute("style","font-weight:bold"),n}});const gr=(e,t)=>{const r=t.withLevel(Xe),n=new B("mpadded",[he(e.body,r)]),s=Oe(e.dy,t);return n.setAttribute("voffset",s.number+s.unit),s.number>0?n.style.padding=s.number+s.unit+" 0 0 0":n.style.padding="0 0 "+Math.abs(s.number)+s.unit+" 0",n};h({type:"raise",names:["\\raise","\\lower"],props:{numArgs:2,argTypes:["size","primitive"],primitive:!0},handler({parser:e,funcName:t},r){const n=Le(r[0],"size").value;"\\lower"===t&&(n.number*=-1);const s=r[1];return{type:"raise",mode:e.mode,dy:n,body:s}},mathmlBuilder:gr}),h({type:"raise",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler({parser:e,funcName:t},r){const n=Le(r[0],"size").value,s=r[1];return{type:"raise",mode:e.mode,dy:n,body:s}},mathmlBuilder:gr}),h({type:"ref",names:["\\ref","\\eqref"],props:{numArgs:1,argTypes:["raw"]},handler:({parser:e,funcName:t},r)=>({type:"ref",mode:e.mode,funcName:t,string:r[0].string.replace(rr,"")}),mathmlBuilder(e,t){const r="\\ref"===e.funcName?["tml-ref"]:["tml-ref","tml-eqref"];return new S("#"+e.string,r,null)}}),h({type:"reflect",names:["\\reflectbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler:({parser:e},t)=>({type:"reflect",mode:e.mode,body:t[0]}),mathmlBuilder(e,t){const r=he(e.body,t);return r.style.transform="scaleX(-1)",r}}),h({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0,allowedInArgument:!0},handler:({parser:e})=>({type:"internal",mode:e.mode})}),h({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["size","size","size"]},handler({parser:e},t,r){const n=r[0],s=Le(t[0],"size"),o=Le(t[1],"size");return{type:"rule",mode:e.mode,shift:n&&Le(n,"size").value,width:s.value,height:o.value}},mathmlBuilder(e,t){const r=Oe(e.width,t),n=Oe(e.height,t),s=e.shift?Oe(e.shift,t):{number:0,unit:"em"},o=t.color&&t.getColor()||"black",a=new B("mspace");if(r.number>0&&n.number>0&&a.setAttribute("mathbackground",o),a.setAttribute("width",r.number+r.unit),a.setAttribute("height",n.number+n.unit),0===s.number)return a;const l=new B("mpadded",[a]);return s.number>=0?l.setAttribute("height","+"+s.number+s.unit):(l.setAttribute("height",s.number+s.unit),l.setAttribute("depth","+"+-s.number+s.unit)),l.setAttribute("voffset",s.number+s.unit),l}});const fr=/^[0-9]$/,br={0:"₀",1:"₁",2:"₂",3:"₃",4:"₄",5:"₅",6:"₆",7:"₇",8:"₈",9:"₉"},yr={0:"⁰",1:"¹",2:"²",3:"³",4:"⁴",5:"⁵",6:"⁶",7:"⁷",8:"⁸",9:"⁹"};h({type:"sfrac",names:["\\sfrac"],props:{numArgs:2,allowedInText:!0,allowedInMath:!0},handler({parser:t},r){let n="";for(const t of r[0].body){if("textord"!==t.type||!fr.test(t.text))throw new e("Numerator must be an integer.",t);n+=t.text}let s="";for(const t of r[1].body){if("textord"!==t.type||!fr.test(t.text))throw new e("Denominator must be an integer.",t);s+=t.text}return{type:"sfrac",mode:t.mode,numerator:n,denominator:s}},mathmlBuilder(e,t){const r=e.numerator.split("").map(e=>yr[e]).join(""),n=e.denominator.split("").map(e=>br[e]).join(""),s=new C(r+"⁄"+n,e.mode,t);return new B("mn",[s],["special-fraction"])}});const wr={"\\tiny":.5,"\\sixptsize":.6,"\\Tiny":.6,"\\scriptsize":.7,"\\footnotesize":.8,"\\small":.9,"\\normalsize":1,"\\large":1.2,"\\Large":1.44,"\\LARGE":1.728,"\\huge":2.074,"\\Huge":2.488};h({type:"sizing",names:["\\tiny","\\sixptsize","\\Tiny","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"],props:{numArgs:0,allowedInText:!0},handler:({breakOnTokenText:e,funcName:t,parser:r},n)=>{r.settings.strict&&"math"===r.mode&&console.log(`Temml strict-mode warning: Command ${t} is invalid in math mode.`);const s=r.parseExpression(!1,e,!0);return{type:"sizing",mode:r.mode,funcName:t,body:s}},mathmlBuilder:(e,t)=>{const r=t.withFontSize(wr[e.funcName]),n=ue(e.body,r),s=N(n),o=(wr[e.funcName]/t.fontSize).toFixed(4);return s.setAttribute("mathsize",o+"em"),s}}),h({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:({parser:e},t,r)=>{let n=!1,s=!1;const o=r[0]&&Le(r[0],"ordgroup");if(o){let e="";for(let t=0;t{const r=new B("mpadded",[he(e.body,t)]);return e.smashHeight&&r.setAttribute("height","0px"),e.smashDepth&&r.setAttribute("depth","0px"),r}});const xr=["a","c","e","ı","m","n","o","r","s","u","v","w","x","z","α","ε","ι","κ","ν","ο","π","σ","τ","υ","ω","\\alpha","\\epsilon","\\iota","\\kappa","\\nu","\\omega","\\pi","\\tau","\\omega"];h({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler({parser:e},t,r){const n=r[0],s=t[0];return s.body&&1===s.body.length&&s.body[0].text&&xr.includes(s.body[0].text)&&s.body.push({type:"rule",mode:"math",shift:null,width:{number:0,unit:"pt"},height:{number:.5,unit:"em"}}),{type:"sqrt",mode:e.mode,body:s,index:n}},mathmlBuilder(e,t){const{body:r,index:n}=e;return n?new B("mroot",[he(r,t),he(n,t.incrementLevel())]):new B("msqrt",[he(r,t)])}});const kr={display:0,text:1,script:2,scriptscript:3},vr={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]};h({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler({breakOnTokenText:e,funcName:t,parser:r},n){const s=r.parseExpression(!0,e,!0),o=t.slice(1,t.length-5);return{type:"styling",mode:r.mode,scriptLevel:o,body:s}},mathmlBuilder(e,t){const r=t.withLevel(kr[e.scriptLevel]),n=ue(e.body,r),s=N(n),o=vr[e.scriptLevel];return s.setAttribute("scriptlevel",o[0]),s.setAttribute("displaystyle",o[1]),s}});const Ar=/^m(over|under|underover)$/;g({type:"supsub",mathmlBuilder(e,t){let r,n,s=!1,o=!1,a=!1,l=!1;e.base&&"horizBracket"===e.base.type&&(n=!!e.sup,n===e.base.isOver&&(s=!0,r=e.base.isOver)),!e.base||e.stack||"op"!==e.base.type&&"operatorname"!==e.base.type||(e.base.parentIsSupSub=!0,o=!e.base.symbol,a=o&&!e.isFollowedByDelimiter,l=e.base.needsLeadingSpace);const i=e.stack&&1===e.base.body.length?[he(e.base.body[0],t)]:[he(e.base,t)],c=t.inSubOrSup();if(e.sub){const r=he(e.sub,c);3===t.level&&r.setAttribute("scriptlevel","2"),i.push(r)}if(e.sup){const r=he(e.sup,c);if(3===t.level&&r.setAttribute("scriptlevel","2"),e.base&&e.base.text&&1===e.base.text.length){const t=e.base.text;"DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ".indexOf(t)>-1?r.classes.push("tml-sml-pad"):"BCEFGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ".indexOf(t)>-1?r.classes.push("tml-med-pad"):"AJdfΔΛ".indexOf(t)>-1&&r.classes.push("tml-lrg-pad")}i.push(r)}let m;if(s)m=r?"mover":"munder";else if(e.sub)if(e.sup){const r=e.base;m=r&&("op"===r.type&&r.limits||"multiscript"===r.type)&&(t.level===We||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(t.level===We||r.limits)?"munderover":"msubsup"}else{const r=e.base;m=e.stack||r&&"op"===r.type&&r.limits&&(t.level===We||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.level===We)?"munder":"msub"}else{const r=e.base;m=r&&"op"===r.type&&r.limits&&(t.level===We||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.level===We)?"mover":"msup"}let p=new B(m,i);if(o){const e=new B("mo",[oe("⁡","text")]);if(l){const t=new B("mspace");t.setAttribute("width","0.1667em"),p=O([t,p,e])}else p=O([p,e]);if(a){const e=new B("mspace");e.setAttribute("width","0.1667em"),p.children.push(e)}}else Ar.test(m)&&(p=new B("mrow",[p]));return p}});const Tr=["\\shortmid","\\nshortmid","\\shortparallel","\\nshortparallel","\\smallsetminus"],Sr=["\\Rsh","\\Lsh","\\restriction"];g({type:"atom",mathmlBuilder(e,t){const r=new B("mo",[oe(e.text,e.mode)]);if("punct"===e.family)r.setAttribute("separator","true");else if("open"===e.family||"close"===e.family)"open"===e.family?(r.setAttribute("form","prefix"),r.setAttribute("stretchy","false")):"close"===e.family&&(r.setAttribute("form","postfix"),r.setAttribute("stretchy","false"));else if("\\mid"===e.text)r.setAttribute("lspace","0.22em"),r.setAttribute("rspace","0.22em"),r.setAttribute("stretchy","false");else if("rel"===e.family&&(e=>{if(1===e.length){const t=e.codePointAt(0);return 8591-1||e.indexOf("harpoon")>-1||Sr.includes(e)})(e.text))r.setAttribute("stretchy","false");else if(Tr.includes(e.text))r.setAttribute("mathsize","70%");else if(":"===e.text)r.attributes.lspace="0.2222em",r.attributes.rspace="0.2222em";else if(e.needsSpacing)return"bin"===e.family?new B("mrow",[Be(.222),r,Be(.222)]):new B("mrow",[Be(.2778),r,Be(.2778)]);return r}});const qr={mathbf:"bold",mathrm:"normal",textit:"italic",mathit:"italic",mathnormal:"italic",mathbb:"double-struck",mathcal:"script",mathfrak:"fraktur",mathscr:"script",mathsf:"sans-serif",mathtt:"monospace"},Or=function(e,t){if("texttt"===t.fontFamily)return"monospace";if("textsc"===t.fontFamily)return"normal";if("textsf"===t.fontFamily)return"textit"===t.fontShape&&"textbf"===t.fontWeight?"sans-serif-bold-italic":"textit"===t.fontShape?"sans-serif-italic":"textbf"===t.fontWeight?"sans-serif-bold":"sans-serif";if("textit"===t.fontShape&&"textbf"===t.fontWeight)return"bold-italic";if("textit"===t.fontShape)return"italic";if("textbf"===t.fontWeight)return"bold";const r=t.font;if(!r||"mathnormal"===r)return null;const n=e.mode;switch(r){case"mathit":case"greekItalic":return"italic";case"mathrm":{const t=e.text.codePointAt(0);return 9390,bold:e=>119743,italic:e=>119795,"bold-italic":e=>119847,script:e=>Br[e]||119899,"script-bold":e=>119951,fraktur:e=>Cr[e]||120003,"fraktur-bold":e=>120107,"double-struck":e=>Nr[e]||120055,"sans-serif":e=>120159,"sans-serif-bold":e=>120211,"sans-serif-italic":e=>120263,"sans-serif-bold-italic":e=>120380,monospace:e=>120367},lowerCaseLatin:{normal:e=>0,bold:e=>119737,italic:e=>"h"===e?8358:119789,"bold-italic":e=>119841,script:e=>Br[e]||119893,"script-bold":e=>119945,fraktur:e=>119997,"fraktur-bold":e=>120101,"double-struck":e=>120049,"sans-serif":e=>120153,"sans-serif-bold":e=>120205,"sans-serif-italic":e=>120257,"sans-serif-bold-italic":e=>120309,monospace:e=>120361},upperCaseGreek:{normal:e=>0,bold:e=>119575,italic:e=>119633,"bold-italic":e=>119575,script:e=>0,"script-bold":e=>0,fraktur:e=>0,"fraktur-bold":e=>0,"double-struck":e=>0,"sans-serif":e=>119749,"sans-serif-bold":e=>119749,"sans-serif-italic":e=>0,"sans-serif-bold-italic":e=>119807,monospace:e=>0},lowerCaseGreek:{normal:e=>0,bold:e=>119569,italic:e=>119627,"bold-italic":e=>"ϕ"===e?119678:119685,script:e=>0,"script-bold":e=>0,fraktur:e=>0,"fraktur-bold":e=>0,"double-struck":e=>0,"sans-serif":e=>119743,"sans-serif-bold":e=>119743,"sans-serif-italic":e=>0,"sans-serif-bold-italic":e=>119801,monospace:e=>0},varGreek:{normal:e=>0,bold:e=>zr[e]||-51,italic:e=>0,"bold-italic":e=>$r[e]||58,script:e=>0,"script-bold":e=>0,fraktur:e=>0,"fraktur-bold":e=>0,"double-struck":e=>0,"sans-serif":e=>Er[e]||116,"sans-serif-bold":e=>Er[e]||116,"sans-serif-italic":e=>0,"sans-serif-bold-italic":e=>Ir[e]||174,monospace:e=>0},numeral:{normal:e=>0,bold:e=>120734,italic:e=>0,"bold-italic":e=>0,script:e=>0,"script-bold":e=>0,fraktur:e=>0,"fraktur-bold":e=>0,"double-struck":e=>120744,"sans-serif":e=>120754,"sans-serif-bold":e=>120764,"sans-serif-italic":e=>0,"sans-serif-bold-italic":e=>0,monospace:e=>120774}}),Fr=(e,t)=>{const r=e.codePointAt(0),n=64{const n=new B(r,[e]),s=new B("mstyle",[n]);return s.style["font-style"]="italic",s.style["font-family"]="Cambria, 'Times New Roman', serif","bold-italic"===t&&(s.style["font-weight"]="bold"),s})(s,o,t);"normal"!==o&&(s.text=s.text.split("").map(e=>Fr(e,o)).join("")),a=new B(t,[s])}else if("text"===e.mode)"normal"!==o&&(s.text=Fr(s.text,o)),a=new B("mtext",[s]);else if(Pr.has(e.text))a=new B("mo",[s]),a.classes.push("tml-prime");else{const e=s.text;"italic"!==o&&(s.text=Fr(s.text,o)),a=new B("mi",[s]),s.text===e&&Gr.test(e)&&a.setAttribute("mathvariant","italic")}return a}});const jr={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},Rr={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};g({type:"spacing",mathmlBuilder(t,r){let n;if(Object.prototype.hasOwnProperty.call(Rr,t.text))n=new B("mtext",[new C(" ")]);else{if(!Object.prototype.hasOwnProperty.call(jr,t.text))throw new e(`Unknown type of space "${t.text}"`);n=new B("mo"),"\\nobreak"===t.text&&n.setAttribute("linebreak","nobreak")}return n}}),g({type:"tag"});const Ur={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm","\\textsc":"textsc"},Hr={"\\textbf":"textbf","\\textmd":"textmd"},Vr={"\\textit":"textit","\\textup":"textup"};h({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textsc","\\textbf","\\textmd","\\textit","\\textup","\\emph"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler({parser:e,funcName:t},r){const n=r[0];return{type:"text",mode:e.mode,body:b(n),font:t}},mathmlBuilder(e,t){const r=((e,t)=>{const r=e.font;return r?Ur[r]?t.withTextFontFamily(Ur[r]):Hr[r]?t.withTextFontWeight(Hr[r]):"\\emph"===r?"textit"===t.fontShape?t.withTextFontShape("textup"):t.withTextFontShape("textit"):t.withTextFontShape(Vr[r]):t})(e,t),n=de(e.body,r);return le(n)}}),h({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler:({parser:e},t)=>({type:"vcenter",mode:e.mode,body:t[0]}),mathmlBuilder(e,t){const r=new B("mtd",[he(e.body,t)]);r.style.padding="0";const n=new B("mtr",[r]);return new B("mtable",[n])}}),h({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(t,r,n){throw new e("\\verb ended by end of line instead of matching delimiter")},mathmlBuilder(e,t){const r=new C(_r(e)),n=new B("mtext",[r]);return n.setAttribute("mathvariant","monospace"),n}});const _r=e=>e.body.replace(/ /g,e.star?"␣":" "),Wr=u,Xr="[ \r\n\t]",Zr=`(\\\\[a-zA-Z@]+)${Xr}*`,Yr="[̀-ͯ]",Jr=new RegExp(`${Yr}+$`),Kr=`(${Xr}+)|\\\\(\n|[ \r\t]+\n?)[ \r\t]*|([!-\\[\\]-‧‪-퟿豈-￿]${Yr}*|[\ud800-\udbff][\udc00-\udfff]${Yr}*|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5|${Zr}|\\\\[^\ud800-\udfff])`;class Qr{constructor(e,t){this.input=e,this.settings=t,this.tokenRegex=new RegExp(Kr,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,t){this.catcodes[e]=t}lex(){const t=this.input,r=this.tokenRegex.lastIndex;if(r===t.length)return new _e("EOF",new Ve(this,r,r));const n=this.tokenRegex.exec(t);if(null===n||n.index!==r)throw new e(`Unexpected character: '${t[r]}'`,new _e(t[r],new Ve(this,r,r+1)));const s=n[6]||n[3]||(n[2]?"\\ ":" ");if(14===this.catcodes[s]){const r=t.indexOf("\n",this.tokenRegex.lastIndex);if(-1===r){if(this.tokenRegex.lastIndex=t.length,this.settings.strict)throw new e("% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode")}else this.tokenRegex.lastIndex=r+1;return this.lex()}return new _e(s,new Ve(this,r,this.tokenRegex.lastIndex))}}class en{constructor(e={},t={}){this.current=t,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(0===this.undefStack.length)throw new e("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");const t=this.undefStack.pop();for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(void 0===t[e]?delete this.current[e]:this.current[e]=t[e])}has(e){return Object.prototype.hasOwnProperty.call(this.current,e)||Object.prototype.hasOwnProperty.call(this.builtins,e)}get(e){return Object.prototype.hasOwnProperty.call(this.current,e)?this.current[e]:this.builtins[e]}set(e,t,r=!1){if(r){for(let t=0;t0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{const t=this.undefStack[this.undefStack.length-1];t&&!Object.prototype.hasOwnProperty.call(t,e)&&(t[e]=this.current[e])}this.current[e]=t}}const tn={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0};class rn{constructor(e,t,r){this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new en(Qe,t.macros),this.mode=r,this.stack=[]}feed(e){this.lexer=new Qr(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}future(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){let t,r,n;if(e){if(this.consumeSpaces(),"["!==this.future().text)return null;t=this.popToken(),({tokens:n,end:r}=this.consumeArg(["]"]))}else({tokens:n,start:t,end:r}=this.consumeArg());return this.pushToken(new _e("EOF",r.loc)),this.pushTokens(n),t.range(r,"")}consumeSpaces(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}}consumeArg(t){const r=[],n=t&&t.length>0;n||this.consumeSpaces();const s=this.future();let o,a=0,l=0;do{if(o=this.popToken(),r.push(o),"{"===o.text)++a;else if("}"===o.text){if(--a,-1===a)throw new e("Extra }",o)}else if("EOF"===o.text)throw new e("Unexpected end of input in a macro argument, expected '"+(t&&n?t[l]:"}")+"'",o);if(t&&n)if((0===a||1===a&&"{"===t[l])&&o.text===t[l]){if(++l,l===t.length){r.splice(-l,l);break}}else l=0}while(0!==a||n);return"{"===s.text&&"}"===r[r.length-1].text&&(r.pop(),r.shift()),r.reverse(),{tokens:r,start:s,end:o}}consumeArgs(t,r){if(r){if(r.length!==t+1)throw new e("The length of delimiters doesn't match the number of args!");const n=r[0];for(let t=0;tthis.settings.maxExpand)throw new e("Too many expansions: infinite loop or need to increase maxExpand setting");let o=s.tokens;const a=this.consumeArgs(s.numArgs,s.delimiters);if(s.numArgs){o=o.slice();for(let t=o.length-1;t>=0;--t){let r=o[t];if("#"===r.text){if(0===t)throw new e("Incomplete placeholder at end of macro body",r);if(r=o[--t],"#"===r.text)o.splice(t+1,1);else{if(!/^[1-9]$/.test(r.text))throw new e("Not a valid argument number",r);o.splice(t,2,...a[+r.text-1])}}}}return this.pushTokens(o),o.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(!1===this.expandOnce()){const e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}throw new Error}expandMacro(e){return this.macros.has(e)?this.expandTokens([new _e(e)]):void 0}expandTokens(e){const t=[],r=this.stack.length;for(this.pushTokens(e);this.stack.length>r;)if(!1===this.expandOnce(!0)){const e=this.stack.pop();e.treatAsRelax&&(e.noexpand=!1,e.treatAsRelax=!1),t.push(e)}return t}expandMacroAsText(e){const t=this.expandMacro(e);return t?t.map(e=>e.text).join(""):t}_getExpansion(e){const t=this.macros.get(e);if(null==t)return t;if(1===e.length){const t=this.lexer.catcodes[e];if(null!=t&&13!==t)return}const r="function"==typeof t?t(this):t;if("string"==typeof r){let e=0;if(-1!==r.indexOf("#")){const t=r.replace(/##/g,"");for(;-1!==t.indexOf("#"+(e+1));)++e}const t=new Qr(r,this.settings),n=[];let s=t.lex();for(;"EOF"!==s.text;)n.push(s),s=t.lex();n.reverse();return{tokens:n,numArgs:e}}return r}isDefined(e){return this.macros.has(e)||Object.prototype.hasOwnProperty.call(Wr,e)||Object.prototype.hasOwnProperty.call(D.math,e)||Object.prototype.hasOwnProperty.call(D.text,e)||Object.prototype.hasOwnProperty.call(tn,e)}isExpandable(e){const t=this.macros.get(e);return null!=t?"string"==typeof t||"function"==typeof t||!t.unexpandable:Object.prototype.hasOwnProperty.call(Wr,e)&&!Wr[e].primitive}}const nn=/^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/,sn=Object.freeze({"₊":"+","₋":"-","₌":"=","₍":"(","₎":")","₀":"0","₁":"1","₂":"2","₃":"3","₄":"4","₅":"5","₆":"6","₇":"7","₈":"8","₉":"9","ₐ":"a","ₑ":"e","ₕ":"h","ᵢ":"i","ⱼ":"j","ₖ":"k","ₗ":"l","ₘ":"m","ₙ":"n","ₒ":"o","ₚ":"p","ᵣ":"r","ₛ":"s","ₜ":"t","ᵤ":"u","ᵥ":"v","ₓ":"x","ᵦ":"β","ᵧ":"γ","ᵨ":"ρ","ᵩ":"ϕ","ᵪ":"χ","⁺":"+","⁻":"-","⁼":"=","⁽":"(","⁾":")","⁰":"0","¹":"1","²":"2","³":"3","⁴":"4","⁵":"5","⁶":"6","⁷":"7","⁸":"8","⁹":"9","ᴬ":"A","ᴮ":"B","ᴰ":"D","ᴱ":"E","ᴳ":"G","ᴴ":"H","ᴵ":"I","ᴶ":"J","ᴷ":"K","ᴸ":"L","ᴹ":"M","ᴺ":"N","ᴼ":"O","ᴾ":"P","ᴿ":"R","ᵀ":"T","ᵁ":"U","ⱽ":"V","ᵂ":"W","ᵃ":"a","ᵇ":"b","ᶜ":"c","ᵈ":"d","ᵉ":"e","ᶠ":"f","ᵍ":"g","ʰ":"h","ⁱ":"i","ʲ":"j","ᵏ":"k","ˡ":"l","ᵐ":"m","ⁿ":"n","ᵒ":"o","ᵖ":"p","ʳ":"r","ˢ":"s","ᵗ":"t","ᵘ":"u","ᵛ":"v","ʷ":"w","ˣ":"x","ʸ":"y","ᶻ":"z","ᵝ":"β","ᵞ":"γ","ᵟ":"δ","ᵠ":"ϕ","ᵡ":"χ","ᶿ":"θ"}),on=Object.freeze({"𝒜":"A","ℬ":"B","𝒞":"C","𝒟":"D","ℰ":"E","ℱ":"F","𝒢":"G","ℋ":"H","ℐ":"I","𝒥":"J","𝒦":"K","ℒ":"L","ℳ":"M","𝒩":"N","𝒪":"O","𝒫":"P","𝒬":"Q","ℛ":"R","𝒮":"S","𝒯":"T","𝒰":"U","𝒱":"V","𝒲":"W","𝒳":"X","𝒴":"Y","𝒵":"Z"});var an={"́":{text:"\\'",math:"\\acute"},"̀":{text:"\\`",math:"\\grave"},"̈":{text:'\\"',math:"\\ddot"},"̃":{text:"\\~",math:"\\tilde"},"̄":{text:"\\=",math:"\\bar"},"̆":{text:"\\u",math:"\\breve"},"̌":{text:"\\v",math:"\\check"},"̂":{text:"\\^",math:"\\hat"},"̇":{text:"\\.",math:"\\dot"},"̊":{text:"\\r",math:"\\mathring"},"̋":{text:"\\H"},"̧":{text:"\\c"}},ln={"á":"á","à":"à","ä":"ä","ǟ":"ǟ","ã":"ã","ā":"ā","ă":"ă","ắ":"ắ","ằ":"ằ","ẵ":"ẵ","ǎ":"ǎ","â":"â","ấ":"ấ","ầ":"ầ","ẫ":"ẫ","ȧ":"ȧ","ǡ":"ǡ","å":"å","ǻ":"ǻ","ḃ":"ḃ","ć":"ć","č":"č","ĉ":"ĉ","ċ":"ċ","ď":"ď","ḋ":"ḋ","é":"é","è":"è","ë":"ë","ẽ":"ẽ","ē":"ē","ḗ":"ḗ","ḕ":"ḕ","ĕ":"ĕ","ě":"ě","ê":"ê","ế":"ế","ề":"ề","ễ":"ễ","ė":"ė","ḟ":"ḟ","ǵ":"ǵ","ḡ":"ḡ","ğ":"ğ","ǧ":"ǧ","ĝ":"ĝ","ġ":"ġ","ḧ":"ḧ","ȟ":"ȟ","ĥ":"ĥ","ḣ":"ḣ","í":"í","ì":"ì","ï":"ï","ḯ":"ḯ","ĩ":"ĩ","ī":"ī","ĭ":"ĭ","ǐ":"ǐ","î":"î","ǰ":"ǰ","ĵ":"ĵ","ḱ":"ḱ","ǩ":"ǩ","ĺ":"ĺ","ľ":"ľ","ḿ":"ḿ","ṁ":"ṁ","ń":"ń","ǹ":"ǹ","ñ":"ñ","ň":"ň","ṅ":"ṅ","ó":"ó","ò":"ò","ö":"ö","ȫ":"ȫ","õ":"õ","ṍ":"ṍ","ṏ":"ṏ","ȭ":"ȭ","ō":"ō","ṓ":"ṓ","ṑ":"ṑ","ŏ":"ŏ","ǒ":"ǒ","ô":"ô","ố":"ố","ồ":"ồ","ỗ":"ỗ","ȯ":"ȯ","ȱ":"ȱ","ő":"ő","ṕ":"ṕ","ṗ":"ṗ","ŕ":"ŕ","ř":"ř","ṙ":"ṙ","ś":"ś","ṥ":"ṥ","š":"š","ṧ":"ṧ","ŝ":"ŝ","ṡ":"ṡ","ẗ":"ẗ","ť":"ť","ṫ":"ṫ","ú":"ú","ù":"ù","ü":"ü","ǘ":"ǘ","ǜ":"ǜ","ǖ":"ǖ","ǚ":"ǚ","ũ":"ũ","ṹ":"ṹ","ū":"ū","ṻ":"ṻ","ŭ":"ŭ","ǔ":"ǔ","û":"û","ů":"ů","ű":"ű","ṽ":"ṽ","ẃ":"ẃ","ẁ":"ẁ","ẅ":"ẅ","ŵ":"ŵ","ẇ":"ẇ","ẘ":"ẘ","ẍ":"ẍ","ẋ":"ẋ","ý":"ý","ỳ":"ỳ","ÿ":"ÿ","ỹ":"ỹ","ȳ":"ȳ","ŷ":"ŷ","ẏ":"ẏ","ẙ":"ẙ","ź":"ź","ž":"ž","ẑ":"ẑ","ż":"ż","Á":"Á","À":"À","Ä":"Ä","Ǟ":"Ǟ","Ã":"Ã","Ā":"Ā","Ă":"Ă","Ắ":"Ắ","Ằ":"Ằ","Ẵ":"Ẵ","Ǎ":"Ǎ","Â":"Â","Ấ":"Ấ","Ầ":"Ầ","Ẫ":"Ẫ","Ȧ":"Ȧ","Ǡ":"Ǡ","Å":"Å","Ǻ":"Ǻ","Ḃ":"Ḃ","Ć":"Ć","Č":"Č","Ĉ":"Ĉ","Ċ":"Ċ","Ď":"Ď","Ḋ":"Ḋ","É":"É","È":"È","Ë":"Ë","Ẽ":"Ẽ","Ē":"Ē","Ḗ":"Ḗ","Ḕ":"Ḕ","Ĕ":"Ĕ","Ě":"Ě","Ê":"Ê","Ế":"Ế","Ề":"Ề","Ễ":"Ễ","Ė":"Ė","Ḟ":"Ḟ","Ǵ":"Ǵ","Ḡ":"Ḡ","Ğ":"Ğ","Ǧ":"Ǧ","Ĝ":"Ĝ","Ġ":"Ġ","Ḧ":"Ḧ","Ȟ":"Ȟ","Ĥ":"Ĥ","Ḣ":"Ḣ","Í":"Í","Ì":"Ì","Ï":"Ï","Ḯ":"Ḯ","Ĩ":"Ĩ","Ī":"Ī","Ĭ":"Ĭ","Ǐ":"Ǐ","Î":"Î","İ":"İ","Ĵ":"Ĵ","Ḱ":"Ḱ","Ǩ":"Ǩ","Ĺ":"Ĺ","Ľ":"Ľ","Ḿ":"Ḿ","Ṁ":"Ṁ","Ń":"Ń","Ǹ":"Ǹ","Ñ":"Ñ","Ň":"Ň","Ṅ":"Ṅ","Ó":"Ó","Ò":"Ò","Ö":"Ö","Ȫ":"Ȫ","Õ":"Õ","Ṍ":"Ṍ","Ṏ":"Ṏ","Ȭ":"Ȭ","Ō":"Ō","Ṓ":"Ṓ","Ṑ":"Ṑ","Ŏ":"Ŏ","Ǒ":"Ǒ","Ô":"Ô","Ố":"Ố","Ồ":"Ồ","Ỗ":"Ỗ","Ȯ":"Ȯ","Ȱ":"Ȱ","Ő":"Ő","Ṕ":"Ṕ","Ṗ":"Ṗ","Ŕ":"Ŕ","Ř":"Ř","Ṙ":"Ṙ","Ś":"Ś","Ṥ":"Ṥ","Š":"Š","Ṧ":"Ṧ","Ŝ":"Ŝ","Ṡ":"Ṡ","Ť":"Ť","Ṫ":"Ṫ","Ú":"Ú","Ù":"Ù","Ü":"Ü","Ǘ":"Ǘ","Ǜ":"Ǜ","Ǖ":"Ǖ","Ǚ":"Ǚ","Ũ":"Ũ","Ṹ":"Ṹ","Ū":"Ū","Ṻ":"Ṻ","Ŭ":"Ŭ","Ǔ":"Ǔ","Û":"Û","Ů":"Ů","Ű":"Ű","Ṽ":"Ṽ","Ẃ":"Ẃ","Ẁ":"Ẁ","Ẅ":"Ẅ","Ŵ":"Ŵ","Ẇ":"Ẇ","Ẍ":"Ẍ","Ẋ":"Ẋ","Ý":"Ý","Ỳ":"Ỳ","Ÿ":"Ÿ","Ỹ":"Ỹ","Ȳ":"Ȳ","Ŷ":"Ŷ","Ẏ":"Ẏ","Ź":"Ź","Ž":"Ž","Ẑ":"Ẑ","Ż":"Ż","ά":"ά","ὰ":"ὰ","ᾱ":"ᾱ","ᾰ":"ᾰ","έ":"έ","ὲ":"ὲ","ή":"ή","ὴ":"ὴ","ί":"ί","ὶ":"ὶ","ϊ":"ϊ","ΐ":"ΐ","ῒ":"ῒ","ῑ":"ῑ","ῐ":"ῐ","ό":"ό","ὸ":"ὸ","ύ":"ύ","ὺ":"ὺ","ϋ":"ϋ","ΰ":"ΰ","ῢ":"ῢ","ῡ":"ῡ","ῠ":"ῠ","ώ":"ώ","ὼ":"ὼ","Ύ":"Ύ","Ὺ":"Ὺ","Ϋ":"Ϋ","Ῡ":"Ῡ","Ῠ":"Ῠ","Ώ":"Ώ","Ὼ":"Ὼ"};const cn=["bin","op","open","punct","rel"],mn=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/,pn=/^ *\\text/;class un{constructor(e,t,r=!1){this.mode="math",this.gullet=new rn(e,t,this.mode),this.settings=t,this.isPreamble=r,this.leftrightDepth=0,this.prevAtomType=""}expect(t,r=!0){if(this.fetch().text!==t)throw new e(`Expected '${t}', got '${this.fetch().text}'`,this.fetch());r&&this.consume()}consume(){this.nextToken=null}fetch(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");const e=this.parseExpression(!1);if(this.expect("EOF"),this.isPreamble){const e=Object.create(null);return Object.entries(this.gullet.macros.current).forEach(([t,r])=>{e[t]=r}),this.gullet.endGroup(),e}const t=this.gullet.macros.get("\\df@tag");return this.gullet.endGroup(),t&&(this.gullet.macros.current["\\df@tag"]=t),e}static get endOfExpression(){return["}","\\endgroup","\\end","\\right","\\endtoggle","&"]}subparse(e){const t=this.nextToken;this.consume(),this.gullet.pushToken(new _e("}")),this.gullet.pushTokens(e);const r=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,r}parseExpression(e,t,r){const n=[];for(this.prevAtomType="";;){"math"===this.mode&&this.consumeSpaces();const s=this.fetch();if(-1!==un.endOfExpression.indexOf(s.text))break;if(t&&s.text===t)break;if(r&&"\\middle"===s.text)break;if(e&&Wr[s.text]&&Wr[s.text].infix)break;const o=this.parseAtom(t);if(!o)break;"internal"!==o.type&&(n.push(o),this.prevAtomType="atom"===o.type?o.family:o.type)}return"text"===this.mode&&this.formLigatures(n),this.handleInfixNodes(n)}handleInfixNodes(t){let r,n=-1;for(let s=0;s=128||Jr.exec(r)))return null;if(this.settings.strict&&"math"===this.mode)throw new e(`Unicode text character "${r[0]}" used in math mode`,t);s={type:"textord",mode:"text",loc:Ve.range(t),text:r}}if(this.consume(),n)for(let r=0;r0&&n[0].type&&"array"===n[0].type&&n[0].addEqnNum)&&s.gullet.macros.get("\\df@tag")){if(!r.displayMode)throw new e("\\tag works only in display mode");s.gullet.feed("\\df@tag"),n=[{type:"tag",mode:"text",body:n,tag:s.parse()}]}return n},hn=[2,2,3,3];class gn{constructor(e){this.level=e.level,this.color=e.color,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontSize=e.fontSize||1,this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.maxSize=e.maxSize}extend(e){const t={level:this.level,color:this.color,font:this.font,fontFamily:this.fontFamily,fontSize:this.fontSize,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return new gn(t)}withLevel(e){return this.extend({level:e})}incrementLevel(){return this.extend({level:Math.min(this.level+1,3)})}inSubOrSup(){return this.extend({level:hn[this.level]})}withColor(e){return this.extend({color:e})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withFontSize(e){return this.extend({fontSize:e})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}getColor(){return this.color}}function fn(e){const t={};let r=0;const n=document.getElementsByClassName("tml-eqn");for(let e of n)for(r+=1,e.setAttribute("id","tml-eqn-"+String(r));"mtable"!==e.tagName;){if(e.getElementsByClassName("tml-label").length>0){const n=e.attributes.id.value;t[n]=String(r);break}e=e.parentElement}const s=document.getElementsByClassName("tml-tageqn");for(const e of s){if(e.getElementsByClassName("tml-label").length>0){const r=e.getElementsByClassName("tml-tag");if(r.length>0){const n=e.attributes.id.value;t[n]=r[0].textContent}}}[...e.getElementsByClassName("tml-ref")].forEach(e=>{const r=e.getAttribute("href");let n=t[r.slice(1)];-1===e.className.indexOf("tml-eqref")?(n=n.replace(/^\(/,""),n=n.replace(/\)$/,"")):("("!==n.charAt(0)&&(n="("+n),")"!==n.slice(-1)&&(n+=")"));const s=document.createElementNS("http://www.w3.org/1998/Math/MathML","mtext");s.appendChild(document.createTextNode(n));const o=document.createElementNS("http://www.w3.org/1998/Math/MathML","math");o.appendChild(s),e.textContent="",e.appendChild(o)})}const bn=function(e,t,r){let n=r,s=0;const o=e.length;for(;ne.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")).join("|")+")");for(;r=e.search(s),-1!==r;){r>0&&(n.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const s=t.findIndex(t=>e.startsWith(t.left));if(r=bn(t[s].right,e,t[s].left.length),-1===r)break;const o=e.slice(0,r+t[s].right.length),a=yn.test(o)?o:e.slice(t[s].left.length,r);n.push({type:"math",data:a,rawData:o,display:t[s].display}),e=e.slice(r+t[s].right.length)}return""!==e&&n.push({type:"text",data:e}),n}(t,r.delimiters);if(1===n.length&&"text"===n[0].type)return null;const s=document.createDocumentFragment();for(let t=0;t-1===e.indexOf(" "+t+" "))&&An(n,t)}}};let Tn=function(e,t,r={}){t.textContent="";const n="math"===t.tagName.toLowerCase();n&&(r.wrap="none");const s=Sn(e,r);n||s.children.length>1?(t.textContent="",s.children.forEach(e=>{t.appendChild(e.toNode())})):t.appendChild(s.toNode())};"undefined"!=typeof document&&"CSS1Compat"!==document.compatMode&&("undefined"!=typeof console&&console.warn("Warning: Temml doesn't work in quirks mode. Make sure your website has a suitable doctype."),Tn=function(){throw new e("Temml doesn't work in quirks mode.")});const Sn=function(t,r){const n=new p(r);try{const e=dn(t,n);return ye(e,t,new gn({level:n.displayMode?We:Xe,maxSize:n.maxSize}),n)}catch(r){return function(t,r,n){if(n.throwOnError||!(t instanceof e))throw t;const s=new A(["temml-error"],[new T(r+"\n\n"+t.toString())]);return s.style.color=n.errorColor,s.style.whiteSpace="pre-line",s}(r,t,n)}};return{version:"0.13.02",render:Tn,renderToString:function(e,t){return Sn(e,t).toMarkup()},renderMathInElement:function(e,t){if(!e)throw new Error("No element provided to render");const r={};for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(r[e]=t[e]);r.fences?r.delimiters=(e=>{if("$"===e||"("===e)return xn[e];if("$+"===e||"(+"===e)return xn[e.slice(0,1)].concat(kn);return"ams"===e?kn:"all"===e?xn["("].concat(xn.$).concat(kn):wn})(r.fences):r.delimiters=r.delimiters||wn,r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code","option"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},An(e,r),fn(e)},postProcess:fn,ParseError:e,definePreamble:function(e,t){const r=new p(t);if(r.macros={},!("string"==typeof e||e instanceof String))throw new TypeError("Temml can only parse string typed expression");const n=new un(e,r,!0);delete n.gullet.macros.current["\\df@tag"];return n.parse()},__parse:function(e,t){const r=new p(t);return dn(e,r)},__renderToMathMLTree:Sn,__defineSymbol:G,__defineMacro:Ke}}(); \ No newline at end of file diff --git a/dist/temml.mjs b/dist/temml.mjs deleted file mode 100644 index f20b6e83..00000000 --- a/dist/temml.mjs +++ /dev/null @@ -1,14683 +0,0 @@ -/** - * This is the ParseError class, which is the main error thrown by Temml - * functions when something has gone wrong. This is used to distinguish internal - * errors from errors in the expression that the user provided. - * - * If possible, a caller should provide a Token or ParseNode with information - * about where in the source string the problem occurred. - */ -class ParseError { - constructor( - message, // The error message - token // An object providing position information - ) { - let error = " " + message; - let start; - - const loc = token && token.loc; - if (loc && loc.start <= loc.end) { - // If we have the input and a position, make the error a bit fancier - - // Get the input - const input = loc.lexer.input; - - // Prepend some information - start = loc.start; - const end = loc.end; - if (start === input.length) { - error += " at end of input: "; - } else { - error += " at position " + (start + 1) + ": \n"; - } - - // Underline token in question using combining underscores - const underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332"); - - // Extract some context from the input and add it to the error - let left; - if (start > 15) { - left = "…" + input.slice(start - 15, start); - } else { - left = input.slice(0, start); - } - let right; - if (end + 15 < input.length) { - right = input.slice(end, end + 15) + "…"; - } else { - right = input.slice(end); - } - error += left + underlined + right; - } - - // Some hackery to make ParseError a prototype of Error - // See http://stackoverflow.com/a/8460753 - const self = new Error(error); - self.name = "ParseError"; - self.__proto__ = ParseError.prototype; - self.position = start; - return self; - } -} - -ParseError.prototype.__proto__ = Error.prototype; - -// -/** - * This file contains a list of utility functions which are useful in other - * files. - */ - -/** - * Provide a default value if a setting is undefined - */ -const deflt = function(setting, defaultIfUndefined) { - return setting === undefined ? defaultIfUndefined : setting; -}; - -// hyphenate and escape adapted from Facebook's React under Apache 2 license - -const uppercase = /([A-Z])/g; -const hyphenate = function(str) { - return str.replace(uppercase, "-$1").toLowerCase(); -}; - -const ESCAPE_LOOKUP = { - "&": "&", - ">": ">", - "<": "<", - '"': """, - "'": "'" -}; - -const ESCAPE_REGEX = /[&><"']/g; - -/** - * Escapes text to prevent scripting attacks. - */ -function escape(text) { - return String(text).replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]); -} - -/** - * Sometimes we want to pull out the innermost element of a group. In most - * cases, this will just be the group itself, but when ordgroups and colors have - * a single element, we want to pull that out. - */ -const getBaseElem = function(group) { - if (group.type === "ordgroup") { - if (group.body.length === 1) { - return getBaseElem(group.body[0]); - } else { - return group; - } - } else if (group.type === "color") { - if (group.body.length === 1) { - return getBaseElem(group.body[0]); - } else { - return group; - } - } else if (group.type === "font") { - return getBaseElem(group.body); - } else { - return group; - } -}; - -/** - * TeXbook algorithms often reference "character boxes", which are simply groups - * with a single character in them. To decide if something is a character box, - * we find its innermost group, and see if it is a single character. - */ -const isCharacterBox = function(group) { - const baseElem = getBaseElem(group); - - // These are all the types of groups which hold single characters - return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom" -}; - -const assert = function(value) { - if (!value) { - throw new Error("Expected non-null, but got " + String(value)); - } - return value; -}; - -/** - * Return the protocol of a URL, or "_relative" if the URL does not specify a - * protocol (and thus is relative), or `null` if URL has invalid protocol - * (so should be outright rejected). - */ -const protocolFromUrl = function(url) { - // Check for possible leading protocol. - // https://url.spec.whatwg.org/#url-parsing strips leading whitespace - // (\x00) or C0 control (\x00-\x1F) characters. - // eslint-disable-next-line no-control-regex - const protocol = /^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(url); - if (!protocol) { - return "_relative"; - } - // Reject weird colons - if (protocol[2] !== ":") { - return null; - } - // Reject invalid characters in scheme according to - // https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 - if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(protocol[1])) { - return null; - } - // Lowercase the protocol - return protocol[1].toLowerCase(); -}; - -/** - * Round `n` to 4 decimal places, or to the nearest 1/10,000th em. The TeXbook - * gives an acceptable rounding error of 100sp (which would be the nearest - * 1/6551.6em with our ptPerEm = 10): - * http://www.ctex.org/documents/shredder/src/texbook.pdf#page=69 - */ -const round = function(n) { - return +n.toFixed(4); -}; - -// Identify short letters. Used for accents and \cancelto. -const smalls = "acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚𝐜𝐞𝐠𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐮𝐯𝐰𝐱𝐲𝐳"; - -/** - * This is a module for storing settings passed into Temml. It correctly handles - * default settings. - */ - - -/** - * The main Settings object - */ -class Settings { - constructor(options) { - // allow null options - options = options || {}; - this.displayMode = deflt(options.displayMode, false); // boolean - this.annotate = deflt(options.annotate, false); // boolean - this.leqno = deflt(options.leqno, false); // boolean - this.throwOnError = deflt(options.throwOnError, false); // boolean - this.errorColor = deflt(options.errorColor, "#b22222"); // string - this.macros = options.macros || {}; - this.wrap = deflt(options.wrap, "none"); // "none" | "tex" | "=" - this.xml = deflt(options.xml, false); // boolean - this.colorIsTextColor = deflt(options.colorIsTextColor, false); // boolean - this.strict = deflt(options.strict, false); // boolean - this.trust = deflt(options.trust, false); // trust context. See html.js. - this.maxSize = (options.maxSize === undefined - ? [Infinity, Infinity] - : Array.isArray(options.maxSize) - ? options.maxSize - : [Infinity, Infinity] - ); - this.maxExpand = Math.max(0, deflt(options.maxExpand, 1000)); // number - this.wrapDelimiterPairs = true; // boolean - } - - /** - * Check whether to test potentially dangerous input, and return - * `true` (trusted) or `false` (untrusted). The sole argument `context` - * should be an object with `command` field specifying the relevant LaTeX - * command (as a string starting with `\`), and any other arguments, etc. - * If `context` has a `url` field, a `protocol` field will automatically - * get added by this function (changing the specified object). - */ - isTrusted(context) { - if (context.url && !context.protocol) { - const protocol = protocolFromUrl(context.url); - if (protocol == null) { - return false - } - context.protocol = protocol; - } - const trust = typeof this.trust === "function" ? this.trust(context) : this.trust; - return Boolean(trust); - } -} - -/** - * All registered functions. - * `functions.js` just exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary. - */ -const _functions = {}; - -/** - * All MathML builders. Should be only used in the `define*` and the `build*ML` - * functions. - */ -const _mathmlGroupBuilders = {}; - -function defineFunction({ - type, - names, - props, - handler, - mathmlBuilder -}) { - // Set default values of functions - const data = { - type, - numArgs: props.numArgs, - argTypes: props.argTypes, - allowedInArgument: !!props.allowedInArgument, - allowedInText: !!props.allowedInText, - allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath, - numOptionalArgs: props.numOptionalArgs || 0, - infix: !!props.infix, - primitive: !!props.primitive, - handler: handler - }; - for (let i = 0; i < names.length; ++i) { - _functions[names[i]] = data; - } - if (type) { - if (mathmlBuilder) { - _mathmlGroupBuilders[type] = mathmlBuilder; - } - } -} - -/** - * Use this to register only the MathML builder for a function(e.g. - * if the function's ParseNode is generated in Parser.js rather than via a - * stand-alone handler provided to `defineFunction`). - */ -function defineFunctionBuilders({ type, mathmlBuilder }) { - defineFunction({ - type, - names: [], - props: { numArgs: 0 }, - handler() { - throw new Error("Should never be called.") - }, - mathmlBuilder - }); -} - -const normalizeArgument = function(arg) { - return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg -}; - -// Since the corresponding buildMathML function expects a -// list of elements, we normalize for different kinds of arguments -const ordargument = function(arg) { - return arg.type === "ordgroup" ? arg.body : [arg] -}; - -/** - * This node represents a document fragment, which contains elements, but when - * placed into the DOM doesn't have any representation itself. It only contains - * children and doesn't have any DOM node properties. - */ -class DocumentFragment { - constructor(children) { - this.children = children; - this.classes = []; - this.style = {}; - } - - hasClass(className) { - return this.classes.includes(className); - } - - /** Convert the fragment into a node. */ - toNode() { - const frag = document.createDocumentFragment(); - - for (let i = 0; i < this.children.length; i++) { - frag.appendChild(this.children[i].toNode()); - } - - return frag; - } - - /** Convert the fragment into HTML markup. */ - toMarkup() { - let markup = ""; - - // Simply concatenate the markup for the children together. - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - return markup; - } - - /** - * Converts the math node into a string, similar to innerText. Applies to - * MathDomNode's only. - */ - toText() { - // To avoid this, we would subclass documentFragment separately for - // MathML, but polyfills for subclassing is expensive per PR 1469. - const toText = (child) => child.toText(); - return this.children.map(toText).join(""); - } -} - -/** - * These objects store the data about the DOM nodes we create, as well as some - * extra data. They can then be transformed into real DOM nodes with the - * `toNode` function or HTML markup using `toMarkup`. They are useful for both - * storing extra properties on the nodes, as well as providing a way to easily - * work with the DOM. - * - * Similar functions for working with MathML nodes exist in mathMLTree.js. - * - */ - -/** - * Create an HTML className based on a list of classes. In addition to joining - * with spaces, we also remove empty classes. - */ -const createClass = function(classes) { - return classes.filter((cls) => cls).join(" "); -}; - -const initNode = function(classes, style) { - this.classes = classes || []; - this.attributes = {}; - this.style = style || {}; -}; - -/** - * Convert into an HTML node - */ -const toNode = function(tagName) { - const node = document.createElement(tagName); - - // Apply the class - node.className = createClass(this.classes); - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - // Apply attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { - node.setAttribute(attr, this.attributes[attr]); - } - } - - // Append the children, also as HTML nodes - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - - return node; -}; - -/** - * Convert into an HTML markup string - */ -const toMarkup = function(tagName) { - let markup = `<${tagName}`; - - // Add the class - if (this.classes.length) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - - let styles = ""; - - // Add the styles, after hyphenation - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - styles += `${hyphenate(style)}:${this.style[style]};`; - } - } - - if (styles) { - markup += ` style="${styles}"`; - } - - // Add the attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { - markup += ` ${attr}="${escape(this.attributes[attr])}"`; - } - } - - markup += ">"; - - // Add the markup of the children, also as markup - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - markup += ``; - - return markup; -}; - -/** - * This node represents a span node, with a className, a list of children, and - * an inline style. - * - */ -class Span { - constructor(classes, children, style) { - initNode.call(this, classes, style); - this.children = children || []; - } - - setAttribute(attribute, value) { - this.attributes[attribute] = value; - } - - toNode() { - return toNode.call(this, "span"); - } - - toMarkup() { - return toMarkup.call(this, "span"); - } -} - -let TextNode$1 = class TextNode { - constructor(text) { - this.text = text; - } - toNode() { - return document.createTextNode(this.text); - } - toMarkup() { - return escape(this.text); - } -}; - -// Create an node. -class AnchorNode { - constructor(href, classes, children) { - this.href = href; - this.classes = classes; - this.children = children || []; - } - - toNode() { - const node = document.createElement("a"); - node.setAttribute("href", this.href); - if (this.classes.length > 0) { - node.className = createClass(this.classes); - } - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - return node - } - - toMarkup() { - let markup = ` 0) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - markup += ">"; - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - markup += ""; - return markup - } -} - -/* - * This node represents an image embed () element. - */ -class Img { - constructor(src, alt, style) { - this.alt = alt; - this.src = src; - this.classes = ["mord"]; - this.style = style; - } - - hasClass(className) { - return this.classes.includes(className); - } - - toNode() { - const node = document.createElement("img"); - node.src = this.src; - node.alt = this.alt; - node.className = "mord"; - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - return node; - } - - toMarkup() { - let markup = `${this.alt}` and - * `` tags). - */ -class MathNode { - constructor(type, children, classes, style) { - this.type = type; - this.attributes = {}; - this.children = children || []; - this.classes = classes || []; - this.style = style || {}; // Used for elements - this.label = ""; - } - - /** - * Sets an attribute on a MathML node. MathML depends on attributes to convey a - * semantic content, so this is used heavily. - */ - setAttribute(name, value) { - this.attributes[name] = value; - } - - /** - * Gets an attribute on a MathML node. - */ - getAttribute(name) { - return this.attributes[name]; - } - - setLabel(value) { - this.label = value; - } - - /** - * Converts the math node into a MathML-namespaced DOM element. - */ - toNode() { - const node = document.createElementNS("http://www.w3.org/1998/Math/MathML", this.type); - - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { - node.setAttribute(attr, this.attributes[attr]); - } - } - - if (this.classes.length > 0) { - node.className = createClass(this.classes); - } - - // Apply inline styles - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - node.style[style] = this.style[style]; - } - } - - for (let i = 0; i < this.children.length; i++) { - node.appendChild(this.children[i].toNode()); - } - - return node; - } - - /** - * Converts the math node into an HTML markup string. - */ - toMarkup() { - let markup = "<" + this.type; - - // Add the attributes - for (const attr in this.attributes) { - if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { - markup += " " + attr + '="'; - markup += escape(this.attributes[attr]); - markup += '"'; - } - } - - if (this.classes.length > 0) { - markup += ` class="${escape(createClass(this.classes))}"`; - } - - let styles = ""; - - // Add the styles, after hyphenation - for (const style in this.style) { - if (Object.prototype.hasOwnProperty.call(this.style, style )) { - styles += `${hyphenate(style)}:${this.style[style]};`; - } - } - - if (styles) { - markup += ` style="${styles}"`; - } - - markup += ">"; - - for (let i = 0; i < this.children.length; i++) { - markup += this.children[i].toMarkup(); - } - - markup += ""; - - return markup; - } - - /** - * Converts the math node into a string, similar to innerText, but escaped. - */ - toText() { - return this.children.map((child) => child.toText()).join(""); - } -} - -/** - * This node represents a piece of text. - */ -class TextNode { - constructor(text) { - this.text = text; - } - - /** - * Converts the text node into a DOM text node. - */ - toNode() { - return document.createTextNode(this.text); - } - - /** - * Converts the text node into escaped HTML markup - * (representing the text itself). - */ - toMarkup() { - return escape(this.toText()); - } - - /** - * Converts the text node into a string - * (representing the text itself). - */ - toText() { - return this.text; - } -} - -// Do not make an the only child of a . -// An acts as its own implicit . -const wrapWithMstyle = expression => { - let node; - if (expression.length === 1 && expression[0].type === "mrow") { - node = expression.pop(); - node.type = "mstyle"; - } else { - node = new MathNode("mstyle", expression); - } - return node -}; - -/** - * This file provides support for building horizontal stretchy elements. - */ - - -// TODO: Remove when Chromium stretches \widetilde & \widehat -const estimatedWidth = node => { - let width = 0; - if (node.body && Array.isArray(node.body)) { - for (const item of node.body) { - width += estimatedWidth(item); - } - } else if (node.body) { - width += estimatedWidth(node.body); - } else if (node.type === "supsub") { - width += estimatedWidth(node.base); - if (node.sub) { width += 0.7 * estimatedWidth(node.sub); } - if (node.sup) { width += 0.7 * estimatedWidth(node.sup); } - } else if (node.type === "mathord" || node.type === "textord") { - for (const ch of node.text.split('')) { - const codePoint = ch.codePointAt(0); - if ((0x60 < codePoint && codePoint < 0x7B) || (0x03B0 < codePoint && codePoint < 0x3CA)) { - width += 0.56; // lower case latin or greek. Use advance width of letter n - } else if (0x2F < codePoint && codePoint < 0x3A) { - width += 0.50; // numerals. - } else { - width += 0.92; // advance width of letter M - } - } - } else { - width += 1.0; - } - return width -}; - -const stretchyCodePoint = { - widehat: "^", - widecheck: "ˇ", - widetilde: "~", - wideparen: "⏜", // \u23dc - utilde: "~", - overleftarrow: "\u2190", - underleftarrow: "\u2190", - xleftarrow: "\u2190", - overrightarrow: "\u2192", - underrightarrow: "\u2192", - xrightarrow: "\u2192", - underbrace: "\u23df", - overbrace: "\u23de", - overbracket: "\u23b4", - underbracket: "\u23b5", - overgroup: "\u23e0", - overparen: "⏜", - undergroup: "\u23e1", - underparen: "\u23dd", - overleftrightarrow: "\u2194", - underleftrightarrow: "\u2194", - xleftrightarrow: "\u2194", - Overrightarrow: "\u21d2", - xRightarrow: "\u21d2", - overleftharpoon: "\u21bc", - xleftharpoonup: "\u21bc", - overrightharpoon: "\u21c0", - xrightharpoonup: "\u21c0", - xLeftarrow: "\u21d0", - xLeftrightarrow: "\u21d4", - xhookleftarrow: "\u21a9", - xhookrightarrow: "\u21aa", - xmapsto: "\u21a6", - xrightharpoondown: "\u21c1", - xleftharpoondown: "\u21bd", - xtwoheadleftarrow: "\u219e", - xtwoheadrightarrow: "\u21a0", - xlongequal: "=", - xrightleftarrows: "\u21c4", - xtofrom: "\u21c4", - xleftrightharpoons: "\u21cb", - xrightleftharpoons: "\u21cc", - yields: "\u2192", - yieldsLeft: "\u2190", - mesomerism: "\u2194", - longrightharpoonup: "\u21c0", - longleftharpoondown: "\u21bd", - eqrightharpoonup: "\u21c0", - eqleftharpoondown: "\u21bd", - "\\cdrightarrow": "\u2192", - "\\cdleftarrow": "\u2190", - "\\cdlongequal": "=", - yieldsLeftRight: "\u21c4", - chemequilibrium: "\u21cc" -}; - -const mathMLnode = function(label) { - const child = new TextNode(stretchyCodePoint[label.slice(1)]); - const node = new MathNode("mo", [child]); - node.setAttribute("stretchy", "true"); - return node -}; - -const crookedWides = ["\\widetilde", "\\widehat", "\\widecheck", "\\utilde"]; - -// TODO: Remove when Chromium stretches \widetilde & \widehat -const accentNode = (group) => { - const mo = mathMLnode(group.label); - if (crookedWides.includes(group.label)) { - const width = estimatedWidth(group.base); - if (1 < width && width < 1.6) { - mo.classes.push("tml-crooked-2"); - } else if (1.6 <= width && width < 2.5) { - mo.classes.push("tml-crooked-3"); - } else if (2.5 <= width) { - mo.classes.push("tml-crooked-4"); - } - } - return mo -}; - -/** - * This file holds a list of all no-argument functions and single-character - * symbols (like 'a' or ';'). - * - * For each of the symbols, there are two properties they can have: - * - group (required): the ParseNode group type the symbol should have (i.e. - "textord", "mathord", etc). - * - replace: the character that this symbol or function should be - * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi - * character in the main font). - * - * The outermost map in the table indicates what mode the symbols should be - * accepted in (e.g. "math" or "text"). - */ - -// Some of these have a "-token" suffix since these are also used as `ParseNode` -// types for raw text tokens, and we want to avoid conflicts with higher-level -// `ParseNode` types. These `ParseNode`s are constructed within `Parser` by -// looking up the `symbols` map. -const ATOMS = { - bin: 1, - close: 1, - inner: 1, - open: 1, - punct: 1, - rel: 1 -}; -const NON_ATOMS = { - "accent-token": 1, - mathord: 1, - "op-token": 1, - spacing: 1, - textord: 1 -}; - -const symbols = { - math: {}, - text: {} -}; - -/** `acceptUnicodeChar = true` is only applicable if `replace` is set. */ -function defineSymbol(mode, group, replace, name, acceptUnicodeChar) { - symbols[mode][name] = { group, replace }; - - if (acceptUnicodeChar && replace) { - symbols[mode][replace] = symbols[mode][name]; - } -} - -// Some abbreviations for commonly used strings. -// This helps minify the code, and also spotting typos using jshint. - -// modes: -const math = "math"; -const text = "text"; - -// groups: -const accent = "accent-token"; -const bin = "bin"; -const close = "close"; -const inner = "inner"; -const mathord = "mathord"; -const op = "op-token"; -const open = "open"; -const punct = "punct"; -const rel = "rel"; -const spacing = "spacing"; -const textord = "textord"; - -// Now comes the symbol table - -// Relation Symbols -defineSymbol(math, rel, "\u2261", "\\equiv", true); -defineSymbol(math, rel, "\u227a", "\\prec", true); -defineSymbol(math, rel, "\u227b", "\\succ", true); -defineSymbol(math, rel, "\u223c", "\\sim", true); -defineSymbol(math, rel, "\u27c2", "\\perp", true); -defineSymbol(math, rel, "\u2aaf", "\\preceq", true); -defineSymbol(math, rel, "\u2ab0", "\\succeq", true); -defineSymbol(math, rel, "\u2243", "\\simeq", true); -defineSymbol(math, rel, "\u224c", "\\backcong", true); -defineSymbol(math, rel, "|", "\\mid", true); -defineSymbol(math, rel, "\u226a", "\\ll", true); -defineSymbol(math, rel, "\u226b", "\\gg", true); -defineSymbol(math, rel, "\u224d", "\\asymp", true); -defineSymbol(math, rel, "\u2225", "\\parallel"); -defineSymbol(math, rel, "\u2323", "\\smile", true); -defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true); -defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true); -defineSymbol(math, rel, "\u2250", "\\doteq", true); -defineSymbol(math, rel, "\u2322", "\\frown", true); -defineSymbol(math, rel, "\u220b", "\\ni", true); -defineSymbol(math, rel, "\u220c", "\\notni", true); -defineSymbol(math, rel, "\u221d", "\\propto", true); -defineSymbol(math, rel, "\u22a2", "\\vdash", true); -defineSymbol(math, rel, "\u22a3", "\\dashv", true); -defineSymbol(math, rel, "\u220b", "\\owns"); -defineSymbol(math, rel, "\u2258", "\\arceq", true); -defineSymbol(math, rel, "\u2259", "\\wedgeq", true); -defineSymbol(math, rel, "\u225a", "\\veeeq", true); -defineSymbol(math, rel, "\u225b", "\\stareq", true); -defineSymbol(math, rel, "\u225d", "\\eqdef", true); -defineSymbol(math, rel, "\u225e", "\\measeq", true); -defineSymbol(math, rel, "\u225f", "\\questeq", true); -defineSymbol(math, rel, "\u2260", "\\ne", true); -defineSymbol(math, rel, "\u2260", "\\neq"); -// unicodemath -defineSymbol(math, rel, "\u2a75", "\\eqeq", true); -defineSymbol(math, rel, "\u2a76", "\\eqeqeq", true); -// mathtools.sty -defineSymbol(math, rel, "\u2237", "\\dblcolon", true); -defineSymbol(math, rel, "\u2254", "\\coloneqq", true); -defineSymbol(math, rel, "\u2255", "\\eqqcolon", true); -defineSymbol(math, rel, "\u2239", "\\eqcolon", true); -defineSymbol(math, rel, "\u2A74", "\\Coloneqq", true); - -// Punctuation -defineSymbol(math, punct, "\u002e", "\\ldotp"); -defineSymbol(math, punct, "\u00b7", "\\cdotp"); - -// Misc Symbols -defineSymbol(math, textord, "\u0023", "\\#"); -defineSymbol(text, textord, "\u0023", "\\#"); -defineSymbol(math, textord, "\u0026", "\\&"); -defineSymbol(text, textord, "\u0026", "\\&"); -defineSymbol(math, textord, "\u2135", "\\aleph", true); -defineSymbol(math, textord, "\u2200", "\\forall", true); -defineSymbol(math, textord, "\u210f", "\\hbar", true); -defineSymbol(math, textord, "\u2203", "\\exists", true); -defineSymbol(math, open, "\u2207", "\\nabla", true); -defineSymbol(math, textord, "\u266d", "\\flat", true); -defineSymbol(math, textord, "\u2113", "\\ell", true); -defineSymbol(math, textord, "\u266e", "\\natural", true); -defineSymbol(math, textord, "Å", "\\Angstrom", true); -defineSymbol(text, textord, "Å", "\\Angstrom", true); -defineSymbol(math, textord, "\u2663", "\\clubsuit", true); -defineSymbol(math, textord, "\u2667", "\\varclubsuit", true); -defineSymbol(math, textord, "\u2118", "\\wp", true); -defineSymbol(math, textord, "\u266f", "\\sharp", true); -defineSymbol(math, textord, "\u2662", "\\diamondsuit", true); -defineSymbol(math, textord, "\u2666", "\\vardiamondsuit", true); -defineSymbol(math, textord, "\u211c", "\\Re", true); -defineSymbol(math, textord, "\u2661", "\\heartsuit", true); -defineSymbol(math, textord, "\u2665", "\\varheartsuit", true); -defineSymbol(math, textord, "\u2111", "\\Im", true); -defineSymbol(math, textord, "\u2660", "\\spadesuit", true); -defineSymbol(math, textord, "\u2664", "\\varspadesuit", true); -defineSymbol(math, textord, "\u2640", "\\female", true); -defineSymbol(math, textord, "\u2642", "\\male", true); -defineSymbol(math, textord, "\u00a7", "\\S", true); -defineSymbol(text, textord, "\u00a7", "\\S"); -defineSymbol(math, textord, "\u00b6", "\\P", true); -defineSymbol(text, textord, "\u00b6", "\\P"); -defineSymbol(text, textord, "\u263a", "\\smiley", true); -defineSymbol(math, textord, "\u263a", "\\smiley", true); - -// Math and Text -defineSymbol(math, textord, "\u2020", "\\dag"); -defineSymbol(text, textord, "\u2020", "\\dag"); -defineSymbol(text, textord, "\u2020", "\\textdagger"); -defineSymbol(math, textord, "\u2021", "\\ddag"); -defineSymbol(text, textord, "\u2021", "\\ddag"); -defineSymbol(text, textord, "\u2021", "\\textdaggerdbl"); - -// Large Delimiters -defineSymbol(math, close, "\u23b1", "\\rmoustache", true); -defineSymbol(math, open, "\u23b0", "\\lmoustache", true); -defineSymbol(math, close, "\u27ef", "\\rgroup", true); -defineSymbol(math, open, "\u27ee", "\\lgroup", true); - -// Binary Operators -defineSymbol(math, bin, "\u2213", "\\mp", true); -defineSymbol(math, bin, "\u2296", "\\ominus", true); -defineSymbol(math, bin, "\u228e", "\\uplus", true); -defineSymbol(math, bin, "\u2293", "\\sqcap", true); -defineSymbol(math, bin, "\u2217", "\\ast"); -defineSymbol(math, bin, "\u2294", "\\sqcup", true); -defineSymbol(math, bin, "\u25ef", "\\bigcirc", true); -defineSymbol(math, bin, "\u2219", "\\bullet", true); -defineSymbol(math, bin, "\u2021", "\\ddagger"); -defineSymbol(math, bin, "\u2240", "\\wr", true); -defineSymbol(math, bin, "\u2a3f", "\\amalg"); -defineSymbol(math, bin, "\u0026", "\\And"); // from amsmath -defineSymbol(math, bin, "\u2AFD", "\\sslash", true); // from stmaryrd - -// Arrow Symbols -defineSymbol(math, rel, "\u27f5", "\\longleftarrow", true); -defineSymbol(math, rel, "\u21d0", "\\Leftarrow", true); -defineSymbol(math, rel, "\u27f8", "\\Longleftarrow", true); -defineSymbol(math, rel, "\u27f6", "\\longrightarrow", true); -defineSymbol(math, rel, "\u21d2", "\\Rightarrow", true); -defineSymbol(math, rel, "\u27f9", "\\Longrightarrow", true); -defineSymbol(math, rel, "\u2194", "\\leftrightarrow", true); -defineSymbol(math, rel, "\u27f7", "\\longleftrightarrow", true); -defineSymbol(math, rel, "\u21d4", "\\Leftrightarrow", true); -defineSymbol(math, rel, "\u27fa", "\\Longleftrightarrow", true); -defineSymbol(math, rel, "\u21a4", "\\mapsfrom", true); -defineSymbol(math, rel, "\u21a6", "\\mapsto", true); -defineSymbol(math, rel, "\u27fc", "\\longmapsto", true); -defineSymbol(math, rel, "\u2197", "\\nearrow", true); -defineSymbol(math, rel, "\u21a9", "\\hookleftarrow", true); -defineSymbol(math, rel, "\u21aa", "\\hookrightarrow", true); -defineSymbol(math, rel, "\u2198", "\\searrow", true); -defineSymbol(math, rel, "\u21bc", "\\leftharpoonup", true); -defineSymbol(math, rel, "\u21c0", "\\rightharpoonup", true); -defineSymbol(math, rel, "\u2199", "\\swarrow", true); -defineSymbol(math, rel, "\u21bd", "\\leftharpoondown", true); -defineSymbol(math, rel, "\u21c1", "\\rightharpoondown", true); -defineSymbol(math, rel, "\u2196", "\\nwarrow", true); -defineSymbol(math, rel, "\u21cc", "\\rightleftharpoons", true); -defineSymbol(math, mathord, "\u21af", "\\lightning", true); -defineSymbol(math, mathord, "\u220E", "\\QED", true); -defineSymbol(math, mathord, "\u2030", "\\permil", true); -defineSymbol(text, textord, "\u2030", "\\permil"); -defineSymbol(math, mathord, "\u2609", "\\astrosun", true); -defineSymbol(math, mathord, "\u263c", "\\sun", true); -defineSymbol(math, mathord, "\u263e", "\\leftmoon", true); -defineSymbol(math, mathord, "\u263d", "\\rightmoon", true); -defineSymbol(math, mathord, "\u2295", "\\Earth"); - -// AMS Negated Binary Relations -defineSymbol(math, rel, "\u226e", "\\nless", true); -// Symbol names preceded by "@" each have a corresponding macro. -defineSymbol(math, rel, "\u2a87", "\\lneq", true); -defineSymbol(math, rel, "\u2268", "\\lneqq", true); -defineSymbol(math, rel, "\u2268\ufe00", "\\lvertneqq"); -defineSymbol(math, rel, "\u22e6", "\\lnsim", true); -defineSymbol(math, rel, "\u2a89", "\\lnapprox", true); -defineSymbol(math, rel, "\u2280", "\\nprec", true); -// unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym. -defineSymbol(math, rel, "\u22e0", "\\npreceq", true); -defineSymbol(math, rel, "\u22e8", "\\precnsim", true); -defineSymbol(math, rel, "\u2ab9", "\\precnapprox", true); -defineSymbol(math, rel, "\u2241", "\\nsim", true); -defineSymbol(math, rel, "\u2224", "\\nmid", true); -defineSymbol(math, rel, "\u2224", "\\nshortmid"); -defineSymbol(math, rel, "\u22ac", "\\nvdash", true); -defineSymbol(math, rel, "\u22ad", "\\nvDash", true); -defineSymbol(math, rel, "\u22ea", "\\ntriangleleft"); -defineSymbol(math, rel, "\u22ec", "\\ntrianglelefteq", true); -defineSymbol(math, rel, "\u2284", "\\nsubset", true); -defineSymbol(math, rel, "\u2285", "\\nsupset", true); -defineSymbol(math, rel, "\u228a", "\\subsetneq", true); -defineSymbol(math, rel, "\u228a\ufe00", "\\varsubsetneq"); -defineSymbol(math, rel, "\u2acb", "\\subsetneqq", true); -defineSymbol(math, rel, "\u2acb\ufe00", "\\varsubsetneqq"); -defineSymbol(math, rel, "\u226f", "\\ngtr", true); -defineSymbol(math, rel, "\u2a88", "\\gneq", true); -defineSymbol(math, rel, "\u2269", "\\gneqq", true); -defineSymbol(math, rel, "\u2269\ufe00", "\\gvertneqq"); -defineSymbol(math, rel, "\u22e7", "\\gnsim", true); -defineSymbol(math, rel, "\u2a8a", "\\gnapprox", true); -defineSymbol(math, rel, "\u2281", "\\nsucc", true); -// unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym. -defineSymbol(math, rel, "\u22e1", "\\nsucceq", true); -defineSymbol(math, rel, "\u22e9", "\\succnsim", true); -defineSymbol(math, rel, "\u2aba", "\\succnapprox", true); -// unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym. -defineSymbol(math, rel, "\u2246", "\\ncong", true); -defineSymbol(math, rel, "\u2226", "\\nparallel", true); -defineSymbol(math, rel, "\u2226", "\\nshortparallel"); -defineSymbol(math, rel, "\u22af", "\\nVDash", true); -defineSymbol(math, rel, "\u22eb", "\\ntriangleright"); -defineSymbol(math, rel, "\u22ed", "\\ntrianglerighteq", true); -defineSymbol(math, rel, "\u228b", "\\supsetneq", true); -defineSymbol(math, rel, "\u228b", "\\varsupsetneq"); -defineSymbol(math, rel, "\u2acc", "\\supsetneqq", true); -defineSymbol(math, rel, "\u2acc\ufe00", "\\varsupsetneqq"); -defineSymbol(math, rel, "\u22ae", "\\nVdash", true); -defineSymbol(math, rel, "\u2ab5", "\\precneqq", true); -defineSymbol(math, rel, "\u2ab6", "\\succneqq", true); -defineSymbol(math, bin, "\u22b4", "\\unlhd"); -defineSymbol(math, bin, "\u22b5", "\\unrhd"); - -// AMS Negated Arrows -defineSymbol(math, rel, "\u219a", "\\nleftarrow", true); -defineSymbol(math, rel, "\u219b", "\\nrightarrow", true); -defineSymbol(math, rel, "\u21cd", "\\nLeftarrow", true); -defineSymbol(math, rel, "\u21cf", "\\nRightarrow", true); -defineSymbol(math, rel, "\u21ae", "\\nleftrightarrow", true); -defineSymbol(math, rel, "\u21ce", "\\nLeftrightarrow", true); - -// AMS Misc -defineSymbol(math, rel, "\u25b3", "\\vartriangle"); -defineSymbol(math, textord, "\u210f", "\\hslash"); -defineSymbol(math, textord, "\u25bd", "\\triangledown"); -defineSymbol(math, textord, "\u25ca", "\\lozenge"); -defineSymbol(math, textord, "\u24c8", "\\circledS"); -defineSymbol(math, textord, "\u00ae", "\\circledR", true); -defineSymbol(text, textord, "\u00ae", "\\circledR"); -defineSymbol(text, textord, "\u00ae", "\\textregistered"); -defineSymbol(math, textord, "\u2221", "\\measuredangle", true); -defineSymbol(math, textord, "\u2204", "\\nexists"); -defineSymbol(math, textord, "\u2127", "\\mho"); -defineSymbol(math, textord, "\u2132", "\\Finv", true); -defineSymbol(math, textord, "\u2141", "\\Game", true); -defineSymbol(math, textord, "\u2035", "\\backprime"); -defineSymbol(math, textord, "\u2036", "\\backdprime"); -defineSymbol(math, textord, "\u2037", "\\backtrprime"); -defineSymbol(math, textord, "\u25b2", "\\blacktriangle"); -defineSymbol(math, textord, "\u25bc", "\\blacktriangledown"); -defineSymbol(math, textord, "\u25a0", "\\blacksquare"); -defineSymbol(math, textord, "\u29eb", "\\blacklozenge"); -defineSymbol(math, textord, "\u2605", "\\bigstar"); -defineSymbol(math, textord, "\u2222", "\\sphericalangle", true); -defineSymbol(math, textord, "\u2201", "\\complement", true); -defineSymbol(math, textord, "\u2571", "\\diagup"); -defineSymbol(math, textord, "\u2572", "\\diagdown"); -defineSymbol(math, textord, "\u25a1", "\\square"); -defineSymbol(math, textord, "\u25a1", "\\Box"); -defineSymbol(math, textord, "\u25ca", "\\Diamond"); -// unicode-math maps U+A5 to \mathyen. We map to AMS function \yen -defineSymbol(math, textord, "\u00a5", "\\yen", true); -defineSymbol(text, textord, "\u00a5", "\\yen", true); -defineSymbol(math, textord, "\u2713", "\\checkmark", true); -defineSymbol(text, textord, "\u2713", "\\checkmark"); -defineSymbol(math, textord, "\u2717", "\\ballotx", true); -defineSymbol(text, textord, "\u2717", "\\ballotx"); -defineSymbol(text, textord, "\u2022", "\\textbullet"); - -// AMS Hebrew -defineSymbol(math, textord, "\u2136", "\\beth", true); -defineSymbol(math, textord, "\u2138", "\\daleth", true); -defineSymbol(math, textord, "\u2137", "\\gimel", true); - -// AMS Greek -defineSymbol(math, textord, "\u03dd", "\\digamma", true); -defineSymbol(math, textord, "\u03f0", "\\varkappa"); - -// AMS Delimiters -defineSymbol(math, open, "\u231C", "\\ulcorner", true); -defineSymbol(math, close, "\u231D", "\\urcorner", true); -defineSymbol(math, open, "\u231E", "\\llcorner", true); -defineSymbol(math, close, "\u231F", "\\lrcorner", true); - -// AMS Binary Relations -defineSymbol(math, rel, "\u2266", "\\leqq", true); -defineSymbol(math, rel, "\u2a7d", "\\leqslant", true); -defineSymbol(math, rel, "\u2a95", "\\eqslantless", true); -defineSymbol(math, rel, "\u2272", "\\lesssim", true); -defineSymbol(math, rel, "\u2a85", "\\lessapprox", true); -defineSymbol(math, rel, "\u224a", "\\approxeq", true); -defineSymbol(math, bin, "\u22d6", "\\lessdot"); -defineSymbol(math, rel, "\u22d8", "\\lll", true); -defineSymbol(math, rel, "\u2276", "\\lessgtr", true); -defineSymbol(math, rel, "\u22da", "\\lesseqgtr", true); -defineSymbol(math, rel, "\u2a8b", "\\lesseqqgtr", true); -defineSymbol(math, rel, "\u2251", "\\doteqdot"); -defineSymbol(math, rel, "\u2253", "\\risingdotseq", true); -defineSymbol(math, rel, "\u2252", "\\fallingdotseq", true); -defineSymbol(math, rel, "\u223d", "\\backsim", true); -defineSymbol(math, rel, "\u22cd", "\\backsimeq", true); -defineSymbol(math, rel, "\u2ac5", "\\subseteqq", true); -defineSymbol(math, rel, "\u22d0", "\\Subset", true); -defineSymbol(math, rel, "\u228f", "\\sqsubset", true); -defineSymbol(math, rel, "\u227c", "\\preccurlyeq", true); -defineSymbol(math, rel, "\u22de", "\\curlyeqprec", true); -defineSymbol(math, rel, "\u227e", "\\precsim", true); -defineSymbol(math, rel, "\u2ab7", "\\precapprox", true); -defineSymbol(math, rel, "\u22b2", "\\vartriangleleft"); -defineSymbol(math, rel, "\u22b4", "\\trianglelefteq"); -defineSymbol(math, rel, "\u22a8", "\\vDash", true); -defineSymbol(math, rel, "\u22ab", "\\VDash", true); -defineSymbol(math, rel, "\u22aa", "\\Vvdash", true); -defineSymbol(math, rel, "\u2323", "\\smallsmile"); -defineSymbol(math, rel, "\u2322", "\\smallfrown"); -defineSymbol(math, rel, "\u224f", "\\bumpeq", true); -defineSymbol(math, rel, "\u224e", "\\Bumpeq", true); -defineSymbol(math, rel, "\u2267", "\\geqq", true); -defineSymbol(math, rel, "\u2a7e", "\\geqslant", true); -defineSymbol(math, rel, "\u2a96", "\\eqslantgtr", true); -defineSymbol(math, rel, "\u2273", "\\gtrsim", true); -defineSymbol(math, rel, "\u2a86", "\\gtrapprox", true); -defineSymbol(math, bin, "\u22d7", "\\gtrdot"); -defineSymbol(math, rel, "\u22d9", "\\ggg", true); -defineSymbol(math, rel, "\u2277", "\\gtrless", true); -defineSymbol(math, rel, "\u22db", "\\gtreqless", true); -defineSymbol(math, rel, "\u2a8c", "\\gtreqqless", true); -defineSymbol(math, rel, "\u2256", "\\eqcirc", true); -defineSymbol(math, rel, "\u2257", "\\circeq", true); -defineSymbol(math, rel, "\u225c", "\\triangleq", true); -defineSymbol(math, rel, "\u223c", "\\thicksim"); -defineSymbol(math, rel, "\u2248", "\\thickapprox"); -defineSymbol(math, rel, "\u2ac6", "\\supseteqq", true); -defineSymbol(math, rel, "\u22d1", "\\Supset", true); -defineSymbol(math, rel, "\u2290", "\\sqsupset", true); -defineSymbol(math, rel, "\u227d", "\\succcurlyeq", true); -defineSymbol(math, rel, "\u22df", "\\curlyeqsucc", true); -defineSymbol(math, rel, "\u227f", "\\succsim", true); -defineSymbol(math, rel, "\u2ab8", "\\succapprox", true); -defineSymbol(math, rel, "\u22b3", "\\vartriangleright"); -defineSymbol(math, rel, "\u22b5", "\\trianglerighteq"); -defineSymbol(math, rel, "\u22a9", "\\Vdash", true); -defineSymbol(math, rel, "\u2223", "\\shortmid"); -defineSymbol(math, rel, "\u2225", "\\shortparallel"); -defineSymbol(math, rel, "\u226c", "\\between", true); -defineSymbol(math, rel, "\u22d4", "\\pitchfork", true); -defineSymbol(math, rel, "\u221d", "\\varpropto"); -defineSymbol(math, rel, "\u25c0", "\\blacktriangleleft"); -// unicode-math says that \therefore is a mathord atom. -// We kept the amssymb atom type, which is rel. -defineSymbol(math, rel, "\u2234", "\\therefore", true); -defineSymbol(math, rel, "\u220d", "\\backepsilon"); -defineSymbol(math, rel, "\u25b6", "\\blacktriangleright"); -// unicode-math says that \because is a mathord atom. -// We kept the amssymb atom type, which is rel. -defineSymbol(math, rel, "\u2235", "\\because", true); -defineSymbol(math, rel, "\u22d8", "\\llless"); -defineSymbol(math, rel, "\u22d9", "\\gggtr"); -defineSymbol(math, bin, "\u22b2", "\\lhd"); -defineSymbol(math, bin, "\u22b3", "\\rhd"); -defineSymbol(math, rel, "\u2242", "\\eqsim", true); -defineSymbol(math, rel, "\u2251", "\\Doteq", true); -defineSymbol(math, rel, "\u297d", "\\strictif", true); -defineSymbol(math, rel, "\u297c", "\\strictfi", true); - -// AMS Binary Operators -defineSymbol(math, bin, "\u2214", "\\dotplus", true); -defineSymbol(math, bin, "\u2216", "\\smallsetminus"); -defineSymbol(math, bin, "\u22d2", "\\Cap", true); -defineSymbol(math, bin, "\u22d3", "\\Cup", true); -defineSymbol(math, bin, "\u2a5e", "\\doublebarwedge", true); -defineSymbol(math, bin, "\u229f", "\\boxminus", true); -defineSymbol(math, bin, "\u229e", "\\boxplus", true); -defineSymbol(math, bin, "\u29C4", "\\boxslash", true); -defineSymbol(math, bin, "\u22c7", "\\divideontimes", true); -defineSymbol(math, bin, "\u22c9", "\\ltimes", true); -defineSymbol(math, bin, "\u22ca", "\\rtimes", true); -defineSymbol(math, bin, "\u22cb", "\\leftthreetimes", true); -defineSymbol(math, bin, "\u22cc", "\\rightthreetimes", true); -defineSymbol(math, bin, "\u22cf", "\\curlywedge", true); -defineSymbol(math, bin, "\u22ce", "\\curlyvee", true); -defineSymbol(math, bin, "\u229d", "\\circleddash", true); -defineSymbol(math, bin, "\u229b", "\\circledast", true); -defineSymbol(math, bin, "\u22ba", "\\intercal", true); -defineSymbol(math, bin, "\u22d2", "\\doublecap"); -defineSymbol(math, bin, "\u22d3", "\\doublecup"); -defineSymbol(math, bin, "\u22a0", "\\boxtimes", true); -defineSymbol(math, bin, "\u22c8", "\\bowtie", true); -defineSymbol(math, bin, "\u22c8", "\\Join"); -defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true); -defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true); -defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true); - -// stix Binary Operators -defineSymbol(math, bin, "\u2238", "\\dotminus", true); -defineSymbol(math, bin, "\u27D1", "\\wedgedot", true); -defineSymbol(math, bin, "\u27C7", "\\veedot", true); -defineSymbol(math, bin, "\u2A62", "\\doublebarvee", true); -defineSymbol(math, bin, "\u2A63", "\\veedoublebar", true); -defineSymbol(math, bin, "\u2A5F", "\\wedgebar", true); -defineSymbol(math, bin, "\u2A60", "\\wedgedoublebar", true); -defineSymbol(math, bin, "\u2A54", "\\Vee", true); -defineSymbol(math, bin, "\u2A53", "\\Wedge", true); -defineSymbol(math, bin, "\u2A43", "\\barcap", true); -defineSymbol(math, bin, "\u2A42", "\\barcup", true); -defineSymbol(math, bin, "\u2A48", "\\capbarcup", true); -defineSymbol(math, bin, "\u2A40", "\\capdot", true); -defineSymbol(math, bin, "\u2A47", "\\capovercup", true); -defineSymbol(math, bin, "\u2A46", "\\cupovercap", true); -defineSymbol(math, bin, "\u2A4D", "\\closedvarcap", true); -defineSymbol(math, bin, "\u2A4C", "\\closedvarcup", true); -defineSymbol(math, bin, "\u2A2A", "\\minusdot", true); -defineSymbol(math, bin, "\u2A2B", "\\minusfdots", true); -defineSymbol(math, bin, "\u2A2C", "\\minusrdots", true); -defineSymbol(math, bin, "\u22BB", "\\Xor", true); -defineSymbol(math, bin, "\u22BC", "\\Nand", true); -defineSymbol(math, bin, "\u22BD", "\\Nor", true); -defineSymbol(math, bin, "\u22BD", "\\barvee"); -defineSymbol(math, bin, "\u2AF4", "\\interleave", true); -defineSymbol(math, bin, "\u29E2", "\\shuffle", true); -defineSymbol(math, bin, "\u2AF6", "\\threedotcolon", true); -defineSymbol(math, bin, "\u2982", "\\typecolon", true); -defineSymbol(math, bin, "\u223E", "\\invlazys", true); -defineSymbol(math, bin, "\u2A4B", "\\twocaps", true); -defineSymbol(math, bin, "\u2A4A", "\\twocups", true); -defineSymbol(math, bin, "\u2A4E", "\\Sqcap", true); -defineSymbol(math, bin, "\u2A4F", "\\Sqcup", true); -defineSymbol(math, bin, "\u2A56", "\\veeonvee", true); -defineSymbol(math, bin, "\u2A55", "\\wedgeonwedge", true); -defineSymbol(math, bin, "\u29D7", "\\blackhourglass", true); -defineSymbol(math, bin, "\u29C6", "\\boxast", true); -defineSymbol(math, bin, "\u29C8", "\\boxbox", true); -defineSymbol(math, bin, "\u29C7", "\\boxcircle", true); -defineSymbol(math, bin, "\u229C", "\\circledequal", true); -defineSymbol(math, bin, "\u29B7", "\\circledparallel", true); -defineSymbol(math, bin, "\u29B6", "\\circledvert", true); -defineSymbol(math, bin, "\u29B5", "\\circlehbar", true); -defineSymbol(math, bin, "\u27E1", "\\concavediamond", true); -defineSymbol(math, bin, "\u27E2", "\\concavediamondtickleft", true); -defineSymbol(math, bin, "\u27E3", "\\concavediamondtickright", true); -defineSymbol(math, bin, "\u22C4", "\\diamond", true); -defineSymbol(math, bin, "\u29D6", "\\hourglass", true); -defineSymbol(math, bin, "\u27E0", "\\lozengeminus", true); -defineSymbol(math, bin, "\u233D", "\\obar", true); -defineSymbol(math, bin, "\u29B8", "\\obslash", true); -defineSymbol(math, bin, "\u2A38", "\\odiv", true); -defineSymbol(math, bin, "\u29C1", "\\ogreaterthan", true); -defineSymbol(math, bin, "\u29C0", "\\olessthan", true); -defineSymbol(math, bin, "\u29B9", "\\operp", true); -defineSymbol(math, bin, "\u2A37", "\\Otimes", true); -defineSymbol(math, bin, "\u2A36", "\\otimeshat", true); -defineSymbol(math, bin, "\u22C6", "\\star", true); -defineSymbol(math, bin, "\u25B3", "\\triangle", true); -defineSymbol(math, bin, "\u2A3A", "\\triangleminus", true); -defineSymbol(math, bin, "\u2A39", "\\triangleplus", true); -defineSymbol(math, bin, "\u2A3B", "\\triangletimes", true); -defineSymbol(math, bin, "\u27E4", "\\whitesquaretickleft", true); -defineSymbol(math, bin, "\u27E5", "\\whitesquaretickright", true); -defineSymbol(math, bin, "\u2A33", "\\smashtimes", true); - -// AMS Arrows -// Note: unicode-math maps \u21e2 to their own function \rightdasharrow. -// We'll map it to AMS function \dashrightarrow. It produces the same atom. -defineSymbol(math, rel, "\u21e2", "\\dashrightarrow", true); -// unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym. -defineSymbol(math, rel, "\u21e0", "\\dashleftarrow", true); -defineSymbol(math, rel, "\u21c7", "\\leftleftarrows", true); -defineSymbol(math, rel, "\u21c6", "\\leftrightarrows", true); -defineSymbol(math, rel, "\u21da", "\\Lleftarrow", true); -defineSymbol(math, rel, "\u219e", "\\twoheadleftarrow", true); -defineSymbol(math, rel, "\u21a2", "\\leftarrowtail", true); -defineSymbol(math, rel, "\u21ab", "\\looparrowleft", true); -defineSymbol(math, rel, "\u21cb", "\\leftrightharpoons", true); -defineSymbol(math, rel, "\u21b6", "\\curvearrowleft", true); -// unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym. -defineSymbol(math, rel, "\u21ba", "\\circlearrowleft", true); -defineSymbol(math, rel, "\u21b0", "\\Lsh", true); -defineSymbol(math, rel, "\u21c8", "\\upuparrows", true); -defineSymbol(math, rel, "\u21bf", "\\upharpoonleft", true); -defineSymbol(math, rel, "\u21c3", "\\downharpoonleft", true); -defineSymbol(math, rel, "\u22b6", "\\origof", true); -defineSymbol(math, rel, "\u22b7", "\\imageof", true); -defineSymbol(math, rel, "\u22b8", "\\multimap", true); -defineSymbol(math, rel, "\u21ad", "\\leftrightsquigarrow", true); -defineSymbol(math, rel, "\u21c9", "\\rightrightarrows", true); -defineSymbol(math, rel, "\u21c4", "\\rightleftarrows", true); -defineSymbol(math, rel, "\u21a0", "\\twoheadrightarrow", true); -defineSymbol(math, rel, "\u21a3", "\\rightarrowtail", true); -defineSymbol(math, rel, "\u21ac", "\\looparrowright", true); -defineSymbol(math, rel, "\u21b7", "\\curvearrowright", true); -// unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym. -defineSymbol(math, rel, "\u21bb", "\\circlearrowright", true); -defineSymbol(math, rel, "\u21b1", "\\Rsh", true); -defineSymbol(math, rel, "\u21ca", "\\downdownarrows", true); -defineSymbol(math, rel, "\u21be", "\\upharpoonright", true); -defineSymbol(math, rel, "\u21c2", "\\downharpoonright", true); -defineSymbol(math, rel, "\u21dd", "\\rightsquigarrow", true); -defineSymbol(math, rel, "\u21dd", "\\leadsto"); -defineSymbol(math, rel, "\u21db", "\\Rrightarrow", true); -defineSymbol(math, rel, "\u21be", "\\restriction"); - -defineSymbol(math, textord, "\u2018", "`"); -defineSymbol(math, textord, "$", "\\$"); -defineSymbol(text, textord, "$", "\\$"); -defineSymbol(text, textord, "$", "\\textdollar"); -defineSymbol(math, textord, "¢", "\\cent"); -defineSymbol(text, textord, "¢", "\\cent"); -defineSymbol(math, textord, "%", "\\%"); -defineSymbol(text, textord, "%", "\\%"); -defineSymbol(math, textord, "_", "\\_"); -defineSymbol(text, textord, "_", "\\_"); -defineSymbol(text, textord, "_", "\\textunderscore"); -defineSymbol(text, textord, "\u2423", "\\textvisiblespace", true); -defineSymbol(math, textord, "\u2220", "\\angle", true); -defineSymbol(math, textord, "\u221e", "\\infty", true); -defineSymbol(math, textord, "\u2032", "\\prime"); -defineSymbol(math, textord, "\u2033", "\\dprime"); -defineSymbol(math, textord, "\u2034", "\\trprime"); -defineSymbol(math, textord, "\u2057", "\\qprime"); -defineSymbol(math, textord, "\u25b3", "\\triangle"); -defineSymbol(text, textord, "\u0391", "\\Alpha", true); -defineSymbol(text, textord, "\u0392", "\\Beta", true); -defineSymbol(text, textord, "\u0393", "\\Gamma", true); -defineSymbol(text, textord, "\u0394", "\\Delta", true); -defineSymbol(text, textord, "\u0395", "\\Epsilon", true); -defineSymbol(text, textord, "\u0396", "\\Zeta", true); -defineSymbol(text, textord, "\u0397", "\\Eta", true); -defineSymbol(text, textord, "\u0398", "\\Theta", true); -defineSymbol(text, textord, "\u0399", "\\Iota", true); -defineSymbol(text, textord, "\u039a", "\\Kappa", true); -defineSymbol(text, textord, "\u039b", "\\Lambda", true); -defineSymbol(text, textord, "\u039c", "\\Mu", true); -defineSymbol(text, textord, "\u039d", "\\Nu", true); -defineSymbol(text, textord, "\u039e", "\\Xi", true); -defineSymbol(text, textord, "\u039f", "\\Omicron", true); -defineSymbol(text, textord, "\u03a0", "\\Pi", true); -defineSymbol(text, textord, "\u03a1", "\\Rho", true); -defineSymbol(text, textord, "\u03a3", "\\Sigma", true); -defineSymbol(text, textord, "\u03a4", "\\Tau", true); -defineSymbol(text, textord, "\u03a5", "\\Upsilon", true); -defineSymbol(text, textord, "\u03a6", "\\Phi", true); -defineSymbol(text, textord, "\u03a7", "\\Chi", true); -defineSymbol(text, textord, "\u03a8", "\\Psi", true); -defineSymbol(text, textord, "\u03a9", "\\Omega", true); -defineSymbol(math, mathord, "\u0391", "\\Alpha", true); -defineSymbol(math, mathord, "\u0392", "\\Beta", true); -defineSymbol(math, mathord, "\u0393", "\\Gamma", true); -defineSymbol(math, mathord, "\u0394", "\\Delta", true); -defineSymbol(math, mathord, "\u0395", "\\Epsilon", true); -defineSymbol(math, mathord, "\u0396", "\\Zeta", true); -defineSymbol(math, mathord, "\u0397", "\\Eta", true); -defineSymbol(math, mathord, "\u0398", "\\Theta", true); -defineSymbol(math, mathord, "\u0399", "\\Iota", true); -defineSymbol(math, mathord, "\u039a", "\\Kappa", true); -defineSymbol(math, mathord, "\u039b", "\\Lambda", true); -defineSymbol(math, mathord, "\u039c", "\\Mu", true); -defineSymbol(math, mathord, "\u039d", "\\Nu", true); -defineSymbol(math, mathord, "\u039e", "\\Xi", true); -defineSymbol(math, mathord, "\u039f", "\\Omicron", true); -defineSymbol(math, mathord, "\u03a0", "\\Pi", true); -defineSymbol(math, mathord, "\u03a1", "\\Rho", true); -defineSymbol(math, mathord, "\u03a3", "\\Sigma", true); -defineSymbol(math, mathord, "\u03a4", "\\Tau", true); -defineSymbol(math, mathord, "\u03a5", "\\Upsilon", true); -defineSymbol(math, mathord, "\u03a6", "\\Phi", true); -defineSymbol(math, mathord, "\u03a7", "\\Chi", true); -defineSymbol(math, mathord, "\u03a8", "\\Psi", true); -defineSymbol(math, mathord, "\u03a9", "\\Omega", true); -defineSymbol(math, open, "\u00ac", "\\neg", true); -defineSymbol(math, open, "\u00ac", "\\lnot"); -defineSymbol(math, textord, "\u22a4", "\\top"); -defineSymbol(math, textord, "\u22a5", "\\bot"); -defineSymbol(math, textord, "\u2205", "\\emptyset"); -defineSymbol(math, textord, "\u2300", "\\varnothing"); -defineSymbol(math, mathord, "\u03b1", "\\alpha", true); -defineSymbol(math, mathord, "\u03b2", "\\beta", true); -defineSymbol(math, mathord, "\u03b3", "\\gamma", true); -defineSymbol(math, mathord, "\u03b4", "\\delta", true); -defineSymbol(math, mathord, "\u03f5", "\\epsilon", true); -defineSymbol(math, mathord, "\u03b6", "\\zeta", true); -defineSymbol(math, mathord, "\u03b7", "\\eta", true); -defineSymbol(math, mathord, "\u03b8", "\\theta", true); -defineSymbol(math, mathord, "\u03b9", "\\iota", true); -defineSymbol(math, mathord, "\u03ba", "\\kappa", true); -defineSymbol(math, mathord, "\u03bb", "\\lambda", true); -defineSymbol(math, mathord, "\u03bc", "\\mu", true); -defineSymbol(math, mathord, "\u03bd", "\\nu", true); -defineSymbol(math, mathord, "\u03be", "\\xi", true); -defineSymbol(math, mathord, "\u03bf", "\\omicron", true); -defineSymbol(math, mathord, "\u03c0", "\\pi", true); -defineSymbol(math, mathord, "\u03c1", "\\rho", true); -defineSymbol(math, mathord, "\u03c3", "\\sigma", true); -defineSymbol(math, mathord, "\u03c4", "\\tau", true); -defineSymbol(math, mathord, "\u03c5", "\\upsilon", true); -defineSymbol(math, mathord, "\u03d5", "\\phi", true); -defineSymbol(math, mathord, "\u03c7", "\\chi", true); -defineSymbol(math, mathord, "\u03c8", "\\psi", true); -defineSymbol(math, mathord, "\u03c9", "\\omega", true); -defineSymbol(math, mathord, "\u03b5", "\\varepsilon", true); -defineSymbol(math, mathord, "\u03d1", "\\vartheta", true); -defineSymbol(math, mathord, "\u03d6", "\\varpi", true); -defineSymbol(math, mathord, "\u03f1", "\\varrho", true); -defineSymbol(math, mathord, "\u03c2", "\\varsigma", true); -defineSymbol(math, mathord, "\u03c6", "\\varphi", true); -defineSymbol(math, mathord, "\u03d8", "\\Coppa", true); -defineSymbol(math, mathord, "\u03d9", "\\coppa", true); -defineSymbol(math, mathord, "\u03d9", "\\varcoppa", true); -defineSymbol(math, mathord, "\u03de", "\\Koppa", true); -defineSymbol(math, mathord, "\u03df", "\\koppa", true); -defineSymbol(math, mathord, "\u03e0", "\\Sampi", true); -defineSymbol(math, mathord, "\u03e1", "\\sampi", true); -defineSymbol(math, mathord, "\u03da", "\\Stigma", true); -defineSymbol(math, mathord, "\u03db", "\\stigma", true); -defineSymbol(math, mathord, "\u2aeb", "\\Bot"); - -// unicode-math maps U+F0 to \matheth. We map to AMS function \eth -defineSymbol(math, textord, "\u00f0", "\\eth", true); // ð -defineSymbol(text, textord, "\u00f0", "\u00f0"); -// Extended ASCII and non-ASCII Letters -defineSymbol(math, textord, "\u00C5", "\\AA"); // Å -defineSymbol(text, textord, "\u00C5", "\\AA", true); -defineSymbol(math, textord, "\u00C6", "\\AE", true); // Æ -defineSymbol(text, textord, "\u00C6", "\\AE", true); -defineSymbol(math, textord, "\u00D0", "\\DH", true); // Ð -defineSymbol(text, textord, "\u00D0", "\\DH", true); -defineSymbol(math, textord, "\u00DE", "\\TH", true); // Þ -defineSymbol(text, textord, "\u00DE", "\\TH", true); -defineSymbol(math, textord, "\u00DF", "\\ss", true); // ß -defineSymbol(text, textord, "\u00DF", "\\ss", true); -defineSymbol(math, textord, "\u00E5", "\\aa"); // å -defineSymbol(text, textord, "\u00E5", "\\aa", true); -defineSymbol(math, textord, "\u00E6", "\\ae", true); // æ -defineSymbol(text, textord, "\u00E6", "\\ae", true); -defineSymbol(math, textord, "\u00F0", "\\dh"); // ð -defineSymbol(text, textord, "\u00F0", "\\dh", true); -defineSymbol(math, textord, "\u00FE", "\\th", true); // þ -defineSymbol(text, textord, "\u00FE", "\\th", true); -defineSymbol(math, textord, "\u0110", "\\DJ", true); // Đ -defineSymbol(text, textord, "\u0110", "\\DJ", true); -defineSymbol(math, textord, "\u0111", "\\dj", true); // đ -defineSymbol(text, textord, "\u0111", "\\dj", true); -defineSymbol(math, textord, "\u0141", "\\L", true); // Ł -defineSymbol(text, textord, "\u0141", "\\L", true); -defineSymbol(math, textord, "\u0141", "\\l", true); // ł -defineSymbol(text, textord, "\u0141", "\\l", true); -defineSymbol(math, textord, "\u014A", "\\NG", true); // Ŋ -defineSymbol(text, textord, "\u014A", "\\NG", true); -defineSymbol(math, textord, "\u014B", "\\ng", true); // ŋ -defineSymbol(text, textord, "\u014B", "\\ng", true); -defineSymbol(math, textord, "\u0152", "\\OE", true); // Œ -defineSymbol(text, textord, "\u0152", "\\OE", true); -defineSymbol(math, textord, "\u0153", "\\oe", true); // œ -defineSymbol(text, textord, "\u0153", "\\oe", true); - -defineSymbol(math, bin, "\u2217", "\u2217", true); -defineSymbol(math, bin, "+", "+"); -defineSymbol(math, bin, "\u2217", "*"); -defineSymbol(math, bin, "\u2044", "/", true); -defineSymbol(math, bin, "\u2044", "\u2044"); -defineSymbol(math, bin, "\u2212", "-", true); -defineSymbol(math, bin, "\u22c5", "\\cdot", true); -defineSymbol(math, bin, "\u2218", "\\circ", true); -defineSymbol(math, bin, "\u00f7", "\\div", true); -defineSymbol(math, bin, "\u00b1", "\\pm", true); -defineSymbol(math, bin, "\u00d7", "\\times", true); -defineSymbol(math, bin, "\u2229", "\\cap", true); -defineSymbol(math, bin, "\u222a", "\\cup", true); -defineSymbol(math, bin, "\u2216", "\\setminus", true); -defineSymbol(math, bin, "\u2227", "\\land"); -defineSymbol(math, bin, "\u2228", "\\lor"); -defineSymbol(math, bin, "\u2227", "\\wedge", true); -defineSymbol(math, bin, "\u2228", "\\vee", true); -defineSymbol(math, open, "\u27e6", "\\llbracket", true); // stmaryrd/semantic packages -defineSymbol(math, close, "\u27e7", "\\rrbracket", true); -defineSymbol(math, open, "\u27e8", "\\langle", true); -defineSymbol(math, open, "\u27ea", "\\lAngle", true); -defineSymbol(math, open, "\u2989", "\\llangle", true); -defineSymbol(math, open, "|", "\\lvert"); -defineSymbol(math, open, "\u2016", "\\lVert", true); -defineSymbol(math, textord, "!", "\\oc"); // cmll package -defineSymbol(math, textord, "?", "\\wn"); -defineSymbol(math, textord, "\u2193", "\\shpos"); -defineSymbol(math, textord, "\u2195", "\\shift"); -defineSymbol(math, textord, "\u2191", "\\shneg"); -defineSymbol(math, close, "?", "?"); -defineSymbol(math, close, "!", "!"); -defineSymbol(math, close, "‼", "‼"); -defineSymbol(math, close, "\u27e9", "\\rangle", true); -defineSymbol(math, close, "\u27eb", "\\rAngle", true); -defineSymbol(math, close, "\u298a", "\\rrangle", true); -defineSymbol(math, close, "|", "\\rvert"); -defineSymbol(math, close, "\u2016", "\\rVert"); -defineSymbol(math, open, "\u2983", "\\lBrace", true); // stmaryrd/semantic packages -defineSymbol(math, close, "\u2984", "\\rBrace", true); -defineSymbol(math, rel, "=", "\\equal", true); -defineSymbol(math, rel, ":", ":"); -defineSymbol(math, rel, "\u2248", "\\approx", true); -defineSymbol(math, rel, "\u2245", "\\cong", true); -defineSymbol(math, rel, "\u2265", "\\ge"); -defineSymbol(math, rel, "\u2265", "\\geq", true); -defineSymbol(math, rel, "\u2190", "\\gets"); -defineSymbol(math, rel, ">", "\\gt", true); -defineSymbol(math, rel, "\u2208", "\\in", true); -defineSymbol(math, rel, "\u2209", "\\notin", true); -defineSymbol(math, rel, "\ue020", "\\@not"); -defineSymbol(math, rel, "\u2282", "\\subset", true); -defineSymbol(math, rel, "\u2283", "\\supset", true); -defineSymbol(math, rel, "\u2286", "\\subseteq", true); -defineSymbol(math, rel, "\u2287", "\\supseteq", true); -defineSymbol(math, rel, "\u2288", "\\nsubseteq", true); -defineSymbol(math, rel, "\u2288", "\\nsubseteqq"); -defineSymbol(math, rel, "\u2289", "\\nsupseteq", true); -defineSymbol(math, rel, "\u2289", "\\nsupseteqq"); -defineSymbol(math, rel, "\u22a8", "\\models"); -defineSymbol(math, rel, "\u2190", "\\leftarrow", true); -defineSymbol(math, rel, "\u2264", "\\le"); -defineSymbol(math, rel, "\u2264", "\\leq", true); -defineSymbol(math, rel, "<", "\\lt", true); -defineSymbol(math, rel, "\u2192", "\\rightarrow", true); -defineSymbol(math, rel, "\u2192", "\\to"); -defineSymbol(math, rel, "\u2271", "\\ngeq", true); -defineSymbol(math, rel, "\u2271", "\\ngeqq"); -defineSymbol(math, rel, "\u2271", "\\ngeqslant"); -defineSymbol(math, rel, "\u2270", "\\nleq", true); -defineSymbol(math, rel, "\u2270", "\\nleqq"); -defineSymbol(math, rel, "\u2270", "\\nleqslant"); -defineSymbol(math, rel, "\u2aeb", "\\Perp", true); //cmll package -defineSymbol(math, spacing, "\u00a0", "\\ "); -defineSymbol(math, spacing, "\u00a0", "\\space"); -// Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{% -defineSymbol(math, spacing, "\u00a0", "\\nobreakspace"); -defineSymbol(text, spacing, "\u00a0", "\\ "); -defineSymbol(text, spacing, "\u00a0", " "); -defineSymbol(text, spacing, "\u00a0", "\\space"); -defineSymbol(text, spacing, "\u00a0", "\\nobreakspace"); -defineSymbol(math, spacing, null, "\\nobreak"); -defineSymbol(math, spacing, null, "\\allowbreak"); -defineSymbol(math, punct, ",", ","); -defineSymbol(text, punct, ":", ":"); -defineSymbol(math, punct, ";", ";"); -defineSymbol(math, bin, "\u22bc", "\\barwedge"); -defineSymbol(math, bin, "\u22bb", "\\veebar"); -defineSymbol(math, bin, "\u2299", "\\odot", true); -// Firefox turns ⊕ into an emoji. So append \uFE0E. Define Unicode character in macros, not here. -defineSymbol(math, bin, "\u2295\uFE0E", "\\oplus"); -defineSymbol(math, bin, "\u2297", "\\otimes", true); -defineSymbol(math, textord, "\u2202", "\\partial", true); -defineSymbol(math, bin, "\u2298", "\\oslash", true); -defineSymbol(math, bin, "\u229a", "\\circledcirc", true); -defineSymbol(math, bin, "\u22a1", "\\boxdot", true); -defineSymbol(math, bin, "\u25b3", "\\bigtriangleup"); -defineSymbol(math, bin, "\u25bd", "\\bigtriangledown"); -defineSymbol(math, bin, "\u2020", "\\dagger"); -defineSymbol(math, bin, "\u22c4", "\\diamond"); -defineSymbol(math, bin, "\u25c3", "\\triangleleft"); -defineSymbol(math, bin, "\u25b9", "\\triangleright"); -defineSymbol(math, open, "{", "\\{"); -defineSymbol(text, textord, "{", "\\{"); -defineSymbol(text, textord, "{", "\\textbraceleft"); -defineSymbol(math, close, "}", "\\}"); -defineSymbol(text, textord, "}", "\\}"); -defineSymbol(text, textord, "}", "\\textbraceright"); -defineSymbol(math, open, "{", "\\lbrace"); -defineSymbol(math, close, "}", "\\rbrace"); -defineSymbol(math, open, "[", "\\lbrack", true); -defineSymbol(text, textord, "[", "\\lbrack", true); -defineSymbol(math, close, "]", "\\rbrack", true); -defineSymbol(text, textord, "]", "\\rbrack", true); -defineSymbol(math, open, "(", "\\lparen", true); -defineSymbol(math, close, ")", "\\rparen", true); -defineSymbol(math, open, "⦇", "\\llparenthesis", true); -defineSymbol(math, close, "⦈", "\\rrparenthesis", true); -defineSymbol(text, textord, "<", "\\textless", true); // in T1 fontenc -defineSymbol(text, textord, ">", "\\textgreater", true); // in T1 fontenc -defineSymbol(math, open, "\u230a", "\\lfloor", true); -defineSymbol(math, close, "\u230b", "\\rfloor", true); -defineSymbol(math, open, "\u2308", "\\lceil", true); -defineSymbol(math, close, "\u2309", "\\rceil", true); -defineSymbol(math, textord, "\\", "\\backslash"); -defineSymbol(math, textord, "|", "|"); -defineSymbol(math, textord, "|", "\\vert"); -defineSymbol(text, textord, "|", "\\textbar", true); // in T1 fontenc -defineSymbol(math, textord, "\u2016", "\\|"); -defineSymbol(math, textord, "\u2016", "\\Vert"); -defineSymbol(text, textord, "\u2016", "\\textbardbl"); -defineSymbol(text, textord, "~", "\\textasciitilde"); -defineSymbol(text, textord, "\\", "\\textbackslash"); -defineSymbol(text, textord, "^", "\\textasciicircum"); -defineSymbol(math, rel, "\u2191", "\\uparrow", true); -defineSymbol(math, rel, "\u21d1", "\\Uparrow", true); -defineSymbol(math, rel, "\u2193", "\\downarrow", true); -defineSymbol(math, rel, "\u21d3", "\\Downarrow", true); -defineSymbol(math, rel, "\u2195", "\\updownarrow", true); -defineSymbol(math, rel, "\u21d5", "\\Updownarrow", true); -defineSymbol(math, op, "\u2210", "\\coprod"); -defineSymbol(math, op, "\u22c1", "\\bigvee"); -defineSymbol(math, op, "\u22c0", "\\bigwedge"); -defineSymbol(math, op, "\u2a04", "\\biguplus"); -defineSymbol(math, op, "\u2a04", "\\bigcupplus"); -defineSymbol(math, op, "\u2a03", "\\bigcupdot"); -defineSymbol(math, op, "\u2a07", "\\bigdoublevee"); -defineSymbol(math, op, "\u2a08", "\\bigdoublewedge"); -defineSymbol(math, op, "\u22c2", "\\bigcap"); -defineSymbol(math, op, "\u22c3", "\\bigcup"); -defineSymbol(math, op, "\u222b", "\\int"); -defineSymbol(math, op, "\u222b", "\\intop"); -defineSymbol(math, op, "\u222c", "\\iint"); -defineSymbol(math, op, "\u222d", "\\iiint"); -defineSymbol(math, op, "\u220f", "\\prod"); -defineSymbol(math, op, "\u2211", "\\sum"); -defineSymbol(math, op, "\u2a02", "\\bigotimes"); -defineSymbol(math, op, "\u2a01", "\\bigoplus"); -defineSymbol(math, op, "\u2a00", "\\bigodot"); -defineSymbol(math, op, "\u2a09", "\\bigtimes"); -defineSymbol(math, op, "\u222e", "\\oint"); -defineSymbol(math, op, "\u222f", "\\oiint"); -defineSymbol(math, op, "\u2230", "\\oiiint"); -defineSymbol(math, op, "\u2231", "\\intclockwise"); -defineSymbol(math, op, "\u2232", "\\varointclockwise"); -defineSymbol(math, op, "\u2a0c", "\\iiiint"); -defineSymbol(math, op, "\u2a0d", "\\intbar"); -defineSymbol(math, op, "\u2a0e", "\\intBar"); -defineSymbol(math, op, "\u2a0f", "\\fint"); -defineSymbol(math, op, "\u2a12", "\\rppolint"); -defineSymbol(math, op, "\u2a13", "\\scpolint"); -defineSymbol(math, op, "\u2a15", "\\pointint"); -defineSymbol(math, op, "\u2a16", "\\sqint"); -defineSymbol(math, op, "\u2a17", "\\intlarhk"); -defineSymbol(math, op, "\u2a18", "\\intx"); -defineSymbol(math, op, "\u2a19", "\\intcap"); -defineSymbol(math, op, "\u2a1a", "\\intcup"); -defineSymbol(math, op, "\u2a05", "\\bigsqcap"); -defineSymbol(math, op, "\u2a06", "\\bigsqcup"); -defineSymbol(math, op, "\u222b", "\\smallint"); -defineSymbol(text, inner, "\u2026", "\\textellipsis"); -defineSymbol(math, inner, "\u2026", "\\mathellipsis"); -defineSymbol(text, inner, "\u2026", "\\ldots", true); -defineSymbol(math, inner, "\u2026", "\\ldots", true); -defineSymbol(math, inner, "\u22f0", "\\iddots", true); -defineSymbol(math, inner, "\u22ef", "\\@cdots", true); -defineSymbol(math, inner, "\u22f1", "\\ddots", true); -defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro -defineSymbol(text, textord, "\u22ee", "\\varvdots"); -defineSymbol(math, accent, "\u00b4", "\\acute"); -defineSymbol(math, accent, "\u0060", "\\grave"); -defineSymbol(math, accent, "\u00a8", "\\ddot"); -defineSymbol(math, accent, "\u2026", "\\dddot"); -defineSymbol(math, accent, "\u2026\u002e", "\\ddddot"); -defineSymbol(math, accent, "\u007e", "\\tilde"); -defineSymbol(math, accent, "\u203e", "\\bar"); -defineSymbol(math, accent, "\u02d8", "\\breve"); -defineSymbol(math, accent, "\u02c7", "\\check"); -defineSymbol(math, accent, "\u005e", "\\hat"); -defineSymbol(math, accent, "\u2192", "\\vec"); -defineSymbol(math, accent, "\u02d9", "\\dot"); -defineSymbol(math, accent, "\u02da", "\\mathring"); -defineSymbol(math, mathord, "\u0131", "\\imath", true); -defineSymbol(math, mathord, "\u0237", "\\jmath", true); -defineSymbol(math, textord, "\u0131", "\u0131"); -defineSymbol(math, textord, "\u0237", "\u0237"); -defineSymbol(text, textord, "\u0131", "\\i", true); -defineSymbol(text, textord, "\u0237", "\\j", true); -defineSymbol(text, textord, "\u00f8", "\\o", true); -defineSymbol(math, mathord, "\u00f8", "\\o", true); -defineSymbol(text, textord, "\u00d8", "\\O", true); -defineSymbol(math, mathord, "\u00d8", "\\O", true); -defineSymbol(text, accent, "\u02ca", "\\'"); // acute -defineSymbol(text, accent, "\u02cb", "\\`"); // grave -defineSymbol(text, accent, "\u02c6", "\\^"); // circumflex -defineSymbol(text, accent, "\u007e", "\\~"); // tilde -defineSymbol(text, accent, "\u02c9", "\\="); // macron -defineSymbol(text, accent, "\u02d8", "\\u"); // breve -defineSymbol(text, accent, "\u02d9", "\\."); // dot above -defineSymbol(text, accent, "\u00b8", "\\c"); // cedilla -defineSymbol(text, accent, "\u02da", "\\r"); // ring above -defineSymbol(text, accent, "\u02c7", "\\v"); // caron -defineSymbol(text, accent, "\u00a8", '\\"'); // diaeresis -defineSymbol(text, accent, "\u02dd", "\\H"); // double acute -defineSymbol(math, accent, "\u02ca", "\\'"); // acute -defineSymbol(math, accent, "\u02cb", "\\`"); // grave -defineSymbol(math, accent, "\u02c6", "\\^"); // circumflex -defineSymbol(math, accent, "\u007e", "\\~"); // tilde -defineSymbol(math, accent, "\u02c9", "\\="); // macron -defineSymbol(math, accent, "\u02d8", "\\u"); // breve -defineSymbol(math, accent, "\u02d9", "\\."); // dot above -defineSymbol(math, accent, "\u00b8", "\\c"); // cedilla -defineSymbol(math, accent, "\u02da", "\\r"); // ring above -defineSymbol(math, accent, "\u02c7", "\\v"); // caron -defineSymbol(math, accent, "\u00a8", '\\"'); // diaeresis -defineSymbol(math, accent, "\u02dd", "\\H"); // double acute - -// These ligatures are detected and created in Parser.js's `formLigatures`. -const ligatures = { - "--": true, - "---": true, - "``": true, - "''": true -}; - -defineSymbol(text, textord, "\u2013", "--", true); -defineSymbol(text, textord, "\u2013", "\\textendash"); -defineSymbol(text, textord, "\u2014", "---", true); -defineSymbol(text, textord, "\u2014", "\\textemdash"); -defineSymbol(text, textord, "\u2018", "`", true); -defineSymbol(text, textord, "\u2018", "\\textquoteleft"); -defineSymbol(text, textord, "\u2019", "'", true); -defineSymbol(text, textord, "\u2019", "\\textquoteright"); -defineSymbol(text, textord, "\u201c", "``", true); -defineSymbol(text, textord, "\u201c", "\\textquotedblleft"); -defineSymbol(text, textord, "\u201d", "''", true); -defineSymbol(text, textord, "\u201d", "\\textquotedblright"); -// \degree from gensymb package -defineSymbol(math, textord, "\u00b0", "\\degree", true); -defineSymbol(text, textord, "\u00b0", "\\degree"); -// \textdegree from inputenc package -defineSymbol(text, textord, "\u00b0", "\\textdegree", true); -// TODO: In LaTeX, \pounds can generate a different character in text and math -// mode, but among our fonts, only Main-Regular defines this character "163". -defineSymbol(math, textord, "\u00a3", "\\pounds"); -defineSymbol(math, textord, "\u00a3", "\\mathsterling", true); -defineSymbol(text, textord, "\u00a3", "\\pounds"); -defineSymbol(text, textord, "\u00a3", "\\textsterling", true); -defineSymbol(math, textord, "\u2720", "\\maltese"); -defineSymbol(text, textord, "\u2720", "\\maltese"); -defineSymbol(math, textord, "\u20ac", "\\euro", true); -defineSymbol(text, textord, "\u20ac", "\\euro", true); -defineSymbol(text, textord, "\u20ac", "\\texteuro"); -defineSymbol(math, textord, "\u00a9", "\\copyright", true); -defineSymbol(text, textord, "\u00a9", "\\textcopyright"); -defineSymbol(math, textord, "\u2300", "\\diameter", true); -defineSymbol(text, textord, "\u2300", "\\diameter"); - -// Italic Greek -defineSymbol(math, textord, "𝛤", "\\varGamma"); -defineSymbol(math, textord, "𝛥", "\\varDelta"); -defineSymbol(math, textord, "𝛩", "\\varTheta"); -defineSymbol(math, textord, "𝛬", "\\varLambda"); -defineSymbol(math, textord, "𝛯", "\\varXi"); -defineSymbol(math, textord, "𝛱", "\\varPi"); -defineSymbol(math, textord, "𝛴", "\\varSigma"); -defineSymbol(math, textord, "𝛶", "\\varUpsilon"); -defineSymbol(math, textord, "𝛷", "\\varPhi"); -defineSymbol(math, textord, "𝛹", "\\varPsi"); -defineSymbol(math, textord, "𝛺", "\\varOmega"); -defineSymbol(text, textord, "𝛤", "\\varGamma"); -defineSymbol(text, textord, "𝛥", "\\varDelta"); -defineSymbol(text, textord, "𝛩", "\\varTheta"); -defineSymbol(text, textord, "𝛬", "\\varLambda"); -defineSymbol(text, textord, "𝛯", "\\varXi"); -defineSymbol(text, textord, "𝛱", "\\varPi"); -defineSymbol(text, textord, "𝛴", "\\varSigma"); -defineSymbol(text, textord, "𝛶", "\\varUpsilon"); -defineSymbol(text, textord, "𝛷", "\\varPhi"); -defineSymbol(text, textord, "𝛹", "\\varPsi"); -defineSymbol(text, textord, "𝛺", "\\varOmega"); - - -// There are lots of symbols which are the same, so we add them in afterwards. -// All of these are textords in math mode -const mathTextSymbols = '0123456789/@."'; -for (let i = 0; i < mathTextSymbols.length; i++) { - const ch = mathTextSymbols.charAt(i); - defineSymbol(math, textord, ch, ch); -} - -// All of these are textords in text mode -const textSymbols = '0123456789!@*()-=+";:?/.,'; -for (let i = 0; i < textSymbols.length; i++) { - const ch = textSymbols.charAt(i); - defineSymbol(text, textord, ch, ch); -} - -// All of these are textords in text mode, and mathords in math mode -const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -for (let i = 0; i < letters.length; i++) { - const ch = letters.charAt(i); - defineSymbol(math, mathord, ch, ch); - defineSymbol(text, textord, ch, ch); -} - -// Some more letters in Unicode Basic Multilingual Plane. -const narrow = "ÇÐÞçþℂℍℕℙℚℝℤℎℏℊℋℌℐℑℒℓ℘ℛℜℬℰℱℳℭℨ"; -for (let i = 0; i < narrow.length; i++) { - const ch = narrow.charAt(i); - defineSymbol(math, mathord, ch, ch); - defineSymbol(text, textord, ch, ch); -} - -// The next loop loads wide (surrogate pair) characters. -// We support some letters in the Unicode range U+1D400 to U+1D7FF, -// Mathematical Alphanumeric Symbols. -let wideChar = ""; -for (let i = 0; i < letters.length; i++) { - // The hex numbers in the next line are a surrogate pair. - // 0xD835 is the high surrogate for all letters in the range we support. - // 0xDC00 is the low surrogate for bold A. - wideChar = String.fromCharCode(0xd835, 0xdc00 + i); // A-Z a-z bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdc34 + i); // A-Z a-z italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdc68 + i); // A-Z a-z bold italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdd04 + i); // A-Z a-z Fractur - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdda0 + i); // A-Z a-z sans-serif - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xddd4 + i); // A-Z a-z sans bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xde08 + i); // A-Z a-z sans italic - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xde70 + i); // A-Z a-z monospace - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdd38 + i); // A-Z a-z double struck - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - const ch = letters.charAt(i); - wideChar = String.fromCharCode(0xd835, 0xdc9c + i); // A-Z a-z calligraphic - defineSymbol(math, mathord, ch, wideChar); - defineSymbol(text, textord, ch, wideChar); -} - -// Next, some wide character numerals -for (let i = 0; i < 10; i++) { - wideChar = String.fromCharCode(0xd835, 0xdfce + i); // 0-9 bold - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdfe2 + i); // 0-9 sans serif - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdfec + i); // 0-9 bold sans - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); - - wideChar = String.fromCharCode(0xd835, 0xdff6 + i); // 0-9 monospace - defineSymbol(math, mathord, wideChar, wideChar); - defineSymbol(text, textord, wideChar, wideChar); -} - -/* - * Neither Firefox nor Chrome support hard line breaks or soft line breaks. - * (Despite https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs) - * So Temml has work-arounds for both hard and soft breaks. - * The work-arounds sadly do not work simultaneously. Any top-level hard - * break makes soft line breaks impossible. - * - * Hard breaks are simulated by creating a and putting each line in its own . - * - * To create soft line breaks, Temml avoids using the and tags. - * Then the top level of a element can be occupied by elements, and the browser - * will break after a if the expression extends beyond the container limit. - * - * The default is for soft line breaks after each top-level binary or - * relational operator, per TeXbook p. 173. So we gather the expression into s so that - * each ends in a binary or relational operator. - * - * An option is for soft line breaks before an "=" sign. That changes the s. - * - * Soft line breaks will not work in Chromium and Safari, only Firefox. - * - * Hopefully browsers will someday do their own linebreaking and we will be able to delete - * much of this module. - */ - -function setLineBreaks(expression, wrapMode, isDisplayMode) { - const mtrs = []; - let mrows = []; - let block = []; - let numTopLevelEquals = 0; - let i = 0; - while (i < expression.length) { - while (expression[i] instanceof DocumentFragment) { - expression.splice(i, 1, ...expression[i].children); // Expand the fragment. - } - const node = expression[i]; - if (node.attributes && node.attributes.linebreak && - node.attributes.linebreak === "newline") { - // A hard line break. Create a for the current block. - if (block.length > 0) { - mrows.push(new MathNode("mrow", block)); - } - mrows.push(node); - block = []; - const mtd = new MathNode("mtd", mrows); - mtd.style.textAlign = "left"; - mtrs.push(new MathNode("mtr", [mtd])); - mrows = []; - i += 1; - continue - } - block.push(node); - if (node.type && node.type === "mo" && node.children.length === 1 && - !(node.attributes.form && node.attributes.form === "prefix") && // unary operators - !Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) { - const ch = node.children[0].text; - if (wrapMode === "=" && ch === "=") { - numTopLevelEquals += 1; - if (numTopLevelEquals > 1) { - block.pop(); - // Start a new block. (Insert a soft linebreak.) - const element = new MathNode("mrow", block); - mrows.push(element); - block = [node]; - } - } else if (wrapMode === "tex") { - // Check if the following node is a \nobreak text node, e.g. "~"" - const next = i < expression.length - 1 ? expression[i + 1] : null; - let glueIsFreeOfNobreak = true; - if ( - !( - next && - next.type === "mtext" && - next.attributes.linebreak && - next.attributes.linebreak === "nobreak" - ) - ) { - // We may need to start a new block. - // First, put any post-operator glue on same line as operator. - for (let j = i + 1; j < expression.length; j++) { - const nd = expression[j]; - if ( - nd.type && - nd.type === "mspace" && - !(nd.attributes.linebreak && nd.attributes.linebreak === "newline") - ) { - block.push(nd); - i += 1; - if ( - nd.attributes && - nd.attributes.linebreak && - nd.attributes.linebreak === "nobreak" - ) { - glueIsFreeOfNobreak = false; - } - } else { - break; - } - } - } - if (glueIsFreeOfNobreak) { - // Start a new block. (Insert a soft linebreak.) - const element = new MathNode("mrow", block); - mrows.push(element); - block = []; - } - } - } - i += 1; - } - if (block.length > 0) { - const element = new MathNode("mrow", block); - mrows.push(element); - } - if (mtrs.length > 0) { - const mtd = new MathNode("mtd", mrows); - mtd.style.textAlign = "left"; - const mtr = new MathNode("mtr", [mtd]); - mtrs.push(mtr); - const mtable = new MathNode("mtable", mtrs); - if (!isDisplayMode) { - mtable.setAttribute("columnalign", "left"); - mtable.setAttribute("rowspacing", "0em"); - } - return mtable - } - return newDocumentFragment(mrows); -} - -/** - * This file converts a parse tree into a corresponding MathML tree. The main - * entry point is the `buildMathML` function, which takes a parse tree from the - * parser. - */ - - -/** - * Takes a symbol and converts it into a MathML text node after performing - * optional replacement from symbols.js. - */ -const makeText = function(text, mode, style) { - if ( - symbols[mode][text] && - symbols[mode][text].replace && - text.charCodeAt(0) !== 0xd835 && - !( - Object.prototype.hasOwnProperty.call(ligatures, text) && - style && - ((style.fontFamily && style.fontFamily.slice(4, 6) === "tt") || - (style.font && style.font.slice(4, 6) === "tt")) - ) - ) { - text = symbols[mode][text].replace; - } - - return new TextNode(text); -}; - -const copyChar = (newRow, child) => { - if (newRow.children.length === 0 || - newRow.children[newRow.children.length - 1].type !== "mtext") { - const mtext = new MathNode( - "mtext", - [new TextNode(child.children[0].text)] - ); - newRow.children.push(mtext); - } else { - newRow.children[newRow.children.length - 1].children[0].text += child.children[0].text; - } -}; - -const consolidateText = mrow => { - // If possible, consolidate adjacent elements into a single element. - if (mrow.type !== "mrow" && mrow.type !== "mstyle") { return mrow } - if (mrow.children.length === 0) { return mrow } // empty group, e.g., \text{} - const newRow = new MathNode("mrow"); - for (let i = 0; i < mrow.children.length; i++) { - const child = mrow.children[i]; - if (child.type === "mtext" && Object.keys(child.attributes).length === 0) { - copyChar(newRow, child); - } else if (child.type === "mrow") { - // We'll also check the children of an mrow. One level only. No recursion. - let canConsolidate = true; - for (let j = 0; j < child.children.length; j++) { - const grandChild = child.children[j]; - if (grandChild.type !== "mtext" || Object.keys(child.attributes).length !== 0) { - canConsolidate = false; - break - } - } - if (canConsolidate) { - for (let j = 0; j < child.children.length; j++) { - const grandChild = child.children[j]; - copyChar(newRow, grandChild); - } - } else { - newRow.children.push(child); - } - } else { - newRow.children.push(child); - } - } - for (let i = 0; i < newRow.children.length; i++) { - if (newRow.children[i].type === "mtext") { - const mtext = newRow.children[i]; - // Firefox does not render a space at either end of an string. - // To get proper rendering, we replace leading or trailing spaces with no-break spaces. - if (mtext.children[0].text.charAt(0) === " ") { - mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1); - } - const L = mtext.children[0].text.length; - if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") { - mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0"; - } - for (const [key, value] of Object.entries(mrow.attributes)) { - mtext.attributes[key] = value; - } - } - } - if (newRow.children.length === 1 && newRow.children[0].type === "mtext") { - return newRow.children[0]; // A consolidated - } else { - return newRow - } -}; - -/** - * Wrap the given array of nodes in an node if needed, i.e., - * unless the array has length 1. Always returns a single node. - */ -const makeRow = function(body, semisimple = false) { - if (body.length === 1 && !(body[0] instanceof DocumentFragment)) { - return body[0]; - } else if (!semisimple) { - // Suppress spacing on nodes at both ends of the row. - if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) { - body[0].attributes.lspace = "0em"; - body[0].attributes.rspace = "0em"; - } - const end = body.length - 1; - if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) { - body[end].attributes.lspace = "0em"; - body[end].attributes.rspace = "0em"; - } - } - return new MathNode("mrow", body); -}; - -/** - * Check for . which is how a dot renders in MathML, - * or , - * which is how a braced comma {,} renders in MathML - */ -function isNumberPunctuation(group) { - if (!group) { - return false - } - if (group.type === 'mi' && group.children.length === 1) { - const child = group.children[0]; - return child instanceof TextNode && child.text === '.' - } else if (group.type === "mtext" && group.children.length === 1) { - const child = group.children[0]; - return child instanceof TextNode && child.text === '\u2008' // punctuation space - } else if (group.type === 'mo' && group.children.length === 1 && - group.getAttribute('separator') === 'true' && - group.getAttribute('lspace') === '0em' && - group.getAttribute('rspace') === '0em') { - const child = group.children[0]; - return child instanceof TextNode && child.text === ',' - } else { - return false - } -} -const isComma = (expression, i) => { - const node = expression[i]; - const followingNode = expression[i + 1]; - return (node.type === "atom" && node.text === ",") && - // Don't consolidate if there is a space after the comma. - node.loc && followingNode.loc && node.loc.end === followingNode.loc.start -}; - -const isRel = item => { - return (item.type === "atom" && item.family === "rel") || - (item.type === "mclass" && item.mclass === "mrel") -}; - -/** - * Takes a list of nodes, builds them, and returns a list of the generated - * MathML nodes. Also do a couple chores along the way: - * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}. - * (2) Suppress spacing between two adjacent relations. - */ -const buildExpression = function(expression, style, semisimple = false) { - if (!semisimple && expression.length === 1) { - const group = buildGroup$1(expression[0], style); - if (group instanceof MathNode && group.type === "mo") { - // When TeX writers want to suppress spacing on an operator, - // they often put the operator by itself inside braces. - group.setAttribute("lspace", "0em"); - group.setAttribute("rspace", "0em"); - } - return [group]; - } - - const groups = []; - const groupArray = []; - let lastGroup; - for (let i = 0; i < expression.length; i++) { - groupArray.push(buildGroup$1(expression[i], style)); - } - - for (let i = 0; i < groupArray.length; i++) { - const group = groupArray[i]; - - // Suppress spacing between adjacent relations - if (i < expression.length - 1 && isRel(expression[i]) && isRel(expression[i + 1])) { - group.setAttribute("rspace", "0em"); - } - if (i > 0 && isRel(expression[i]) && isRel(expression[i - 1])) { - group.setAttribute("lspace", "0em"); - } - - // Concatenate numbers - if (group.type === 'mn' && lastGroup && lastGroup.type === 'mn') { - // Concatenate ... followed by . - lastGroup.children.push(...group.children); - continue - } else if (isNumberPunctuation(group) && lastGroup && lastGroup.type === 'mn') { - // Concatenate ... followed by . - lastGroup.children.push(...group.children); - continue - } else if (lastGroup && lastGroup.type === "mn" && i < groupArray.length - 1 && - groupArray[i + 1].type === "mn" && isComma(expression, i)) { - lastGroup.children.push(...group.children); - continue - } else if (group.type === 'mn' && isNumberPunctuation(lastGroup)) { - // Concatenate . followed by ... - group.children = [...lastGroup.children, ...group.children]; - groups.pop(); - } else if ((group.type === 'msup' || group.type === 'msub') && - group.children.length >= 1 && lastGroup && - (lastGroup.type === 'mn' || isNumberPunctuation(lastGroup))) { - // Put preceding ... or . inside base of - // ...base......exponent... (or ) - const base = group.children[0]; - if (base instanceof MathNode && base.type === 'mn' && lastGroup) { - base.children = [...lastGroup.children, ...base.children]; - groups.pop(); - } - } - groups.push(group); - lastGroup = group; - } - return groups -}; - -/** - * Equivalent to buildExpression, but wraps the elements in an - * if there's more than one. Returns a single node instead of an array. - */ -const buildExpressionRow = function(expression, style, semisimple = false) { - return makeRow(buildExpression(expression, style, semisimple), semisimple); -}; - -/** - * Takes a group from the parser and calls the appropriate groupBuilders function - * on it to produce a MathML node. - */ -const buildGroup$1 = function(group, style) { - if (!group) { - return new MathNode("mrow"); - } - - if (_mathmlGroupBuilders[group.type]) { - // Call the groupBuilders function - const result = _mathmlGroupBuilders[group.type](group, style); - return result; - } else { - throw new ParseError("Got group of unknown type: '" + group.type + "'"); - } -}; - -const glue$1 = _ => { - return new MathNode("mtd", [], [], { padding: "0", width: "50%" }) -}; - -const labelContainers = ["mrow", "mtd", "mtable", "mtr"]; -const getLabel = parent => { - for (const node of parent.children) { - if (node.type && labelContainers.includes(node.type)) { - if (node.classes && node.classes[0] === "tml-label") { - const label = node.label; - return label - } else { - const label = getLabel(node); - if (label) { return label } - } - } else if (!node.type) { - const label = getLabel(node); - if (label) { return label } - } - } -}; - -const taggedExpression = (expression, tag, style, leqno) => { - tag = buildExpressionRow(tag[0].body, style); - tag = consolidateText(tag); // tag is now an element - tag.classes.push("tml-tag"); // to be available for \ref - - const label = getLabel(expression); // from a \label{} function. - expression = new MathNode("mtd", [expression]); - const rowArray = [glue$1(), expression, glue$1()]; - rowArray[leqno ? 0 : 2].children.push(tag); - const mtr = new MathNode("mtr", rowArray, ["tml-tageqn"]); - if (label) { mtr.setAttribute("id", label); } - const table = new MathNode("mtable", [mtr]); - table.style.width = "100%"; - table.setAttribute("displaystyle", "true"); - return table -}; - -/** - * Takes a full parse tree and settings and builds a MathML representation of - * it. - */ -function buildMathML(tree, texExpression, style, settings) { - // Strip off outer tag wrapper for processing below. - let tag = null; - if (tree.length === 1 && tree[0].type === "tag") { - tag = tree[0].tag; - tree = tree[0].body; - } - - const expression = buildExpression(tree, style); - - if (expression.length === 1 && expression[0] instanceof AnchorNode) { - return expression[0] - } - - const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap; - - const n1 = expression.length === 0 ? null : expression[0]; - let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode) - ? expression[0] - : setLineBreaks(expression, wrap, settings.displayMode); - - if (tag) { - wrapper = taggedExpression(wrapper, tag, style, settings.leqno); - } - - if (settings.annotate) { - // Build a TeX annotation of the source - const annotation = new MathNode( - "annotation", [new TextNode(texExpression)]); - annotation.setAttribute("encoding", "application/x-tex"); - wrapper = new MathNode("semantics", [wrapper, annotation]); - } - - const math = new MathNode("math", [wrapper]); - - if (settings.xml) { - math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML"); - } - if (settings.displayMode) { - math.setAttribute("display", "block"); - math.style.display = "block math"; // necessary in Chromium. - // Firefox and Safari do not recognize display: "block math". - // Set a class so that the CSS file can set display: block. - math.classes = ["tml-display"]; - } - return math; -} - -// From the KaTeX font metrics, identify letters whose accents need a italic correction. -const smallNudge = "DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ"; -const mediumNudge = "BCEGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ"; -const largeNudge = "AFJdfΔΛ"; - -const mathmlBuilder$a = (group, style) => { - const accentNode$1 = group.isStretchy - ? accentNode(group) - : new MathNode("mo", [makeText(group.label, group.mode)]); - if (!group.isStretchy) { - accentNode$1.setAttribute("stretchy", "false"); // Keep Firefox from stretching \check - } - if (group.label !== "\\vec") { - accentNode$1.style.mathDepth = "0"; // not scriptstyle - // Don't use attribute accent="true" because MathML Core eliminates a needed space. - } - const tag = group.label === "\\c" ? "munder" : "mover"; - const needsWbkVertShift = needsWebkitVerticalShift.has(group.label); - if (tag === "mover" && group.mode === "math" && (!group.isStretchy) && group.base.text - && group.base.text.length === 1) { - const text = group.base.text; - const isVec = group.label === "\\vec"; - const vecPostfix = isVec === "\\vec" ? "-vec" : ""; - if (isVec) { - accentNode$1.classes.push("tml-vec"); // Firefox sizing of \vec arrow - } - const wbkPostfix = isVec ? "-vec" : needsWbkVertShift ? "-acc" : ""; - if (smallNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-sml${vecPostfix}`); - accentNode$1.classes.push(`wbk-sml${wbkPostfix}`); - } else if (mediumNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-med${vecPostfix}`); - accentNode$1.classes.push(`wbk-med${wbkPostfix}`); - } else if (largeNudge.indexOf(text) > -1) { - accentNode$1.classes.push(`chr-lrg${vecPostfix}`); - accentNode$1.classes.push(`wbk-lrg${wbkPostfix}`); - } else if (isVec) { - accentNode$1.classes.push(`wbk-vec`); - } else if (needsWbkVertShift) { - accentNode$1.classes.push(`wbk-acc`); - } - } else if (needsWbkVertShift) { - // text-mode accents - accentNode$1.classes.push("wbk-acc"); - } - const node = new MathNode(tag, [buildGroup$1(group.base, style), accentNode$1]); - return node; -}; - -const nonStretchyAccents = new Set([ - "\\acute", - "\\check", - "\\grave", - "\\ddot", - "\\dddot", - "\\ddddot", - "\\tilde", - "\\bar", - "\\breve", - "\\check", - "\\hat", - "\\vec", - "\\dot", - "\\mathring" -]); - -const needsWebkitVerticalShift = new Set([ - "\\acute", - "\\bar", - "\\breve", - "\\check", - "\\dot", - "\\ddot", - "\\grave", - "\\hat", - "\\mathring", - "\\`", "\\'", "\\^", "\\=", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v" -]); - -const combiningChar = { - "\\`": "\u0300", - "\\'": "\u0301", - "\\^": "\u0302", - "\\~": "\u0303", - "\\=": "\u0304", - "\\u": "\u0306", - "\\.": "\u0307", - '\\"': "\u0308", - "\\r": "\u030A", - "\\H": "\u030B", - "\\v": "\u030C", - "\\c": "\u0327" -}; - -// Accents -defineFunction({ - type: "accent", - names: [ - "\\acute", - "\\grave", - "\\ddot", - "\\dddot", - "\\ddddot", - "\\tilde", - "\\bar", - "\\breve", - "\\check", - "\\hat", - "\\vec", - "\\dot", - "\\mathring", - "\\overparen", - "\\widecheck", - "\\widehat", - "\\wideparen", - "\\widetilde", - "\\overrightarrow", - "\\overleftarrow", - "\\Overrightarrow", - "\\overleftrightarrow", - "\\overgroup", - "\\overleftharpoon", - "\\overrightharpoon" - ], - props: { - numArgs: 1 - }, - handler: (context, args) => { - const base = normalizeArgument(args[0]); - - const isStretchy = !nonStretchyAccents.has(context.funcName); - - return { - type: "accent", - mode: context.parser.mode, - label: context.funcName, - isStretchy, - base - }; - }, - mathmlBuilder: mathmlBuilder$a -}); - -// Text-mode accents -defineFunction({ - type: "accent", - names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\c", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"], - props: { - numArgs: 1, - allowedInText: true, - allowedInMath: true, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const base = normalizeArgument(args[0]); - const mode = context.parser.mode; - - if (mode === "math" && context.parser.settings.strict) { - // LaTeX only writes a warning. It doesn't stop. We'll issue the same warning. - // eslint-disable-next-line no-console - console.log(`Temml parse error: Command ${context.funcName} is invalid in math mode.`); - } - - if (mode === "text" && base.text && base.text.length === 1 - && context.funcName in combiningChar && smalls.indexOf(base.text) > -1) { - // Return a combining accent character - return { - type: "textord", - mode: "text", - text: base.text + combiningChar[context.funcName] - } - } else if (context.funcName === "\\c" && mode === "text" && base.text - && base.text.length === 1) { - // combining cedilla - return { type: "textord", mode: "text", text: base.text + "\u0327" } - } else { - // Build up the accent - return { - type: "accent", - mode, - label: context.funcName, - isStretchy: false, - base - } - } - }, - mathmlBuilder: mathmlBuilder$a -}); - -defineFunction({ - type: "accentUnder", - names: [ - "\\underleftarrow", - "\\underrightarrow", - "\\underleftrightarrow", - "\\undergroup", - "\\underparen", - "\\utilde" - ], - props: { - numArgs: 1 - }, - handler: ({ parser, funcName }, args) => { - const base = args[0]; - return { - type: "accentUnder", - mode: parser.mode, - label: funcName, - base: base - }; - }, - mathmlBuilder: (group, style) => { - const accentNode$1 = accentNode(group); - accentNode$1.style["math-depth"] = 0; - const node = new MathNode("munder", [ - buildGroup$1(group.base, style), - accentNode$1 - ]); - return node; - } -}); - -/** - * This file does conversion between units. In particular, it provides - * calculateSize to convert other units into CSS units. - */ - - -const ptPerUnit = { - // Convert to CSS (Postscipt) points, not TeX points - // https://en.wikibooks.org/wiki/LaTeX/Lengths and - // https://tex.stackexchange.com/a/8263 - pt: 800 / 803, // convert TeX point to CSS (Postscript) point - pc: (12 * 800) / 803, // pica - dd: ((1238 / 1157) * 800) / 803, // didot - cc: ((14856 / 1157) * 800) / 803, // cicero (12 didot) - nd: ((685 / 642) * 800) / 803, // new didot - nc: ((1370 / 107) * 800) / 803, // new cicero (12 new didot) - sp: ((1 / 65536) * 800) / 803, // scaled point (TeX's internal smallest unit) - mm: (25.4 / 72), - cm: (2.54 / 72), - in: (1 / 72), - px: (96 / 72) -}; - -/** - * Determine whether the specified unit (either a string defining the unit - * or a "size" parse node containing a unit field) is valid. - */ -const validUnits = [ - "em", - "ex", - "mu", - "pt", - "mm", - "cm", - "in", - "px", - "bp", - "pc", - "dd", - "cc", - "nd", - "nc", - "sp" -]; - -const validUnit = function(unit) { - if (typeof unit !== "string") { - unit = unit.unit; - } - return validUnits.indexOf(unit) > -1 -}; - -const emScale = styleLevel => { - const scriptLevel = Math.max(styleLevel - 1, 0); - return [1, 0.7, 0.5][scriptLevel] -}; - -/* - * Convert a "size" parse node (with numeric "number" and string "unit" fields, - * as parsed by functions.js argType "size") into a CSS value. - */ -const calculateSize = function(sizeValue, style) { - let number = sizeValue.number; - if (style.maxSize[0] < 0 && number > 0) { - return { number: 0, unit: "em" } - } - const unit = sizeValue.unit; - switch (unit) { - case "mm": - case "cm": - case "in": - case "px": { - const numInCssPts = number * ptPerUnit[unit]; - if (numInCssPts > style.maxSize[1]) { - return { number: style.maxSize[1], unit: "pt" } - } - return { number, unit }; // absolute CSS units. - } - case "em": - case "ex": { - // In TeX, em and ex do not change size in \scriptstyle. - if (unit === "ex") { number *= 0.431; } - number = Math.min(number / emScale(style.level), style.maxSize[0]); - return { number: round(number), unit: "em" }; - } - case "bp": { - if (number > style.maxSize[1]) { number = style.maxSize[1]; } - return { number, unit: "pt" }; // TeX bp is a CSS pt. (1/72 inch). - } - case "pt": - case "pc": - case "dd": - case "cc": - case "nd": - case "nc": - case "sp": { - number = Math.min(number * ptPerUnit[unit], style.maxSize[1]); - return { number: round(number), unit: "pt" } - } - case "mu": { - number = Math.min(number / 18, style.maxSize[0]); - return { number: round(number), unit: "em" } - } - default: - throw new ParseError("Invalid unit: '" + unit + "'") - } -}; - -// Helper functions - -const padding = width => { - const node = new MathNode("mspace"); - node.setAttribute("width", width + "em"); - return node -}; - -const paddedNode = (group, lspace = 0.3, rspace = 0, mustSmash = false) => { - if (group == null && rspace === 0) { return padding(lspace) } - const row = group ? [group] : []; - if (lspace !== 0) { row.unshift(padding(lspace)); } - if (rspace > 0) { row.push(padding(rspace)); } - if (mustSmash) { - // Used for the bottom arrow in a {CD} environment - const mpadded = new MathNode("mpadded", row); - mpadded.setAttribute("height", "0.1px"); // Don't use 0. WebKit would hide it. - return mpadded - } else { - return new MathNode("mrow", row) - } -}; - -const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel); - -const munderoverNode = (fName, body, below, style) => { - const arrowNode = mathMLnode(fName); - // Is this the short part of a mhchem equilibrium arrow? - const isEq = fName.slice(1, 3) === "eq"; - const minWidth = fName.charAt(1) === "x" - ? "1.75" // mathtools extensible arrows are ≥ 1.75em long - : fName.slice(2, 4) === "cd" - ? "3.0" // cd package arrows - : isEq - ? "1.0" // The shorter harpoon of a mhchem equilibrium arrow - : "2.0"; // other mhchem arrows - // TODO: When Firefox supports minsize, use the next line. - //arrowNode.setAttribute("minsize", String(minWidth) + "em") - arrowNode.setAttribute("lspace", "0"); - arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0")); - - // upper and lower labels are set to scriptlevel by MathML - // So we have to adjust our label dimensions accordingly. - const labelStyle = style.withLevel(style.level < 2 ? 2 : 3); - const minArrowWidth = labelSize(minWidth, labelStyle.level); - // The dummyNode will be inside a inside a - // So it will be at scriptlevel 3 - const dummyWidth = labelSize(minWidth, 3); - const emptyLabel = paddedNode(null, minArrowWidth.toFixed(4), 0); - const dummyNode = paddedNode(null, dummyWidth.toFixed(4), 0); - // The arrow is a little longer than the label. Set a spacer length. - const space = labelSize((isEq ? 0 : 0.3), labelStyle.level).toFixed(4); - let upperNode; - let lowerNode; - - const gotUpper = (body && body.body && - // \hphantom visible content - (body.body.body || body.body.length > 0)); - if (gotUpper) { - let label = buildGroup$1(body, labelStyle); - const mustSmash = (fName === "\\\\cdrightarrow" || fName === "\\\\cdleftarrow"); - label = paddedNode(label, space, space, mustSmash); - // Since Firefox does not support minsize, stack a invisible node - // on top of the label. Its width will serve as a min-width. - // TODO: Refactor this after Firefox supports minsize. - upperNode = new MathNode("mover", [label, dummyNode]); - } - const gotLower = (below && below.body && - (below.body.body || below.body.length > 0)); - if (gotLower) { - let label = buildGroup$1(below, labelStyle); - label = paddedNode(label, space, space); - lowerNode = new MathNode("munder", [label, dummyNode]); - } - - let node; - if (!gotUpper && !gotLower) { - node = new MathNode("mover", [arrowNode, emptyLabel]); - } else if (gotUpper && gotLower) { - node = new MathNode("munderover", [arrowNode, lowerNode, upperNode]); - } else if (gotUpper) { - node = new MathNode("mover", [arrowNode, upperNode]); - } else { - node = new MathNode("munder", [arrowNode, lowerNode]); - } - if (minWidth === "3.0") { node.style.height = "1em"; } // CD environment - node.setAttribute("accent", "false"); // Necessary for MS Word - return node -}; - -// Stretchy arrows with an optional argument -defineFunction({ - type: "xArrow", - names: [ - "\\xleftarrow", - "\\xrightarrow", - "\\xLeftarrow", - "\\xRightarrow", - "\\xleftrightarrow", - "\\xLeftrightarrow", - "\\xhookleftarrow", - "\\xhookrightarrow", - "\\xmapsto", - "\\xrightharpoondown", - "\\xrightharpoonup", - "\\xleftharpoondown", - "\\xleftharpoonup", - "\\xlongequal", - "\\xtwoheadrightarrow", - "\\xtwoheadleftarrow", - "\\xtofrom", // expfeil - "\\xleftrightharpoons", // mathtools - "\\xrightleftharpoons", // mathtools - // The next 7 functions are here only to support mhchem - "\\yields", - "\\yieldsLeft", - "\\mesomerism", - "\\longrightharpoonup", - "\\longleftharpoondown", - "\\yieldsLeftRight", - "\\chemequilibrium", - // The next 3 functions are here only to support the {CD} environment. - "\\\\cdrightarrow", - "\\\\cdleftarrow", - "\\\\cdlongequal" - ], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser, funcName }, args, optArgs) { - return { - type: "xArrow", - mode: parser.mode, - name: funcName, - body: args[0], - below: optArgs[0] - }; - }, - mathmlBuilder(group, style) { - // Build the arrow and its labels. - const node = munderoverNode(group.name, group.body, group.below, style); - // Create operator spacing for a relation. - const row = [node]; - row.unshift(padding(0.2778)); - row.push(padding(0.2778)); - return new MathNode("mrow", row) - } -}); - -const arrowComponent = { - "\\equilibriumRight": ["\\longrightharpoonup", "\\eqleftharpoondown"], - "\\equilibriumLeft": ["\\eqrightharpoonup", "\\longleftharpoondown"] -}; - -// Math fonts do not have a single glyph for these two mhchem functions. -// So we stack a pair of single harpoons. -defineFunction({ - type: "stackedArrow", - names: [ - "\\equilibriumRight", - "\\equilibriumLeft" - ], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser, funcName }, args, optArgs) { - const lowerArrowBody = args[0] - ? { - type: "hphantom", - mode: parser.mode, - body: args[0] - } - : null; - const upperArrowBelow = optArgs[0] - ? { - type: "hphantom", - mode: parser.mode, - body: optArgs[0] - } - : null; - return { - type: "stackedArrow", - mode: parser.mode, - name: funcName, - body: args[0], - upperArrowBelow, - lowerArrowBody, - below: optArgs[0] - }; - }, - mathmlBuilder(group, style) { - const topLabel = arrowComponent[group.name][0]; - const botLabel = arrowComponent[group.name][1]; - const topArrow = munderoverNode(topLabel, group.body, group.upperArrowBelow, style); - const botArrow = munderoverNode(botLabel, group.lowerArrowBody, group.below, style); - let wrapper; - - const raiseNode = new MathNode("mpadded", [topArrow]); - raiseNode.setAttribute("voffset", "0.3em"); - raiseNode.setAttribute("height", "+0.3em"); - raiseNode.setAttribute("depth", "-0.3em"); - // One of the arrows is given ~zero width. so the other has the same horzontal alignment. - if (group.name === "\\equilibriumLeft") { - const botNode = new MathNode("mpadded", [botArrow]); - botNode.setAttribute("width", "0.5em"); - wrapper = new MathNode( - "mpadded", - [padding(0.2778), botNode, raiseNode, padding(0.2778)] - ); - } else { - raiseNode.setAttribute("width", (group.name === "\\equilibriumRight" ? "0.5em" : "0")); - wrapper = new MathNode( - "mpadded", - [padding(0.2778), raiseNode, botArrow, padding(0.2778)] - ); - } - - wrapper.setAttribute("voffset", "-0.18em"); - wrapper.setAttribute("height", "-0.18em"); - wrapper.setAttribute("depth", "+0.18em"); - return wrapper - } -}); - -/** - * All registered environments. - * `environments.js` exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary via `environments.js`. - */ -const _environments = {}; - -function defineEnvironment({ type, names, props, handler, mathmlBuilder }) { - // Set default values of environments. - const data = { - type, - numArgs: props.numArgs || 0, - allowedInText: false, - numOptionalArgs: 0, - handler - }; - for (let i = 0; i < names.length; ++i) { - _environments[names[i]] = data; - } - if (mathmlBuilder) { - _mathmlGroupBuilders[type] = mathmlBuilder; - } -} - -/** - * Asserts that the node is of the given type and returns it with stricter - * typing. Throws if the node's type does not match. - */ -function assertNodeType(node, type) { - if (!node || node.type !== type) { - throw new Error( - `Expected node of type ${type}, but got ` + - (node ? `node of type ${node.type}` : String(node)) - ); - } - return node; -} - -/** - * Returns the node more strictly typed iff it is of the given type. Otherwise, - * returns null. - */ -function assertSymbolNodeType(node) { - const typedNode = checkSymbolNodeType(node); - if (!typedNode) { - throw new Error( - `Expected node of symbol group type, but got ` + - (node ? `node of type ${node.type}` : String(node)) - ); - } - return typedNode; -} - -/** - * Returns the node more strictly typed iff it is of the given type. Otherwise, - * returns null. - */ -function checkSymbolNodeType(node) { - if (node && (node.type === "atom" || node.type === "delimiter" || - Object.prototype.hasOwnProperty.call(NON_ATOMS, node.type))) { - return node; - } - return null; -} - -const cdArrowFunctionName = { - ">": "\\\\cdrightarrow", - "<": "\\\\cdleftarrow", - "=": "\\\\cdlongequal", - A: "\\uparrow", - V: "\\downarrow", - "|": "\\Vert", - ".": "no arrow" -}; - -const newCell = () => { - // Create an empty cell, to be filled below with parse nodes. - return { type: "styling", body: [], mode: "math", scriptLevel: "display" }; -}; - -const isStartOfArrow = (node) => { - return node.type === "textord" && node.text === "@"; -}; - -const isLabelEnd = (node, endChar) => { - return (node.type === "mathord" || node.type === "atom") && node.text === endChar; -}; - -function cdArrow(arrowChar, labels, parser) { - // Return a parse tree of an arrow and its labels. - // This acts in a way similar to a macro expansion. - const funcName = cdArrowFunctionName[arrowChar]; - switch (funcName) { - case "\\\\cdrightarrow": - case "\\\\cdleftarrow": - return parser.callFunction(funcName, [labels[0]], [labels[1]]); - case "\\uparrow": - case "\\downarrow": { - const leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []); - const bareArrow = { - type: "atom", - text: funcName, - mode: "math", - family: "rel" - }; - const sizedArrow = parser.callFunction("\\Big", [bareArrow], []); - const rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []); - const arrowGroup = { - type: "ordgroup", - mode: "math", - body: [leftLabel, sizedArrow, rightLabel], - semisimple: true - }; - return parser.callFunction("\\\\cdparent", [arrowGroup], []); - } - case "\\\\cdlongequal": - return parser.callFunction("\\\\cdlongequal", [], []); - case "\\Vert": { - const arrow = { type: "textord", text: "\\Vert", mode: "math" }; - return parser.callFunction("\\Big", [arrow], []); - } - default: - return { type: "textord", text: " ", mode: "math" }; - } -} - -function parseCD(parser) { - // Get the array's parse nodes with \\ temporarily mapped to \cr. - const parsedRows = []; - parser.gullet.beginGroup(); - parser.gullet.macros.set("\\cr", "\\\\\\relax"); - parser.gullet.beginGroup(); - while (true) { - // Get the parse nodes for the next row. - parsedRows.push(parser.parseExpression(false, "\\\\")); - parser.gullet.endGroup(); - parser.gullet.beginGroup(); - const next = parser.fetch().text; - if (next === "&" || next === "\\\\") { - parser.consume(); - } else if (next === "\\end") { - if (parsedRows[parsedRows.length - 1].length === 0) { - parsedRows.pop(); // final row ended in \\ - } - break; - } else { - throw new ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken); - } - } - - let row = []; - const body = [row]; - - // Loop thru the parse nodes. Collect them into cells and arrows. - for (let i = 0; i < parsedRows.length; i++) { - // Start a new row. - const rowNodes = parsedRows[i]; - // Create the first cell. - let cell = newCell(); - - for (let j = 0; j < rowNodes.length; j++) { - if (!isStartOfArrow(rowNodes[j])) { - // If a parseNode is not an arrow, it goes into a cell. - cell.body.push(rowNodes[j]); - } else { - // Parse node j is an "@", the start of an arrow. - // Before starting on the arrow, push the cell into `row`. - row.push(cell); - - // Now collect parseNodes into an arrow. - // The character after "@" defines the arrow type. - j += 1; - const arrowChar = assertSymbolNodeType(rowNodes[j]).text; - - // Create two empty label nodes. We may or may not use them. - const labels = new Array(2); - labels[0] = { type: "ordgroup", mode: "math", body: [] }; - labels[1] = { type: "ordgroup", mode: "math", body: [] }; - - // Process the arrow. - if ("=|.".indexOf(arrowChar) > -1) ; else if ("<>AV".indexOf(arrowChar) > -1) { - // Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take - // two optional labels. E.g. the right-point arrow syntax is - // really: @>{optional label}>{optional label}> - // Collect parseNodes into labels. - for (let labelNum = 0; labelNum < 2; labelNum++) { - let inLabel = true; - for (let k = j + 1; k < rowNodes.length; k++) { - if (isLabelEnd(rowNodes[k], arrowChar)) { - inLabel = false; - j = k; - break; - } - if (isStartOfArrow(rowNodes[k])) { - throw new ParseError( - "Missing a " + arrowChar + " character to complete a CD arrow.", - rowNodes[k] - ); - } - - labels[labelNum].body.push(rowNodes[k]); - } - if (inLabel) { - // isLabelEnd never returned a true. - throw new ParseError( - "Missing a " + arrowChar + " character to complete a CD arrow.", - rowNodes[j] - ); - } - } - } else { - throw new ParseError(`Expected one of "<>AV=|." after @.`); - } - - // Now join the arrow to its labels. - const arrow = cdArrow(arrowChar, labels, parser); - - // Wrap the arrow in a styling node - row.push(arrow); - // In CD's syntax, cells are implicit. That is, everything that - // is not an arrow gets collected into a cell. So create an empty - // cell now. It will collect upcoming parseNodes. - cell = newCell(); - } - } - if (i % 2 === 0) { - // Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell - // The last cell is not yet pushed into `row`, so: - row.push(cell); - } else { - // Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow - // Remove the empty cell that was placed at the beginning of `row`. - row.shift(); - } - row = []; - body.push(row); - } - body.pop(); - - // End row group - parser.gullet.endGroup(); - // End array group defining \\ - parser.gullet.endGroup(); - - return { - type: "array", - mode: "math", - body, - tags: null, - labels: new Array(body.length + 1).fill(""), - envClasses: ["jot", "cd"], - cols: [], - hLinesBeforeRow: new Array(body.length + 1).fill([]) - }; -} - -// The functions below are not available for general use. -// They are here only for internal use by the {CD} environment in placing labels -// next to vertical arrows. - -// We don't need any such functions for horizontal arrows because we can reuse -// the functionality that already exists for extensible arrows. - -defineFunction({ - type: "cdlabel", - names: ["\\\\cdleft", "\\\\cdright"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - return { - type: "cdlabel", - mode: parser.mode, - side: funcName.slice(4), - label: args[0] - }; - }, - mathmlBuilder(group, style) { - if (group.label.body.length === 0) { - return new MathNode("mrow", style) // empty label - } - // Abuse an to create vertically centered content. - const mrow = buildGroup$1(group.label, style); - if (group.side === "left") { - mrow.classes.push("tml-shift-left"); - } - const mtd = new MathNode("mtd", [mrow]); - mtd.style.padding = "0"; - const mtr = new MathNode("mtr", [mtd]); - const mtable = new MathNode("mtable", [mtr]); - const label = new MathNode("mpadded", [mtable]); - // Set the label width to zero so that the arrow will be centered under the corner cell. - label.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - label.setAttribute("displaystyle", "false"); - label.setAttribute("scriptlevel", "1"); - return label; - } -}); - -defineFunction({ - type: "cdlabelparent", - names: ["\\\\cdparent"], - props: { - numArgs: 1 - }, - handler({ parser }, args) { - return { - type: "cdlabelparent", - mode: parser.mode, - fragment: args[0] - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow", [buildGroup$1(group.fragment, style)]); - } -}); - -const ordGroup = (body) => { - return { - "type": "ordgroup", - "mode": "math", - "body": body, - "semisimple": true - } -}; - -const phantom = (body, type) => { - return { - "type": type, - "mode": "math", - "body": ordGroup(body) - } -}; - -/* - * A helper for \bordermatrix. - * parseArray() has parsed the tokens as if the environment - * was \begin{matrix}. That parse tree is this function’s input. - * Here, we rearrange the parse tree to get one that will - * result in TeX \bordermatrix. - * The final result includes a {pmatrix}, which is the bottom - * half of a element. The top of the contains - * the \bordermatrix headings. The top section also contains the - * contents of the bottom {pmatrix}. Those elements are hidden via - * \hphantom, but they ensure that column widths are the same top and - * bottom. - * - * We also create a left {matrix} with a single column that contains - * elements shifted out of the matrix. The left {matrix} also - * contains \vphantom copies of the other {pmatrix} elements. - * As before, this ensures consistent row heights of left and main. - */ - -const bordermatrixParseTree = (matrix, delimiters) => { - const body = matrix.body; - body[0].shift(); // dispose of top left cell - - // Create an array for the left column - const leftColumnBody = new Array(body.length - 1).fill().map(() => []); - for (let i = 1; i < body.length; i++) { - // The visible part of the cell - leftColumnBody[i - 1].push(body[i].shift()); - // A vphantom with contents from the pmatrix, to set minimum cell height - const phantomBody = []; - for (let j = 0; j < body[i].length; j++) { - phantomBody.push(body[i][j]); - } - leftColumnBody[i - 1].push(phantom(phantomBody, "vphantom")); - } - - // Create an array for the top row - const topRowBody = new Array(body.length).fill().map(() => []); - for (let j = 0; j < body[0].length; j++) { - topRowBody[0].push(body[0][j]); - } - // Copy the rest of the pmatrix, but squashed via \hphantom - for (let i = 1; i < body.length; i++) { - for (let j = 0; j < body[0].length; j++) { - topRowBody[i].push(phantom(body[i][j].body, "hphantom")); - } - } - - // Squash the top row of the main {pmatrix} - for (let j = 0; j < body[0].length; j++) { - body[0][j] = phantom(body[0][j].body, "hphantom"); - } - - // Now wrap the arrays in the proper parse nodes. - - const leftColumn = { - type: "array", - mode: "math", - body: leftColumnBody, - cols: [{ type: "align", align: "c" }], - rowGaps: new Array(leftColumnBody.length - 1).fill(null), - hLinesBeforeRow: new Array(leftColumnBody.length + 1).fill().map(() => []), - envClasses: [], - scriptLevel: "text", - arraystretch: 1, - labels: new Array(leftColumnBody.length).fill(""), - arraycolsep: { "number": 0.04, unit: "em" } - }; - - const topRow = { - type: "array", - mode: "math", - body: topRowBody, - cols: new Array(topRowBody.length).fill({ type: "align", align: "c" }), - rowGaps: new Array(topRowBody.length - 1).fill(null), - hLinesBeforeRow: new Array(topRowBody.length + 1).fill().map(() => []), - envClasses: [], - scriptLevel: "text", - arraystretch: 1, - labels: new Array(topRowBody.length).fill(""), - arraycolsep: null - }; - - const topWrapper = { - type: "styling", - mode: "math", - scriptLevel: "text", // Must set this explicitly. - body: [topRow] // Default level is "script". - }; - - const container = { - type: "leftright", - mode: "math", - body: [matrix], - left: delimiters ? delimiters[0] : "(", - right: delimiters ? delimiters[1] : ")", - rightColor: undefined - }; - - const base = { - type: "op", // The base of a TeX \overset - mode: "math", - limits: true, - alwaysHandleSupSub: true, - parentIsSupSub: true, - symbol: false, - suppressBaseShift: true, - body: [container] - }; - - const mover = { - type: "supsub", // We're using the MathML equivalent - mode: "math", // of TeX \overset. - stack: true, - base: base, // That keeps the {pmatrix} aligned with - sup: topWrapper, // the math centerline. - sub: null - }; - - return ordGroup([leftColumn, mover]) -}; - -/** - * Lexing or parsing positional information for error reporting. - * This object is immutable. - */ -class SourceLocation { - constructor(lexer, start, end) { - this.lexer = lexer; // Lexer holding the input string. - this.start = start; // Start offset, zero-based inclusive. - this.end = end; // End offset, zero-based exclusive. - } - - /** - * Merges two `SourceLocation`s from location providers, given they are - * provided in order of appearance. - * - Returns the first one's location if only the first is provided. - * - Returns a merged range of the first and the last if both are provided - * and their lexers match. - * - Otherwise, returns null. - */ - static range(first, second) { - if (!second) { - return first && first.loc; - } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) { - return null; - } else { - return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end); - } - } -} - -/** - * Interface required to break circular dependency between Token, Lexer, and - * ParseError. - */ - -/** - * The resulting token returned from `lex`. - * - * It consists of the token text plus some position information. - * The position information is essentially a range in an input string, - * but instead of referencing the bare input string, we refer to the lexer. - * That way it is possible to attach extra metadata to the input string, - * like for example a file name or similar. - * - * The position information is optional, so it is OK to construct synthetic - * tokens if appropriate. Not providing available position information may - * lead to degraded error reporting, though. - */ -class Token { - constructor( - text, // the text of this token - loc - ) { - this.text = text; - this.loc = loc; - } - - /** - * Given a pair of tokens (this and endToken), compute a `Token` encompassing - * the whole input range enclosed by these two. - */ - range( - endToken, // last token of the range, inclusive - text // the text of the newly constructed token - ) { - return new Token(text, SourceLocation.range(this, endToken)); - } -} - -// In TeX, there are actually three sets of dimensions, one for each of -// textstyle, scriptstyle, and scriptscriptstyle. These are -// provided in the the arrays below, in that order. -// - -// Math style is not quite the same thing as script level. -const StyleLevel = { - DISPLAY: 0, - TEXT: 1, - SCRIPT: 2, - SCRIPTSCRIPT: 3 -}; - -/** - * All registered global/built-in macros. - * `macros.js` exports this same dictionary again and makes it public. - * `Parser.js` requires this dictionary via `macros.js`. - */ -const _macros = {}; - -// This function might one day accept an additional argument and do more things. -function defineMacro(name, body) { - _macros[name] = body; -} - -/** - * Predefined macros for Temml. - * This can be used to define some commands in terms of others. - */ - -const macros = _macros; - -////////////////////////////////////////////////////////////////////// -// macro tools - -defineMacro("\\noexpand", function(context) { - // The expansion is the token itself; but that token is interpreted - // as if its meaning were ‘\relax’ if it is a control sequence that - // would ordinarily be expanded by TeX’s expansion rules. - const t = context.popToken(); - if (context.isExpandable(t.text)) { - t.noexpand = true; - t.treatAsRelax = true; - } - return { tokens: [t], numArgs: 0 }; -}); - -defineMacro("\\expandafter", function(context) { - // TeX first reads the token that comes immediately after \expandafter, - // without expanding it; let’s call this token t. Then TeX reads the - // token that comes after t (and possibly more tokens, if that token - // has an argument), replacing it by its expansion. Finally TeX puts - // t back in front of that expansion. - const t = context.popToken(); - context.expandOnce(true); // expand only an expandable token - return { tokens: [t], numArgs: 0 }; -}); - -// LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2 -// TeX source: \long\def\@firstoftwo#1#2{#1} -defineMacro("\\@firstoftwo", function(context) { - const args = context.consumeArgs(2); - return { tokens: args[0], numArgs: 0 }; -}); - -// LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1 -// TeX source: \long\def\@secondoftwo#1#2{#2} -defineMacro("\\@secondoftwo", function(context) { - const args = context.consumeArgs(2); - return { tokens: args[1], numArgs: 0 }; -}); - -// LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded) -// symbol that isn't a space, consuming any spaces but not consuming the -// first nonspace character. If that nonspace character matches #1, then -// the macro expands to #2; otherwise, it expands to #3. -defineMacro("\\@ifnextchar", function(context) { - const args = context.consumeArgs(3); // symbol, if, else - context.consumeSpaces(); - const nextToken = context.future(); - if (args[0].length === 1 && args[0][0].text === nextToken.text) { - return { tokens: args[1], numArgs: 0 }; - } else { - return { tokens: args[2], numArgs: 0 }; - } -}); - -// LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol. -// If it is `*`, then it consumes the symbol, and the macro expands to #1; -// otherwise, the macro expands to #2 (without consuming the symbol). -// TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}} -defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}"); - -// LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode -defineMacro("\\TextOrMath", function(context) { - const args = context.consumeArgs(2); - if (context.mode === "text") { - return { tokens: args[0], numArgs: 0 }; - } else { - return { tokens: args[1], numArgs: 0 }; - } -}); - -const stringFromArg = arg => { - // Reverse the order of the arg and return a string. - let str = ""; - for (let i = arg.length - 1; i > -1; i--) { - str += arg[i].text; - } - return str -}; - -// Lookup table for parsing numbers in base 8 through 16 -const digitToNumber = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - a: 10, - A: 10, - b: 11, - B: 11, - c: 12, - C: 12, - d: 13, - D: 13, - e: 14, - E: 14, - f: 15, - F: 15 -}; - -const nextCharNumber = context => { - const numStr = context.future().text; - if (numStr === "EOF") { return [null, ""] } - return [digitToNumber[numStr.charAt(0)], numStr] -}; - -const appendCharNumbers = (number, numStr, base) => { - for (let i = 1; i < numStr.length; i++) { - const digit = digitToNumber[numStr.charAt(i)]; - number *= base; - number += digit; - } - return number -}; - -// TeX \char makes a literal character (catcode 12) using the following forms: -// (see The TeXBook, p. 43) -// \char123 -- decimal -// \char'123 -- octal -// \char"123 -- hex -// \char`x -- character that can be written (i.e. isn't active) -// \char`\x -- character that cannot be written (e.g. %) -// These all refer to characters from the font, so we turn them into special -// calls to a function \@char dealt with in the Parser. -defineMacro("\\char", function(context) { - let token = context.popToken(); - let base; - let number = ""; - if (token.text === "'") { - base = 8; - token = context.popToken(); - } else if (token.text === '"') { - base = 16; - token = context.popToken(); - } else if (token.text === "`") { - token = context.popToken(); - if (token.text[0] === "\\") { - number = token.text.charCodeAt(1); - } else if (token.text === "EOF") { - throw new ParseError("\\char` missing argument"); - } else { - number = token.text.charCodeAt(0); - } - } else { - base = 10; - } - if (base) { - // Parse a number in the given base, starting with first `token`. - let numStr = token.text; - number = digitToNumber[numStr.charAt(0)]; - if (number == null || number >= base) { - throw new ParseError(`Invalid base-${base} digit ${token.text}`); - } - number = appendCharNumbers(number, numStr, base); - let digit; - [digit, numStr] = nextCharNumber(context); - while (digit != null && digit < base) { - number *= base; - number += digit; - number = appendCharNumbers(number, numStr, base); - context.popToken(); - [digit, numStr] = nextCharNumber(context); - } - } - return `\\@char{${number}}`; -}); - -function recreateArgStr(context) { - // Recreate the macro's original argument string from the array of parse tokens. - const tokens = context.consumeArgs(1)[0]; - let str = ""; - let expectedLoc = tokens[tokens.length - 1].loc.start; - for (let i = tokens.length - 1; i >= 0; i--) { - const actualLoc = tokens[i].loc.start; - if (actualLoc > expectedLoc) { - // context.consumeArgs has eaten a space. - str += " "; - expectedLoc = actualLoc; - } - str += tokens[i].text; - expectedLoc += tokens[i].text.length; - } - return str -} - -// The Latin Modern font renders at the wrong vertical alignment. -// This macro provides a better rendering. -defineMacro("\\surd", '\\sqrt{\\vphantom{|}}'); - -// See comment for \oplus in symbols.js. -defineMacro("\u2295", "\\oplus"); - -// Since Temml has no \par, ignore \long. -defineMacro("\\long", ""); - -////////////////////////////////////////////////////////////////////// -// Grouping -// \let\bgroup={ \let\egroup=} -defineMacro("\\bgroup", "{"); -defineMacro("\\egroup", "}"); - -// Symbols from latex.ltx: -// \def~{\nobreakspace{}} -// \def\lq{`} -// \def\rq{'} -// \def \aa {\r a} -defineMacro("~", "\\nobreakspace"); -defineMacro("\\lq", "`"); -defineMacro("\\rq", "'"); -defineMacro("\\aa", "\\r a"); - -defineMacro("\\Bbbk", "\\Bbb{k}"); - -// \mathstrut from the TeXbook, p 360 -defineMacro("\\mathstrut", "\\vphantom{(}"); - -// \underbar from TeXbook p 353 -defineMacro("\\underbar", "\\underline{\\text{#1}}"); - -////////////////////////////////////////////////////////////////////// -// LaTeX_2ε - -// \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@ -// \kern6\p@\hbox{.}\hbox{.}\hbox{.}}} -// We'll call \varvdots, which gets a glyph from symbols.js. -// The zero-width rule gets us an equivalent to the vertical 6pt kern. -defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}"); -defineMacro("\u22ee", "\\vdots"); - -// {array} environment gaps -defineMacro("\\arraystretch", "1"); // line spacing factor times 12pt -defineMacro("\\arraycolsep", "6pt"); // half the width separating columns - -////////////////////////////////////////////////////////////////////// -// amsmath.sty -// http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf - -//\newcommand{\substack}[1]{\subarray{c}#1\endsubarray} -defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"); - -// \def\iff{\DOTSB\;\Longleftrightarrow\;} -// \def\implies{\DOTSB\;\Longrightarrow\;} -// \def\impliedby{\DOTSB\;\Longleftarrow\;} -defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"); -defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"); -defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"); - -// AMSMath's automatic \dots, based on \mdots@@ macro. -const dotsByToken = { - ",": "\\dotsc", - "\\not": "\\dotsb", - // \keybin@ checks for the following: - "+": "\\dotsb", - "=": "\\dotsb", - "<": "\\dotsb", - ">": "\\dotsb", - "-": "\\dotsb", - "*": "\\dotsb", - ":": "\\dotsb", - // Symbols whose definition starts with \DOTSB: - "\\DOTSB": "\\dotsb", - "\\coprod": "\\dotsb", - "\\bigvee": "\\dotsb", - "\\bigwedge": "\\dotsb", - "\\biguplus": "\\dotsb", - "\\bigcap": "\\dotsb", - "\\bigcup": "\\dotsb", - "\\prod": "\\dotsb", - "\\sum": "\\dotsb", - "\\bigotimes": "\\dotsb", - "\\bigoplus": "\\dotsb", - "\\bigodot": "\\dotsb", - "\\bigsqcap": "\\dotsb", - "\\bigsqcup": "\\dotsb", - "\\bigtimes": "\\dotsb", - "\\And": "\\dotsb", - "\\longrightarrow": "\\dotsb", - "\\Longrightarrow": "\\dotsb", - "\\longleftarrow": "\\dotsb", - "\\Longleftarrow": "\\dotsb", - "\\longleftrightarrow": "\\dotsb", - "\\Longleftrightarrow": "\\dotsb", - "\\mapsto": "\\dotsb", - "\\longmapsto": "\\dotsb", - "\\hookrightarrow": "\\dotsb", - "\\doteq": "\\dotsb", - // Symbols whose definition starts with \mathbin: - "\\mathbin": "\\dotsb", - // Symbols whose definition starts with \mathrel: - "\\mathrel": "\\dotsb", - "\\relbar": "\\dotsb", - "\\Relbar": "\\dotsb", - "\\xrightarrow": "\\dotsb", - "\\xleftarrow": "\\dotsb", - // Symbols whose definition starts with \DOTSI: - "\\DOTSI": "\\dotsi", - "\\int": "\\dotsi", - "\\oint": "\\dotsi", - "\\iint": "\\dotsi", - "\\iiint": "\\dotsi", - "\\iiiint": "\\dotsi", - // Symbols whose definition starts with \DOTSX: - "\\DOTSX": "\\dotsx" -}; - -defineMacro("\\dots", function(context) { - // TODO: If used in text mode, should expand to \textellipsis. - // However, in Temml, \textellipsis and \ldots behave the same - // (in text mode), and it's unlikely we'd see any of the math commands - // that affect the behavior of \dots when in text mode. So fine for now - // (until we support \ifmmode ... \else ... \fi). - let thedots = "\\dotso"; - const next = context.expandAfterFuture().text; - if (next in dotsByToken) { - thedots = dotsByToken[next]; - } else if (next.slice(0, 4) === "\\not") { - thedots = "\\dotsb"; - } else if (next in symbols.math) { - if (["bin", "rel"].includes(symbols.math[next].group)) { - thedots = "\\dotsb"; - } - } - return thedots; -}); - -const spaceAfterDots = { - // \rightdelim@ checks for the following: - ")": true, - "]": true, - "\\rbrack": true, - "\\}": true, - "\\rbrace": true, - "\\rangle": true, - "\\rceil": true, - "\\rfloor": true, - "\\rgroup": true, - "\\rmoustache": true, - "\\right": true, - "\\bigr": true, - "\\biggr": true, - "\\Bigr": true, - "\\Biggr": true, - // \extra@ also tests for the following: - $: true, - // \extrap@ checks for the following: - ";": true, - ".": true, - ",": true -}; - -defineMacro("\\dotso", function(context) { - const next = context.future().text; - if (next in spaceAfterDots) { - return "\\ldots\\,"; - } else { - return "\\ldots"; - } -}); - -defineMacro("\\dotsc", function(context) { - const next = context.future().text; - // \dotsc uses \extra@ but not \extrap@, instead specially checking for - // ';' and '.', but doesn't check for ','. - if (next in spaceAfterDots && next !== ",") { - return "\\ldots\\,"; - } else { - return "\\ldots"; - } -}); - -defineMacro("\\cdots", function(context) { - const next = context.future().text; - if (next in spaceAfterDots) { - return "\\@cdots\\,"; - } else { - return "\\@cdots"; - } -}); - -defineMacro("\\dotsb", "\\cdots"); -defineMacro("\\dotsm", "\\cdots"); -defineMacro("\\dotsi", "\\!\\cdots"); -defineMacro("\\idotsint", "\\int\\!\\cdots\\!\\int"); -// amsmath doesn't actually define \dotsx, but \dots followed by a macro -// starting with \DOTSX implies \dotso, and then \extra@ detects this case -// and forces the added `\,`. -defineMacro("\\dotsx", "\\ldots\\,"); - -// \let\DOTSI\relax -// \let\DOTSB\relax -// \let\DOTSX\relax -defineMacro("\\DOTSI", "\\relax"); -defineMacro("\\DOTSB", "\\relax"); -defineMacro("\\DOTSX", "\\relax"); - -// Spacing, based on amsmath.sty's override of LaTeX defaults -// \DeclareRobustCommand{\tmspace}[3]{% -// \ifmmode\mskip#1#2\else\kern#1#3\fi\relax} -defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"); -// \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}} -// TODO: math mode should use \thinmuskip -defineMacro("\\,", "{\\tmspace+{3mu}{.1667em}}"); -// \let\thinspace\, -defineMacro("\\thinspace", "\\,"); -// \def\>{\mskip\medmuskip} -// \renewcommand{\:}{\tmspace+\medmuskip{.2222em}} -// TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu -defineMacro("\\>", "\\mskip{4mu}"); -defineMacro("\\:", "{\\tmspace+{4mu}{.2222em}}"); -// \let\medspace\: -defineMacro("\\medspace", "\\:"); -// \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}} -// TODO: math mode should use \thickmuskip = 5mu plus 5mu -defineMacro("\\;", "{\\tmspace+{5mu}{.2777em}}"); -// \let\thickspace\; -defineMacro("\\thickspace", "\\;"); -// \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}} -// TODO: math mode should use \thinmuskip -defineMacro("\\!", "{\\tmspace-{3mu}{.1667em}}"); -// \let\negthinspace\! -defineMacro("\\negthinspace", "\\!"); -// \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}} -// TODO: math mode should use \medmuskip -defineMacro("\\negmedspace", "{\\tmspace-{4mu}{.2222em}}"); -// \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}} -// TODO: math mode should use \thickmuskip -defineMacro("\\negthickspace", "{\\tmspace-{5mu}{.277em}}"); -// \def\enspace{\kern.5em } -defineMacro("\\enspace", "\\kern.5em "); -// \def\enskip{\hskip.5em\relax} -defineMacro("\\enskip", "\\hskip.5em\\relax"); -// \def\quad{\hskip1em\relax} -defineMacro("\\quad", "\\hskip1em\\relax"); -// \def\qquad{\hskip2em\relax} -defineMacro("\\qquad", "\\hskip2em\\relax"); - -defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax"); - -// \tag@in@display form of \tag -defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren"); -defineMacro("\\tag@paren", "\\tag@literal{({#1})}"); -defineMacro("\\tag@literal", (context) => { - if (context.macros.get("\\df@tag")) { - throw new ParseError("Multiple \\tag"); - } - return "\\gdef\\df@tag{\\text{#1}}"; -}); -defineMacro("\\notag", "\\nonumber"); -defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}"); - -// \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin -// {\operator@font mod}\penalty900 -// \mkern5mu\nonscript\mskip-\medmuskip} -// \newcommand{\pod}[1]{\allowbreak -// \if@display\mkern18mu\else\mkern8mu\fi(#1)} -// \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}} -// \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu -// \else\mkern12mu\fi{\operator@font mod}\,\,#1} -// TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu -defineMacro("\\bmod", "\\mathbin{\\text{mod}}"); -defineMacro( - "\\pod", - "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)" -); -defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"); -defineMacro( - "\\mod", - "\\allowbreak" + - "\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" + - "{\\rm mod}\\,\\,#1" -); - -////////////////////////////////////////////////////////////////////// -// LaTeX source2e - -// \expandafter\let\expandafter\@normalcr -// \csname\expandafter\@gobble\string\\ \endcsname -// \DeclareRobustCommand\newline{\@normalcr\relax} -defineMacro("\\newline", "\\\\\\relax"); - -// \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@} -// TODO: Doesn't normally work in math mode because \@ fails. -defineMacro("\\TeX", "\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}"); - -defineMacro( - "\\LaTeX", - "\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX" -); - -defineMacro( - "\\Temml", - // eslint-disable-next-line max-len - "\\textrm{T}\\kern-0.2em\\lower{0.2em}{\\textrm{E}}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}" -); - -// \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace} -// \def\@hspace#1{\hskip #1\relax} -// \def\@hspacer#1{\vrule \@width\z@\nobreak -// \hskip #1\hskip \z@skip} -defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace"); -defineMacro("\\@hspace", "\\hskip #1\\relax"); -defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"); - -defineMacro("\\colon", `\\mathpunct{\\char"3a}`); - -////////////////////////////////////////////////////////////////////// -// mathtools.sty - -defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}"); - -//\providecommand\ordinarycolon{:} -defineMacro("\\ordinarycolon", `\\char"3a`); -// Raise to center on the math axis, as closely as possible. -defineMacro("\\vcentcolon", "\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}"); -// \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}} -defineMacro("\\coloneq", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}'); -// \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}} -defineMacro("\\Coloneq", '\\mathrel{\\char"2237\\char"2212}'); -// \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon} -defineMacro("\\Eqqcolon", '\\mathrel{\\char"3d\\char"2237}'); -// \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon} -defineMacro("\\Eqcolon", '\\mathrel{\\char"2212\\char"2237}'); -// \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx} -defineMacro("\\colonapprox", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}'); -// \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx} -defineMacro("\\Colonapprox", '\\mathrel{\\char"2237\\char"2248}'); -// \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim} -defineMacro("\\colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); -// \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim} -defineMacro("\\Colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); - -////////////////////////////////////////////////////////////////////// -// colonequals.sty - -// Alternate names for mathtools's macros: -defineMacro("\\ratio", "\\vcentcolon"); -defineMacro("\\coloncolon", "\\dblcolon"); -defineMacro("\\colonequals", "\\coloneqq"); -defineMacro("\\coloncolonequals", "\\Coloneqq"); -defineMacro("\\equalscolon", "\\eqqcolon"); -defineMacro("\\equalscoloncolon", "\\Eqqcolon"); -defineMacro("\\colonminus", "\\coloneq"); -defineMacro("\\coloncolonminus", "\\Coloneq"); -defineMacro("\\minuscolon", "\\eqcolon"); -defineMacro("\\minuscoloncolon", "\\Eqcolon"); -// \colonapprox name is same in mathtools and colonequals. -defineMacro("\\coloncolonapprox", "\\Colonapprox"); -// \colonsim name is same in mathtools and colonequals. -defineMacro("\\coloncolonsim", "\\Colonsim"); - -// Present in newtxmath, pxfonts and txfonts -defineMacro("\\notni", "\\mathrel{\\char`\u220C}"); -defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"); -defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"); - -////////////////////////////////////////////////////////////////////// -// From amsopn.sty -defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"); -defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"); -defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\text{lim}}}"); -defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\text{lim}}}"); -defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}"); -defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}"); - -defineMacro("\\centerdot", "{\\medspace\\rule{0.167em}{0.189em}\\medspace}"); - -////////////////////////////////////////////////////////////////////// -// statmath.sty -// https://ctan.math.illinois.edu/macros/latex/contrib/statmath/statmath.pdf - -defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"); -defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"); -defineMacro("\\plim", "\\DOTSB\\operatorname*{plim}"); - -////////////////////////////////////////////////////////////////////// -// MnSymbol.sty - -defineMacro("\\leftmodels", "\\mathop{\\reflectbox{$\\models$}}"); - -////////////////////////////////////////////////////////////////////// -// braket.sty -// http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf - -defineMacro("\\bra", "\\mathinner{\\langle{#1}|}"); -defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}"); -defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}"); -defineMacro("\\Bra", "\\left\\langle#1\\right|"); -defineMacro("\\Ket", "\\left|#1\\right\\rangle"); -// A helper for \Braket and \Set -const replaceVert = (argStr, match) => { - const ch = match[0] === "|" ? "\\vert" : "\\Vert"; - const replaceStr = `}\\,\\middle${ch}\\,{`; - return argStr.slice(0, match.index) + replaceStr + argStr.slice(match.index + match[0].length) -}; -defineMacro("\\Braket", function(context) { - let argStr = recreateArgStr(context); - const regEx = /\|\||\||\\\|/g; - let match; - while ((match = regEx.exec(argStr)) !== null) { - argStr = replaceVert(argStr, match); - } - return "\\left\\langle{" + argStr + "}\\right\\rangle" -}); -defineMacro("\\Set", function(context) { - let argStr = recreateArgStr(context); - const match = /\|\||\||\\\|/.exec(argStr); - if (match) { - argStr = replaceVert(argStr, match); - } - return "\\left\\{\\:{" + argStr + "}\\:\\right\\}" -}); -defineMacro("\\set", function(context) { - const argStr = recreateArgStr(context); - return "\\{{" + argStr.replace(/\|/, "}\\mid{") + "}\\}" -}); - -////////////////////////////////////////////////////////////////////// -// actuarialangle.dtx -defineMacro("\\angln", "{\\angl n}"); - -////////////////////////////////////////////////////////////////////// -// derivative.sty -defineMacro("\\odv", "\\@ifstar\\odv@next\\odv@numerator"); -defineMacro("\\odv@numerator", "\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}"); -defineMacro("\\odv@next", "\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1"); -defineMacro("\\pdv", "\\@ifstar\\pdv@next\\pdv@numerator"); - -const pdvHelper = args => { - const numerator = args[0][0].text; - const denoms = stringFromArg(args[1]).split(","); - const power = String(denoms.length); - const numOp = power === "1" ? "\\partial" : `\\partial^${power}`; - let denominator = ""; - denoms.map(e => { denominator += "\\partial " + e.trim() + "\\,";}); - return [numerator, numOp, denominator.replace(/\\,$/, "")] -}; -defineMacro("\\pdv@numerator", function(context) { - const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); - return `\\frac{${numOp} ${numerator}}{${denominator}}` -}); -defineMacro("\\pdv@next", function(context) { - const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); - return `\\frac{${numOp}}{${denominator}} ${numerator}` -}); - -////////////////////////////////////////////////////////////////////// -// upgreek.dtx -defineMacro("\\upalpha", "\\up@greek{\\alpha}"); -defineMacro("\\upbeta", "\\up@greek{\\beta}"); -defineMacro("\\upgamma", "\\up@greek{\\gamma}"); -defineMacro("\\updelta", "\\up@greek{\\delta}"); -defineMacro("\\upepsilon", "\\up@greek{\\epsilon}"); -defineMacro("\\upzeta", "\\up@greek{\\zeta}"); -defineMacro("\\upeta", "\\up@greek{\\eta}"); -defineMacro("\\uptheta", "\\up@greek{\\theta}"); -defineMacro("\\upiota", "\\up@greek{\\iota}"); -defineMacro("\\upkappa", "\\up@greek{\\kappa}"); -defineMacro("\\uplambda", "\\up@greek{\\lambda}"); -defineMacro("\\upmu", "\\up@greek{\\mu}"); -defineMacro("\\upnu", "\\up@greek{\\nu}"); -defineMacro("\\upxi", "\\up@greek{\\xi}"); -defineMacro("\\upomicron", "\\up@greek{\\omicron}"); -defineMacro("\\uppi", "\\up@greek{\\pi}"); -defineMacro("\\upalpha", "\\up@greek{\\alpha}"); -defineMacro("\\uprho", "\\up@greek{\\rho}"); -defineMacro("\\upsigma", "\\up@greek{\\sigma}"); -defineMacro("\\uptau", "\\up@greek{\\tau}"); -defineMacro("\\upupsilon", "\\up@greek{\\upsilon}"); -defineMacro("\\upphi", "\\up@greek{\\phi}"); -defineMacro("\\upchi", "\\up@greek{\\chi}"); -defineMacro("\\uppsi", "\\up@greek{\\psi}"); -defineMacro("\\upomega", "\\up@greek{\\omega}"); - -////////////////////////////////////////////////////////////////////// -// cmll package -defineMacro("\\invamp", '\\mathbin{\\char"214b}'); -defineMacro("\\parr", '\\mathbin{\\char"214b}'); -defineMacro("\\upand", '\\mathbin{\\char"214b}'); // STIX package -defineMacro("\\with", '\\mathbin{\\char"26}'); -defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}'); -defineMacro("\\multimapboth", '\\mathrel{\\char"29df}'); -defineMacro("\\scoh", '{\\mkern5mu\\char"2322\\mkern5mu}'); -defineMacro("\\sincoh", '{\\mkern5mu\\char"2323\\mkern5mu}'); -defineMacro("\\coh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}} -{\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}`); -defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}} -{\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}`); - - -////////////////////////////////////////////////////////////////////// -// chemstyle package -defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}"); - -/* eslint-disable */ -/* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ - -/************************************************************* - * - * Temml mhchem.js - * - * This file implements a Temml version of mhchem version 3.3.0. - * It is adapted from MathJax/extensions/TeX/mhchem.js - * It differs from the MathJax version as follows: - * 1. The interface is changed so that it can be called from Temml, not MathJax. - * 2. \rlap and \llap are replaced with \mathrlap and \mathllap. - * 3. The reaction arrow code is simplified. All reaction arrows are rendered - * using Temml extensible arrows instead of building non-extensible arrows. - * 4. The ~bond forms are composed entirely of \rule elements. - * 5. Two dashes in _getBond are wrapped in braces to suppress spacing. i.e., {-} - * 6. The electron dot uses \textbullet instead of \bullet. - * 7. \smash[T] has been removed. (WebKit hides anything inside \smash{…}) - * - * This code, as other Temml code, is released under the MIT license. - * - * /************************************************************* - * - * MathJax/extensions/TeX/mhchem.js - * - * Implements the \ce command for handling chemical formulas - * from the mhchem LaTeX package. - * - * --------------------------------------------------------------------- - * - * Copyright (c) 2011-2015 The MathJax Consortium - * Copyright (c) 2015-2018 Martin Hensel - * - * 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. - */ - -// -// Coding Style -// - use '' for identifiers that can by minified/uglified -// - use "" for strings that need to stay untouched - -// version: "3.3.0" for MathJax and Temml - - -// Add \ce, \pu, and \tripleDash to the Temml macros. - -defineMacro("\\ce", function(context) { - return chemParse(context.consumeArgs(1)[0], "ce") -}); - -defineMacro("\\pu", function(context) { - return chemParse(context.consumeArgs(1)[0], "pu"); -}); - -// Math fonts do not include glyphs for the ~ form of bonds. So we'll send path geometry -// So we'll compose characters built from \rule elements. -defineMacro("\\uniDash", `{\\rule{0.672em}{0.06em}}`) -defineMacro("\\triDash", `{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}`) -defineMacro("\\tripleDash", `\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em`) -defineMacro("\\tripleDashOverLine", `\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em`) -defineMacro("\\tripleDashOverDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) -defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\uniDash}}\\raise0.27em{\\triDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) - - // - // This is the main function for handing the \ce and \pu commands. - // It takes the argument to \ce or \pu and returns the corresponding TeX string. - // - - var chemParse = function (tokens, stateMachine) { - // Recreate the argument string from Temml's array of tokens. - var str = ""; - var expectedLoc = tokens.length && tokens[tokens.length - 1].loc.start - for (var i = tokens.length - 1; i >= 0; i--) { - if(tokens[i].loc.start > expectedLoc) { - // context.consumeArgs has eaten a space. - str += " "; - expectedLoc = tokens[i].loc.start; - } - str += tokens[i].text; - expectedLoc += tokens[i].text.length; - } - // Call the mhchem core parser. - var tex = texify.go(mhchemParser.go(str, stateMachine)); - return tex; - }; - - // - // Core parser for mhchem syntax (recursive) - // - /** @type {MhchemParser} */ - var mhchemParser = { - // - // Parses mchem \ce syntax - // - // Call like - // go("H2O"); - // - go: function (input, stateMachine) { - if (!input) { return []; } - if (stateMachine === undefined) { stateMachine = 'ce'; } - var state = '0'; - - // - // String buffers for parsing: - // - // buffer.a == amount - // buffer.o == element - // buffer.b == left-side superscript - // buffer.p == left-side subscript - // buffer.q == right-side subscript - // buffer.d == right-side superscript - // - // buffer.r == arrow - // buffer.rdt == arrow, script above, type - // buffer.rd == arrow, script above, content - // buffer.rqt == arrow, script below, type - // buffer.rq == arrow, script below, content - // - // buffer.text_ - // buffer.rm - // etc. - // - // buffer.parenthesisLevel == int, starting at 0 - // buffer.sb == bool, space before - // buffer.beginsWithBond == bool - // - // These letters are also used as state names. - // - // Other states: - // 0 == begin of main part (arrow/operator unlikely) - // 1 == next entity - // 2 == next entity (arrow/operator unlikely) - // 3 == next atom - // c == macro - // - /** @type {Buffer} */ - var buffer = {}; - buffer['parenthesisLevel'] = 0; - - input = input.replace(/\n/g, " "); - input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-"); - input = input.replace(/[\u2026]/g, "..."); - - // - // Looks through mhchemParser.transitions, to execute a matching action - // (recursive) - // - var lastInput; - var watchdog = 10; - /** @type {ParserOutput[]} */ - var output = []; - while (true) { - if (lastInput !== input) { - watchdog = 10; - lastInput = input; - } else { - watchdog--; - } - // - // Find actions in transition table - // - var machine = mhchemParser.stateMachines[stateMachine]; - var t = machine.transitions[state] || machine.transitions['*']; - iterateTransitions: - for (var i=0; i 0) { - if (!task.revisit) { - input = matches.remainder; - } - if (!task.toContinue) { - break iterateTransitions; - } - } else { - return output; - } - } - } - // - // Prevent infinite loop - // - if (watchdog <= 0) { - throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character - } - } - }, - concatArray: function (a, b) { - if (b) { - if (Array.isArray(b)) { - for (var iB=0; iB': /^[=<>]/, - '#': /^[#\u2261]/, - '+': /^\+/, - '-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, // -space -, -; -] -/ -$ -state-of-aggregation - '-9': /^-(?=[0-9])/, - '- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/, - '-': /^-/, - 'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/, - 'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/, - 'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/, - '\\bond{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\bond{", "", "", "}"); }, - '->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/, - 'CMT': /^[CMT](?=\[)/, - '[(...)]': function (input) { return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]"); }, - '1st-level escape': /^(&|\\\\|\\hline)\s*/, - '\\,': /^(?:\\[,\ ;:])/, // \\x - but output no space before - '\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); }, - '\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", ""); }, - '\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/, - '\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/, - 'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, // only those with numbers in front, because the others will be formatted correctly anyway - 'others': /^[\/~|]/, - '\\frac{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\frac{", "", "", "}", "{", "", "", "}"); }, - '\\overset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\overset{", "", "", "}", "{", "", "", "}"); }, - '\\underset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underset{", "", "", "}", "{", "", "", "}"); }, - '\\underbrace{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underbrace{", "", "", "}_", "{", "", "", "}"); }, - '\\color{(...)}0': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}"); }, - '\\color{(...)}{(...)}1': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}", "{", "", "", "}"); }, - '\\color(...){(...)}2': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}"); }, - '\\ce{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\ce{", "", "", "}"); }, - 'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, - 'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, // 0 could be oxidation or charge - 'roman numeral': /^[IVX]+/, - '1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/, - 'amount': function (input) { - var match; - // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing - match = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/); - if (match) { - return { match_: match[0], remainder: input.substr(match[0].length) }; - } - var a = mhchemParser.patterns.findObserveGroups(input, "", "$", "$", ""); - if (a) { // e.g. $2n-1$, $-$ - match = a.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/); - if (match) { - return { match_: match[0], remainder: input.substr(match[0].length) }; - } - } - return null; - }, - 'amount2': function (input) { return this['amount'](input); }, - '(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/, - 'formula$': function (input) { - if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula - var match = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/); - if (match) { - return { match_: match[0], remainder: input.substr(match[0].length) }; - } - return null; - }, - 'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/, - '/': /^\s*(\/)\s*/, - '//': /^\s*(\/\/)\s*/, - '*': /^\s*[*.]\s*/ - }, - findObserveGroups: function (input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) { - /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */ - var _match = function (input, pattern) { - if (typeof pattern === "string") { - if (input.indexOf(pattern) !== 0) { return null; } - return pattern; - } else { - var match = input.match(pattern); - if (!match) { return null; } - return match[0]; - } - }; - /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */ - var _findObserveGroups = function (input, i, endChars) { - var braces = 0; - while (i < input.length) { - var a = input.charAt(i); - var match = _match(input.substr(i), endChars); - if (match !== null && braces === 0) { - return { endMatchBegin: i, endMatchEnd: i + match.length }; - } else if (a === "{") { - braces++; - } else if (a === "}") { - if (braces === 0) { - throw ["ExtraCloseMissingOpen", "Extra close brace or missing open brace"]; - } else { - braces--; - } - } - i++; - } - if (braces > 0) { - return null; - } - return null; - }; - var match = _match(input, begExcl); - if (match === null) { return null; } - input = input.substr(match.length); - match = _match(input, begIncl); - if (match === null) { return null; } - var e = _findObserveGroups(input, match.length, endIncl || endExcl); - if (e === null) { return null; } - var match1 = input.substring(0, (endIncl ? e.endMatchEnd : e.endMatchBegin)); - if (!(beg2Excl || beg2Incl)) { - return { - match_: match1, - remainder: input.substr(e.endMatchEnd) - }; - } else { - var group2 = this.findObserveGroups(input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl); - if (group2 === null) { return null; } - /** @type {string[]} */ - var matchRet = [match1, group2.match_]; - return { - match_: (combine ? matchRet.join("") : matchRet), - remainder: group2.remainder - }; - } - }, - - // - // Matching function - // e.g. match("a", input) will look for the regexp called "a" and see if it matches - // returns null or {match_:"a", remainder:"bc"} - // - match_: function (m, input) { - var pattern = mhchemParser.patterns.patterns[m]; - if (pattern === undefined) { - throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern - } else if (typeof pattern === "function") { - return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser - } else { // RegExp - var match = input.match(pattern); - if (match) { - var mm; - if (match[2]) { - mm = [ match[1], match[2] ]; - } else if (match[1]) { - mm = match[1]; - } else { - mm = match[0]; - } - return { match_: mm, remainder: input.substr(match[0].length) }; - } - return null; - } - } - }, - - // - // Generic state machine actions - // - actions: { - 'a=': function (buffer, m) { buffer.a = (buffer.a || "") + m; }, - 'b=': function (buffer, m) { buffer.b = (buffer.b || "") + m; }, - 'p=': function (buffer, m) { buffer.p = (buffer.p || "") + m; }, - 'o=': function (buffer, m) { buffer.o = (buffer.o || "") + m; }, - 'q=': function (buffer, m) { buffer.q = (buffer.q || "") + m; }, - 'd=': function (buffer, m) { buffer.d = (buffer.d || "") + m; }, - 'rm=': function (buffer, m) { buffer.rm = (buffer.rm || "") + m; }, - 'text=': function (buffer, m) { buffer.text_ = (buffer.text_ || "") + m; }, - 'insert': function (buffer, m, a) { return { type_: a }; }, - 'insert+p1': function (buffer, m, a) { return { type_: a, p1: m }; }, - 'insert+p1+p2': function (buffer, m, a) { return { type_: a, p1: m[0], p2: m[1] }; }, - 'copy': function (buffer, m) { return m; }, - 'rm': function (buffer, m) { return { type_: 'rm', p1: m || ""}; }, - 'text': function (buffer, m) { return mhchemParser.go(m, 'text'); }, - '{text}': function (buffer, m) { - var ret = [ "{" ]; - mhchemParser.concatArray(ret, mhchemParser.go(m, 'text')); - ret.push("}"); - return ret; - }, - 'tex-math': function (buffer, m) { return mhchemParser.go(m, 'tex-math'); }, - 'tex-math tight': function (buffer, m) { return mhchemParser.go(m, 'tex-math tight'); }, - 'bond': function (buffer, m, k) { return { type_: 'bond', kind_: k || m }; }, - 'color0-output': function (buffer, m) { return { type_: 'color0', color: m[0] }; }, - 'ce': function (buffer, m) { return mhchemParser.go(m); }, - '1/2': function (buffer, m) { - /** @type {ParserOutput[]} */ - var ret = []; - if (m.match(/^[+\-]/)) { - ret.push(m.substr(0, 1)); - m = m.substr(1); - } - var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/); - n[1] = n[1].replace(/\$/g, ""); - ret.push({ type_: 'frac', p1: n[1], p2: n[2] }); - if (n[3]) { - n[3] = n[3].replace(/\$/g, ""); - ret.push({ type_: 'tex-math', p1: n[3] }); - } - return ret; - }, - '9,9': function (buffer, m) { return mhchemParser.go(m, '9,9'); } - }, - // - // createTransitions - // convert { 'letter': { 'state': { action_: 'output' } } } to { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] } - // with expansion of 'a|b' to 'a' and 'b' (at 2 places) - // - createTransitions: function (o) { - var pattern, state; - /** @type {string[]} */ - var stateArray; - var i; - // - // 1. Collect all states - // - /** @type {Transitions} */ - var transitions = {}; - for (pattern in o) { - for (state in o[pattern]) { - stateArray = state.split("|"); - o[pattern][state].stateArray = stateArray; - for (i=0; i': { - '0|1|2|3': { action_: 'r=', nextState: 'r' }, - 'a|as': { action_: [ 'output', 'r=' ], nextState: 'r' }, - '*': { action_: [ 'output', 'r=' ], nextState: 'r' } }, - '+': { - 'o': { action_: 'd= kv', nextState: 'd' }, - 'd|D': { action_: 'd=', nextState: 'd' }, - 'q': { action_: 'd=', nextState: 'qd' }, - 'qd|qD': { action_: 'd=', nextState: 'qd' }, - 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' }, - '3': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } }, - 'amount': { - '0|2': { action_: 'a=', nextState: 'a' } }, - 'pm-operator': { - '0|1|2|a|as': { action_: [ 'sb=false', 'output', { type_: 'operator', option: '\\pm' } ], nextState: '0' } }, - 'operator': { - '0|1|2|a|as': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } }, - '-$': { - 'o|q': { action_: [ 'charge or bond', 'output' ], nextState: 'qd' }, - 'd': { action_: 'd=', nextState: 'd' }, - 'D': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' }, - 'q': { action_: 'd=', nextState: 'qd' }, - 'qd': { action_: 'd=', nextState: 'qd' }, - 'qD|dq': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } }, - '-9': { - '3|o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '3' } }, - '- orbital overlap': { - 'o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' }, - 'd': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' } }, - '-': { - '0|1|2': { action_: [ { type_: 'output', option: 1 }, 'beginsWithBond=true', { type_: 'bond', option: "-" } ], nextState: '3' }, - '3': { action_: { type_: 'bond', option: "-" } }, - 'a': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' }, - 'as': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "-" } ], nextState: '3' }, - 'b': { action_: 'b=' }, - 'o': { action_: { type_: '- after o/d', option: false }, nextState: '2' }, - 'q': { action_: { type_: '- after o/d', option: false }, nextState: '2' }, - 'd|qd|dq': { action_: { type_: '- after o/d', option: true }, nextState: '2' }, - 'D|qD|p': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } }, - 'amount2': { - '1|3': { action_: 'a=', nextState: 'a' } }, - 'letters': { - '0|1|2|3|a|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, - 'q|dq': { action_: ['output', 'o='], nextState: 'o' }, - 'd|D|qd|qD': { action_: 'o after d', nextState: 'o' } }, - 'digits': { - 'o': { action_: 'q=', nextState: 'q' }, - 'd|D': { action_: 'q=', nextState: 'dq' }, - 'q': { action_: [ 'output', 'o=' ], nextState: 'o' }, - 'a': { action_: 'o=', nextState: 'o' } }, - 'space A': { - 'b|p|bp': {} }, - 'space': { - 'a': { nextState: 'as' }, - '0': { action_: 'sb=false' }, - '1|2': { action_: 'sb=true' }, - 'r|rt|rd|rdt|rdq': { action_: 'output', nextState: '0' }, - '*': { action_: [ 'output', 'sb=true' ], nextState: '1'} }, - '1st-level escape': { - '1|2': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ] }, - '*': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ], nextState: '0' } }, - '[(...)]': { - 'r|rt': { action_: 'rd=', nextState: 'rd' }, - 'rd|rdt': { action_: 'rq=', nextState: 'rdq' } }, - '...': { - 'o|d|D|dq|qd|qD': { action_: [ 'output', { type_: 'bond', option: "..." } ], nextState: '3' }, - '*': { action_: [ { type_: 'output', option: 1 }, { type_: 'insert', option: 'ellipsis' } ], nextState: '1' } }, - '. |* ': { - '*': { action_: [ 'output', { type_: 'insert', option: 'addition compound' } ], nextState: '1' } }, - 'state of aggregation $': { - '*': { action_: [ 'output', 'state of aggregation' ], nextState: '1' } }, - '{[(': { - 'a|as|o': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' }, - '0|1|2|3': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' }, - '*': { action_: [ 'output', 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' } }, - ')]}': { - '0|1|2|3|b|p|bp|o': { action_: [ 'o=', 'parenthesisLevel--' ], nextState: 'o' }, - 'a|as|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=', 'parenthesisLevel--' ], nextState: 'o' } }, - ', ': { - '*': { action_: [ 'output', 'comma' ], nextState: '0' } }, - '^_': { // ^ and _ without a sensible argument - '*': { } }, - '^{(...)}|^($...$)': { - '0|1|2|as': { action_: 'b=', nextState: 'b' }, - 'p': { action_: 'b=', nextState: 'bp' }, - '3|o': { action_: 'd= kv', nextState: 'D' }, - 'q': { action_: 'd=', nextState: 'qD' }, - 'd|D|qd|qD|dq': { action_: [ 'output', 'd=' ], nextState: 'D' } }, - '^a|^\\x{}{}|^\\x{}|^\\x|\'': { - '0|1|2|as': { action_: 'b=', nextState: 'b' }, - 'p': { action_: 'b=', nextState: 'bp' }, - '3|o': { action_: 'd= kv', nextState: 'd' }, - 'q': { action_: 'd=', nextState: 'qd' }, - 'd|qd|D|qD': { action_: 'd=' }, - 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' } }, - '_{(state of aggregation)}$': { - 'd|D|q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } }, - '_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': { - '0|1|2|as': { action_: 'p=', nextState: 'p' }, - 'b': { action_: 'p=', nextState: 'bp' }, - '3|o': { action_: 'q=', nextState: 'q' }, - 'd|D': { action_: 'q=', nextState: 'dq' }, - 'q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } }, - '=<>': { - '0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: '3' } }, - '#': { - '0|1|2|3|a|as|o': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "#" } ], nextState: '3' } }, - '{}': { - '*': { action_: { type_: 'output', option: 1 }, nextState: '1' } }, - '{...}': { - '0|1|2|3|a|as|b|p|bp': { action_: 'o=', nextState: 'o' }, - 'o|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } }, - '$...$': { - 'a': { action_: 'a=' }, // 2$n$ - '0|1|2|3|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, // not 'amount' - 'as|o': { action_: 'o=' }, - 'q|d|D|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } }, - '\\bond{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: "3" } }, - '\\frac{(...)}': { - '*': { action_: [ { type_: 'output', option: 1 }, 'frac-output' ], nextState: '3' } }, - '\\overset{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'overset-output' ], nextState: '3' } }, - '\\underset{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'underset-output' ], nextState: '3' } }, - '\\underbrace{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'underbrace-output' ], nextState: '3' } }, - '\\color{(...)}{(...)}1|\\color(...){(...)}2': { - '*': { action_: [ { type_: 'output', option: 2 }, 'color-output' ], nextState: '3' } }, - '\\color{(...)}0': { - '*': { action_: [ { type_: 'output', option: 2 }, 'color0-output' ] } }, - '\\ce{(...)}': { - '*': { action_: [ { type_: 'output', option: 2 }, 'ce' ], nextState: '3' } }, - '\\,': { - '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '1' } }, - '\\x{}{}|\\x{}|\\x': { - '0|1|2|3|a|as|b|p|bp|o|c0': { action_: [ 'o=', 'output' ], nextState: '3' }, - '*': { action_: ['output', 'o=', 'output' ], nextState: '3' } }, - 'others': { - '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '3' } }, - 'else2': { - 'a': { action_: 'a to o', nextState: 'o', revisit: true }, - 'as': { action_: [ 'output', 'sb=true' ], nextState: '1', revisit: true }, - 'r|rt|rd|rdt|rdq': { action_: [ 'output' ], nextState: '0', revisit: true }, - '*': { action_: [ 'output', 'copy' ], nextState: '3' } } - }), - actions: { - 'o after d': function (buffer, m) { - var ret; - if ((buffer.d || "").match(/^[0-9]+$/)) { - var tmp = buffer.d; - buffer.d = undefined; - ret = this['output'](buffer); - buffer.b = tmp; - } else { - ret = this['output'](buffer); - } - mhchemParser.actions['o='](buffer, m); - return ret; - }, - 'd= kv': function (buffer, m) { - buffer.d = m; - buffer.dType = 'kv'; - }, - 'charge or bond': function (buffer, m) { - if (buffer['beginsWithBond']) { - /** @type {ParserOutput[]} */ - var ret = []; - mhchemParser.concatArray(ret, this['output'](buffer)); - mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); - return ret; - } else { - buffer.d = m; - } - }, - '- after o/d': function (buffer, m, isAfterD) { - var c1 = mhchemParser.patterns.match_('orbital', buffer.o || ""); - var c2 = mhchemParser.patterns.match_('one lowercase greek letter $', buffer.o || ""); - var c3 = mhchemParser.patterns.match_('one lowercase latin letter $', buffer.o || ""); - var c4 = mhchemParser.patterns.match_('$one lowercase latin letter$ $', buffer.o || ""); - var hyphenFollows = m==="-" && ( c1 && c1.remainder==="" || c2 || c3 || c4 ); - if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) { - buffer.o = '$' + buffer.o + '$'; - } - /** @type {ParserOutput[]} */ - var ret = []; - if (hyphenFollows) { - mhchemParser.concatArray(ret, this['output'](buffer)); - ret.push({ type_: 'hyphen' }); - } else { - c1 = mhchemParser.patterns.match_('digits', buffer.d || ""); - if (isAfterD && c1 && c1.remainder==='') { - mhchemParser.concatArray(ret, mhchemParser.actions['d='](buffer, m)); - mhchemParser.concatArray(ret, this['output'](buffer)); - } else { - mhchemParser.concatArray(ret, this['output'](buffer)); - mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); - } - } - return ret; - }, - 'a to o': function (buffer) { - buffer.o = buffer.a; - buffer.a = undefined; - }, - 'sb=true': function (buffer) { buffer.sb = true; }, - 'sb=false': function (buffer) { buffer.sb = false; }, - 'beginsWithBond=true': function (buffer) { buffer['beginsWithBond'] = true; }, - 'beginsWithBond=false': function (buffer) { buffer['beginsWithBond'] = false; }, - 'parenthesisLevel++': function (buffer) { buffer['parenthesisLevel']++; }, - 'parenthesisLevel--': function (buffer) { buffer['parenthesisLevel']--; }, - 'state of aggregation': function (buffer, m) { - return { type_: 'state of aggregation', p1: mhchemParser.go(m, 'o') }; - }, - 'comma': function (buffer, m) { - var a = m.replace(/\s*$/, ''); - var withSpace = (a !== m); - if (withSpace && buffer['parenthesisLevel'] === 0) { - return { type_: 'comma enumeration L', p1: a }; - } else { - return { type_: 'comma enumeration M', p1: a }; - } - }, - 'output': function (buffer, m, entityFollows) { - // entityFollows: - // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb) - // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1) - // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as) - /** @type {ParserOutput | ParserOutput[]} */ - var ret; - if (!buffer.r) { - ret = []; - if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) { - //ret = []; - } else { - if (buffer.sb) { - ret.push({ type_: 'entitySkip' }); - } - if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows!==2) { - buffer.o = buffer.a; - buffer.a = undefined; - } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) { - buffer.o = buffer.a; - buffer.d = buffer.b; - buffer.q = buffer.p; - buffer.a = buffer.b = buffer.p = undefined; - } else { - if (buffer.o && buffer.dType==='kv' && mhchemParser.patterns.match_('d-oxidation$', buffer.d || "")) { - buffer.dType = 'oxidation'; - } else if (buffer.o && buffer.dType==='kv' && !buffer.q) { - buffer.dType = undefined; - } - } - ret.push({ - type_: 'chemfive', - a: mhchemParser.go(buffer.a, 'a'), - b: mhchemParser.go(buffer.b, 'bd'), - p: mhchemParser.go(buffer.p, 'pq'), - o: mhchemParser.go(buffer.o, 'o'), - q: mhchemParser.go(buffer.q, 'pq'), - d: mhchemParser.go(buffer.d, (buffer.dType === 'oxidation' ? 'oxidation' : 'bd')), - dType: buffer.dType - }); - } - } else { // r - /** @type {ParserOutput[]} */ - var rd; - if (buffer.rdt === 'M') { - rd = mhchemParser.go(buffer.rd, 'tex-math'); - } else if (buffer.rdt === 'T') { - rd = [ { type_: 'text', p1: buffer.rd || "" } ]; - } else { - rd = mhchemParser.go(buffer.rd); - } - /** @type {ParserOutput[]} */ - var rq; - if (buffer.rqt === 'M') { - rq = mhchemParser.go(buffer.rq, 'tex-math'); - } else if (buffer.rqt === 'T') { - rq = [ { type_: 'text', p1: buffer.rq || ""} ]; - } else { - rq = mhchemParser.go(buffer.rq); - } - ret = { - type_: 'arrow', - r: buffer.r, - rd: rd, - rq: rq - }; - } - for (var p in buffer) { - if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') { - delete buffer[p]; - } - } - return ret; - }, - 'oxidation-output': function (buffer, m) { - var ret = [ "{" ]; - mhchemParser.concatArray(ret, mhchemParser.go(m, 'oxidation')); - ret.push("}"); - return ret; - }, - 'frac-output': function (buffer, m) { - return { type_: 'frac-ce', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'overset-output': function (buffer, m) { - return { type_: 'overset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'underset-output': function (buffer, m) { - return { type_: 'underset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'underbrace-output': function (buffer, m) { - return { type_: 'underbrace', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; - }, - 'color-output': function (buffer, m) { - return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1]) }; - }, - 'r=': function (buffer, m) { buffer.r = m; }, - 'rdt=': function (buffer, m) { buffer.rdt = m; }, - 'rd=': function (buffer, m) { buffer.rd = m; }, - 'rqt=': function (buffer, m) { buffer.rqt = m; }, - 'rq=': function (buffer, m) { buffer.rq = m; }, - 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; } - } - }, - 'a': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - '1/2$': { - '0': { action_: '1/2' } }, - 'else': { - '0': { nextState: '1', revisit: true } }, - '$(...)$': { - '*': { action_: 'tex-math tight', nextState: '1' } }, - ',': { - '*': { action_: { type_: 'insert', option: 'commaDecimal' } } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: {} - }, - 'o': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - '1/2$': { - '0': { action_: '1/2' } }, - 'else': { - '0': { nextState: '1', revisit: true } }, - 'letters': { - '*': { action_: 'rm' } }, - '\\ca': { - '*': { action_: { type_: 'insert', option: 'circa' } } }, - '\\x{}{}|\\x{}|\\x': { - '*': { action_: 'copy' } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '{(...)}': { - '*': { action_: '{text}' } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: {} - }, - 'text': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '{...}': { - '*': { action_: 'text=' } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '\\greek': { - '*': { action_: [ 'output', 'rm' ] } }, - '\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: [ 'output', 'copy' ] } }, - 'else': { - '*': { action_: 'text=' } } - }), - actions: { - 'output': function (buffer) { - if (buffer.text_) { - /** @type {ParserOutput} */ - var ret = { type_: 'text', p1: buffer.text_ }; - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - } - }, - 'pq': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - 'state of aggregation $': { - '*': { action_: 'state of aggregation' } }, - 'i$': { - '0': { nextState: '!f', revisit: true } }, - '(KV letters),': { - '0': { action_: 'rm', nextState: '0' } }, - 'formula$': { - '0': { nextState: 'f', revisit: true } }, - '1/2$': { - '0': { action_: '1/2' } }, - 'else': { - '0': { nextState: '!f', revisit: true } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '{(...)}': { - '*': { action_: 'text' } }, - 'a-z': { - 'f': { action_: 'tex-math' } }, - 'letters': { - '*': { action_: 'rm' } }, - '-9.,9': { - '*': { action_: '9,9' } }, - ',': { - '*': { action_: { type_: 'insert+p1', option: 'comma enumeration S' } } }, - '\\color{(...)}{(...)}1|\\color(...){(...)}2': { - '*': { action_: 'color-output' } }, - '\\color{(...)}0': { - '*': { action_: 'color0-output' } }, - '\\ce{(...)}': { - '*': { action_: 'ce' } }, - '\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'copy' } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: { - 'state of aggregation': function (buffer, m) { - return { type_: 'state of aggregation subscript', p1: mhchemParser.go(m, 'o') }; - }, - 'color-output': function (buffer, m) { - return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'pq') }; - } - } - }, - 'bd': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - 'x$': { - '0': { nextState: '!f', revisit: true } }, - 'formula$': { - '0': { nextState: 'f', revisit: true } }, - 'else': { - '0': { nextState: '!f', revisit: true } }, - '-9.,9 no missing 0': { - '*': { action_: '9,9' } }, - '.': { - '*': { action_: { type_: 'insert', option: 'electron dot' } } }, - 'a-z': { - 'f': { action_: 'tex-math' } }, - 'x': { - '*': { action_: { type_: 'insert', option: 'KV x' } } }, - 'letters': { - '*': { action_: 'rm' } }, - '\'': { - '*': { action_: { type_: 'insert', option: 'prime' } } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - '{(...)}': { - '*': { action_: 'text' } }, - '\\color{(...)}{(...)}1|\\color(...){(...)}2': { - '*': { action_: 'color-output' } }, - '\\color{(...)}0': { - '*': { action_: 'color0-output' } }, - '\\ce{(...)}': { - '*': { action_: 'ce' } }, - '\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'copy' } }, - 'else2': { - '*': { action_: 'copy' } } - }), - actions: { - 'color-output': function (buffer, m) { - return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'bd') }; - } - } - }, - 'oxidation': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - 'roman numeral': { - '*': { action_: 'roman-numeral' } }, - '${(...)}$|$(...)$': { - '*': { action_: 'tex-math' } }, - 'else': { - '*': { action_: 'copy' } } - }), - actions: { - 'roman-numeral': function (buffer, m) { return { type_: 'roman numeral', p1: m || "" }; } - } - }, - 'tex-math': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '\\ce{(...)}': { - '*': { action_: [ 'output', 'ce' ] } }, - '{...}|\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'o=' } }, - 'else': { - '*': { action_: 'o=' } } - }), - actions: { - 'output': function (buffer) { - if (buffer.o) { - /** @type {ParserOutput} */ - var ret = { type_: 'tex-math', p1: buffer.o }; - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - } - }, - 'tex-math tight': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '\\ce{(...)}': { - '*': { action_: [ 'output', 'ce' ] } }, - '{...}|\\,|\\x{}{}|\\x{}|\\x': { - '*': { action_: 'o=' } }, - '-|+': { - '*': { action_: 'tight operator' } }, - 'else': { - '*': { action_: 'o=' } } - }), - actions: { - 'tight operator': function (buffer, m) { buffer.o = (buffer.o || "") + "{"+m+"}"; }, - 'output': function (buffer) { - if (buffer.o) { - /** @type {ParserOutput} */ - var ret = { type_: 'tex-math', p1: buffer.o }; - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - } - }, - '9,9': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': {} }, - ',': { - '*': { action_: 'comma' } }, - 'else': { - '*': { action_: 'copy' } } - }), - actions: { - 'comma': function () { return { type_: 'commaDecimal' }; } - } - }, - //#endregion - // - // \pu state machines - // - //#region pu - 'pu': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - 'space$': { - '*': { action_: [ 'output', 'space' ] } }, - '{[(|)]}': { - '0|a': { action_: 'copy' } }, - '(-)(9)^(-9)': { - '0': { action_: 'number^', nextState: 'a' } }, - '(-)(9.,9)(e)(99)': { - '0': { action_: 'enumber', nextState: 'a' } }, - 'space': { - '0|a': {} }, - 'pm-operator': { - '0|a': { action_: { type_: 'operator', option: '\\pm' }, nextState: '0' } }, - 'operator': { - '0|a': { action_: 'copy', nextState: '0' } }, - '//': { - 'd': { action_: 'o=', nextState: '/' } }, - '/': { - 'd': { action_: 'o=', nextState: '/' } }, - '{...}|else': { - '0|d': { action_: 'd=', nextState: 'd' }, - 'a': { action_: [ 'space', 'd=' ], nextState: 'd' }, - '/|q': { action_: 'q=', nextState: 'q' } } - }), - actions: { - 'enumber': function (buffer, m) { - /** @type {ParserOutput[]} */ - var ret = []; - if (m[0] === "+-" || m[0] === "+/-") { - ret.push("\\pm "); - } else if (m[0]) { - ret.push(m[0]); - } - if (m[1]) { - mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); - if (m[2]) { - if (m[2].match(/[,.]/)) { - mhchemParser.concatArray(ret, mhchemParser.go(m[2], 'pu-9,9')); - } else { - ret.push(m[2]); - } - } - m[3] = m[4] || m[3]; - if (m[3]) { - m[3] = m[3].trim(); - if (m[3] === "e" || m[3].substr(0, 1) === "*") { - ret.push({ type_: 'cdot' }); - } else { - ret.push({ type_: 'times' }); - } - } - } - if (m[3]) { - ret.push("10^{"+m[5]+"}"); - } - return ret; - }, - 'number^': function (buffer, m) { - /** @type {ParserOutput[]} */ - var ret = []; - if (m[0] === "+-" || m[0] === "+/-") { - ret.push("\\pm "); - } else if (m[0]) { - ret.push(m[0]); - } - mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); - ret.push("^{"+m[2]+"}"); - return ret; - }, - 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; }, - 'space': function () { return { type_: 'pu-space-1' }; }, - 'output': function (buffer) { - /** @type {ParserOutput | ParserOutput[]} */ - var ret; - var md = mhchemParser.patterns.match_('{(...)}', buffer.d || ""); - if (md && md.remainder === '') { buffer.d = md.match_; } - var mq = mhchemParser.patterns.match_('{(...)}', buffer.q || ""); - if (mq && mq.remainder === '') { buffer.q = mq.match_; } - if (buffer.d) { - buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); - buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); - } - if (buffer.q) { // fraction - buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); - buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); - var b5 = { - d: mhchemParser.go(buffer.d, 'pu'), - q: mhchemParser.go(buffer.q, 'pu') - }; - if (buffer.o === '//') { - ret = { type_: 'pu-frac', p1: b5.d, p2: b5.q }; - } else { - ret = b5.d; - if (b5.d.length > 1 || b5.q.length > 1) { - ret.push({ type_: ' / ' }); - } else { - ret.push({ type_: '/' }); - } - mhchemParser.concatArray(ret, b5.q); - } - } else { // no fraction - ret = mhchemParser.go(buffer.d, 'pu-2'); - } - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - }, - 'pu-2': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '*': { action_: 'output' } }, - '*': { - '*': { action_: [ 'output', 'cdot' ], nextState: '0' } }, - '\\x': { - '*': { action_: 'rm=' } }, - 'space': { - '*': { action_: [ 'output', 'space' ], nextState: '0' } }, - '^{(...)}|^(-1)': { - '1': { action_: '^(-1)' } }, - '-9.,9': { - '0': { action_: 'rm=', nextState: '0' }, - '1': { action_: '^(-1)', nextState: '0' } }, - '{...}|else': { - '*': { action_: 'rm=', nextState: '1' } } - }), - actions: { - 'cdot': function () { return { type_: 'tight cdot' }; }, - '^(-1)': function (buffer, m) { buffer.rm += "^{"+m+"}"; }, - 'space': function () { return { type_: 'pu-space-2' }; }, - 'output': function (buffer) { - /** @type {ParserOutput | ParserOutput[]} */ - var ret = []; - if (buffer.rm) { - var mrm = mhchemParser.patterns.match_('{(...)}', buffer.rm || ""); - if (mrm && mrm.remainder === '') { - ret = mhchemParser.go(mrm.match_, 'pu'); - } else { - ret = { type_: 'rm', p1: buffer.rm }; - } - } - for (var p in buffer) { delete buffer[p]; } - return ret; - } - } - }, - 'pu-9,9': { - transitions: mhchemParser.createTransitions({ - 'empty': { - '0': { action_: 'output-0' }, - 'o': { action_: 'output-o' } }, - ',': { - '0': { action_: [ 'output-0', 'comma' ], nextState: 'o' } }, - '.': { - '0': { action_: [ 'output-0', 'copy' ], nextState: 'o' } }, - 'else': { - '*': { action_: 'text=' } } - }), - actions: { - 'comma': function () { return { type_: 'commaDecimal' }; }, - 'output-0': function (buffer) { - /** @type {ParserOutput[]} */ - var ret = []; - buffer.text_ = buffer.text_ || ""; - if (buffer.text_.length > 4) { - var a = buffer.text_.length % 3; - if (a === 0) { a = 3; } - for (var i=buffer.text_.length-3; i>0; i-=3) { - ret.push(buffer.text_.substr(i, 3)); - ret.push({ type_: '1000 separator' }); - } - ret.push(buffer.text_.substr(0, a)); - ret.reverse(); - } else { - ret.push(buffer.text_); - } - for (var p in buffer) { delete buffer[p]; } - return ret; - }, - 'output-o': function (buffer) { - /** @type {ParserOutput[]} */ - var ret = []; - buffer.text_ = buffer.text_ || ""; - if (buffer.text_.length > 4) { - var a = buffer.text_.length - 3; - for (var i=0; i w/height="0" - //res += "^{\\smash[t]{\\vphantom{2}}\\mathllap{"+(b5.b||"")+"}}"; - res += "^{\\vphantom{2}\\mathllap{"+(b5.b||"")+"}}"; - //res += "_{\\vphantom{2}\\mathllap{\\smash[t]{"+(b5.p||"")+"}}}"; - res += "_{\\vphantom{2}\\mathllap{"+(b5.p||"")+"}}"; - } - // - // o - // - if (b5.o) { - if (b5.o.match(/^[+\-]/)) { b5.o = "{"+b5.o+"}"; } - res += b5.o; - } - // - // q and d - // - if (buf.dType === 'kv') { - if (b5.d || b5.q) { - res += "{\\vphantom{X}}"; - } - if (b5.d) { - res += "^{"+b5.d+"}"; - } - if (b5.q) { - // In the next line, I've removed \smash[t] (ron) - // TODO: Revert \smash[t] when WebKit properly renders w/height="0" - //res += "_{\\smash[t]{"+b5.q+"}}"; - res += "_{"+b5.q+"}"; - } - } else if (buf.dType === 'oxidation') { - if (b5.d) { - res += "{\\vphantom{X}}"; - res += "^{"+b5.d+"}"; - } - if (b5.q) { - // A Firefox bug adds a bogus depth to , so we change \vphantom{X} to {} - // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. -// res += "{\\vphantom{X}}"; - res += "{{}}"; - // In the next line, I've removed \smash[t] (ron) - // TODO: Revert \smash[t] when WebKit properly renders w/height="0" - //res += "_{\\smash[t]{"+b5.q+"}}"; - res += "_{"+b5.q+"}"; - } - } else { - if (b5.q) { - // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. -// res += "{\\vphantom{X}}"; - res += "{{}}"; - // In the next line, I've removed \smash[t] (ron) - // TODO: Revert \smash[t] when WebKit properly renders w/height="0" - //res += "_{\\smash[t]{"+b5.q+"}}"; - res += "_{"+b5.q+"}"; - } - if (b5.d) { - // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. -// res += "{\\vphantom{X}}"; - res += "{{}}"; - res += "^{"+b5.d+"}"; - } - } - break; - case 'rm': - res = "\\mathrm{"+buf.p1+"}"; - break; - case 'text': - if (buf.p1.match(/[\^_]/)) { - buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}"); - res = "\\mathrm{"+buf.p1+"}"; - } else { - res = "\\text{"+buf.p1+"}"; - } - break; - case 'roman numeral': - res = "\\mathrm{"+buf.p1+"}"; - break; - case 'state of aggregation': - res = "\\mskip2mu "+texify._goInner(buf.p1); - break; - case 'state of aggregation subscript': - res = "\\mskip1mu "+texify._goInner(buf.p1); - break; - case 'bond': - res = texify._getBond(buf.kind_); - if (!res) { - throw ["MhchemErrorBond", "mhchem Error. Unknown bond type (" + buf.kind_ + ")"]; - } - break; - case 'frac': - var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}"; - res = "\\mathchoice{\\textstyle"+c+"}{"+c+"}{"+c+"}{"+c+"}"; - break; - case 'pu-frac': - var d = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - res = "\\mathchoice{\\textstyle"+d+"}{"+d+"}{"+d+"}{"+d+"}"; - break; - case 'tex-math': - res = buf.p1 + " "; - break; - case 'frac-ce': - res = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - break; - case 'overset': - res = "\\overset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - break; - case 'underset': - res = "\\underset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; - break; - case 'underbrace': - res = "\\underbrace{" + texify._goInner(buf.p1) + "}_{" + texify._goInner(buf.p2) + "}"; - break; - case 'color': - res = "{\\color{" + buf.color1 + "}{" + texify._goInner(buf.color2) + "}}"; - break; - case 'color0': - res = "\\color{" + buf.color + "}"; - break; - case 'arrow': - var b6 = { - rd: texify._goInner(buf.rd), - rq: texify._goInner(buf.rq) - }; - var arrow = texify._getArrow(buf.r); - if (b6.rq) { arrow += "[{\\rm " + b6.rq + "}]"; } - if (b6.rd) { - arrow += "{\\rm " + b6.rd + "}"; - } else { - arrow += "{}"; - } - res = arrow; - break; - case 'operator': - res = texify._getOperator(buf.kind_); - break; - case '1st-level escape': - res = buf.p1+" "; // &, \\\\, \\hlin - break; - case 'space': - res = " "; - break; - case 'entitySkip': - res = "~"; - break; - case 'pu-space-1': - res = "~"; - break; - case 'pu-space-2': - res = "\\mkern3mu "; - break; - case '1000 separator': - res = "\\mkern2mu "; - break; - case 'commaDecimal': - res = "{,}"; - break; - case 'comma enumeration L': - res = "{"+buf.p1+"}\\mkern6mu "; - break; - case 'comma enumeration M': - res = "{"+buf.p1+"}\\mkern3mu "; - break; - case 'comma enumeration S': - res = "{"+buf.p1+"}\\mkern1mu "; - break; - case 'hyphen': - res = "\\text{-}"; - break; - case 'addition compound': - res = "\\,{\\cdot}\\,"; - break; - case 'electron dot': - res = "\\mkern1mu \\text{\\textbullet}\\mkern1mu "; - break; - case 'KV x': - res = "{\\times}"; - break; - case 'prime': - res = "\\prime "; - break; - case 'cdot': - res = "\\cdot "; - break; - case 'tight cdot': - res = "\\mkern1mu{\\cdot}\\mkern1mu "; - break; - case 'times': - res = "\\times "; - break; - case 'circa': - res = "{\\sim}"; - break; - case '^': - res = "uparrow"; - break; - case 'v': - res = "downarrow"; - break; - case 'ellipsis': - res = "\\ldots "; - break; - case '/': - res = "/"; - break; - case ' / ': - res = "\\,/\\,"; - break; - default: - assertNever(buf); - throw ["MhchemBugT", "mhchem bug T. Please report."]; // Missing texify rule or unknown MhchemParser output - } - assertString(res); - return res; - }, - _getArrow: function (a) { - switch (a) { - case "->": return "\\yields"; - case "\u2192": return "\\yields"; - case "\u27F6": return "\\yields"; - case "<-": return "\\yieldsLeft"; - case "<->": return "\\mesomerism"; - case "<-->": return "\\yieldsLeftRight"; - case "<=>": return "\\chemequilibrium"; - case "\u21CC": return "\\chemequilibrium"; - case "<=>>": return "\\equilibriumRight"; - case "<<=>": return "\\equilibriumLeft"; - default: - assertNever(a); - throw ["MhchemBugT", "mhchem bug T. Please report."]; - } - }, - _getBond: function (a) { - switch (a) { - case "-": return "{-}"; - case "1": return "{-}"; - case "=": return "{=}"; - case "2": return "{=}"; - case "#": return "{\\equiv}"; - case "3": return "{\\equiv}"; - case "~": return "{\\tripleDash}"; - case "~-": return "{\\tripleDashOverLine}"; - case "~=": return "{\\tripleDashOverDoubleLine}"; - case "~--": return "{\\tripleDashOverDoubleLine}"; - case "-~-": return "{\\tripleDashBetweenDoubleLine}"; - case "...": return "{{\\cdot}{\\cdot}{\\cdot}}"; - case "....": return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}"; - case "->": return "{\\rightarrow}"; - case "<-": return "{\\leftarrow}"; - case "<": return "{<}"; - case ">": return "{>}"; - default: - assertNever(a); - throw ["MhchemBugT", "mhchem bug T. Please report."]; - } - }, - _getOperator: function (a) { - switch (a) { - case "+": return " {}+{} "; - case "-": return " {}-{} "; - case "=": return " {}={} "; - case "<": return " {}<{} "; - case ">": return " {}>{} "; - case "<<": return " {}\\ll{} "; - case ">>": return " {}\\gg{} "; - case "\\pm": return " {}\\pm{} "; - case "\\approx": return " {}\\approx{} "; - case "$\\approx$": return " {}\\approx{} "; - case "v": return " \\downarrow{} "; - case "(v)": return " \\downarrow{} "; - case "^": return " \\uparrow{} "; - case "(^)": return " \\uparrow{} "; - default: - assertNever(a); - throw ["MhchemBugT", "mhchem bug T. Please report."]; - } - } - }; - - // - // Helpers for code analysis - // Will show type error at calling position - // - /** @param {number} a */ - function assertNever(a) {} - /** @param {string} a */ - function assertString(a) {} - -/* eslint-disable no-undef */ - -////////////////////////////////////////////////////////////////////// -// texvc.sty - -// The texvc package contains macros available in mediawiki pages. -// We omit the functions deprecated at -// https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax - -// We also omit texvc's \O, which conflicts with \text{\O} - -defineMacro("\\darr", "\\downarrow"); -defineMacro("\\dArr", "\\Downarrow"); -defineMacro("\\Darr", "\\Downarrow"); -defineMacro("\\lang", "\\langle"); -defineMacro("\\rang", "\\rangle"); -defineMacro("\\uarr", "\\uparrow"); -defineMacro("\\uArr", "\\Uparrow"); -defineMacro("\\Uarr", "\\Uparrow"); -defineMacro("\\N", "\\mathbb{N}"); -defineMacro("\\R", "\\mathbb{R}"); -defineMacro("\\Z", "\\mathbb{Z}"); -defineMacro("\\alef", "\\aleph"); -defineMacro("\\alefsym", "\\aleph"); -defineMacro("\\bull", "\\bullet"); -defineMacro("\\clubs", "\\clubsuit"); -defineMacro("\\cnums", "\\mathbb{C}"); -defineMacro("\\Complex", "\\mathbb{C}"); -defineMacro("\\Dagger", "\\ddagger"); -defineMacro("\\diamonds", "\\diamondsuit"); -defineMacro("\\empty", "\\emptyset"); -defineMacro("\\exist", "\\exists"); -defineMacro("\\harr", "\\leftrightarrow"); -defineMacro("\\hArr", "\\Leftrightarrow"); -defineMacro("\\Harr", "\\Leftrightarrow"); -defineMacro("\\hearts", "\\heartsuit"); -defineMacro("\\image", "\\Im"); -defineMacro("\\infin", "\\infty"); -defineMacro("\\isin", "\\in"); -defineMacro("\\larr", "\\leftarrow"); -defineMacro("\\lArr", "\\Leftarrow"); -defineMacro("\\Larr", "\\Leftarrow"); -defineMacro("\\lrarr", "\\leftrightarrow"); -defineMacro("\\lrArr", "\\Leftrightarrow"); -defineMacro("\\Lrarr", "\\Leftrightarrow"); -defineMacro("\\natnums", "\\mathbb{N}"); -defineMacro("\\plusmn", "\\pm"); -defineMacro("\\rarr", "\\rightarrow"); -defineMacro("\\rArr", "\\Rightarrow"); -defineMacro("\\Rarr", "\\Rightarrow"); -defineMacro("\\real", "\\Re"); -defineMacro("\\reals", "\\mathbb{R}"); -defineMacro("\\Reals", "\\mathbb{R}"); -defineMacro("\\sdot", "\\cdot"); -defineMacro("\\sect", "\\S"); -defineMacro("\\spades", "\\spadesuit"); -defineMacro("\\sub", "\\subset"); -defineMacro("\\sube", "\\subseteq"); -defineMacro("\\supe", "\\supseteq"); -defineMacro("\\thetasym", "\\vartheta"); -defineMacro("\\weierp", "\\wp"); - -/* eslint-disable no-undef */ - -/**************************************************** - * - * physics.js - * - * Implements the Physics Package for LaTeX input. - * - * --------------------------------------------------------------------- - * - * The original version of this file is licensed as follows: - * Copyright (c) 2015-2016 Kolen Cheung . - * - * 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. - * - * --------------------------------------------------------------------- - * - * This file has been revised from the original in the following ways: - * 1. The interface is changed so that it can be called from Temml, not MathJax. - * 2. \Re and \Im are not used, to avoid conflict with existing LaTeX letters. - * - * This revision of the file is released under the MIT license. - * https://mit-license.org/ - */ -defineMacro("\\quantity", "{\\left\\{ #1 \\right\\}}"); -defineMacro("\\qty", "{\\left\\{ #1 \\right\\}}"); -defineMacro("\\pqty", "{\\left( #1 \\right)}"); -defineMacro("\\bqty", "{\\left[ #1 \\right]}"); -defineMacro("\\vqty", "{\\left\\vert #1 \\right\\vert}"); -defineMacro("\\Bqty", "{\\left\\{ #1 \\right\\}}"); -defineMacro("\\absolutevalue", "{\\left\\vert #1 \\right\\vert}"); -defineMacro("\\abs", "{\\left\\vert #1 \\right\\vert}"); -defineMacro("\\norm", "{\\left\\Vert #1 \\right\\Vert}"); -defineMacro("\\evaluated", "{\\left.#1 \\right\\vert}"); -defineMacro("\\eval", "{\\left.#1 \\right\\vert}"); -defineMacro("\\order", "{\\mathcal{O} \\left( #1 \\right)}"); -defineMacro("\\commutator", "{\\left[ #1 , #2 \\right]}"); -defineMacro("\\comm", "{\\left[ #1 , #2 \\right]}"); -defineMacro("\\anticommutator", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\acomm", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\poissonbracket", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\pb", "{\\left\\{ #1 , #2 \\right\\}}"); -defineMacro("\\vectorbold", "{\\boldsymbol{ #1 }}"); -defineMacro("\\vb", "{\\boldsymbol{ #1 }}"); -defineMacro("\\vectorarrow", "{\\vec{\\boldsymbol{ #1 }}}"); -defineMacro("\\va", "{\\vec{\\boldsymbol{ #1 }}}"); -defineMacro("\\vectorunit", "{{\\boldsymbol{\\hat{ #1 }}}}"); -defineMacro("\\vu", "{{\\boldsymbol{\\hat{ #1 }}}}"); -defineMacro("\\dotproduct", "\\mathbin{\\boldsymbol\\cdot}"); -defineMacro("\\vdot", "{\\boldsymbol\\cdot}"); -defineMacro("\\crossproduct", "\\mathbin{\\boldsymbol\\times}"); -defineMacro("\\cross", "\\mathbin{\\boldsymbol\\times}"); -defineMacro("\\cp", "\\mathbin{\\boldsymbol\\times}"); -defineMacro("\\gradient", "{\\boldsymbol\\nabla}"); -defineMacro("\\grad", "{\\boldsymbol\\nabla}"); -defineMacro("\\divergence", "{\\grad\\vdot}"); -//defineMacro("\\div", "{\\grad\\vdot}"); Not included in Temml. Conflicts w/LaTeX \div -defineMacro("\\curl", "{\\grad\\cross}"); -defineMacro("\\laplacian", "\\nabla^2"); -defineMacro("\\tr", "{\\operatorname{tr}}"); -defineMacro("\\Tr", "{\\operatorname{Tr}}"); -defineMacro("\\rank", "{\\operatorname{rank}}"); -defineMacro("\\erf", "{\\operatorname{erf}}"); -defineMacro("\\Res", "{\\operatorname{Res}}"); -defineMacro("\\principalvalue", "{\\mathcal{P}}"); -defineMacro("\\pv", "{\\mathcal{P}}"); -defineMacro("\\PV", "{\\operatorname{P.V.}}"); -// Temml does not use the next two lines. They conflict with LaTeX letters. -//defineMacro("\\Re", "{\\operatorname{Re} \\left\\{ #1 \\right\\}}"); -//defineMacro("\\Im", "{\\operatorname{Im} \\left\\{ #1 \\right\\}}"); -defineMacro("\\qqtext", "{\\quad\\text{ #1 }\\quad}"); -defineMacro("\\qq", "{\\quad\\text{ #1 }\\quad}"); -defineMacro("\\qcomma", "{\\text{,}\\quad}"); -defineMacro("\\qc", "{\\text{,}\\quad}"); -defineMacro("\\qcc", "{\\quad\\text{c.c.}\\quad}"); -defineMacro("\\qif", "{\\quad\\text{if}\\quad}"); -defineMacro("\\qthen", "{\\quad\\text{then}\\quad}"); -defineMacro("\\qelse", "{\\quad\\text{else}\\quad}"); -defineMacro("\\qotherwise", "{\\quad\\text{otherwise}\\quad}"); -defineMacro("\\qunless", "{\\quad\\text{unless}\\quad}"); -defineMacro("\\qgiven", "{\\quad\\text{given}\\quad}"); -defineMacro("\\qusing", "{\\quad\\text{using}\\quad}"); -defineMacro("\\qassume", "{\\quad\\text{assume}\\quad}"); -defineMacro("\\qsince", "{\\quad\\text{since}\\quad}"); -defineMacro("\\qlet", "{\\quad\\text{let}\\quad}"); -defineMacro("\\qfor", "{\\quad\\text{for}\\quad}"); -defineMacro("\\qall", "{\\quad\\text{all}\\quad}"); -defineMacro("\\qeven", "{\\quad\\text{even}\\quad}"); -defineMacro("\\qodd", "{\\quad\\text{odd}\\quad}"); -defineMacro("\\qinteger", "{\\quad\\text{integer}\\quad}"); -defineMacro("\\qand", "{\\quad\\text{and}\\quad}"); -defineMacro("\\qor", "{\\quad\\text{or}\\quad}"); -defineMacro("\\qas", "{\\quad\\text{as}\\quad}"); -defineMacro("\\qin", "{\\quad\\text{in}\\quad}"); -defineMacro("\\differential", "{\\text{d}}"); -defineMacro("\\dd", "{\\text{d}}"); -defineMacro("\\derivative", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}"); -defineMacro("\\dv", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}"); -defineMacro("\\partialderivative", "{\\frac{\\partial{ #1 }}{\\partial{ #2 }}}"); -defineMacro("\\variation", "{\\delta}"); -defineMacro("\\var", "{\\delta}"); -defineMacro("\\functionalderivative", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}"); -defineMacro("\\fdv", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}"); -defineMacro("\\innerproduct", "{\\left\\langle {#1} \\mid { #2} \\right\\rangle}"); -defineMacro("\\outerproduct", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\dyad", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\ketbra", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\op", - "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); -defineMacro("\\expectationvalue", "{\\left\\langle {#1 } \\right\\rangle}"); -defineMacro("\\expval", "{\\left\\langle {#1 } \\right\\rangle}"); -defineMacro("\\ev", "{\\left\\langle {#1 } \\right\\rangle}"); -defineMacro("\\matrixelement", - "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); -defineMacro("\\matrixel", - "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); -defineMacro("\\mel", - "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); - -// Helper functions -function getHLines(parser) { - // Return an array. The array length = number of hlines. - // Each element in the array tells if the line is dashed. - const hlineInfo = []; - parser.consumeSpaces(); - let nxt = parser.fetch().text; - if (nxt === "\\relax") { - parser.consume(); - parser.consumeSpaces(); - nxt = parser.fetch().text; - } - while (nxt === "\\hline" || nxt === "\\hdashline") { - parser.consume(); - hlineInfo.push(nxt === "\\hdashline"); - parser.consumeSpaces(); - nxt = parser.fetch().text; - } - return hlineInfo; -} - -const validateAmsEnvironmentContext = context => { - const settings = context.parser.settings; - if (!settings.displayMode) { - throw new ParseError(`{${context.envName}} can be used only in display mode.`); - } -}; - -const sizeRegEx$1 = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; -const arrayGaps = macros => { - let arraystretch = macros.get("\\arraystretch"); - if (typeof arraystretch !== "string") { - arraystretch = stringFromArg(arraystretch.tokens); - } - arraystretch = isNaN(arraystretch) ? null : Number(arraystretch); - let arraycolsepStr = macros.get("\\arraycolsep"); - if (typeof arraycolsepStr !== "string") { - arraycolsepStr = stringFromArg(arraycolsepStr.tokens); - } - const match = sizeRegEx$1.exec(arraycolsepStr); - const arraycolsep = match - ? { number: +(match[1] + match[2]), unit: match[3] } - : null; - return [arraystretch, arraycolsep] -}; - -const checkCellForLabels = cell => { - // Check if the author wrote a \tag{} inside this cell. - let rowLabel = ""; - for (let i = 0; i < cell.length; i++) { - if (cell[i].type === "label") { - if (rowLabel) { throw new ParseError(("Multiple \\labels in one row")) } - rowLabel = cell[i].string; - } - } - return rowLabel -}; - -// autoTag (an argument to parseArray) can be one of three values: -// * undefined: Regular (not-top-level) array; no tags on each row -// * true: Automatic equation numbering, overridable by \tag -// * false: Tags allowed on each row, but no automatic numbering -// This function *doesn't* work with the "split" environment name. -function getAutoTag(name) { - if (name.indexOf("ed") === -1) { - return name.indexOf("*") === -1; - } - // return undefined; -} - -/** - * Parse the body of the environment, with rows delimited by \\ and - * columns delimited by &, and create a nested list in row-major order - * with one group per cell. If given an optional argument scriptLevel - * ("text", "display", etc.), then each cell is cast into that scriptLevel. - */ -function parseArray( - parser, - { - cols, // [{ type: string , align: l|c|r|null }] - envClasses, // align(ed|at|edat) | array | cases | cd | small | multline - autoTag, // boolean - singleRow, // boolean - emptySingleRow, // boolean - maxNumCols, // number - leqno, // boolean - arraystretch, // number | null - arraycolsep // size value | null -}, - scriptLevel -) { - const endToken = envClasses && envClasses.includes("bordermatrix") ? "}" : "\\end"; - parser.gullet.beginGroup(); - if (!singleRow) { - // \cr is equivalent to \\ without the optional size argument (see below) - // TODO: provide helpful error when \cr is used outside array environment - parser.gullet.macros.set("\\cr", "\\\\\\relax"); - } - - // Start group for first cell - parser.gullet.beginGroup(); - - let row = []; - const body = [row]; - const rowGaps = []; - const labels = []; - - const hLinesBeforeRow = []; - - const tags = (autoTag != null ? [] : undefined); - - // amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent - // whether this row should have an equation number. Simulate this with - // a \@eqnsw macro set to 1 or 0. - function beginRow() { - if (autoTag) { - parser.gullet.macros.set("\\@eqnsw", "1", true); - } - } - function endRow() { - if (tags) { - if (parser.gullet.macros.get("\\df@tag")) { - tags.push(parser.subparse([new Token("\\df@tag")])); - parser.gullet.macros.set("\\df@tag", undefined, true); - } else { - tags.push(Boolean(autoTag) && - parser.gullet.macros.get("\\@eqnsw") === "1"); - } - } - } - beginRow(); - - // Test for \hline at the top of the array. - hLinesBeforeRow.push(getHLines(parser)); - - while (true) { - // Parse each cell in its own group (namespace) - let cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\"); - parser.gullet.endGroup(); - parser.gullet.beginGroup(); - - cell = { - type: "ordgroup", - mode: parser.mode, - body: cell, - semisimple: true - }; - row.push(cell); - const next = parser.fetch().text; - if (next === "&") { - if (maxNumCols && row.length === maxNumCols) { - if (envClasses.includes("array")) { - if (parser.settings.strict) { - throw new ParseError("Too few columns " + "specified in the {array} column argument.", - parser.nextToken) - } - } else if (maxNumCols === 2) { - throw new ParseError("The split environment accepts no more than two columns", - parser.nextToken); - } else { - throw new ParseError("The equation environment accepts only one column", - parser.nextToken) - } - } - parser.consume(); - } else if (next === endToken) { - endRow(); - // Arrays terminate newlines with `\crcr` which consumes a `\cr` if - // the last line is empty. However, AMS environments keep the - // empty row if it's the only one. - // NOTE: Currently, `cell` is the last item added into `row`. - if (row.length === 1 && cell.body.length === 0 && (body.length > 1 || !emptySingleRow)) { - body.pop(); - } - labels.push(checkCellForLabels(cell.body)); - if (hLinesBeforeRow.length < body.length + 1) { - hLinesBeforeRow.push([]); - } - break; - } else if (next === "\\\\") { - parser.consume(); - let size; - // \def\Let@{\let\\\math@cr} - // \def\math@cr{...\math@cr@} - // \def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}} - // \def\math@cr@@[#1]{...\math@cr@@@...} - // \def\math@cr@@@{\cr} - if (parser.gullet.future().text !== " ") { - size = parser.parseSizeGroup(true); - } - rowGaps.push(size ? size.value : null); - endRow(); - - labels.push(checkCellForLabels(cell.body)); - - // check for \hline(s) following the row separator - hLinesBeforeRow.push(getHLines(parser)); - - row = []; - body.push(row); - beginRow(); - } else { - throw new ParseError("Expected & or \\\\ or \\cr or " + endToken, parser.nextToken); - } - } - - // End cell group - parser.gullet.endGroup(); - // End array group defining \cr - parser.gullet.endGroup(); - - return { - type: "array", - mode: parser.mode, - body, - cols, - rowGaps, - hLinesBeforeRow, - envClasses, - autoTag, - scriptLevel, - tags, - labels, - leqno, - arraystretch, - arraycolsep - }; -} - -// Decides on a scriptLevel for cells in an array according to whether the given -// environment name starts with the letter 'd'. -function dCellStyle(envName) { - return envName.slice(0, 1) === "d" ? "display" : "text" -} - -const alignMap = { - c: "center ", - l: "left ", - r: "right " -}; - -const glue = group => { - const glueNode = new MathNode("mtd", []); - glueNode.style = { padding: "0", width: "50%" }; - if (group.envClasses.includes("multline")) { - glueNode.style.width = "7.5%"; - } - return glueNode -}; - -const mathmlBuilder$9 = function(group, style) { - const tbl = []; - const numRows = group.body.length; - const hlines = group.hLinesBeforeRow; - const tagIsPresent = (group.tags && group.tags.some((tag) => tag)); - - for (let i = 0; i < numRows; i++) { - const rw = group.body[i]; - const row = []; - const cellLevel = group.scriptLevel === "text" - ? StyleLevel.TEXT - : group.scriptLevel === "script" - ? StyleLevel.SCRIPT - : StyleLevel.DISPLAY; - - for (let j = 0; j < rw.length; j++) { - const mtd = new MathNode( - "mtd", - [buildGroup$1(rw[j], style.withLevel(cellLevel))] - ); - - if (group.envClasses.includes("multline")) { - const align = i === 0 ? "left" : i === numRows - 1 ? "right" : "center"; - if (align !== "center") { - mtd.classes.push("tml-" + align); - } - } - row.push(mtd); - } - const numColumns = group.body[0].length; - // Fill out a short row with empty elements. - for (let k = 0; k < numColumns - rw.length; k++) { - row.push(new MathNode("mtd", [], [], style)); - } - if (tagIsPresent) { - const tag = group.tags[i]; - let tagElement; - if (tag === true) { // automatic numbering - tagElement = new MathNode("mtext", [new Span(["tml-eqn"])]); - } else if (tag === false) { - // \nonumber/\notag or starred environment - tagElement = new MathNode("mtext", [], []); - } else { // manual \tag - tagElement = buildExpressionRow(tag[0].body, style.withLevel(cellLevel), true); - tagElement = consolidateText(tagElement); - tagElement.classes = ["tml-tag"]; - } - if (tagElement) { - row.unshift(glue(group)); - row.push(glue(group)); - if (group.leqno) { - row[0].children.push(tagElement); - } else { - row[row.length - 1].children.push(tagElement); - } - } - } - const mtr = new MathNode("mtr", row, []); - const label = group.labels.shift(); - if (label && group.tags && group.tags[i]) { - mtr.setAttribute("id", label); - if (Array.isArray(group.tags[i])) { mtr.classes.push("tml-tageqn"); } - } - - // Write horizontal rules - if (i === 0 && hlines[0].length > 0) { - if (hlines[0].length === 2) { - mtr.children.forEach(cell => { cell.style.borderTop = "0.15em double"; }); - } else { - mtr.children.forEach(cell => { - cell.style.borderTop = hlines[0][0] ? "0.06em dashed" : "0.06em solid"; - }); - } - } - if (hlines[i + 1].length > 0) { - if (hlines[i + 1].length === 2) { - mtr.children.forEach(cell => { cell.style.borderBottom = "0.15em double"; }); - } else { - mtr.children.forEach(cell => { - cell.style.borderBottom = hlines[i + 1][0] ? "0.06em dashed" : "0.06em solid"; - }); - } - } - - // Check for \hphantom \from \bordermatrix - let mustSquashRow = true; - for (let j = 0; j < mtr.children.length; j++) { - const child = mtr.children[j].children[0]; - if (!(child && child.type === "mpadded" && child.attributes.height === "0px")) { - mustSquashRow = false; - break - } - } - if (mustSquashRow) { - // All the cell contents are \hphantom. Squash the cell. - for (let j = 0; j < mtr.children.length; j++) { - mtr.children[j].style.display = "block"; // necessary in Firefox only - mtr.children[j].style.height = "0"; // necessary in Firefox only - mtr.children[j].style.paddingTop = "0"; - mtr.children[j].style.paddingBottom = "0"; - } - } - - tbl.push(mtr); - } - - if (group.arraystretch && group.arraystretch !== 1) { - // In LaTeX, \arraystretch is a factor applied to a 12pt strut height. - // It defines a baseline to baseline distance. - // Here, we do an approximation of that approach. - const pad = String(1.4 * group.arraystretch - 0.8) + "ex"; - for (let i = 0; i < tbl.length; i++) { - for (let j = 0; j < tbl[i].children.length; j++) { - tbl[i].children[j].style.paddingTop = pad; - tbl[i].children[j].style.paddingBottom = pad; - } - } - } - - let sidePadding; - let sidePadUnit; - if (group.envClasses.length > 0) { - sidePadding = group.envClasses.includes("abut") - ? "0" - : group.envClasses.includes("cases") - ? "0" - : group.envClasses.includes("small") - ? "0.1389" - : group.envClasses.includes("cd") - ? "0.25" - : "0.4"; // default side padding - sidePadUnit = "em"; - } - if (group.arraycolsep) { - const arraySidePad = calculateSize(group.arraycolsep, style); - sidePadding = arraySidePad.number.toFixed(4); - sidePadUnit = arraySidePad.unit; - } - if (sidePadding) { - const numCols = tbl.length === 0 ? 0 : tbl[0].children.length; - - const sidePad = (j, hand) => { - if (j === 0 && hand === 0) { return "0" } - if (j === numCols - 1 && hand === 1) { return "0" } - if (group.envClasses[0] !== "align") { return sidePadding } - if (hand === 1) { return "0" } - if (tagIsPresent) { - return (j % 2) ? "1" : "0" - } else { - return (j % 2) ? "0" : "1" - } - }; - - // Side padding - for (let i = 0; i < tbl.length; i++) { - for (let j = 0; j < tbl[i].children.length; j++) { - tbl[i].children[j].style.paddingLeft = `${sidePad(j, 0)}${sidePadUnit}`; - tbl[i].children[j].style.paddingRight = `${sidePad(j, 1)}${sidePadUnit}`; - } - } - } - if (group.envClasses.length === 0) { - // Set zero padding on side of the matrix - for (let i = 0; i < tbl.length; i++) { - tbl[i].children[0].style.paddingLeft = "0em"; - if (tbl[i].children.length === tbl[0].children.length) { - tbl[i].children[tbl[i].children.length - 1].style.paddingRight = "0em"; - } - } - } - - if (group.envClasses.length > 0) { - // Justification - const align = group.envClasses.includes("align") || group.envClasses.includes("alignat"); - for (let i = 0; i < tbl.length; i++) { - const row = tbl[i]; - if (align) { - for (let j = 0; j < row.children.length; j++) { - // Chromium does not recognize text-align: left. Use -webkit- - // TODO: Remove -webkit- when Chromium no longer needs it. - row.children[j].classes = ["tml-" + (j % 2 ? "left" : "right")]; - } - if (tagIsPresent) { - const k = group.leqno ? 0 : row.children.length - 1; - row.children[k].classes = []; // Default is center. - } - } - if (row.children.length > 1 && group.envClasses.includes("cases")) { - row.children[1].style.paddingLeft = "1em"; - } - - if (group.envClasses.includes("cases") || group.envClasses.includes("subarray")) { - for (const cell of row.children) { - cell.classes.push("tml-left"); - } - } - } - } - - let table = new MathNode("mtable", tbl); - if (group.envClasses.length > 0) { - // Top & bottom padding - if (group.envClasses.includes("jot")) { - table.classes.push("tml-jot"); - } else if (group.envClasses.includes("small")) { - table.classes.push("tml-small"); - } - } - if (group.scriptLevel === "display") { table.setAttribute("displaystyle", "true"); } - - if (group.autoTag || group.envClasses.includes("multline")) { - table.style.width = "100%"; - } - - // Column separator lines and column alignment - - if (group.cols && group.cols.length > 0) { - const cols = group.cols; - let prevTypeWasAlign = false; - let iStart = 0; - let iEnd = cols.length; - - while (cols[iStart].type === "separator") { - iStart += 1; - } - while (cols[iEnd - 1].type === "separator") { - iEnd -= 1; - } - - if (cols[0].type === "separator") { - const sep = cols[1].type === "separator" - ? "0.15em double" - : cols[0].separator === "|" - ? "0.06em solid " - : "0.06em dashed "; - for (const row of table.children) { - row.children[0].style.borderLeft = sep; - } - } - let iCol = tagIsPresent ? 0 : -1; - for (let i = iStart; i < iEnd; i++) { - if (cols[i].type === "align") { - const colAlign = alignMap[cols[i].align]; - iCol += 1; - for (const row of table.children) { - if (colAlign.trim() !== "center" && iCol < row.children.length) { - row.children[iCol].classes = ["tml-" + colAlign.trim()]; - } - } - prevTypeWasAlign = true; - } else if (cols[i].type === "separator") { - // MathML accepts only single lines between cells. - // So we read only the first of consecutive separators. - if (prevTypeWasAlign) { - const sep = cols[i + 1].type === "separator" - ? "0.15em double" - : cols[i].separator === "|" - ? "0.06em solid" - : "0.06em dashed"; - for (const row of table.children) { - if (iCol < row.children.length) { - row.children[iCol].style.borderRight = sep; - } - } - } - prevTypeWasAlign = false; - } - } - if (cols[cols.length - 1].type === "separator") { - const sep = cols[cols.length - 2].type === "separator" - ? "0.15em double" - : cols[cols.length - 1].separator === "|" - ? "0.06em solid" - : "0.06em dashed"; - for (const row of table.children) { - row.children[row.children.length - 1].style.borderRight = sep; - row.children[row.children.length - 1].style.paddingRight = "0.4em"; - } - } - } - - if (group.envClasses.includes("small")) { - // A small array. Wrap in scriptstyle. - table = new MathNode("mstyle", [table]); - table.setAttribute("scriptlevel", "1"); - } - - return table -}; - -// Convenience function for align, align*, aligned, alignat, alignat*, alignedat, split. -const alignedHandler = function(context, args) { - if (context.envName.indexOf("ed") === -1) { - validateAmsEnvironmentContext(context); - } - const isSplit = context.envName === "split"; - const cols = []; - const res = parseArray( - context.parser, - { - cols, - emptySingleRow: true, - autoTag: isSplit ? undefined : getAutoTag(context.envName), - envClasses: ["abut", "jot"], // set row spacing & provisional column spacing - maxNumCols: context.envName === "split" ? 2 : undefined, - leqno: context.parser.settings.leqno - }, - "display" - ); - - // Determining number of columns. - // 1. If the first argument is given, we use it as a number of columns, - // and makes sure that each row doesn't exceed that number. - // 2. Otherwise, just count number of columns = maximum number - // of cells in each row ("aligned" mode -- isAligned will be true). - // - // At the same time, prepend empty group {} at beginning of every second - // cell in each row (starting with second cell) so that operators become - // binary. This behavior is implemented in amsmath's \start@aligned. - let numMaths; - let numCols = 0; - const isAlignedAt = context.envName.indexOf("at") > -1; - if (args[0] && isAlignedAt) { - // alignat environment takes an argument w/ number of columns - let arg0 = ""; - for (let i = 0; i < args[0].body.length; i++) { - const textord = assertNodeType(args[0].body[i], "textord"); - arg0 += textord.text; - } - if (isNaN(arg0)) { - throw new ParseError("The alignat enviroment requires a numeric first argument.") - } - numMaths = Number(arg0); - numCols = numMaths * 2; - } - res.body.forEach(function(row) { - if (isAlignedAt) { - // Case 1 - const curMaths = row.length / 2; - if (numMaths < curMaths) { - throw new ParseError( - "Too many math in a row: " + `expected ${numMaths}, but got ${curMaths}`, - row[0] - ); - } - } else if (numCols < row.length) { - // Case 2 - numCols = row.length; - } - }); - - // Adjusting alignment. - // In aligned mode, we add one \qquad between columns; - // otherwise we add nothing. - for (let i = 0; i < numCols; ++i) { - let align = "r"; - if (i % 2 === 1) { - align = "l"; - } - cols[i] = { - type: "align", - align: align - }; - } - if (context.envName === "split") ; else if (isAlignedAt) { - res.envClasses.push("alignat"); // Sets justification - } else { - res.envClasses[0] = "align"; // Sets column spacing & justification - } - return res; -}; - -// Arrays are part of LaTeX, defined in lttab.dtx so its documentation -// is part of the source2e.pdf file of LaTeX2e source documentation. -// {darray} is an {array} environment where cells are set in \displaystyle, -// as defined in nccmath.sty. -defineEnvironment({ - type: "array", - names: ["array", "darray"], - props: { - numArgs: 1 - }, - handler(context, args) { - // Since no types are specified above, the two possibilities are - // - The argument is wrapped in {} or [], in which case Parser's - // parseGroup() returns an "ordgroup" wrapping some symbol node. - // - The argument is a bare symbol node. - const symNode = checkSymbolNodeType(args[0]); - const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; - const cols = colalign.map(function(nde) { - const node = assertSymbolNodeType(nde); - const ca = node.text; - if ("lcr".indexOf(ca) !== -1) { - return { - type: "align", - align: ca - }; - } else if (ca === "|") { - return { - type: "separator", - separator: "|" - }; - } else if (ca === ":") { - return { - type: "separator", - separator: ":" - }; - } - throw new ParseError("Unknown column alignment: " + ca, nde); - }); - const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); - const res = { - cols, - envClasses: ["array"], - maxNumCols: cols.length, - arraystretch, - arraycolsep - }; - return parseArray(context.parser, res, dCellStyle(context.envName)); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// The matrix environments of amsmath build on the array environment -// of LaTeX, which is discussed above. -// The mathtools package adds starred versions of the same environments. -// These have an optional argument to choose left|center|right justification. -defineEnvironment({ - type: "array", - names: [ - "matrix", - "pmatrix", - "bmatrix", - "Bmatrix", - "vmatrix", - "Vmatrix", - "matrix*", - "pmatrix*", - "bmatrix*", - "Bmatrix*", - "vmatrix*", - "Vmatrix*" - ], - props: { - numArgs: 0 - }, - handler(context) { - const delimiters = { - matrix: null, - pmatrix: ["(", ")"], - bmatrix: ["[", "]"], - Bmatrix: ["\\{", "\\}"], - vmatrix: ["|", "|"], - Vmatrix: ["\\Vert", "\\Vert"] - }[context.envName.replace("*", "")]; - // \hskip -\arraycolsep in amsmath - let colAlign = "c"; - const payload = { - envClasses: [], - cols: [] - }; - if (context.envName.charAt(context.envName.length - 1) === "*") { - // It's one of the mathtools starred functions. - // Parse the optional alignment argument. - const parser = context.parser; - parser.consumeSpaces(); - if (parser.fetch().text === "[") { - parser.consume(); - parser.consumeSpaces(); - colAlign = parser.fetch().text; - if ("lcr".indexOf(colAlign) === -1) { - throw new ParseError("Expected l or c or r", parser.nextToken); - } - parser.consume(); - parser.consumeSpaces(); - parser.expect("]"); - parser.consume(); - payload.cols = []; - } - } - const res = parseArray(context.parser, payload, "text"); - res.cols = res.body.length > 0 - ? new Array(res.body[0].length).fill({ type: "align", align: colAlign }) - : []; - const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); - res.arraystretch = arraystretch; - if (arraycolsep && !(arraycolsep === 6 && arraycolsep === "pt")) { - res.arraycolsep = arraycolsep; - } - return delimiters - ? { - type: "leftright", - mode: context.mode, - body: [res], - left: delimiters[0], - right: delimiters[1], - rightColor: undefined // \right uninfluenced by \color in array - } - : res; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["bordermatrix"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { cols: [], envClasses: ["bordermatrix"] }; - const res = parseArray(context.parser, payload, "text"); - res.cols = res.body.length > 0 - ? new Array(res.body[0].length).fill({ type: "align", align: "c" }) - : []; - res.envClasses = []; - res.arraystretch = 1; - if (context.envName === "matrix") { return res} - return bordermatrixParseTree(res, context.delimiters) - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["smallmatrix"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { envClasses: ["small"] }; - const res = parseArray(context.parser, payload, "script"); - return res; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["subarray"], - props: { - numArgs: 1 - }, - handler(context, args) { - // Parsing of {subarray} is similar to {array} - const symNode = checkSymbolNodeType(args[0]); - const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; - const cols = colalign.map(function(nde) { - const node = assertSymbolNodeType(nde); - const ca = node.text; - // {subarray} only recognizes "l" & "c" - if ("lc".indexOf(ca) !== -1) { - return { - type: "align", - align: ca - }; - } - throw new ParseError("Unknown column alignment: " + ca, nde); - }); - if (cols.length > 1) { - throw new ParseError("{subarray} can contain only one column"); - } - let res = { - cols, - envClasses: ["small"] - }; - res = parseArray(context.parser, res, "script"); - if (res.body.length > 0 && res.body[0].length > 1) { - throw new ParseError("{subarray} can contain only one column"); - } - return res; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// A cases environment (in amsmath.sty) is almost equivalent to -// \def -// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. -// {dcases} is a {cases} environment where cells are set in \displaystyle, -// as defined in mathtools.sty. -// {rcases} is another mathtools environment. It's brace is on the right side. -defineEnvironment({ - type: "array", - names: ["cases", "dcases", "rcases", "drcases"], - props: { - numArgs: 0 - }, - handler(context) { - const payload = { - cols: [], - envClasses: ["cases"] - }; - const res = parseArray(context.parser, payload, dCellStyle(context.envName)); - return { - type: "leftright", - mode: context.mode, - body: [res], - left: context.envName.indexOf("r") > -1 ? "." : "\\{", - right: context.envName.indexOf("r") > -1 ? "\\}" : ".", - rightColor: undefined - }; - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// In the align environment, one uses ampersands, &, to specify number of -// columns in each row, and to locate spacing between each column. -// align gets automatic numbering. align* and aligned do not. -// The alignedat environment can be used in math mode. -defineEnvironment({ - type: "array", - names: ["align", "align*", "aligned", "split"], - props: { - numArgs: 0 - }, - handler: alignedHandler, - mathmlBuilder: mathmlBuilder$9 -}); - -// alignat environment is like an align environment, but one must explicitly -// specify maximum number of columns in each row, and can adjust where spacing occurs. -defineEnvironment({ - type: "array", - names: ["alignat", "alignat*", "alignedat"], - props: { - numArgs: 1 - }, - handler: alignedHandler, - mathmlBuilder: mathmlBuilder$9 -}); - -// A gathered environment is like an array environment with one centered -// column, but where rows are considered lines so get \jot line spacing -// and contents are set in \displaystyle. -defineEnvironment({ - type: "array", - names: ["gathered", "gather", "gather*"], - props: { - numArgs: 0 - }, - handler(context) { - if (context.envName !== "gathered") { - validateAmsEnvironmentContext(context); - } - const res = { - cols: [], - envClasses: ["abut", "jot"], - autoTag: getAutoTag(context.envName), - emptySingleRow: true, - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["equation", "equation*"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - const res = { - autoTag: getAutoTag(context.envName), - emptySingleRow: true, - singleRow: true, - maxNumCols: 1, - envClasses: ["align"], - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["multline", "multline*"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - const res = { - autoTag: context.envName === "multline", - maxNumCols: 1, - envClasses: ["jot", "multline"], - leqno: context.parser.settings.leqno - }; - return parseArray(context.parser, res, "display"); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -defineEnvironment({ - type: "array", - names: ["CD"], - props: { - numArgs: 0 - }, - handler(context) { - validateAmsEnvironmentContext(context); - return parseCD(context.parser); - }, - mathmlBuilder: mathmlBuilder$9 -}); - -// Catch \hline outside array environment -defineFunction({ - type: "text", // Doesn't matter what this is. - names: ["\\hline", "\\hdashline"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: true - }, - handler(context, args) { - throw new ParseError(`${context.funcName} valid only within array environment`); - } -}); - -const environments = _environments; - -// \bordermatrix from TeXbook pp 177 & 361 -// Optional argument from Herbert Voß, Math mode, p 20 -// Ref: https://tug.ctan.org/obsolete/info/math/voss/mathmode/Mathmode.pdf - -defineFunction({ - type: "bordermatrix", - names: ["\\bordermatrix", "\\matrix"], - props: { - numArgs: 0, - numOptionalArgs: 1 - }, - handler: ({ parser, funcName }, args, optArgs) => { - // Find out if the author has defined custom delimiters - let delimiters = ["(", ")"]; - if (funcName === "\\bordermatrix" && optArgs[0] && optArgs[0].body) { - const body = optArgs[0].body; - if (body.length === 2 && body[0].type === "atom" && body[1].type === "atom") { - if (body[0].family === "open" && body[1].family === "close") { - delimiters = [body[0].text, body[1].text]; - } - } - } - // consume the opening brace - parser.consumeSpaces(); - parser.consume(); - - // Pass control to the environment handler in array.js. - const env = environments["bordermatrix"]; - const context = { - mode: parser.mode, - envName: funcName.slice(1), - delimiters, - parser - }; - const result = env.handler(context); - parser.expect("}", true); - return result - } -}); - -defineFunction({ - type: "cancelto", - names: ["\\cancelto"], - props: { - numArgs: 2 - }, - handler({ parser }, args) { - const to = args[0]; - const body = args[1]; - return { - type: "cancelto", - mode: parser.mode, - body, - to, - isCharacterBox: isCharacterBox(body) - }; - }, - mathmlBuilder(group, style) { - const fromNode = new MathNode( - "mrow", - [buildGroup$1(group.body, style)], - ["ff-narrow"] // A zero-width mrow. - ); - // Write the arrow in a node written after the content. - // That way, the arrow will be an overlay on the content. - const phantom = new MathNode("mphantom", [buildGroup$1(group.body, style)]); - const arrow = new MathNode("mrow", [phantom], ["tml-cancelto"]); - arrow.style.color = style.color; - if (group.isCharacterBox && smalls.indexOf(group.body.body[0].text) > -1) { - arrow.style.left = "0.1em"; - arrow.style.width = "90%"; - } - const node = new MathNode("mrow", [fromNode, arrow], ["menclose"]); - if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) { - // Add 0.2em space to right of content to make room for the arrowhead. - phantom.style.paddingRight = "0.2em"; - } else { - phantom.style.padding = "0.5ex 0.1em 0 0"; - const strut = new MathNode('mspace', []); - strut.setAttribute('height', "0.85em"); - fromNode.children.push(strut); - } - - // Create the "to" value above and to the right of the arrow. - // First, we want a dummy node with the same height as the `from` content. - // We'll place the `to` node above the dummy to get the correct vertical alignment. - let dummyNode; - if (group.isCharacterBox) { - dummyNode = new MathNode('mspace', []); - dummyNode.setAttribute('height', "1em"); - } else { - // Create a phantom node with the same content as the body. - const inner = buildGroup$1(group.body, style); - // The phantom node will be zero-width, so it won't affect horizontal spacing. - const zeroWidthNode = new MathNode("mpadded", [inner]); - zeroWidthNode.setAttribute("width", "0.1px"); // Don't use 0. WebKit would omit it. - dummyNode = new MathNode("mphantom", [zeroWidthNode]); // Hide it. - } - const toNode = buildGroup$1(group.to, style); - toNode.style.color = style.color; - const zeroWidthToNode = new MathNode("mpadded", [toNode]); - if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) { - const w = new MathNode("mspace", []); - w.setAttribute('width', "0.2em"); - zeroWidthToNode.children.unshift(w); - } - zeroWidthToNode.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - const mover = new MathNode("mover", [dummyNode, zeroWidthToNode]); - // Fix Firefox positioning. - const nudgeLeft = new MathNode('mrow', [], ["ff-nudge-left"]); - return newDocumentFragment([makeRow([node, mover]), nudgeLeft]) - } -}); - -// \@char is an internal function that takes a grouped decimal argument like -// {123} and converts into symbol with code 123. It is used by the *macro* -// \char defined in macros.js. -defineFunction({ - type: "textord", - names: ["\\@char"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser, token }, args) { - const arg = assertNodeType(args[0], "ordgroup"); - const group = arg.body; - let number = ""; - for (let i = 0; i < group.length; i++) { - const node = assertNodeType(group[i], "textord"); - number += node.text; - } - const code = parseInt(number); - if (isNaN(code)) { - throw new ParseError(`\\@char has non-numeric argument ${number}`, token) - } - return { - type: "textord", - mode: parser.mode, - text: String.fromCodePoint(code) - } - } -}); - -// Helpers -const htmlRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6})$/i; -const htmlOrNameRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i; -const RGBregEx = /^ *\d{1,3} *(?:, *\d{1,3} *){2}$/; -const rgbRegEx = /^ *[10](?:\.\d*)? *(?:, *[10](?:\.\d*)? *){2}$/; -const xcolorHtmlRegEx = /^[a-f0-9]{6}$/i; -const toHex = num => { - let str = num.toString(16); - if (str.length === 1) { str = "0" + str; } - return str -}; - -// Colors from Tables 4.1 and 4.2 of the xcolor package. -// Table 4.1 (lower case) RGB values are taken from chroma and xcolor.dtx. -// Table 4.2 (Capitalized) values were sampled, because Chroma contains a unreliable -// conversion from cmyk to RGB. See https://tex.stackexchange.com/a/537274. -const xcolors = JSON.parse(`{ - "Apricot": "#ffb484", - "Aquamarine": "#08b4bc", - "Bittersweet": "#c84c14", - "blue": "#0000FF", - "Blue": "#303494", - "BlueGreen": "#08b4bc", - "BlueViolet": "#503c94", - "BrickRed": "#b8341c", - "brown": "#BF8040", - "Brown": "#802404", - "BurntOrange": "#f8941c", - "CadetBlue": "#78749c", - "CarnationPink": "#f884b4", - "Cerulean": "#08a4e4", - "CornflowerBlue": "#40ace4", - "cyan": "#00FFFF", - "Cyan": "#08acec", - "Dandelion": "#ffbc44", - "darkgray": "#404040", - "DarkOrchid": "#a8548c", - "Emerald": "#08ac9c", - "ForestGreen": "#089c54", - "Fuchsia": "#90348c", - "Goldenrod": "#ffdc44", - "gray": "#808080", - "Gray": "#98949c", - "green": "#00FF00", - "Green": "#08a44c", - "GreenYellow": "#e0e474", - "JungleGreen": "#08ac9c", - "Lavender": "#f89cc4", - "lightgray": "#c0c0c0", - "lime": "#BFFF00", - "LimeGreen": "#90c43c", - "magenta": "#FF00FF", - "Magenta": "#f0048c", - "Mahogany": "#b0341c", - "Maroon": "#b03434", - "Melon": "#f89c7c", - "MidnightBlue": "#086494", - "Mulberry": "#b03c94", - "NavyBlue": "#086cbc", - "olive": "#7F7F00", - "OliveGreen": "#407c34", - "orange": "#FF8000", - "Orange": "#f8843c", - "OrangeRed": "#f0145c", - "Orchid": "#b074ac", - "Peach": "#f8945c", - "Periwinkle": "#8074bc", - "PineGreen": "#088c74", - "pink": "#ff7f7f", - "Plum": "#98248c", - "ProcessBlue": "#08b4ec", - "purple": "#BF0040", - "Purple": "#a0449c", - "RawSienna": "#983c04", - "red": "#ff0000", - "Red": "#f01c24", - "RedOrange": "#f86434", - "RedViolet": "#a0246c", - "Rhodamine": "#f0549c", - "Royallue": "#0874bc", - "RoyalPurple": "#683c9c", - "RubineRed": "#f0047c", - "Salmon": "#f8948c", - "SeaGreen": "#30bc9c", - "Sepia": "#701404", - "SkyBlue": "#48c4dc", - "SpringGreen": "#c8dc64", - "Tan": "#e09c74", - "teal": "#007F7F", - "TealBlue": "#08acb4", - "Thistle": "#d884b4", - "Turquoise": "#08b4cc", - "violet": "#800080", - "Violet": "#60449c", - "VioletRed": "#f054a4", - "WildStrawberry": "#f0246c", - "yellow": "#FFFF00", - "Yellow": "#fff404", - "YellowGreen": "#98cc6c", - "YellowOrange": "#ffa41c" -}`); - -const colorFromSpec = (model, spec) => { - let color = ""; - if (model === "HTML") { - if (!htmlRegEx.test(spec)) { - throw new ParseError("Invalid HTML input.") - } - color = spec; - } else if (model === "RGB") { - if (!RGBregEx.test(spec)) { - throw new ParseError("Invalid RGB input.") - } - spec.split(",").map(e => { color += toHex(Number(e.trim())); }); - } else { - if (!rgbRegEx.test(spec)) { - throw new ParseError("Invalid rbg input.") - } - spec.split(",").map(e => { - const num = Number(e.trim()); - if (num > 1) { throw new ParseError("Color rgb input must be < 1.") } - color += toHex(Number((num * 255).toFixed(0))); - }); - } - if (color.charAt(0) !== "#") { color = "#" + color; } - return color -}; - -const validateColor = (color, macros, token) => { - const macroName = `\\\\color@${color}`; // from \defineColor. - const match = htmlOrNameRegEx.exec(color); - if (!match) { throw new ParseError("Invalid color: '" + color + "'", token) } - // We allow a 6-digit HTML color spec without a leading "#". - // This follows the xcolor package's HTML color model. - // Predefined color names are all missed by this RegEx pattern. - if (xcolorHtmlRegEx.test(color)) { - return "#" + color - } else if (color.charAt(0) === "#") { - return color - } else if (macros.has(macroName)) { - color = macros.get(macroName).tokens[0].text; - } else if (xcolors[color]) { - color = xcolors[color]; - } - return color -}; - -const mathmlBuilder$8 = (group, style) => { - // In LaTeX, color is not supposed to change the spacing of any node. - // So instead of wrapping the group in an , we apply - // the color individually to each node and return a document fragment. - let expr = buildExpression(group.body, style.withColor(group.color)); - if (expr.length === 0) { - expr.push(new MathNode("mrow")); - } - expr = expr.map(e => { - e.style.color = group.color; - return e - }); - return newDocumentFragment(expr) -}; - -defineFunction({ - type: "color", - names: ["\\textcolor"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "original"] - }, - handler({ parser, token }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); - } - const body = args[1]; - return { - type: "color", - mode: parser.mode, - color, - isTextColor: true, - body: ordargument(body) - } - }, - mathmlBuilder: mathmlBuilder$8 -}); - -defineFunction({ - type: "color", - names: ["\\color"], - props: { - numArgs: 1, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw"] - }, - handler({ parser, breakOnTokenText, token }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); - } - - // Parse out the implicit body that should be colored. - const body = parser.parseExpression(true, breakOnTokenText, true); - - return { - type: "color", - mode: parser.mode, - color, - isTextColor: false, - body - } - }, - mathmlBuilder: mathmlBuilder$8 -}); - -defineFunction({ - type: "color", - names: ["\\definecolor"], - props: { - numArgs: 3, - allowedInText: true, - argTypes: ["raw", "raw", "raw"] - }, - handler({ parser, funcName, token }, args) { - const name = assertNodeType(args[0], "raw").string; - if (!/^[A-Za-z]+$/.test(name)) { - throw new ParseError("Color name must be latin letters.", token) - } - const model = assertNodeType(args[1], "raw").string; - if (!["HTML", "RGB", "rgb"].includes(model)) { - throw new ParseError("Color model must be HTML, RGB, or rgb.", token) - } - const spec = assertNodeType(args[2], "raw").string; - const color = colorFromSpec(model, spec); - parser.gullet.macros.set(`\\\\color@${name}`, { tokens: [{ text: color }], numArgs: 0 }); - return { type: "internal", mode: parser.mode } - } - // No mathmlBuilder. The point of \definecolor is to set a macro. -}); - -// Row breaks within tabular environments, and line breaks at top level - - -// \DeclareRobustCommand\\{...\@xnewline} -defineFunction({ - type: "cr", - names: ["\\\\"], - props: { - numArgs: 0, - numOptionalArgs: 0, - allowedInText: true - }, - - handler({ parser }, args, optArgs) { - const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null; - const newLine = !parser.settings.displayMode; - return { - type: "cr", - mode: parser.mode, - newLine, - size: size && assertNodeType(size, "size").value - } - }, - - // The following builder is called only at the top level, - // not within tabular/array environments. - - mathmlBuilder(group, style) { - // MathML 3.0 calls for newline to occur in an or an . - // Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.linebreaking - const node = new MathNode("mo"); - if (group.newLine) { - node.setAttribute("linebreak", "newline"); - if (group.size) { - const size = calculateSize(group.size, style); - node.setAttribute("height", size.number + size.unit); - } - } - return node - } -}); - -const globalMap = { - "\\global": "\\global", - "\\long": "\\\\globallong", - "\\\\globallong": "\\\\globallong", - "\\def": "\\gdef", - "\\gdef": "\\gdef", - "\\edef": "\\xdef", - "\\xdef": "\\xdef", - "\\let": "\\\\globallet", - "\\futurelet": "\\\\globalfuture" -}; - -const checkControlSequence = (tok) => { - const name = tok.text; - if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { - throw new ParseError("Expected a control sequence", tok); - } - return name; -}; - -const getRHS = (parser) => { - let tok = parser.gullet.popToken(); - if (tok.text === "=") { - // consume optional equals - tok = parser.gullet.popToken(); - if (tok.text === " ") { - // consume one optional space - tok = parser.gullet.popToken(); - } - } - return tok; -}; - -const letCommand = (parser, name, tok, global) => { - let macro = parser.gullet.macros.get(tok.text); - if (macro == null) { - // don't expand it later even if a macro with the same name is defined - // e.g., \let\foo=\frac \def\frac{\relax} \frac12 - tok.noexpand = true; - macro = { - tokens: [tok], - numArgs: 0, - // reproduce the same behavior in expansion - unexpandable: !parser.gullet.isExpandable(tok.text) - }; - } - parser.gullet.macros.set(name, macro, global); -}; - -// -> | -// -> |\global -// -> | -// -> \global|\long|\outer -defineFunction({ - type: "internal", - names: [ - "\\global", - "\\long", - "\\\\globallong" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true - }, - handler({ parser, funcName }) { - parser.consumeSpaces(); - const token = parser.fetch(); - if (globalMap[token.text]) { - // Temml doesn't have \par, so ignore \long - if (funcName === "\\global" || funcName === "\\\\globallong") { - token.text = globalMap[token.text]; - } - return assertNodeType(parser.parseFunction(), "internal"); - } - throw new ParseError(`Invalid token after macro prefix`, token); - } -}); - -// Basic support for macro definitions: \def, \gdef, \edef, \xdef -// -> -// -> \def|\gdef|\edef|\xdef -// -> -defineFunction({ - type: "internal", - names: ["\\def", "\\gdef", "\\edef", "\\xdef"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - let tok = parser.gullet.popToken(); - const name = tok.text; - if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { - throw new ParseError("Expected a control sequence", tok); - } - - let numArgs = 0; - let insert; - const delimiters = [[]]; - // contains no braces - while (parser.gullet.future().text !== "{") { - tok = parser.gullet.popToken(); - if (tok.text === "#") { - // If the very last character of the is #, so that - // this # is immediately followed by {, TeX will behave as if the { - // had been inserted at the right end of both the parameter text - // and the replacement text. - if (parser.gullet.future().text === "{") { - insert = parser.gullet.future(); - delimiters[numArgs].push("{"); - break; - } - - // A parameter, the first appearance of # must be followed by 1, - // the next by 2, and so on; up to nine #’s are allowed - tok = parser.gullet.popToken(); - if (!/^[1-9]$/.test(tok.text)) { - throw new ParseError(`Invalid argument number "${tok.text}"`); - } - if (parseInt(tok.text) !== numArgs + 1) { - throw new ParseError(`Argument number "${tok.text}" out of order`); - } - numArgs++; - delimiters.push([]); - } else if (tok.text === "EOF") { - throw new ParseError("Expected a macro definition"); - } else { - delimiters[numArgs].push(tok.text); - } - } - // replacement text, enclosed in '{' and '}' and properly nested - let { tokens } = parser.gullet.consumeArg(); - if (insert) { - tokens.unshift(insert); - } - - if (funcName === "\\edef" || funcName === "\\xdef") { - tokens = parser.gullet.expandTokens(tokens); - if (tokens.length > parser.gullet.settings.maxExpand) { - throw new ParseError("Too many expansions in an " + funcName); - } - tokens.reverse(); // to fit in with stack order - } - // Final arg is the expansion of the macro - parser.gullet.macros.set( - name, - { tokens, numArgs, delimiters }, - funcName === globalMap[funcName] - ); - return { type: "internal", mode: parser.mode }; - } -}); - -// -> -// -> \futurelet -// | \let -// -> |= -defineFunction({ - type: "internal", - names: [ - "\\let", - "\\\\globallet" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - const name = checkControlSequence(parser.gullet.popToken()); - parser.gullet.consumeSpaces(); - const tok = getRHS(parser); - letCommand(parser, name, tok, funcName === "\\\\globallet"); - return { type: "internal", mode: parser.mode }; - } -}); - -// ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf -defineFunction({ - type: "internal", - names: [ - "\\futurelet", - "\\\\globalfuture" // can’t be entered directly - ], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - const name = checkControlSequence(parser.gullet.popToken()); - const middle = parser.gullet.popToken(); - const tok = parser.gullet.popToken(); - letCommand(parser, name, tok, funcName === "\\\\globalfuture"); - parser.gullet.pushToken(tok); - parser.gullet.pushToken(middle); - return { type: "internal", mode: parser.mode }; - } -}); - -defineFunction({ - type: "internal", - names: ["\\newcommand", "\\renewcommand", "\\providecommand"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ parser, funcName }) { - let name = ""; - const tok = parser.gullet.popToken(); - if (tok.text === "{") { - name = checkControlSequence(parser.gullet.popToken()); - parser.gullet.popToken(); - } else { - name = checkControlSequence(tok); - } - - const exists = parser.gullet.isDefined(name); - if (exists && funcName === "\\newcommand") { - throw new ParseError( - `\\newcommand{${name}} attempting to redefine ${name}; use \\renewcommand` - ); - } - if (!exists && funcName === "\\renewcommand") { - throw new ParseError( - `\\renewcommand{${name}} when command ${name} does not yet exist; use \\newcommand` - ); - } - - let numArgs = 0; - if (parser.gullet.future().text === "[") { - let tok = parser.gullet.popToken(); - tok = parser.gullet.popToken(); - if (!/^[0-9]$/.test(tok.text)) { - throw new ParseError(`Invalid number of arguments: "${tok.text}"`); - } - numArgs = parseInt(tok.text); - tok = parser.gullet.popToken(); - if (tok.text !== "]") { - throw new ParseError(`Invalid argument "${tok.text}"`); - } - } - - // replacement text, enclosed in '{' and '}' and properly nested - const { tokens } = parser.gullet.consumeArg(); - - if (!(funcName === "\\providecommand" && parser.gullet.macros.has(name))) { - // Ignore \providecommand - parser.gullet.macros.set( - name, - { tokens, numArgs } - ); - } - - return { type: "internal", mode: parser.mode }; - - } -}); - -// Extra data needed for the delimiter handler down below -const delimiterSizes = { - "\\bigl": { mclass: "mopen", size: 1 }, - "\\Bigl": { mclass: "mopen", size: 2 }, - "\\biggl": { mclass: "mopen", size: 3 }, - "\\Biggl": { mclass: "mopen", size: 4 }, - "\\bigr": { mclass: "mclose", size: 1 }, - "\\Bigr": { mclass: "mclose", size: 2 }, - "\\biggr": { mclass: "mclose", size: 3 }, - "\\Biggr": { mclass: "mclose", size: 4 }, - "\\bigm": { mclass: "mrel", size: 1 }, - "\\Bigm": { mclass: "mrel", size: 2 }, - "\\biggm": { mclass: "mrel", size: 3 }, - "\\Biggm": { mclass: "mrel", size: 4 }, - "\\big": { mclass: "mord", size: 1 }, - "\\Big": { mclass: "mord", size: 2 }, - "\\bigg": { mclass: "mord", size: 3 }, - "\\Bigg": { mclass: "mord", size: 4 } -}; - -const leftToRight = { - "(": ")", - "\\lparen": "\\rparen", - "[": "]", - "\\lbrack": "\\rbrack", - "\\{": "\\}", - "\\lbrace": "\\rbrace", - "⦇": "⦈", - "\\llparenthesis": "\\rrparenthesis", - "\\lfloor": "\\rfloor", - "\u230a": "\u230b", - "\\lceil": "\\rceil", - "\u2308": "\u2309", - "\\langle": "\\rangle", - "\u27e8": "\u27e9", - "\\lAngle": "\\rAngle", - "\u27ea": "\u27eb", - "\\llangle": "\\rrangle", - "⦉": "⦊", - "\\lvert": "\\rvert", - "\\lVert": "\\rVert", - "\\lgroup": "\\rgroup", - "\u27ee": "\u27ef", - "\\lmoustache": "\\rmoustache", - "\u23b0": "\u23b1", - "\\llbracket": "\\rrbracket", - "\u27e6": "\u27e7", - "\\lBrace": "\\rBrace", - "\u2983": "\u2984" -}; - -const leftDelimiterNames = new Set(Object.keys(leftToRight)); -new Set(Object.values(leftToRight)); - -const delimiters = new Set([ - "(", - "\\lparen", - ")", - "\\rparen", - "[", - "\\lbrack", - "]", - "\\rbrack", - "\\{", - "\\lbrace", - "\\}", - "\\rbrace", - "⦇", - "\\llparenthesis", - "⦈", - "\\rrparenthesis", - "\\lfloor", - "\\rfloor", - "\u230a", - "\u230b", - "\\lceil", - "\\rceil", - "\u2308", - "\u2309", - "<", - ">", - "\\langle", - "\u27e8", - "\\rangle", - "\u27e9", - "\\lAngle", - "\u27ea", - "\\rAngle", - "\u27eb", - "\\llangle", - "⦉", - "\\rrangle", - "⦊", - "\\lt", - "\\gt", - "\\lvert", - "\\rvert", - "\\lVert", - "\\rVert", - "\\lgroup", - "\\rgroup", - "\u27ee", - "\u27ef", - "\\lmoustache", - "\\rmoustache", - "\u23b0", - "\u23b1", - "\\llbracket", - "\\rrbracket", - "\u27e6", - "\u27e7", - "\\lBrace", - "\\rBrace", - "\u2983", - "\u2984", - "/", - "\\backslash", - "|", - "\\vert", - "\\|", - "\\Vert", - "\u2016", - "\\uparrow", - "\\Uparrow", - "\\downarrow", - "\\Downarrow", - "\\updownarrow", - "\\Updownarrow", - "." -]); - -// Export isDelimiter for benefit of parser. -const dels = new Set(["}", "\\left", "\\middle", "\\right"]); -const isDelimiter = str => str.length > 0 && - (delimiters.has(str) || delimiterSizes[str] || dels.has(str)); - -// Metrics of the different sizes. Found by looking at TeX's output of -// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$ -// Used to create stacked delimiters of appropriate sizes in makeSizedDelim. -const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; - -// Delimiter functions -function checkDelimiter(delim, context) { - if (delim.type === "ordgroup" && delim.body.length === 1) { - delim = delim.body[0]; // Unwrap the braces - } - const symDelim = checkSymbolNodeType(delim); - if (symDelim && delimiters.has(symDelim.text)) { - // If a character is not in the MathML operator dictionary, it will not stretch. - // Replace such characters w/characters that will stretch. - if (symDelim.text === "<" || symDelim.text === "\\lt") { symDelim.text = "⟨"; } - if (symDelim.text === ">" || symDelim.text === "\\gt") { symDelim.text = "⟩"; } - return symDelim; - } else if (symDelim) { - throw new ParseError(`Invalid delimiter '${symDelim.text}' after '${context.funcName}'`, delim); - } else { - throw new ParseError(`Invalid delimiter type '${delim.type}'`, delim); - } -} - -// / \ -const needExplicitStretch = new Set(["\u002F", "\u005C", "\\backslash", "\u2216", "\\vert", "|"]); - -const makeFenceMo = (delim, mode, form, isStretchy) => { - const text = delim === "." ? "" : delim; - const node = new MathNode("mo", [makeText(text, mode)]); - node.setAttribute("fence", "true"); - node.setAttribute("form", form); - node.setAttribute("stretchy", isStretchy ? "true" : "false"); - return node; -}; - -defineFunction({ - type: "delimsizing", - names: [ - "\\bigl", - "\\Bigl", - "\\biggl", - "\\Biggl", - "\\bigr", - "\\Bigr", - "\\biggr", - "\\Biggr", - "\\bigm", - "\\Bigm", - "\\biggm", - "\\Biggm", - "\\big", - "\\Big", - "\\bigg", - "\\Bigg" - ], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - - const delimNode = { - type: "delimsizing", - mode: context.parser.mode, - size: delimiterSizes[context.funcName].size, - mclass: delimiterSizes[context.funcName].mclass, - delim: delim.text - }; - const nextToken = context.parser.fetch().text; - if (nextToken !== "^" && nextToken !== "_") { - return delimNode - } else { - // Chromium mis-renders a sized delim if it is the base of a supsub. - // So wrap it in a ordgroup. - return { - type: "ordgroup", - mode: "math", - body: [delimNode, { type: "ordgroup", mode: "math", body: [] }] - } - } - }, - mathmlBuilder: (group) => { - const children = []; - const delim = group.delim === "." ? "" : group.delim; - - children.push(makeText(delim, group.mode)); - - const node = new MathNode("mo", children); - - if (group.mclass === "mopen" || group.mclass === "mclose") { - // Only some of the delimsizing functions act as fences, and they - // return "mopen" or "mclose" mclass. - node.setAttribute("fence", "true"); - } else { - // Explicitly disable fencing if it's not a fence, to override the - // defaults. - node.setAttribute("fence", "false"); - } - if (needExplicitStretch.has(delim) || delim.indexOf("arrow") > -1) { - // We have to explicitly set stretchy to true. - node.setAttribute("stretchy", "true"); - } - node.setAttribute("symmetric", "true"); // Needed for tall arrows in Firefox. - node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em"); - node.setAttribute("maxsize", sizeToMaxHeight[group.size] + "em"); - return node; - } -}); - -function assertParsed(group) { - if (!group.body) { - throw new Error("Bug: The delim ParseNode wasn't fully parsed."); - } -} - -defineFunction({ - type: "leftright-right", - names: ["\\right"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - return { - type: "leftright-right", - mode: context.parser.mode, - delim: checkDelimiter(args[0], context).text - }; - } -}); - -defineFunction({ - type: "leftright", - names: ["\\left"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - - const parser = context.parser; - ++parser.leftrightDepth; - let body = parser.parseExpression(false, "\\right", true); - let nextToken = parser.fetch(); - while (nextToken.text === "\\middle") { - parser.consume(); - const middle = parser.fetch().text; - if (!symbols.math[middle]) { - throw new ParseError(`Invalid delimiter '${middle}' after '\\middle'`); - } - checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" }); - body.push({ type: "middle", mode: "math", delim: middle }); - parser.consume(); - body = body.concat(parser.parseExpression(false, "\\right", true)); - nextToken = parser.fetch(); - } - --parser.leftrightDepth; - parser.expect("\\right", false); - const right = assertNodeType(parser.parseFunction(), "leftright-right"); - return { - type: "leftright", - mode: parser.mode, - body, - left: delim.text, - right: right.delim, - isStretchy: true - }; - }, - mathmlBuilder: (group, style) => { - assertParsed(group); - const inner = buildExpression(group.body, style); - - const leftNode = makeFenceMo(group.left, group.mode, "prefix", true); - inner.unshift(leftNode); - - const rightNode = makeFenceMo(group.right, group.mode, "postfix", true); - if (group.body.length > 0) { - const lastElement = group.body[group.body.length - 1]; - if (lastElement.type === "color" && !lastElement.isTextColor) { - rightNode.setAttribute("mathcolor", lastElement.color); - } - } - inner.push(rightNode); - - return makeRow(inner); - } -}); - -defineFunction({ - type: "delimiter", - names: Array.from(leftDelimiterNames), - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: true, - allowedInArgument: true - }, - handler: ({ parser, funcName, token }) => { - if (parser.mode === "text") { - return { - type: "textord", - mode: "text", - text: funcName, - loc: token.loc - } - } else if (!parser.settings.wrapDelimiterPairs) { - // Treat this token as an ordinary symbol. - return { - type: "atom", - mode: "math", - family: "open", - loc: token.loc, - text: funcName - }; - } - // Otherwise, try to wrap a pair of delimiters with an . - const rightDelim = leftToRight[funcName]; - // Parse the inner expression, looking for the corresponding right delimiter. - const body = parser.parseExpression(false, rightDelim, false); - const nextToken = parser.fetch().text; - - if (nextToken !== rightDelim) { - // We were unable to find a matching right delimiter. - // Throw control back to renderToMathMLTree. - // It will reparse the entire expression with wrapDelimiterPairs set to false. - throw new ParseError("Unmatched delimiter"); - } - parser.consume(); - - return { - type: "delimiter", - mode: parser.mode, - body, - left: funcName, - right: rightDelim - }; - }, - mathmlBuilder: (group, style) => { - assertParsed(group); - const inner = buildExpression(group.body, style); - - const leftNode = makeFenceMo(group.left, group.mode, "prefix", false); - inner.unshift(leftNode); - - const rightNode = makeFenceMo(group.right, group.mode, "postfix", false); - if (group.body.length > 0) { - const lastElement = group.body[group.body.length - 1]; - if (lastElement.type === "color" && !lastElement.isTextColor) { - rightNode.setAttribute("mathcolor", lastElement.color); - } - } - inner.push(rightNode); - - return makeRow(inner); - } -}); - -defineFunction({ - type: "middle", - names: ["\\middle"], - props: { - numArgs: 1, - argTypes: ["primitive"] - }, - handler: (context, args) => { - const delim = checkDelimiter(args[0], context); - if (!context.parser.leftrightDepth) { - throw new ParseError("\\middle without preceding \\left", delim); - } - - return { - type: "middle", - mode: context.parser.mode, - delim: delim.text - }; - }, - mathmlBuilder: (group) => { - const textNode = makeText(group.delim, group.mode); - const middleNode = new MathNode("mo", [textNode]); - middleNode.setAttribute("fence", "true"); - if (group.delim.indexOf("arrow") > -1) { - middleNode.setAttribute("stretchy", "true"); - } - // The next line is not semantically correct, but - // Chromium fails to stretch if it is not there. - middleNode.setAttribute("form", "prefix"); - // MathML gives 5/18em spacing to each element. - // \middle should get delimiter spacing instead. - middleNode.setAttribute("lspace", "0.05em"); - middleNode.setAttribute("rspace", "0.05em"); - return middleNode; - } -}); - -const boxTags = ["\\boxed", "\\fcolorbox", "\\colorbox"]; - -const mathmlBuilder$7 = (group, style) => { - const tag = boxTags.includes(group.label) ? "mrow" : "menclose"; - const node = new MathNode(tag, [buildGroup$1(group.body, style)]); - switch (group.label) { - case "\\overline": - node.setAttribute("notation", "top"); // for Firefox & WebKit - node.classes.push("tml-overline"); // for Chromium - break - case "\\underline": - node.setAttribute("notation", "bottom"); - node.classes.push("tml-underline"); - break - case "\\cancel": - node.setAttribute("notation", "updiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "upstrike"])); - break - case "\\bcancel": - node.setAttribute("notation", "downdiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "downstrike"])); - break - case "\\sout": - node.setAttribute("notation", "horizontalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "sout"])); - break - case "\\xcancel": - node.setAttribute("notation", "updiagonalstrike downdiagonalstrike"); - node.children.push(new MathNode("mrow", [], ["tml-cancel", "tml-xcancel"])); - break - // cancelto is handled in cancelto.js - case "\\longdiv": - node.setAttribute("notation", "longdiv"); - node.classes.push("longdiv-top"); - node.children.push(new MathNode("mrow", [], ["longdiv-arc"])); - break - case "\\phase": - node.setAttribute("notation", "phasorangle"); - node.classes.push("phasor-bottom"); - node.children.push(new MathNode("mrow", [], ["phasor-angle"])); - break - case "\\textcircled": - node.setAttribute("notation", "circle"); - node.classes.push("circle-pad"); - node.children.push(new MathNode("mrow", [], ["textcircle"])); - break - case "\\angl": - node.setAttribute("notation", "actuarial"); - node.classes.push("actuarial"); - break - case "\\boxed": - // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty - node.style.padding = "3pt"; - node.style.border = "1px solid"; - node.setAttribute("scriptlevel", "0"); - node.setAttribute("displaystyle", "true"); - break - case "\\fbox": - node.setAttribute("notation", "box"); - node.classes.push("tml-fbox"); - break - case "\\fcolorbox": - case "\\colorbox": { - // Don't use . WebKit would show a radical. - node.style.padding = "0.3em"; // 3 pt from LaTeX source2e for a 10pt font - if (group.label === "\\fcolorbox") { - node.style.border = "0.0667em solid " + String(group.borderColor); - } - break - } - } - if (group.backgroundColor) { - node.setAttribute("mathbackground", group.backgroundColor); - } - return node; -}; - -defineFunction({ - type: "enclose", - names: ["\\colorbox"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "text"] - }, - handler({ parser, funcName }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let color = ""; - if (model) { - const spec = assertNodeType(args[0], "raw").string; - color = colorFromSpec(model, spec); - } else { - color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); - } - const body = args[1]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - backgroundColor: color, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -defineFunction({ - type: "enclose", - names: ["\\fcolorbox"], - props: { - numArgs: 3, - numOptionalArgs: 1, - allowedInText: true, - argTypes: ["raw", "raw", "raw", "text"] - }, - handler({ parser, funcName }, args, optArgs) { - const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; - let borderColor = ""; - let backgroundColor; - if (model) { - const borderSpec = assertNodeType(args[0], "raw").string; - const backgroundSpec = assertNodeType(args[0], "raw").string; - borderColor = colorFromSpec(model, borderSpec); - backgroundColor = colorFromSpec(model, backgroundSpec); - } else { - borderColor = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); - backgroundColor = validateColor(assertNodeType(args[1], "raw").string, parser.gullet.macros); - } - const body = args[2]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - backgroundColor, - borderColor, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -defineFunction({ - type: "enclose", - names: ["\\fbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "enclose", - mode: parser.mode, - label: "\\fbox", - body: args[0] - }; - } -}); - -defineFunction({ - type: "enclose", - names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", - "\\boxed", "\\longdiv", "\\phase"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -defineFunction({ - type: "enclose", - names: ["\\underline"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - - -defineFunction({ - type: "enclose", - names: ["\\textcircled"], - props: { - numArgs: 1, - argTypes: ["text"], - allowedInArgument: true, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "enclose", - mode: parser.mode, - label: funcName, - body - }; - }, - mathmlBuilder: mathmlBuilder$7 -}); - -// Environment delimiters. HTML/MathML rendering is defined in the corresponding -// defineEnvironment definitions. -defineFunction({ - type: "environment", - names: ["\\begin", "\\end"], - props: { - numArgs: 1, - argTypes: ["text"] - }, - handler({ parser, funcName }, args) { - const nameGroup = args[0]; - if (nameGroup.type !== "ordgroup") { - throw new ParseError("Invalid environment name", nameGroup); - } - let envName = ""; - for (let i = 0; i < nameGroup.body.length; ++i) { - envName += assertNodeType(nameGroup.body[i], "textord").text; - } - - if (funcName === "\\begin") { - // begin...end is similar to left...right - if (!Object.prototype.hasOwnProperty.call(environments, envName )) { - throw new ParseError("No such environment: " + envName, nameGroup); - } - // Build the environment object. Arguments and other information will - // be made available to the begin and end methods using properties. - const env = environments[envName]; - const { args, optArgs } = parser.parseArguments("\\begin{" + envName + "}", env); - const context = { - mode: parser.mode, - envName, - parser - }; - const result = env.handler(context, args, optArgs); - parser.expect("\\end", false); - const endNameToken = parser.nextToken; - const end = assertNodeType(parser.parseFunction(), "environment"); - if (end.name !== envName) { - throw new ParseError( - `Mismatch: \\begin{${envName}} matched by \\end{${end.name}}`, - endNameToken - ); - } - return result; - } - - return { - type: "environment", - mode: parser.mode, - name: envName, - nameGroup - }; - } -}); - -defineFunction({ - type: "envTag", - names: ["\\env@tag"], - props: { - numArgs: 1, - argTypes: ["math"] - }, - handler({ parser }, args) { - return { - type: "envTag", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow"); - } -}); - -defineFunction({ - type: "noTag", - names: ["\\env@notag"], - props: { - numArgs: 0 - }, - handler({ parser }) { - return { - type: "noTag", - mode: parser.mode - }; - }, - mathmlBuilder(group, style) { - return new MathNode("mrow"); - } -}); - -const isLongVariableName = (group, font) => { - if (font !== "mathrm" || group.body.type !== "ordgroup" || group.body.body.length === 1) { - return false - } - if (group.body.body[0].type !== "mathord") { return false } - for (let i = 1; i < group.body.body.length; i++) { - const parseNodeType = group.body.body[i].type; - if (!(parseNodeType === "mathord" || - (parseNodeType === "textord" && !isNaN(group.body.body[i].text)))) { - return false - } - } - return true -}; - -const mathmlBuilder$6 = (group, style) => { - const font = group.font; - const newStyle = style.withFont(font); - const mathGroup = buildGroup$1(group.body, newStyle); - - if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{} - if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) { - mathGroup.style.fontWeight = "bold"; - return mathGroup - } - // Check if it is possible to consolidate elements into a single element. - if (isLongVariableName(group, font)) { - // This is a \mathrm{…} group. It gets special treatment because symbolsOrd.js - // wraps elements with s to work around a Firefox bug. - const mi = mathGroup.children[0].children[0].children - ? mathGroup.children[0].children[0] - : mathGroup.children[0]; - delete mi.attributes.mathvariant; - for (let i = 1; i < mathGroup.children.length; i++) { - mi.children[0].text += mathGroup.children[i].children[0].children - ? mathGroup.children[i].children[0].children[0].text - : mathGroup.children[i].children[0].text; - } - // Wrap in a to prevent the same Firefox bug. - const mpadded = new MathNode("mpadded", [mi]); - mpadded.setAttribute("lspace", "0"); - return mpadded - } - let canConsolidate = mathGroup.children[0].type === "mo"; - for (let i = 1; i < mathGroup.children.length; i++) { - if (mathGroup.children[i].type === "mo" && font === "boldsymbol") { - mathGroup.children[i].style.fontWeight = "bold"; - } - if (mathGroup.children[i].type !== "mi") { canConsolidate = false; } - const localVariant = mathGroup.children[i].attributes && - mathGroup.children[i].attributes.mathvariant || ""; - if (localVariant !== "normal") { canConsolidate = false; } - } - if (!canConsolidate) { return mathGroup } - // Consolidate the elements. - const mi = mathGroup.children[0]; - for (let i = 1; i < mathGroup.children.length; i++) { - mi.children.push(mathGroup.children[i].children[0]); - } - if (mi.attributes.mathvariant && mi.attributes.mathvariant === "normal") { - // Workaround for a Firefox bug that renders spurious space around - // a - // Ref: https://bugs.webkit.org/show_bug.cgi?id=129097 - // We insert a text node that contains a zero-width space and wrap in an mrow. - // TODO: Get rid of this workaround when the Firefox bug is fixed. - const bogus = new MathNode("mtext", new TextNode("\u200b")); - return new MathNode("mrow", [bogus, mi]) - } - return mi -}; - -const fontAliases = { - "\\Bbb": "\\mathbb", - "\\bold": "\\mathbf", - "\\frak": "\\mathfrak", - "\\bm": "\\boldsymbol" -}; - -defineFunction({ - type: "font", - names: [ - // styles - "\\mathrm", - "\\mathit", - "\\mathbf", - "\\mathnormal", - "\\up@greek", - "\\boldsymbol", - - // families - "\\mathbb", - "\\mathcal", - "\\mathfrak", - "\\mathscr", - "\\mathsf", - "\\mathsfit", - "\\mathtt", - - // aliases - "\\Bbb", - "\\bm", - "\\bold", - "\\frak" - ], - props: { - numArgs: 1, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const body = normalizeArgument(args[0]); - let func = funcName; - if (func in fontAliases) { - func = fontAliases[func]; - } - return { - type: "font", - mode: parser.mode, - font: func.slice(1), - body - }; - }, - mathmlBuilder: mathmlBuilder$6 -}); - -// Old font changing functions -defineFunction({ - type: "font", - names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"], - props: { - numArgs: 0, - allowedInText: true - }, - handler: ({ parser, funcName, breakOnTokenText }, args) => { - const { mode } = parser; - const body = parser.parseExpression(true, breakOnTokenText, true); - const fontStyle = `math${funcName.slice(1)}`; - - return { - type: "font", - mode: mode, - font: fontStyle, - body: { - type: "ordgroup", - mode: parser.mode, - body - } - }; - }, - mathmlBuilder: mathmlBuilder$6 -}); - -const stylArray = ["display", "text", "script", "scriptscript"]; -const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 }; - -const adjustStyle = (functionSize, originalStyle) => { - // Figure out what style this fraction should be in based on the - // function used - let style = originalStyle; - if (functionSize === "display") { //\tfrac or \cfrac - // Get display style as a default. - // If incoming style is sub/sup, use style.text() to get correct size. - const newSize = style.level >= StyleLevel.SCRIPT ? StyleLevel.TEXT : StyleLevel.DISPLAY; - style = style.withLevel(newSize); - } else if (functionSize === "text" && - style.level === StyleLevel.DISPLAY) { - // We're in a \tfrac but incoming style is displaystyle, so: - style = style.withLevel(StyleLevel.TEXT); - } else if (functionSize === "auto") { - style = style.incrementLevel(); - } else if (functionSize === "script") { - style = style.withLevel(StyleLevel.SCRIPT); - } else if (functionSize === "scriptscript") { - style = style.withLevel(StyleLevel.SCRIPTSCRIPT); - } - return style; -}; - -const mathmlBuilder$5 = (group, style) => { - style = adjustStyle(group.scriptLevel, style); - - // Chromium (wrongly) continues to shrink fractions beyond scriptscriptlevel. - // So we check for levels that Chromium shrinks too small. - // If necessary, set an explicit fraction depth. - const numer = buildGroup$1(group.numer, style); - const denom = buildGroup$1(group.denom, style); - if (style.level === 3) { - numer.style.mathDepth = "2"; - numer.setAttribute("scriptlevel", "2"); - denom.style.mathDepth = "2"; - denom.setAttribute("scriptlevel", "2"); - } - - let node = new MathNode("mfrac", [numer, denom]); - - if (!group.hasBarLine) { - node.setAttribute("linethickness", "0px"); - } else if (group.barSize) { - const ruleWidth = calculateSize(group.barSize, style); - node.setAttribute("linethickness", ruleWidth.number + ruleWidth.unit); - } - - if (group.leftDelim != null || group.rightDelim != null) { - const withDelims = []; - - if (group.leftDelim != null) { - const leftOp = new MathNode("mo", [ - new TextNode(group.leftDelim.replace("\\", "")) - ]); - leftOp.setAttribute("fence", "true"); - withDelims.push(leftOp); - } - - withDelims.push(node); - - if (group.rightDelim != null) { - const rightOp = new MathNode("mo", [ - new TextNode(group.rightDelim.replace("\\", "")) - ]); - rightOp.setAttribute("fence", "true"); - withDelims.push(rightOp); - } - - node = makeRow(withDelims); - } - - if (group.scriptLevel !== "auto") { - node = new MathNode("mstyle", [node]); - node.setAttribute("displaystyle", String(group.scriptLevel === "display")); - node.setAttribute("scriptlevel", scriptLevel[group.scriptLevel]); - } - - return node; -}; - -defineFunction({ - type: "genfrac", - names: [ - "\\cfrac", - "\\dfrac", - "\\frac", - "\\tfrac", - "\\dbinom", - "\\binom", - "\\tbinom", - "\\\\atopfrac", // can’t be entered directly - "\\\\bracefrac", - "\\\\brackfrac" // ditto - ], - props: { - numArgs: 2, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const numer = args[0]; - const denom = args[1]; - let hasBarLine = false; - let leftDelim = null; - let rightDelim = null; - let scriptLevel = "auto"; - - switch (funcName) { - case "\\cfrac": - case "\\dfrac": - case "\\frac": - case "\\tfrac": - hasBarLine = true; - break; - case "\\\\atopfrac": - hasBarLine = false; - break; - case "\\dbinom": - case "\\binom": - case "\\tbinom": - leftDelim = "("; - rightDelim = ")"; - break; - case "\\\\bracefrac": - leftDelim = "\\{"; - rightDelim = "\\}"; - break; - case "\\\\brackfrac": - leftDelim = "["; - rightDelim = "]"; - break; - default: - throw new Error("Unrecognized genfrac command"); - } - - if (funcName === "\\cfrac" || funcName.startsWith("\\d")) { - scriptLevel = "display"; - } else if (funcName.startsWith("\\t")) { - scriptLevel = "text"; - } - - return { - type: "genfrac", - mode: parser.mode, - continued: false, - numer, - denom, - hasBarLine, - leftDelim, - rightDelim, - scriptLevel, - barSize: null - }; - }, - mathmlBuilder: mathmlBuilder$5 -}); - -// Infix generalized fractions -- these are not rendered directly, but replaced -// immediately by one of the variants above. -defineFunction({ - type: "infix", - names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"], - props: { - numArgs: 0, - infix: true - }, - handler({ parser, funcName, token }) { - let replaceWith; - switch (funcName) { - case "\\over": - replaceWith = "\\frac"; - break; - case "\\choose": - replaceWith = "\\binom"; - break; - case "\\atop": - replaceWith = "\\\\atopfrac"; - break; - case "\\brace": - replaceWith = "\\\\bracefrac"; - break; - case "\\brack": - replaceWith = "\\\\brackfrac"; - break; - default: - throw new Error("Unrecognized infix genfrac command"); - } - return { - type: "infix", - mode: parser.mode, - replaceWith, - token - }; - } -}); - -const delimFromValue = function(delimString) { - let delim = null; - if (delimString.length > 0) { - delim = delimString; - delim = delim === "." ? null : delim; - } - return delim; -}; - -defineFunction({ - type: "genfrac", - names: ["\\genfrac"], - props: { - numArgs: 6, - allowedInArgument: true, - argTypes: ["math", "math", "size", "text", "math", "math"] - }, - handler({ parser }, args) { - const numer = args[4]; - const denom = args[5]; - - // Look into the parse nodes to get the desired delimiters. - const leftNode = normalizeArgument(args[0]); - const leftDelim = leftNode.type === "atom" && leftNode.family === "open" - ? delimFromValue(leftNode.text) - : null; - const rightNode = normalizeArgument(args[1]); - const rightDelim = - rightNode.type === "atom" && rightNode.family === "close" - ? delimFromValue(rightNode.text) - : null; - - const barNode = assertNodeType(args[2], "size"); - let hasBarLine; - let barSize = null; - if (barNode.isBlank) { - // \genfrac acts differently than \above. - // \genfrac treats an empty size group as a signal to use a - // standard bar size. \above would see size = 0 and omit the bar. - hasBarLine = true; - } else { - barSize = barNode.value; - hasBarLine = barSize.number > 0; - } - - // Find out if we want displaystyle, textstyle, etc. - let scriptLevel = "auto"; - let styl = args[3]; - if (styl.type === "ordgroup") { - if (styl.body.length > 0) { - const textOrd = assertNodeType(styl.body[0], "textord"); - scriptLevel = stylArray[Number(textOrd.text)]; - } - } else { - styl = assertNodeType(styl, "textord"); - scriptLevel = stylArray[Number(styl.text)]; - } - - return { - type: "genfrac", - mode: parser.mode, - numer, - denom, - continued: false, - hasBarLine, - barSize, - leftDelim, - rightDelim, - scriptLevel - }; - }, - mathmlBuilder: mathmlBuilder$5 -}); - -// \above is an infix fraction that also defines a fraction bar size. -defineFunction({ - type: "infix", - names: ["\\above"], - props: { - numArgs: 1, - argTypes: ["size"], - infix: true - }, - handler({ parser, funcName, token }, args) { - return { - type: "infix", - mode: parser.mode, - replaceWith: "\\\\abovefrac", - barSize: assertNodeType(args[0], "size").value, - token - }; - } -}); - -defineFunction({ - type: "genfrac", - names: ["\\\\abovefrac"], - props: { - numArgs: 3, - argTypes: ["math", "size", "math"] - }, - handler: ({ parser, funcName }, args) => { - const numer = args[0]; - const barSize = assert(assertNodeType(args[1], "infix").barSize); - const denom = args[2]; - - const hasBarLine = barSize.number > 0; - return { - type: "genfrac", - mode: parser.mode, - numer, - denom, - continued: false, - hasBarLine, - barSize, - leftDelim: null, - rightDelim: null, - scriptLevel: "auto" - }; - }, - - mathmlBuilder: mathmlBuilder$5 -}); - -// \hbox is provided for compatibility with LaTeX functions that act on a box. -// This function by itself doesn't do anything but set scriptlevel to \textstyle -// and prevent a soft line break. - -defineFunction({ - type: "hbox", - names: ["\\hbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInArgument: true, - allowedInText: false - }, - handler({ parser }, args) { - return { - type: "hbox", - mode: parser.mode, - body: ordargument(args[0]) - }; - }, - mathmlBuilder(group, style) { - const newStyle = style.withLevel(StyleLevel.TEXT); - const mrow = buildExpressionRow(group.body, newStyle); - return consolidateText(mrow) - } -}); - -const mathmlBuilder$4 = (group, style) => { - const accentNode = mathMLnode(group.label); - accentNode.style["math-depth"] = 0; - return new MathNode(group.isOver ? "mover" : "munder", [ - buildGroup$1(group.base, style), - accentNode - ]); -}; - -// Horizontal stretchy brackets -defineFunction({ - type: "horizBracket", - names: ["\\overbrace", "\\underbrace", "\\overbracket", "\\underbracket"], - props: { - numArgs: 1 - }, - handler({ parser, funcName }, args) { - return { - type: "horizBracket", - mode: parser.mode, - label: funcName, - isOver: /^\\over/.test(funcName), - base: args[0] - }; - }, - mathmlBuilder: mathmlBuilder$4 -}); - -defineFunction({ - type: "html", - names: ["\\class", "\\id", "\\style", "\\data"], - props: { - numArgs: 2, - argTypes: ["raw", "original"], - allowedInText: true - }, - handler: ({ parser, funcName, token }, args) => { - const value = assertNodeType(args[0], "raw").string; - const body = args[1]; - - if (parser.settings.strict) { - throw new ParseError(`Function "${funcName}" is disabled in strict mode`, token) - } - - let trustContext; - const attributes = {}; - - switch (funcName) { - case "\\class": - attributes.class = value; - trustContext = { - command: "\\class", - class: value - }; - break; - case "\\id": - attributes.id = value; - trustContext = { - command: "\\id", - id: value - }; - break; - case "\\style": - attributes.style = value; - trustContext = { - command: "\\style", - style: value - }; - break; - case "\\data": { - const data = value.split(","); - for (let i = 0; i < data.length; i++) { - const keyVal = data[i].split("="); - if (keyVal.length !== 2) { - throw new ParseError("Error parsing key-value for \\data"); - } - attributes["data-" + keyVal[0].trim()] = keyVal[1].trim(); - } - - trustContext = { - command: "\\data", - attributes - }; - break; - } - default: - throw new Error("Unrecognized html command"); - } - - if (!parser.settings.isTrusted(trustContext)) { - throw new ParseError(`Function "${funcName}" is not trusted`, token) - } - return { - type: "html", - mode: parser.mode, - attributes, - body: ordargument(body) - }; - }, - mathmlBuilder: (group, style) => { - const element = buildExpressionRow(group.body, style); - - const classes = []; - if (group.attributes.class) { - classes.push(...group.attributes.class.trim().split(/\s+/)); - } - element.classes = classes; - - for (const attr in group.attributes) { - if (attr !== "class" && Object.prototype.hasOwnProperty.call(group.attributes, attr)) { - element.setAttribute(attr, group.attributes[attr]); - } - } - - return element; - } -}); - -const sizeData = function(str) { - if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) { - // str is a number with no unit specified. - // default unit is bp, per graphix package. - return { number: +str, unit: "bp" } - } else { - const match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(str); - if (!match) { - throw new ParseError("Invalid size: '" + str + "' in \\includegraphics"); - } - const data = { - number: +(match[1] + match[2]), // sign + magnitude, cast to number - unit: match[3] - }; - if (!validUnit(data)) { - throw new ParseError("Invalid unit: '" + data.unit + "' in \\includegraphics."); - } - return data - } -}; - -defineFunction({ - type: "includegraphics", - names: ["\\includegraphics"], - props: { - numArgs: 1, - numOptionalArgs: 1, - argTypes: ["raw", "url"], - allowedInText: false - }, - handler: ({ parser, token }, args, optArgs) => { - let width = { number: 0, unit: "em" }; - let height = { number: 0.9, unit: "em" }; // sorta character sized. - let totalheight = { number: 0, unit: "em" }; - let alt = ""; - - if (optArgs[0]) { - const attributeStr = assertNodeType(optArgs[0], "raw").string; - - // Parser.js does not parse key/value pairs. We get a string. - const attributes = attributeStr.split(","); - for (let i = 0; i < attributes.length; i++) { - const keyVal = attributes[i].split("="); - if (keyVal.length === 2) { - const str = keyVal[1].trim(); - switch (keyVal[0].trim()) { - case "alt": - alt = str; - break - case "width": - width = sizeData(str); - break - case "height": - height = sizeData(str); - break - case "totalheight": - totalheight = sizeData(str); - break - default: - throw new ParseError("Invalid key: '" + keyVal[0] + "' in \\includegraphics.") - } - } - } - } - - const src = assertNodeType(args[0], "url").url; - - if (alt === "") { - // No alt given. Use the file name. Strip away the path. - alt = src; - alt = alt.replace(/^.*[\\/]/, ""); - alt = alt.substring(0, alt.lastIndexOf(".")); - } - - if ( - !parser.settings.isTrusted({ - command: "\\includegraphics", - url: src - }) - ) { - throw new ParseError(`Function "\\includegraphics" is not trusted`, token) - } - - return { - type: "includegraphics", - mode: parser.mode, - alt: alt, - width: width, - height: height, - totalheight: totalheight, - src: src - } - }, - mathmlBuilder: (group, style) => { - const height = calculateSize(group.height, style); - const depth = { number: 0, unit: "em" }; - - if (group.totalheight.number > 0) { - if (group.totalheight.unit === height.unit && - group.totalheight.number > height.number) { - depth.number = group.totalheight.number - height.number; - depth.unit = height.unit; - } - } - - let width = 0; - if (group.width.number > 0) { - width = calculateSize(group.width, style); - } - - const graphicStyle = { height: height.number + depth.number + "em" }; - if (width.number > 0) { - graphicStyle.width = width.number + width.unit; - } - if (depth.number > 0) { - graphicStyle.verticalAlign = -depth.number + depth.unit; - } - - const node = new Img(group.src, group.alt, graphicStyle); - node.height = height; - node.depth = depth; - return new MathNode("mtext", [node]) - } -}); - -// Horizontal spacing commands - - -// TODO: \hskip and \mskip should support plus and minus in lengths - -defineFunction({ - type: "kern", - names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"], - props: { - numArgs: 1, - argTypes: ["size"], - primitive: true, - allowedInText: true - }, - handler({ parser, funcName, token }, args) { - const size = assertNodeType(args[0], "size"); - if (parser.settings.strict) { - const mathFunction = funcName[1] === "m"; // \mkern, \mskip - const muUnit = size.value.unit === "mu"; - if (mathFunction) { - if (!muUnit) { - throw new ParseError(`LaTeX's ${funcName} supports only mu units, ` + - `not ${size.value.unit} units`, token) - } - if (parser.mode !== "math") { - throw new ParseError(`LaTeX's ${funcName} works only in math mode`, token) - } - } else { - // !mathFunction - if (muUnit) { - throw new ParseError(`LaTeX's ${funcName} doesn't support mu units`, token) - } - } - } - return { - type: "kern", - mode: parser.mode, - dimension: size.value - }; - }, - mathmlBuilder(group, style) { - const dimension = calculateSize(group.dimension, style); - const ch = dimension.number > 0 && dimension.unit === "em" - ? spaceCharacter(dimension.number) - : ""; - if (group.mode === "text" && ch.length > 0) { - const character = new TextNode(ch); - return new MathNode("mtext", [character]); - } else { - if (dimension.number >= 0) { - const node = new MathNode("mspace"); - node.setAttribute("width", dimension.number + dimension.unit); - return node - } else { - // Don't use or because - // WebKit recognizes negative left margin only on a element - const node = new MathNode("mrow"); - node.style.marginLeft = dimension.number + dimension.unit; - return node - } - } - } -}); - -const spaceCharacter = function(width) { - if (width >= 0.05555 && width <= 0.05556) { - return "\u200a"; //   - } else if (width >= 0.1666 && width <= 0.1667) { - return "\u2009"; //   - } else if (width >= 0.2222 && width <= 0.2223) { - return "\u2005"; //   - } else if (width >= 0.2777 && width <= 0.2778) { - return "\u2005\u200a"; //    - } else { - return ""; - } -}; - -// Limit valid characters to a small set, for safety. -const invalidIdRegEx = /[^A-Za-z_0-9-]/g; - -defineFunction({ - type: "label", - names: ["\\label"], - props: { - numArgs: 1, - argTypes: ["raw"] - }, - handler({ parser }, args) { - return { - type: "label", - mode: parser.mode, - string: args[0].string.replace(invalidIdRegEx, "") - }; - }, - mathmlBuilder(group, style) { - // Return a no-width, no-ink element with an HTML id. - const node = new MathNode("mrow", [], ["tml-label"]); - if (group.string.length > 0) { - node.setLabel(group.string); - } - return node - } -}); - -// Horizontal overlap functions - -const textModeLap = ["\\clap", "\\llap", "\\rlap"]; - -defineFunction({ - type: "lap", - names: ["\\mathllap", "\\mathrlap", "\\mathclap", "\\clap", "\\llap", "\\rlap"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser, funcName, token }, args) => { - if (textModeLap.includes(funcName)) { - if (parser.settings.strict && parser.mode !== "text") { - throw new ParseError(`{${funcName}} can be used only in text mode. - Try \\math${funcName.slice(1)}`, token) - } - funcName = funcName.slice(1); - } else { - funcName = funcName.slice(5); - } - const body = args[0]; - return { - type: "lap", - mode: parser.mode, - alignment: funcName, - body - } - }, - mathmlBuilder: (group, style) => { - // mathllap, mathrlap, mathclap - let strut; - if (group.alignment === "llap") { - // We need an invisible strut with the same depth as the group. - // We can't just read the depth, so we use \vphantom methods. - const phantomInner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", phantomInner); - strut = new MathNode("mpadded", [phantom]); - strut.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - } - - const inner = buildGroup$1(group.body, style); - let node; - if (group.alignment === "llap") { - inner.style.position = "absolute"; - inner.style.right = "0"; - inner.style.bottom = `0`; // If we could have read the ink depth, it would go here. - node = new MathNode("mpadded", [strut, inner]); - } else { - node = new MathNode("mpadded", [inner]); - } - - if (group.alignment === "rlap") { - if (group.body.body.length > 0 && group.body.body[0].type === "genfrac") { - // In Firefox, a squashes the 3/18em padding of a child \frac. Put it back. - node.setAttribute("lspace", "0.16667em"); - } - } else { - const offset = group.alignment === "llap" ? "-1" : "-0.5"; - node.setAttribute("lspace", offset + "width"); - if (group.alignment === "llap") { - node.style.position = "relative"; - } else { - node.style.display = "flex"; - node.style.justifyContent = "center"; - } - } - node.setAttribute("width", "0.1px"); // Don't use 0. WebKit would hide it. - return node - } -}); - -// Switching from text mode back to math mode -defineFunction({ - type: "ordgroup", - names: ["\\(", "$"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: false - }, - handler({ funcName, parser }, args) { - const outerMode = parser.mode; - parser.switchMode("math"); - const close = funcName === "\\(" ? "\\)" : "$"; - const body = parser.parseExpression(false, close); - parser.expect(close); - parser.switchMode(outerMode); - return { - type: "ordgroup", - mode: parser.mode, - body - }; - } -}); - -// Check for extra closing math delimiters -defineFunction({ - type: "text", // Doesn't matter what this is. - names: ["\\)", "\\]"], - props: { - numArgs: 0, - allowedInText: true, - allowedInMath: false - }, - handler(context, token) { - throw new ParseError(`Mismatched ${context.funcName}`, token); - } -}); - -const chooseStyle = (group, style) => { - switch (style.level) { - case StyleLevel.DISPLAY: // 0 - return group.display; - case StyleLevel.TEXT: // 1 - return group.text; - case StyleLevel.SCRIPT: // 2 - return group.script; - case StyleLevel.SCRIPTSCRIPT: // 3 - return group.scriptscript; - default: - return group.text; - } -}; - -defineFunction({ - type: "mathchoice", - names: ["\\mathchoice"], - props: { - numArgs: 4, - primitive: true - }, - handler: ({ parser }, args) => { - return { - type: "mathchoice", - mode: parser.mode, - display: ordargument(args[0]), - text: ordargument(args[1]), - script: ordargument(args[2]), - scriptscript: ordargument(args[3]) - }; - }, - mathmlBuilder: (group, style) => { - const body = chooseStyle(group, style); - return buildExpressionRow(body, style); - } -}); - -const textAtomTypes = ["text", "textord", "mathord", "atom"]; - -function mathmlBuilder$3(group, style) { - let node; - const inner = buildExpression(group.body, style); - - if (group.mclass === "minner") { - node = new MathNode("mpadded", inner); - } else if (group.mclass === "mord") { - if (group.isCharacterBox || inner[0].type === "mathord") { - node = inner[0]; - node.type = "mi"; - if (node.children.length === 1 && node.children[0].text && node.children[0].text === "∇") { - node.setAttribute("mathvariant", "normal"); - } - } else { - node = new MathNode("mi", inner); - } - } else { - node = new MathNode("mrow", inner); - if (group.mustPromote) { - node = inner[0]; - node.type = "mo"; - if (group.isCharacterBox && group.body[0].text && /[A-Za-z]/.test(group.body[0].text)) { - node.setAttribute("mathvariant", "italic"); - } - } else { - node = new MathNode("mrow", inner); - } - - // Set spacing based on what is the most likely adjacent atom type. - // See TeXbook p170. - const doSpacing = style.level < 2; // Operator spacing is zero inside a (sub|super)script. - if (node.type === "mrow") { - if (doSpacing ) { - if (group.mclass === "mbin") { - // medium space - node.children.unshift(padding(0.2222)); - node.children.push(padding(0.2222)); - } else if (group.mclass === "mrel") { - // thickspace - node.children.unshift(padding(0.2778)); - node.children.push(padding(0.2778)); - } else if (group.mclass === "mpunct") { - node.children.push(padding(0.1667)); - } else if (group.mclass === "minner") { - node.children.unshift(padding(0.0556)); // 1 mu is the most likely option - node.children.push(padding(0.0556)); - } - } - } else { - if (group.mclass === "mbin") { - // medium space - node.attributes.lspace = (doSpacing ? "0.2222em" : "0"); - node.attributes.rspace = (doSpacing ? "0.2222em" : "0"); - } else if (group.mclass === "mrel") { - // thickspace - node.attributes.lspace = (doSpacing ? "0.2778em" : "0"); - node.attributes.rspace = (doSpacing ? "0.2778em" : "0"); - } else if (group.mclass === "mpunct") { - node.attributes.lspace = "0em"; - node.attributes.rspace = (doSpacing ? "0.1667em" : "0"); - } else if (group.mclass === "mopen" || group.mclass === "mclose") { - node.attributes.lspace = "0em"; - node.attributes.rspace = "0em"; - } else if (group.mclass === "minner" && doSpacing) { - node.attributes.lspace = "0.0556em"; // 1 mu is the most likely option - node.attributes.width = "+0.1111em"; - } - } - - if (!(group.mclass === "mopen" || group.mclass === "mclose")) { - delete node.attributes.stretchy; - delete node.attributes.form; - } - } - return node; -} - -// Math class commands except \mathop -defineFunction({ - type: "mclass", - names: [ - "\\mathord", - "\\mathbin", - "\\mathrel", - "\\mathopen", - "\\mathclose", - "\\mathpunct", - "\\mathinner" - ], - props: { - numArgs: 1, - primitive: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - const isCharacterBox$1 = isCharacterBox(body); - // We should not wrap a around a or . That would be invalid MathML. - // In that case, we instead promote the text contents of the body to the parent. - let mustPromote = true; - const mord = { type: "mathord", text: "", mode: parser.mode }; - const arr = (body.body) ? body.body : [body]; - for (const arg of arr) { - if (textAtomTypes.includes(arg.type)) { - if (symbols[parser.mode][arg.text]) { - mord.text += symbols[parser.mode][arg.text].replace; - } else if (arg.text) { - mord.text += arg.text; - } else if (arg.body) { - arg.body.map(e => { mord.text += e.text; }); - } - } else { - mustPromote = false; - break - } - } - if (mustPromote && funcName === "\\mathord" && mord.type === "mathord" - && mord.text.length > 1) { - return mord - } else { - return { - type: "mclass", - mode: parser.mode, - mclass: "m" + funcName.slice(5), - body: ordargument(mustPromote ? mord : body), - isCharacterBox: isCharacterBox$1, - mustPromote - }; - } - }, - mathmlBuilder: mathmlBuilder$3 -}); - -const binrelClass = (arg) => { - // \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument. - // (by rendering separately and with {}s before and after, and measuring - // the change in spacing). We'll do roughly the same by detecting the - // atom type directly. - const atom = arg.type === "ordgroup" && arg.body.length && arg.body.length === 1 - ? arg.body[0] - : arg; - if (atom.type === "atom") { - // BIN args are sometimes changed to OPEN, so check the original family. - const family = arg.body.length > 0 && arg.body[0].text && symbols.math[arg.body[0].text] - ? symbols.math[arg.body[0].text].group - : atom.family; - if (family === "bin" || family === "rel") { - return "m" + family; - } else { - return "mord"; - } - } else { - return "mord"; - } -}; - -// \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord. -// This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX. -defineFunction({ - type: "mclass", - names: ["\\@binrel"], - props: { - numArgs: 2 - }, - handler({ parser }, args) { - return { - type: "mclass", - mode: parser.mode, - mclass: binrelClass(args[0]), - body: ordargument(args[1]), - isCharacterBox: isCharacterBox(args[1]) - }; - } -}); - -// Build a relation or stacked op by placing one symbol on top of another -defineFunction({ - type: "mclass", - names: ["\\stackrel", "\\overset", "\\underset"], - props: { - numArgs: 2 - }, - handler({ parser, funcName }, args) { - const baseArg = args[1]; - const shiftedArg = args[0]; - - let mclass; - if (funcName !== "\\stackrel") { - // LaTeX applies \binrel spacing to \overset and \underset. - mclass = binrelClass(baseArg); - } else { - mclass = "mrel"; // for \stackrel - } - - const baseType = mclass === "mrel" || mclass === "mbin" - ? "op" - : "ordgroup"; - - const baseOp = { - type: baseType, - mode: baseArg.mode, - limits: true, - alwaysHandleSupSub: true, - parentIsSupSub: false, - symbol: false, - suppressBaseShift: funcName !== "\\stackrel", - body: ordargument(baseArg) - }; - - return { - type: "supsub", - mode: shiftedArg.mode, - stack: true, - base: baseOp, - sup: funcName === "\\underset" ? null : shiftedArg, - sub: funcName === "\\underset" ? shiftedArg : null - }; - }, - mathmlBuilder: mathmlBuilder$3 -}); - -// Helper function -const buildGroup = (el, style, noneNode) => { - if (!el) { return noneNode } - const node = buildGroup$1(el, style); - if (node.type === "mrow" && node.children.length === 0) { return noneNode } - return node -}; - -defineFunction({ - type: "multiscript", - names: ["\\sideset", "\\pres@cript"], // See macros.js for \prescript - props: { - numArgs: 3 - }, - handler({ parser, funcName, token }, args) { - if (args[2].body.length === 0) { - throw new ParseError(funcName + `cannot parse an empty base.`) - } - const base = args[2].body[0]; - if (parser.settings.strict && funcName === "\\sideset" && !base.symbol) { - throw new ParseError(`The base of \\sideset must be a big operator. Try \\prescript.`) - } - - if ((args[0].body.length > 0 && args[0].body[0].type !== "supsub") || - (args[1].body.length > 0 && args[1].body[0].type !== "supsub")) { - throw new ParseError("\\sideset can parse only subscripts and " + - "superscripts in its first two arguments", token) - } - - // The prescripts and postscripts come wrapped in a supsub. - const prescripts = args[0].body.length > 0 ? args[0].body[0] : null; - const postscripts = args[1].body.length > 0 ? args[1].body[0] : null; - - if (!prescripts && !postscripts) { - return base - } else if (!prescripts) { - // It's not a multi-script. Get a \textstyle supsub. - return { - type: "styling", - mode: parser.mode, - scriptLevel: "text", - body: [{ - type: "supsub", - mode: parser.mode, - base, - sup: postscripts.sup, - sub: postscripts.sub - }] - } - } else { - return { - type: "multiscript", - mode: parser.mode, - isSideset: funcName === "\\sideset", - prescripts, - postscripts, - base - } - } - }, - mathmlBuilder(group, style) { - const base = buildGroup$1(group.base, style); - - const prescriptsNode = new MathNode("mprescripts"); - const noneNode = new MathNode("none"); - let children = []; - - const preSub = buildGroup(group.prescripts.sub, style, noneNode); - const preSup = buildGroup(group.prescripts.sup, style, noneNode); - if (group.isSideset) { - // This seems silly, but LaTeX does this. Firefox ignores it, which does not make me sad. - preSub.setAttribute("style", "text-align: left;"); - preSup.setAttribute("style", "text-align: left;"); - } - - if (group.postscripts) { - const postSub = buildGroup(group.postscripts.sub, style, noneNode); - const postSup = buildGroup(group.postscripts.sup, style, noneNode); - children = [base, postSub, postSup, prescriptsNode, preSub, preSup]; - } else { - children = [base, prescriptsNode, preSub, preSup]; - } - - return new MathNode("mmultiscripts", children); - } -}); - -defineFunction({ - type: "not", - names: ["\\not"], - props: { - numArgs: 1, - primitive: true, - allowedInText: false - }, - handler({ parser }, args) { - const isCharacterBox$1 = isCharacterBox(args[0]); - let body; - if (isCharacterBox$1) { - body = ordargument(args[0]); - if (body[0].text.charAt(0) === "\\") { - body[0].text = symbols.math[body[0].text].replace; - } - // \u0338 is the Unicode Combining Long Solidus Overlay - body[0].text = body[0].text.slice(0, 1) + "\u0338" + body[0].text.slice(1); - } else { - // When the argument is not a character box, TeX does an awkward, poorly placed overlay. - // We'll do the same. - const notNode = { type: "textord", mode: "math", text: "\u0338" }; - const kernNode = { type: "kern", mode: "math", dimension: { number: -0.6, unit: "em" } }; - body = [notNode, kernNode, args[0]]; - } - return { - type: "not", - mode: parser.mode, - body, - isCharacterBox: isCharacterBox$1 - }; - }, - mathmlBuilder(group, style) { - if (group.isCharacterBox) { - const inner = buildExpression(group.body, style, true); - return inner[0] - } else { - return buildExpressionRow(group.body, style) - } - } -}); - -// Limits, symbols - -// Some helpers - -const ordAtomTypes = ["textord", "mathord", "atom"]; - -// Most operators have a large successor symbol, but these don't. -const noSuccessor = ["\\smallint"]; - -// Math operators (e.g. \sin) need a space between these types and themselves: -const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright", "font"]; - -// NOTE: Unlike most `builders`s, this one handles not only "op", but also -// "supsub" since some of them (like \int) can affect super/subscripting. - -const setSpacing = node => { - // The user wrote a \mathop{…} function. Change spacing from default to OP spacing. - // The most likely spacing for an OP is a thin space per TeXbook p170. - node.attributes.lspace = "0.1667em"; - node.attributes.rspace = "0.1667em"; -}; - -const mathmlBuilder$2 = (group, style) => { - let node; - - if (group.symbol) { - // This is a symbol. Just add the symbol. - node = new MathNode("mo", [makeText(group.name, group.mode)]); - if (noSuccessor.includes(group.name)) { - node.setAttribute("largeop", "false"); - } else { - node.setAttribute("movablelimits", "false"); - } - if (group.fromMathOp) { setSpacing(node); } - } else if (group.body) { - // This is an operator with children. Add them. - node = new MathNode("mo", buildExpression(group.body, style)); - if (group.fromMathOp) { setSpacing(node); } - } else { - // This is a text operator. Add all of the characters from the operator's name. - node = new MathNode("mi", [new TextNode(group.name.slice(1))]); - - if (!group.parentIsSupSub) { - // Append an invisible . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - const row = [node, operator]; - // Set spacing - if (group.needsLeadingSpace) { - const lead = new MathNode("mspace"); - lead.setAttribute("width", "0.1667em"); // thin space. - row.unshift(lead); - } - if (!group.isFollowedByDelimiter) { - const trail = new MathNode("mspace"); - trail.setAttribute("width", "0.1667em"); // thin space. - row.push(trail); - } - node = new MathNode("mrow", row); - } - } - - return node; -}; - -const singleCharBigOps = { - "\u220F": "\\prod", - "\u2210": "\\coprod", - "\u2211": "\\sum", - "\u22c0": "\\bigwedge", - "\u22c1": "\\bigvee", - "\u22c2": "\\bigcap", - "\u22c3": "\\bigcup", - "\u2a00": "\\bigodot", - "\u2a01": "\\bigoplus", - "\u2a02": "\\bigotimes", - "\u2a04": "\\biguplus", - "\u2a05": "\\bigsqcap", - "\u2a06": "\\bigsqcup", - "\u2a03": "\\bigcupdot", - "\u2a07": "\\bigdoublevee", - "\u2a08": "\\bigdoublewedge", - "\u2a09": "\\bigtimes" -}; - -defineFunction({ - type: "op", - names: [ - "\\coprod", - "\\bigvee", - "\\bigwedge", - "\\biguplus", - "\\bigcupplus", - "\\bigcupdot", - "\\bigcap", - "\\bigcup", - "\\bigdoublevee", - "\\bigdoublewedge", - "\\intop", - "\\prod", - "\\sum", - "\\bigotimes", - "\\bigoplus", - "\\bigodot", - "\\bigsqcap", - "\\bigsqcup", - "\\bigtimes", - "\\smallint", - "\u220F", - "\u2210", - "\u2211", - "\u22c0", - "\u22c1", - "\u22c2", - "\u22c3", - "\u2a00", - "\u2a01", - "\u2a02", - "\u2a03", - "\u2a04", - "\u2a05", - "\u2a06", - "\u2a07", - "\u2a08", - "\u2a09" - ], - props: { - numArgs: 0 - }, - handler: ({ parser, funcName }, args) => { - let fName = funcName; - if (fName.length === 1) { - fName = singleCharBigOps[fName]; - } - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: true, - stack: false, // This is true for \stackrel{}, not here. - name: fName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// Note: calling defineFunction with a type that's already been defined only -// works because the same mathmlBuilder is being used. -defineFunction({ - type: "op", - names: ["\\mathop"], - props: { - numArgs: 1, - primitive: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - // It would be convienient to just wrap a around the argument. - // But if the argument is a or , that would be invalid MathML. - // In that case, we instead promote the text contents of the body to the parent. - const arr = (body.body) ? body.body : [body]; - const isSymbol = arr.length === 1 && ordAtomTypes.includes(arr[0].type); - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: isSymbol, - fromMathOp: true, - stack: false, - name: isSymbol ? arr[0].text : null, - body: isSymbol ? null : ordargument(body) - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// There are 2 flags for operators; whether they produce limits in -// displaystyle, and whether they are symbols and should grow in -// displaystyle. These four groups cover the four possible choices. - -const singleCharIntegrals = { - "\u222b": "\\int", - "\u222c": "\\iint", - "\u222d": "\\iiint", - "\u222e": "\\oint", - "\u222f": "\\oiint", - "\u2230": "\\oiiint", - "\u2231": "\\intclockwise", - "\u2232": "\\varointclockwise", - "\u2a0c": "\\iiiint", - "\u2a0d": "\\intbar", - "\u2a0e": "\\intBar", - "\u2a0f": "\\fint", - "\u2a12": "\\rppolint", - "\u2a13": "\\scpolint", - "\u2a15": "\\pointint", - "\u2a16": "\\sqint", - "\u2a17": "\\intlarhk", - "\u2a18": "\\intx", - "\u2a19": "\\intcap", - "\u2a1a": "\\intcup" -}; - -// No limits, not symbols -defineFunction({ - type: "op", - names: [ - "\\arcsin", - "\\arccos", - "\\arctan", - "\\arctg", - "\\arcctg", - "\\arg", - "\\ch", - "\\cos", - "\\cosec", - "\\cosh", - "\\cot", - "\\cotg", - "\\coth", - "\\csc", - "\\ctg", - "\\cth", - "\\deg", - "\\dim", - "\\exp", - "\\hom", - "\\ker", - "\\lg", - "\\ln", - "\\log", - "\\sec", - "\\sin", - "\\sinh", - "\\sh", - "\\sgn", - "\\tan", - "\\tanh", - "\\tg", - "\\th" - ], - props: { - numArgs: 0 - }, - handler({ parser, funcName }) { - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "op", - mode: parser.mode, - limits: false, - parentIsSupSub: false, - symbol: false, - stack: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), - name: funcName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// Limits, not symbols -defineFunction({ - type: "op", - names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"], - props: { - numArgs: 0 - }, - handler({ parser, funcName }) { - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "op", - mode: parser.mode, - limits: true, - parentIsSupSub: false, - symbol: false, - stack: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), - name: funcName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// No limits, symbols -defineFunction({ - type: "op", - names: [ - "\\int", - "\\iint", - "\\iiint", - "\\iiiint", - "\\oint", - "\\oiint", - "\\oiiint", - "\\intclockwise", - "\\varointclockwise", - "\\intbar", - "\\intBar", - "\\fint", - "\\rppolint", - "\\scpolint", - "\\pointint", - "\\sqint", - "\\intlarhk", - "\\intx", - "\\intcap", - "\\intcup", - "\u222b", - "\u222c", - "\u222d", - "\u222e", - "\u222f", - "\u2230", - "\u2231", - "\u2232", - "\u2a0c", - "\u2a0d", - "\u2a0e", - "\u2a0f", - "\u2a12", - "\u2a13", - "\u2a15", - "\u2a16", - "\u2a17", - "\u2a18", - "\u2a19", - "\u2a1a" - ], - props: { - numArgs: 0, - allowedInArgument: true - }, - handler({ parser, funcName }) { - let fName = funcName; - if (fName.length === 1) { - fName = singleCharIntegrals[fName]; - } - return { - type: "op", - mode: parser.mode, - limits: false, - parentIsSupSub: false, - symbol: true, - stack: false, - name: fName - }; - }, - mathmlBuilder: mathmlBuilder$2 -}); - -// NOTE: Unlike most builders, this one handles not only -// "operatorname", but also "supsub" since \operatorname* can -// affect super/subscripting. - -const mathmlBuilder$1 = (group, style) => { - let expression = buildExpression(group.body, style.withFont("mathrm")); - - // Is expression a string or has it something like a fraction? - let isAllString = true; // default - for (let i = 0; i < expression.length; i++) { - let node = expression[i]; - if (node instanceof MathNode) { - if ((node.type === "mrow" || node.type === "mpadded") && node.children.length === 1 && - node.children[0] instanceof MathNode) { - node = node.children[0]; - } else if (node.type === "mrow" && node.children.length === 2 && - node.children[0] instanceof MathNode && - node.children[1] instanceof MathNode && - node.children[1].type === "mspace" && !node.children[1].attributes.width && - node.children[1].children.length === 0) { - // This is a workaround for a Firefox bug that applies spacing to - // an with mathvariant="normal". - node = node.children[0]; - } - switch (node.type) { - case "mi": - case "mn": - case "ms": - case "mtext": - break; // Do nothing yet. - case "mspace": - { - if (node.attributes.width) { - const width = node.attributes.width.replace("em", ""); - const ch = spaceCharacter(Number(width)); - if (ch === "") { - isAllString = false; - } else { - expression[i] = new MathNode("mtext", [new TextNode(ch)]); - } - } - } - break - case "mo": { - const child = node.children[0]; - if (node.children.length === 1 && child instanceof TextNode) { - child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); - } else { - isAllString = false; - } - break - } - default: - isAllString = false; - } - } else { - isAllString = false; - } - } - - if (isAllString) { - // Write a single TextNode instead of multiple nested tags. - const word = expression.map((node) => node.toText()).join(""); - expression = [new TextNode(word)]; - } else if ( - expression.length === 1 - && ["mover", "munder"].includes(expression[0].type) && - (expression[0].children[0].type === "mi" || expression[0].children[0].type === "mtext") - ) { - expression[0].children[0].type = "mi"; - if (group.parentIsSupSub) { - return new MathNode("mrow", expression) - } else { - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - return newDocumentFragment([expression[0], operator]) - } - } - - let wrapper; - if (isAllString) { - wrapper = new MathNode("mi", expression); - if (expression[0].text.length === 1) { - wrapper.setAttribute("mathvariant", "normal"); - } - } else { - wrapper = new MathNode("mrow", expression); - } - - if (!group.parentIsSupSub) { - // Append an . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - const fragment = [wrapper, operator]; - if (group.needsLeadingSpace) { - // LaTeX gives operator spacing, but a gets ord spacing. - // So add a leading space. - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - fragment.unshift(space); - } - if (!group.isFollowedByDelimiter) { - const trail = new MathNode("mspace"); - trail.setAttribute("width", "0.1667em"); // thin space. - fragment.push(trail); - } - return newDocumentFragment(fragment) - } - - return wrapper -}; - -// \operatorname -// amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@ -defineFunction({ - type: "operatorname", - names: ["\\operatorname@", "\\operatornamewithlimits"], - props: { - numArgs: 1, - allowedInArgument: true - }, - handler: ({ parser, funcName }, args) => { - const body = args[0]; - const prevAtomType = parser.prevAtomType; - const next = parser.gullet.future().text; - return { - type: "operatorname", - mode: parser.mode, - body: ordargument(body), - alwaysHandleSupSub: (funcName === "\\operatornamewithlimits"), - limits: false, - parentIsSupSub: false, - isFollowedByDelimiter: isDelimiter(next), - needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType) - }; - }, - mathmlBuilder: mathmlBuilder$1 -}); - -defineMacro("\\operatorname", - "\\@ifstar\\operatornamewithlimits\\operatorname@"); - -defineFunctionBuilders({ - type: "ordgroup", - mathmlBuilder(group, style) { - return buildExpressionRow(group.body, style, group.semisimple); - } -}); - -defineFunction({ - type: "phantom", - names: ["\\phantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "phantom", - mode: parser.mode, - body: ordargument(body) - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(group.body, style); - return new MathNode("mphantom", inner); - } -}); - -defineFunction({ - type: "hphantom", - names: ["\\hphantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "hphantom", - mode: parser.mode, - body - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", inner); - const node = new MathNode("mpadded", [phantom]); - node.setAttribute("height", "0px"); - node.setAttribute("depth", "0px"); - return node; - } -}); - -defineFunction({ - type: "vphantom", - names: ["\\vphantom"], - props: { - numArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args) => { - const body = args[0]; - return { - type: "vphantom", - mode: parser.mode, - body - }; - }, - mathmlBuilder: (group, style) => { - const inner = buildExpression(ordargument(group.body), style); - const phantom = new MathNode("mphantom", inner); - const node = new MathNode("mpadded", [phantom]); - node.setAttribute("width", "0px"); - return node; - } -}); - -// In LaTeX, \pmb is a simulation of bold font. -// The version of \pmb in ambsy.sty works by typesetting three copies of the argument -// with small offsets. We use CSS font-weight:bold. - -defineFunction({ - type: "pmb", - names: ["\\pmb"], - props: { - numArgs: 1, - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "pmb", - mode: parser.mode, - body: ordargument(args[0]) - } - }, - mathmlBuilder(group, style) { - const inner = buildExpression(group.body, style); - // Wrap with an element. - const node = wrapWithMstyle(inner); - node.setAttribute("style", "font-weight:bold"); - return node - } -}); - -// \raise, \lower, and \raisebox - -const mathmlBuilder = (group, style) => { - const newStyle = style.withLevel(StyleLevel.TEXT); - const node = new MathNode("mpadded", [buildGroup$1(group.body, newStyle)]); - const dy = calculateSize(group.dy, style); - node.setAttribute("voffset", dy.number + dy.unit); - // Add padding, which acts to increase height in Chromium. - // TODO: Figure out some way to change height in Firefox w/o breaking Chromium. - if (dy.number > 0) { - node.style.padding = dy.number + dy.unit + " 0 0 0"; - } else { - node.style.padding = "0 0 " + Math.abs(dy.number) + dy.unit + " 0"; - } - return node -}; - -defineFunction({ - type: "raise", - names: ["\\raise", "\\lower"], - props: { - numArgs: 2, - argTypes: ["size", "primitive"], - primitive: true - }, - handler({ parser, funcName }, args) { - const amount = assertNodeType(args[0], "size").value; - if (funcName === "\\lower") { amount.number *= -1; } - const body = args[1]; - return { - type: "raise", - mode: parser.mode, - dy: amount, - body - }; - }, - mathmlBuilder -}); - - -defineFunction({ - type: "raise", - names: ["\\raisebox"], - props: { - numArgs: 2, - argTypes: ["size", "hbox"], - allowedInText: true - }, - handler({ parser, funcName }, args) { - const amount = assertNodeType(args[0], "size").value; - const body = args[1]; - return { - type: "raise", - mode: parser.mode, - dy: amount, - body - }; - }, - mathmlBuilder -}); - -defineFunction({ - type: "ref", - names: ["\\ref", "\\eqref"], - props: { - numArgs: 1, - argTypes: ["raw"] - }, - handler({ parser, funcName }, args) { - return { - type: "ref", - mode: parser.mode, - funcName, - string: args[0].string.replace(invalidIdRegEx, "") - }; - }, - mathmlBuilder(group, style) { - // Create an empty node. Set a class and an href attribute. - // The post-processor will populate with the target's tag or equation number. - const classes = group.funcName === "\\ref" ? ["tml-ref"] : ["tml-ref", "tml-eqref"]; - return new AnchorNode("#" + group.string, classes, null) - } -}); - -defineFunction({ - type: "reflect", - names: ["\\reflectbox"], - props: { - numArgs: 1, - argTypes: ["hbox"], - allowedInText: true - }, - handler({ parser }, args) { - return { - type: "reflect", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - const node = buildGroup$1(group.body, style); - node.style.transform = "scaleX(-1)"; - return node - } -}); - -defineFunction({ - type: "internal", - names: ["\\relax"], - props: { - numArgs: 0, - allowedInText: true, - allowedInArgument: true - }, - handler({ parser }) { - return { - type: "internal", - mode: parser.mode - }; - } -}); - -defineFunction({ - type: "rule", - names: ["\\rule"], - props: { - numArgs: 2, - numOptionalArgs: 1, - allowedInText: true, - allowedInMath: true, - argTypes: ["size", "size", "size"] - }, - handler({ parser }, args, optArgs) { - const shift = optArgs[0]; - const width = assertNodeType(args[0], "size"); - const height = assertNodeType(args[1], "size"); - return { - type: "rule", - mode: parser.mode, - shift: shift && assertNodeType(shift, "size").value, - width: width.value, - height: height.value - }; - }, - mathmlBuilder(group, style) { - const width = calculateSize(group.width, style); - const height = calculateSize(group.height, style); - const shift = group.shift - ? calculateSize(group.shift, style) - : { number: 0, unit: "em" }; - const color = (style.color && style.getColor()) || "black"; - - const rule = new MathNode("mspace"); - if (width.number > 0 && height.number > 0) { - rule.setAttribute("mathbackground", color); - } - rule.setAttribute("width", width.number + width.unit); - rule.setAttribute("height", height.number + height.unit); - if (shift.number === 0) { return rule } - - const wrapper = new MathNode("mpadded", [rule]); - if (shift.number >= 0) { - wrapper.setAttribute("height", "+" + shift.number + shift.unit); - } else { - wrapper.setAttribute("height", shift.number + shift.unit); - wrapper.setAttribute("depth", "+" + -shift.number + shift.unit); - } - wrapper.setAttribute("voffset", shift.number + shift.unit); - return wrapper; - } -}); - -const numRegEx = /^[0-9]$/; -const unicodeNumSubs = { - '0': '₀', - '1': '₁', - '2': '₂', - '3': '₃', - '4': '₄', - '5': '₅', - '6': '₆', - '7': '₇', - '8': '₈', - '9': '₉' -}; -const unicodeNumSups = { - '0': '⁰', - '1': '¹', - '2': '²', - '3': '³', - '4': '⁴', - '5': '⁵', - '6': '⁶', - '7': '⁷', - '8': '⁸', - '9': '⁹' -}; - -defineFunction({ - type: "sfrac", - names: ["\\sfrac"], - props: { - numArgs: 2, - allowedInText: true, - allowedInMath: true - }, - handler({ parser }, args) { - let numerator = ""; - for (const node of args[0].body) { - if (node.type !== "textord" || !numRegEx.test(node.text)) { - throw new ParseError("Numerator must be an integer.", node) - } - numerator += node.text; - } - let denominator = ""; - for (const node of args[1].body) { - if (node.type !== "textord" || !numRegEx.test(node.text)) { - throw new ParseError("Denominator must be an integer.", node) - } - denominator += node.text; - } - return { - type: "sfrac", - mode: parser.mode, - numerator, - denominator - }; - }, - mathmlBuilder(group, style) { - const numerator = group.numerator.split('').map(c => unicodeNumSups[c]).join(''); - const denominator = group.denominator.split('').map(c => unicodeNumSubs[c]).join(''); - // Use a fraction slash. - const text = new TextNode(numerator + "\u2044" + denominator, group.mode, style); - return new MathNode("mn", [text], ["special-fraction"]) - } -}); - -// The size mappings are taken from TeX with \normalsize=10pt. -// We don't have to track script level. MathML does that. -const sizeMap = { - "\\tiny": 0.5, - "\\sixptsize": 0.6, - "\\Tiny": 0.6, - "\\scriptsize": 0.7, - "\\footnotesize": 0.8, - "\\small": 0.9, - "\\normalsize": 1.0, - "\\large": 1.2, - "\\Large": 1.44, - "\\LARGE": 1.728, - "\\huge": 2.074, - "\\Huge": 2.488 -}; - -defineFunction({ - type: "sizing", - names: [ - "\\tiny", - "\\sixptsize", - "\\Tiny", - "\\scriptsize", - "\\footnotesize", - "\\small", - "\\normalsize", - "\\large", - "\\Large", - "\\LARGE", - "\\huge", - "\\Huge" - ], - props: { - numArgs: 0, - allowedInText: true - }, - handler: ({ breakOnTokenText, funcName, parser }, args) => { - if (parser.settings.strict && parser.mode === "math") { - // eslint-disable-next-line no-console - console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`); - } - const body = parser.parseExpression(false, breakOnTokenText, true); - return { - type: "sizing", - mode: parser.mode, - funcName, - body - }; - }, - mathmlBuilder: (group, style) => { - const newStyle = style.withFontSize(sizeMap[group.funcName]); - const inner = buildExpression(group.body, newStyle); - // Wrap with an element. - const node = wrapWithMstyle(inner); - const factor = (sizeMap[group.funcName] / style.fontSize).toFixed(4); - node.setAttribute("mathsize", factor + "em"); - return node; - } -}); - -// smash, with optional [tb], as in AMS - -defineFunction({ - type: "smash", - names: ["\\smash"], - props: { - numArgs: 1, - numOptionalArgs: 1, - allowedInText: true - }, - handler: ({ parser }, args, optArgs) => { - let smashHeight = false; - let smashDepth = false; - const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup"); - if (tbArg) { - // Optional [tb] argument is engaged. - // ref: amsmath: \renewcommand{\smash}[1][tb]{% - // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}% - let letter = ""; - for (let i = 0; i < tbArg.body.length; ++i) { - const node = tbArg.body[i]; - // TODO: Write an AssertSymbolNode - letter = node.text; - if (letter === "t") { - smashHeight = true; - } else if (letter === "b") { - smashDepth = true; - } else { - smashHeight = false; - smashDepth = false; - break; - } - } - } else { - smashHeight = true; - smashDepth = true; - } - - const body = args[0]; - return { - type: "smash", - mode: parser.mode, - body, - smashHeight, - smashDepth - }; - }, - mathmlBuilder: (group, style) => { - const node = new MathNode("mpadded", [buildGroup$1(group.body, style)]); - - if (group.smashHeight) { - node.setAttribute("height", "0px"); - } - - if (group.smashDepth) { - node.setAttribute("depth", "0px"); - } - - return node; - } -}); - -// Letters that are x-height w/o a descender. -const xHeights = ['a', 'c', 'e', 'ı', 'm', 'n', 'o', 'r', 's', 'u', 'v', 'w', 'x', 'z', 'α', - 'ε', 'ι', 'κ', 'ν', 'ο', 'π', 'σ', 'τ', 'υ', 'ω', '\\alpha', '\\epsilon', "\\iota", - '\\kappa', '\\nu', '\\omega', '\\pi', '\\tau', '\\omega']; - -defineFunction({ - type: "sqrt", - names: ["\\sqrt"], - props: { - numArgs: 1, - numOptionalArgs: 1 - }, - handler({ parser }, args, optArgs) { - const index = optArgs[0]; - const body = args[0]; - // Check if the body consists entirely of an x-height letter. - // TODO: Remove this check after Chromium is fixed. - if (body.body && body.body.length === 1 && body.body[0].text && - xHeights.includes(body.body[0].text)) { - // Chromium does not put enough space above an x-height letter. - // Insert a strut. - body.body.push({ - "type": "rule", - "mode": "math", - "shift": null, - "width": { "number": 0, "unit": "pt" }, - "height": { "number": 0.5, "unit": "em" } - }); - } - return { - type: "sqrt", - mode: parser.mode, - body, - index - }; - }, - mathmlBuilder(group, style) { - const { body, index } = group; - return index - ? new MathNode("mroot", [ - buildGroup$1(body, style), - buildGroup$1(index, style.incrementLevel()) - ]) - : new MathNode("msqrt", [buildGroup$1(body, style)]); - } -}); - -const styleMap = { - display: 0, - text: 1, - script: 2, - scriptscript: 3 -}; - -const styleAttributes = { - display: ["0", "true"], - text: ["0", "false"], - script: ["1", "false"], - scriptscript: ["2", "false"] -}; - -defineFunction({ - type: "styling", - names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"], - props: { - numArgs: 0, - allowedInText: true, - primitive: true - }, - handler({ breakOnTokenText, funcName, parser }, args) { - // parse out the implicit body - const body = parser.parseExpression(true, breakOnTokenText, true); - - const scriptLevel = funcName.slice(1, funcName.length - 5); - return { - type: "styling", - mode: parser.mode, - // Figure out what scriptLevel to use by pulling out the scriptLevel from - // the function name - scriptLevel, - body - }; - }, - mathmlBuilder(group, style) { - // Figure out what scriptLevel we're changing to. - const newStyle = style.withLevel(styleMap[group.scriptLevel]); - // The style argument in the next line does NOT directly set a MathML script level. - // It just tracks the style level, in case we need to know it for supsub or mathchoice. - const inner = buildExpression(group.body, newStyle); - // Wrap with an element. - const node = wrapWithMstyle(inner); - - const attr = styleAttributes[group.scriptLevel]; - - // Here is where we set the MathML script level. - node.setAttribute("scriptlevel", attr[0]); - node.setAttribute("displaystyle", attr[1]); - - return node; - } -}); - -/** - * Sometimes, groups perform special rules when they have superscripts or - * subscripts attached to them. This function lets the `supsub` group know that - * Sometimes, groups perform special rules when they have superscripts or - * its inner element should handle the superscripts and subscripts instead of - * handling them itself. - */ - -// Helpers -const symbolRegEx = /^m(over|under|underover)$/; - -// From the KaTeX font metrics, identify letters that encroach on a superscript. -const smallPad = "DHKLUcegorsuvxyzΠΥΨαδηιμνοτυχϵ"; -const mediumPad = "BCEFGIMNOPQRSTXZlpqtwΓΘΞΣΦΩβεζθξρςφψϑϕϱ"; -const largePad = "AJdfΔΛ"; - -// Super scripts and subscripts, whose precise placement can depend on other -// functions that precede them. -defineFunctionBuilders({ - type: "supsub", - mathmlBuilder(group, style) { - // Is the inner group a relevant horizontal brace or bracket? - let isBracket = false; - let isOver; - let isSup; - let appendApplyFunction = false; - let appendSpace = false; - let needsLeadingSpace = false; - - if (group.base && group.base.type === "horizBracket") { - isSup = !!group.sup; - if (isSup === group.base.isOver) { - isBracket = true; - isOver = group.base.isOver; - } - } - - if (group.base && !group.stack && - (group.base.type === "op" || group.base.type === "operatorname")) { - group.base.parentIsSupSub = true; - appendApplyFunction = !group.base.symbol; - appendSpace = appendApplyFunction && !group.isFollowedByDelimiter; - needsLeadingSpace = group.base.needsLeadingSpace; - } - - const children = group.stack && group.base.body.length === 1 - ? [buildGroup$1(group.base.body[0], style)] - : [buildGroup$1(group.base, style)]; - - // Note regarding scriptstyle level. - // (Sub|super)scripts should not shrink beyond MathML scriptlevel 2 aka \scriptscriptstyle - // Ref: https://w3c.github.io/mathml-core/#the-displaystyle-and-scriptlevel-attributes - // (BTW, MathML scriptlevel 2 is equal to Temml level 3.) - // But Chromium continues to shrink the (sub|super)scripts. So we explicitly set scriptlevel 2. - - const childStyle = style.inSubOrSup(); - if (group.sub) { - const sub = buildGroup$1(group.sub, childStyle); - if (style.level === 3) { sub.setAttribute("scriptlevel", "2"); } - children.push(sub); - } - - if (group.sup) { - const sup = buildGroup$1(group.sup, childStyle); - if (style.level === 3) { sup.setAttribute("scriptlevel", "2"); } - if (group.base && group.base.text && group.base.text.length === 1) { - // Make an italic correction on the superscript. - const text = group.base.text; - if (smallPad.indexOf(text) > -1) { - sup.classes.push("tml-sml-pad"); - } else if (mediumPad.indexOf(text) > -1) { - sup.classes.push("tml-med-pad"); - } else if (largePad.indexOf(text) > -1) { - sup.classes.push("tml-lrg-pad"); - } - } - children.push(sup); - } - - let nodeType; - if (isBracket) { - nodeType = isOver ? "mover" : "munder"; - } else if (!group.sub) { - const base = group.base; - if ( - base && - base.type === "op" && - base.limits && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "mover"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (base.limits || style.level === StyleLevel.DISPLAY) - ) { - nodeType = "mover"; - } else { - nodeType = "msup"; - } - } else if (!group.sup) { - const base = group.base; - if (group.stack) { - nodeType = "munder"; - } else if ( - base && - base.type === "op" && - base.limits && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "munder"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (base.limits || style.level === StyleLevel.DISPLAY) - ) { - nodeType = "munder"; - } else { - nodeType = "msub"; - } - } else { - const base = group.base; - if (base && ((base.type === "op" && base.limits) || base.type === "multiscript") && - (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) - ) { - nodeType = "munderover"; - } else if ( - base && - base.type === "operatorname" && - base.alwaysHandleSupSub && - (style.level === StyleLevel.DISPLAY || base.limits) - ) { - nodeType = "munderover"; - } else { - nodeType = "msubsup"; - } - } - - let node = new MathNode(nodeType, children); - if (appendApplyFunction) { - // Append an . - // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 - const operator = new MathNode("mo", [makeText("\u2061", "text")]); - if (needsLeadingSpace) { - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - node = newDocumentFragment([space, node, operator]); - } else { - node = newDocumentFragment([node, operator]); - } - if (appendSpace) { - const space = new MathNode("mspace"); - space.setAttribute("width", "0.1667em"); // thin space. - node.children.push(space); - } - } else if (symbolRegEx.test(nodeType)) { - // Wrap in a . Otherwise Firefox stretchy parens will not stretch to include limits. - node = new MathNode("mrow", [node]); - } - - return node - } -}); - -// Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js. - -const short = ["\\shortmid", "\\nshortmid", "\\shortparallel", - "\\nshortparallel", "\\smallsetminus"]; - -const arrows = ["\\Rsh", "\\Lsh", "\\restriction"]; - -const isArrow = str => { - if (str.length === 1) { - const codePoint = str.codePointAt(0); - return (0x218f < codePoint && codePoint < 0x2200) - } - return str.indexOf("arrow") > -1 || str.indexOf("harpoon") > -1 || arrows.includes(str) -}; - -defineFunctionBuilders({ - type: "atom", - mathmlBuilder(group, style) { - const node = new MathNode("mo", [makeText(group.text, group.mode)]); - if (group.family === "punct") { - node.setAttribute("separator", "true"); - } else if (group.family === "open" || group.family === "close") { - // Delims built here should not stretch vertically. - // See delimsizing.js for stretchy delims. - if (group.family === "open") { - node.setAttribute("form", "prefix"); - // Set an explicit attribute for stretch. Otherwise Firefox may do it wrong. - node.setAttribute("stretchy", "false"); - } else if (group.family === "close") { - node.setAttribute("form", "postfix"); - node.setAttribute("stretchy", "false"); - } - } else if (group.text === "\\mid") { - // Firefox messes up this spacing if at the end of an . See it explicitly. - node.setAttribute("lspace", "0.22em"); // medium space - node.setAttribute("rspace", "0.22em"); - node.setAttribute("stretchy", "false"); - } else if (group.family === "rel" && isArrow(group.text)) { - node.setAttribute("stretchy", "false"); - } else if (short.includes(group.text)) { - node.setAttribute("mathsize", "70%"); - } else if (group.text === ":") { - // ":" is not in the MathML operator dictionary. Give it BIN spacing. - node.attributes.lspace = "0.2222em"; - node.attributes.rspace = "0.2222em"; - } else if (group.needsSpacing) { - // Fix a MathML bug that occurs when a is between two elements. - if (group.family === "bin") { - return new MathNode("mrow", [padding(0.222), node, padding(0.222)]) - } else { - // REL spacing - return new MathNode("mrow", [padding(0.2778), node, padding(0.2778)]) - } - } - return node; - } -}); - -/** - * Maps TeX font commands to "mathvariant" attribute in buildMathML.js - */ -const fontMap = { - // styles - mathbf: "bold", - mathrm: "normal", - textit: "italic", - mathit: "italic", - mathnormal: "italic", - - // families - mathbb: "double-struck", - mathcal: "script", - mathfrak: "fraktur", - mathscr: "script", - mathsf: "sans-serif", - mathtt: "monospace" -}; - -/** - * Returns the math variant as a string or null if none is required. - */ -const getVariant = function(group, style) { - // Handle font specifiers as best we can. - // Chromium does not support the MathML mathvariant attribute. - // So we'll use Unicode replacement characters instead. - // But first, determine the math variant. - - // Deal with the \textit, \textbf, etc., functions. - if (style.fontFamily === "texttt") { - return "monospace" - } else if (style.fontFamily === "textsc") { - return "normal"; // handled via character substitution in symbolsOrd.js. - } else if (style.fontFamily === "textsf") { - if (style.fontShape === "textit" && style.fontWeight === "textbf") { - return "sans-serif-bold-italic" - } else if (style.fontShape === "textit") { - return "sans-serif-italic" - } else if (style.fontWeight === "textbf") { - return "sans-serif-bold" - } else { - return "sans-serif" - } - } else if (style.fontShape === "textit" && style.fontWeight === "textbf") { - return "bold-italic" - } else if (style.fontShape === "textit") { - return "italic" - } else if (style.fontWeight === "textbf") { - return "bold" - } - - // Deal with the \mathit, mathbf, etc, functions. - const font = style.font; - if (!font || font === "mathnormal") { - return null - } - - const mode = group.mode; - switch (font) { - case "mathit": - return "italic" - case "mathrm": { - const codePoint = group.text.codePointAt(0); - // LaTeX \mathrm returns italic for Greek characters. - return (0x03ab < codePoint && codePoint < 0x03cf) ? "italic" : "normal" - } - case "greekItalic": - return "italic" - case "up@greek": - return "normal" - case "boldsymbol": - case "mathboldsymbol": - return "bold-italic" - case "mathbf": - return "bold" - case "mathbb": - return "double-struck" - case "mathfrak": - return "fraktur" - case "mathscr": - case "mathcal": - return "script" - case "mathsf": - return "sans-serif" - case "mathsfit": - return "sans-serif-italic" - case "mathtt": - return "monospace" - } - - let text = group.text; - if (symbols[mode][text] && symbols[mode][text].replace) { - text = symbols[mode][text].replace; - } - - return Object.prototype.hasOwnProperty.call(fontMap, font) ? fontMap[font] : null -}; - -// Chromium does not support the MathML `mathvariant` attribute. -// Instead, we replace ASCII characters with Unicode characters that -// are defined in the font as bold, italic, double-struck, etc. -// This module identifies those Unicode code points. - -// First, a few helpers. -const script = Object.freeze({ - B: 0x20EA, // Offset from ASCII B to Unicode script B - E: 0x20EB, - F: 0x20EB, - H: 0x20C3, - I: 0x20C7, - L: 0x20C6, - M: 0x20E6, - R: 0x20C9, - e: 0x20CA, - g: 0x20A3, - o: 0x20C5 -}); - -const frak = Object.freeze({ - C: 0x20EA, - H: 0x20C4, - I: 0x20C8, - R: 0x20CA, - Z: 0x20CE -}); - -const bbb = Object.freeze({ - C: 0x20BF, // blackboard bold - H: 0x20C5, - N: 0x20C7, - P: 0x20C9, - Q: 0x20C9, - R: 0x20CB, - Z: 0x20CA -}); - -const bold = Object.freeze({ - "\u03f5": 0x1D2E7, // lunate epsilon - "\u03d1": 0x1D30C, // vartheta - "\u03f0": 0x1D2EE, // varkappa - "\u03c6": 0x1D319, // varphi - "\u03f1": 0x1D2EF, // varrho - "\u03d6": 0x1D30B // varpi -}); - -const boldItalic = Object.freeze({ - "\u03f5": 0x1D35B, // lunate epsilon - "\u03d1": 0x1D380, // vartheta - "\u03f0": 0x1D362, // varkappa - "\u03c6": 0x1D38D, // varphi - "\u03f1": 0x1D363, // varrho - "\u03d6": 0x1D37F // varpi -}); - -const boldsf = Object.freeze({ - "\u03f5": 0x1D395, // lunate epsilon - "\u03d1": 0x1D3BA, // vartheta - "\u03f0": 0x1D39C, // varkappa - "\u03c6": 0x1D3C7, // varphi - "\u03f1": 0x1D39D, // varrho - "\u03d6": 0x1D3B9 // varpi -}); - -const bisf = Object.freeze({ - "\u03f5": 0x1D3CF, // lunate epsilon - "\u03d1": 0x1D3F4, // vartheta - "\u03f0": 0x1D3D6, // varkappa - "\u03c6": 0x1D401, // varphi - "\u03f1": 0x1D3D7, // varrho - "\u03d6": 0x1D3F3 // varpi -}); - -// Code point offsets below are derived from https://www.unicode.org/charts/PDF/U1D400.pdf -const offset = Object.freeze({ - upperCaseLatin: { // A-Z - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D3BF }, - "italic": ch => { return 0x1D3F3 }, - "bold-italic": ch => { return 0x1D427 }, - "script": ch => { return script[ch] || 0x1D45B }, - "script-bold": ch => { return 0x1D48F }, - "fraktur": ch => { return frak[ch] || 0x1D4C3 }, - "fraktur-bold": ch => { return 0x1D52B }, - "double-struck": ch => { return bbb[ch] || 0x1D4F7 }, - "sans-serif": ch => { return 0x1D55F }, - "sans-serif-bold": ch => { return 0x1D593 }, - "sans-serif-italic": ch => { return 0x1D5C7 }, - "sans-serif-bold-italic": ch => { return 0x1D63C }, - "monospace": ch => { return 0x1D62F } - }, - lowerCaseLatin: { // a-z - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D3B9 }, - "italic": ch => { return ch === "h" ? 0x20A6 : 0x1D3ED }, - "bold-italic": ch => { return 0x1D421 }, - "script": ch => { return script[ch] || 0x1D455 }, - "script-bold": ch => { return 0x1D489 }, - "fraktur": ch => { return 0x1D4BD }, - "fraktur-bold": ch => { return 0x1D525 }, - "double-struck": ch => { return 0x1D4F1 }, - "sans-serif": ch => { return 0x1D559 }, - "sans-serif-bold": ch => { return 0x1D58D }, - "sans-serif-italic": ch => { return 0x1D5C1 }, - "sans-serif-bold-italic": ch => { return 0x1D5F5 }, - "monospace": ch => { return 0x1D629 } - }, - upperCaseGreek: { // A-Ω - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D317 }, - "italic": ch => { return 0x1D351 }, - // \boldsymbol actually returns upright bold for upperCaseGreek - "bold-italic": ch => { return 0x1D317 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - // Unicode has no code points for regular-weight san-serif Greek. Use bold. - "sans-serif": ch => { return 0x1D3C5 }, - "sans-serif-bold": ch => { return 0x1D3C5 }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0x1D3FF }, - "monospace": ch => { return 0 } - }, - lowerCaseGreek: { // α-ω - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D311 }, - "italic": ch => { return 0x1D34B }, - "bold-italic": ch => { return ch === "\u03d5" ? 0x1D37E : 0x1D385 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - // Unicode has no code points for regular-weight san-serif Greek. Use bold. - "sans-serif": ch => { return 0x1D3BF }, - "sans-serif-bold": ch => { return 0x1D3BF }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0x1D3F9 }, - "monospace": ch => { return 0 } - }, - varGreek: { // \varGamma, etc - "normal": ch => { return 0 }, - "bold": ch => { return bold[ch] || -51 }, - "italic": ch => { return 0 }, - "bold-italic": ch => { return boldItalic[ch] || 0x3A }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0 }, - "sans-serif": ch => { return boldsf[ch] || 0x74 }, - "sans-serif-bold": ch => { return boldsf[ch] || 0x74 }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return bisf[ch] || 0xAE }, - "monospace": ch => { return 0 } - }, - numeral: { // 0-9 - "normal": ch => { return 0 }, - "bold": ch => { return 0x1D79E }, - "italic": ch => { return 0 }, - "bold-italic": ch => { return 0 }, - "script": ch => { return 0 }, - "script-bold": ch => { return 0 }, - "fraktur": ch => { return 0 }, - "fraktur-bold": ch => { return 0 }, - "double-struck": ch => { return 0x1D7A8 }, - "sans-serif": ch => { return 0x1D7B2 }, - "sans-serif-bold": ch => { return 0x1D7BC }, - "sans-serif-italic": ch => { return 0 }, - "sans-serif-bold-italic": ch => { return 0 }, - "monospace": ch => { return 0x1D7C6 } - } -}); - -const variantChar = (ch, variant) => { - const codePoint = ch.codePointAt(0); - const block = 0x40 < codePoint && codePoint < 0x5b - ? "upperCaseLatin" - : 0x60 < codePoint && codePoint < 0x7b - ? "lowerCaseLatin" - : (0x390 < codePoint && codePoint < 0x3AA) - ? "upperCaseGreek" - : 0x3B0 < codePoint && codePoint < 0x3CA || ch === "\u03d5" - ? "lowerCaseGreek" - : 0x1D6E1 < codePoint && codePoint < 0x1D6FC || bold[ch] - ? "varGreek" - : (0x2F < codePoint && codePoint < 0x3A) - ? "numeral" - : "other"; - return block === "other" - ? ch - : String.fromCodePoint(codePoint + offset[block][variant](ch)) -}; - -const smallCaps = Object.freeze({ - a: "ᴀ", - b: "ʙ", - c: "ᴄ", - d: "ᴅ", - e: "ᴇ", - f: "ꜰ", - g: "ɢ", - h: "ʜ", - i: "ɪ", - j: "ᴊ", - k: "ᴋ", - l: "ʟ", - m: "ᴍ", - n: "ɴ", - o: "ᴏ", - p: "ᴘ", - q: "ǫ", - r: "ʀ", - s: "s", - t: "ᴛ", - u: "ᴜ", - v: "ᴠ", - w: "ᴡ", - x: "x", - y: "ʏ", - z: "ᴢ" -}); - -// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in -// src/symbols.js. - -const numberRegEx = /^\d(?:[\d,.]*\d)?$/; -const latinRegEx = /[A-Ba-z]/; -const primes = new Set(["\\prime", "\\dprime", "\\trprime", "\\qprime", - "\\backprime", "\\backdprime", "\\backtrprime"]); - -const italicNumber = (text, variant, tag) => { - const mn = new MathNode(tag, [text]); - const wrapper = new MathNode("mstyle", [mn]); - wrapper.style["font-style"] = "italic"; - wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif"; - if (variant === "bold-italic") { wrapper.style["font-weight"] = "bold"; } - return wrapper -}; - -defineFunctionBuilders({ - type: "mathord", - mathmlBuilder(group, style) { - const text = makeText(group.text, group.mode, style); - const codePoint = text.text.codePointAt(0); - // Test for upper-case Greek - const defaultVariant = (0x0390 < codePoint && codePoint < 0x03aa) ? "normal" : "italic"; - const variant = getVariant(group, style) || defaultVariant; - if (variant === "script") { - text.text = variantChar(text.text, variant); - return new MathNode("mi", [text], [style.font]) - } else if (variant !== "italic") { - text.text = variantChar(text.text, variant); - } - let node = new MathNode("mi", [text]); - // TODO: Handle U+1D49C - U+1D4CF per https://www.unicode.org/charts/PDF/U1D400.pdf - if (variant === "normal") { - node.setAttribute("mathvariant", "normal"); - if (text.text.length === 1) { - // A Firefox bug will apply spacing here, but there should be none. Fix it. - const mspace = new MathNode("mspace", []); - node = new MathNode("mrow", [node, mspace]); - } - } - return node - } -}); - -defineFunctionBuilders({ - type: "textord", - mathmlBuilder(group, style) { - let ch = group.text; - const codePoint = ch.codePointAt(0); - if (style.fontFamily === "textsc") { - // Convert small latin letters to small caps. - if (96 < codePoint && codePoint < 123) { - ch = smallCaps[ch]; - } - } - const text = makeText(ch, group.mode, style); - const variant = getVariant(group, style) || "normal"; - - let node; - if (numberRegEx.test(group.text)) { - const tag = group.mode === "text" ? "mtext" : "mn"; - if (variant === "italic" || variant === "bold-italic") { - return italicNumber(text, variant, tag) - } else { - if (variant !== "normal") { - text.text = text.text.split("").map(c => variantChar(c, variant)).join(""); - } - node = new MathNode(tag, [text]); - } - } else if (group.mode === "text") { - if (variant !== "normal") { - text.text = variantChar(text.text, variant); - } - node = new MathNode("mtext", [text]); - } else if (primes.has(group.text)) { - node = new MathNode("mo", [text]); - // TODO: If/when Chromium uses ssty variant for prime, remove the next line. - node.classes.push("tml-prime"); - } else { - const origText = text.text; - if (variant !== "italic") { - text.text = variantChar(text.text, variant); - } - node = new MathNode("mi", [text]); - if (text.text === origText && latinRegEx.test(origText)) { - node.setAttribute("mathvariant", "italic"); - } - } - return node - } -}); - -// A map of CSS-based spacing functions to their CSS class. -const cssSpace = { - "\\nobreak": "nobreak", - "\\allowbreak": "allowbreak" -}; - -// A lookup table to determine whether a spacing function/symbol should be -// treated like a regular space character. If a symbol or command is a key -// in this table, then it should be a regular space character. Furthermore, -// the associated value may have a `className` specifying an extra CSS class -// to add to the created `span`. -const regularSpace = { - " ": {}, - "\\ ": {}, - "~": { - className: "nobreak" - }, - "\\space": {}, - "\\nobreakspace": { - className: "nobreak" - } -}; - -// ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in -// src/symbols.js. -defineFunctionBuilders({ - type: "spacing", - mathmlBuilder(group, style) { - let node; - - if (Object.prototype.hasOwnProperty.call(regularSpace, group.text)) { - // Firefox does not render a space in a . So write a no-break space. - // TODO: If Firefox fixes that bug, uncomment the next line and write ch into the node. - //const ch = (regularSpace[group.text].className === "nobreak") ? "\u00a0" : " " - node = new MathNode("mtext", [new TextNode("\u00a0")]); - } else if (Object.prototype.hasOwnProperty.call(cssSpace, group.text)) { - // MathML 3.0 calls for nobreak to occur in an , not an - // Ref: https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs - node = new MathNode("mo"); - if (group.text === "\\nobreak") { - node.setAttribute("linebreak", "nobreak"); - } - } else { - throw new ParseError(`Unknown type of space "${group.text}"`) - } - - return node - } -}); - -defineFunctionBuilders({ - type: "tag" -}); - -// For a \tag, the work usually done in a mathmlBuilder is instead done in buildMathML.js. -// That way, a \tag can be pulled out of the parse tree and wrapped around the outer node. - -// Non-mathy text, possibly in a font -const textFontFamilies = { - "\\text": undefined, - "\\textrm": "textrm", - "\\textsf": "textsf", - "\\texttt": "texttt", - "\\textnormal": "textrm", - "\\textsc": "textsc" // small caps -}; - -const textFontWeights = { - "\\textbf": "textbf", - "\\textmd": "textmd" -}; - -const textFontShapes = { - "\\textit": "textit", - "\\textup": "textup" -}; - -const styleWithFont = (group, style) => { - const font = group.font; - // Checks if the argument is a font family or a font style. - if (!font) { - return style; - } else if (textFontFamilies[font]) { - return style.withTextFontFamily(textFontFamilies[font]); - } else if (textFontWeights[font]) { - return style.withTextFontWeight(textFontWeights[font]); - } else if (font === "\\emph") { - return style.fontShape === "textit" - ? style.withTextFontShape("textup") - : style.withTextFontShape("textit") - } - return style.withTextFontShape(textFontShapes[font]) -}; - -defineFunction({ - type: "text", - names: [ - // Font families - "\\text", - "\\textrm", - "\\textsf", - "\\texttt", - "\\textnormal", - "\\textsc", - // Font weights - "\\textbf", - "\\textmd", - // Font Shapes - "\\textit", - "\\textup", - "\\emph" - ], - props: { - numArgs: 1, - argTypes: ["text"], - allowedInArgument: true, - allowedInText: true - }, - handler({ parser, funcName }, args) { - const body = args[0]; - return { - type: "text", - mode: parser.mode, - body: ordargument(body), - font: funcName - }; - }, - mathmlBuilder(group, style) { - const newStyle = styleWithFont(group, style); - const mrow = buildExpressionRow(group.body, newStyle); - return consolidateText(mrow) - } -}); - -// \vcenter: Vertically center the argument group on the math axis. - -defineFunction({ - type: "vcenter", - names: ["\\vcenter"], - props: { - numArgs: 1, - argTypes: ["original"], - allowedInText: false - }, - handler({ parser }, args) { - return { - type: "vcenter", - mode: parser.mode, - body: args[0] - }; - }, - mathmlBuilder(group, style) { - // Use a math table to create vertically centered content. - const mtd = new MathNode("mtd", [buildGroup$1(group.body, style)]); - mtd.style.padding = "0"; - const mtr = new MathNode("mtr", [mtd]); - return new MathNode("mtable", [mtr]) - } -}); - -defineFunction({ - type: "verb", - names: ["\\verb"], - props: { - numArgs: 0, - allowedInText: true - }, - handler(context, args, optArgs) { - // \verb and \verb* are dealt with directly in Parser.js. - // If we end up here, it's because of a failure to match the two delimiters - // in the regex in Lexer.js. LaTeX raises the following error when \verb is - // terminated by end of line (or file). - throw new ParseError("\\verb ended by end of line instead of matching delimiter"); - }, - mathmlBuilder(group, style) { - const text = new TextNode(makeVerb(group)); - const node = new MathNode("mtext", [text]); - node.setAttribute("mathvariant", "monospace"); - return node; - } -}); - -/** - * Converts verb group into body string. - * - * \verb* replaces each space with an open box \u2423 - * \verb replaces each space with a no-break space \xA0 - */ -const makeVerb = (group) => group.body.replace(/ /g, group.star ? "\u2423" : "\xA0"); - -/** Include this to ensure that all functions are defined. */ - -const functions = _functions; - -/** - * The Lexer class handles tokenizing the input in various ways. Since our - * parser expects us to be able to backtrack, the lexer allows lexing from any - * given starting point. - * - * Its main exposed function is the `lex` function, which takes a position to - * lex from and a type of token to lex. It defers to the appropriate `_innerLex` - * function. - * - * The various `_innerLex` functions perform the actual lexing of different - * kinds. - */ - - -/* The following tokenRegex - * - matches typical whitespace (but not NBSP etc.) using its first two groups - * - does not match any control character \x00-\x1f except whitespace - * - does not match a bare backslash - * - matches any ASCII character except those just mentioned - * - does not match the BMP private use area \uE000-\uF8FF - * - does not match bare surrogate code units - * - matches any BMP character except for those just described - * - matches any valid Unicode surrogate pair - * - mathches numerals - * - matches a backslash followed by one or more whitespace characters - * - matches a backslash followed by one or more letters then whitespace - * - matches a backslash followed by any BMP character - * Capturing groups: - * [1] regular whitespace - * [2] backslash followed by whitespace - * [3] anything else, which may include: - * [4] left character of \verb* - * [5] left character of \verb - * [6] backslash followed by word, excluding any trailing whitespace - * Just because the Lexer matches something doesn't mean it's valid input: - * If there is no matching function or symbol definition, the Parser will - * still reject the input. - */ -const spaceRegexString = "[ \r\n\t]"; -const controlWordRegexString = "\\\\[a-zA-Z@]+"; -const controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]"; -const controlWordWhitespaceRegexString = `(${controlWordRegexString})${spaceRegexString}*`; -const controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*"; -const combiningDiacriticalMarkString = "[\u0300-\u036f]"; -const combiningDiacriticalMarksEndRegex = new RegExp(`${combiningDiacriticalMarkString}+$`); -const tokenRegexString = - `(${spaceRegexString}+)|` + // whitespace - `${controlSpaceRegexString}|` + // whitespace - "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint - `${combiningDiacriticalMarkString}*` + // ...plus accents - "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair - `${combiningDiacriticalMarkString}*` + // ...plus accents - "|\\\\verb\\*([^]).*?\\4" + // \verb* - "|\\\\verb([^*a-zA-Z]).*?\\5" + // \verb unstarred - `|${controlWordWhitespaceRegexString}` + // \macroName + spaces - `|${controlSymbolRegexString})`; // \\, \', etc. - -/** Main Lexer class */ -class Lexer { - constructor(input, settings) { - // Separate accents from characters - this.input = input; - this.settings = settings; - this.tokenRegex = new RegExp(tokenRegexString, 'g'); - // Category codes. The lexer only supports comment characters (14) for now. - // MacroExpander additionally distinguishes active (13). - this.catcodes = { - "%": 14, // comment character - "~": 13 // active character - }; - } - - setCatcode(char, code) { - this.catcodes[char] = code; - } - - /** - * This function lexes a single token. - */ - lex() { - const input = this.input; - const pos = this.tokenRegex.lastIndex; - if (pos === input.length) { - return new Token("EOF", new SourceLocation(this, pos, pos)); - } - const match = this.tokenRegex.exec(input); - if (match === null || match.index !== pos) { - throw new ParseError( - `Unexpected character: '${input[pos]}'`, - new Token(input[pos], new SourceLocation(this, pos, pos + 1)) - ); - } - const text = match[6] || match[3] || (match[2] ? "\\ " : " "); - - if (this.catcodes[text] === 14) { - // comment character - const nlIndex = input.indexOf("\n", this.tokenRegex.lastIndex); - if (nlIndex === -1) { - this.tokenRegex.lastIndex = input.length; // EOF - if (this.settings.strict) { - throw new ParseError("% comment has no terminating newline; LaTeX would " + - "fail because of commenting the end of math mode") - } - } else { - this.tokenRegex.lastIndex = nlIndex + 1; - } - return this.lex(); - } - - return new Token(text, new SourceLocation(this, pos, this.tokenRegex.lastIndex)); - } -} - -/** - * A `Namespace` refers to a space of nameable things like macros or lengths, - * which can be `set` either globally or local to a nested group, using an - * undo stack similar to how TeX implements this functionality. - * Performance-wise, `get` and local `set` take constant time, while global - * `set` takes time proportional to the depth of group nesting. - */ - - -class Namespace { - /** - * Both arguments are optional. The first argument is an object of - * built-in mappings which never change. The second argument is an object - * of initial (global-level) mappings, which will constantly change - * according to any global/top-level `set`s done. - */ - constructor(builtins = {}, globalMacros = {}) { - this.current = globalMacros; - this.builtins = builtins; - this.undefStack = []; - } - - /** - * Start a new nested group, affecting future local `set`s. - */ - beginGroup() { - this.undefStack.push({}); - } - - /** - * End current nested group, restoring values before the group began. - */ - endGroup() { - if (this.undefStack.length === 0) { - throw new ParseError( - "Unbalanced namespace destruction: attempt " + - "to pop global namespace; please report this as a bug" - ); - } - const undefs = this.undefStack.pop(); - for (const undef in undefs) { - if (Object.prototype.hasOwnProperty.call(undefs, undef )) { - if (undefs[undef] === undefined) { - delete this.current[undef]; - } else { - this.current[undef] = undefs[undef]; - } - } - } - } - - /** - * Detect whether `name` has a definition. Equivalent to - * `get(name) != null`. - */ - has(name) { - return Object.prototype.hasOwnProperty.call(this.current, name ) || - Object.prototype.hasOwnProperty.call(this.builtins, name ); - } - - /** - * Get the current value of a name, or `undefined` if there is no value. - * - * Note: Do not use `if (namespace.get(...))` to detect whether a macro - * is defined, as the definition may be the empty string which evaluates - * to `false` in JavaScript. Use `if (namespace.get(...) != null)` or - * `if (namespace.has(...))`. - */ - get(name) { - if (Object.prototype.hasOwnProperty.call(this.current, name )) { - return this.current[name]; - } else { - return this.builtins[name]; - } - } - - /** - * Set the current value of a name, and optionally set it globally too. - * Local set() sets the current value and (when appropriate) adds an undo - * operation to the undo stack. Global set() may change the undo - * operation at every level, so takes time linear in their number. - */ - set(name, value, global = false) { - if (global) { - // Global set is equivalent to setting in all groups. Simulate this - // by destroying any undos currently scheduled for this name, - // and adding an undo with the *new* value (in case it later gets - // locally reset within this environment). - for (let i = 0; i < this.undefStack.length; i++) { - delete this.undefStack[i][name]; - } - if (this.undefStack.length > 0) { - this.undefStack[this.undefStack.length - 1][name] = value; - } - } else { - // Undo this set at end of this group (possibly to `undefined`), - // unless an undo is already in place, in which case that older - // value is the correct one. - const top = this.undefStack[this.undefStack.length - 1]; - if (top && !Object.prototype.hasOwnProperty.call(top, name )) { - top[name] = this.current[name]; - } - } - this.current[name] = value; - } -} - -/** - * This file contains the “gullet” where macros are expanded - * until only non-macro tokens remain. - */ - - -// List of commands that act like macros but aren't defined as a macro, -// function, or symbol. Used in `isDefined`. -const implicitCommands = { - "^": true, // Parser.js - _: true, // Parser.js - "\\limits": true, // Parser.js - "\\nolimits": true // Parser.js -}; - -class MacroExpander { - constructor(input, settings, mode) { - this.settings = settings; - this.expansionCount = 0; - this.feed(input); - // Make new global namespace - this.macros = new Namespace(macros, settings.macros); - this.mode = mode; - this.stack = []; // contains tokens in REVERSE order - } - - /** - * Feed a new input string to the same MacroExpander - * (with existing macros etc.). - */ - feed(input) { - this.lexer = new Lexer(input, this.settings); - } - - /** - * Switches between "text" and "math" modes. - */ - switchMode(newMode) { - this.mode = newMode; - } - - /** - * Start a new group nesting within all namespaces. - */ - beginGroup() { - this.macros.beginGroup(); - } - - /** - * End current group nesting within all namespaces. - */ - endGroup() { - this.macros.endGroup(); - } - - /** - * Returns the topmost token on the stack, without expanding it. - * Similar in behavior to TeX's `\futurelet`. - */ - future() { - if (this.stack.length === 0) { - this.pushToken(this.lexer.lex()); - } - return this.stack[this.stack.length - 1] - } - - /** - * Remove and return the next unexpanded token. - */ - popToken() { - this.future(); // ensure non-empty stack - return this.stack.pop(); - } - - /** - * Add a given token to the token stack. In particular, this get be used - * to put back a token returned from one of the other methods. - */ - pushToken(token) { - this.stack.push(token); - } - - /** - * Append an array of tokens to the token stack. - */ - pushTokens(tokens) { - this.stack.push(...tokens); - } - - /** - * Find an macro argument without expanding tokens and append the array of - * tokens to the token stack. Uses Token as a container for the result. - */ - scanArgument(isOptional) { - let start; - let end; - let tokens; - if (isOptional) { - this.consumeSpaces(); // \@ifnextchar gobbles any space following it - if (this.future().text !== "[") { - return null; - } - start = this.popToken(); // don't include [ in tokens - ({ tokens, end } = this.consumeArg(["]"])); - } else { - ({ tokens, start, end } = this.consumeArg()); - } - - // indicate the end of an argument - this.pushToken(new Token("EOF", end.loc)); - - this.pushTokens(tokens); - return start.range(end, ""); - } - - /** - * Consume all following space tokens, without expansion. - */ - consumeSpaces() { - for (;;) { - const token = this.future(); - if (token.text === " ") { - this.stack.pop(); - } else { - break; - } - } - } - - /** - * Consume an argument from the token stream, and return the resulting array - * of tokens and start/end token. - */ - consumeArg(delims) { - // The argument for a delimited parameter is the shortest (possibly - // empty) sequence of tokens with properly nested {...} groups that is - // followed ... by this particular list of non-parameter tokens. - // The argument for an undelimited parameter is the next nonblank - // token, unless that token is ‘{’, when the argument will be the - // entire {...} group that follows. - const tokens = []; - const isDelimited = delims && delims.length > 0; - if (!isDelimited) { - // Ignore spaces between arguments. As the TeXbook says: - // "After you have said ‘\def\row#1#2{...}’, you are allowed to - // put spaces between the arguments (e.g., ‘\row x n’), because - // TeX doesn’t use single spaces as undelimited arguments." - this.consumeSpaces(); - } - const start = this.future(); - let tok; - let depth = 0; - let match = 0; - do { - tok = this.popToken(); - tokens.push(tok); - if (tok.text === "{") { - ++depth; - } else if (tok.text === "}") { - --depth; - if (depth === -1) { - throw new ParseError("Extra }", tok); - } - } else if (tok.text === "EOF") { - throw new ParseError( - "Unexpected end of input in a macro argument" + - ", expected '" + - (delims && isDelimited ? delims[match] : "}") + - "'", - tok - ); - } - if (delims && isDelimited) { - if ((depth === 0 || (depth === 1 && delims[match] === "{")) && tok.text === delims[match]) { - ++match; - if (match === delims.length) { - // don't include delims in tokens - tokens.splice(-match, match); - break; - } - } else { - match = 0; - } - } - } while (depth !== 0 || isDelimited); - // If the argument found ... has the form ‘{}’, - // ... the outermost braces enclosing the argument are removed - if (start.text === "{" && tokens[tokens.length - 1].text === "}") { - tokens.pop(); - tokens.shift(); - } - tokens.reverse(); // to fit in with stack order - return { tokens, start, end: tok }; - } - - /** - * Consume the specified number of (delimited) arguments from the token - * stream and return the resulting array of arguments. - */ - consumeArgs(numArgs, delimiters) { - if (delimiters) { - if (delimiters.length !== numArgs + 1) { - throw new ParseError("The length of delimiters doesn't match the number of args!"); - } - const delims = delimiters[0]; - for (let i = 0; i < delims.length; i++) { - const tok = this.popToken(); - if (delims[i] !== tok.text) { - throw new ParseError("Use of the macro doesn't match its definition", tok); - } - } - } - - const args = []; - for (let i = 0; i < numArgs; i++) { - args.push(this.consumeArg(delimiters && delimiters[i + 1]).tokens); - } - return args; - } - - /** - * Expand the next token only once if possible. - * - * If the token is expanded, the resulting tokens will be pushed onto - * the stack in reverse order, and the number of such tokens will be - * returned. This number might be zero or positive. - * - * If not, the return value is `false`, and the next token remains at the - * top of the stack. - * - * In either case, the next token will be on the top of the stack, - * or the stack will be empty (in case of empty expansion - * and no other tokens). - * - * Used to implement `expandAfterFuture` and `expandNextToken`. - * - * If expandableOnly, only expandable tokens are expanded and - * an undefined control sequence results in an error. - */ - expandOnce(expandableOnly) { - const topToken = this.popToken(); - const name = topToken.text; - const expansion = !topToken.noexpand ? this._getExpansion(name) : null; - if (expansion == null || (expandableOnly && expansion.unexpandable)) { - if (expandableOnly && expansion == null && name[0] === "\\" && !this.isDefined(name)) { - throw new ParseError("Undefined control sequence: " + name); - } - this.pushToken(topToken); - return false; - } - this.expansionCount++; - if (this.expansionCount > this.settings.maxExpand) { - throw new ParseError( - "Too many expansions: infinite loop or " + "need to increase maxExpand setting" - ); - } - let tokens = expansion.tokens; - const args = this.consumeArgs(expansion.numArgs, expansion.delimiters); - if (expansion.numArgs) { - // paste arguments in place of the placeholders - tokens = tokens.slice(); // make a shallow copy - for (let i = tokens.length - 1; i >= 0; --i) { - let tok = tokens[i]; - if (tok.text === "#") { - if (i === 0) { - throw new ParseError("Incomplete placeholder at end of macro body", tok); - } - tok = tokens[--i]; // next token on stack - if (tok.text === "#") { - // ## → # - tokens.splice(i + 1, 1); // drop first # - } else if (/^[1-9]$/.test(tok.text)) { - // replace the placeholder with the indicated argument - tokens.splice(i, 2, ...args[+tok.text - 1]); - } else { - throw new ParseError("Not a valid argument number", tok); - } - } - } - } - // Concatenate expansion onto top of stack. - this.pushTokens(tokens); - return tokens.length; - } - - /** - * Expand the next token only once (if possible), and return the resulting - * top token on the stack (without removing anything from the stack). - * Similar in behavior to TeX's `\expandafter\futurelet`. - * Equivalent to expandOnce() followed by future(). - */ - expandAfterFuture() { - this.expandOnce(); - return this.future(); - } - - /** - * Recursively expand first token, then return first non-expandable token. - */ - expandNextToken() { - for (;;) { - if (this.expandOnce() === false) { // fully expanded - const token = this.stack.pop(); - // The token after \noexpand is interpreted as if its meaning were ‘\relax’ - if (token.treatAsRelax) { - token.text = "\\relax"; - } - return token - } - } - - // This pathway is impossible. - throw new Error(); // eslint-disable-line no-unreachable - } - - /** - * Fully expand the given macro name and return the resulting list of - * tokens, or return `undefined` if no such macro is defined. - */ - expandMacro(name) { - return this.macros.has(name) ? this.expandTokens([new Token(name)]) : undefined; - } - - /** - * Fully expand the given token stream and return the resulting list of - * tokens. Note that the input tokens are in reverse order, but the - * output tokens are in forward order. - */ - expandTokens(tokens) { - const output = []; - const oldStackLength = this.stack.length; - this.pushTokens(tokens); - while (this.stack.length > oldStackLength) { - // Expand only expandable tokens - if (this.expandOnce(true) === false) { // fully expanded - const token = this.stack.pop(); - if (token.treatAsRelax) { - // the expansion of \noexpand is the token itself - token.noexpand = false; - token.treatAsRelax = false; - } - output.push(token); - } - } - return output; - } - - /** - * Fully expand the given macro name and return the result as a string, - * or return `undefined` if no such macro is defined. - */ - expandMacroAsText(name) { - const tokens = this.expandMacro(name); - if (tokens) { - return tokens.map((token) => token.text).join(""); - } else { - return tokens; - } - } - - /** - * Returns the expanded macro as a reversed array of tokens and a macro - * argument count. Or returns `null` if no such macro. - */ - _getExpansion(name) { - const definition = this.macros.get(name); - if (definition == null) { - // mainly checking for undefined here - return definition; - } - // If a single character has an associated catcode other than 13 - // (active character), then don't expand it. - if (name.length === 1) { - const catcode = this.lexer.catcodes[name]; - if (catcode != null && catcode !== 13) { - return - } - } - const expansion = typeof definition === "function" ? definition(this) : definition; - if (typeof expansion === "string") { - let numArgs = 0; - if (expansion.indexOf("#") !== -1) { - const stripped = expansion.replace(/##/g, ""); - while (stripped.indexOf("#" + (numArgs + 1)) !== -1) { - ++numArgs; - } - } - const bodyLexer = new Lexer(expansion, this.settings); - const tokens = []; - let tok = bodyLexer.lex(); - while (tok.text !== "EOF") { - tokens.push(tok); - tok = bodyLexer.lex(); - } - tokens.reverse(); // to fit in with stack using push and pop - const expanded = { tokens, numArgs }; - return expanded; - } - - return expansion; - } - - /** - * Determine whether a command is currently "defined" (has some - * functionality), meaning that it's a macro (in the current group), - * a function, a symbol, or one of the special commands listed in - * `implicitCommands`. - */ - isDefined(name) { - return ( - this.macros.has(name) || - Object.prototype.hasOwnProperty.call(functions, name ) || - Object.prototype.hasOwnProperty.call(symbols.math, name ) || - Object.prototype.hasOwnProperty.call(symbols.text, name ) || - Object.prototype.hasOwnProperty.call(implicitCommands, name ) - ); - } - - /** - * Determine whether a command is expandable. - */ - isExpandable(name) { - const macro = this.macros.get(name); - return macro != null - ? typeof macro === "string" || typeof macro === "function" || !macro.unexpandable - : Object.prototype.hasOwnProperty.call(functions, name ) && !functions[name].primitive; - } -} - -// Helpers for Parser.js handling of Unicode (sub|super)script characters. - -const unicodeSubRegEx = /^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/; - -const uSubsAndSups = Object.freeze({ - '₊': '+', - '₋': '-', - '₌': '=', - '₍': '(', - '₎': ')', - '₀': '0', - '₁': '1', - '₂': '2', - '₃': '3', - '₄': '4', - '₅': '5', - '₆': '6', - '₇': '7', - '₈': '8', - '₉': '9', - '\u2090': 'a', - '\u2091': 'e', - '\u2095': 'h', - '\u1D62': 'i', - '\u2C7C': 'j', - '\u2096': 'k', - '\u2097': 'l', - '\u2098': 'm', - '\u2099': 'n', - '\u2092': 'o', - '\u209A': 'p', - '\u1D63': 'r', - '\u209B': 's', - '\u209C': 't', - '\u1D64': 'u', - '\u1D65': 'v', - '\u2093': 'x', - '\u1D66': 'β', - '\u1D67': 'γ', - '\u1D68': 'ρ', - '\u1D69': '\u03d5', - '\u1D6A': 'χ', - '⁺': '+', - '⁻': '-', - '⁼': '=', - '⁽': '(', - '⁾': ')', - '⁰': '0', - '¹': '1', - '²': '2', - '³': '3', - '⁴': '4', - '⁵': '5', - '⁶': '6', - '⁷': '7', - '⁸': '8', - '⁹': '9', - '\u1D2C': 'A', - '\u1D2E': 'B', - '\u1D30': 'D', - '\u1D31': 'E', - '\u1D33': 'G', - '\u1D34': 'H', - '\u1D35': 'I', - '\u1D36': 'J', - '\u1D37': 'K', - '\u1D38': 'L', - '\u1D39': 'M', - '\u1D3A': 'N', - '\u1D3C': 'O', - '\u1D3E': 'P', - '\u1D3F': 'R', - '\u1D40': 'T', - '\u1D41': 'U', - '\u2C7D': 'V', - '\u1D42': 'W', - '\u1D43': 'a', - '\u1D47': 'b', - '\u1D9C': 'c', - '\u1D48': 'd', - '\u1D49': 'e', - '\u1DA0': 'f', - '\u1D4D': 'g', - '\u02B0': 'h', - '\u2071': 'i', - '\u02B2': 'j', - '\u1D4F': 'k', - '\u02E1': 'l', - '\u1D50': 'm', - '\u207F': 'n', - '\u1D52': 'o', - '\u1D56': 'p', - '\u02B3': 'r', - '\u02E2': 's', - '\u1D57': 't', - '\u1D58': 'u', - '\u1D5B': 'v', - '\u02B7': 'w', - '\u02E3': 'x', - '\u02B8': 'y', - '\u1DBB': 'z', - '\u1D5D': 'β', - '\u1D5E': 'γ', - '\u1D5F': 'δ', - '\u1D60': '\u03d5', - '\u1D61': 'χ', - '\u1DBF': 'θ' -}); - -// Used for Unicode input of calligraphic and script letters -const asciiFromScript = Object.freeze({ - "\ud835\udc9c": "A", - "\u212c": "B", - "\ud835\udc9e": "C", - "\ud835\udc9f": "D", - "\u2130": "E", - "\u2131": "F", - "\ud835\udca2": "G", - "\u210B": "H", - "\u2110": "I", - "\ud835\udca5": "J", - "\ud835\udca6": "K", - "\u2112": "L", - "\u2133": "M", - "\ud835\udca9": "N", - "\ud835\udcaa": "O", - "\ud835\udcab": "P", - "\ud835\udcac": "Q", - "\u211B": "R", - "\ud835\udcae": "S", - "\ud835\udcaf": "T", - "\ud835\udcb0": "U", - "\ud835\udcb1": "V", - "\ud835\udcb2": "W", - "\ud835\udcb3": "X", - "\ud835\udcb4": "Y", - "\ud835\udcb5": "Z" -}); - -// Mapping of Unicode accent characters to their LaTeX equivalent in text and -// math mode (when they exist). -var unicodeAccents = { - "\u0301": { text: "\\'", math: "\\acute" }, - "\u0300": { text: "\\`", math: "\\grave" }, - "\u0308": { text: '\\"', math: "\\ddot" }, - "\u0303": { text: "\\~", math: "\\tilde" }, - "\u0304": { text: "\\=", math: "\\bar" }, - "\u0306": { text: "\\u", math: "\\breve" }, - "\u030c": { text: "\\v", math: "\\check" }, - "\u0302": { text: "\\^", math: "\\hat" }, - "\u0307": { text: "\\.", math: "\\dot" }, - "\u030a": { text: "\\r", math: "\\mathring" }, - "\u030b": { text: "\\H" }, - '\u0327': { text: '\\c' } -}; - -var unicodeSymbols = { - "á": "á", - "à": "à", - "ä": "ä", - "ǟ": "ǟ", - "ã": "ã", - "ā": "ā", - "ă": "ă", - "ắ": "ắ", - "ằ": "ằ", - "ẵ": "ẵ", - "ǎ": "ǎ", - "â": "â", - "ấ": "ấ", - "ầ": "ầ", - "ẫ": "ẫ", - "ȧ": "ȧ", - "ǡ": "ǡ", - "å": "å", - "ǻ": "ǻ", - "ḃ": "ḃ", - "ć": "ć", - "č": "č", - "ĉ": "ĉ", - "ċ": "ċ", - "ď": "ď", - "ḋ": "ḋ", - "é": "é", - "è": "è", - "ë": "ë", - "ẽ": "ẽ", - "ē": "ē", - "ḗ": "ḗ", - "ḕ": "ḕ", - "ĕ": "ĕ", - "ě": "ě", - "ê": "ê", - "ế": "ế", - "ề": "ề", - "ễ": "ễ", - "ė": "ė", - "ḟ": "ḟ", - "ǵ": "ǵ", - "ḡ": "ḡ", - "ğ": "ğ", - "ǧ": "ǧ", - "ĝ": "ĝ", - "ġ": "ġ", - "ḧ": "ḧ", - "ȟ": "ȟ", - "ĥ": "ĥ", - "ḣ": "ḣ", - "í": "í", - "ì": "ì", - "ï": "ï", - "ḯ": "ḯ", - "ĩ": "ĩ", - "ī": "ī", - "ĭ": "ĭ", - "ǐ": "ǐ", - "î": "î", - "ǰ": "ǰ", - "ĵ": "ĵ", - "ḱ": "ḱ", - "ǩ": "ǩ", - "ĺ": "ĺ", - "ľ": "ľ", - "ḿ": "ḿ", - "ṁ": "ṁ", - "ń": "ń", - "ǹ": "ǹ", - "ñ": "ñ", - "ň": "ň", - "ṅ": "ṅ", - "ó": "ó", - "ò": "ò", - "ö": "ö", - "ȫ": "ȫ", - "õ": "õ", - "ṍ": "ṍ", - "ṏ": "ṏ", - "ȭ": "ȭ", - "ō": "ō", - "ṓ": "ṓ", - "ṑ": "ṑ", - "ŏ": "ŏ", - "ǒ": "ǒ", - "ô": "ô", - "ố": "ố", - "ồ": "ồ", - "ỗ": "ỗ", - "ȯ": "ȯ", - "ȱ": "ȱ", - "ő": "ő", - "ṕ": "ṕ", - "ṗ": "ṗ", - "ŕ": "ŕ", - "ř": "ř", - "ṙ": "ṙ", - "ś": "ś", - "ṥ": "ṥ", - "š": "š", - "ṧ": "ṧ", - "ŝ": "ŝ", - "ṡ": "ṡ", - "ẗ": "ẗ", - "ť": "ť", - "ṫ": "ṫ", - "ú": "ú", - "ù": "ù", - "ü": "ü", - "ǘ": "ǘ", - "ǜ": "ǜ", - "ǖ": "ǖ", - "ǚ": "ǚ", - "ũ": "ũ", - "ṹ": "ṹ", - "ū": "ū", - "ṻ": "ṻ", - "ŭ": "ŭ", - "ǔ": "ǔ", - "û": "û", - "ů": "ů", - "ű": "ű", - "ṽ": "ṽ", - "ẃ": "ẃ", - "ẁ": "ẁ", - "ẅ": "ẅ", - "ŵ": "ŵ", - "ẇ": "ẇ", - "ẘ": "ẘ", - "ẍ": "ẍ", - "ẋ": "ẋ", - "ý": "ý", - "ỳ": "ỳ", - "ÿ": "ÿ", - "ỹ": "ỹ", - "ȳ": "ȳ", - "ŷ": "ŷ", - "ẏ": "ẏ", - "ẙ": "ẙ", - "ź": "ź", - "ž": "ž", - "ẑ": "ẑ", - "ż": "ż", - "Á": "Á", - "À": "À", - "Ä": "Ä", - "Ǟ": "Ǟ", - "Ã": "Ã", - "Ā": "Ā", - "Ă": "Ă", - "Ắ": "Ắ", - "Ằ": "Ằ", - "Ẵ": "Ẵ", - "Ǎ": "Ǎ", - "Â": "Â", - "Ấ": "Ấ", - "Ầ": "Ầ", - "Ẫ": "Ẫ", - "Ȧ": "Ȧ", - "Ǡ": "Ǡ", - "Å": "Å", - "Ǻ": "Ǻ", - "Ḃ": "Ḃ", - "Ć": "Ć", - "Č": "Č", - "Ĉ": "Ĉ", - "Ċ": "Ċ", - "Ď": "Ď", - "Ḋ": "Ḋ", - "É": "É", - "È": "È", - "Ë": "Ë", - "Ẽ": "Ẽ", - "Ē": "Ē", - "Ḗ": "Ḗ", - "Ḕ": "Ḕ", - "Ĕ": "Ĕ", - "Ě": "Ě", - "Ê": "Ê", - "Ế": "Ế", - "Ề": "Ề", - "Ễ": "Ễ", - "Ė": "Ė", - "Ḟ": "Ḟ", - "Ǵ": "Ǵ", - "Ḡ": "Ḡ", - "Ğ": "Ğ", - "Ǧ": "Ǧ", - "Ĝ": "Ĝ", - "Ġ": "Ġ", - "Ḧ": "Ḧ", - "Ȟ": "Ȟ", - "Ĥ": "Ĥ", - "Ḣ": "Ḣ", - "Í": "Í", - "Ì": "Ì", - "Ï": "Ï", - "Ḯ": "Ḯ", - "Ĩ": "Ĩ", - "Ī": "Ī", - "Ĭ": "Ĭ", - "Ǐ": "Ǐ", - "Î": "Î", - "İ": "İ", - "Ĵ": "Ĵ", - "Ḱ": "Ḱ", - "Ǩ": "Ǩ", - "Ĺ": "Ĺ", - "Ľ": "Ľ", - "Ḿ": "Ḿ", - "Ṁ": "Ṁ", - "Ń": "Ń", - "Ǹ": "Ǹ", - "Ñ": "Ñ", - "Ň": "Ň", - "Ṅ": "Ṅ", - "Ó": "Ó", - "Ò": "Ò", - "Ö": "Ö", - "Ȫ": "Ȫ", - "Õ": "Õ", - "Ṍ": "Ṍ", - "Ṏ": "Ṏ", - "Ȭ": "Ȭ", - "Ō": "Ō", - "Ṓ": "Ṓ", - "Ṑ": "Ṑ", - "Ŏ": "Ŏ", - "Ǒ": "Ǒ", - "Ô": "Ô", - "Ố": "Ố", - "Ồ": "Ồ", - "Ỗ": "Ỗ", - "Ȯ": "Ȯ", - "Ȱ": "Ȱ", - "Ő": "Ő", - "Ṕ": "Ṕ", - "Ṗ": "Ṗ", - "Ŕ": "Ŕ", - "Ř": "Ř", - "Ṙ": "Ṙ", - "Ś": "Ś", - "Ṥ": "Ṥ", - "Š": "Š", - "Ṧ": "Ṧ", - "Ŝ": "Ŝ", - "Ṡ": "Ṡ", - "Ť": "Ť", - "Ṫ": "Ṫ", - "Ú": "Ú", - "Ù": "Ù", - "Ü": "Ü", - "Ǘ": "Ǘ", - "Ǜ": "Ǜ", - "Ǖ": "Ǖ", - "Ǚ": "Ǚ", - "Ũ": "Ũ", - "Ṹ": "Ṹ", - "Ū": "Ū", - "Ṻ": "Ṻ", - "Ŭ": "Ŭ", - "Ǔ": "Ǔ", - "Û": "Û", - "Ů": "Ů", - "Ű": "Ű", - "Ṽ": "Ṽ", - "Ẃ": "Ẃ", - "Ẁ": "Ẁ", - "Ẅ": "Ẅ", - "Ŵ": "Ŵ", - "Ẇ": "Ẇ", - "Ẍ": "Ẍ", - "Ẋ": "Ẋ", - "Ý": "Ý", - "Ỳ": "Ỳ", - "Ÿ": "Ÿ", - "Ỹ": "Ỹ", - "Ȳ": "Ȳ", - "Ŷ": "Ŷ", - "Ẏ": "Ẏ", - "Ź": "Ź", - "Ž": "Ž", - "Ẑ": "Ẑ", - "Ż": "Ż", - "ά": "ά", - "ὰ": "ὰ", - "ᾱ": "ᾱ", - "ᾰ": "ᾰ", - "έ": "έ", - "ὲ": "ὲ", - "ή": "ή", - "ὴ": "ὴ", - "ί": "ί", - "ὶ": "ὶ", - "ϊ": "ϊ", - "ΐ": "ΐ", - "ῒ": "ῒ", - "ῑ": "ῑ", - "ῐ": "ῐ", - "ό": "ό", - "ὸ": "ὸ", - "ύ": "ύ", - "ὺ": "ὺ", - "ϋ": "ϋ", - "ΰ": "ΰ", - "ῢ": "ῢ", - "ῡ": "ῡ", - "ῠ": "ῠ", - "ώ": "ώ", - "ὼ": "ὼ", - "Ύ": "Ύ", - "Ὺ": "Ὺ", - "Ϋ": "Ϋ", - "Ῡ": "Ῡ", - "Ῠ": "Ῠ", - "Ώ": "Ώ", - "Ὼ": "Ὼ" -}; - -/* eslint no-constant-condition:0 */ - -const binLeftCancellers = ["bin", "op", "open", "punct", "rel"]; -const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; -const textRegEx = /^ *\\text/; - -/** - * This file contains the parser used to parse out a TeX expression from the - * input. Since TeX isn't context-free, standard parsers don't work particularly - * well. - * - * The strategy of this parser is as such: - * - * The main functions (the `.parse...` ones) take a position in the current - * parse string to parse tokens from. The lexer (found in Lexer.js, stored at - * this.gullet.lexer) also supports pulling out tokens at arbitrary places. When - * individual tokens are needed at a position, the lexer is called to pull out a - * token, which is then used. - * - * The parser has a property called "mode" indicating the mode that - * the parser is currently in. Currently it has to be one of "math" or - * "text", which denotes whether the current environment is a math-y - * one or a text-y one (e.g. inside \text). Currently, this serves to - * limit the functions which can be used in text mode. - * - * The main functions then return an object which contains the useful data that - * was parsed at its given point, and a new position at the end of the parsed - * data. The main functions can call each other and continue the parsing by - * using the returned position as a new starting point. - * - * There are also extra `.handle...` functions, which pull out some reused - * functionality into self-contained functions. - * - * The functions return ParseNodes. - */ - -class Parser { - constructor(input, settings, isPreamble = false) { - // Start in math mode - this.mode = "math"; - // Create a new macro expander (gullet) and (indirectly via that) also a - // new lexer (mouth) for this parser (stomach, in the language of TeX) - this.gullet = new MacroExpander(input, settings, this.mode); - // Store the settings for use in parsing - this.settings = settings; - // Are we defining a preamble? - this.isPreamble = isPreamble; - // Count leftright depth (for \middle errors) - this.leftrightDepth = 0; - this.prevAtomType = ""; - } - - /** - * Checks a result to make sure it has the right type, and throws an - * appropriate error otherwise. - */ - expect(text, consume = true) { - if (this.fetch().text !== text) { - throw new ParseError(`Expected '${text}', got '${this.fetch().text}'`, this.fetch()); - } - if (consume) { - this.consume(); - } - } - - /** - * Discards the current lookahead token, considering it consumed. - */ - consume() { - this.nextToken = null; - } - - /** - * Return the current lookahead token, or if there isn't one (at the - * beginning, or if the previous lookahead token was consume()d), - * fetch the next token as the new lookahead token and return it. - */ - fetch() { - if (this.nextToken == null) { - this.nextToken = this.gullet.expandNextToken(); - } - return this.nextToken; - } - - /** - * Switches between "text" and "math" modes. - */ - switchMode(newMode) { - this.mode = newMode; - this.gullet.switchMode(newMode); - } - - /** - * Main parsing function, which parses an entire input. - */ - parse() { - // Create a group namespace for every $...$, $$...$$, \[...\].) - // A \def is then valid only within that pair of delimiters. - this.gullet.beginGroup(); - - if (this.settings.colorIsTextColor) { - // Use old \color behavior (same as LaTeX's \textcolor) if requested. - // We do this within the group for the math expression, so it doesn't - // pollute settings.macros. - this.gullet.macros.set("\\color", "\\textcolor"); - } - - // Try to parse the input - const parse = this.parseExpression(false); - - // If we succeeded, make sure there's an EOF at the end - this.expect("EOF"); - - if (this.isPreamble) { - const macros = Object.create(null); - Object.entries(this.gullet.macros.current).forEach(([key, value]) => { - macros[key] = value; - }); - this.gullet.endGroup(); - return macros - } - - // The only local macro that we want to save is from \tag. - const tag = this.gullet.macros.get("\\df@tag"); - - // End the group namespace for the expression - this.gullet.endGroup(); - - if (tag) { this.gullet.macros.current["\\df@tag"] = tag; } - - return parse; - } - - static get endOfExpression() { - return ["}", "\\endgroup", "\\end", "\\right", "\\endtoggle", "&"]; - } - - /** - * Fully parse a separate sequence of tokens as a separate job. - * Tokens should be specified in reverse order, as in a MacroDefinition. - */ - subparse(tokens) { - // Save the next token from the current job. - const oldToken = this.nextToken; - this.consume(); - - // Run the new job, terminating it with an excess '}' - this.gullet.pushToken(new Token("}")); - this.gullet.pushTokens(tokens); - const parse = this.parseExpression(false); - this.expect("}"); - - // Restore the next token from the current job. - this.nextToken = oldToken; - - return parse; - } - -/** - * Parses an "expression", which is a list of atoms. - * - * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This - * happens when functions have higher precedence than infix - * nodes in implicit parses. - * - * `breakOnTokenText`: The text of the token that the expression should end - * with, or `null` if something else should end the - * expression. - * - * `breakOnMiddle`: \color, \over, and old styling functions work on an implicit group. - * These groups end just before the usual tokens, but they also - * end just before `\middle`. - */ - parseExpression(breakOnInfix, breakOnTokenText, breakOnMiddle) { - const body = []; - this.prevAtomType = ""; - // Keep adding atoms to the body until we can't parse any more atoms (either - // we reached the end, a }, or a \right) - while (true) { - // Ignore spaces in math mode - if (this.mode === "math") { - this.consumeSpaces(); - } - const lex = this.fetch(); - if (Parser.endOfExpression.indexOf(lex.text) !== -1) { - break; - } - if (breakOnTokenText && lex.text === breakOnTokenText) { - break; - } - if (breakOnMiddle && lex.text === "\\middle") { - break - } - if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) { - break; - } - const atom = this.parseAtom(breakOnTokenText); - if (!atom) { - break; - } else if (atom.type === "internal") { - // Internal nodes do not appear in parse tree - continue; - } - body.push(atom); - // Keep a record of the atom type, so that op.js can set correct spacing. - this.prevAtomType = atom.type === "atom" ? atom.family : atom.type; - } - if (this.mode === "text") { - this.formLigatures(body); - } - return this.handleInfixNodes(body); - } - - /** - * Rewrites infix operators such as \over with corresponding commands such - * as \frac. - * - * There can only be one infix operator per group. If there's more than one - * then the expression is ambiguous. This can be resolved by adding {}. - */ - handleInfixNodes(body) { - let overIndex = -1; - let funcName; - - for (let i = 0; i < body.length; i++) { - if (body[i].type === "infix") { - if (overIndex !== -1) { - throw new ParseError("only one infix operator per group", body[i].token); - } - overIndex = i; - funcName = body[i].replaceWith; - } - } - - if (overIndex !== -1 && funcName) { - let numerNode; - let denomNode; - - const numerBody = body.slice(0, overIndex); - const denomBody = body.slice(overIndex + 1); - - if (numerBody.length === 1 && numerBody[0].type === "ordgroup") { - numerNode = numerBody[0]; - } else { - numerNode = { type: "ordgroup", mode: this.mode, body: numerBody }; - } - - if (denomBody.length === 1 && denomBody[0].type === "ordgroup") { - denomNode = denomBody[0]; - } else { - denomNode = { type: "ordgroup", mode: this.mode, body: denomBody }; - } - - let node; - if (funcName === "\\\\abovefrac") { - node = this.callFunction(funcName, [numerNode, body[overIndex], denomNode], []); - } else { - node = this.callFunction(funcName, [numerNode, denomNode], []); - } - return [node]; - } else { - return body; - } - } - - /** - * Handle a subscript or superscript with nice errors. - */ - handleSupSubscript( - name // For error reporting. - ) { - const symbolToken = this.fetch(); - const symbol = symbolToken.text; - this.consume(); - this.consumeSpaces(); // ignore spaces before sup/subscript argument - // Skip over allowed internal nodes such as \relax - let group; - do { - group = this.parseGroup(name); - } while (group.type && group.type === "internal") - - if (!group) { - throw new ParseError("Expected group after '" + symbol + "'", symbolToken); - } - - return group; - } - - /** - * Converts the textual input of an unsupported command into a text node - * contained within a color node whose color is determined by errorColor - */ - formatUnsupportedCmd(text) { - const textordArray = []; - - for (let i = 0; i < text.length; i++) { - textordArray.push({ type: "textord", mode: "text", text: text[i] }); - } - - const textNode = { - type: "text", - mode: this.mode, - body: textordArray - }; - - const colorNode = { - type: "color", - mode: this.mode, - color: this.settings.errorColor, - body: [textNode] - }; - - return colorNode; - } - - /** - * Parses a group with optional super/subscripts. - */ - parseAtom(breakOnTokenText) { - // The body of an atom is an implicit group, so that things like - // \left(x\right)^2 work correctly. - const base = this.parseGroup("atom", breakOnTokenText); - - // Internal nodes (e.g. \relax) cannot support super/subscripts. - // Instead we will pick up super/subscripts with blank base next round. - if (base && base.type === "internal") { - return base - } - - // In text mode, we don't have superscripts or subscripts - if (this.mode === "text") { - return base - } - - // Note that base may be empty (i.e. null) at this point. - - let superscript; - let subscript; - while (true) { - // Guaranteed in math mode, so eat any spaces first. - this.consumeSpaces(); - - // Lex the first token - const lex = this.fetch(); - - if (lex.text === "\\limits" || lex.text === "\\nolimits") { - // We got a limit control - if (base && base.type === "op") { - const limits = lex.text === "\\limits"; - base.limits = limits; - base.alwaysHandleSupSub = true; - } else if (base && base.type === "operatorname") { - if (base.alwaysHandleSupSub) { - base.limits = lex.text === "\\limits"; - } - } else { - throw new ParseError("Limit controls must follow a math operator", lex); - } - this.consume(); - } else if (lex.text === "^") { - // We got a superscript start - if (superscript) { - throw new ParseError("Double superscript", lex); - } - superscript = this.handleSupSubscript("superscript"); - } else if (lex.text === "_") { - // We got a subscript start - if (subscript) { - throw new ParseError("Double subscript", lex); - } - subscript = this.handleSupSubscript("subscript"); - } else if (lex.text === "'") { - // We got a prime - if (superscript) { - throw new ParseError("Double superscript", lex); - } - const prime = { type: "textord", mode: this.mode, text: "\\prime" }; - - // Many primes can be grouped together, so we handle this here - const primes = [prime]; - this.consume(); - // Keep lexing tokens until we get something that's not a prime - while (this.fetch().text === "'") { - // For each one, add another prime to the list - primes.push(prime); - this.consume(); - } - // If there's a superscript following the primes, combine that - // superscript in with the primes. - if (this.fetch().text === "^") { - primes.push(this.handleSupSubscript("superscript")); - } - // Put everything into an ordgroup as the superscript - superscript = { type: "ordgroup", mode: this.mode, body: primes }; - } else if (uSubsAndSups[lex.text]) { - // A Unicode subscript or superscript character. - // We treat these similarly to the unicode-math package. - // So we render a string of Unicode (sub|super)scripts the - // same as a (sub|super)script of regular characters. - const isSub = unicodeSubRegEx.test(lex.text); - const subsupTokens = []; - subsupTokens.push(new Token(uSubsAndSups[lex.text])); - this.consume(); - // Continue fetching tokens to fill out the group. - while (true) { - const token = this.fetch().text; - if (!(uSubsAndSups[token])) { break } - if (unicodeSubRegEx.test(token) !== isSub) { break } - subsupTokens.unshift(new Token(uSubsAndSups[token])); - this.consume(); - } - // Now create a (sub|super)script. - const body = this.subparse(subsupTokens); - if (isSub) { - subscript = { type: "ordgroup", mode: "math", body }; - } else { - superscript = { type: "ordgroup", mode: "math", body }; - } - } else { - // If it wasn't ^, _, a Unicode (sub|super)script, or ', stop parsing super/subscripts - break; - } - } - - if (superscript || subscript) { - if (base && base.type === "multiscript" && !base.postscripts) { - // base is the result of a \prescript function. - // Write the sub- & superscripts into the multiscript element. - base.postscripts = { sup: superscript, sub: subscript }; - return base - } else { - // We got either a superscript or subscript, create a supsub - const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname") - ? undefined - : isDelimiter(this.nextToken.text); - return { - type: "supsub", - mode: this.mode, - base: base, - sup: superscript, - sub: subscript, - isFollowedByDelimiter - } - } - } else { - // Otherwise return the original body - return base; - } - } - - /** - * Parses an entire function, including its base and all of its arguments. - */ - parseFunction( - breakOnTokenText, - name // For determining its context - ) { - const token = this.fetch(); - const func = token.text; - const funcData = functions[func]; - if (!funcData) { - return null; - } - this.consume(); // consume command token - - if (name && name !== "atom" && !funcData.allowedInArgument) { - throw new ParseError( - "Got function '" + func + "' with no arguments" + (name ? " as " + name : ""), - token - ); - } else if (this.mode === "text" && !funcData.allowedInText) { - throw new ParseError("Can't use function '" + func + "' in text mode", token); - } else if (this.mode === "math" && funcData.allowedInMath === false) { - throw new ParseError("Can't use function '" + func + "' in math mode", token); - } - - const prevAtomType = this.prevAtomType; - const { args, optArgs } = this.parseArguments(func, funcData); - this.prevAtomType = prevAtomType; - return this.callFunction(func, args, optArgs, token, breakOnTokenText); - } - - /** - * Call a function handler with a suitable context and arguments. - */ - callFunction(name, args, optArgs, token, breakOnTokenText) { - const context = { - funcName: name, - parser: this, - token, - breakOnTokenText - }; - const func = functions[name]; - if (func && func.handler) { - return func.handler(context, args, optArgs); - } else { - throw new ParseError(`No function handler for ${name}`); - } - } - - /** - * Parses the arguments of a function or environment - */ - parseArguments( - func, // Should look like "\name" or "\begin{name}". - funcData - ) { - const totalArgs = funcData.numArgs + funcData.numOptionalArgs; - if (totalArgs === 0) { - return { args: [], optArgs: [] }; - } - - const args = []; - const optArgs = []; - - for (let i = 0; i < totalArgs; i++) { - let argType = funcData.argTypes && funcData.argTypes[i]; - const isOptional = i < funcData.numOptionalArgs; - - if ( - (funcData.primitive && argType == null) || - // \sqrt expands into primitive if optional argument doesn't exist - (funcData.type === "sqrt" && i === 1 && optArgs[0] == null) - ) { - argType = "primitive"; - } - - const arg = this.parseGroupOfType(`argument to '${func}'`, argType, isOptional); - if (isOptional) { - optArgs.push(arg); - } else if (arg != null) { - args.push(arg); - } else { - // should be unreachable - throw new ParseError("Null argument, please report this as a bug"); - } - } - - return { args, optArgs }; - } - - /** - * Parses a group when the mode is changing. - */ - parseGroupOfType(name, type, optional) { - switch (type) { - case "size": - return this.parseSizeGroup(optional); - case "url": - return this.parseUrlGroup(optional); - case "math": - case "text": - return this.parseArgumentGroup(optional, type); - case "hbox": { - // hbox argument type wraps the argument in the equivalent of - // \hbox, which is like \text but switching to \textstyle size. - const group = this.parseArgumentGroup(optional, "text"); - return group != null - ? { - type: "styling", - mode: group.mode, - body: [group], - scriptLevel: "text" // simulate \textstyle - } - : null; - } - case "raw": { - const token = this.parseStringGroup("raw", optional); - return token != null - ? { - type: "raw", - mode: "text", - string: token.text - } - : null; - } - case "primitive": { - if (optional) { - throw new ParseError("A primitive argument cannot be optional"); - } - const group = this.parseGroup(name); - if (group == null) { - throw new ParseError("Expected group as " + name, this.fetch()); - } - return group; - } - case "original": - case null: - case undefined: - return this.parseArgumentGroup(optional); - default: - throw new ParseError("Unknown group type as " + name, this.fetch()); - } - } - - /** - * Discard any space tokens, fetching the next non-space token. - */ - consumeSpaces() { - while (true) { - const ch = this.fetch().text; - // \ufe0e is the Unicode variation selector to supress emoji. Ignore it. - if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") { - this.consume(); - } else { - break - } - } - } - - /** - * Parses a group, essentially returning the string formed by the - * brace-enclosed tokens plus some position information. - */ - parseStringGroup( - modeName, // Used to describe the mode in error messages. - optional - ) { - const argToken = this.gullet.scanArgument(optional); - if (argToken == null) { - return null; - } - let str = ""; - let nextToken; - while ((nextToken = this.fetch()).text !== "EOF") { - str += nextToken.text; - this.consume(); - } - this.consume(); // consume the end of the argument - argToken.text = str; - return argToken; - } - - /** - * Parses a regex-delimited group: the largest sequence of tokens - * whose concatenated strings match `regex`. Returns the string - * formed by the tokens plus some position information. - */ - parseRegexGroup( - regex, - modeName // Used to describe the mode in error messages. - ) { - const firstToken = this.fetch(); - let lastToken = firstToken; - let str = ""; - let nextToken; - while ((nextToken = this.fetch()).text !== "EOF" && regex.test(str + nextToken.text)) { - lastToken = nextToken; - str += lastToken.text; - this.consume(); - } - if (str === "") { - throw new ParseError("Invalid " + modeName + ": '" + firstToken.text + "'", firstToken); - } - return firstToken.range(lastToken, str); - } - - /** - * Parses a size specification, consisting of magnitude and unit. - */ - parseSizeGroup(optional) { - let res; - let isBlank = false; - // don't expand before parseStringGroup - this.gullet.consumeSpaces(); - if (!optional && this.gullet.future().text !== "{") { - res = this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size"); - } else { - res = this.parseStringGroup("size", optional); - } - if (!res) { - return null; - } - if (!optional && res.text.length === 0) { - // Because we've tested for what is !optional, this block won't - // affect \kern, \hspace, etc. It will capture the mandatory arguments - // to \genfrac and \above. - res.text = "0pt"; // Enable \above{} - isBlank = true; // This is here specifically for \genfrac - } - const match = sizeRegEx.exec(res.text); - if (!match) { - throw new ParseError("Invalid size: '" + res.text + "'", res); - } - const data = { - number: +(match[1] + match[2]), // sign + magnitude, cast to number - unit: match[3] - }; - if (!validUnit(data)) { - throw new ParseError("Invalid unit: '" + data.unit + "'", res); - } - return { - type: "size", - mode: this.mode, - value: data, - isBlank - }; - } - - /** - * Parses an URL, checking escaped letters and allowed protocols, - * and setting the catcode of % as an active character (as in \hyperref). - */ - parseUrlGroup(optional) { - this.gullet.lexer.setCatcode("%", 13); // active character - this.gullet.lexer.setCatcode("~", 12); // other character - const res = this.parseStringGroup("url", optional); - this.gullet.lexer.setCatcode("%", 14); // comment character - this.gullet.lexer.setCatcode("~", 13); // active character - if (res == null) { - return null; - } - // hyperref package allows backslashes alone in href, but doesn't - // generate valid links in such cases; we interpret this as - // "undefined" behaviour, and keep them as-is. Some browser will - // replace backslashes with forward slashes. - let url = res.text.replace(/\\([#$%&~_^{}])/g, "$1"); - url = res.text.replace(/{\u2044}/g, "/"); - return { - type: "url", - mode: this.mode, - url - }; - } - - /** - * Parses an argument with the mode specified. - */ - parseArgumentGroup(optional, mode) { - const argToken = this.gullet.scanArgument(optional); - if (argToken == null) { - return null; - } - const outerMode = this.mode; - if (mode) { - // Switch to specified mode - this.switchMode(mode); - } - - this.gullet.beginGroup(); - const expression = this.parseExpression(false, "EOF"); - // TODO: find an alternative way to denote the end - this.expect("EOF"); // expect the end of the argument - this.gullet.endGroup(); - const result = { - type: "ordgroup", - mode: this.mode, - loc: argToken.loc, - body: expression - }; - - if (mode) { - // Switch mode back - this.switchMode(outerMode); - } - return result; - } - - /** - * Parses an ordinary group, which is either a single nucleus (like "x") - * or an expression in braces (like "{x+y}") or an implicit group, a group - * that starts at the current position, and ends right before a higher explicit - * group ends, or at EOF. - */ - parseGroup( - name, // For error reporting. - breakOnTokenText - ) { - const firstToken = this.fetch(); - const text = firstToken.text; - if (name === "argument to '\\left'") { return this.parseSymbol() } - let result; - // Try to parse an open brace or \begingroup - if (text === "{" || text === "\\begingroup" || text === "\\toggle") { - this.consume(); - const groupEnd = text === "{" - ? "}" - : text === "\\begingroup" - ? "\\endgroup" - : "\\endtoggle"; - - this.gullet.beginGroup(); - // If we get a brace, parse an expression - const expression = this.parseExpression(false, groupEnd); - const lastToken = this.fetch(); - this.expect(groupEnd); // Check that we got a matching closing brace - this.gullet.endGroup(); - result = { - type: (lastToken.text === "\\endtoggle" ? "toggle" : "ordgroup"), - mode: this.mode, - loc: SourceLocation.range(firstToken, lastToken), - body: expression, - // A group formed by \begingroup...\endgroup is a semi-simple group - // which doesn't affect spacing in math mode, i.e., is transparent. - // https://tex.stackexchange.com/questions/1930/ - semisimple: text === "\\begingroup" || undefined - }; - } else { - // If there exists a function with this name, parse the function. - // Otherwise, just return a nucleus - result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol(); - if (result == null && text[0] === "\\" && - !Object.prototype.hasOwnProperty.call(implicitCommands, text )) { - if (this.settings.throwOnError) { - throw new ParseError("Unsupported function name: " + text, firstToken); - } - // For people getting dyanamically rendered math, it's better to - // show the unsupported command in red rather than panicking for every - // partially written expression. - result = this.formatUnsupportedCmd(text); - this.consume(); - } - } - return result; - } - - /** - * Form ligature-like combinations of characters for text mode. - * This includes inputs like "--", "---", "``" and "''". - * The result will simply replace multiple textord nodes with a single - * character in each value by a single textord node having multiple - * characters in its value. The representation is still ASCII source. - * The group will be modified in place. - */ - formLigatures(group) { - let n = group.length - 1; - for (let i = 0; i < n; ++i) { - const a = group[i]; - const v = a.text; - if (v === "-" && group[i + 1].text === "-") { - if (i + 1 < n && group[i + 2].text === "-") { - group.splice(i, 3, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 2]), - text: "---" - }); - n -= 2; - } else { - group.splice(i, 2, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 1]), - text: "--" - }); - n -= 1; - } - } - if ((v === "'" || v === "`") && group[i + 1].text === v) { - group.splice(i, 2, { - type: "textord", - mode: "text", - loc: SourceLocation.range(a, group[i + 1]), - text: v + v - }); - n -= 1; - } - } - } - - /** - * Parse a single symbol out of the string. Here, we handle single character - * symbols and special functions like \verb. - */ - parseSymbol() { - const nucleus = this.fetch(); - let text = nucleus.text; - - if (/^\\verb[^a-zA-Z]/.test(text)) { - this.consume(); - let arg = text.slice(5); - const star = arg.charAt(0) === "*"; - if (star) { - arg = arg.slice(1); - } - // Lexer's tokenRegex is constructed to always have matching - // first/last characters. - if (arg.length < 2 || arg.charAt(0) !== arg.slice(-1)) { - throw new ParseError(`\\verb assertion failed -- - please report what input caused this bug`); - } - arg = arg.slice(1, -1); // remove first and last char - return { - type: "verb", - mode: "text", - body: arg, - star - }; - } - // At this point, we should have a symbol, possibly with accents. - // First expand any accented base symbol according to unicodeSymbols. - if (Object.prototype.hasOwnProperty.call(unicodeSymbols, text[0]) && - this.mode === "math" && !symbols[this.mode][text[0]]) { - // This behavior is not strict (XeTeX-compatible) in math mode. - if (this.settings.strict && this.mode === "math") { - throw new ParseError(`Accented Unicode text character "${text[0]}" used in ` + `math mode`, - nucleus - ); - } - text = unicodeSymbols[text[0]] + text.slice(1); - } - // Strip off any combining characters - const match = this.mode === "math" - ? combiningDiacriticalMarksEndRegex.exec(text) - : null; - if (match) { - text = text.substring(0, match.index); - if (text === "i") { - text = "\u0131"; // dotless i, in math and text mode - } else if (text === "j") { - text = "\u0237"; // dotless j, in math and text mode - } - } - // Recognize base symbol - let symbol; - if (symbols[this.mode][text]) { - let group = symbols[this.mode][text].group; - if (group === "bin" && - (binLeftCancellers.includes(this.prevAtomType) || this.prevAtomType === "")) { - // Change from a binary operator to a unary (prefix) operator - group = "open"; - } - const loc = SourceLocation.range(nucleus); - let s; - if (Object.prototype.hasOwnProperty.call(ATOMS, group )) { - const family = group; - s = { - type: "atom", - mode: this.mode, - family, - loc, - text - }; - if ((family === "rel" || family === "bin") && this.prevAtomType === "text") { - if (textRegEx.test(loc.lexer.input.slice(loc.end))) { - s.needsSpacing = true; // Fix a MathML bug. - } - } - } else { - if (asciiFromScript[text]) { - // Unicode 14 disambiguates chancery from roundhand. - // See https://www.unicode.org/charts/PDF/U1D400.pdf - this.consume(); - const nextCode = this.fetch().text.charCodeAt(0); - // mathcal is Temml default. Use mathscript if called for. - const font = nextCode === 0xfe01 ? "mathscr" : "mathcal"; - if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume(); } - return { - type: "font", - mode: "math", - font, - body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] } - } - } - // Default ord character. No disambiguation necessary. - s = { - type: group, - mode: this.mode, - loc, - text - }; - } - symbol = s; - } else if (text.charCodeAt(0) >= 0x80 || combiningDiacriticalMarksEndRegex.exec(text)) { - // no symbol for e.g. ^ - if (this.settings.strict && this.mode === "math") { - throw new ParseError(`Unicode text character "${text[0]}" used in math mode`, nucleus) - } - // All nonmathematical Unicode characters are rendered as if they - // are in text mode (wrapped in \text) because that's what it - // takes to render them in LaTeX. - symbol = { - type: "textord", - mode: "text", - loc: SourceLocation.range(nucleus), - text - }; - } else { - return null; // EOF, ^, _, {, }, etc. - } - this.consume(); - // Transform combining characters into accents - if (match) { - for (let i = 0; i < match[0].length; i++) { - const accent = match[0][i]; - if (!unicodeAccents[accent]) { - throw new ParseError(`Unknown accent ' ${accent}'`, nucleus); - } - const command = unicodeAccents[accent][this.mode] || - unicodeAccents[accent].text; - if (!command) { - throw new ParseError(`Accent ${accent} unsupported in ${this.mode} mode`, nucleus); - } - symbol = { - type: "accent", - mode: this.mode, - loc: SourceLocation.range(nucleus), - label: command, - isStretchy: false, - base: symbol - }; - } - } - return symbol; - } -} - -/** - * Parses an expression using a Parser, then returns the parsed result. - */ -const parseTree = function(toParse, settings) { - if (!(typeof toParse === "string" || toParse instanceof String)) { - throw new TypeError("Temml can only parse string typed expression") - } - let tree; - let parser; - try { - parser = new Parser(toParse, settings); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - - tree = parser.parse(); - } catch (error) { - if (error.toString() === "ParseError: Unmatched delimiter") { - // Abandon the attempt to wrap delimiter pairs in an . - // Try again, and put each delimiter into an element. - settings.wrapDelimiterPairs = false; - parser = new Parser(toParse, settings); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - tree = parser.parse(); - } else { - throw error; - } - } - - // LaTeX ignores a \tag placed outside an AMS environment. - if (!(tree.length > 0 && tree[0].type && tree[0].type === "array" && tree[0].addEqnNum)) { - // If the input used \tag, it will set the \df@tag macro to the tag. - // In this case, we separately parse the tag and wrap the tree. - if (parser.gullet.macros.get("\\df@tag")) { - if (!settings.displayMode) { - throw new ParseError("\\tag works only in display mode") - } - parser.gullet.feed("\\df@tag"); - tree = [ - { - type: "tag", - mode: "text", - body: tree, - tag: parser.parse() - } - ]; - } - } - - return tree -}; - -/** - * This file contains information about the style that the mathmlBuilder carries - * around with it. Data is held in an `Style` object, and when - * recursing, a new `Style` object can be created with the `.with*` functions. - */ - -const subOrSupLevel = [2, 2, 3, 3]; - -/** - * This is the main Style class. It contains the current style.level, color, and font. - * - * Style objects should not be modified. To create a new Style with - * different properties, call a `.with*` method. - */ -class Style { - constructor(data) { - // Style.level can be 0 | 1 | 2 | 3, which correspond to - // displaystyle, textstyle, scriptstyle, and scriptscriptstyle. - // style.level usually does not directly set MathML's script level. MathML does that itself. - // However, Chromium does not stop shrinking after scriptscriptstyle, so we do explicitly - // set a scriptlevel attribute in those conditions. - // We also use style.level to track math style so that we can get the correct - // scriptlevel when needed in supsub.js, mathchoice.js, or for dimensions in em. - this.level = data.level; - this.color = data.color; // string | void - // A font family applies to a group of fonts (i.e. SansSerif), while a font - // represents a specific font (i.e. SansSerif Bold). - // See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm - this.font = data.font || ""; // string - this.fontFamily = data.fontFamily || ""; // string - this.fontSize = data.fontSize || 1.0; // number - this.fontWeight = data.fontWeight || ""; - this.fontShape = data.fontShape || ""; - this.maxSize = data.maxSize; // [number, number] - } - - /** - * Returns a new style object with the same properties as "this". Properties - * from "extension" will be copied to the new style object. - */ - extend(extension) { - const data = { - level: this.level, - color: this.color, - font: this.font, - fontFamily: this.fontFamily, - fontSize: this.fontSize, - fontWeight: this.fontWeight, - fontShape: this.fontShape, - maxSize: this.maxSize - }; - - for (const key in extension) { - if (Object.prototype.hasOwnProperty.call(extension, key)) { - data[key] = extension[key]; - } - } - - return new Style(data); - } - - withLevel(n) { - return this.extend({ - level: n - }); - } - - incrementLevel() { - return this.extend({ - level: Math.min(this.level + 1, 3) - }); - } - - inSubOrSup() { - return this.extend({ - level: subOrSupLevel[this.level] - }) - } - - /** - * Create a new style object with the given color. - */ - withColor(color) { - return this.extend({ - color: color - }); - } - - /** - * Creates a new style object with the given math font or old text font. - * @type {[type]} - */ - withFont(font) { - return this.extend({ - font - }); - } - - /** - * Create a new style objects with the given fontFamily. - */ - withTextFontFamily(fontFamily) { - return this.extend({ - fontFamily, - font: "" - }); - } - - /** - * Creates a new style object with the given font size - */ - withFontSize(num) { - return this.extend({ - fontSize: num - }); - } - - /** - * Creates a new style object with the given font weight - */ - withTextFontWeight(fontWeight) { - return this.extend({ - fontWeight, - font: "" - }); - } - - /** - * Creates a new style object with the given font weight - */ - withTextFontShape(fontShape) { - return this.extend({ - fontShape, - font: "" - }); - } - - /** - * Gets the CSS color of the current style object - */ - getColor() { - return this.color; - } -} - -/* Temml Post Process - * Populate the text contents of each \ref & \eqref - * - * As with other Temml code, this file is released under terms of the MIT license. - * https://mit-license.org/ - */ - -const version = "0.13.02"; - -function postProcess(block) { - const labelMap = {}; - let i = 0; - - // Get a collection of the parents of each \tag & auto-numbered equation - const amsEqns = document.getElementsByClassName('tml-eqn'); - for (let parent of amsEqns) { - // AMS automatically numbered equation. - // Assign an id. - i += 1; - parent.setAttribute("id", "tml-eqn-" + String(i)); - // No need to write a number into the text content of the element. - // A CSS counter has done that even if this postProcess() function is not used. - - // Find any \label that refers to an AMS automatic eqn number. - while (true) { - if (parent.tagName === "mtable") { break } - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = String(i); - break - } else { - parent = parent.parentElement; - } - } - } - - // Find \labels associated with \tag - const taggedEqns = document.getElementsByClassName('tml-tageqn'); - for (const parent of taggedEqns) { - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const tags = parent.getElementsByClassName("tml-tag"); - if (tags.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = tags[0].textContent; - } - } - } - - // Populate \ref & \eqref text content - const refs = block.getElementsByClassName("tml-ref"); - [...refs].forEach(ref => { - const attr = ref.getAttribute("href"); - let str = labelMap[attr.slice(1)]; - if (ref.className.indexOf("tml-eqref") === -1) { - // \ref. Omit parens. - str = str.replace(/^\(/, ""); - str = str.replace(/\)$/, ""); - } else { - // \eqref. Include parens - if (str.charAt(0) !== "(") { str = "(" + str; } - if (str.slice(-1) !== ")") { str = str + ")"; } - } - const mtext = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mtext"); - mtext.appendChild(document.createTextNode(str)); - const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"); - math.appendChild(mtext); - ref.textContent = ''; - ref.appendChild(math); - }); -} - -const findEndOfMath = function(delimiter, text, startIndex) { - // Adapted from - // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx - let index = startIndex; - let braceLevel = 0; - - const delimLength = delimiter.length; - - while (index < text.length) { - const character = text[index]; - - if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) { - return index; - } else if (character === "\\") { - index++; - } else if (character === "{") { - braceLevel++; - } else if (character === "}") { - braceLevel--; - } - - index++; - } - - return -1; -}; - -const escapeRegex = function(string) { - return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); -}; - -const amsRegex = /^\\(?:begin|(?:eq)?ref){/; - -const splitAtDelimiters = function(text, delimiters) { - let index; - const data = []; - - const regexLeft = new RegExp( - "(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")" - ); - - while (true) { - index = text.search(regexLeft); - if (index === -1) { - break; - } - if (index > 0) { - data.push({ - type: "text", - data: text.slice(0, index) - }); - text = text.slice(index); // now text starts with delimiter - } - // ... so this always succeeds: - const i = delimiters.findIndex((delim) => text.startsWith(delim.left)); - index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length); - if (index === -1) { - break; - } - const rawData = text.slice(0, index + delimiters[i].right.length); - const math = amsRegex.test(rawData) - ? rawData - : text.slice(delimiters[i].left.length, index); - data.push({ - type: "math", - data: math, - rawData, - display: delimiters[i].display - }); - text = text.slice(index + delimiters[i].right.length); - } - - if (text !== "") { - data.push({ - type: "text", - data: text - }); - } - - return data; -}; - -const defaultDelimiters = [ - { left: "$$", right: "$$", display: true }, - { left: "\\(", right: "\\)", display: false }, - // LaTeX uses $…$, but it ruins the display of normal `$` in text: - // {left: "$", right: "$", display: false}, - // $ must come after $$ - - // Render AMS environments even if outside $$…$$ delimiters. - { left: "\\begin{equation}", right: "\\end{equation}", display: true }, - { left: "\\begin{equation*}", right: "\\end{equation*}", display: true }, - { left: "\\begin{align}", right: "\\end{align}", display: true }, - { left: "\\begin{align*}", right: "\\end{align*}", display: true }, - { left: "\\begin{alignat}", right: "\\end{alignat}", display: true }, - { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true }, - { left: "\\begin{gather}", right: "\\end{gather}", display: true }, - { left: "\\begin{gather*}", right: "\\end{gather*}", display: true }, - { left: "\\begin{CD}", right: "\\end{CD}", display: true }, - // Ditto \ref & \eqref - { left: "\\ref{", right: "}", display: false }, - { left: "\\eqref{", right: "}", display: false }, - - { left: "\\[", right: "\\]", display: true } -]; - -const firstDraftDelimiters = { - "$": [ - { left: "$$", right: "$$", display: true }, - { left: "$`", right: "`$", display: false }, - { left: "$", right: "$", display: false } - ], - "(": [ - { left: "\\[", right: "\\]", display: true }, - { left: "\\(", right: "\\)", display: false } - ] -}; - -const amsDelimiters = [ - { left: "\\begin{equation}", right: "\\end{equation}", display: true }, - { left: "\\begin{equation*}", right: "\\end{equation*}", display: true }, - { left: "\\begin{align}", right: "\\end{align}", display: true }, - { left: "\\begin{align*}", right: "\\end{align*}", display: true }, - { left: "\\begin{alignat}", right: "\\end{alignat}", display: true }, - { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true }, - { left: "\\begin{gather}", right: "\\end{gather}", display: true }, - { left: "\\begin{gather*}", right: "\\end{gather*}", display: true }, - { left: "\\begin{CD}", right: "\\end{CD}", display: true }, - { left: "\\ref{", right: "}", display: false }, - { left: "\\eqref{", right: "}", display: false } -]; - -const delimitersFromKey = key => { - if (key === "$" || key === "(") { - return firstDraftDelimiters[key]; - } else if (key === "$+" || key === "(+") { - const firstDraft = firstDraftDelimiters[key.slice(0, 1)]; - return firstDraft.concat(amsDelimiters) - } else if (key === "ams") { - return amsDelimiters - } else if (key === "all") { - return (firstDraftDelimiters["("]).concat(firstDraftDelimiters["$"]).concat(amsDelimiters) - } else { - return defaultDelimiters - } -}; - -/* Note: optionsCopy is mutated by this method. If it is ever exposed in the - * API, we should copy it before mutating. - */ -const renderMathInText = function(text, optionsCopy) { - const data = splitAtDelimiters(text, optionsCopy.delimiters); - if (data.length === 1 && data[0].type === "text") { - // There is no formula in the text. - // Let's return null which means there is no need to replace - // the current text node with a new one. - return null; - } - - const fragment = document.createDocumentFragment(); - - for (let i = 0; i < data.length; i++) { - if (data[i].type === "text") { - fragment.appendChild(document.createTextNode(data[i].data)); - } else { - const span = document.createElement("span"); - let math = data[i].data; - // Override any display mode defined in the settings with that - // defined by the text itself - optionsCopy.displayMode = data[i].display; - try { - if (optionsCopy.preProcess) { - math = optionsCopy.preProcess(math); - } - // Importing render() from temml.js would be a circular dependency. - // So call the global version. - // eslint-disable-next-line no-undef - temml.render(math, span, optionsCopy); - } catch (e) { - if (!(e instanceof ParseError)) { - throw e; - } - optionsCopy.errorCallback( - "Temml auto-render: Failed to parse `" + data[i].data + "` with ", - e - ); - fragment.appendChild(document.createTextNode(data[i].rawData)); - continue; - } - fragment.appendChild(span); - } - } - - return fragment; -}; - -const renderElem = function(elem, optionsCopy) { - for (let i = 0; i < elem.childNodes.length; i++) { - const childNode = elem.childNodes[i]; - if (childNode.nodeType === 3) { - // Text node - const frag = renderMathInText(childNode.textContent, optionsCopy); - if (frag) { - i += frag.childNodes.length - 1; - elem.replaceChild(frag, childNode); - } - } else if (childNode.nodeType === 1) { - // Element node - const className = " " + childNode.className + " "; - const shouldRender = - optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && - optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1); - - if (shouldRender) { - renderElem(childNode, optionsCopy); - } - } - // Otherwise, it's something else, and ignore it. - } -}; - -const renderMathInElement = function(elem, options) { - if (!elem) { - throw new Error("No element provided to render"); - } - - const optionsCopy = {}; - - // Object.assign(optionsCopy, option) - for (const option in options) { - if (Object.prototype.hasOwnProperty.call(options, option)) { - optionsCopy[option] = options[option]; - } - } - - if (optionsCopy.fences) { - optionsCopy.delimiters = delimitersFromKey(optionsCopy.fences); - } else { - optionsCopy.delimiters = optionsCopy.delimiters || defaultDelimiters; - } - optionsCopy.ignoredTags = optionsCopy.ignoredTags || [ - "script", - "noscript", - "style", - "textarea", - "pre", - "code", - "option" - ]; - optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || []; - // eslint-disable-next-line no-console - optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; - - // Enable sharing of global macros defined via `\gdef` between different - // math elements within a single call to `renderMathInElement`. - optionsCopy.macros = optionsCopy.macros || {}; - - renderElem(elem, optionsCopy); - postProcess(elem); -}; - -/* eslint no-console:0 */ -/** - * This is the main entry point for Temml. Here, we expose functions for - * rendering expressions either to DOM nodes or to markup strings. - * - * We also expose the ParseError class to check if errors thrown from Temml are - * errors in the expression, or errors in javascript handling. - */ - - -/** - * @type {import('./temml').render} - * Parse and build an expression, and place that expression in the DOM node - * given. - */ -let render = function(expression, baseNode, options = {}) { - baseNode.textContent = ""; - const alreadyInMathElement = baseNode.tagName.toLowerCase() === "math"; - if (alreadyInMathElement) { options.wrap = "none"; } - const math = renderToMathMLTree(expression, options); - if (alreadyInMathElement) { - // The element already exists. Populate it. - baseNode.textContent = ""; - math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); - } else if (math.children.length > 1) { - baseNode.textContent = ""; - math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); - } else { - baseNode.appendChild(math.toNode()); - } -}; - -// Temml's styles don't work properly in quirks mode. Print out an error, and -// disable rendering. -if (typeof document !== "undefined") { - if (document.compatMode !== "CSS1Compat") { - typeof console !== "undefined" && - console.warn( - "Warning: Temml doesn't work in quirks mode. Make sure your " + - "website has a suitable doctype." - ); - - render = function() { - throw new ParseError("Temml doesn't work in quirks mode."); - }; - } -} - -/** - * @type {import('./temml').renderToString} - * Parse and build an expression, and return the markup for that. - */ -const renderToString = function(expression, options) { - const markup = renderToMathMLTree(expression, options).toMarkup(); - return markup; -}; - -/** - * @type {import('./temml').generateParseTree} - * Parse an expression and return the parse tree. - */ -const generateParseTree = function(expression, options) { - const settings = new Settings(options); - return parseTree(expression, settings); -}; - -/** - * @type {import('./temml').definePreamble} - * Take an expression which contains a preamble. - * Parse it and return the macros. - */ -const definePreamble = function(expression, options) { - const settings = new Settings(options); - settings.macros = {}; - if (!(typeof expression === "string" || expression instanceof String)) { - throw new TypeError("Temml can only parse string typed expression") - } - const parser = new Parser(expression, settings, true); - // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors - delete parser.gullet.macros.current["\\df@tag"]; - const macros = parser.parse(); - return macros -}; - -/** - * If the given error is a Temml ParseError, - * renders the invalid LaTeX as a span with hover title giving the Temml - * error message. Otherwise, simply throws the error. - */ -const renderError = function(error, expression, options) { - if (options.throwOnError || !(error instanceof ParseError)) { - throw error; - } - const node = new Span(["temml-error"], [new TextNode$1(expression + "\n\n" + error.toString())]); - node.style.color = options.errorColor; - node.style.whiteSpace = "pre-line"; - return node; -}; - -/** - * @type {import('./temml').renderToMathMLTree} - * Generates and returns the Temml build tree. This is used for advanced - * use cases (like rendering to custom output). - */ -const renderToMathMLTree = function(expression, options) { - const settings = new Settings(options); - try { - const tree = parseTree(expression, settings); - const style = new Style({ - level: settings.displayMode ? StyleLevel.DISPLAY : StyleLevel.TEXT, - maxSize: settings.maxSize - }); - return buildMathML(tree, expression, style, settings); - } catch (error) { - return renderError(error, expression, settings); - } -}; - -/** @type {import('./temml').default} */ -var temml$1 = { - /** - * Current Temml version - */ - version: version, - /** - * Renders the given LaTeX into MathML, and adds - * it as a child to the specified DOM node. - */ - render, - /** - * Renders the given LaTeX into MathML string, - * for sending to the client. - */ - renderToString, - /** - * Finds all the math delimiters in a given element of a running HTML document - * and converts the contents of each instance into a element. - */ - renderMathInElement, - /** - * Post-process an entire HTML block. - * Writes AMS auto-numbers and implements \ref{}. - * Typcally called once, after a loop has rendered many individual spans. - */ - postProcess, - /** - * Temml error, usually during parsing. - */ - ParseError, - /** - * Creates a set of macros with document-wide scope. - */ - definePreamble, - /** - * Parses the given LaTeX into Temml's internal parse tree structure, - * without rendering to HTML or MathML. - * - * NOTE: This method is not currently recommended for public use. - * The internal tree representation is unstable and is very likely - * to change. Use at your own risk. - */ - __parse: generateParseTree, - /** - * Renders the given LaTeX into a MathML internal DOM tree - * representation, without flattening that representation to a string. - * - * NOTE: This method is not currently recommended for public use. - * The internal tree representation is unstable and is very likely - * to change. Use at your own risk. - */ - __renderToMathMLTree: renderToMathMLTree, - /** - * adds a new symbol to builtin symbols table - */ - __defineSymbol: defineSymbol, - /** - * adds a new macro to builtin macro list - */ - __defineMacro: defineMacro -}; - -export { temml$1 as default }; diff --git a/dist/temmlPostProcess.js b/dist/temmlPostProcess.js deleted file mode 100644 index 9a30ba35..00000000 --- a/dist/temmlPostProcess.js +++ /dev/null @@ -1,83 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.temml = {})); -})(this, (function (exports) { 'use strict'; - - /* Temml Post Process - * Populate the text contents of each \ref & \eqref - * - * As with other Temml code, this file is released under terms of the MIT license. - * https://mit-license.org/ - */ - - const version = "0.13.02"; - - function postProcess(block) { - const labelMap = {}; - let i = 0; - - // Get a collection of the parents of each \tag & auto-numbered equation - const amsEqns = document.getElementsByClassName('tml-eqn'); - for (let parent of amsEqns) { - // AMS automatically numbered equation. - // Assign an id. - i += 1; - parent.setAttribute("id", "tml-eqn-" + String(i)); - // No need to write a number into the text content of the element. - // A CSS counter has done that even if this postProcess() function is not used. - - // Find any \label that refers to an AMS automatic eqn number. - while (true) { - if (parent.tagName === "mtable") { break } - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = String(i); - break - } else { - parent = parent.parentElement; - } - } - } - - // Find \labels associated with \tag - const taggedEqns = document.getElementsByClassName('tml-tageqn'); - for (const parent of taggedEqns) { - const labels = parent.getElementsByClassName("tml-label"); - if (labels.length > 0) { - const tags = parent.getElementsByClassName("tml-tag"); - if (tags.length > 0) { - const id = parent.attributes.id.value; - labelMap[id] = tags[0].textContent; - } - } - } - - // Populate \ref & \eqref text content - const refs = block.getElementsByClassName("tml-ref"); - [...refs].forEach(ref => { - const attr = ref.getAttribute("href"); - let str = labelMap[attr.slice(1)]; - if (ref.className.indexOf("tml-eqref") === -1) { - // \ref. Omit parens. - str = str.replace(/^\(/, ""); - str = str.replace(/\)$/, ""); - } else { - // \eqref. Include parens - if (str.charAt(0) !== "(") { str = "(" + str; } - if (str.slice(-1) !== ")") { str = str + ")"; } - } - const mtext = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mtext"); - mtext.appendChild(document.createTextNode(str)); - const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"); - math.appendChild(mtext); - ref.textContent = ''; - ref.appendChild(math); - }); - } - - exports.postProcess = postProcess; - exports.version = version; - -})); diff --git a/package-lock.json b/package-lock.json index 31ca47aa..1d4e4723 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "temml", - "version": "0.13.01", + "version": "0.13.02", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "temml", - "version": "0.13.01", + "version": "0.13.02", "license": "MIT", "devDependencies": { "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.11.1", "eslint": "^9.11.1", - "esm": "^3.2.25", "globals": "^15.9.0", "rollup": "^4.59.0", "terser": "^5.34.0" @@ -26,6 +25,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -44,6 +44,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -56,19 +57,21 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -79,6 +82,7 @@ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0" }, @@ -91,6 +95,7 @@ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -99,10 +104,11 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz", - "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", @@ -111,7 +117,7 @@ "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", - "minimatch": "^3.1.3", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -126,6 +132,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -134,10 +141,11 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.3", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", - "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -150,6 +158,7 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -159,6 +168,7 @@ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -172,6 +182,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } @@ -181,6 +192,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -194,6 +206,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -207,6 +220,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -220,6 +234,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -230,6 +245,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -239,6 +255,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -248,338 +265,365 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -589,19 +633,23 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, + "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -614,6 +662,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -623,6 +672,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -639,6 +689,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -653,19 +704,22 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -675,13 +729,15 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -691,6 +747,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -707,6 +764,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -718,25 +776,29 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -751,6 +813,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -767,13 +830,15 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -782,24 +847,26 @@ } }, "node_modules/eslint": { - "version": "9.39.3", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", - "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", + "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.3", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -818,7 +885,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -845,6 +912,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -861,6 +929,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -868,20 +937,12 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -899,6 +960,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -911,6 +973,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -923,6 +986,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -932,6 +996,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -940,25 +1005,29 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -971,6 +1040,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -987,6 +1057,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -996,10 +1067,11 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -1007,6 +1079,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1020,6 +1093,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -1032,6 +1106,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1044,6 +1119,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1053,6 +1129,7 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -1062,6 +1139,7 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -1078,6 +1156,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -1087,6 +1166,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1096,6 +1176,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1107,13 +1188,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1125,25 +1208,29 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -1153,6 +1240,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -1166,6 +1254,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -1180,13 +1269,15 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1198,19 +1289,22 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -1228,6 +1322,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -1243,6 +1338,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -1258,6 +1354,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -1270,6 +1367,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1279,6 +1377,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1288,6 +1387,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -1297,6 +1397,7 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1306,15 +1407,17 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -1326,31 +1429,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" } }, @@ -1359,6 +1462,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1371,6 +1475,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1380,6 +1485,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -1389,6 +1495,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -1399,6 +1506,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -1411,6 +1519,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1419,10 +1528,11 @@ } }, "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -1441,6 +1551,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -1453,6 +1564,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -1462,6 +1574,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -1477,6 +1590,7 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1486,6 +1600,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index f10b3586..536c14d6 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.13.02", "description": "TeX to MathML conversion in JavaScript.", "main": "dist/temml.js", + "sideEffects": true, "engines": { "node": ">=18.13.0" }, @@ -17,9 +18,8 @@ "homepage": "https://temml.org", "repository": { "type": "git", - "url": "git://github.com/ronkok/Temml" + "url": "https://github.com/ronkok/Temml.git" }, - "packageManager": "yarn@3.3.1", "files": [ "temml.js", "src/", @@ -31,7 +31,6 @@ "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.11.1", "eslint": "^9.11.1", - "esm": "^3.2.25", "globals": "^15.9.0", "rollup": "^4.59.0", "terser": "^5.34.0" @@ -40,10 +39,11 @@ "lint": "eslint temml.js src", "unit-test": "node ./test/unit-test.cjs", "visual-test": "node utils/buildTests.js", - "test": "yarn lint && node utils/buildTests.js && yarn unit-test", + "test": "npm run lint && node utils/buildTests.js && npm run unit-test", "minify": "terser test/temml.js -o site/assets/temml.min.js -c -m && terser contrib/mhchem/mhchem.js -o site/assets/mhchem.min.js -c -m", - "build": "rollup --config ./utils/rollupConfig.mjs && yarn minify && node utils/insertPlugins.js", + "build": "rollup --config ./utils/rollupConfig.mjs && npm run minify && node utils/insertPlugins.js", "docs": "node utils/buildDocs.js", - "dist": "yarn build && node ./utils/copyfiles.js" + "dist": "npm run build && node ./utils/copyfiles.js", + "prepublishOnly": "npm run dist" } } diff --git a/utils/copyfiles.js b/utils/copyfiles.js index e1051d72..cb8f0557 100644 --- a/utils/copyfiles.js +++ b/utils/copyfiles.js @@ -1,6 +1,7 @@ const fs = require('fs'); // Populate the `dist` folder. +fs.mkdirSync('dist', { recursive: true }); fs.copyFile('site/assets/Temml-Local.css', 'dist/Temml-Local.css', (err) => { if (err) { throw err } diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index a49af69f..00000000 --- a/yarn.lock +++ /dev/null @@ -1,1669 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 6 - cacheKey: 8 - -"@eslint-community/eslint-utils@npm:^4.8.0": - version: 4.9.1 - resolution: "@eslint-community/eslint-utils@npm:4.9.1" - dependencies: - eslint-visitor-keys: ^3.4.3 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 0a27c2d676c4be6b329ebb5dd8f6c5ef5fae9a019ff575655306d72874bb26f3ab20e0b241a5f086464bb1f2511ca26a29ff6f80c1e2b0b02eca4686b4dfe1b5 - languageName: node - linkType: hard - -"@eslint-community/regexpp@npm:^4.12.1": - version: 4.12.2 - resolution: "@eslint-community/regexpp@npm:4.12.2" - checksum: 1770bc81f676a72f65c7200b5675ff7a349786521f30e66125faaf767fde1ba1c19c3790e16ba8508a62a3933afcfc806a893858b3b5906faf693d862b9e4120 - languageName: node - linkType: hard - -"@eslint/config-array@npm:^0.21.1": - version: 0.21.1 - resolution: "@eslint/config-array@npm:0.21.1" - dependencies: - "@eslint/object-schema": ^2.1.7 - debug: ^4.3.1 - minimatch: ^3.1.2 - checksum: fc5b57803b059f7c1f62950ef83baf045a01887fc00551f9e87ac119246fcc6d71c854a7f678accc79cbf829ed010e8135c755a154b0f54b129c538950cd7e6a - languageName: node - linkType: hard - -"@eslint/config-helpers@npm:^0.4.2": - version: 0.4.2 - resolution: "@eslint/config-helpers@npm:0.4.2" - dependencies: - "@eslint/core": ^0.17.0 - checksum: 63ff6a0730c9fff2edb80c89b39b15b28d6a635a1c3f32cf0d7eb3e2625f2efbc373c5531ae84e420ae36d6e37016dd40c365b6e5dee6938478e9907aaadae0b - languageName: node - linkType: hard - -"@eslint/core@npm:^0.17.0": - version: 0.17.0 - resolution: "@eslint/core@npm:0.17.0" - dependencies: - "@types/json-schema": ^7.0.15 - checksum: ff9b5b4987f0bae4f2a4cfcdc7ae584ad3b0cb58526ca562fb281d6837700a04c7f3c86862e95126462318f33f60bf38e1cb07ed0e2449532d4b91cd5f4ab1f2 - languageName: node - linkType: hard - -"@eslint/eslintrc@npm:^3.1.0, @eslint/eslintrc@npm:^3.3.1": - version: 3.3.4 - resolution: "@eslint/eslintrc@npm:3.3.4" - dependencies: - ajv: ^6.14.0 - debug: ^4.3.2 - espree: ^10.0.1 - globals: ^14.0.0 - ignore: ^5.2.0 - import-fresh: ^3.2.1 - js-yaml: ^4.1.1 - minimatch: ^3.1.3 - strip-json-comments: ^3.1.1 - checksum: c16df92611b927af454d3ab9b1e003d75ae5a0e91009bdab8487bc37d4eb507adf071bd208857fd397a2c311cff1c28f617e01250078e532ab3ac7f1353cee13 - languageName: node - linkType: hard - -"@eslint/js@npm:9.39.3, @eslint/js@npm:^9.11.1": - version: 9.39.3 - resolution: "@eslint/js@npm:9.39.3" - checksum: 6018c13073204cf1b79de561cca74284c0387bf753e0dcd85ff750f1441c4c2914896d8feff3afd8c07d6934ac6f8ae36a5cc241f5645041b645dad588442d46 - languageName: node - linkType: hard - -"@eslint/object-schema@npm:^2.1.7": - version: 2.1.7 - resolution: "@eslint/object-schema@npm:2.1.7" - checksum: fc5708f192476956544def13455d60fd1bafbf8f062d1e05ec5c06dd470b02078eaf721e696a8b31c1c45d2056723a514b941ae5eea1398cc7e38eba6711a775 - languageName: node - linkType: hard - -"@eslint/plugin-kit@npm:^0.4.1": - version: 0.4.1 - resolution: "@eslint/plugin-kit@npm:0.4.1" - dependencies: - "@eslint/core": ^0.17.0 - levn: ^0.4.1 - checksum: 3f4492e02a3620e05d46126c5cfeff5f651ecf33466c8f88efb4812ae69db5f005e8c13373afabc070ecca7becd319b656d6670ad5093f05ca63c2a8841d99ba - languageName: node - linkType: hard - -"@gar/promise-retry@npm:^1.0.0": - version: 1.0.2 - resolution: "@gar/promise-retry@npm:1.0.2" - dependencies: - retry: ^0.13.1 - checksum: b91326999ce94677cbe91973079eabc689761a93a045f6a2d34d4070e9305b27f6c54e4021688c7080cb14caf89eafa0c0f300af741b94c20d18608bdb66ca46 - languageName: node - linkType: hard - -"@humanfs/core@npm:^0.19.1": - version: 0.19.1 - resolution: "@humanfs/core@npm:0.19.1" - checksum: 611e0545146f55ddfdd5c20239cfb7911f9d0e28258787c4fc1a1f6214250830c9367aaaeace0096ed90b6739bee1e9c52ad5ba8adaf74ab8b449119303babfe - languageName: node - linkType: hard - -"@humanfs/node@npm:^0.16.6": - version: 0.16.7 - resolution: "@humanfs/node@npm:0.16.7" - dependencies: - "@humanfs/core": ^0.19.1 - "@humanwhocodes/retry": ^0.4.0 - checksum: 7d2a396a94d80158ce320c0fd7df9aebb82edb8b667e5aaf8f87f4ca50518d0941ca494e0cd68e06b061e777ce5f7d26c45f93ac3fa9f7b11fd1ff26e3cd1440 - languageName: node - linkType: hard - -"@humanwhocodes/module-importer@npm:^1.0.1": - version: 1.0.1 - resolution: "@humanwhocodes/module-importer@npm:1.0.1" - checksum: 0fd22007db8034a2cdf2c764b140d37d9020bbfce8a49d3ec5c05290e77d4b0263b1b972b752df8c89e5eaa94073408f2b7d977aed131faf6cf396ebb5d7fb61 - languageName: node - linkType: hard - -"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2": - version: 0.4.3 - resolution: "@humanwhocodes/retry@npm:0.4.3" - checksum: d423455b9d53cf01f778603404512a4246fb19b83e74fe3e28c70d9a80e9d4ae147d2411628907ca983e91a855a52535859a8bb218050bc3f6dbd7a553b7b442 - languageName: node - linkType: hard - -"@isaacs/fs-minipass@npm:^4.0.0": - version: 4.0.1 - resolution: "@isaacs/fs-minipass@npm:4.0.1" - dependencies: - minipass: ^7.0.4 - checksum: 5d36d289960e886484362d9eb6a51d1ea28baed5f5d0140bbe62b99bac52eaf06cc01c2bc0d3575977962f84f6b2c4387b043ee632216643d4787b0999465bf2 - languageName: node - linkType: hard - -"@jridgewell/gen-mapping@npm:^0.3.5": - version: 0.3.13 - resolution: "@jridgewell/gen-mapping@npm:0.3.13" - dependencies: - "@jridgewell/sourcemap-codec": ^1.5.0 - "@jridgewell/trace-mapping": ^0.3.24 - checksum: f2105acefc433337145caa3c84bba286de954f61c0bc46279bbd85a9e6a02871089717fa060413cfb6a9d44189fe8313b2d1cabf3a2eb3284d208fd5f75c54ff - languageName: node - linkType: hard - -"@jridgewell/resolve-uri@npm:^3.1.0": - version: 3.1.2 - resolution: "@jridgewell/resolve-uri@npm:3.1.2" - checksum: 83b85f72c59d1c080b4cbec0fef84528963a1b5db34e4370fa4bd1e3ff64a0d80e0cee7369d11d73c704e0286fb2865b530acac7a871088fbe92b5edf1000870 - languageName: node - linkType: hard - -"@jridgewell/source-map@npm:^0.3.3": - version: 0.3.11 - resolution: "@jridgewell/source-map@npm:0.3.11" - dependencies: - "@jridgewell/gen-mapping": ^0.3.5 - "@jridgewell/trace-mapping": ^0.3.25 - checksum: c8a0011cc67e701f270fa042e32b312f382c413bcc70ca9c03684687cbf5b64d5eed87d4afa36dddaabe60ab3da6db4935f878febd9cfc7f82724ea1a114d344 - languageName: node - linkType: hard - -"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": - version: 1.5.5 - resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" - checksum: c2e36e67971f719a8a3a85ef5a5f580622437cc723c35d03ebd0c9c0b06418700ef006f58af742791f71f6a4fc68fcfaf1f6a74ec2f9a3332860e9373459dae7 - languageName: node - linkType: hard - -"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": - version: 0.3.31 - resolution: "@jridgewell/trace-mapping@npm:0.3.31" - dependencies: - "@jridgewell/resolve-uri": ^3.1.0 - "@jridgewell/sourcemap-codec": ^1.4.14 - checksum: af8fda2431348ad507fbddf8e25f5d08c79ecc94594061ce402cf41bc5aba1a7b3e59bf0fd70a619b35f33983a3f488ceeba8faf56bff784f98bb5394a8b7d47 - languageName: node - linkType: hard - -"@npmcli/agent@npm:^4.0.0": - version: 4.0.0 - resolution: "@npmcli/agent@npm:4.0.0" - dependencies: - agent-base: ^7.1.0 - http-proxy-agent: ^7.0.0 - https-proxy-agent: ^7.0.1 - lru-cache: ^11.2.1 - socks-proxy-agent: ^8.0.3 - checksum: 89ae20b44859ff8d4de56ade319d8ceaa267a0742d6f7345fe98aa5cd8614ced7db85ea4dc5bfbd6614dbb200a10b134e087143582534c939e8a02219e8665c8 - languageName: node - linkType: hard - -"@npmcli/fs@npm:^5.0.0": - version: 5.0.0 - resolution: "@npmcli/fs@npm:5.0.0" - dependencies: - semver: ^7.3.5 - checksum: 897dac32eb37e011800112d406b9ea2ebd96f1dab01bb8fbeb59191b86f6825dffed6a89f3b6c824753d10f8735b76d630927bd7610e9e123b129ef2e5f02cb5 - languageName: node - linkType: hard - -"@rollup/rollup-android-arm-eabi@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.59.0" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@rollup/rollup-android-arm64@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-android-arm64@npm:4.59.0" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@rollup/rollup-darwin-arm64@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-darwin-arm64@npm:4.59.0" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@rollup/rollup-darwin-x64@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-darwin-x64@npm:4.59.0" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@rollup/rollup-freebsd-arm64@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.59.0" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - -"@rollup/rollup-freebsd-x64@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-freebsd-x64@npm:4.59.0" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@rollup/rollup-linux-arm-gnueabihf@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.59.0" - conditions: os=linux & cpu=arm & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-arm-musleabihf@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.59.0" - conditions: os=linux & cpu=arm & libc=musl - languageName: node - linkType: hard - -"@rollup/rollup-linux-arm64-gnu@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.59.0" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-arm64-musl@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.59.0" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@rollup/rollup-linux-loong64-gnu@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.59.0" - conditions: os=linux & cpu=loong64 & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-loong64-musl@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-loong64-musl@npm:4.59.0" - conditions: os=linux & cpu=loong64 & libc=musl - languageName: node - linkType: hard - -"@rollup/rollup-linux-ppc64-gnu@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.59.0" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-ppc64-musl@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.59.0" - conditions: os=linux & cpu=ppc64 & libc=musl - languageName: node - linkType: hard - -"@rollup/rollup-linux-riscv64-gnu@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.59.0" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-riscv64-musl@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.59.0" - conditions: os=linux & cpu=riscv64 & libc=musl - languageName: node - linkType: hard - -"@rollup/rollup-linux-s390x-gnu@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.59.0" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-x64-gnu@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.59.0" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-x64-musl@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.59.0" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@rollup/rollup-openbsd-x64@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-openbsd-x64@npm:4.59.0" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - -"@rollup/rollup-openharmony-arm64@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-openharmony-arm64@npm:4.59.0" - conditions: os=openharmony & cpu=arm64 - languageName: node - linkType: hard - -"@rollup/rollup-win32-arm64-msvc@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.59.0" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@rollup/rollup-win32-ia32-msvc@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.59.0" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@rollup/rollup-win32-x64-gnu@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-win32-x64-gnu@npm:4.59.0" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@rollup/rollup-win32-x64-msvc@npm:4.59.0": - version: 4.59.0 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.59.0" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.6": - version: 1.0.8 - resolution: "@types/estree@npm:1.0.8" - checksum: bd93e2e415b6f182ec4da1074e1f36c480f1d26add3e696d54fb30c09bc470897e41361c8fd957bf0985024f8fbf1e6e2aff977d79352ef7eb93a5c6dcff6c11 - languageName: node - linkType: hard - -"@types/json-schema@npm:^7.0.15": - version: 7.0.15 - resolution: "@types/json-schema@npm:7.0.15" - checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 - languageName: node - linkType: hard - -"abbrev@npm:^4.0.0": - version: 4.0.0 - resolution: "abbrev@npm:4.0.0" - checksum: d0344b63d28e763f259b4898c41bdc92c08e9d06d0da5617d0bbe4d78244e46daea88c510a2f9472af59b031d9060ec1a999653144e793fd029a59dae2f56dc8 - languageName: node - linkType: hard - -"acorn-jsx@npm:^5.3.2": - version: 5.3.2 - resolution: "acorn-jsx@npm:5.3.2" - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: c3d3b2a89c9a056b205b69530a37b972b404ee46ec8e5b341666f9513d3163e2a4f214a71f4dfc7370f5a9c07472d2fd1c11c91c3f03d093e37637d95da98950 - languageName: node - linkType: hard - -"acorn@npm:^8.15.0": - version: 8.16.0 - resolution: "acorn@npm:8.16.0" - bin: - acorn: bin/acorn - checksum: bbfa466cd0dbd18b4460a85e9d0fc2f35db999380892403c573261beda91f23836db2aa71fd3ae65e94424ad14ff8e2b7bd37c7a2624278fd89137cd6e448c41 - languageName: node - linkType: hard - -"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": - version: 7.1.4 - resolution: "agent-base@npm:7.1.4" - checksum: 86a7f542af277cfbd77dd61e7df8422f90bac512953709003a1c530171a9d019d072e2400eab2b59f84b49ab9dd237be44315ca663ac73e82b3922d10ea5eafa - languageName: node - linkType: hard - -"ajv@npm:^6.12.4, ajv@npm:^6.14.0": - version: 6.14.0 - resolution: "ajv@npm:6.14.0" - dependencies: - fast-deep-equal: ^3.1.1 - fast-json-stable-stringify: ^2.0.0 - json-schema-traverse: ^0.4.1 - uri-js: ^4.2.2 - checksum: 7bb3ea97bb8af52521589079f427e799b6561acaa94f50e13410cb87588c51df8db1afe1157b3e48f1a829269adaa11116e0c2cafe2b998add1523789809a3c5 - languageName: node - linkType: hard - -"ansi-styles@npm:^4.1.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: ^2.0.1 - checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 - languageName: node - linkType: hard - -"argparse@npm:^2.0.1": - version: 2.0.1 - resolution: "argparse@npm:2.0.1" - checksum: 83644b56493e89a254bae05702abf3a1101b4fa4d0ca31df1c9985275a5a5bd47b3c27b7fa0b71098d41114d8ca000e6ed90cad764b306f8a503665e4d517ced - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 - languageName: node - linkType: hard - -"balanced-match@npm:^4.0.2": - version: 4.0.4 - resolution: "balanced-match@npm:4.0.4" - checksum: fb07bb66a0959c2843fc055838047e2a95ccebb837c519614afb067ebfdf2fa967ca8d712c35ced07f2cd26fc6f07964230b094891315ad74f11eba3d53178a0 - languageName: node - linkType: hard - -"brace-expansion@npm:^1.1.7": - version: 1.1.12 - resolution: "brace-expansion@npm:1.1.12" - dependencies: - balanced-match: ^1.0.0 - concat-map: 0.0.1 - checksum: 12cb6d6310629e3048cadb003e1aca4d8c9bb5c67c3c321bafdd7e7a50155de081f78ea3e0ed92ecc75a9015e784f301efc8132383132f4f7904ad1ac529c562 - languageName: node - linkType: hard - -"brace-expansion@npm:^5.0.2": - version: 5.0.4 - resolution: "brace-expansion@npm:5.0.4" - dependencies: - balanced-match: ^4.0.2 - checksum: ded86c0f0b138734110d67437fee52c1f97bc19175644788b1d71afec2d87d405cf05424ce428f88ae3abe8e09e13ee55f2675534b38076ef70e1e583ed75686 - languageName: node - linkType: hard - -"buffer-from@npm:^1.0.0": - version: 1.1.2 - resolution: "buffer-from@npm:1.1.2" - checksum: 0448524a562b37d4d7ed9efd91685a5b77a50672c556ea254ac9a6d30e3403a517d8981f10e565db24e8339413b43c97ca2951f10e399c6125a0d8911f5679bb - languageName: node - linkType: hard - -"cacache@npm:^20.0.1": - version: 20.0.3 - resolution: "cacache@npm:20.0.3" - dependencies: - "@npmcli/fs": ^5.0.0 - fs-minipass: ^3.0.0 - glob: ^13.0.0 - lru-cache: ^11.1.0 - minipass: ^7.0.3 - minipass-collect: ^2.0.1 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - p-map: ^7.0.2 - ssri: ^13.0.0 - unique-filename: ^5.0.0 - checksum: 595e6b91d72972d596e1e9ccab8ddbf08b773f27240220b1b5b1b7b3f52173cfbcf095212e5d7acd86c3bd453c28e69b116469889c511615ef3589523d542639 - languageName: node - linkType: hard - -"callsites@npm:^3.0.0": - version: 3.1.0 - resolution: "callsites@npm:3.1.0" - checksum: 072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 - languageName: node - linkType: hard - -"chalk@npm:^4.0.0": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc - languageName: node - linkType: hard - -"chownr@npm:^3.0.0": - version: 3.0.0 - resolution: "chownr@npm:3.0.0" - checksum: fd73a4bab48b79e66903fe1cafbdc208956f41ea4f856df883d0c7277b7ab29fd33ee65f93b2ec9192fc0169238f2f8307b7735d27c155821d886b84aa97aa8d - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: ~1.1.4 - checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 - languageName: node - linkType: hard - -"commander@npm:^2.20.0": - version: 2.20.3 - resolution: "commander@npm:2.20.3" - checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e - languageName: node - linkType: hard - -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.6": - version: 7.0.6 - resolution: "cross-spawn@npm:7.0.6" - dependencies: - path-key: ^3.1.0 - shebang-command: ^2.0.0 - which: ^2.0.1 - checksum: 8d306efacaf6f3f60e0224c287664093fa9185680b2d195852ba9a863f85d02dcc737094c6e512175f8ee0161f9b87c73c6826034c2422e39de7d6569cf4503b - languageName: node - linkType: hard - -"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": - version: 4.4.3 - resolution: "debug@npm:4.4.3" - dependencies: - ms: ^2.1.3 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 4805abd570e601acdca85b6aa3757186084a45cff9b2fa6eee1f3b173caa776b45f478b2a71a572d616d2010cea9211d0ac4a02a610e4c18ac4324bde3760834 - languageName: node - linkType: hard - -"deep-is@npm:^0.1.3": - version: 0.1.4 - resolution: "deep-is@npm:0.1.4" - checksum: edb65dd0d7d1b9c40b2f50219aef30e116cedd6fc79290e740972c132c09106d2e80aa0bc8826673dd5a00222d4179c84b36a790eef63a4c4bca75a37ef90804 - languageName: node - linkType: hard - -"env-paths@npm:^2.2.0": - version: 2.2.1 - resolution: "env-paths@npm:2.2.1" - checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e - languageName: node - linkType: hard - -"escape-string-regexp@npm:^4.0.0": - version: 4.0.0 - resolution: "escape-string-regexp@npm:4.0.0" - checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 - languageName: node - linkType: hard - -"eslint-scope@npm:^8.4.0": - version: 8.4.0 - resolution: "eslint-scope@npm:8.4.0" - dependencies: - esrecurse: ^4.3.0 - estraverse: ^5.2.0 - checksum: cf88f42cd5e81490d549dc6d350fe01e6fe420f9d9ea34f134bb359b030e3c4ef888d36667632e448937fe52449f7181501df48c08200e3d3b0fee250d05364e - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^3.4.3": - version: 3.4.3 - resolution: "eslint-visitor-keys@npm:3.4.3" - checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^4.2.1": - version: 4.2.1 - resolution: "eslint-visitor-keys@npm:4.2.1" - checksum: 3a77e3f99a49109f6fb2c5b7784bc78f9743b834d238cdba4d66c602c6b52f19ed7bcd0a5c5dbbeae3a8689fd785e76c001799f53d2228b278282cf9f699fff5 - languageName: node - linkType: hard - -"eslint@npm:^9.11.1": - version: 9.39.3 - resolution: "eslint@npm:9.39.3" - dependencies: - "@eslint-community/eslint-utils": ^4.8.0 - "@eslint-community/regexpp": ^4.12.1 - "@eslint/config-array": ^0.21.1 - "@eslint/config-helpers": ^0.4.2 - "@eslint/core": ^0.17.0 - "@eslint/eslintrc": ^3.3.1 - "@eslint/js": 9.39.3 - "@eslint/plugin-kit": ^0.4.1 - "@humanfs/node": ^0.16.6 - "@humanwhocodes/module-importer": ^1.0.1 - "@humanwhocodes/retry": ^0.4.2 - "@types/estree": ^1.0.6 - ajv: ^6.12.4 - chalk: ^4.0.0 - cross-spawn: ^7.0.6 - debug: ^4.3.2 - escape-string-regexp: ^4.0.0 - eslint-scope: ^8.4.0 - eslint-visitor-keys: ^4.2.1 - espree: ^10.4.0 - esquery: ^1.5.0 - esutils: ^2.0.2 - fast-deep-equal: ^3.1.3 - file-entry-cache: ^8.0.0 - find-up: ^5.0.0 - glob-parent: ^6.0.2 - ignore: ^5.2.0 - imurmurhash: ^0.1.4 - is-glob: ^4.0.0 - json-stable-stringify-without-jsonify: ^1.0.1 - lodash.merge: ^4.6.2 - minimatch: ^3.1.2 - natural-compare: ^1.4.0 - optionator: ^0.9.3 - peerDependencies: - jiti: "*" - peerDependenciesMeta: - jiti: - optional: true - bin: - eslint: bin/eslint.js - checksum: c242078b30198a1fb358adac08803553f7071bec76138f16977e64a49e0a5bcf7b41aea00fbeede0f7ed86c4c4f5744b2dd9a340567ecc5fdf779f2227651d57 - languageName: node - linkType: hard - -"esm@npm:^3.2.25": - version: 3.2.25 - resolution: "esm@npm:3.2.25" - checksum: 978aabe2de83541c105605a6d60a26ed8e627ef6bb0a7605fe15a95bbdea6b8348bd045255cb22219c054dd09a81a94823df00843d9e97f42419c92015ce3a64 - languageName: node - linkType: hard - -"espree@npm:^10.0.1, espree@npm:^10.4.0": - version: 10.4.0 - resolution: "espree@npm:10.4.0" - dependencies: - acorn: ^8.15.0 - acorn-jsx: ^5.3.2 - eslint-visitor-keys: ^4.2.1 - checksum: 5f9d0d7c81c1bca4bfd29a55270067ff9d575adb8c729a5d7f779c2c7b910bfc68ccf8ec19b29844b707440fc159a83868f22c8e87bbf7cbcb225ed067df6c85 - languageName: node - linkType: hard - -"esquery@npm:^1.5.0": - version: 1.7.0 - resolution: "esquery@npm:1.7.0" - dependencies: - estraverse: ^5.1.0 - checksum: 3239792b68cf39fe18966d0ca01549bb15556734f0144308fd213739b0f153671ae916013fce0bca032044a4dbcda98b43c1c667f20c20a54dec3597ac0d7c27 - languageName: node - linkType: hard - -"esrecurse@npm:^4.3.0": - version: 4.3.0 - resolution: "esrecurse@npm:4.3.0" - dependencies: - estraverse: ^5.2.0 - checksum: ebc17b1a33c51cef46fdc28b958994b1dc43cd2e86237515cbc3b4e5d2be6a811b2315d0a1a4d9d340b6d2308b15322f5c8291059521cc5f4802f65e7ec32837 - languageName: node - linkType: hard - -"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": - version: 5.3.0 - resolution: "estraverse@npm:5.3.0" - checksum: 072780882dc8416ad144f8fe199628d2b3e7bbc9989d9ed43795d2c90309a2047e6bc5979d7e2322a341163d22cfad9e21f4110597fe487519697389497e4e2b - languageName: node - linkType: hard - -"esutils@npm:^2.0.2": - version: 2.0.3 - resolution: "esutils@npm:2.0.3" - checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87 - languageName: node - linkType: hard - -"exponential-backoff@npm:^3.1.1": - version: 3.1.3 - resolution: "exponential-backoff@npm:3.1.3" - checksum: 471fdb70fd3d2c08a74a026973bdd4105b7832911f610ca67bbb74e39279411c1eed2f2a110c9d41c2edd89459ba58fdaba1c174beed73e7a42d773882dcff82 - languageName: node - linkType: hard - -"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": - version: 3.1.3 - resolution: "fast-deep-equal@npm:3.1.3" - checksum: e21a9d8d84f53493b6aa15efc9cfd53dd5b714a1f23f67fb5dc8f574af80df889b3bce25dc081887c6d25457cce704e636395333abad896ccdec03abaf1f3f9d - languageName: node - linkType: hard - -"fast-json-stable-stringify@npm:^2.0.0": - version: 2.1.0 - resolution: "fast-json-stable-stringify@npm:2.1.0" - checksum: b191531e36c607977e5b1c47811158733c34ccb3bfde92c44798929e9b4154884378536d26ad90dfecd32e1ffc09c545d23535ad91b3161a27ddbb8ebe0cbecb - languageName: node - linkType: hard - -"fast-levenshtein@npm:^2.0.6": - version: 2.0.6 - resolution: "fast-levenshtein@npm:2.0.6" - checksum: 92cfec0a8dfafd9c7a15fba8f2cc29cd0b62b85f056d99ce448bbcd9f708e18ab2764bda4dd5158364f4145a7c72788538994f0d1787b956ef0d1062b0f7c24c - languageName: node - linkType: hard - -"fdir@npm:^6.5.0": - version: 6.5.0 - resolution: "fdir@npm:6.5.0" - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - checksum: bd537daa9d3cd53887eed35efa0eab2dbb1ca408790e10e024120e7a36c6e9ae2b33710cb8381e35def01bc9c1d7eaba746f886338413e68ff6ebaee07b9a6e8 - languageName: node - linkType: hard - -"file-entry-cache@npm:^8.0.0": - version: 8.0.0 - resolution: "file-entry-cache@npm:8.0.0" - dependencies: - flat-cache: ^4.0.0 - checksum: f67802d3334809048c69b3d458f672e1b6d26daefda701761c81f203b80149c35dea04d78ea4238969dd617678e530876722a0634c43031a0957f10cc3ed190f - languageName: node - linkType: hard - -"find-up@npm:^5.0.0": - version: 5.0.0 - resolution: "find-up@npm:5.0.0" - dependencies: - locate-path: ^6.0.0 - path-exists: ^4.0.0 - checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 - languageName: node - linkType: hard - -"flat-cache@npm:^4.0.0": - version: 4.0.1 - resolution: "flat-cache@npm:4.0.1" - dependencies: - flatted: ^3.2.9 - keyv: ^4.5.4 - checksum: 899fc86bf6df093547d76e7bfaeb900824b869d7d457d02e9b8aae24836f0a99fbad79328cfd6415ee8908f180699bf259dc7614f793447cb14f707caf5996f6 - languageName: node - linkType: hard - -"flatted@npm:^3.2.9": - version: 3.3.3 - resolution: "flatted@npm:3.3.3" - checksum: 8c96c02fbeadcf4e8ffd0fa24983241e27698b0781295622591fc13585e2f226609d95e422bcf2ef044146ffacb6b68b1f20871454eddf75ab3caa6ee5f4a1fe - languageName: node - linkType: hard - -"fs-minipass@npm:^3.0.0": - version: 3.0.3 - resolution: "fs-minipass@npm:3.0.3" - dependencies: - minipass: ^7.0.3 - checksum: 8722a41109130851d979222d3ec88aabaceeaaf8f57b2a8f744ef8bd2d1ce95453b04a61daa0078822bc5cd21e008814f06fe6586f56fef511e71b8d2394d802 - languageName: node - linkType: hard - -"fsevents@npm:~2.3.2": - version: 2.3.3 - resolution: "fsevents@npm:2.3.3" - dependencies: - node-gyp: latest - checksum: 11e6ea6fea15e42461fc55b4b0e4a0a3c654faa567f1877dbd353f39156f69def97a69936d1746619d656c4b93de2238bf731f6085a03a50cabf287c9d024317 - conditions: os=darwin - languageName: node - linkType: hard - -"fsevents@patch:fsevents@~2.3.2#~builtin": - version: 2.3.3 - resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=df0bf1" - dependencies: - node-gyp: latest - conditions: os=darwin - languageName: node - linkType: hard - -"glob-parent@npm:^6.0.2": - version: 6.0.2 - resolution: "glob-parent@npm:6.0.2" - dependencies: - is-glob: ^4.0.3 - checksum: c13ee97978bef4f55106b71e66428eb1512e71a7466ba49025fc2aec59a5bfb0954d5abd58fc5ee6c9b076eef4e1f6d3375c2e964b88466ca390da4419a786a8 - languageName: node - linkType: hard - -"glob@npm:^13.0.0": - version: 13.0.6 - resolution: "glob@npm:13.0.6" - dependencies: - minimatch: ^10.2.2 - minipass: ^7.1.3 - path-scurry: ^2.0.2 - checksum: 1eb421c696c66af3c26e4845dbdd222d3b982ede17448456b49272722d872e9a91741b50e4e827370c57d17a39a69790061f45033523f085c076d8fcc0f69d2b - languageName: node - linkType: hard - -"globals@npm:^14.0.0": - version: 14.0.0 - resolution: "globals@npm:14.0.0" - checksum: 534b8216736a5425737f59f6e6a5c7f386254560c9f41d24a9227d60ee3ad4a9e82c5b85def0e212e9d92162f83a92544be4c7fd4c902cb913736c10e08237ac - languageName: node - linkType: hard - -"globals@npm:^15.9.0": - version: 15.15.0 - resolution: "globals@npm:15.15.0" - checksum: a2a92199a112db00562a2f85eeef2a7e3943e171f7f7d9b17dfa9231e35fd612588f3c199d1509ab1757273467e413b08c80424cf6e399e96acdaf93deb3ee88 - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.6": - version: 4.2.11 - resolution: "graceful-fs@npm:4.2.11" - checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 - languageName: node - linkType: hard - -"has-flag@npm:^4.0.0": - version: 4.0.0 - resolution: "has-flag@npm:4.0.0" - checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad - languageName: node - linkType: hard - -"http-cache-semantics@npm:^4.1.1": - version: 4.2.0 - resolution: "http-cache-semantics@npm:4.2.0" - checksum: 7a7246ddfce629f96832791176fd643589d954e6f3b49548dadb4290451961237fab8fcea41cd2008fe819d95b41c1e8b97f47d088afc0a1c81705287b4ddbcc - languageName: node - linkType: hard - -"http-proxy-agent@npm:^7.0.0": - version: 7.0.2 - resolution: "http-proxy-agent@npm:7.0.2" - dependencies: - agent-base: ^7.1.0 - debug: ^4.3.4 - checksum: 670858c8f8f3146db5889e1fa117630910101db601fff7d5a8aa637da0abedf68c899f03d3451cac2f83bcc4c3d2dabf339b3aa00ff8080571cceb02c3ce02f3 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.1": - version: 7.0.6 - resolution: "https-proxy-agent@npm:7.0.6" - dependencies: - agent-base: ^7.1.2 - debug: 4 - checksum: b882377a120aa0544846172e5db021fa8afbf83fea2a897d397bd2ddd8095ab268c24bc462f40a15f2a8c600bf4aa05ce52927f70038d4014e68aefecfa94e8d - languageName: node - linkType: hard - -"iconv-lite@npm:^0.7.2": - version: 0.7.2 - resolution: "iconv-lite@npm:0.7.2" - dependencies: - safer-buffer: ">= 2.1.2 < 3.0.0" - checksum: faf884c1f631a5d676e3e64054bed891c7c5f616b790082d99ccfbfd017c661a39db8009160268fd65fae57c9154d4d491ebc9c301f3446a078460ef114dc4b8 - languageName: node - linkType: hard - -"ignore@npm:^5.2.0": - version: 5.3.2 - resolution: "ignore@npm:5.3.2" - checksum: 2acfd32a573260ea522ea0bfeff880af426d68f6831f973129e2ba7363f422923cf53aab62f8369cbf4667c7b25b6f8a3761b34ecdb284ea18e87a5262a865be - languageName: node - linkType: hard - -"import-fresh@npm:^3.2.1": - version: 3.3.1 - resolution: "import-fresh@npm:3.3.1" - dependencies: - parent-module: ^1.0.0 - resolve-from: ^4.0.0 - checksum: a06b19461b4879cc654d46f8a6244eb55eb053437afd4cbb6613cad6be203811849ed3e4ea038783092879487299fda24af932b86bdfff67c9055ba3612b8c87 - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 7cae75c8cd9a50f57dadd77482359f659eaebac0319dd9368bcd1714f55e65badd6929ca58569da2b6494ef13fdd5598cd700b1eba23f8b79c5f19d195a3ecf7 - languageName: node - linkType: hard - -"ip-address@npm:^10.0.1": - version: 10.1.0 - resolution: "ip-address@npm:10.1.0" - checksum: 76b1abcdf52a32e2e05ca1f202f3a8ab8547e5651a9233781b330271bd7f1a741067748d71c4cbb9d9906d9f1fa69e7ddc8b4a11130db4534fdab0e908c84e0d - languageName: node - linkType: hard - -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 - languageName: node - linkType: hard - -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.3": - version: 4.0.3 - resolution: "is-glob@npm:4.0.3" - dependencies: - is-extglob: ^2.1.1 - checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 - languageName: node - linkType: hard - -"isexe@npm:^4.0.0": - version: 4.0.0 - resolution: "isexe@npm:4.0.0" - checksum: 2ead327ef596042ef9c9ec5f236b316acfaedb87f4bb61b3c3d574fb2e9c8a04b67305e04733bde52c24d9622fdebd3270aadb632adfbf9cadef88fe30f479e5 - languageName: node - linkType: hard - -"js-yaml@npm:^4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" - dependencies: - argparse: ^2.0.1 - bin: - js-yaml: bin/js-yaml.js - checksum: ea2339c6930fe048ec31b007b3c90be2714ab3e7defcc2c27ebf30c74fd940358f29070b4345af0019ef151875bf3bc3f8644bea1bab0372652b5044813ac02d - languageName: node - linkType: hard - -"json-buffer@npm:3.0.1": - version: 3.0.1 - resolution: "json-buffer@npm:3.0.1" - checksum: 9026b03edc2847eefa2e37646c579300a1f3a4586cfb62bf857832b60c852042d0d6ae55d1afb8926163fa54c2b01d83ae24705f34990348bdac6273a29d4581 - languageName: node - linkType: hard - -"json-schema-traverse@npm:^0.4.1": - version: 0.4.1 - resolution: "json-schema-traverse@npm:0.4.1" - checksum: 7486074d3ba247769fda17d5181b345c9fb7d12e0da98b22d1d71a5db9698d8b4bd900a3ec1a4ffdd60846fc2556274a5c894d0c48795f14cb03aeae7b55260b - languageName: node - linkType: hard - -"json-stable-stringify-without-jsonify@npm:^1.0.1": - version: 1.0.1 - resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" - checksum: cff44156ddce9c67c44386ad5cddf91925fe06b1d217f2da9c4910d01f358c6e3989c4d5a02683c7a5667f9727ff05831f7aa8ae66c8ff691c556f0884d49215 - languageName: node - linkType: hard - -"keyv@npm:^4.5.4": - version: 4.5.4 - resolution: "keyv@npm:4.5.4" - dependencies: - json-buffer: 3.0.1 - checksum: 74a24395b1c34bd44ad5cb2b49140d087553e170625240b86755a6604cd65aa16efdbdeae5cdb17ba1284a0fbb25ad06263755dbc71b8d8b06f74232ce3cdd72 - languageName: node - linkType: hard - -"levn@npm:^0.4.1": - version: 0.4.1 - resolution: "levn@npm:0.4.1" - dependencies: - prelude-ls: ^1.2.1 - type-check: ~0.4.0 - checksum: 12c5021c859bd0f5248561bf139121f0358285ec545ebf48bb3d346820d5c61a4309535c7f387ed7d84361cf821e124ce346c6b7cef8ee09a67c1473b46d0fc4 - languageName: node - linkType: hard - -"locate-path@npm:^6.0.0": - version: 6.0.0 - resolution: "locate-path@npm:6.0.0" - dependencies: - p-locate: ^5.0.0 - checksum: 72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a - languageName: node - linkType: hard - -"lodash.merge@npm:^4.6.2": - version: 4.6.2 - resolution: "lodash.merge@npm:4.6.2" - checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 - languageName: node - linkType: hard - -"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": - version: 11.2.6 - resolution: "lru-cache@npm:11.2.6" - checksum: 26fe602c92a0cb7a8da9a85db162ddd810d84507d9c4ef8d95a785a805648f9579e1148aaeac260f6b6315197bcf27c1b7e60a0a066621d6e95b3587699a0c70 - languageName: node - linkType: hard - -"make-fetch-happen@npm:^15.0.0": - version: 15.0.4 - resolution: "make-fetch-happen@npm:15.0.4" - dependencies: - "@gar/promise-retry": ^1.0.0 - "@npmcli/agent": ^4.0.0 - cacache: ^20.0.1 - http-cache-semantics: ^4.1.1 - minipass: ^7.0.2 - minipass-fetch: ^5.0.0 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - negotiator: ^1.0.0 - proc-log: ^6.0.0 - ssri: ^13.0.0 - checksum: eb875e1fbdf58b4d6d7c6c2ee97a2521b407f85d1fb2c0e6bc2c2d9d13fa34c3198eff3512589dc26e8fb32684e4e52f3c9d844a704ac96b393559a88b5e57ca - languageName: node - linkType: hard - -"minimatch@npm:^10.2.2": - version: 10.2.4 - resolution: "minimatch@npm:10.2.4" - dependencies: - brace-expansion: ^5.0.2 - checksum: 56dce6b04c6b30b500d81d7a29822c108b7d58c46696ec7332d04a2bd104a5cb69e5c7ce93e1783dc66d61400d831e6e226ca101ac23665aff32ca303619dc3d - languageName: node - linkType: hard - -"minimatch@npm:^3.1.2, minimatch@npm:^3.1.3": - version: 3.1.5 - resolution: "minimatch@npm:3.1.5" - dependencies: - brace-expansion: ^1.1.7 - checksum: 47ef6f412c08be045a7291d11b1c40777925accf7252dc6d3caa39b1bfbb3a7ea390ba7aba464d762d783265c644143d2c8a204e6b5763145024d52ee65a1941 - languageName: node - linkType: hard - -"minipass-collect@npm:^2.0.1": - version: 2.0.1 - resolution: "minipass-collect@npm:2.0.1" - dependencies: - minipass: ^7.0.3 - checksum: b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 - languageName: node - linkType: hard - -"minipass-fetch@npm:^5.0.0": - version: 5.0.2 - resolution: "minipass-fetch@npm:5.0.2" - dependencies: - iconv-lite: ^0.7.2 - minipass: ^7.0.3 - minipass-sized: ^2.0.0 - minizlib: ^3.0.1 - dependenciesMeta: - iconv-lite: - optional: true - checksum: d4dfdd9700fc8aba445834f75f2abaf9e5d404c10eda06e2db4a8ba89fc66a26956d19703d0edf9be864cb30dec22356d343509ad0a105446516c0ead4330328 - languageName: node - linkType: hard - -"minipass-flush@npm:^1.0.5": - version: 1.0.5 - resolution: "minipass-flush@npm:1.0.5" - dependencies: - minipass: ^3.0.0 - checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf - languageName: node - linkType: hard - -"minipass-pipeline@npm:^1.2.4": - version: 1.2.4 - resolution: "minipass-pipeline@npm:1.2.4" - dependencies: - minipass: ^3.0.0 - checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b - languageName: node - linkType: hard - -"minipass-sized@npm:^2.0.0": - version: 2.0.0 - resolution: "minipass-sized@npm:2.0.0" - dependencies: - minipass: ^7.1.2 - checksum: 1a1fd251aef4e24050a04ea03fdc0514960f7304a374fd01f352bfdb72c0a2c084ad05d63e76011c181cadfb38dbf487f8782e1e778337f6a099ac2da26b6d5d - languageName: node - linkType: hard - -"minipass@npm:^3.0.0": - version: 3.3.6 - resolution: "minipass@npm:3.3.6" - dependencies: - yallist: ^4.0.0 - checksum: a30d083c8054cee83cdcdc97f97e4641a3f58ae743970457b1489ce38ee1167b3aaf7d815cd39ec7a99b9c40397fd4f686e83750e73e652b21cb516f6d845e48 - languageName: node - linkType: hard - -"minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3": - version: 7.1.3 - resolution: "minipass@npm:7.1.3" - checksum: 2ede17c0bf8fec499be3360fd07f0ec7666189e3907320a9b653f1530cf84af98928c5b12d80bfb75f321833bf2e97785b940540213ebdafe97a5f10327e664d - languageName: node - linkType: hard - -"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": - version: 3.1.0 - resolution: "minizlib@npm:3.1.0" - dependencies: - minipass: ^7.1.2 - checksum: a15e6f0128f514b7d41a1c68ce531155447f4669e32d279bba1c1c071ef6c2abd7e4d4579bb59ccc2ed1531346749665968fdd7be8d83eb6b6ae2fe1f3d370a7 - languageName: node - linkType: hard - -"ms@npm:^2.1.3": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d - languageName: node - linkType: hard - -"natural-compare@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare@npm:1.4.0" - checksum: 23ad088b08f898fc9b53011d7bb78ec48e79de7627e01ab5518e806033861bef68d5b0cd0e2205c2f36690ac9571ff6bcb05eb777ced2eeda8d4ac5b44592c3d - languageName: node - linkType: hard - -"negotiator@npm:^1.0.0": - version: 1.0.0 - resolution: "negotiator@npm:1.0.0" - checksum: 20ebfe79b2d2e7cf9cbc8239a72662b584f71164096e6e8896c8325055497c96f6b80cd22c258e8a2f2aa382a787795ec3ee8b37b422a302c7d4381b0d5ecfbb - languageName: node - linkType: hard - -"node-gyp@npm:latest": - version: 12.2.0 - resolution: "node-gyp@npm:12.2.0" - dependencies: - env-paths: ^2.2.0 - exponential-backoff: ^3.1.1 - graceful-fs: ^4.2.6 - make-fetch-happen: ^15.0.0 - nopt: ^9.0.0 - proc-log: ^6.0.0 - semver: ^7.3.5 - tar: ^7.5.4 - tinyglobby: ^0.2.12 - which: ^6.0.0 - bin: - node-gyp: bin/node-gyp.js - checksum: d4ce0acd08bd41004f45e10cef468f4bd15eaafb3acc388a0c567416e1746dc005cc080b8a3495e4e2ae2eed170a2123ff622c2d6614062f4a839837dcf1dd9d - languageName: node - linkType: hard - -"nopt@npm:^9.0.0": - version: 9.0.0 - resolution: "nopt@npm:9.0.0" - dependencies: - abbrev: ^4.0.0 - bin: - nopt: bin/nopt.js - checksum: 7a5d9ab0629eaec1944a95438cc4efa6418ed2834aa8eb21a1bea579a7d8ac3e30120131855376a96ef59ab0e23ad8e0bc94d3349770a95e5cb7119339f7c7fb - languageName: node - linkType: hard - -"optionator@npm:^0.9.3": - version: 0.9.4 - resolution: "optionator@npm:0.9.4" - dependencies: - deep-is: ^0.1.3 - fast-levenshtein: ^2.0.6 - levn: ^0.4.1 - prelude-ls: ^1.2.1 - type-check: ^0.4.0 - word-wrap: ^1.2.5 - checksum: ecbd010e3dc73e05d239976422d9ef54a82a13f37c11ca5911dff41c98a6c7f0f163b27f922c37e7f8340af9d36febd3b6e9cef508f3339d4c393d7276d716bb - languageName: node - linkType: hard - -"p-limit@npm:^3.0.2": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" - dependencies: - yocto-queue: ^0.1.0 - checksum: 7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 - languageName: node - linkType: hard - -"p-locate@npm:^5.0.0": - version: 5.0.0 - resolution: "p-locate@npm:5.0.0" - dependencies: - p-limit: ^3.0.2 - checksum: 1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 - languageName: node - linkType: hard - -"p-map@npm:^7.0.2": - version: 7.0.4 - resolution: "p-map@npm:7.0.4" - checksum: 4be2097e942f2fd3a4f4b0c6585c721f23851de8ad6484d20c472b3ea4937d5cd9a59914c832b1bceac7bf9d149001938036b82a52de0bc381f61ff2d35d26a5 - languageName: node - linkType: hard - -"parent-module@npm:^1.0.0": - version: 1.0.1 - resolution: "parent-module@npm:1.0.1" - dependencies: - callsites: ^3.0.0 - checksum: 6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff - languageName: node - linkType: hard - -"path-exists@npm:^4.0.0": - version: 4.0.0 - resolution: "path-exists@npm:4.0.0" - checksum: 505807199dfb7c50737b057dd8d351b82c033029ab94cb10a657609e00c1bc53b951cfdbccab8de04c5584d5eff31128ce6afd3db79281874a5ef2adbba55ed1 - languageName: node - linkType: hard - -"path-key@npm:^3.1.0": - version: 3.1.1 - resolution: "path-key@npm:3.1.1" - checksum: 55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 - languageName: node - linkType: hard - -"path-scurry@npm:^2.0.2": - version: 2.0.2 - resolution: "path-scurry@npm:2.0.2" - dependencies: - lru-cache: ^11.0.0 - minipass: ^7.1.2 - checksum: a723afe86e342e19dd1b49ce4f5b64a9a84b1e2e07ffc62f051c11623ecd461b1bf1599eee1ecacfce03dda8b6bb866a5df80c0ded45375d258ff22f631920a7 - languageName: node - linkType: hard - -"picomatch@npm:^4.0.3": - version: 4.0.3 - resolution: "picomatch@npm:4.0.3" - checksum: 6817fb74eb745a71445debe1029768de55fd59a42b75606f478ee1d0dc1aa6e78b711d041a7c9d5550e042642029b7f373dc1a43b224c4b7f12d23436735dba0 - languageName: node - linkType: hard - -"prelude-ls@npm:^1.2.1": - version: 1.2.1 - resolution: "prelude-ls@npm:1.2.1" - checksum: cd192ec0d0a8e4c6da3bb80e4f62afe336df3f76271ac6deb0e6a36187133b6073a19e9727a1ff108cd8b9982e4768850d413baa71214dd80c7979617dca827a - languageName: node - linkType: hard - -"proc-log@npm:^6.0.0": - version: 6.1.0 - resolution: "proc-log@npm:6.1.0" - checksum: ac450ff8244e95b0c9935b52d629fef92ae69b7e39aea19972a8234259614d644402dd62ce9cb094f4a637d8a4514cba90c1456ad785a40ad5b64d502875a817 - languageName: node - linkType: hard - -"punycode@npm:^2.1.0": - version: 2.3.1 - resolution: "punycode@npm:2.3.1" - checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 - languageName: node - linkType: hard - -"resolve-from@npm:^4.0.0": - version: 4.0.0 - resolution: "resolve-from@npm:4.0.0" - checksum: f4ba0b8494846a5066328ad33ef8ac173801a51739eb4d63408c847da9a2e1c1de1e6cbbf72699211f3d13f8fc1325648b169bd15eb7da35688e30a5fb0e4a7f - languageName: node - linkType: hard - -"retry@npm:^0.13.1": - version: 0.13.1 - resolution: "retry@npm:0.13.1" - checksum: 47c4d5be674f7c13eee4cfe927345023972197dbbdfba5d3af7e461d13b44de1bfd663bfc80d2f601f8ef3fc8164c16dd99655a221921954a65d044a2fc1233b - languageName: node - linkType: hard - -"rollup@npm:^4.59.0": - version: 4.59.0 - resolution: "rollup@npm:4.59.0" - dependencies: - "@rollup/rollup-android-arm-eabi": 4.59.0 - "@rollup/rollup-android-arm64": 4.59.0 - "@rollup/rollup-darwin-arm64": 4.59.0 - "@rollup/rollup-darwin-x64": 4.59.0 - "@rollup/rollup-freebsd-arm64": 4.59.0 - "@rollup/rollup-freebsd-x64": 4.59.0 - "@rollup/rollup-linux-arm-gnueabihf": 4.59.0 - "@rollup/rollup-linux-arm-musleabihf": 4.59.0 - "@rollup/rollup-linux-arm64-gnu": 4.59.0 - "@rollup/rollup-linux-arm64-musl": 4.59.0 - "@rollup/rollup-linux-loong64-gnu": 4.59.0 - "@rollup/rollup-linux-loong64-musl": 4.59.0 - "@rollup/rollup-linux-ppc64-gnu": 4.59.0 - "@rollup/rollup-linux-ppc64-musl": 4.59.0 - "@rollup/rollup-linux-riscv64-gnu": 4.59.0 - "@rollup/rollup-linux-riscv64-musl": 4.59.0 - "@rollup/rollup-linux-s390x-gnu": 4.59.0 - "@rollup/rollup-linux-x64-gnu": 4.59.0 - "@rollup/rollup-linux-x64-musl": 4.59.0 - "@rollup/rollup-openbsd-x64": 4.59.0 - "@rollup/rollup-openharmony-arm64": 4.59.0 - "@rollup/rollup-win32-arm64-msvc": 4.59.0 - "@rollup/rollup-win32-ia32-msvc": 4.59.0 - "@rollup/rollup-win32-x64-gnu": 4.59.0 - "@rollup/rollup-win32-x64-msvc": 4.59.0 - "@types/estree": 1.0.8 - fsevents: ~2.3.2 - dependenciesMeta: - "@rollup/rollup-android-arm-eabi": - optional: true - "@rollup/rollup-android-arm64": - optional: true - "@rollup/rollup-darwin-arm64": - optional: true - "@rollup/rollup-darwin-x64": - optional: true - "@rollup/rollup-freebsd-arm64": - optional: true - "@rollup/rollup-freebsd-x64": - optional: true - "@rollup/rollup-linux-arm-gnueabihf": - optional: true - "@rollup/rollup-linux-arm-musleabihf": - optional: true - "@rollup/rollup-linux-arm64-gnu": - optional: true - "@rollup/rollup-linux-arm64-musl": - optional: true - "@rollup/rollup-linux-loong64-gnu": - optional: true - "@rollup/rollup-linux-loong64-musl": - optional: true - "@rollup/rollup-linux-ppc64-gnu": - optional: true - "@rollup/rollup-linux-ppc64-musl": - optional: true - "@rollup/rollup-linux-riscv64-gnu": - optional: true - "@rollup/rollup-linux-riscv64-musl": - optional: true - "@rollup/rollup-linux-s390x-gnu": - optional: true - "@rollup/rollup-linux-x64-gnu": - optional: true - "@rollup/rollup-linux-x64-musl": - optional: true - "@rollup/rollup-openbsd-x64": - optional: true - "@rollup/rollup-openharmony-arm64": - optional: true - "@rollup/rollup-win32-arm64-msvc": - optional: true - "@rollup/rollup-win32-ia32-msvc": - optional: true - "@rollup/rollup-win32-x64-gnu": - optional: true - "@rollup/rollup-win32-x64-msvc": - optional: true - fsevents: - optional: true - bin: - rollup: dist/bin/rollup - checksum: c2427c6907f05bb98c92064c1660bbeaebb11f3022ec853635e6a994f8e1d39ed18ccee8d70b219954787dca0a2eb3082941bd2c3e054071eec2569acc1c1509 - languageName: node - linkType: hard - -"safer-buffer@npm:>= 2.1.2 < 3.0.0": - version: 2.1.2 - resolution: "safer-buffer@npm:2.1.2" - checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 - languageName: node - linkType: hard - -"semver@npm:^7.3.5": - version: 7.7.4 - resolution: "semver@npm:7.7.4" - bin: - semver: bin/semver.js - checksum: 9b4a6a58e98b9723fafcafa393c9d4e8edefaa60b8dfbe39e30892a3604cf1f45f52df9cfb1ae1a22b44c8b3d57fec8a9bb7b3e1645431587cb272399ede152e - languageName: node - linkType: hard - -"shebang-command@npm:^2.0.0": - version: 2.0.0 - resolution: "shebang-command@npm:2.0.0" - dependencies: - shebang-regex: ^3.0.0 - checksum: 6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa - languageName: node - linkType: hard - -"shebang-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "shebang-regex@npm:3.0.0" - checksum: 1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 - languageName: node - linkType: hard - -"smart-buffer@npm:^4.2.0": - version: 4.2.0 - resolution: "smart-buffer@npm:4.2.0" - checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^8.0.3": - version: 8.0.5 - resolution: "socks-proxy-agent@npm:8.0.5" - dependencies: - agent-base: ^7.1.2 - debug: ^4.3.4 - socks: ^2.8.3 - checksum: b4fbcdb7ad2d6eec445926e255a1fb95c975db0020543fbac8dfa6c47aecc6b3b619b7fb9c60a3f82c9b2969912a5e7e174a056ae4d98cb5322f3524d6036e1d - languageName: node - linkType: hard - -"socks@npm:^2.8.3": - version: 2.8.7 - resolution: "socks@npm:2.8.7" - dependencies: - ip-address: ^10.0.1 - smart-buffer: ^4.2.0 - checksum: 4bbe2c88cf0eeaf49f94b7f11564a99b2571bde6fd1e714ff95b38f89e1f97858c19e0ab0e6d39eb7f6a984fa67366825895383ed563fe59962a1d57a1d55318 - languageName: node - linkType: hard - -"source-map-support@npm:~0.5.20": - version: 0.5.21 - resolution: "source-map-support@npm:0.5.21" - dependencies: - buffer-from: ^1.0.0 - source-map: ^0.6.0 - checksum: 43e98d700d79af1d36f859bdb7318e601dfc918c7ba2e98456118ebc4c4872b327773e5a1df09b0524e9e5063bb18f0934538eace60cca2710d1fa687645d137 - languageName: node - linkType: hard - -"source-map@npm:^0.6.0": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 - languageName: node - linkType: hard - -"ssri@npm:^13.0.0": - version: 13.0.1 - resolution: "ssri@npm:13.0.1" - dependencies: - minipass: ^7.0.3 - checksum: 42acbdbd485e9a5a198de2198b6fd474d1e84bff6bea5d95aa0a8aa26ea78ce44f2097ac481e767f0406de7ceccfa4669584116d4fcf2d4e2dba7034d7c34930 - languageName: node - linkType: hard - -"strip-json-comments@npm:^3.1.1": - version: 3.1.1 - resolution: "strip-json-comments@npm:3.1.1" - checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 - languageName: node - linkType: hard - -"supports-color@npm:^7.1.0": - version: 7.2.0 - resolution: "supports-color@npm:7.2.0" - dependencies: - has-flag: ^4.0.0 - checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a - languageName: node - linkType: hard - -"tar@npm:^7.5.4": - version: 7.5.9 - resolution: "tar@npm:7.5.9" - dependencies: - "@isaacs/fs-minipass": ^4.0.0 - chownr: ^3.0.0 - minipass: ^7.1.2 - minizlib: ^3.1.0 - yallist: ^5.0.0 - checksum: 26fbbdf536895814167d03e4883f80febb6520729169c54d0f29ee8a163557283862752493f0e5b60800a6f3608aac3250c41fac8e20a4f056ba4fa63f3dbad7 - languageName: node - linkType: hard - -"temml@workspace:.": - version: 0.0.0-use.local - resolution: "temml@workspace:." - dependencies: - "@eslint/eslintrc": ^3.1.0 - "@eslint/js": ^9.11.1 - eslint: ^9.11.1 - esm: ^3.2.25 - globals: ^15.9.0 - rollup: ^4.59.0 - terser: ^5.34.0 - languageName: unknown - linkType: soft - -"terser@npm:^5.34.0": - version: 5.46.0 - resolution: "terser@npm:5.46.0" - dependencies: - "@jridgewell/source-map": ^0.3.3 - acorn: ^8.15.0 - commander: ^2.20.0 - source-map-support: ~0.5.20 - bin: - terser: bin/terser - checksum: 39d28f3723e84e80ddb4576a441adb12a6d365258fb9262e25f8b6d1e4514954e81f711008ee2ad9927f00b860a5bcbd4c1db7a6873d0f712bdcc667fb7b7557 - languageName: node - linkType: hard - -"tinyglobby@npm:^0.2.12": - version: 0.2.15 - resolution: "tinyglobby@npm:0.2.15" - dependencies: - fdir: ^6.5.0 - picomatch: ^4.0.3 - checksum: 0e33b8babff966c6ab86e9b825a350a6a98a63700fa0bb7ae6cf36a7770a508892383adc272f7f9d17aaf46a9d622b455e775b9949a3f951eaaf5dfb26331d44 - languageName: node - linkType: hard - -"type-check@npm:^0.4.0, type-check@npm:~0.4.0": - version: 0.4.0 - resolution: "type-check@npm:0.4.0" - dependencies: - prelude-ls: ^1.2.1 - checksum: ec688ebfc9c45d0c30412e41ca9c0cdbd704580eb3a9ccf07b9b576094d7b86a012baebc95681999dd38f4f444afd28504cb3a89f2ef16b31d4ab61a0739025a - languageName: node - linkType: hard - -"unique-filename@npm:^5.0.0": - version: 5.0.0 - resolution: "unique-filename@npm:5.0.0" - dependencies: - unique-slug: ^6.0.0 - checksum: a5f67085caef74bdd2a6869a200ed5d68d171f5cc38435a836b5fd12cce4e4eb55e6a190298035c325053a5687ed7a3c96f0a91e82215fd14729769d9ac57d9b - languageName: node - linkType: hard - -"unique-slug@npm:^6.0.0": - version: 6.0.0 - resolution: "unique-slug@npm:6.0.0" - dependencies: - imurmurhash: ^0.1.4 - checksum: ad6cf238b10292d944521714d31bc9f3ca79fa80cb7a154aad183056493f98e85de669412c6bbfe527ffa9bdeff36d3dd4d5bccaf562c794f2580ab11932b691 - languageName: node - linkType: hard - -"uri-js@npm:^4.2.2": - version: 4.4.1 - resolution: "uri-js@npm:4.4.1" - dependencies: - punycode: ^2.1.0 - checksum: 7167432de6817fe8e9e0c9684f1d2de2bb688c94388f7569f7dbdb1587c9f4ca2a77962f134ec90be0cc4d004c939ff0d05acc9f34a0db39a3c797dada262633 - languageName: node - linkType: hard - -"which@npm:^2.0.1": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: ^2.0.0 - bin: - node-which: ./bin/node-which - checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 - languageName: node - linkType: hard - -"which@npm:^6.0.0": - version: 6.0.1 - resolution: "which@npm:6.0.1" - dependencies: - isexe: ^4.0.0 - bin: - node-which: bin/which.js - checksum: dbea77c7d3058bf6c78bf9659d2dce4d2b57d39a15b826b2af6ac2e5a219b99dc8a831b79fdbc453c0598adb4f3f84cf9c2491fd52beb9f5d2dececcad117f68 - languageName: node - linkType: hard - -"word-wrap@npm:^1.2.5": - version: 1.2.5 - resolution: "word-wrap@npm:1.2.5" - checksum: f93ba3586fc181f94afdaff3a6fef27920b4b6d9eaefed0f428f8e07adea2a7f54a5f2830ce59406c8416f033f86902b91eb824072354645eea687dff3691ccb - languageName: node - linkType: hard - -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 - languageName: node - linkType: hard - -"yallist@npm:^5.0.0": - version: 5.0.0 - resolution: "yallist@npm:5.0.0" - checksum: eba51182400b9f35b017daa7f419f434424410691bbc5de4f4240cc830fdef906b504424992700dc047f16b4d99100a6f8b8b11175c193f38008e9c96322b6a5 - languageName: node - linkType: hard - -"yocto-queue@npm:^0.1.0": - version: 0.1.0 - resolution: "yocto-queue@npm:0.1.0" - checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 - languageName: node - linkType: hard