katex 0.10.0-rc → 0.10.2

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 (144) hide show
  1. package/CHANGELOG.md +214 -126
  2. package/README.md +18 -17
  3. package/cli.js +5 -1
  4. package/contrib/auto-render/README.md +1 -1
  5. package/contrib/auto-render/auto-render.js +4 -1
  6. package/contrib/auto-render/test/auto-render-spec.js +17 -0
  7. package/contrib/copy-tex/README.md +8 -2
  8. package/contrib/copy-tex/copy-tex.js +0 -1
  9. package/contrib/copy-tex/copy-tex.webpack.js +6 -0
  10. package/contrib/mathtex-script-type/README.md +10 -6
  11. package/contrib/mhchem/README.md +19 -0
  12. package/contrib/mhchem/mhchem.js +1695 -0
  13. package/contrib/mhchem/mhchem.patch +235 -0
  14. package/dist/README.md +18 -17
  15. package/dist/contrib/auto-render.js +179 -161
  16. package/dist/contrib/auto-render.min.js +1 -1
  17. package/dist/contrib/auto-render.mjs +215 -0
  18. package/dist/contrib/copy-tex.js +84 -62
  19. package/dist/contrib/copy-tex.min.css +1 -1
  20. package/dist/contrib/copy-tex.min.js +1 -1
  21. package/dist/contrib/copy-tex.mjs +85 -0
  22. package/dist/contrib/mathtex-script-type.js +17 -14
  23. package/dist/contrib/mathtex-script-type.mjs +24 -0
  24. package/dist/contrib/mhchem.js +3241 -0
  25. package/dist/contrib/mhchem.min.js +1 -0
  26. package/dist/contrib/mhchem.mjs +3109 -0
  27. package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
  28. package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
  29. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  30. package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  31. package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  32. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  33. package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  34. package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  35. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  36. package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  37. package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  38. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  39. package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  40. package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  41. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  42. package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
  43. package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
  44. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  45. package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  46. package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  47. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  48. package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
  49. package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
  50. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  51. package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
  52. package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
  53. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  54. package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  55. package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  56. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  57. package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
  58. package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
  59. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  60. package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  61. package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  62. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  63. package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  64. package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  65. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  66. package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  67. package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  68. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  69. package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
  70. package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
  71. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  72. package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
  73. package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
  74. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  75. package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
  76. package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
  77. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  78. package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
  79. package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
  80. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  81. package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
  82. package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
  83. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  84. package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  85. package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  86. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  87. package/dist/katex.css +24 -9
  88. package/dist/katex.js +13295 -12413
  89. package/dist/katex.min.css +1 -1
  90. package/dist/katex.min.js +1 -1
  91. package/dist/katex.mjs +13388 -11826
  92. package/katex.js +1 -2
  93. package/package.json +60 -48
  94. package/src/Lexer.js +25 -25
  95. package/src/MacroExpander.js +0 -1
  96. package/src/Options.js +11 -75
  97. package/src/Parser.js +231 -313
  98. package/src/Settings.js +6 -0
  99. package/src/buildCommon.js +140 -103
  100. package/src/buildHTML.js +125 -121
  101. package/src/buildMathML.js +14 -4
  102. package/src/buildTree.js +16 -10
  103. package/src/delimiter.js +4 -3
  104. package/src/domTree.js +91 -44
  105. package/src/environments/array.js +120 -7
  106. package/src/fontMetrics.js +3 -2
  107. package/src/functions/arrow.js +21 -7
  108. package/src/functions/color.js +2 -37
  109. package/src/functions/delimsizing.js +18 -11
  110. package/src/functions/enclose.js +19 -4
  111. package/src/functions/environment.js +35 -4
  112. package/src/functions/font.js +1 -2
  113. package/src/functions/genfrac.js +35 -20
  114. package/src/functions/href.js +5 -3
  115. package/src/functions/includegraphics.js +146 -0
  116. package/src/functions/mclass.js +1 -0
  117. package/src/functions/op.js +21 -32
  118. package/src/functions/operatorname.js +1 -2
  119. package/src/functions/ordgroup.js +4 -0
  120. package/src/functions/phantom.js +7 -3
  121. package/src/functions/rule.js +20 -9
  122. package/src/functions/sizing.js +2 -4
  123. package/src/functions/smash.js +5 -2
  124. package/src/functions/sqrt.js +1 -4
  125. package/src/functions/styling.js +0 -1
  126. package/src/functions/supsub.js +6 -2
  127. package/src/functions/symbolsOp.js +4 -0
  128. package/src/functions/symbolsSpacing.js +29 -6
  129. package/src/functions/tag.js +20 -4
  130. package/src/functions/text.js +6 -4
  131. package/src/functions/verb.js +16 -4
  132. package/src/functions.js +2 -0
  133. package/src/katex.less +35 -12
  134. package/src/macros.js +161 -36
  135. package/src/mathMLTree.js +17 -19
  136. package/src/parseNode.js +27 -1
  137. package/src/stretchy.js +3 -1
  138. package/src/svgGeometry.js +1 -1
  139. package/src/symbols.js +39 -17
  140. package/src/tree.js +0 -4
  141. package/src/types.js +4 -3
  142. package/src/unicodeMake.js +1 -1
  143. package/src/utils.js +1 -62
  144. package/src/wide-character.js +2 -2
