From 9a0b231e7e2705447d405a433a6decaa2b40791a Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:44:00 +1100 Subject: [PATCH 1/9] add temml-default.js --- temml-default.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 temml-default.js diff --git a/temml-default.js b/temml-default.js new file mode 100644 index 0000000..f28be8a --- /dev/null +++ b/temml-default.js @@ -0,0 +1,7 @@ +/** + * An entry point that only has the default export of Temml. + * This is used for the iife and CommonJS builds. + */ + +import { default as Temml } from "./temml.js"; +export default Temml; From b17c5dea414734dfe0f87cf14ed727de9cf68f27 Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:44:06 +1100 Subject: [PATCH 2/9] update dts --- temml.d.ts | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/temml.d.ts b/temml.d.ts index 7ed1f01..cdfeae4 100644 --- a/temml.d.ts +++ b/temml.d.ts @@ -14,21 +14,19 @@ export interface Options { maxExpand?: number; } -export function render( - expression: string, - baseNode: HTMLElement | MathMLElement, - options?: Options, -): void; +declare const version: string; + +declare function render(expression: string, baseNode: HTMLElement | MathMLElement, options?: Options): void; -export function renderToString(expression: string, options?: Options): string; +declare function renderToString(expression: string, options?: Options): string; -export function renderMathInElement(block: any, options?: Options): void; +declare function renderMathInElement(block: any, options?: Options): void; -export function generateParseTree(expression: string, options?: Options): any; +declare function generateParseTree(expression: string, options?: Options): any; -export function definePreamble(expression: string, options?: Options): any; +declare function definePreamble(expression: string, options?: Options): any; -export function renderToMathMLTree(expression: string, options?: Options): any; +declare function renderToMathMLTree(expression: string, options?: Options): any; declare function postProcess(block: any): void; declare function defineMacro(name: string, body: any): void; @@ -46,6 +44,20 @@ declare class ParseError { ); } +export { + version, + render, + renderToString, + renderMathInElement, + postProcess, + ParseError, + definePreamble, + generateParseTree as __parse, + renderToMathMLTree as __renderToMathMLTree, + defineSymbol as __defineSymbol, + defineMacro as __defineMacro +} + declare const Temml: { version: string; render: typeof render; From cffd6f2f5a0e3650d71afcff07b9544dd7da37d7 Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:44:13 +1100 Subject: [PATCH 3/9] update js --- temml.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/temml.js b/temml.js index 742d144..a7f2ec0 100644 --- a/temml.js +++ b/temml.js @@ -69,7 +69,7 @@ const renderToString = function(expression, options) { }; /** - * @type {import('./temml').generateParseTree} + * @type {import('./temml').__parse} * Parse an expression and return the parse tree. */ const generateParseTree = function(expression, options) { @@ -111,7 +111,7 @@ const renderError = function(error, expression, options) { }; /** - * @type {import('./temml').renderToMathMLTree} + * @type {import('./temml').__renderToMathMLTree} * Generates and returns the Temml build tree. This is used for advanced * use cases (like rendering to custom output). */ @@ -129,8 +129,24 @@ const renderToMathMLTree = function(expression, options) { } }; +// ESM exports +export { + version, + render, + renderToString, + renderMathInElement, + postProcess, + ParseError, + definePreamble, + generateParseTree as __parse, + renderToMathMLTree as __renderToMathMLTree, + defineSymbol as __defineSymbol, + defineMacro as __defineMacro +}; + +// CJS exports and ESM default export /** @type {import('./temml').default} */ -export default { +const Temml = { /** * Current Temml version */ @@ -191,3 +207,5 @@ export default { */ __defineMacro: defineMacro } + +export default Temml; From 1e5b24deb751e3ca346e92951feba25ead104bc5 Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:44:23 +1100 Subject: [PATCH 4/9] update config --- utils/rollupConfig.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/rollupConfig.mjs b/utils/rollupConfig.mjs index 56ba61a..d5581cd 100644 --- a/utils/rollupConfig.mjs +++ b/utils/rollupConfig.mjs @@ -1,11 +1,11 @@ export default [ { - input: "./temml.js", + input: "./temml-default.js", output: { format: "iife", name: "temml", file: "./test/temml.js" } }, { - input: "./temml.js", - output: { format: "cjs", name: "temml", exports: "auto", file: "./utils/temml.cjs" } + input: "./temml-default.js", + output: { format: "cjs", name: "temml", file: "./utils/temml.cjs" } }, { input: "./temml.js", From 9a028ecf2ec6716d47d094bdc07bcf7a3ecc039a Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:45:08 +1100 Subject: [PATCH 5/9] wip: disbale mini --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f10b358..d1ad1ab 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "unit-test": "node ./test/unit-test.cjs", "visual-test": "node utils/buildTests.js", "test": "yarn lint && node utils/buildTests.js && yarn 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", + "minify": "cp test/temml.js site/assets/temml.min.js && cp contrib/mhchem/mhchem.js site/assets/mhchem.min.js", "build": "rollup --config ./utils/rollupConfig.mjs && yarn minify && node utils/insertPlugins.js", "docs": "node utils/buildDocs.js", "dist": "yarn build && node ./utils/copyfiles.js" From 6e5f4ca328747e26deac4a399994e40863798cbc Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:45:44 +1100 Subject: [PATCH 6/9] wip: build without mini --- site/assets/mhchem.min.js | 1720 ++++- site/assets/temml.min.js | 12775 +++++++++++++++++++++++++++++++++++- 2 files changed, 14493 insertions(+), 2 deletions(-) diff --git a/site/assets/mhchem.min.js b/site/assets/mhchem.min.js index 574a4fd..dae6792 100644 --- a/site/assets/mhchem.min.js +++ b/site/assets/mhchem.min.js @@ -1 +1,1719 @@ -temml.__defineMacro("\\ce",function(t){return chemParse(t.consumeArgs(1)[0],"ce")}),temml.__defineMacro("\\pu",function(t){return chemParse(t.consumeArgs(1)[0],"pu")}),temml.__defineMacro("\\uniDash","{\\rule{0.672em}{0.06em}}"),temml.__defineMacro("\\triDash","{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}"),temml.__defineMacro("\\tripleDash","\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em"),temml.__defineMacro("\\tripleDashOverLine","\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em"),temml.__defineMacro("\\tripleDashOverDoubleLine","\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em"),temml.__defineMacro("\\tripleDashBetweenDoubleLine","\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\uniDash}}\\raise0.27em{\\triDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em");var chemParse=function(t,e){for(var r="",a=t.length&&t[t.length-1].loc.start,n=t.length-1;n>=0;n--)t[n].loc.start>a&&(r+=" ",a=t[n].loc.start),r+=t[n].text,a+=t[n].text.length;return texify.go(mhchemParser.go(r,e))},mhchemParser={go:function(t,e){if(!t)return[];void 0===e&&(e="ce");var r,a="0",n={};n.parenthesisLevel=0,t=(t=(t=t.replace(/\n/g," ")).replace(/[\u2212\u2013\u2014\u2010]/g,"-")).replace(/[\u2026]/g,"...");for(var o=10,i=[];;){r!==t?(o=10,r=t):o--;var c=mhchemParser.stateMachines[e],s=c.transitions[a]||c.transitions["*"];t:for(var u=0;u0))return i;if(m.revisit||(t=p.remainder),!m.toContinue)break t}}if(o<=0)throw["MhchemBugU","mhchem bug U. Please report."]}},concatArray:function(t,e){if(e)if(Array.isArray(e))for(var r=0;r":/^[=<>]/,"#":/^[#\u2261]/,"+":/^\+/,"-$":/^-(?=[\s_},;\]/]|$|\([a-z]+\))/,"-9":/^-(?=[0-9])/,"- orbital overlap":/^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,"-":/^-/,"pm-operator":/^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,operator:/^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,arrowUpDown:/^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,"\\bond{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\bond{","","","}")},"->":/^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,CMT:/^[CMT](?=\[)/,"[(...)]":function(t){return mhchemParser.patterns.findObserveGroups(t,"[","","","]")},"1st-level escape":/^(&|\\\\|\\hline)\s*/,"\\,":/^(?:\\[,\ ;:])/,"\\x{}{}":function(t){return mhchemParser.patterns.findObserveGroups(t,"",/^\\[a-zA-Z]+\{/,"}","","","{","}","",!0)},"\\x{}":function(t){return mhchemParser.patterns.findObserveGroups(t,"",/^\\[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])/,others:/^[\/~|]/,"\\frac{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\frac{","","","}","{","","","}")},"\\overset{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\overset{","","","}","{","","","}")},"\\underset{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\underset{","","","}","{","","","}")},"\\underbrace{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\underbrace{","","","}_","{","","","}")},"\\color{(...)}0":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\color{","","","}")},"\\color{(...)}{(...)}1":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\color{","","","}","{","","","}")},"\\color(...){(...)}2":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\color","\\","",/^(?=\{)/,"{","","","}")},"\\ce{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\ce{","","","}")},oxidation$:/^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,"d-oxidation$":/^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,"roman numeral":/^[IVX]+/,"1/2$":/^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,amount:function(t){var e;if(e=t.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))/))return{match_:e[0],remainder:t.substr(e[0].length)};var r=mhchemParser.patterns.findObserveGroups(t,"","$","$","");return r&&(e=r.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/))?{match_:e[0],remainder:t.substr(e[0].length)}:null},amount2:function(t){return this.amount(t)},"(KV letters),":/^(?:[A-Z][a-z]{0,2}|i)(?=,)/,formula$:function(t){if(t.match(/^\([a-z]+\)$/))return null;var e=t.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/);return e?{match_:e[0],remainder:t.substr(e[0].length)}:null},uprightEntities:/^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,"/":/^\s*(\/)\s*/,"//":/^\s*(\/\/)\s*/,"*":/^\s*[*.]\s*/},findObserveGroups:function(t,e,r,a,n,o,i,c,s,u){var p=function(t,e){if("string"==typeof e)return 0!==t.indexOf(e)?null:e;var r=t.match(e);return r?r[0]:null},m=p(t,e);if(null===m)return null;if(t=t.substr(m.length),null===(m=p(t,r)))return null;var h=function(t,e,r){for(var a=0;e":{"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:!1},nextState:"2"},q:{action_:{type_:"- after o/d",option:!1},nextState:"2"},"d|qd|dq":{action_:{type_:"- after o/d",option:!0},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"}},"^_":{"*":{}},"^{(...)}|^($...$)":{"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="},"0|1|2|3|as|b|p|bp|o":{action_:"o=",nextState:"o"},"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:!0},as:{action_:["output","sb=true"],nextState:"1",revisit:!0},"r|rt|rd|rdt|rdq":{action_:["output"],nextState:"0",revisit:!0},"*":{action_:["output","copy"],nextState:"3"}}}),actions:{"o after d":function(t,e){var r;if((t.d||"").match(/^[0-9]+$/)){var a=t.d;t.d=void 0,r=this.output(t),t.b=a}else r=this.output(t);return mhchemParser.actions["o="](t,e),r},"d= kv":function(t,e){t.d=e,t.dType="kv"},"charge or bond":function(t,e){if(t.beginsWithBond){var r=[];return mhchemParser.concatArray(r,this.output(t)),mhchemParser.concatArray(r,mhchemParser.actions.bond(t,e,"-")),r}t.d=e},"- after o/d":function(t,e,r){var a=mhchemParser.patterns.match_("orbital",t.o||""),n=mhchemParser.patterns.match_("one lowercase greek letter $",t.o||""),o=mhchemParser.patterns.match_("one lowercase latin letter $",t.o||""),i=mhchemParser.patterns.match_("$one lowercase latin letter$ $",t.o||""),c="-"===e&&(a&&""===a.remainder||n||o||i);!c||t.a||t.b||t.p||t.d||t.q||a||!o||(t.o="$"+t.o+"$");var s=[];return c?(mhchemParser.concatArray(s,this.output(t)),s.push({type_:"hyphen"})):(a=mhchemParser.patterns.match_("digits",t.d||""),r&&a&&""===a.remainder?(mhchemParser.concatArray(s,mhchemParser.actions["d="](t,e)),mhchemParser.concatArray(s,this.output(t))):(mhchemParser.concatArray(s,this.output(t)),mhchemParser.concatArray(s,mhchemParser.actions.bond(t,e,"-")))),s},"a to o":function(t){t.o=t.a,t.a=void 0},"sb=true":function(t){t.sb=!0},"sb=false":function(t){t.sb=!1},"beginsWithBond=true":function(t){t.beginsWithBond=!0},"beginsWithBond=false":function(t){t.beginsWithBond=!1},"parenthesisLevel++":function(t){t.parenthesisLevel++},"parenthesisLevel--":function(t){t.parenthesisLevel--},"state of aggregation":function(t,e){return{type_:"state of aggregation",p1:mhchemParser.go(e,"o")}},comma:function(t,e){var r=e.replace(/\s*$/,"");return r!==e&&0===t.parenthesisLevel?{type_:"comma enumeration L",p1:r}:{type_:"comma enumeration M",p1:r}},output:function(t,e,r){var a,n,o;t.r?(n="M"===t.rdt?mhchemParser.go(t.rd,"tex-math"):"T"===t.rdt?[{type_:"text",p1:t.rd||""}]:mhchemParser.go(t.rd),o="M"===t.rqt?mhchemParser.go(t.rq,"tex-math"):"T"===t.rqt?[{type_:"text",p1:t.rq||""}]:mhchemParser.go(t.rq),a={type_:"arrow",r:t.r,rd:n,rq:o}):(a=[],(t.a||t.b||t.p||t.o||t.q||t.d||r)&&(t.sb&&a.push({type_:"entitySkip"}),t.o||t.q||t.d||t.b||t.p||2===r?t.o||t.q||t.d||!t.b&&!t.p?t.o&&"kv"===t.dType&&mhchemParser.patterns.match_("d-oxidation$",t.d||"")?t.dType="oxidation":t.o&&"kv"===t.dType&&!t.q&&(t.dType=void 0):(t.o=t.a,t.d=t.b,t.q=t.p,t.a=t.b=t.p=void 0):(t.o=t.a,t.a=void 0),a.push({type_:"chemfive",a:mhchemParser.go(t.a,"a"),b:mhchemParser.go(t.b,"bd"),p:mhchemParser.go(t.p,"pq"),o:mhchemParser.go(t.o,"o"),q:mhchemParser.go(t.q,"pq"),d:mhchemParser.go(t.d,"oxidation"===t.dType?"oxidation":"bd"),dType:t.dType})));for(var i in t)"parenthesisLevel"!==i&&"beginsWithBond"!==i&&delete t[i];return a},"oxidation-output":function(t,e){var r=["{"];return mhchemParser.concatArray(r,mhchemParser.go(e,"oxidation")),r.push("}"),r},"frac-output":function(t,e){return{type_:"frac-ce",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"overset-output":function(t,e){return{type_:"overset",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"underset-output":function(t,e){return{type_:"underset",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"underbrace-output":function(t,e){return{type_:"underbrace",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"color-output":function(t,e){return{type_:"color",color1:e[0],color2:mhchemParser.go(e[1])}},"r=":function(t,e){t.r=e},"rdt=":function(t,e){t.rdt=e},"rd=":function(t,e){t.rd=e},"rqt=":function(t,e){t.rqt=e},"rq=":function(t,e){t.rq=e},operator:function(t,e,r){return{type_:"operator",kind_:r||e}}}},a:{transitions:mhchemParser.createTransitions({empty:{"*":{}},"1/2$":{0:{action_:"1/2"}},else:{0:{nextState:"1",revisit:!0}},"$(...)$":{"*":{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:!0}},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(t){if(t.text_){var e={type_:"text",p1:t.text_};for(var r in t)delete t[r];return e}}}},pq:{transitions:mhchemParser.createTransitions({empty:{"*":{}},"state of aggregation $":{"*":{action_:"state of aggregation"}},i$:{0:{nextState:"!f",revisit:!0}},"(KV letters),":{0:{action_:"rm",nextState:"0"}},formula$:{0:{nextState:"f",revisit:!0}},"1/2$":{0:{action_:"1/2"}},else:{0:{nextState:"!f",revisit:!0}},"${(...)}$|$(...)$":{"*":{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(t,e){return{type_:"state of aggregation subscript",p1:mhchemParser.go(e,"o")}},"color-output":function(t,e){return{type_:"color",color1:e[0],color2:mhchemParser.go(e[1],"pq")}}}},bd:{transitions:mhchemParser.createTransitions({empty:{"*":{}},x$:{0:{nextState:"!f",revisit:!0}},formula$:{0:{nextState:"f",revisit:!0}},else:{0:{nextState:"!f",revisit:!0}},"-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(t,e){return{type_:"color",color1:e[0],color2:mhchemParser.go(e[1],"bd")}}}},oxidation:{transitions:mhchemParser.createTransitions({empty:{"*":{}},"roman numeral":{"*":{action_:"roman-numeral"}},"${(...)}$|$(...)$":{"*":{action_:"tex-math"}},else:{"*":{action_:"copy"}}}),actions:{"roman-numeral":function(t,e){return{type_:"roman numeral",p1:e||""}}}},"tex-math":{transitions:mhchemParser.createTransitions({empty:{"*":{action_:"output"}},"\\ce{(...)}":{"*":{action_:["output","ce"]}},"{...}|\\,|\\x{}{}|\\x{}|\\x":{"*":{action_:"o="}},else:{"*":{action_:"o="}}}),actions:{output:function(t){if(t.o){var e={type_:"tex-math",p1:t.o};for(var r in t)delete t[r];return e}}}},"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(t,e){t.o=(t.o||"")+"{"+e+"}"},output:function(t){if(t.o){var e={type_:"tex-math",p1:t.o};for(var r in t)delete t[r];return e}}}},"9,9":{transitions:mhchemParser.createTransitions({empty:{"*":{}},",":{"*":{action_:"comma"}},else:{"*":{action_:"copy"}}}),actions:{comma:function(){return{type_:"commaDecimal"}}}},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(t,e){var r=[];return"+-"===e[0]||"+/-"===e[0]?r.push("\\pm "):e[0]&&r.push(e[0]),e[1]&&(mhchemParser.concatArray(r,mhchemParser.go(e[1],"pu-9,9")),e[2]&&(e[2].match(/[,.]/)?mhchemParser.concatArray(r,mhchemParser.go(e[2],"pu-9,9")):r.push(e[2])),e[3]=e[4]||e[3],e[3]&&(e[3]=e[3].trim(),"e"===e[3]||"*"===e[3].substr(0,1)?r.push({type_:"cdot"}):r.push({type_:"times"}))),e[3]&&r.push("10^{"+e[5]+"}"),r},"number^":function(t,e){var r=[];return"+-"===e[0]||"+/-"===e[0]?r.push("\\pm "):e[0]&&r.push(e[0]),mhchemParser.concatArray(r,mhchemParser.go(e[1],"pu-9,9")),r.push("^{"+e[2]+"}"),r},operator:function(t,e,r){return{type_:"operator",kind_:r||e}},space:function(){return{type_:"pu-space-1"}},output:function(t){var e,r=mhchemParser.patterns.match_("{(...)}",t.d||"");r&&""===r.remainder&&(t.d=r.match_);var a=mhchemParser.patterns.match_("{(...)}",t.q||"");if(a&&""===a.remainder&&(t.q=a.match_),t.d&&(t.d=t.d.replace(/\u00B0C|\^oC|\^{o}C/g,"{}^{\\circ}C"),t.d=t.d.replace(/\u00B0F|\^oF|\^{o}F/g,"{}^{\\circ}F")),t.q){t.q=t.q.replace(/\u00B0C|\^oC|\^{o}C/g,"{}^{\\circ}C"),t.q=t.q.replace(/\u00B0F|\^oF|\^{o}F/g,"{}^{\\circ}F");var n={d:mhchemParser.go(t.d,"pu"),q:mhchemParser.go(t.q,"pu")};"//"===t.o?e={type_:"pu-frac",p1:n.d,p2:n.q}:(e=n.d,n.d.length>1||n.q.length>1?e.push({type_:" / "}):e.push({type_:"/"}),mhchemParser.concatArray(e,n.q))}else e=mhchemParser.go(t.d,"pu-2");for(var o in t)delete t[o];return e}}},"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(t,e){t.rm+="^{"+e+"}"},space:function(){return{type_:"pu-space-2"}},output:function(t){var e=[];if(t.rm){var r=mhchemParser.patterns.match_("{(...)}",t.rm||"");e=r&&""===r.remainder?mhchemParser.go(r.match_,"pu"):{type_:"rm",p1:t.rm}}for(var a in t)delete t[a];return e}}},"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(t){var e=[];if(t.text_=t.text_||"",t.text_.length>4){var r=t.text_.length%3;0===r&&(r=3);for(var a=t.text_.length-3;a>0;a-=3)e.push(t.text_.substr(a,3)),e.push({type_:"1000 separator"});e.push(t.text_.substr(0,r)),e.reverse()}else e.push(t.text_);for(var n in t)delete t[n];return e},"output-o":function(t){var e=[];if(t.text_=t.text_||"",t.text_.length>4){for(var r=t.text_.length-3,a=0;a":case"→":case"⟶":return"\\yields";case"<-":return"\\yieldsLeft";case"<->":return"\\mesomerism";case"<--\x3e":return"\\yieldsLeftRight";case"<=>":case"⇌":return"\\chemequilibrium";case"<=>>":return"\\equilibriumRight";case"<<=>":return"\\equilibriumLeft";default:throw assertNever(t),["MhchemBugT","mhchem bug T. Please report."]}},_getBond:function(t){switch(t){case"-":case"1":return"{-}";case"=":case"2":return"{=}";case"#":case"3":return"{\\equiv}";case"~":return"{\\tripleDash}";case"~-":return"{\\tripleDashOverLine}";case"~=":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:throw assertNever(t),["MhchemBugT","mhchem bug T. Please report."]}},_getOperator:function(t){switch(t){case"+":return" {}+{} ";case"-":return" {}-{} ";case"=":return" {}={} ";case"<":return" {}<{} ";case">":return" {}>{} ";case"<<":return" {}\\ll{} ";case">>":return" {}\\gg{} ";case"\\pm":return" {}\\pm{} ";case"\\approx":case"$\\approx$":return" {}\\approx{} ";case"v":case"(v)":return" \\downarrow{} ";case"^":case"(^)":return" \\uparrow{} ";default:throw assertNever(t),["MhchemBugT","mhchem bug T. Please report."]}}};function assertNever(t){}function assertString(t){} \ No newline at end of file +/* 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. + +temml.__defineMacro("\\ce", function(context) { + return chemParse(context.consumeArgs(1)[0], "ce") +}); + +temml.__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. +temml.__defineMacro("\\uniDash", `{\\rule{0.672em}{0.06em}}`) +temml.__defineMacro("\\triDash", `{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}`) +temml.__defineMacro("\\tripleDash", `\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em`) +temml.__defineMacro("\\tripleDashOverLine", `\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em`) +temml.__defineMacro("\\tripleDashOverDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) +temml.__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) {} diff --git a/site/assets/temml.min.js b/site/assets/temml.min.js index d41cf78..94b2154 100644 --- a/site/assets/temml.min.js +++ b/site/assets/temml.min.js @@ -1 +1,12774 @@ -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 +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; + +})(); From 4f73abb1a74208da6563f92d8de031a486afaa8a Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:46:42 +1100 Subject: [PATCH 7/9] wip: build again --- site/assets/temml.min.js | 14 ++++++++++---- test/temml.js | 14 ++++++++++---- utils/temml.cjs | 14 ++++++++++---- utils/temml.mjs | 9 +++++---- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/site/assets/temml.min.js b/site/assets/temml.min.js index 94b2154..f08e69e 100644 --- a/site/assets/temml.min.js +++ b/site/assets/temml.min.js @@ -12646,7 +12646,7 @@ var temml = (function () { }; /** - * @type {import('./temml').generateParseTree} + * @type {import('./temml').__parse} * Parse an expression and return the parse tree. */ const generateParseTree = function(expression, options) { @@ -12688,7 +12688,7 @@ var temml = (function () { }; /** - * @type {import('./temml').renderToMathMLTree} + * @type {import('./temml').__renderToMathMLTree} * Generates and returns the Temml build tree. This is used for advanced * use cases (like rendering to custom output). */ @@ -12706,8 +12706,9 @@ var temml = (function () { } }; + // CJS exports and ESM default export /** @type {import('./temml').default} */ - var temml$1 = { + const Temml = { /** * Current Temml version */ @@ -12769,6 +12770,11 @@ var temml = (function () { __defineMacro: defineMacro }; - return temml$1; + /** + * An entry point that only has the default export of Temml. + * This is used for the iife and CommonJS builds. + */ + + return Temml; })(); diff --git a/test/temml.js b/test/temml.js index 94b2154..f08e69e 100644 --- a/test/temml.js +++ b/test/temml.js @@ -12646,7 +12646,7 @@ var temml = (function () { }; /** - * @type {import('./temml').generateParseTree} + * @type {import('./temml').__parse} * Parse an expression and return the parse tree. */ const generateParseTree = function(expression, options) { @@ -12688,7 +12688,7 @@ var temml = (function () { }; /** - * @type {import('./temml').renderToMathMLTree} + * @type {import('./temml').__renderToMathMLTree} * Generates and returns the Temml build tree. This is used for advanced * use cases (like rendering to custom output). */ @@ -12706,8 +12706,9 @@ var temml = (function () { } }; + // CJS exports and ESM default export /** @type {import('./temml').default} */ - var temml$1 = { + const Temml = { /** * Current Temml version */ @@ -12769,6 +12770,11 @@ var temml = (function () { __defineMacro: defineMacro }; - return temml$1; + /** + * An entry point that only has the default export of Temml. + * This is used for the iife and CommonJS builds. + */ + + return Temml; })(); diff --git a/utils/temml.cjs b/utils/temml.cjs index 9726221..22934af 100644 --- a/utils/temml.cjs +++ b/utils/temml.cjs @@ -14559,7 +14559,7 @@ const renderToString = function(expression, options) { }; /** - * @type {import('./temml').generateParseTree} + * @type {import('./temml').__parse} * Parse an expression and return the parse tree. */ const generateParseTree = function(expression, options) { @@ -14601,7 +14601,7 @@ const renderError = function(error, expression, options) { }; /** - * @type {import('./temml').renderToMathMLTree} + * @type {import('./temml').__renderToMathMLTree} * Generates and returns the Temml build tree. This is used for advanced * use cases (like rendering to custom output). */ @@ -14619,8 +14619,9 @@ const renderToMathMLTree = function(expression, options) { } }; +// CJS exports and ESM default export /** @type {import('./temml').default} */ -var temml$1 = { +const Temml = { /** * Current Temml version */ @@ -14682,4 +14683,9 @@ var temml$1 = { __defineMacro: defineMacro }; -module.exports = temml$1; +/** + * An entry point that only has the default export of Temml. + * This is used for the iife and CommonJS builds. + */ + +module.exports = Temml; diff --git a/utils/temml.mjs b/utils/temml.mjs index f20b6e8..5b700c3 100644 --- a/utils/temml.mjs +++ b/utils/temml.mjs @@ -14557,7 +14557,7 @@ const renderToString = function(expression, options) { }; /** - * @type {import('./temml').generateParseTree} + * @type {import('./temml').__parse} * Parse an expression and return the parse tree. */ const generateParseTree = function(expression, options) { @@ -14599,7 +14599,7 @@ const renderError = function(error, expression, options) { }; /** - * @type {import('./temml').renderToMathMLTree} + * @type {import('./temml').__renderToMathMLTree} * Generates and returns the Temml build tree. This is used for advanced * use cases (like rendering to custom output). */ @@ -14617,8 +14617,9 @@ const renderToMathMLTree = function(expression, options) { } }; +// CJS exports and ESM default export /** @type {import('./temml').default} */ -var temml$1 = { +const Temml = { /** * Current Temml version */ @@ -14680,4 +14681,4 @@ var temml$1 = { __defineMacro: defineMacro }; -export { temml$1 as default }; +export { ParseError, defineMacro as __defineMacro, defineSymbol as __defineSymbol, generateParseTree as __parse, renderToMathMLTree as __renderToMathMLTree, Temml as default, definePreamble, postProcess, render, renderMathInElement, renderToString, version }; From d8f3830868f176e528b8b31741028db9c45579e9 Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:46:49 +1100 Subject: [PATCH 8/9] Revert "wip: disbale mini" This reverts commit 9a028ecf2ec6716d47d094bdc07bcf7a3ecc039a. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d1ad1ab..f10b358 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "unit-test": "node ./test/unit-test.cjs", "visual-test": "node utils/buildTests.js", "test": "yarn lint && node utils/buildTests.js && yarn unit-test", - "minify": "cp test/temml.js site/assets/temml.min.js && cp contrib/mhchem/mhchem.js site/assets/mhchem.min.js", + "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", "docs": "node utils/buildDocs.js", "dist": "yarn build && node ./utils/copyfiles.js" From 68c20e70491e75693e349f813b569197453e5212 Mon Sep 17 00:00:00 2001 From: ocavue Date: Mon, 23 Mar 2026 19:47:00 +1100 Subject: [PATCH 9/9] clean up --- site/assets/mhchem.min.js | 1720 +---- site/assets/temml.min.js | 12781 +----------------------------------- 2 files changed, 2 insertions(+), 14499 deletions(-) diff --git a/site/assets/mhchem.min.js b/site/assets/mhchem.min.js index dae6792..ac5ab96 100644 --- a/site/assets/mhchem.min.js +++ b/site/assets/mhchem.min.js @@ -1,1719 +1 @@ -/* 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. - -temml.__defineMacro("\\ce", function(context) { - return chemParse(context.consumeArgs(1)[0], "ce") -}); - -temml.__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. -temml.__defineMacro("\\uniDash", `{\\rule{0.672em}{0.06em}}`) -temml.__defineMacro("\\triDash", `{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}`) -temml.__defineMacro("\\tripleDash", `\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em`) -temml.__defineMacro("\\tripleDashOverLine", `\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em`) -temml.__defineMacro("\\tripleDashOverDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) -temml.__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) {} +temml.__defineMacro("\\ce",(function(t){return chemParse(t.consumeArgs(1)[0],"ce")})),temml.__defineMacro("\\pu",(function(t){return chemParse(t.consumeArgs(1)[0],"pu")})),temml.__defineMacro("\\uniDash","{\\rule{0.672em}{0.06em}}"),temml.__defineMacro("\\triDash","{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}"),temml.__defineMacro("\\tripleDash","\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em"),temml.__defineMacro("\\tripleDashOverLine","\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em"),temml.__defineMacro("\\tripleDashOverDoubleLine","\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em"),temml.__defineMacro("\\tripleDashBetweenDoubleLine","\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\uniDash}}\\raise0.27em{\\triDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em");var chemParse=function(t,e){for(var r="",a=t.length&&t[t.length-1].loc.start,n=t.length-1;n>=0;n--)t[n].loc.start>a&&(r+=" ",a=t[n].loc.start),r+=t[n].text,a+=t[n].text.length;return texify.go(mhchemParser.go(r,e))},mhchemParser={go:function(t,e){if(!t)return[];void 0===e&&(e="ce");var r,a="0",n={};n.parenthesisLevel=0,t=(t=(t=t.replace(/\n/g," ")).replace(/[\u2212\u2013\u2014\u2010]/g,"-")).replace(/[\u2026]/g,"...");for(var o=10,i=[];;){r!==t?(o=10,r=t):o--;var c=mhchemParser.stateMachines[e],s=c.transitions[a]||c.transitions["*"];t:for(var u=0;u0))return i;if(m.revisit||(t=p.remainder),!m.toContinue)break t}}if(o<=0)throw["MhchemBugU","mhchem bug U. Please report."]}},concatArray:function(t,e){if(e)if(Array.isArray(e))for(var r=0;r":/^[=<>]/,"#":/^[#\u2261]/,"+":/^\+/,"-$":/^-(?=[\s_},;\]/]|$|\([a-z]+\))/,"-9":/^-(?=[0-9])/,"- orbital overlap":/^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,"-":/^-/,"pm-operator":/^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,operator:/^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,arrowUpDown:/^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,"\\bond{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\bond{","","","}")},"->":/^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,CMT:/^[CMT](?=\[)/,"[(...)]":function(t){return mhchemParser.patterns.findObserveGroups(t,"[","","","]")},"1st-level escape":/^(&|\\\\|\\hline)\s*/,"\\,":/^(?:\\[,\ ;:])/,"\\x{}{}":function(t){return mhchemParser.patterns.findObserveGroups(t,"",/^\\[a-zA-Z]+\{/,"}","","","{","}","",!0)},"\\x{}":function(t){return mhchemParser.patterns.findObserveGroups(t,"",/^\\[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])/,others:/^[\/~|]/,"\\frac{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\frac{","","","}","{","","","}")},"\\overset{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\overset{","","","}","{","","","}")},"\\underset{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\underset{","","","}","{","","","}")},"\\underbrace{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\underbrace{","","","}_","{","","","}")},"\\color{(...)}0":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\color{","","","}")},"\\color{(...)}{(...)}1":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\color{","","","}","{","","","}")},"\\color(...){(...)}2":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\color","\\","",/^(?=\{)/,"{","","","}")},"\\ce{(...)}":function(t){return mhchemParser.patterns.findObserveGroups(t,"\\ce{","","","}")},oxidation$:/^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,"d-oxidation$":/^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,"roman numeral":/^[IVX]+/,"1/2$":/^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,amount:function(t){var e;if(e=t.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))/))return{match_:e[0],remainder:t.substr(e[0].length)};var r=mhchemParser.patterns.findObserveGroups(t,"","$","$","");return r&&(e=r.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/))?{match_:e[0],remainder:t.substr(e[0].length)}:null},amount2:function(t){return this.amount(t)},"(KV letters),":/^(?:[A-Z][a-z]{0,2}|i)(?=,)/,formula$:function(t){if(t.match(/^\([a-z]+\)$/))return null;var e=t.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/);return e?{match_:e[0],remainder:t.substr(e[0].length)}:null},uprightEntities:/^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,"/":/^\s*(\/)\s*/,"//":/^\s*(\/\/)\s*/,"*":/^\s*[*.]\s*/},findObserveGroups:function(t,e,r,a,n,o,i,c,s,u){var p=function(t,e){if("string"==typeof e)return 0!==t.indexOf(e)?null:e;var r=t.match(e);return r?r[0]:null},m=p(t,e);if(null===m)return null;if(t=t.substr(m.length),null===(m=p(t,r)))return null;var h=function(t,e,r){for(var a=0;e":{"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:!1},nextState:"2"},q:{action_:{type_:"- after o/d",option:!1},nextState:"2"},"d|qd|dq":{action_:{type_:"- after o/d",option:!0},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"}},"^_":{"*":{}},"^{(...)}|^($...$)":{"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="},"0|1|2|3|as|b|p|bp|o":{action_:"o=",nextState:"o"},"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:!0},as:{action_:["output","sb=true"],nextState:"1",revisit:!0},"r|rt|rd|rdt|rdq":{action_:["output"],nextState:"0",revisit:!0},"*":{action_:["output","copy"],nextState:"3"}}}),actions:{"o after d":function(t,e){var r;if((t.d||"").match(/^[0-9]+$/)){var a=t.d;t.d=void 0,r=this.output(t),t.b=a}else r=this.output(t);return mhchemParser.actions["o="](t,e),r},"d= kv":function(t,e){t.d=e,t.dType="kv"},"charge or bond":function(t,e){if(t.beginsWithBond){var r=[];return mhchemParser.concatArray(r,this.output(t)),mhchemParser.concatArray(r,mhchemParser.actions.bond(t,e,"-")),r}t.d=e},"- after o/d":function(t,e,r){var a=mhchemParser.patterns.match_("orbital",t.o||""),n=mhchemParser.patterns.match_("one lowercase greek letter $",t.o||""),o=mhchemParser.patterns.match_("one lowercase latin letter $",t.o||""),i=mhchemParser.patterns.match_("$one lowercase latin letter$ $",t.o||""),c="-"===e&&(a&&""===a.remainder||n||o||i);!c||t.a||t.b||t.p||t.d||t.q||a||!o||(t.o="$"+t.o+"$");var s=[];return c?(mhchemParser.concatArray(s,this.output(t)),s.push({type_:"hyphen"})):(a=mhchemParser.patterns.match_("digits",t.d||""),r&&a&&""===a.remainder?(mhchemParser.concatArray(s,mhchemParser.actions["d="](t,e)),mhchemParser.concatArray(s,this.output(t))):(mhchemParser.concatArray(s,this.output(t)),mhchemParser.concatArray(s,mhchemParser.actions.bond(t,e,"-")))),s},"a to o":function(t){t.o=t.a,t.a=void 0},"sb=true":function(t){t.sb=!0},"sb=false":function(t){t.sb=!1},"beginsWithBond=true":function(t){t.beginsWithBond=!0},"beginsWithBond=false":function(t){t.beginsWithBond=!1},"parenthesisLevel++":function(t){t.parenthesisLevel++},"parenthesisLevel--":function(t){t.parenthesisLevel--},"state of aggregation":function(t,e){return{type_:"state of aggregation",p1:mhchemParser.go(e,"o")}},comma:function(t,e){var r=e.replace(/\s*$/,"");return r!==e&&0===t.parenthesisLevel?{type_:"comma enumeration L",p1:r}:{type_:"comma enumeration M",p1:r}},output:function(t,e,r){var a,n,o;t.r?(n="M"===t.rdt?mhchemParser.go(t.rd,"tex-math"):"T"===t.rdt?[{type_:"text",p1:t.rd||""}]:mhchemParser.go(t.rd),o="M"===t.rqt?mhchemParser.go(t.rq,"tex-math"):"T"===t.rqt?[{type_:"text",p1:t.rq||""}]:mhchemParser.go(t.rq),a={type_:"arrow",r:t.r,rd:n,rq:o}):(a=[],(t.a||t.b||t.p||t.o||t.q||t.d||r)&&(t.sb&&a.push({type_:"entitySkip"}),t.o||t.q||t.d||t.b||t.p||2===r?t.o||t.q||t.d||!t.b&&!t.p?t.o&&"kv"===t.dType&&mhchemParser.patterns.match_("d-oxidation$",t.d||"")?t.dType="oxidation":t.o&&"kv"===t.dType&&!t.q&&(t.dType=void 0):(t.o=t.a,t.d=t.b,t.q=t.p,t.a=t.b=t.p=void 0):(t.o=t.a,t.a=void 0),a.push({type_:"chemfive",a:mhchemParser.go(t.a,"a"),b:mhchemParser.go(t.b,"bd"),p:mhchemParser.go(t.p,"pq"),o:mhchemParser.go(t.o,"o"),q:mhchemParser.go(t.q,"pq"),d:mhchemParser.go(t.d,"oxidation"===t.dType?"oxidation":"bd"),dType:t.dType})));for(var i in t)"parenthesisLevel"!==i&&"beginsWithBond"!==i&&delete t[i];return a},"oxidation-output":function(t,e){var r=["{"];return mhchemParser.concatArray(r,mhchemParser.go(e,"oxidation")),r.push("}"),r},"frac-output":function(t,e){return{type_:"frac-ce",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"overset-output":function(t,e){return{type_:"overset",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"underset-output":function(t,e){return{type_:"underset",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"underbrace-output":function(t,e){return{type_:"underbrace",p1:mhchemParser.go(e[0]),p2:mhchemParser.go(e[1])}},"color-output":function(t,e){return{type_:"color",color1:e[0],color2:mhchemParser.go(e[1])}},"r=":function(t,e){t.r=e},"rdt=":function(t,e){t.rdt=e},"rd=":function(t,e){t.rd=e},"rqt=":function(t,e){t.rqt=e},"rq=":function(t,e){t.rq=e},operator:function(t,e,r){return{type_:"operator",kind_:r||e}}}},a:{transitions:mhchemParser.createTransitions({empty:{"*":{}},"1/2$":{0:{action_:"1/2"}},else:{0:{nextState:"1",revisit:!0}},"$(...)$":{"*":{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:!0}},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(t){if(t.text_){var e={type_:"text",p1:t.text_};for(var r in t)delete t[r];return e}}}},pq:{transitions:mhchemParser.createTransitions({empty:{"*":{}},"state of aggregation $":{"*":{action_:"state of aggregation"}},i$:{0:{nextState:"!f",revisit:!0}},"(KV letters),":{0:{action_:"rm",nextState:"0"}},formula$:{0:{nextState:"f",revisit:!0}},"1/2$":{0:{action_:"1/2"}},else:{0:{nextState:"!f",revisit:!0}},"${(...)}$|$(...)$":{"*":{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(t,e){return{type_:"state of aggregation subscript",p1:mhchemParser.go(e,"o")}},"color-output":function(t,e){return{type_:"color",color1:e[0],color2:mhchemParser.go(e[1],"pq")}}}},bd:{transitions:mhchemParser.createTransitions({empty:{"*":{}},x$:{0:{nextState:"!f",revisit:!0}},formula$:{0:{nextState:"f",revisit:!0}},else:{0:{nextState:"!f",revisit:!0}},"-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(t,e){return{type_:"color",color1:e[0],color2:mhchemParser.go(e[1],"bd")}}}},oxidation:{transitions:mhchemParser.createTransitions({empty:{"*":{}},"roman numeral":{"*":{action_:"roman-numeral"}},"${(...)}$|$(...)$":{"*":{action_:"tex-math"}},else:{"*":{action_:"copy"}}}),actions:{"roman-numeral":function(t,e){return{type_:"roman numeral",p1:e||""}}}},"tex-math":{transitions:mhchemParser.createTransitions({empty:{"*":{action_:"output"}},"\\ce{(...)}":{"*":{action_:["output","ce"]}},"{...}|\\,|\\x{}{}|\\x{}|\\x":{"*":{action_:"o="}},else:{"*":{action_:"o="}}}),actions:{output:function(t){if(t.o){var e={type_:"tex-math",p1:t.o};for(var r in t)delete t[r];return e}}}},"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(t,e){t.o=(t.o||"")+"{"+e+"}"},output:function(t){if(t.o){var e={type_:"tex-math",p1:t.o};for(var r in t)delete t[r];return e}}}},"9,9":{transitions:mhchemParser.createTransitions({empty:{"*":{}},",":{"*":{action_:"comma"}},else:{"*":{action_:"copy"}}}),actions:{comma:function(){return{type_:"commaDecimal"}}}},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(t,e){var r=[];return"+-"===e[0]||"+/-"===e[0]?r.push("\\pm "):e[0]&&r.push(e[0]),e[1]&&(mhchemParser.concatArray(r,mhchemParser.go(e[1],"pu-9,9")),e[2]&&(e[2].match(/[,.]/)?mhchemParser.concatArray(r,mhchemParser.go(e[2],"pu-9,9")):r.push(e[2])),e[3]=e[4]||e[3],e[3]&&(e[3]=e[3].trim(),"e"===e[3]||"*"===e[3].substr(0,1)?r.push({type_:"cdot"}):r.push({type_:"times"}))),e[3]&&r.push("10^{"+e[5]+"}"),r},"number^":function(t,e){var r=[];return"+-"===e[0]||"+/-"===e[0]?r.push("\\pm "):e[0]&&r.push(e[0]),mhchemParser.concatArray(r,mhchemParser.go(e[1],"pu-9,9")),r.push("^{"+e[2]+"}"),r},operator:function(t,e,r){return{type_:"operator",kind_:r||e}},space:function(){return{type_:"pu-space-1"}},output:function(t){var e,r=mhchemParser.patterns.match_("{(...)}",t.d||"");r&&""===r.remainder&&(t.d=r.match_);var a=mhchemParser.patterns.match_("{(...)}",t.q||"");if(a&&""===a.remainder&&(t.q=a.match_),t.d&&(t.d=t.d.replace(/\u00B0C|\^oC|\^{o}C/g,"{}^{\\circ}C"),t.d=t.d.replace(/\u00B0F|\^oF|\^{o}F/g,"{}^{\\circ}F")),t.q){t.q=t.q.replace(/\u00B0C|\^oC|\^{o}C/g,"{}^{\\circ}C"),t.q=t.q.replace(/\u00B0F|\^oF|\^{o}F/g,"{}^{\\circ}F");var n={d:mhchemParser.go(t.d,"pu"),q:mhchemParser.go(t.q,"pu")};"//"===t.o?e={type_:"pu-frac",p1:n.d,p2:n.q}:(e=n.d,n.d.length>1||n.q.length>1?e.push({type_:" / "}):e.push({type_:"/"}),mhchemParser.concatArray(e,n.q))}else e=mhchemParser.go(t.d,"pu-2");for(var o in t)delete t[o];return e}}},"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(t,e){t.rm+="^{"+e+"}"},space:function(){return{type_:"pu-space-2"}},output:function(t){var e=[];if(t.rm){var r=mhchemParser.patterns.match_("{(...)}",t.rm||"");e=r&&""===r.remainder?mhchemParser.go(r.match_,"pu"):{type_:"rm",p1:t.rm}}for(var a in t)delete t[a];return e}}},"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(t){var e=[];if(t.text_=t.text_||"",t.text_.length>4){var r=t.text_.length%3;0===r&&(r=3);for(var a=t.text_.length-3;a>0;a-=3)e.push(t.text_.substr(a,3)),e.push({type_:"1000 separator"});e.push(t.text_.substr(0,r)),e.reverse()}else e.push(t.text_);for(var n in t)delete t[n];return e},"output-o":function(t){var e=[];if(t.text_=t.text_||"",t.text_.length>4){for(var r=t.text_.length-3,a=0;a":case"→":case"⟶":return"\\yields";case"<-":return"\\yieldsLeft";case"<->":return"\\mesomerism";case"<--\x3e":return"\\yieldsLeftRight";case"<=>":case"⇌":return"\\chemequilibrium";case"<=>>":return"\\equilibriumRight";case"<<=>":return"\\equilibriumLeft";default:throw assertNever(t),["MhchemBugT","mhchem bug T. Please report."]}},_getBond:function(t){switch(t){case"-":case"1":return"{-}";case"=":case"2":return"{=}";case"#":case"3":return"{\\equiv}";case"~":return"{\\tripleDash}";case"~-":return"{\\tripleDashOverLine}";case"~=":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:throw assertNever(t),["MhchemBugT","mhchem bug T. Please report."]}},_getOperator:function(t){switch(t){case"+":return" {}+{} ";case"-":return" {}-{} ";case"=":return" {}={} ";case"<":return" {}<{} ";case">":return" {}>{} ";case"<<":return" {}\\ll{} ";case">>":return" {}\\gg{} ";case"\\pm":return" {}\\pm{} ";case"\\approx":case"$\\approx$":return" {}\\approx{} ";case"v":case"(v)":return" \\downarrow{} ";case"^":case"(^)":return" \\uparrow{} ";default:throw assertNever(t),["MhchemBugT","mhchem bug T. Please report."]}}};function assertNever(t){}function assertString(t){} \ No newline at end of file diff --git a/site/assets/temml.min.js b/site/assets/temml.min.js index f08e69e..07c10f0 100644 --- a/site/assets/temml.min.js +++ b/site/assets/temml.min.js @@ -1,12780 +1 @@ -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').__parse} - * 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); - } - }; - - // CJS exports and ESM default export - /** @type {import('./temml').default} */ - const Temml = { - /** - * 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 - }; - - /** - * An entry point that only has the default export of Temml. - * This is used for the iife and CommonJS builds. - */ - - return Temml; - -})(); +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}else{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=e=>"textord"===e.type&&"@"===e.text;function Pe(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 je=e=>({type:"ordgroup",mode:"math",body:e,semisimple:!0}),Re=(e,t)=>({type:t,mode:"math",body:je(e)});class Ue{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 Ue(e.loc.lexer,e.loc.start,t.loc.end):null:e&&e.loc}}class He{constructor(e,t){this.text=e,this.loc=t}range(e,t){return new He(t,Ue.range(this,e))}}const Ve=0,_e=1,We=2,Xe=3,Ze={};function Ye(e,t){Ze[e]=t}const Je=Ze;Ye("\\noexpand",(function(e){const t=e.popToken();return e.isExpandable(t.text)&&(t.noexpand=!0,t.treatAsRelax=!0),{tokens:[t],numArgs:0}})),Ye("\\expandafter",(function(e){const t=e.popToken();return e.expandOnce(!0),{tokens:[t],numArgs:0}})),Ye("\\@firstoftwo",(function(e){return{tokens:e.consumeArgs(2)[0],numArgs:0}})),Ye("\\@secondoftwo",(function(e){return{tokens:e.consumeArgs(2)[1],numArgs:0}})),Ye("\\@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}})),Ye("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Ye("\\TextOrMath",(function(e){const t=e.consumeArgs(2);return"text"===e.mode?{tokens:t[0],numArgs:0}:{tokens:t[1],numArgs:0}}));const Ke=e=>{let t="";for(let r=e.length-1;r>-1;r--)t+=e[r].text;return t},Qe={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},et=e=>{const t=e.future().text;return"EOF"===t?[null,""]:[Qe[t.charAt(0)],t]},tt=(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}Ye("\\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=Qe[a.charAt(0)],null==s||s>=r)throw new e(`Invalid base-${r} digit ${n.text}`);for(s=tt(s,a,r),[o,a]=et(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"};Ye("\\dots",(function(e){let t="\\dotso";const r=e.expandAfterFuture().text;return r in nt?t=nt[r]:("\\not"===r.slice(0,4)||r in D.math&&["bin","rel"].includes(D.math[r].group))&&(t="\\dotsb"),t}));const st={")":!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};Ye("\\dotso",(function(e){return e.future().text in st?"\\ldots\\,":"\\ldots"})),Ye("\\dotsc",(function(e){const t=e.future().text;return t in st&&","!==t?"\\ldots\\,":"\\ldots"})),Ye("\\cdots",(function(e){return e.future().text in st?"\\@cdots\\,":"\\@cdots"})),Ye("\\dotsb","\\cdots"),Ye("\\dotsm","\\cdots"),Ye("\\dotsi","\\!\\cdots"),Ye("\\idotsint","\\int\\!\\cdots\\!\\int"),Ye("\\dotsx","\\ldots\\,"),Ye("\\DOTSI","\\relax"),Ye("\\DOTSB","\\relax"),Ye("\\DOTSX","\\relax"),Ye("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Ye("\\,","{\\tmspace+{3mu}{.1667em}}"),Ye("\\thinspace","\\,"),Ye("\\>","\\mskip{4mu}"),Ye("\\:","{\\tmspace+{4mu}{.2222em}}"),Ye("\\medspace","\\:"),Ye("\\;","{\\tmspace+{5mu}{.2777em}}"),Ye("\\thickspace","\\;"),Ye("\\!","{\\tmspace-{3mu}{.1667em}}"),Ye("\\negthinspace","\\!"),Ye("\\negmedspace","{\\tmspace-{4mu}{.2222em}}"),Ye("\\negthickspace","{\\tmspace-{5mu}{.277em}}"),Ye("\\enspace","\\kern.5em "),Ye("\\enskip","\\hskip.5em\\relax"),Ye("\\quad","\\hskip1em\\relax"),Ye("\\qquad","\\hskip2em\\relax"),Ye("\\AA","\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax"),Ye("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Ye("\\tag@paren","\\tag@literal{({#1})}"),Ye("\\tag@literal",(t=>{if(t.macros.get("\\df@tag"))throw new e("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"})),Ye("\\notag","\\nonumber"),Ye("\\nonumber","\\gdef\\@eqnsw{0}"),Ye("\\bmod","\\mathbin{\\text{mod}}"),Ye("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Ye("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Ye("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Ye("\\newline","\\\\\\relax"),Ye("\\TeX","\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}"),Ye("\\LaTeX","\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX"),Ye("\\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}}"),Ye("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Ye("\\@hspace","\\hskip #1\\relax"),Ye("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Ye("\\colon",'\\mathpunct{\\char"3a}'),Ye("\\prescript","\\pres@cript{_{#1}^{#2}}{}{#3}"),Ye("\\ordinarycolon",'\\char"3a'),Ye("\\vcentcolon","\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}"),Ye("\\coloneq",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}'),Ye("\\Coloneq",'\\mathrel{\\char"2237\\char"2212}'),Ye("\\Eqqcolon",'\\mathrel{\\char"3d\\char"2237}'),Ye("\\Eqcolon",'\\mathrel{\\char"2212\\char"2237}'),Ye("\\colonapprox",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}'),Ye("\\Colonapprox",'\\mathrel{\\char"2237\\char"2248}'),Ye("\\colonsim",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'),Ye("\\Colonsim",'\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'),Ye("\\ratio","\\vcentcolon"),Ye("\\coloncolon","\\dblcolon"),Ye("\\colonequals","\\coloneqq"),Ye("\\coloncolonequals","\\Coloneqq"),Ye("\\equalscolon","\\eqqcolon"),Ye("\\equalscoloncolon","\\Eqqcolon"),Ye("\\colonminus","\\coloneq"),Ye("\\coloncolonminus","\\Coloneq"),Ye("\\minuscolon","\\eqcolon"),Ye("\\minuscoloncolon","\\Eqcolon"),Ye("\\coloncolonapprox","\\Colonapprox"),Ye("\\coloncolonsim","\\Colonsim"),Ye("\\notni","\\mathrel{\\char`∌}"),Ye("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Ye("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Ye("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Ye("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Ye("\\varlimsup","\\DOTSB\\operatorname*{\\overline{\\text{lim}}}"),Ye("\\varliminf","\\DOTSB\\operatorname*{\\underline{\\text{lim}}}"),Ye("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}"),Ye("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}"),Ye("\\centerdot","{\\medspace\\rule{0.167em}{0.189em}\\medspace}"),Ye("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Ye("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Ye("\\plim","\\DOTSB\\operatorname*{plim}"),Ye("\\leftmodels","\\mathop{\\reflectbox{$\\models$}}"),Ye("\\bra","\\mathinner{\\langle{#1}|}"),Ye("\\ket","\\mathinner{|{#1}\\rangle}"),Ye("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Ye("\\Bra","\\left\\langle#1\\right|"),Ye("\\Ket","\\left|#1\\right\\rangle");const ot=(e,t)=>{const r=`}\\,\\middle${"|"===t[0]?"\\vert":"\\Vert"}\\,{`;return e.slice(0,t.index)+r+e.slice(t.index+t[0].length)};Ye("\\Braket",(function(e){let t=rt(e);const r=/\|\||\||\\\|/g;let n;for(;null!==(n=r.exec(t));)t=ot(t,n);return"\\left\\langle{"+t+"}\\right\\rangle"})),Ye("\\Set",(function(e){let t=rt(e);const r=/\|\||\||\\\|/.exec(t);return r&&(t=ot(t,r)),"\\left\\{\\:{"+t+"}\\:\\right\\}"})),Ye("\\set",(function(e){return"\\{{"+rt(e).replace(/\|/,"}\\mid{")+"}\\}"})),Ye("\\angln","{\\angl n}"),Ye("\\odv","\\@ifstar\\odv@next\\odv@numerator"),Ye("\\odv@numerator","\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}"),Ye("\\odv@next","\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1"),Ye("\\pdv","\\@ifstar\\pdv@next\\pdv@numerator");const at=e=>{const t=e[0][0].text,r=Ke(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 lt(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}Ye("\\pdv@numerator",(function(e){const[t,r,n]=at(e.consumeArgs(2));return`\\frac{${r} ${t}}{${n}}`})),Ye("\\pdv@next",(function(e){const[t,r,n]=at(e.consumeArgs(2));return`\\frac{${r}}{${n}} ${t}`})),Ye("\\upalpha","\\up@greek{\\alpha}"),Ye("\\upbeta","\\up@greek{\\beta}"),Ye("\\upgamma","\\up@greek{\\gamma}"),Ye("\\updelta","\\up@greek{\\delta}"),Ye("\\upepsilon","\\up@greek{\\epsilon}"),Ye("\\upzeta","\\up@greek{\\zeta}"),Ye("\\upeta","\\up@greek{\\eta}"),Ye("\\uptheta","\\up@greek{\\theta}"),Ye("\\upiota","\\up@greek{\\iota}"),Ye("\\upkappa","\\up@greek{\\kappa}"),Ye("\\uplambda","\\up@greek{\\lambda}"),Ye("\\upmu","\\up@greek{\\mu}"),Ye("\\upnu","\\up@greek{\\nu}"),Ye("\\upxi","\\up@greek{\\xi}"),Ye("\\upomicron","\\up@greek{\\omicron}"),Ye("\\uppi","\\up@greek{\\pi}"),Ye("\\upalpha","\\up@greek{\\alpha}"),Ye("\\uprho","\\up@greek{\\rho}"),Ye("\\upsigma","\\up@greek{\\sigma}"),Ye("\\uptau","\\up@greek{\\tau}"),Ye("\\upupsilon","\\up@greek{\\upsilon}"),Ye("\\upphi","\\up@greek{\\phi}"),Ye("\\upchi","\\up@greek{\\chi}"),Ye("\\uppsi","\\up@greek{\\psi}"),Ye("\\upomega","\\up@greek{\\omega}"),Ye("\\invamp",'\\mathbin{\\char"214b}'),Ye("\\parr",'\\mathbin{\\char"214b}'),Ye("\\upand",'\\mathbin{\\char"214b}'),Ye("\\with",'\\mathbin{\\char"26}'),Ye("\\multimapinv",'\\mathrel{\\char"27dc}'),Ye("\\multimapboth",'\\mathrel{\\char"29df}'),Ye("\\scoh",'{\\mkern5mu\\char"2322\\mkern5mu}'),Ye("\\sincoh",'{\\mkern5mu\\char"2323\\mkern5mu}'),Ye("\\coh",'{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}}\n{\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}'),Ye("\\incoh",'{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}}\n{\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}'),Ye("\\standardstate","\\text{\\tiny\\char`⦵}");const it=t=>{if(!t.parser.settings.displayMode)throw new e(`{${t.envName}} can be used only in display mode.`)},ct=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/,mt=e=>{let t=e.get("\\arraystretch");"string"!=typeof t&&(t=Ke(t.tokens)),t=isNaN(t)?null:Number(t);let r=e.get("\\arraycolsep");"string"!=typeof r&&(r=Ke(r.tokens));const n=ct.exec(r);return[t,n?{number:+(n[1]+n[2]),unit:n[3]}:null]},pt=t=>{let r="";for(let n=0;n1||!a)&&h.pop(),f.push(pt(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},bt=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]=mt(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:bt}),Ie({type:"array",names:["bordermatrix"],props:{numArgs:0},handler(e){const t=dt(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 je([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:bt}),Ie({type:"array",names:["smallmatrix"],props:{numArgs:0},handler:e=>dt(e.parser,{envClasses:["small"]},"script"),mathmlBuilder:bt}),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=dt(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:bt}),Ie({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(e){const t=dt(e.parser,{cols:[],envClasses:["cases"]},ht(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:bt}),Ie({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:yt,mathmlBuilder:bt}),Ie({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:yt,mathmlBuilder:bt}),Ie({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(e){"gathered"!==e.envName&&it(e);const t={cols:[],envClasses:["abut","jot"],autoTag:ut(e.envName),emptySingleRow:!0,leqno:e.parser.settings.leqno};return dt(e.parser,t,"display")},mathmlBuilder:bt}),Ie({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(e){it(e);const t={autoTag:ut(e.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,envClasses:["align"],leqno:e.parser.settings.leqno};return dt(e.parser,t,"display")},mathmlBuilder:bt}),Ie({type:"array",names:["multline","multline*"],props:{numArgs:0},handler(e){it(e);const t={autoTag:"multline"===e.envName,maxNumCols:1,envClasses:["jot","multline"],leqno:e.parser.settings.leqno};return dt(e.parser,t,"display")},mathmlBuilder:bt}),Ie({type:"array",names:["CD"],props:{numArgs:0},handler:t=>(it(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 l=0;l-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 c=r+1;c{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=wt.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},qt=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}'),Ot=(t,r)=>{let n="";if("HTML"===t){if(!xt.test(r))throw new e("Invalid HTML input.");n=r}else if("RGB"===t){if(!vt.test(r))throw new e("Invalid RGB input.");r.split(",").map((e=>{n+=St(Number(e.trim()))}))}else{if(!At.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+=St(Number((255*r).toFixed(0)))}))}return"#"!==n.charAt(0)&&(n="#"+n),n},Bt=(t,r,n)=>{const s=`\\\\color@${t}`;if(!kt.exec(t))throw new e("Invalid color: '"+t+"'",n);return Tt.test(t)?"#"+t:("#"===t.charAt(0)||(r.has(s)?t=r.get(s).tokens[0].text:qt[t]&&(t=qt[t])),t)},Ct=(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=Ot(s,e)}else o=Bt(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:Ct}),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=Ot(o,e)}else a=Bt(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:Ct}),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=Ot(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 Nt={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},zt=t=>{const r=t.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(r))throw new e("Expected a control sequence",t);return r},$t=(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(Nt[n.text])return"\\global"!==r&&"\\\\globallong"!==r||(n.text=Nt[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===Nt[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=zt(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 $t(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=zt(e.gullet.popToken()),n=e.gullet.popToken(),s=e.gullet.popToken();return $t(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=zt(t.gullet.popToken()),t.gullet.popToken()):n=zt(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 Et={"\\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}},It={"(":")","\\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","⦃":"⦄"},Lt=new Set(Object.keys(It));new Set(Object.values(It));const Ft=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","."]),Mt=new Set(["}","\\left","\\middle","\\right"]),Dt=e=>e.length>0&&(Ft.has(e)||Et[e]||Mt.has(e)),Gt=[0,1.2,1.8,2.4,3];function Pt(t,r){"ordgroup"===t.type&&1===t.body.length&&(t=t.body[0]);const n=Me(t);if(n&&Ft.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 jt=new Set(["/","\\","\\backslash","∖","\\vert","|"]),Rt=(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 Ut(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=Pt(t[0],e),n={type:"delimsizing",mode:e.parser.mode,size:Et[e.funcName].size,mclass:Et[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"),(jt.has(r)||r.indexOf("arrow")>-1)&&n.setAttribute("stretchy","true"),n.setAttribute("symmetric","true"),n.setAttribute("minsize",Gt[e.size]+"em"),n.setAttribute("maxsize",Gt[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:Pt(t[0],e).text})}),h({type:"leftright",names:["\\left"],props:{numArgs:1,argTypes:["primitive"]},handler:(t,r)=>{const n=Pt(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'`);Pt({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)=>{Ut(e);const r=ue(e.body,t),n=Rt(e.left,e.mode,"prefix",!0);r.unshift(n);const s=Rt(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(Lt),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=It[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)=>{Ut(e);const r=ue(e.body,t),n=Rt(e.left,e.mode,"prefix",!1);r.unshift(n);const s=Rt(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=Pt(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 Ht=["\\boxed","\\fcolorbox","\\colorbox"],Vt=(e,t)=>{const r=Ht.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=Ot(s,e)}else o=Bt(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:Vt}),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=Ot(s,e),o=Ot(s,t)}else a=Bt(Le(r[0],"raw").string,e.gullet.macros),o=Bt(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:Vt}),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:Vt}),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:Vt}),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:Vt}),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 _t=(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 Wt&&(s=Wt[s]),{type:"font",mode:e.mode,font:s.slice(1),body:n}},mathmlBuilder:_t}),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:_t});const Xt=["display","text","script","scriptscript"],Zt={auto:-1,display:0,text:0,script:1,scriptscript:2},Yt=(e,t)=>{t=((e,t)=>{let r=t;if("display"===e){const e=r.level>=We?_e:Ve;r=r.withLevel(e)}else"text"===e&&r.level===Ve?r=r.withLevel(_e):"auto"===e?r=r.incrementLevel():"script"===e?r=r.withLevel(We):"scriptscript"===e&&(r=r.withLevel(Xe));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",Zt[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:Yt}),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 Jt=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?Jt(s.text):null,a=f(t[1]),l="atom"===a.type&&"close"===a.family?Jt(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=Xt[Number(e.text)]}}else u=Le(u,"textord"),p=Xt[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:Yt}),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:Yt}),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(_e),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 Kt=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?Qt(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 Qt=function(e){return e>=.05555&&e<=.05556?" ":e>=.1666&&e<=.1667?" ":e>=.2222&&e<=.2223?" ":e>=.2777&&e<=.2778?"  ":""},er=/[^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(er,"")}),mathmlBuilder(e,t){const r=new B("mrow",[],["tml-label"]);return e.string.length>0&&r.setLabel(e.string),r}});const tr=["\\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(tr.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 Ve:return e.display;case _e:return e.text;case We:return e.script;case Xe:return e.scriptscript;default:return e.text}})(e,t);return de(r,t)}});const rr=["text","textord","mathord","atom"];function nr(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(!rr.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:nr});const sr=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:sr(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?sr(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:nr});const or=(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=or(e.prescripts.sub,t,s),l=or(e.prescripts.sup,t,s);if(e.isSideset&&(a.setAttribute("style","text-align: left;"),l.setAttribute("style","text-align: left;")),e.postscripts){o=[r,or(e.postscripts.sub,t,s),or(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 ar=["textord","mathord","atom"],lr=["\\smallint"],ir=["textord","mathord","ordgroup","close","leftright","font"],cr=e=>{e.attributes.lspace="0.1667em",e.attributes.rspace="0.1667em"},mr=(e,t)=>{let r;if(e.symbol)r=new B("mo",[oe(e.name,e.mode)]),lr.includes(e.name)?r.setAttribute("largeop","false"):r.setAttribute("movablelimits","false"),e.fromMathOp&&cr(r);else if(e.body)r=new B("mo",ue(e.body,t)),e.fromMathOp&&cr(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},pr={"∏":"\\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=pr[n]),{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!0,stack:!1,name:n}},mathmlBuilder:mr}),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&&ar.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:mr});const ur={"∫":"\\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:Dt(n),needsLeadingSpace:r.length>0&&ir.includes(r),name:t}},mathmlBuilder:mr}),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:Dt(n),needsLeadingSpace:r.length>0&&ir.includes(r),name:t}},mathmlBuilder:mr}),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=ur[r]),{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!0,stack:!1,name:r}},mathmlBuilder:mr});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:Dt(o),needsLeadingSpace:s.length>0&&ir.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}}),Ye("\\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 dr=(e,t)=>{const r=t.withLevel(_e),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:dr}),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:dr}),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(er,"")}),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 hr=/^[0-9]$/,gr={0:"₀",1:"₁",2:"₂",3:"₃",4:"₄",5:"₅",6:"₆",7:"₇",8:"₈",9:"₉"},fr={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||!hr.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||!hr.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=>fr[e])).join(""),n=e.denominator.split("").map((e=>gr[e])).join(""),s=new C(r+"⁄"+n,e.mode,t);return new B("mn",[s],["special-fraction"])}});const br={"\\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(br[e.funcName]),n=ue(e.body,r),s=N(n),o=(br[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 yr=["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&&yr.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 wr={display:0,text:1,script:2,scriptscript:3},xr={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(wr[e.scriptLevel]),n=ue(e.body,r),s=N(n),o=xr[e.scriptLevel];return s.setAttribute("scriptlevel",o[0]),s.setAttribute("displaystyle",o[1]),s}});const kr=/^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===Ve||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(t.level===Ve||r.limits)?"munderover":"msubsup"}else{const r=e.base;m=e.stack||r&&"op"===r.type&&r.limits&&(t.level===Ve||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.level===Ve)?"munder":"msub"}else{const r=e.base;m=r&&"op"===r.type&&r.limits&&(t.level===Ve||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.level===Ve)?"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 kr.test(m)&&(p=new B("mrow",[p]));return p}});const vr=["\\shortmid","\\nshortmid","\\shortparallel","\\nshortparallel","\\smallsetminus"],Ar=["\\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||Ar.includes(e)})(e.text))r.setAttribute("stretchy","false");else if(vr.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 Tr={mathbf:"bold",mathrm:"normal",textit:"italic",mathit:"italic",mathnormal:"italic",mathbb:"double-struck",mathcal:"script",mathfrak:"fraktur",mathscr:"script",mathsf:"sans-serif",mathtt:"monospace"},Sr=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=>qr[e]||119899,"script-bold":e=>119951,fraktur:e=>Or[e]||120003,"fraktur-bold":e=>120107,"double-struck":e=>Br[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=>qr[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=>Cr[e]||-51,italic:e=>0,"bold-italic":e=>Nr[e]||58,script:e=>0,"script-bold":e=>0,fraktur:e=>0,"fraktur-bold":e=>0,"double-struck":e=>0,"sans-serif":e=>zr[e]||116,"sans-serif-bold":e=>zr[e]||116,"sans-serif-italic":e=>0,"sans-serif-bold-italic":e=>$r[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}}),Ir=(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=>Ir(e,o))).join("")),a=new B(t,[s])}else if("text"===e.mode)"normal"!==o&&(s.text=Ir(s.text,o)),a=new B("mtext",[s]);else if(Dr.has(e.text))a=new B("mo",[s]),a.classes.push("tml-prime");else{const e=s.text;"italic"!==o&&(s.text=Ir(s.text,o)),a=new B("mi",[s]),s.text===e&&Mr.test(e)&&a.setAttribute("mathvariant","italic")}return a}});const Gr={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},Pr={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};g({type:"spacing",mathmlBuilder(t,r){let n;if(Object.prototype.hasOwnProperty.call(Pr,t.text))n=new B("mtext",[new C(" ")]);else{if(!Object.prototype.hasOwnProperty.call(Gr,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 jr={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm","\\textsc":"textsc"},Rr={"\\textbf":"textbf","\\textmd":"textmd"},Ur={"\\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?jr[r]?t.withTextFontFamily(jr[r]):Rr[r]?t.withTextFontWeight(Rr[r]):"\\emph"===r?"textit"===t.fontShape?t.withTextFontShape("textup"):t.withTextFontShape("textit"):t.withTextFontShape(Ur[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(Hr(e)),n=new B("mtext",[r]);return n.setAttribute("mathvariant","monospace"),n}});const Hr=e=>e.body.replace(/ /g,e.star?"␣":" "),Vr=u,_r="[ \r\n\t]",Wr=`(\\\\[a-zA-Z@]+)${_r}*`,Xr="[̀-ͯ]",Zr=new RegExp(`${Xr}+$`),Yr=`(${_r}+)|\\\\(\n|[ \r\t]+\n?)[ \r\t]*|([!-\\[\\]-‧‪-퟿豈-￿]${Xr}*|[\ud800-\udbff][\udc00-\udfff]${Xr}*|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5|${Wr}|\\\\[^\ud800-\udfff])`;class Jr{constructor(e,t){this.input=e,this.settings=t,this.tokenRegex=new RegExp(Yr,"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 He("EOF",new Ue(this,r,r));const n=this.tokenRegex.exec(t);if(null===n||n.index!==r)throw new e(`Unexpected character: '${t[r]}'`,new He(t[r],new Ue(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 He(s,new Ue(this,r,this.tokenRegex.lastIndex))}}class Kr{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 Qr={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0};class en{constructor(e,t,r){this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new Kr(Je,t.macros),this.mode=r,this.stack=[]}feed(e){this.lexer=new Jr(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 He("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 He(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 Jr(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(Vr,e)||Object.prototype.hasOwnProperty.call(D.math,e)||Object.prototype.hasOwnProperty.call(D.text,e)||Object.prototype.hasOwnProperty.call(Qr,e)}isExpandable(e){const t=this.macros.get(e);return null!=t?"string"==typeof t||"function"==typeof t||!t.unexpandable:Object.prototype.hasOwnProperty.call(Vr,e)&&!Vr[e].primitive}}const tn=/^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/,rn=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","ᵝ":"β","ᵞ":"γ","ᵟ":"δ","ᵠ":"ϕ","ᵡ":"χ","ᶿ":"θ"}),nn=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 sn={"́":{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"}},on={"á":"á","à":"à","ä":"ä","ǟ":"ǟ","ã":"ã","ā":"ā","ă":"ă","ắ":"ắ","ằ":"ằ","ẵ":"ẵ","ǎ":"ǎ","â":"â","ấ":"ấ","ầ":"ầ","ẫ":"ẫ","ȧ":"ȧ","ǡ":"ǡ","å":"å","ǻ":"ǻ","ḃ":"ḃ","ć":"ć","č":"č","ĉ":"ĉ","ċ":"ċ","ď":"ď","ḋ":"ḋ","é":"é","è":"è","ë":"ë","ẽ":"ẽ","ē":"ē","ḗ":"ḗ","ḕ":"ḕ","ĕ":"ĕ","ě":"ě","ê":"ê","ế":"ế","ề":"ề","ễ":"ễ","ė":"ė","ḟ":"ḟ","ǵ":"ǵ","ḡ":"ḡ","ğ":"ğ","ǧ":"ǧ","ĝ":"ĝ","ġ":"ġ","ḧ":"ḧ","ȟ":"ȟ","ĥ":"ĥ","ḣ":"ḣ","í":"í","ì":"ì","ï":"ï","ḯ":"ḯ","ĩ":"ĩ","ī":"ī","ĭ":"ĭ","ǐ":"ǐ","î":"î","ǰ":"ǰ","ĵ":"ĵ","ḱ":"ḱ","ǩ":"ǩ","ĺ":"ĺ","ľ":"ľ","ḿ":"ḿ","ṁ":"ṁ","ń":"ń","ǹ":"ǹ","ñ":"ñ","ň":"ň","ṅ":"ṅ","ó":"ó","ò":"ò","ö":"ö","ȫ":"ȫ","õ":"õ","ṍ":"ṍ","ṏ":"ṏ","ȭ":"ȭ","ō":"ō","ṓ":"ṓ","ṑ":"ṑ","ŏ":"ŏ","ǒ":"ǒ","ô":"ô","ố":"ố","ồ":"ồ","ỗ":"ỗ","ȯ":"ȯ","ȱ":"ȱ","ő":"ő","ṕ":"ṕ","ṗ":"ṗ","ŕ":"ŕ","ř":"ř","ṙ":"ṙ","ś":"ś","ṥ":"ṥ","š":"š","ṧ":"ṧ","ŝ":"ŝ","ṡ":"ṡ","ẗ":"ẗ","ť":"ť","ṫ":"ṫ","ú":"ú","ù":"ù","ü":"ü","ǘ":"ǘ","ǜ":"ǜ","ǖ":"ǖ","ǚ":"ǚ","ũ":"ũ","ṹ":"ṹ","ū":"ū","ṻ":"ṻ","ŭ":"ŭ","ǔ":"ǔ","û":"û","ů":"ů","ű":"ű","ṽ":"ṽ","ẃ":"ẃ","ẁ":"ẁ","ẅ":"ẅ","ŵ":"ŵ","ẇ":"ẇ","ẘ":"ẘ","ẍ":"ẍ","ẋ":"ẋ","ý":"ý","ỳ":"ỳ","ÿ":"ÿ","ỹ":"ỹ","ȳ":"ȳ","ŷ":"ŷ","ẏ":"ẏ","ẙ":"ẙ","ź":"ź","ž":"ž","ẑ":"ẑ","ż":"ż","Á":"Á","À":"À","Ä":"Ä","Ǟ":"Ǟ","Ã":"Ã","Ā":"Ā","Ă":"Ă","Ắ":"Ắ","Ằ":"Ằ","Ẵ":"Ẵ","Ǎ":"Ǎ","Â":"Â","Ấ":"Ấ","Ầ":"Ầ","Ẫ":"Ẫ","Ȧ":"Ȧ","Ǡ":"Ǡ","Å":"Å","Ǻ":"Ǻ","Ḃ":"Ḃ","Ć":"Ć","Č":"Č","Ĉ":"Ĉ","Ċ":"Ċ","Ď":"Ď","Ḋ":"Ḋ","É":"É","È":"È","Ë":"Ë","Ẽ":"Ẽ","Ē":"Ē","Ḗ":"Ḗ","Ḕ":"Ḕ","Ĕ":"Ĕ","Ě":"Ě","Ê":"Ê","Ế":"Ế","Ề":"Ề","Ễ":"Ễ","Ė":"Ė","Ḟ":"Ḟ","Ǵ":"Ǵ","Ḡ":"Ḡ","Ğ":"Ğ","Ǧ":"Ǧ","Ĝ":"Ĝ","Ġ":"Ġ","Ḧ":"Ḧ","Ȟ":"Ȟ","Ĥ":"Ĥ","Ḣ":"Ḣ","Í":"Í","Ì":"Ì","Ï":"Ï","Ḯ":"Ḯ","Ĩ":"Ĩ","Ī":"Ī","Ĭ":"Ĭ","Ǐ":"Ǐ","Î":"Î","İ":"İ","Ĵ":"Ĵ","Ḱ":"Ḱ","Ǩ":"Ǩ","Ĺ":"Ĺ","Ľ":"Ľ","Ḿ":"Ḿ","Ṁ":"Ṁ","Ń":"Ń","Ǹ":"Ǹ","Ñ":"Ñ","Ň":"Ň","Ṅ":"Ṅ","Ó":"Ó","Ò":"Ò","Ö":"Ö","Ȫ":"Ȫ","Õ":"Õ","Ṍ":"Ṍ","Ṏ":"Ṏ","Ȭ":"Ȭ","Ō":"Ō","Ṓ":"Ṓ","Ṑ":"Ṑ","Ŏ":"Ŏ","Ǒ":"Ǒ","Ô":"Ô","Ố":"Ố","Ồ":"Ồ","Ỗ":"Ỗ","Ȯ":"Ȯ","Ȱ":"Ȱ","Ő":"Ő","Ṕ":"Ṕ","Ṗ":"Ṗ","Ŕ":"Ŕ","Ř":"Ř","Ṙ":"Ṙ","Ś":"Ś","Ṥ":"Ṥ","Š":"Š","Ṧ":"Ṧ","Ŝ":"Ŝ","Ṡ":"Ṡ","Ť":"Ť","Ṫ":"Ṫ","Ú":"Ú","Ù":"Ù","Ü":"Ü","Ǘ":"Ǘ","Ǜ":"Ǜ","Ǖ":"Ǖ","Ǚ":"Ǚ","Ũ":"Ũ","Ṹ":"Ṹ","Ū":"Ū","Ṻ":"Ṻ","Ŭ":"Ŭ","Ǔ":"Ǔ","Û":"Û","Ů":"Ů","Ű":"Ű","Ṽ":"Ṽ","Ẃ":"Ẃ","Ẁ":"Ẁ","Ẅ":"Ẅ","Ŵ":"Ŵ","Ẇ":"Ẇ","Ẍ":"Ẍ","Ẋ":"Ẋ","Ý":"Ý","Ỳ":"Ỳ","Ÿ":"Ÿ","Ỹ":"Ỹ","Ȳ":"Ȳ","Ŷ":"Ŷ","Ẏ":"Ẏ","Ź":"Ź","Ž":"Ž","Ẑ":"Ẑ","Ż":"Ż","ά":"ά","ὰ":"ὰ","ᾱ":"ᾱ","ᾰ":"ᾰ","έ":"έ","ὲ":"ὲ","ή":"ή","ὴ":"ὴ","ί":"ί","ὶ":"ὶ","ϊ":"ϊ","ΐ":"ΐ","ῒ":"ῒ","ῑ":"ῑ","ῐ":"ῐ","ό":"ό","ὸ":"ὸ","ύ":"ύ","ὺ":"ὺ","ϋ":"ϋ","ΰ":"ΰ","ῢ":"ῢ","ῡ":"ῡ","ῠ":"ῠ","ώ":"ώ","ὼ":"ὼ","Ύ":"Ύ","Ὺ":"Ὺ","Ϋ":"Ϋ","Ῡ":"Ῡ","Ῠ":"Ῠ","Ώ":"Ώ","Ὼ":"Ὼ"};const an=["bin","op","open","punct","rel"],ln=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/,cn=/^ *\\text/;class mn{constructor(e,t,r=!1){this.mode="math",this.gullet=new en(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 He("}")),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!==mn.endOfExpression.indexOf(s.text))break;if(t&&s.text===t)break;if(r&&"\\middle"===s.text)break;if(e&&Vr[s.text]&&Vr[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||Zr.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:Ue.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},un=[2,2,3,3];class dn{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 dn(t)}withLevel(e){return this.extend({level:e})}incrementLevel(){return this.extend({level:Math.min(this.level+1,3)})}inSubOrSup(){return this.extend({level:un[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 hn(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 gn=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=gn(t[s].right,e,t[s].left.length),-1===r)break;const o=e.slice(0,r+t[s].right.length),a=fn.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+" ")))&&kn(n,t)}}};let vn=function(e,t,r={}){t.textContent="";const n="math"===t.tagName.toLowerCase();n&&(r.wrap="none");const s=An(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."),vn=function(){throw new e("Temml doesn't work in quirks mode.")});const An=function(t,r){const n=new p(r);try{const e=pn(t,n);return ye(e,t,new dn({level:n.displayMode?Ve:_e,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:vn,renderToString:function(e,t){return An(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 yn[e];if("$+"===e||"(+"===e)return yn[e.slice(0,1)].concat(wn);return"ams"===e?wn:"all"===e?yn["("].concat(yn.$).concat(wn):bn})(r.fences):r.delimiters=r.delimiters||bn,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||{},kn(e,r),hn(e)},postProcess:hn,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 mn(e,r,!0);delete n.gullet.macros.current["\\df@tag"];return n.parse()},__parse:function(e,t){const r=new p(t);return pn(e,r)},__renderToMathMLTree:An,__defineSymbol:G,__defineMacro:Ye}}(); \ No newline at end of file