temml 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/contrib/auto-render/README.md +89 -0
  4. package/contrib/auto-render/auto-render.js +128 -0
  5. package/contrib/auto-render/dist/auto-render.js +217 -0
  6. package/contrib/auto-render/dist/auto-render.min.js +1 -0
  7. package/contrib/auto-render/splitAtDelimiters.js +84 -0
  8. package/contrib/auto-render/test/auto-render-spec.js +234 -0
  9. package/contrib/auto-render/test/auto-render.js +217 -0
  10. package/contrib/auto-render/test/test_page.html +59 -0
  11. package/contrib/mhchem/README.md +26 -0
  12. package/contrib/mhchem/mhchem.js +1705 -0
  13. package/contrib/mhchem/mhchem.min.js +1 -0
  14. package/contrib/physics/README.md +20 -0
  15. package/contrib/physics/physics.js +131 -0
  16. package/contrib/texvc/README.md +23 -0
  17. package/contrib/texvc/texvc.js +61 -0
  18. package/dist/Temml-Asana.css +201 -0
  19. package/dist/Temml-Latin-Modern.css +216 -0
  20. package/dist/Temml-Libertinus.css +214 -0
  21. package/dist/Temml-Local.css +194 -0
  22. package/dist/Temml-STIX2.css +203 -0
  23. package/dist/Temml.woff2 +0 -0
  24. package/dist/temml.cjs +13122 -0
  25. package/dist/temml.js +11225 -0
  26. package/dist/temml.min.js +1 -0
  27. package/dist/temml.mjs +13120 -0
  28. package/dist/temmlPostProcess.js +70 -0
  29. package/package.json +34 -0
  30. package/src/Lexer.js +121 -0
  31. package/src/MacroExpander.js +437 -0
  32. package/src/Namespace.js +107 -0
  33. package/src/ParseError.js +64 -0
  34. package/src/Parser.js +977 -0
  35. package/src/Settings.js +49 -0
  36. package/src/SourceLocation.js +29 -0
  37. package/src/Style.js +144 -0
  38. package/src/Token.js +40 -0
  39. package/src/buildMathML.js +235 -0
  40. package/src/constants.js +25 -0
  41. package/src/defineEnvironment.js +25 -0
  42. package/src/defineFunction.js +69 -0
  43. package/src/defineMacro.js +11 -0
  44. package/src/domTree.js +185 -0
  45. package/src/environments/array.js +791 -0
  46. package/src/environments/cd.js +252 -0
  47. package/src/environments.js +8 -0
  48. package/src/functions/accent.js +127 -0
  49. package/src/functions/accentunder.js +38 -0
  50. package/src/functions/arrow.js +204 -0
  51. package/src/functions/cancelto.js +36 -0
  52. package/src/functions/char.js +33 -0
  53. package/src/functions/color.js +253 -0
  54. package/src/functions/cr.js +46 -0
  55. package/src/functions/def.js +259 -0
  56. package/src/functions/delimsizing.js +304 -0
  57. package/src/functions/enclose.js +193 -0
  58. package/src/functions/envTag.js +38 -0
  59. package/src/functions/environment.js +59 -0
  60. package/src/functions/font.js +123 -0
  61. package/src/functions/genfrac.js +333 -0
  62. package/src/functions/hbox.js +29 -0
  63. package/src/functions/horizBrace.js +32 -0
  64. package/src/functions/href.js +90 -0
  65. package/src/functions/html.js +95 -0
  66. package/src/functions/includegraphics.js +131 -0
  67. package/src/functions/kern.js +75 -0
  68. package/src/functions/label.js +29 -0
  69. package/src/functions/lap.js +75 -0
  70. package/src/functions/math.js +40 -0
  71. package/src/functions/mathchoice.js +41 -0
  72. package/src/functions/mclass.js +201 -0
  73. package/src/functions/multiscript.js +91 -0
  74. package/src/functions/not.js +46 -0
  75. package/src/functions/op.js +338 -0
  76. package/src/functions/operatorname.js +139 -0
  77. package/src/functions/ordgroup.js +9 -0
  78. package/src/functions/phantom.js +73 -0
  79. package/src/functions/pmb.js +31 -0
  80. package/src/functions/raise.js +68 -0
  81. package/src/functions/ref.js +28 -0
  82. package/src/functions/relax.js +16 -0
  83. package/src/functions/rule.js +52 -0
  84. package/src/functions/sizing.js +64 -0
  85. package/src/functions/smash.js +66 -0
  86. package/src/functions/sqrt.js +31 -0
  87. package/src/functions/styling.js +58 -0
  88. package/src/functions/supsub.js +135 -0
  89. package/src/functions/symbolsOp.js +53 -0
  90. package/src/functions/symbolsOrd.js +102 -0
  91. package/src/functions/symbolsSpacing.js +53 -0
  92. package/src/functions/tag.js +8 -0
  93. package/src/functions/text.js +75 -0
  94. package/src/functions/tip.js +63 -0
  95. package/src/functions/toggle.js +13 -0
  96. package/src/functions/verb.js +33 -0
  97. package/src/functions.js +57 -0
  98. package/src/linebreaking.js +159 -0
  99. package/src/macros.js +708 -0
  100. package/src/mathMLTree.js +175 -0
  101. package/src/parseNode.js +42 -0
  102. package/src/parseTree.js +40 -0
  103. package/src/postProcess.js +57 -0
  104. package/src/replace.js +225 -0
  105. package/src/stretchy.js +66 -0
  106. package/src/svg.js +110 -0
  107. package/src/symbols.js +972 -0
  108. package/src/tree.js +50 -0
  109. package/src/unicodeAccents.js +16 -0
  110. package/src/unicodeScripts.js +119 -0
  111. package/src/unicodeSupOrSub.js +108 -0
  112. package/src/unicodeSymbolBuilder.js +31 -0
  113. package/src/unicodeSymbols.js +320 -0
  114. package/src/units.js +109 -0
  115. package/src/utils.js +109 -0
  116. package/src/variant.js +103 -0
  117. package/temml.js +181 -0