package/src/buildHTML.js CHANGED
@@ -10,7 +10,7 @@ import ParseError from "./ParseError";
10
10
  import Style from "./Style";
11
11
  import buildCommon from "./buildCommon";
12
12
  import {Anchor} from "./domTree";
13
- import utils, {assert} from "./utils";
13
+ import utils from "./utils";
14
14
  import {checkNodeType} from "./parseNode";
15
15
  import {spacings, tightSpacings} from "./spacingData";
16
16
  import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
@@ -25,32 +25,8 @@ const makeSpan = buildCommon.makeSpan;
25
25
  // Binary atoms (first class `mbin`) change into ordinary atoms (`mord`)
26
26
  // depending on their surroundings. See TeXbook pg. 442-446, Rules 5 and 6,
27
27
  // and the text before Rule 19.
28
- const isBinLeftCanceller = function(
29
- node: ?HtmlDomNode,
30
- isRealGroup: boolean,
31
- ): boolean {
32
- // TODO: This code assumes that a node's math class is the first element
33
- // of its `classes` array. A later cleanup should ensure this, for
34
- // instance by changing the signature of `makeSpan`.
35
- if (node) {
36
- return utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
37
- getTypeOfDomTree(node, "right"));
38
- } else {
39
- return isRealGroup;
40
- }
41
- };
42
-
43
- const isBinRightCanceller = function(
44
- node: ?HtmlDomNode,
45
- isRealGroup: boolean,
46
- ): boolean {
47
- if (node) {
48
- return utils.contains(["mrel", "mclose", "mpunct"],
49
- getTypeOfDomTree(node, "left"));
50
- } else {
51
- return isRealGroup;
52
- }
53
- };
28
+ const binLeftCanceller = ["leftmost", "mbin", "mopen", "mrel", "mop", "mpunct"];
29
+ const binRightCanceller = ["rightmost", "mrel", "mclose", "mpunct"];
54
30
 
