temml 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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.