@@ -0,0 +1,28 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import { invalidIdRegEx } from "./label";
4
+
5
+ defineFunction({
6
+ type: "ref",
7
+ names: ["\\ref", "\\eqref"],
8
+ props: {
9
+ numArgs: 1,
10
+ argTypes: ["raw"]
11
+ },
12
+ handler({ parser, funcName }, args) {
13
+ return {
14
+ type: "ref",
15
+ mode: parser.mode,
16
+ funcName,
17
+ string: args[0].string.replace(invalidIdRegEx, "")
18
+ };
19
+ },
20
+ mathmlBuilder(group, style) {
21
+ // Create an empty text node. Set a class and an href.
22
+ // The post-processor will populate with the target's tag or equation number.
23
+ const classes = group.funcName === "\\ref" ? ["tml-ref"] : ["tml-ref", "tml-eqref"]
24
+ const node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("")], classes)
25
+ node.setAttribute("href", "#" + group.string)
26
+ return node
27
+ }
28
+ });
@@ -0,0 +1,16 @@
1
+ import defineFunction from "../defineFunction"
2
+
3
+ defineFunction({
4
+ type: "internal",
5
+ names: ["\\relax"],
6
+ props: {
7
+ numArgs: 0,
8
+ allowedInText: true
9
+ },
10
+ handler({ parser }) {
11
+ return {
12
+ type: "internal",
13
+ mode: parser.mode
14
+ };
15
+ }
16
+ })
@@ -0,0 +1,52 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import { assertNodeType } from "../parseNode";
4
+ import { calculateSize } from "../units";
5
+
6
+ defineFunction({
7
+ type: "rule",
8
+ names: ["\\rule"],
9
+ props: {
10
+ numArgs: 2,
11
+ numOptionalArgs: 1,
12
+ argTypes: ["size", "size", "size"]
13
+ },
14
+ handler({ parser }, args, optArgs) {
15
+ const shift = optArgs[0];
16
+ const width = assertNodeType(args[0], "size");
17
+ const height = assertNodeType(args[1], "size");
18
+ return {
19
+ type: "rule",
20
+ mode: parser.mode,
21
+ shift: shift && assertNodeType(shift, "size").value,
22
+ width: width.value,
23
+ height: height.value
24
+ };
25
+ },
26
+ mathmlBuilder(group, style) {
27
+ const width = calculateSize(group.width, style);
28
+ const height = calculateSize(group.height, style);
29
+ const shift = group.shift
30
+ ? calculateSize(group.shift, style)
31
+ : { number: 0, unit: "em" };
32
+ const color = (style.color && style.getColor()) || "black";
33
+
34
+ const rule = new mathMLTree.MathNode("mspace");
35
+ if (width.number > 0 && height.number > 0) {
36
+ rule.setAttribute("mathbackground", color);
37
+ }
38
+ rule.setAttribute("width", width.number + width.unit);
39
+ rule.setAttribute("height", height.number + height.unit);
40
+ if (shift.number === 0) { return rule }
41
+
42
+ const wrapper = new mathMLTree.MathNode("mpadded", [rule]);
43
+ if (shift.number >= 0) {
44
+ wrapper.setAttribute("height", "+" + shift.number + shift.unit);
45
+ } else {
46
+ wrapper.setAttribute("height", shift.number + shift.unit);
47
+ wrapper.setAttribute("depth", "+" + -shift.number + shift.unit);
48
+ }
49
+ wrapper.setAttribute("voffset", shift.number + shift.unit);
50
+ return wrapper;
51
+ }
52
+ });
@@ -0,0 +1,64 @@
1
+ import defineFunction from "../defineFunction";
2
+ import { wrapWithMstyle } from "../mathMLTree"
3
+ import * as mml from "../buildMathML";
4
+
5
+ // The size mappings are taken from TeX with \normalsize=10pt.
6
+ // We don't have to track script level. MathML does that.
7
+ const sizeMap = {
8
+ "\\tiny": 0.5,
9
+ "\\sixptsize": 0.6,
10
+ "\\Tiny": 0.6,
11
+ "\\scriptsize": 0.7,
12
+ "\\footnotesize": 0.8,
13
+ "\\small": 0.9,
14
+ "\\normalsize": 1.0,
15
+ "\\large": 1.2,
16
+ "\\Large": 1.44,
17
+ "\\LARGE": 1.728,
18
+ "\\huge": 2.074,
19
+ "\\Huge": 2.488
20
+ };
21
+
22
+ defineFunction({
23
+ type: "sizing",
24
+ names: [
25
+ "\\tiny",
26
+ "\\sixptsize",
27
+ "\\Tiny",
28
+ "\\scriptsize",
29
+ "\\footnotesize",
30
+ "\\small",
31
+ "\\normalsize",
32
+ "\\large",
33
+ "\\Large",
34
+ "\\LARGE",
35
+ "\\huge",
36
+ "\\Huge"
37
+ ],
38
+ props: {
39
+ numArgs: 0,
40
+ allowedInText: true
41
+ },
42
+ handler: ({ breakOnTokenText, funcName, parser }, args) => {
43
+ if (parser.settings.strict && parser.mode === "math") {
44
+ // eslint-disable-next-line no-console
45
+ console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`)
46
+ }
47
+ const body = parser.parseExpression(false, breakOnTokenText);
48
+ return {
49
+ type: "sizing",
50
+ mode: parser.mode,
51
+ funcName,
52
+ body
53
+ };
54
+ },
55
+ mathmlBuilder: (group, style) => {
56
+ const newStyle = style.withFontSize(sizeMap[group.funcName]);
57
+ const inner = mml.buildExpression(group.body, newStyle);
58
+ // Wrap with an <mstyle> element.
59
+ const node = wrapWithMstyle(inner)
60
+ const factor = (sizeMap[group.funcName] / style.fontSize).toFixed(4)
61
+ node.setAttribute("mathsize", factor + "em");
62
+ return node;
63
+ }
64
+ });
@@ -0,0 +1,66 @@
1
+ // smash, with optional [tb], as in AMS
2
+ import defineFunction from "../defineFunction";
3
+ import mathMLTree from "../mathMLTree";
4
+ import { assertNodeType } from "../parseNode";
5
+
6
+ import * as mml from "../buildMathML";
7
+
8
+ defineFunction({
9
+ type: "smash",
10
+ names: ["\\smash"],
11
+ props: {
12
+ numArgs: 1,
13
+ numOptionalArgs: 1,
14
+ allowedInText: true
15
+ },
16
+ handler: ({ parser }, args, optArgs) => {
17
+ let smashHeight = false;
18
+ let smashDepth = false;
19
+ const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup");
20
+ if (tbArg) {
21
+ // Optional [tb] argument is engaged.
22
+ // ref: amsmath: \renewcommand{\smash}[1][tb]{%
23
+ // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}%
24
+ let letter = "";
25
+ for (let i = 0; i < tbArg.body.length; ++i) {
26
+ const node = tbArg.body[i];
27
+ // TODO: Write an AssertSymbolNode
28
+ letter = node.text;
29
+ if (letter === "t") {
30
+ smashHeight = true;
31
+ } else if (letter === "b") {
32
+ smashDepth = true;
33
+ } else {
34
+ smashHeight = false;
35
+ smashDepth = false;
36
+ break;
37
+ }
38
+ }
39
+ } else {
40
+ smashHeight = true;
41
+ smashDepth = true;
42
+ }
43
+
44
+ const body = args[0];
45
+ return {
46
+ type: "smash",
47
+ mode: parser.mode,
48
+ body,
49
+ smashHeight,
50
+ smashDepth
51
+ };
52
+ },
53
+ mathmlBuilder: (group, style) => {
54
+ const node = new mathMLTree.MathNode("mpadded", [mml.buildGroup(group.body, style)]);
55
+
56
+ if (group.smashHeight) {
57
+ node.setAttribute("height", "0px");
58
+ }
59
+
60
+ if (group.smashDepth) {
61
+ node.setAttribute("depth", "0px");
62
+ }
63
+
64
+ return node;
65
+ }
66
+ });
@@ -0,0 +1,31 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import * as mml from "../buildMathML";
4
+
5
+ defineFunction({
6
+ type: "sqrt",
7
+ names: ["\\sqrt"],
8
+ props: {
9
+ numArgs: 1,
10
+ numOptionalArgs: 1
11
+ },
12
+ handler({ parser }, args, optArgs) {
13
+ const index = optArgs[0];
14
+ const body = args[0];
15
+ return {
16
+ type: "sqrt",
17
+ mode: parser.mode,
18
+ body,
19
+ index
20
+ };
21
+ },
22
+ mathmlBuilder(group, style) {
23
+ const { body, index } = group;
24
+ return index
25
+ ? new mathMLTree.MathNode("mroot", [
26
+ mml.buildGroup(body, style),
27
+ mml.buildGroup(index, style.incrementLevel())
28
+ ])
29
+ : new mathMLTree.MathNode("msqrt", [mml.buildGroup(body, style)]);
30
+ }
31
+ });
@@ -0,0 +1,58 @@
1
+ import defineFunction from "../defineFunction";
2
+ import { wrapWithMstyle } from "../mathMLTree"
3
+ import * as mml from "../buildMathML";
4
+
5
+ const styleMap = {
6
+ display: 0,
7
+ text: 1,
8
+ script: 2,
9
+ scriptscript: 3
10
+ };
11
+
12
+ const styleAttributes = {
13
+ display: ["0", "true"],
14
+ text: ["0", "false"],
15
+ script: ["1", "false"],
16
+ scriptscript: ["2", "false"]
17
+ };
18
+
19
+ defineFunction({
20
+ type: "styling",
21
+ names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"],
22
+ props: {
23
+ numArgs: 0,
24
+ allowedInText: true,
25
+ primitive: true
26
+ },
27
+ handler({ breakOnTokenText, funcName, parser }, args) {
28
+ // parse out the implicit body
29
+ const body = parser.parseExpression(true, breakOnTokenText);
30
+
31
+ const scriptLevel = funcName.slice(1, funcName.length - 5);
32
+ return {
33
+ type: "styling",
34
+ mode: parser.mode,
35
+ // Figure out what scriptLevel to use by pulling out the scriptLevel from
36
+ // the function name
37
+ scriptLevel,
38
+ body
39
+ };
40
+ },
41
+ mathmlBuilder(group, style) {
42
+ // Figure out what scriptLevel we're changing to.
43
+ const newStyle = style.withLevel(styleMap[group.scriptLevel]);
44
+ // The style argument in the next line does NOT directly set a MathML script level.
45
+ // It just tracks the style level, in case we need to know it for supsub or mathchoice.
46
+ const inner = mml.buildExpression(group.body, newStyle);
47
+ // Wrap with an <mstyle> element.
48
+ const node = wrapWithMstyle(inner)
49
+
50
+ const attr = styleAttributes[group.scriptLevel];
51
+
52
+ // Here is where we set the MathML script level.
53
+ node.setAttribute("scriptlevel", attr[0]);
54
+ node.setAttribute("displaystyle", attr[1]);
55
+
56
+ return node;
57
+ }
58
+ });
@@ -0,0 +1,135 @@
1
+ import { defineFunctionBuilders } from "../defineFunction"
2
+ import { StyleLevel } from "../constants"
3
+ import mathMLTree from "../mathMLTree"
4
+ import * as mml from "../buildMathML"
5
+
6
+ /**
7
+ * Sometimes, groups perform special rules when they have superscripts or
8
+ * subscripts attached to them. This function lets the `supsub` group know that
9
+ * Sometimes, groups perform special rules when they have superscripts or
10
+ * its inner element should handle the superscripts and subscripts instead of
11
+ * handling them itself.
12
+ */
13
+
14
+ // Helpers
15
+ const symbolRegEx = /^m(over|under|underover)$/
16
+
17
+ // Super scripts and subscripts, whose precise placement can depend on other
18
+ // functions that precede them.
19
+ defineFunctionBuilders({
20
+ type: "supsub",
21
+ mathmlBuilder(group, style) {
22
+ // Is the inner group a relevant horizonal brace?
23
+ let isBrace = false
24
+ let isOver
25
+ let isSup
26
+ let appendApplyFunction = false
27
+ let needsLeadingSpace = false
28
+
29
+ if (group.base && group.base.type === "horizBrace") {
30
+ isSup = !!group.sup
31
+ if (isSup === group.base.isOver) {
32
+ isBrace = true
33
+ isOver = group.base.isOver
34
+ }
35
+ }
36
+
37
+ if (group.base && !group.base.stack &&
38
+ (group.base.type === "op" || group.base.type === "operatorname")) {
39
+ group.base.parentIsSupSub = true
40
+ appendApplyFunction = !group.base.symbol
41
+ needsLeadingSpace = group.base.needsLeadingSpace
42
+ }
43
+
44
+ const children = group.base && group.base.stack
45
+ ? [mml.buildGroup(group.base.body[0], style)]
46
+ : [mml.buildGroup(group.base, style)]
47
+
48
+ const childStyle = style.inSubOrSup()
49
+ if (group.sub) {
50
+ children.push(mml.buildGroup(group.sub, childStyle))
51
+ }
52
+
53
+ if (group.sup) {
54
+ children.push(mml.buildGroup(group.sup, childStyle))
55
+ }
56
+
57
+ let nodeType;
58
+ if (isBrace) {
59
+ nodeType = isOver ? "mover" : "munder"
60
+ } else if (!group.sub) {
61
+ const base = group.base
62
+ if (
63
+ base &&
64
+ base.type === "op" &&
65
+ base.limits &&
66
+ (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
67
+ ) {
68
+ nodeType = "mover"
69
+ } else if (
70
+ base &&
71
+ base.type === "operatorname" &&
72
+ base.alwaysHandleSupSub &&
73
+ (base.limits || style.level === StyleLevel.DISPLAY)
74
+ ) {
75
+ nodeType = "mover"
76
+ } else {
77
+ nodeType = "msup"
78
+ }
79
+ } else if (!group.sup) {
80
+ const base = group.base;
81
+ if (
82
+ base &&
83
+ base.type === "op" &&
84
+ base.limits &&
85
+ (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
86
+ ) {
87
+ nodeType = "munder";
88
+ } else if (
89
+ base &&
90
+ base.type === "operatorname" &&
91
+ base.alwaysHandleSupSub &&
92
+ (base.limits || style.level === StyleLevel.DISPLAY)
93
+ ) {
94
+ nodeType = "munder"
95
+ } else {
96
+ nodeType = "msub"
97
+ }
98
+ } else {
99
+ const base = group.base;
100
+ if (base && ((base.type === "op" && base.limits) || base.type === "multiscript") &&
101
+ (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
102
+ ) {
103
+ nodeType = "munderover"
104
+ } else if (
105
+ base &&
106
+ base.type === "operatorname" &&
107
+ base.alwaysHandleSupSub &&
108
+ (style.level === StyleLevel.DISPLAY || base.limits)
109
+ ) {
110
+ nodeType = "munderover"
111
+ } else {
112
+ nodeType = "msubsup"
113
+ }
114
+ }
115
+
116
+ let node = new mathMLTree.MathNode(nodeType, children)
117
+ if (appendApplyFunction) {
118
+ // Append an <mo>&ApplyFunction;</mo>.
119
+ // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
120
+ const operator = new mathMLTree.MathNode("mo", [mml.makeText("\u2061", "text")])
121
+ if (needsLeadingSpace) {
122
+ const space = new mathMLTree.MathNode("mspace")
123
+ space.setAttribute("width", "0.1667em") // thin space.
124
+ node = mathMLTree.newDocumentFragment([space, node, operator])
125
+ } else {
126
+ node = mathMLTree.newDocumentFragment([node, operator])
127
+ }
128
+ } else if (symbolRegEx.test(nodeType)) {
129
+ // Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
130
+ node = new mathMLTree.MathNode("mrow", [node])
131
+ }
132
+
133
+ return node
134
+ }
135
+ });
@@ -0,0 +1,53 @@
1
+ import { defineFunctionBuilders } from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import * as mml from "../buildMathML";
4
+
5
+ // Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js.
6
+
7
+ const short = ["\\shortmid", "\\nshortmid", "\\shortparallel",
8
+ "\\nshortparallel", "\\smallsetminus"]
9
+
10
+ const arrows = ["\\Rsh", "\\Lsh", "\\restriction"]
11
+
12
+ const isArrow = str => {
13
+ if (str.length === 1) {
14
+ const codePoint = str.codePointAt(0)
15
+ return (0x218f < codePoint && codePoint < 0x2200)
16
+ }
17
+ return str.indexOf("arrow") > -1 || str.indexOf("harpoon") > -1 || arrows.includes(str)
18
+ }
19
+
20
+ defineFunctionBuilders({
21
+ type: "atom",
22
+ mathmlBuilder(group, style) {
23
+ const node = new mathMLTree.MathNode("mo", [mml.makeText(group.text, group.mode)]);
24
+ if (group.family === "punct") {
25
+ node.setAttribute("separator", "true");
26
+ } else if (group.family === "open" || group.family === "close") {
27
+ // Delims built here should not stretch vertically.
28
+ // See delimsizing.js for stretchy delims.
29
+ if (group.family === "open") {
30
+ node.setAttribute("form", "prefix")
31
+ // Set an explicit attribute for stretch. Otherwise Firefox may do it wrong.
32
+ node.setAttribute("stretchy", "false")
33
+ } else if (group.family === "close") {
34
+ node.setAttribute("form", "postfix");
35
+ node.setAttribute("stretchy", "false")
36
+ }
37
+ } else if (group.text === "\\mid") {
38
+ // Firefox messes up this spacing if at the end of an <mrow>. See it explicitly.
39
+ node.setAttribute("lspace", "0.22em") // medium space
40
+ node.setAttribute("rspace", "0.22em")
41
+ node.setAttribute("stretchy", "false")
42
+ } else if (group.family === "rel" && isArrow(group.text)) {
43
+ node.setAttribute("stretchy", "false")
44
+ } else if (short.includes(group.text)) {
45
+ node.setAttribute("mathsize", "70%")
46
+ } else if (group.text === ":") {
47
+ // ":" is not in the MathML operator dictionary. Give it BIN spacing.
48
+ node.attributes.lspace = "0.2222em"
49
+ node.attributes.rspace = "0.2222em"
50
+ }
51
+ return node;
52
+ }
53
+ });
@@ -0,0 +1,102 @@
1
+ import { defineFunctionBuilders } from "../defineFunction"
2
+ import { getVariant } from "../variant"
3
+ import { variantChar, smallCaps } from "../replace"
4
+ import mathMLTree from "../mathMLTree"
5
+ import * as mml from "../buildMathML"
6
+
7
+ // "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
8
+ // src/symbols.js.
9
+
10
+ const numberRegEx = /^\d(?:[\d,.]*\d)?$/ // Keep in sync with numberRegEx in Parser.js
11
+ const latinRegEx = /[A-Ba-z]/
12
+
13
+ const italicNumber = (text, variant) => {
14
+ const mn = new mathMLTree.MathNode("mn", [text])
15
+ const wrapper = new mathMLTree.MathNode("mstyle", [mn])
16
+ wrapper.style["font-style"] = "italic"
17
+ wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif"
18
+ if (variant === "bold-italic") { wrapper.style["font-weight"] = "bold" }
19
+ return wrapper
20
+ }
21
+
22
+ defineFunctionBuilders({
23
+ type: "mathord",
24
+ mathmlBuilder(group, style) {
25
+ const text = mml.makeText(group.text, group.mode, style)
26
+ const codePoint = text.text.codePointAt(0)
27
+ // Test for upper-case Greek
28
+ const defaultVariant = (0x0390 < codePoint && codePoint < 0x03aa) ? "normal" : "italic"
29
+ const variant = getVariant(group, style) || defaultVariant
30
+ if (variant === "script") {
31
+ text.text = variantChar(text.text, variant)
32
+ return new mathMLTree.MathNode("mi", [text], [style.font])
33
+ } else if (variant !== "italic") {
34
+ text.text = variantChar(text.text, variant)
35
+ }
36
+ let node = new mathMLTree.MathNode("mi", [text])
37
+ // TODO: Handle U+1D49C - U+1D4CF per https://www.unicode.org/charts/PDF/U1D400.pdf
38
+ if (variant === "normal") {
39
+ node.setAttribute("mathvariant", "normal")
40
+ if (text.text.length === 1) {
41
+ // A Firefox bug will apply spacing here, but there should be none. Fix it.
42
+ node = new mathMLTree.MathNode("mrow", [node])
43
+ }
44
+ }
45
+ return node
46
+ }
47
+ })
48
+
49
+ defineFunctionBuilders({
50
+ type: "textord",
51
+ mathmlBuilder(group, style) {
52
+ let ch = group.text
53
+ const codePoint = ch.codePointAt(0)
54
+ if (style.fontFamily === "textsc") {
55
+ // Convert small latin letters to small caps.
56
+ if (96 < codePoint && codePoint < 123) {
57
+ ch = smallCaps[ch]
58
+ }
59
+ }
60
+ const text = mml.makeText(ch, group.mode, style)
61
+ const variant = getVariant(group, style) || "normal"
62
+
63
+ let node
64
+ if (group.mode === "text") {
65
+ if (variant === "italic" || variant === "bold-italic") {
66
+ if (numberRegEx.test(group.text)) {
67
+ return italicNumber(text, variant)
68
+ }
69
+ }
70
+ if (variant !== "normal") {
71
+ text.text = variantChar(text.text, variant)
72
+ }
73
+ node = new mathMLTree.MathNode("mtext", [text])
74
+ } else if (numberRegEx.test(group.text)) {
75
+ if (variant === "oldstylenums") {
76
+ const ms = new mathMLTree.MathNode("mstyle", [text], ["oldstylenums"])
77
+ node = new mathMLTree.MathNode("mn", [ms])
78
+ } else if (variant === "italic" || variant === "bold-italic") {
79
+ return italicNumber(text, variant)
80
+ } else {
81
+ if (variant !== "normal") {
82
+ text.text = text.text.split("").map(c => variantChar(c, variant)).join("")
83
+ }
84
+ node = new mathMLTree.MathNode("mn", [text])
85
+ }
86
+ } else if (group.text === "\\prime") {
87
+ node = new mathMLTree.MathNode("mo", [text])
88
+ // TODO: If/when Chromium uses ssty variant for prime, remove the next line.
89
+ node.classes.push("tml-prime")
90
+ } else {
91
+ const origText = text.text
92
+ if (variant !== "italic") {
93
+ text.text = variantChar(text.text, variant)
94
+ }
95
+ node = new mathMLTree.MathNode("mi", [text])
96
+ if (text.text === origText && latinRegEx.test(origText)) {
97
+ node.setAttribute("mathvariant", "italic")
98
+ }
99
+ }
100
+ return node
101
+ }
102
+ })
@@ -0,0 +1,53 @@
1
+ import { defineFunctionBuilders } from "../defineFunction"
2
+ import mathMLTree from "../mathMLTree"
3
+ import ParseError from "../ParseError"
4
+
5
+ // A map of CSS-based spacing functions to their CSS class.
6
+ const cssSpace = {
7
+ "\\nobreak": "nobreak",
8
+ "\\allowbreak": "allowbreak"
9
+ }
10
+
11
+ // A lookup table to determine whether a spacing function/symbol should be
12
+ // treated like a regular space character. If a symbol or command is a key
13
+ // in this table, then it should be a regular space character. Furthermore,
14
+ // the associated value may have a `className` specifying an extra CSS class
15
+ // to add to the created `span`.
16
+ const regularSpace = {
17
+ " ": {},
18
+ "\\ ": {},
19
+ "~": {
20
+ className: "nobreak"
21
+ },
22
+ "\\space": {},
23
+ "\\nobreakspace": {
24
+ className: "nobreak"
25
+ }
26
+ }
27
+
28
+ // ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in
29
+ // src/symbols.js.
30
+ defineFunctionBuilders({
31
+ type: "spacing",
32
+ mathmlBuilder(group, style) {
33
+ let node
34
+
35
+ if (Object.prototype.hasOwnProperty.call(regularSpace, group.text)) {
36
+ // Firefox does not render a space in a <mtext> </mtext>. So write a no-break space.
37
+ // TODO: If Firefox fixes that bug, uncomment the next line and write ch into the node.
38
+ //const ch = (regularSpace[group.text].className === "nobreak") ? "\u00a0" : " "
39
+ node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("\u00a0")])
40
+ } else if (Object.prototype.hasOwnProperty.call(cssSpace, group.text)) {
41
+ // MathML 3.0 calls for nobreak to occur in an <mo>, not an <mtext>
42
+ // Ref: https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs
43
+ node = new mathMLTree.MathNode("mo")
44
+ if (group.text === "\\nobreak") {
45
+ node.setAttribute("linebreak", "nobreak")
46
+ }
47
+ } else {
48
+ throw new ParseError(`Unknown type of space "${group.text}"`)
49
+ }
50
+
51
+ return node
52
+ }
53
+ })
@@ -0,0 +1,8 @@
1
+ import { defineFunctionBuilders } from "../defineFunction";
2
+
3
+ defineFunctionBuilders({
4
+ type: "tag"
5
+ });
6
+
7
+ // For a \tag, the work usually done in a mathmlBuilder is instead done in buildMathML.js.
8
+ // That way, a \tag can be pulled out of the parse tree and wrapped around the outer node.