55
31
  const styleMap = {
56
32
  "display": Style.DISPLAY,
@@ -71,7 +47,7 @@ const DomEnum = {
71
47
  mpunct: "mpunct",
72
48
  minner: "minner",
73
49
  };
74
- export type DomType = $Keys<typeof DomEnum>;
50
+ type DomType = $Keys<typeof DomEnum>;
75
51
 
76
52
  /**
77
53
  * Take a list of nodes, build them in order, and return a list of the built
@@ -88,101 +64,135 @@ export const buildExpression = function(
88
64
  surrounding: [?DomType, ?DomType] = [null, null],
89
65
  ): HtmlDomNode[] {
90
66
  // Parse expressions into `groups`.
91
- const rawGroups: HtmlDomNode[] = [];
67
+ const groups: HtmlDomNode[] = [];
92
68
  for (let i = 0; i < expression.length; i++) {
93
69
  const output = buildGroup(expression[i], options);
94
70
  if (output instanceof DocumentFragment) {
95
71
  const children: HtmlDomNode[] = output.children;
96
- rawGroups.push(...children);
72
+ groups.push(...children);
97
73
  } else {
98
- rawGroups.push(output);
74
+ groups.push(output);
75
+ }
76
+ }
77
+
78
+ // If `expression` is a partial group, let the parent handle spacings
79
+ // to avoid processing groups multiple times.
80
+ if (!isRealGroup) {
81
+ return groups;
82
+ }
83
+
84
+ let glueOptions = options;
85
+ if (expression.length === 1) {
86
+ const node = checkNodeType(expression[0], "sizing") ||
87
+ checkNodeType(expression[0], "styling");
88
+ if (!node) {
89
+ // No match.
90
+ } else if (node.type === "sizing") {
91
+ glueOptions = options.havingSize(node.size);
92
+ } else if (node.type === "styling") {
93
+ glueOptions = options.havingStyle(styleMap[node.style]);
99
94
  }
100
95
  }
101
- // At this point `rawGroups` consists entirely of `symbolNode`s and `span`s.
102
96
 
103
- // Ignore explicit spaces (e.g., \;, \,) when determining what implicit
104
- // spacing should go between atoms of different classes, and add dummy
105
- // spans for determining spacings between surrounding atoms.
106
- const nonSpaces: (?HtmlDomNode)[] = [
107
- surrounding[0] ? makeSpan([surrounding[0]], [], options) : null,
108
- ...rawGroups.filter(group => group && group.classes[0] !== "mspace"),
109
- surrounding[1] ? makeSpan([surrounding[1]], [], options) : null,
110
- ];
97
+ // Dummy spans for determining spacings between surrounding atoms.
98
+ // If `expression` has no atoms on the left or right, class "leftmost"
99
+ // or "rightmost", respectively, is used to indicate it.
100
+ const dummyPrev = makeSpan([surrounding[0] || "leftmost"], [], options);
101
+ const dummyNext = makeSpan([surrounding[1] || "rightmost"], [], options);
102
+
103
+ // TODO: These code assumes that a node's math class is the first element
104
+ // of its `classes` array. A later cleanup should ensure this, for
105
+ // instance by changing the signature of `makeSpan`.
111
106
 
112
107
  // Before determining what spaces to insert, perform bin cancellation.
113
108
  // Binary operators change to ordinary symbols in some contexts.
114
- for (let i = 1; i < nonSpaces.length - 1; i++) {
115
- const nonSpacesI: HtmlDomNode = assert(nonSpaces[i]);
116
- const left = getOutermostNode(nonSpacesI, "left");
117
- if (left.classes[0] === "mbin" &&
118
- isBinLeftCanceller(nonSpaces[i - 1], isRealGroup)) {
119
- left.classes[0] = "mord";
109
+ traverseNonSpaceNodes(groups, (node, prev) => {
110
+ const prevType = prev.classes[0];
111
+ const type = node.classes[0];
112
+ if (prevType === "mbin" && utils.contains(binRightCanceller, type)) {
113
+ prev.classes[0] = "mord";
114
+ } else if (type === "mbin" && utils.contains(binLeftCanceller, prevType)) {
115
+ node.classes[0] = "mord";
120
116
  }
121
-
122
- const right = getOutermostNode(nonSpacesI, "right");
123
- if (right.classes[0] === "mbin" &&
124
- isBinRightCanceller(nonSpaces[i + 1], isRealGroup)) {
125
- right.classes[0] = "mord";
117
+ }, {node: dummyPrev}, dummyNext);
118
+
119
+ traverseNonSpaceNodes(groups, (node, prev) => {
120
+ const prevType = getTypeOfDomTree(prev);
121
+ const type = getTypeOfDomTree(node);
122
+
123
+ // 'mtight' indicates that the node is script or scriptscript style.
124
+ const space = prevType && type ? (node.hasClass("mtight")
125
+ ? tightSpacings[prevType][type]
126
+ : spacings[prevType][type]) : null;
127
+ if (space) { // Insert glue (spacing) after the `prev`.
128
+ return buildCommon.makeGlue(space, glueOptions);
126
129
  }
130
+ }, {node: dummyPrev}, dummyNext);
131
+
132
+ return groups;
133
+ };
134
+
135
+ // Depth-first traverse non-space `nodes`, calling `callback` with the current and
136
+ // previous node as arguments, optionally returning a node to insert after the
137
+ // previous node. `prev` is an object with the previous node and `insertAfter`
138
+ // function to insert after it. `next` is a node that will be added to the right.
139
+ // Used for bin cancellation and inserting spacings.
140
+ const traverseNonSpaceNodes = function(
141
+ nodes: HtmlDomNode[],
142
+ callback: (HtmlDomNode, HtmlDomNode) => ?HtmlDomNode,
143
+ prev: {|
144
+ node: HtmlDomNode,
145
+ insertAfter?: HtmlDomNode => void,
146
+ |},
147
+ next: ?HtmlDomNode,
148
+ ) {
149
+ if (next) { // temporarily append the right node, if exists
150
+ nodes.push(next);
127
151
  }
152
+ let i = 0;
153
+ for (; i < nodes.length; i++) {
154
+ const node = nodes[i];
155
+ const partialGroup = checkPartialGroup(node);
156
+ if (partialGroup) { // Recursive DFS
157
+ traverseNonSpaceNodes(partialGroup.children, callback, prev);
158
+ continue;
159
+ }
128
160
 
129
- const groups = [];
130
- let j = 0;
131
- for (let i = 0; i < rawGroups.length; i++) {
132
- groups.push(rawGroups[i]);
133
-
134
- // For any group that is not a space, get the next non-space. Then
135
- // lookup what implicit space should be placed between those atoms and
136
- // add it to groups.
137
- if (rawGroups[i].classes[0] !== "mspace" && j < nonSpaces.length - 1) {
138
- // if current non-space node is left dummy span, add a glue before
139
- // first real non-space node
140
- if (j === 0) {
141
- groups.pop();
142
- i--;
143
- }
161
+ // Ignore explicit spaces (e.g., \;, \,) when determining what implicit
162
+ // spacing should go between atoms of different classes
163
+ if (node.classes[0] === "mspace") {
164
+ continue;
165
+ }
144
166
 
145
- // Get the type of the current non-space node. If it's a document
146
- // fragment, get the type of the rightmost node in the fragment.
147
- const left = getTypeOfDomTree(nonSpaces[j], "right");
148
-
149
- // Get the type of the next non-space node. If it's a document
150
- // fragment, get the type of the leftmost node in the fragment.
151
- const right = getTypeOfDomTree(nonSpaces[j + 1], "left");
152
-
153
- // We use buildExpression inside of sizingGroup, but it returns a
154
- // document fragment of elements. sizingGroup sets `isRealGroup`
155
- // to false to avoid processing spans multiple times.
156
- if (left && right && isRealGroup) {
157
- const nonSpacesJp1: HtmlDomNode = assert(nonSpaces[j + 1]);
158
- const space = isLeftTight(nonSpacesJp1)
159
- ? tightSpacings[left][right]
160
- : spacings[left][right];
161
-
162
- if (space) {
163
- let glueOptions = options;
164
-
165
- if (expression.length === 1) {
166
- const node =
167
- checkNodeType(expression[0], "sizing") ||
168
- checkNodeType(expression[0], "styling");
169
- if (!node) {
170
- // No match.
171
- } else if (node.type === "sizing") {
172
- glueOptions = options.havingSize(node.size);
173
- } else if (node.type === "styling") {
174
- glueOptions = options.havingStyle(styleMap[node.style]);
175
- }
176
- }
177
-
178
- groups.push(buildCommon.makeGlue(space, glueOptions));
179
- }
167
+ const result = callback(node, prev.node);
168
+ if (result) {
169
+ if (prev.insertAfter) {
170
+ prev.insertAfter(result);
171
+ } else { // insert at front
172
+ nodes.unshift(result);
173
+ i++;
180
174
  }
181
- j++;
182
175
  }
176
+
177
+ prev.node = node;
178
+ prev.insertAfter = (index => n => {
179
+ nodes.splice(index + 1, 0, n);
180
+ i++;
181
+ })(i);
183
182
  }
183
+ if (next) {
184
+ nodes.pop();
185
+ }
186
+ };
184
187
 
185
- return groups;
188
+ // Check if given node is a partial group, i.e., does not affect spacing around.
189
+ const checkPartialGroup = function(
190
+ node: HtmlDomNode,
191
+ ): ?(DocumentFragment<HtmlDomNode> | Anchor) {
192
+ if (node instanceof DocumentFragment || node instanceof Anchor) {
193
+ return node;
194
+ }
195
+ return null;
186
196
  };
187
197
 
188
198
  // Return the outermost node of a domTree.
@@ -190,14 +200,14 @@ const getOutermostNode = function(
190
200
  node: HtmlDomNode,
191
201
  side: Side,
192
202
  ): HtmlDomNode {
193
- if (node instanceof DocumentFragment ||
194
- node instanceof Anchor) {
195
- const children = node.children;
203
+ const partialGroup = checkPartialGroup(node);
204
+ if (partialGroup) {
205
+ const children = partialGroup.children;
196
206
  if (children.length) {
197
207
  if (side === "right") {
198
208
  return getOutermostNode(children[children.length - 1], "right");
199
209
  } else if (side === "left") {
200
- return getOutermostNode(children[0], "right");
210
+ return getOutermostNode(children[0], "left");
201
211
  }
202
212
  }
203
213
  }
@@ -205,29 +215,22 @@ const getOutermostNode = function(
205
215
  };
206
216
 
207
217
  // Return math atom class (mclass) of a domTree.
218
+ // If `side` is given, it will get the type of the outermost node at given side.
208
219
  export const getTypeOfDomTree = function(
209
220
  node: ?HtmlDomNode,
210
- side: Side,
221
+ side: ?Side,
211
222
  ): ?DomType {
212
223
  if (!node) {
213
224
  return null;
214
225
  }
215
-
216
- node = getOutermostNode(node, side);
226
+ if (side) {
227
+ node = getOutermostNode(node, side);
228
+ }
217
229
  // This makes a lot of assumptions as to where the type of atom
218
230
  // appears. We should do a better job of enforcing this.
219
231
  return DomEnum[node.classes[0]] || null;
220
232
  };
221
233
 
222
- // If `node` is an atom return whether it's been assigned the mtight class.
223
- // If `node` is a document fragment, return the value of isLeftTight() for the
224
- // leftmost node in the fragment.
225
- // 'mtight' indicates that the node is script or scriptscript style.
226
- export const isLeftTight = function(node: HtmlDomNode): boolean {
227
- node = getOutermostNode(node, "left");
228
- return node.hasClass("mtight");
229
- };
230
-
231
234
  export const makeNullDelimiter = function(
232
235
  options: Options,
233
236
  classes: string[],
@@ -331,10 +334,11 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
331
334
  expression[i].hasClass("mrel") ||
332
335
  expression[i].hasClass("allowbreak")) {
333
336
  // Put any post-operator glue on same line as operator.
334
- // Watch for \nobreak along the way.
337
+ // Watch for \nobreak along the way, and stop at \newline.
335
338
  let nobreak = false;
336
339
  while (i < expression.length - 1 &&
337
- expression[i + 1].hasClass("mspace")) {
340
+ expression[i + 1].hasClass("mspace") &&
341
+ !expression[i + 1].hasClass("newline")) {
338
342
  i++;
339
343
  parts.push(expression[i]);
340
344
  if (expression[i].hasClass("nobreak")) {
@@ -85,7 +85,7 @@ export const getVariant = function(
85
85
  }
86
86
 
87
87
  const font = options.font;
88
- if (!font) {
88
+ if (!font || font === "mathnormal") {
89
89
  return null;
90
90
  }
91
91
 
@@ -145,14 +145,24 @@ export const buildExpression = function(
145
145
  lastGroup.children.push(...group.children);
146
146
  continue;
147
147
  }
148
+ } else if (lastGroup.type === 'mi' && lastGroup.children.length === 1) {
149
+ const lastChild = lastGroup.children[0];
150
+ if (lastChild instanceof TextNode && lastChild.text === '\u0338' &&
151
+ (group.type === 'mo' || group.type === 'mi' ||
152
+ group.type === 'mn')) {
153
+ const child = group.children[0];
154
+ if (child instanceof TextNode && child.text.length > 0) {
155
+ // Overlay with combining character long solidus
156
+ child.text = child.text.slice(0, 1) + "\u0338" +
157
+ child.text.slice(1);
158
+ groups.pop();
159
+ }
160
+ }
148
161
  }
149
162
  }
150
163
  groups.push(group);
151
164
  lastGroup = group;
152
165
  }
153
-
154
- // TODO(kevinb): combine \\not with mrels and mords
155
-
156
166
  return groups;
157
167
  };
158
168
 
package/src/buildTree.js CHANGED
@@ -16,6 +16,20 @@ const optionsFromSettings = function(settings: Settings) {
16
16
  });
17
17
  };
18
18
 
19
+ const displayWrap = function(node: DomSpan, settings: Settings): DomSpan {
20
+ if (settings.displayMode) {
21
+ const classes = ["katex-display"];
22
+ if (settings.leqno) {
23
+ classes.push("leqno");
24
+ }
25
+ if (settings.fleqn) {
26
+ classes.push("fleqn");
27
+ }
28
+ node = buildCommon.makeSpan(classes, [node]);
29
+ }
30
+ return node;
31
+ };
32
+
19
33
  export const buildTree = function(
20
34
  tree: AnyParseNode[],
21
35
  expression: string,
@@ -29,11 +43,7 @@ export const buildTree = function(
29
43
  mathMLNode, htmlNode,
30
44
  ]);
31
45
 
32
- if (settings.displayMode) {
33
- return buildCommon.makeSpan(["katex-display"], [katexNode]);
34
- } else {
35
- return katexNode;
36
- }
46
+ return displayWrap(katexNode, settings);
37
47
  };
38
48
 
39
49
  export const buildHTMLTree = function(
@@ -44,11 +54,7 @@ export const buildHTMLTree = function(
44
54
  const options = optionsFromSettings(settings);
45
55
  const htmlNode = buildHTML(tree, options);
46
56
  const katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
47
- if (settings.displayMode) {
48
- return buildCommon.makeSpan(["katex-display"], [katexNode]);
49
- } else {
50
- return katexNode;
51
- }
57
+ return displayWrap(katexNode, settings);
52
58
  };
53
59
 
54
60
  export default buildTree;
package/src/delimiter.js CHANGED
@@ -245,12 +245,12 @@ const makeStackedDelim = function(
245
245
  top = "\u23a4";
246
246
  repeat = bottom = "\u23a5";
247
247
  font = "Size4-Regular";
248
- } else if (delim === "(") {
248
+ } else if (delim === "(" || delim === "\\lparen") {
249
249
  top = "\u239b";
250
250
  repeat = "\u239c";
251
251
  bottom = "\u239d";
252
252
  font = "Size4-Regular";
253
- } else if (delim === ")") {
253
+ } else if (delim === ")" || delim === "\\rparen") {
254
254
  top = "\u239e";
255
255
  repeat = "\u239f";
256
256
  bottom = "\u23a0";
@@ -489,7 +489,8 @@ const makeSqrtImage = function(
489
489
  // There are three kinds of delimiters, delimiters that stack when they become
490
490
  // too large
491
491
  const stackLargeDelimiters = [
492
- "(", ")", "[", "\\lbrack", "]", "\\rbrack",
492
+ "(", "\\lparen", ")", "\\rparen",
493
+ "[", "\\lbrack", "]", "\\rbrack",
493
494
  "\\{", "\\lbrace", "\\}", "\\rbrace",
494
495
  "\\lfloor", "\\rfloor", "\u230a", "\u230b",
495
496
  "\\lceil", "\\rceil", "\u2308", "\u2309",
package/src/domTree.js CHANGED
@@ -24,7 +24,7 @@ import type {VirtualNode} from "./tree";
24
24
  * Create an HTML className based on a list of classes. In addition to joining
25
25
  * with spaces, we also remove empty classes.
26
26
  */
27
- const createClass = function(classes: string[]): string {
27
+ export const createClass = function(classes: string[]): string {
28
28
  return classes.filter(cls => cls).join(" ");
29
29
  };
30
30
 
@@ -125,7 +125,33 @@ const toMarkup = function(tagName: string): string {
125
125
  return markup;
126
126
  };
127
127
 
128
- export type CssStyle = {[name: string]: string};
128
+ // Making the type below exact with all optional fields doesn't work due to
129
+ // - https://github.com/facebook/flow/issues/4582
130
+ // - https://github.com/facebook/flow/issues/5688
131
+ // However, since *all* fields are optional, $Shape<> works as suggested in 5688
132
+ // above.
133
+ // This type does not include all CSS properties. Additional properties should
134
+ // be added as needed.
135
+ export type CssStyle = $Shape<{
136
+ backgroundColor: string,
137
+ borderBottomWidth: string,
138
+ borderColor: string,
139
+ borderRightWidth: string,
140
+ borderTopWidth: string,
141
+ bottom: string,
142
+ color: string,
143
+ height: string,
144
+ left: string,
145
+ marginLeft: string,
146
+ marginRight: string,
147
+ marginTop: string,
148
+ minWidth: string,
149
+ paddingLeft: string,
150
+ position: string,
151
+ top: string,
152
+ width: string,
153
+ verticalAlign: string,
154
+ }> & {};
129
155
 
130
156
  export interface HtmlDomNode extends VirtualNode {
131
157
  classes: string[];
@@ -135,7 +161,6 @@ export interface HtmlDomNode extends VirtualNode {
135
161
  style: CssStyle;
136
162
 
137
163
  hasClass(className: string): boolean;
138
- tryCombine(sibling: HtmlDomNode): boolean;
139
164
  }
140
165
 
141
166
  // Span wrapping other DOM nodes.
@@ -189,15 +214,6 @@ export class Span<ChildType: VirtualNode> implements HtmlDomNode {
189
214
  return utils.contains(this.classes, className);
190
215
  }
191
216
 
192
- /**
193
- * Try to combine with given sibling. Returns true if the sibling has
194
- * been successfully merged into this node, and false otherwise.
195
- * Default behavior fails (returns false).
196
- */
197
- tryCombine(sibling: HtmlDomNode): boolean {
198
- return false;
199
- }
200
-
201
217
  toNode(): HTMLElement {
202
218
  return toNode.call(this, "span");
203
219
  }
@@ -239,10 +255,6 @@ export class Anchor implements HtmlDomNode {
239
255
  return utils.contains(this.classes, className);
240
256
  }
241
257
 
242
- tryCombine(sibling: HtmlDomNode): boolean {
243
- return false;
244
- }
245
-
246
258
  toNode(): HTMLElement {
247
259
  return toNode.call(this, "a");
248
260
  }
@@ -252,6 +264,69 @@ export class Anchor implements HtmlDomNode {
252
264
  }
253
265
  }
254
266
 
267
+ /**
268
+ * This node represents an image embed (<img>) element.
269
+ */
270
+ export class Img implements VirtualNode {
271
+ src: string;
272
+ alt: string;
273
+ classes: string[];
274
+ height: number;
275
+ depth: number;
276
+ maxFontSize: number;
277
+ style: CssStyle;
278
+
279
+ constructor(
280
+ src: string,
281
+ alt: string,
282
+ style: CssStyle,
283
+ ) {
284
+ this.alt = alt;
285
+ this.src = src;
286
+ this.classes = ["mord"];
287
+ this.style = style;
288
+ }
289
+
290
+ hasClass(className: string): boolean {
291
+ return utils.contains(this.classes, className);
292
+ }
293
+
294
+ toNode(): Node {
295
+ const node = document.createElement("img");
296
+ node.src = this.src;
297
+ node.alt = this.alt;
298
+ node.className = "mord";
299
+
300
+ // Apply inline styles
301
+ for (const style in this.style) {
302
+ if (this.style.hasOwnProperty(style)) {
303
+ // $FlowFixMe
304
+ node.style[style] = this.style[style];
305
+ }
306
+ }
307
+
308
+ return node;
309
+ }
310
+
311
+ toMarkup(): string {
312
+ let markup = `<img src='${this.src} 'alt='${this.alt}' `;
313
+
314
+ // Add the styles, after hyphenation
315
+ let styles = "";
316
+ for (const style in this.style) {
317
+ if (this.style.hasOwnProperty(style)) {
318
+ styles += `${utils.hyphenate(style)}:${this.style[style]};`;
319
+ }
320
+ }
321
+ if (styles) {
322
+ markup += ` style="${utils.escape(styles)}"`;
323
+ }
324
+
325
+ markup += "'/>";
326
+ return markup;
327
+ }
328
+ }
329
+
255
330
  const iCombinations = {
256
331
  'î': '\u0131\u0302',
257
332
  'ï': '\u0131\u0308',
@@ -317,34 +392,6 @@ export class SymbolNode implements HtmlDomNode {
317
392
  return utils.contains(this.classes, className);
318
393
  }
319
394
 
320
- tryCombine(sibling: HtmlDomNode): boolean {
321
- if (!sibling
322
- || !(sibling instanceof SymbolNode)
323
- || this.italic > 0
324
- || createClass(this.classes) !== createClass(sibling.classes)
325
- || this.skew !== sibling.skew
326
- || this.maxFontSize !== sibling.maxFontSize) {
327
- return false;
328
- }
329
- for (const style in this.style) {
330
- if (this.style.hasOwnProperty(style)
331
- && this.style[style] !== sibling.style[style]) {
332
- return false;
333
- }
334
- }
335
- for (const style in sibling.style) {
336
- if (sibling.style.hasOwnProperty(style)
337
- && this.style[style] !== sibling.style[style]) {
338
- return false;
339
- }
340
- }
341
- this.text += sibling.text;
342
- this.height = Math.max(this.height, sibling.height);
343
- this.depth = Math.max(this.depth, sibling.depth);
344
- this.italic = sibling.italic;
345
- return true;
346
- }
347
-
348
395
  /**
349
396
  * Creates a text node or span from a symbol node. Note that a span is only
350
397
  * created if it is needed.