diff --git a/src/buildHTML.js b/src/buildHTML.js
index 299b70e8..7e697e63 100644
--- a/src/buildHTML.js
+++ b/src/buildHTML.js
@@ -22,17 +22,13 @@ const makeSpan = buildCommon.makeSpan;
// Binary atoms (first class `mbin`) change into ordinary atoms (`mord`)
// depending on their surroundings. See TeXbook pg. 442-446, Rules 5 and 6,
// and the text before Rule 19.
-const isBin = function(node) {
- return node && node.classes[0] === "mbin";
-};
-
const isBinLeftCanceller = function(node, isRealGroup) {
// TODO: This code assumes that a node's math class is the first element
// of its `classes` array. A later cleanup should ensure this, for
// instance by changing the signature of `makeSpan`.
if (node) {
return utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
- node.classes[0]);
+ getTypeOfDomTree(node, "right"));
} else {
return isRealGroup;
}
@@ -40,7 +36,8 @@ const isBinLeftCanceller = function(node, isRealGroup) {
const isBinRightCanceller = function(node, isRealGroup) {
if (node) {
- return utils.contains(["mrel", "mclose", "mpunct"], node.classes[0]);
+ return utils.contains(["mrel", "mclose", "mpunct"],
+ getTypeOfDomTree(node, "left"));
} else {
return isRealGroup;
}
@@ -58,9 +55,11 @@ const styleMap = {
* nodes. documentFragments are flattened into their contents, so the
* returned list contains no fragments. `isRealGroup` is true if `expression`
* is a real group (no atoms will be added on either side), as opposed to
- * a partial group (e.g. one created by \color).
+ * a partial group (e.g. one created by \color). `surrounding` is an array
+ * consisting type of nodes that will be added to the left and right.
*/
-export const buildExpression = function(expression, options, isRealGroup) {
+export const buildExpression = function(expression, options, isRealGroup,
+ surrounding = [null, null]) {
// Parse expressions into `groups`.
const rawGroups = [];
for (let i = 0; i < expression.length; i++) {
@@ -75,18 +74,27 @@ export const buildExpression = function(expression, options, isRealGroup) {
// At this point `rawGroups` consists entirely of `symbolNode`s and `span`s.
// Ignore explicit spaces (e.g., \;, \,) when determining what implicit
- // spacing should go between atoms of different classes.
- const nonSpaces =
- rawGroups.filter(group => group && group.classes[0] !== "mspace");
+ // spacing should go between atoms of different classes, and add dummy
+ // spans for determining spacings between surrounding atoms
+ const nonSpaces = [
+ surrounding[0] && makeSpan([surrounding[0]], [], options),
+ ...rawGroups.filter(group => group && group.classes[0] !== "mspace"),
+ surrounding[1] && makeSpan([surrounding[1]], [], options),
+ ];
// Before determining what spaces to insert, perform bin cancellation.
// Binary operators change to ordinary symbols in some contexts.
- for (let i = 0; i < nonSpaces.length; i++) {
- if (isBin(nonSpaces[i])) {
- if (isBinLeftCanceller(nonSpaces[i - 1], isRealGroup)
- || isBinRightCanceller(nonSpaces[i + 1], isRealGroup)) {
- nonSpaces[i].classes[0] = "mord";
- }
+ for (let i = 1; i < nonSpaces.length - 1; i++) {
+ const left = getOutermostNode(nonSpaces[i], "left");
+ if (left.classes[0] === "mbin" &&
+ isBinLeftCanceller(nonSpaces[i - 1], isRealGroup)) {
+ left.classes[0] = "mord";
+ }
+
+ const right = getOutermostNode(nonSpaces[i], "right");
+ if (right.classes[0] === "mbin" &&
+ isBinRightCanceller(nonSpaces[i + 1], isRealGroup)) {
+ right.classes[0] = "mord";
}
}
@@ -99,6 +107,13 @@ export const buildExpression = function(expression, options, isRealGroup) {
// lookup what implicit space should be placed between those atoms and
// add it to groups.
if (rawGroups[i].classes[0] !== "mspace" && j < nonSpaces.length - 1) {
+ // if current non-space node is left dummy span, add a glue before
+ // first real non-space node
+ if (j === 0) {
+ groups.pop();
+ i--;
+ }
+
// Get the type of the current non-space node. If it's a document
// fragment, get the type of the rightmost node in the fragment.
const left = getTypeOfDomTree(nonSpaces[j], "right");
@@ -150,28 +165,37 @@ export const buildExpression = function(expression, options, isRealGroup) {
return groups;
};
-// Return math atom class (mclass) of a domTree.
-export const getTypeOfDomTree = function(node, side = "right") {
+// Return the outermost node of a domTree.
+const getOutermostNode = function(node, side = "right") {
if (node instanceof domTree.documentFragment ||
node instanceof domTree.anchor) {
if (node.children.length) {
if (side === "right") {
- return getTypeOfDomTree(
+ return getOutermostNode(
node.children[node.children.length - 1]);
} else if (side === "left") {
- return getTypeOfDomTree(
+ return getOutermostNode(
node.children[0]);
}
}
- } else {
- // This makes a lot of assumptions as to where the type of atom
- // appears. We should do a better job of enforcing this.
- if (utils.contains([
- "mord", "mop", "mbin", "mrel", "mopen", "mclose",
- "mpunct", "minner",
- ], node.classes[0])) {
- return node.classes[0];
- }
+ }
+ return node;
+};
+
+// Return math atom class (mclass) of a domTree.
+export const getTypeOfDomTree = function(node, side = "right") {
+ if (!node) {
+ return null;
+ }
+
+ node = getOutermostNode(node, side);
+ // This makes a lot of assumptions as to where the type of atom
+ // appears. We should do a better job of enforcing this.
+ if (utils.contains([
+ "mord", "mop", "mbin", "mrel", "mopen", "mclose",
+ "mpunct", "minner",
+ ], node.classes[0])) {
+ return node.classes[0];
}
return null;
};
@@ -181,14 +205,8 @@ export const getTypeOfDomTree = function(node, side = "right") {
// leftmost node in the fragment.
// 'mtight' indicates that the node is script or scriptscript style.
export const isLeftTight = function(node) {
- if (node instanceof domTree.documentFragment) {
- if (node.children.length) {
- return isLeftTight(node.children[0]);
- }
- } else {
- return utils.contains(node.classes, "mtight");
- }
- return false;
+ node = getOutermostNode(node, "left");
+ return utils.contains(node.classes, "mtight");
};
/**
diff --git a/src/functions/delimsizing.js b/src/functions/delimsizing.js
index 1d2ae561..5920a2e7 100644
--- a/src/functions/delimsizing.js
+++ b/src/functions/delimsizing.js
@@ -5,8 +5,6 @@ import delimiter from "../delimiter";
import mathMLTree from "../mathMLTree";
import ParseError from "../ParseError";
import utils from "../utils";
-import { calculateSize } from "../units";
-import { spacings, tightSpacings } from "../spacingData";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
@@ -161,7 +159,8 @@ defineFunction({
},
htmlBuilder: (group, options) => {
// Build the inner expression
- const inner = html.buildExpression(group.value.body, options, true);
+ const inner = html.buildExpression(group.value.body, options, true,
+ [null, "mclose"]);
let innerHeight = 0;
let innerDepth = 0;
@@ -210,18 +209,6 @@ defineFunction({
}
}
- const lastChildType = html.getTypeOfDomTree(inner[inner.length - 1]);
- const activeSpacings = options.style.isTight() ? tightSpacings : spacings;
-
- if (lastChildType && activeSpacings[lastChildType]["mclose"]) {
- const glue =
- buildCommon.makeSpan(["mord", "rule"], [], options);
- const dimension =
- calculateSize(activeSpacings[lastChildType]["mclose"], options);
- glue.style.marginRight = `${dimension}em`;
- inner.push(glue);
- }
-
let rightDelim;
// Same for the right delimiter
if (group.value.right === ".") {
diff --git a/src/functions/href.js b/src/functions/href.js
index bd529760..46b03764 100644
--- a/src/functions/href.js
+++ b/src/functions/href.js
@@ -31,40 +31,7 @@ defineFunction({
const href = group.value.href;
- /**
- * Determining class for anchors.
- * 1. if it has the only element, use its class;
- * 2. if it has more than two elements, and the classes
- * of its first and last elements coincide, then use it;
- * 3. otherwise, we will inject an empty s at both ends,
- * with the same classes of both ends of elements, with the
- * first span having the same class as the first element of body,
- * and the second one the same as the last.
- */
-
- let classes = []; // Default behaviour for Case 3.
- let first; // mathtype of the first child
- let last; // mathtype of the last child
- // Invariants: both first and last must be non-null if classes is null.
- if (elements.length === 1) { // Case 1
- classes = elements[0].classes;
- } else if (elements.length >= 2) {
- first = html.getTypeOfDomTree(elements[0]) || 'mord';
- last = html.getTypeOfDomTree(elements[elements.length - 1]) || 'mord';
- if (first === last) { // Case 2 : type of both ends coincides
- classes = [first];
- } else { // Case 3: both ends have different types.
- // TODO(kevinb): figure out a better way to communicate this
- // information to buildHTML.js#buildExpression.
- const anc = buildCommon.makeAnchor(href, [], elements, options);
- return new buildCommon.makeFragment([
- new buildCommon.makeSpan([first], [], options),
- anc,
- new buildCommon.makeSpan([last], [], options),
- ]);
- }
- }
- return new buildCommon.makeAnchor(href, classes, elements, options);
+ return new buildCommon.makeAnchor(href, [], elements, options);
},
mathmlBuilder: (group, options) => {
const inner = mml.buildExpression(group.value.body, options);