Add support for \left and \right
Summary: Added stacked delimiter support for more delimiters. Split out delimiter functions into its own file, and split out some tree building functions into a common file. Supports the empty `.` delimiter with \left and \right, and doesn't try to produce huge /, \backslash, <, or > delimiters. Depends on D7844 Test input: \left( \left) \left[ \left\lbrack \left] \left\rbrack \left\{ \left\lbrace \left\} \left\rbrace \left\lfloor \left\rfloor \left\lceil \left\rceil \left\langle \left\rangle \left/ \left\backslash \left| \left\vert \left\| \left\Vert \left\uparrow \left\Uparrow \left\downarrow \left\Downarrow \left\updownarrow \left\Updownarrow {x^{x^{x^{x^{x^{x^{x^{x^{x^{x^x}}}}}}}}}} \right.\right.\right.\right.\right.\right.\right.\right.\right.\right. \right.\right.\right.\right.\right.\right.\right.\right.\right.\right. \right.\right.\right.\right.\right.\right.\right.\right. Test Plan: - Run the test input, see that it works - Run the tests, see that they work - Look at huxley screenshots (not here yet :( ) and make sure they look good Reviewers: alpert Reviewed By: alpert Differential Revision: http://phabricator.khanacademy.org/D11602
37
Parser.js
|
@ -289,7 +289,8 @@ var delimiters = [
|
||||||
"|", "\\vert", "\\|", "\\Vert",
|
"|", "\\vert", "\\|", "\\Vert",
|
||||||
"\\uparrow", "\\Uparrow",
|
"\\uparrow", "\\Uparrow",
|
||||||
"\\downarrow", "\\Downarrow",
|
"\\downarrow", "\\Downarrow",
|
||||||
"\\updownarrow", "\\Updownarrow"
|
"\\updownarrow", "\\Updownarrow",
|
||||||
|
"."
|
||||||
];
|
];
|
||||||
|
|
||||||
// Parse a single delimiter
|
// Parse a single delimiter
|
||||||
|
@ -429,6 +430,40 @@ Parser.prototype.parseNucleus = function(pos, mode) {
|
||||||
throw new ParseError(
|
throw new ParseError(
|
||||||
"Expected delimiter after '" + nucleus.text + "'");
|
"Expected delimiter after '" + nucleus.text + "'");
|
||||||
}
|
}
|
||||||
|
} else if (mode === "math" && nucleus.type === "\\left") {
|
||||||
|
// If we see a \left, first we parse the left delimiter
|
||||||
|
var leftDelim = this.parseDelimiter(nucleus.position, mode);
|
||||||
|
if (leftDelim) {
|
||||||
|
// Then, we parse an inner expression. Due to the handling of \right
|
||||||
|
// below, this should end just before the \right
|
||||||
|
var expression = this.parseExpression(leftDelim.position, mode);
|
||||||
|
|
||||||
|
// Make sure we see a \right
|
||||||
|
var right = this.lexer.lex(expression.position, mode);
|
||||||
|
this.expect(right, "\\right");
|
||||||
|
|
||||||
|
// Parse the right delimiter
|
||||||
|
var rightDelim = this.parseDelimiter(right.position, mode);
|
||||||
|
if (rightDelim) {
|
||||||
|
return new ParseResult(
|
||||||
|
new ParseNode("leftright", {
|
||||||
|
left: leftDelim.result.value,
|
||||||
|
right: rightDelim.result.value,
|
||||||
|
body: expression.result
|
||||||
|
}, mode),
|
||||||
|
rightDelim.position);
|
||||||
|
} else {
|
||||||
|
throw new ParseError(
|
||||||
|
"Expected delimiter after '" + right.text + "'");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ParseError(
|
||||||
|
"Expected delimiter after '" + nucleus.text + "'");
|
||||||
|
}
|
||||||
|
} else if (mode === "math" && nucleus.type === "\\right") {
|
||||||
|
// If we see a right, we explicitly return null to break out of the
|
||||||
|
// parseExpression loop. The code for \left will handle the delimiter
|
||||||
|
return null;
|
||||||
} else if (nucleus.type === "\\llap" || nucleus.type === "\\rlap") {
|
} else if (nucleus.type === "\\llap" || nucleus.type === "\\rlap") {
|
||||||
// If this is an llap or rlap, parse its argument and return
|
// If this is an llap or rlap, parse its argument and return
|
||||||
var group = this.parseGroup(nucleus.position, mode);
|
var group = this.parseGroup(nucleus.position, mode);
|
||||||
|
|
4
Style.js
|
@ -77,5 +77,7 @@ var cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
DISPLAY: styles[D],
|
DISPLAY: styles[D],
|
||||||
TEXT: styles[T]
|
TEXT: styles[T],
|
||||||
|
SCRIPT: styles[S],
|
||||||
|
SCRIPTSCRIPT: styles[SS]
|
||||||
};
|
};
|
||||||
|
|
104
buildCommon.js
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
var domTree = require("./domTree");
|
||||||
|
var fontMetrics = require("./fontMetrics");
|
||||||
|
var symbols = require("./symbols");
|
||||||
|
|
||||||
|
var makeText = function(value, style, mode) {
|
||||||
|
if (symbols[mode][value] && symbols[mode][value].replace) {
|
||||||
|
value = symbols[mode][value].replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
var metrics = fontMetrics.getCharacterMetrics(value, style);
|
||||||
|
|
||||||
|
if (metrics) {
|
||||||
|
var textNode = new domTree.textNode(value, metrics.height,
|
||||||
|
metrics.depth);
|
||||||
|
if (metrics.italic > 0) {
|
||||||
|
var span = makeSpan([], [textNode]);
|
||||||
|
span.style.marginRight = metrics.italic + "em";
|
||||||
|
|
||||||
|
return span;
|
||||||
|
} else {
|
||||||
|
return textNode;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console && console.warn("No character metrics for '" + value +
|
||||||
|
"' in style '" + style + "'");
|
||||||
|
return new domTree.textNode(value, 0, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var mathit = function(value, mode) {
|
||||||
|
return makeSpan(["mathit"], [makeText(value, "Math-Italic", mode)]);
|
||||||
|
};
|
||||||
|
|
||||||
|
var mathrm = function(value, mode) {
|
||||||
|
if (symbols[mode][value].font === "main") {
|
||||||
|
return makeText(value, "Main-Regular", mode);
|
||||||
|
} else {
|
||||||
|
return makeSpan(["amsrm"], [makeText(value, "AMS-Regular", mode)]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var sizeElementFromChildren = function(elem) {
|
||||||
|
var height = 0;
|
||||||
|
var depth = 0;
|
||||||
|
var maxFontSize = 0;
|
||||||
|
|
||||||
|
if (elem.children) {
|
||||||
|
for (var i = 0; i < elem.children.length; i++) {
|
||||||
|
if (elem.children[i].height > height) {
|
||||||
|
height = elem.children[i].height;
|
||||||
|
}
|
||||||
|
if (elem.children[i].depth > depth) {
|
||||||
|
depth = elem.children[i].depth;
|
||||||
|
}
|
||||||
|
if (elem.children[i].maxFontSize > maxFontSize) {
|
||||||
|
maxFontSize = elem.children[i].maxFontSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.height = height;
|
||||||
|
elem.depth = depth;
|
||||||
|
elem.maxFontSize = maxFontSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeSpan = function(classes, children, color) {
|
||||||
|
var span = new domTree.span(classes, children);
|
||||||
|
|
||||||
|
sizeElementFromChildren(span);
|
||||||
|
|
||||||
|
if (color) {
|
||||||
|
span.style.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeFragment = function(children) {
|
||||||
|
var fragment = new domTree.documentFragment(children);
|
||||||
|
|
||||||
|
sizeElementFromChildren(fragment);
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeFontSizer = function(options, fontSize) {
|
||||||
|
var fontSizeInner = makeSpan([], [new domTree.textNode("\u200b")]);
|
||||||
|
fontSizeInner.style.fontSize = (fontSize / options.style.sizeMultiplier) + "em";
|
||||||
|
|
||||||
|
var fontSizer = makeSpan(
|
||||||
|
["fontsize-ensurer", "reset-" + options.size, "size5"],
|
||||||
|
[fontSizeInner]);
|
||||||
|
|
||||||
|
return fontSizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
makeText: makeText,
|
||||||
|
mathit: mathit,
|
||||||
|
mathrm: mathrm,
|
||||||
|
makeSpan: makeSpan,
|
||||||
|
makeFragment: makeFragment,
|
||||||
|
makeFontSizer: makeFontSizer
|
||||||
|
};
|
397
buildTree.js
|
@ -2,11 +2,15 @@ var Options = require("./Options");
|
||||||
var ParseError = require("./ParseError");
|
var ParseError = require("./ParseError");
|
||||||
var Style = require("./Style");
|
var Style = require("./Style");
|
||||||
|
|
||||||
|
var buildCommon = require("./buildCommon");
|
||||||
|
var delimiter = require("./delimiter");
|
||||||
var domTree = require("./domTree");
|
var domTree = require("./domTree");
|
||||||
var fontMetrics = require("./fontMetrics");
|
var fontMetrics = require("./fontMetrics");
|
||||||
var parseTree = require("./parseTree");
|
var parseTree = require("./parseTree");
|
||||||
var utils = require("./utils");
|
|
||||||
var symbols = require("./symbols");
|
var symbols = require("./symbols");
|
||||||
|
var utils = require("./utils");
|
||||||
|
|
||||||
|
var makeSpan = buildCommon.makeSpan;
|
||||||
|
|
||||||
var buildExpression = function(expression, options, prev) {
|
var buildExpression = function(expression, options, prev) {
|
||||||
var groups = [];
|
var groups = [];
|
||||||
|
@ -18,46 +22,6 @@ var buildExpression = function(expression, options, prev) {
|
||||||
return groups;
|
return groups;
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeSpan = function(classes, children, color) {
|
|
||||||
var height = 0;
|
|
||||||
var depth = 0;
|
|
||||||
var maxFontSize = 0;
|
|
||||||
|
|
||||||
if (children) {
|
|
||||||
for (var i = 0; i < children.length; i++) {
|
|
||||||
if (children[i].height > height) {
|
|
||||||
height = children[i].height;
|
|
||||||
}
|
|
||||||
if (children[i].depth > depth) {
|
|
||||||
depth = children[i].depth;
|
|
||||||
}
|
|
||||||
if (children[i].maxFontSize > maxFontSize) {
|
|
||||||
maxFontSize = children[i].maxFontSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var span = new domTree.span(
|
|
||||||
classes, children, height, depth, maxFontSize);
|
|
||||||
|
|
||||||
if (color) {
|
|
||||||
span.style.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
return span;
|
|
||||||
};
|
|
||||||
|
|
||||||
var makeFontSizer = function(options, fontSize) {
|
|
||||||
var fontSizeInner = makeSpan([], [new domTree.textNode("\u200b")]);
|
|
||||||
fontSizeInner.style.fontSize = (fontSize / options.style.sizeMultiplier) + "em";
|
|
||||||
|
|
||||||
var fontSizer = makeSpan(
|
|
||||||
["fontsize-ensurer", "reset-" + options.size, "size5"],
|
|
||||||
[fontSizeInner]);
|
|
||||||
|
|
||||||
return fontSizer;
|
|
||||||
};
|
|
||||||
|
|
||||||
var groupToType = {
|
var groupToType = {
|
||||||
mathord: "mord",
|
mathord: "mord",
|
||||||
textord: "mord",
|
textord: "mord",
|
||||||
|
@ -73,7 +37,8 @@ var groupToType = {
|
||||||
namedfn: "mop",
|
namedfn: "mop",
|
||||||
katex: "mord",
|
katex: "mord",
|
||||||
overline: "mord",
|
overline: "mord",
|
||||||
rule: "mord"
|
rule: "mord",
|
||||||
|
leftright: "minner"
|
||||||
};
|
};
|
||||||
|
|
||||||
var getTypeOfGroup = function(group) {
|
var getTypeOfGroup = function(group) {
|
||||||
|
@ -89,7 +54,7 @@ var getTypeOfGroup = function(group) {
|
||||||
} else if (group.type === "sizing") {
|
} else if (group.type === "sizing") {
|
||||||
return getTypeOfGroup(group.value.value);
|
return getTypeOfGroup(group.value.value);
|
||||||
} else if (group.type === "delimsizing") {
|
} else if (group.type === "delimsizing") {
|
||||||
return group.value.type;
|
return groupToType[group.value.type];
|
||||||
} else {
|
} else {
|
||||||
return groupToType[group.type];
|
return groupToType[group.type];
|
||||||
}
|
}
|
||||||
|
@ -117,7 +82,7 @@ var groupTypes = {
|
||||||
mathord: function(group, options, prev) {
|
mathord: function(group, options, prev) {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mord"],
|
["mord"],
|
||||||
[mathit(group.value, group.mode)],
|
[buildCommon.mathit(group.value, group.mode)],
|
||||||
options.getColor()
|
options.getColor()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -125,7 +90,7 @@ var groupTypes = {
|
||||||
textord: function(group, options, prev) {
|
textord: function(group, options, prev) {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mord"],
|
["mord"],
|
||||||
[mathrm(group.value, group.mode)],
|
[buildCommon.mathrm(group.value, group.mode)],
|
||||||
options.getColor()
|
options.getColor()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -137,14 +102,14 @@ var groupTypes = {
|
||||||
var atoms = prevAtom.value.value;
|
var atoms = prevAtom.value.value;
|
||||||
prevAtom = atoms[atoms.length - 1];
|
prevAtom = atoms[atoms.length - 1];
|
||||||
}
|
}
|
||||||
if (!prev || utils.contains(["bin", "open", "rel", "op", "punct"],
|
if (!prev || utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
|
||||||
prevAtom.type)) {
|
getTypeOfGroup(prevAtom))) {
|
||||||
group.type = "ord";
|
group.type = "ord";
|
||||||
className = "mord";
|
className = "mord";
|
||||||
}
|
}
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
[className],
|
[className],
|
||||||
[mathrm(group.value, group.mode)],
|
[buildCommon.mathrm(group.value, group.mode)],
|
||||||
options.getColor()
|
options.getColor()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -152,7 +117,7 @@ var groupTypes = {
|
||||||
rel: function(group, options, prev) {
|
rel: function(group, options, prev) {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mrel"],
|
["mrel"],
|
||||||
[mathrm(group.value, group.mode)],
|
[buildCommon.mathrm(group.value, group.mode)],
|
||||||
options.getColor()
|
options.getColor()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -199,13 +164,13 @@ var groupTypes = {
|
||||||
|
|
||||||
var multiplier = Style.TEXT.sizeMultiplier *
|
var multiplier = Style.TEXT.sizeMultiplier *
|
||||||
options.style.sizeMultiplier;
|
options.style.sizeMultiplier;
|
||||||
// \scriptspace is 0.5pt = 0.05em * 10pt/em
|
var scriptspace =
|
||||||
var scriptspace = 0.05 / multiplier + "em";
|
(0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em";
|
||||||
|
|
||||||
var supsub;
|
var supsub;
|
||||||
|
|
||||||
if (!group.value.sup) {
|
if (!group.value.sup) {
|
||||||
var fontSizer = makeFontSizer(options, submid.maxFontSize);
|
var fontSizer = buildCommon.makeFontSizer(options, submid.maxFontSize);
|
||||||
var subwrap = makeSpan(["msub"], [fontSizer, submid]);
|
var subwrap = makeSpan(["msub"], [fontSizer, submid]);
|
||||||
|
|
||||||
v = Math.max(v, fontMetrics.metrics.sub1,
|
v = Math.max(v, fontMetrics.metrics.sub1,
|
||||||
|
@ -221,7 +186,7 @@ var groupTypes = {
|
||||||
|
|
||||||
supsub = makeSpan(["msupsub"], [subwrap, fixIE]);
|
supsub = makeSpan(["msupsub"], [subwrap, fixIE]);
|
||||||
} else if (!group.value.sub) {
|
} else if (!group.value.sub) {
|
||||||
var fontSizer = makeFontSizer(options, supmid.maxFontSize);
|
var fontSizer = buildCommon.makeFontSizer(options, supmid.maxFontSize);
|
||||||
var supwrap = makeSpan(["msup"], [fontSizer, supmid]);
|
var supwrap = makeSpan(["msup"], [fontSizer, supmid]);
|
||||||
|
|
||||||
u = Math.max(u, p,
|
u = Math.max(u, p,
|
||||||
|
@ -237,7 +202,7 @@ var groupTypes = {
|
||||||
|
|
||||||
supsub = makeSpan(["msupsub"], [supwrap, fixIE]);
|
supsub = makeSpan(["msupsub"], [supwrap, fixIE]);
|
||||||
} else {
|
} else {
|
||||||
var fontSizer = makeFontSizer(options,
|
var fontSizer = buildCommon.makeFontSizer(options,
|
||||||
Math.max(submid.maxFontSize, supmid.maxFontSize));
|
Math.max(submid.maxFontSize, supmid.maxFontSize));
|
||||||
var subwrap = makeSpan(["msub"], [fontSizer, submid]);
|
var subwrap = makeSpan(["msub"], [fontSizer, submid]);
|
||||||
var supwrap = makeSpan(["msup"], [fontSizer, supmid]);
|
var supwrap = makeSpan(["msup"], [fontSizer, supmid]);
|
||||||
|
@ -274,13 +239,14 @@ var groupTypes = {
|
||||||
supsub = makeSpan(["msupsub"], [supwrap, subwrap, fixIE]);
|
supsub = makeSpan(["msupsub"], [supwrap, subwrap, fixIE]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeSpan([getTypeOfGroup(group.value.base)], [base, supsub]);
|
return makeSpan([getTypeOfGroup(group.value.base)],
|
||||||
|
[base, supsub]);
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(group, options, prev) {
|
open: function(group, options, prev) {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mopen"],
|
["mopen"],
|
||||||
[mathrm(group.value, group.mode)],
|
[buildCommon.mathrm(group.value, group.mode)],
|
||||||
options.getColor()
|
options.getColor()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -288,7 +254,7 @@ var groupTypes = {
|
||||||
close: function(group, options, prev) {
|
close: function(group, options, prev) {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mclose"],
|
["mclose"],
|
||||||
[mathrm(group.value, group.mode)],
|
[buildCommon.mathrm(group.value, group.mode)],
|
||||||
options.getColor()
|
options.getColor()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -310,7 +276,7 @@ var groupTypes = {
|
||||||
var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
|
var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
|
||||||
var denomdenom = makeSpan([fstyle.reset(), dstyle.cls()], [denom])
|
var denomdenom = makeSpan([fstyle.reset(), dstyle.cls()], [denom])
|
||||||
|
|
||||||
var fontSizer = makeFontSizer(options,
|
var fontSizer = buildCommon.makeFontSizer(options,
|
||||||
Math.max(numer.maxFontSize, denom.maxFontSize));
|
Math.max(numer.maxFontSize, denom.maxFontSize));
|
||||||
|
|
||||||
var numerrow = makeSpan(["mfracnum"], [fontSizer, numernumer]);
|
var numerrow = makeSpan(["mfracnum"], [fontSizer, numernumer]);
|
||||||
|
@ -358,7 +324,8 @@ var groupTypes = {
|
||||||
frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
||||||
frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
||||||
|
|
||||||
var wrap = makeSpan([options.style.reset(), fstyle.cls()], [frac]);
|
var wrap = makeSpan(
|
||||||
|
[options.style.reset(), fstyle.cls()], [frac]);
|
||||||
|
|
||||||
return makeSpan(["minner"], [
|
return makeSpan(["minner"], [
|
||||||
makeSpan(["mfrac"], [wrap])
|
makeSpan(["mfrac"], [wrap])
|
||||||
|
@ -366,25 +333,13 @@ var groupTypes = {
|
||||||
},
|
},
|
||||||
|
|
||||||
color: function(group, options, prev) {
|
color: function(group, options, prev) {
|
||||||
var els = buildExpression(
|
var elements = buildExpression(
|
||||||
group.value.value,
|
group.value.value,
|
||||||
options.withColor(group.value.color),
|
options.withColor(group.value.color),
|
||||||
prev
|
prev
|
||||||
);
|
);
|
||||||
|
|
||||||
var height = 0;
|
return new buildCommon.makeFragment(elements);
|
||||||
var depth = 0;
|
|
||||||
|
|
||||||
for (var i = 0; i < els.length; i++) {
|
|
||||||
if (els[i].height > height) {
|
|
||||||
var height = els[i].height;
|
|
||||||
}
|
|
||||||
if (els[i].depth > depth) {
|
|
||||||
var depth = els[i].depth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new domTree.documentFragment(els, height, depth);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
spacing: function(group, options, prev) {
|
spacing: function(group, options, prev) {
|
||||||
|
@ -392,7 +347,7 @@ var groupTypes = {
|
||||||
group.value === " " || group.value === "~") {
|
group.value === " " || group.value === "~") {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mord", "mspace"],
|
["mord", "mspace"],
|
||||||
[mathrm(group.value, group.mode)]
|
[buildCommon.mathrm(group.value, group.mode)]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
var spacingClassMap = {
|
var spacingClassMap = {
|
||||||
|
@ -405,7 +360,8 @@ var groupTypes = {
|
||||||
"\\!": "negativethinspace"
|
"\\!": "negativethinspace"
|
||||||
};
|
};
|
||||||
|
|
||||||
return makeSpan(["mord", "mspace", spacingClassMap[group.value]]);
|
return makeSpan(
|
||||||
|
["mord", "mspace", spacingClassMap[group.value]]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -413,20 +369,22 @@ var groupTypes = {
|
||||||
var inner = makeSpan(
|
var inner = makeSpan(
|
||||||
["inner"], [buildGroup(group.value, options.reset())]);
|
["inner"], [buildGroup(group.value, options.reset())]);
|
||||||
var fix = makeSpan(["fix"], []);
|
var fix = makeSpan(["fix"], []);
|
||||||
return makeSpan(["llap", options.style.cls()], [inner, fix]);
|
return makeSpan(
|
||||||
|
["llap", options.style.cls()], [inner, fix]);
|
||||||
},
|
},
|
||||||
|
|
||||||
rlap: function(group, options, prev) {
|
rlap: function(group, options, prev) {
|
||||||
var inner = makeSpan(
|
var inner = makeSpan(
|
||||||
["inner"], [buildGroup(group.value, options.reset())]);
|
["inner"], [buildGroup(group.value, options.reset())]);
|
||||||
var fix = makeSpan(["fix"], []);
|
var fix = makeSpan(["fix"], []);
|
||||||
return makeSpan(["rlap", options.style.cls()], [inner, fix]);
|
return makeSpan(
|
||||||
|
["rlap", options.style.cls()], [inner, fix]);
|
||||||
},
|
},
|
||||||
|
|
||||||
punct: function(group, options, prev) {
|
punct: function(group, options, prev) {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mpunct"],
|
["mpunct"],
|
||||||
[mathrm(group.value, group.mode)],
|
[buildCommon.mathrm(group.value, group.mode)],
|
||||||
options.getColor()
|
options.getColor()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -441,35 +399,41 @@ var groupTypes = {
|
||||||
namedfn: function(group, options, prev) {
|
namedfn: function(group, options, prev) {
|
||||||
var chars = [];
|
var chars = [];
|
||||||
for (var i = 1; i < group.value.length; i++) {
|
for (var i = 1; i < group.value.length; i++) {
|
||||||
chars.push(mathrm(group.value[i], group.mode));
|
chars.push(buildCommon.mathrm(group.value[i], group.mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeSpan(["mop"], chars, options.getColor());
|
return makeSpan(["mop"], chars, options.getColor());
|
||||||
},
|
},
|
||||||
|
|
||||||
katex: function(group, options, prev) {
|
katex: function(group, options, prev) {
|
||||||
var k = makeSpan(["k"], [mathrm("K", group.mode)]);
|
var k = makeSpan(
|
||||||
var a = makeSpan(["a"], [mathrm("A", group.mode)]);
|
["k"], [buildCommon.mathrm("K", group.mode)]);
|
||||||
|
var a = makeSpan(
|
||||||
|
["a"], [buildCommon.mathrm("A", group.mode)]);
|
||||||
|
|
||||||
a.height = (a.height + 0.2) * 0.75;
|
a.height = (a.height + 0.2) * 0.75;
|
||||||
a.depth = (a.height - 0.2) * 0.75;
|
a.depth = (a.height - 0.2) * 0.75;
|
||||||
|
|
||||||
var t = makeSpan(["t"], [mathrm("T", group.mode)]);
|
var t = makeSpan(
|
||||||
var e = makeSpan(["e"], [mathrm("E", group.mode)]);
|
["t"], [buildCommon.mathrm("T", group.mode)]);
|
||||||
|
var e = makeSpan(
|
||||||
|
["e"], [buildCommon.mathrm("E", group.mode)]);
|
||||||
|
|
||||||
e.height = (e.height - 0.2155);
|
e.height = (e.height - 0.2155);
|
||||||
e.depth = (e.depth + 0.2155);
|
e.depth = (e.depth + 0.2155);
|
||||||
|
|
||||||
var x = makeSpan(["x"], [mathrm("X", group.mode)]);
|
var x = makeSpan(
|
||||||
|
["x"], [buildCommon.mathrm("X", group.mode)]);
|
||||||
|
|
||||||
return makeSpan(["katex-logo"], [k, a, t, e, x], options.getColor());
|
return makeSpan(
|
||||||
|
["katex-logo"], [k, a, t, e, x], options.getColor());
|
||||||
},
|
},
|
||||||
|
|
||||||
overline: function(group, options, prev) {
|
overline: function(group, options, prev) {
|
||||||
var innerGroup = buildGroup(group.value.result,
|
var innerGroup = buildGroup(group.value.result,
|
||||||
options.withStyle(options.style.cramp()));
|
options.withStyle(options.style.cramp()));
|
||||||
|
|
||||||
var fontSizer = makeFontSizer(options, innerGroup.maxFontSize);
|
var fontSizer = buildCommon.makeFontSizer(options, innerGroup.maxFontSize);
|
||||||
|
|
||||||
// The theta variable in the TeXbook
|
// The theta variable in the TeXbook
|
||||||
var lineWidth = fontMetrics.metrics.defaultRuleThickness;
|
var lineWidth = fontMetrics.metrics.defaultRuleThickness;
|
||||||
|
@ -518,185 +482,51 @@ var groupTypes = {
|
||||||
},
|
},
|
||||||
|
|
||||||
delimsizing: function(group, options, prev) {
|
delimsizing: function(group, options, prev) {
|
||||||
var normalDelimiters = [
|
var delim = group.value.value;
|
||||||
"(", ")", "[", "\\lbrack", "]", "\\rbrack",
|
|
||||||
"\\{", "\\lbrace", "\\}", "\\rbrace",
|
|
||||||
"\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
|
|
||||||
"<", ">", "\\langle", "\\rangle", "/", "\\backslash"
|
|
||||||
];
|
|
||||||
|
|
||||||
var stackDelimiters = [
|
if (delim === ".") {
|
||||||
"\\uparrow", "\\downarrow", "\\updownarrow",
|
return buildCommon.makeSpan([groupToType[group.value.type]]);
|
||||||
"\\Uparrow", "\\Downarrow", "\\Updownarrow",
|
|
||||||
"|", "\\|", "\\vert", "\\Vert"
|
|
||||||
];
|
|
||||||
|
|
||||||
// Metrics of the different sizes. Found by looking at TeX's output of
|
|
||||||
// $\bigl| \Bigl| \biggl| \Biggl| \showlists$
|
|
||||||
var sizeToMetrics = {
|
|
||||||
1: {height: .85, depth: .35},
|
|
||||||
2: {height: 1.15, depth: .65},
|
|
||||||
3: {height: 1.45, depth: .95},
|
|
||||||
4: {height: 1.75, depth: 1.25}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make an inner span with the given offset and in the given font
|
|
||||||
var makeInner = function(symbol, offset, font) {
|
|
||||||
var sizeClass;
|
|
||||||
if (font === "Size1-Regular") {
|
|
||||||
sizeClass = "size1";
|
|
||||||
}
|
|
||||||
|
|
||||||
var inner = makeSpan(
|
|
||||||
["delimsizinginner", sizeClass],
|
|
||||||
[makeSpan([], [makeText(symbol, font, group.mode)])]);
|
|
||||||
|
|
||||||
inner.style.top = offset + "em";
|
|
||||||
inner.height -= offset;
|
|
||||||
inner.depth += offset;
|
|
||||||
|
|
||||||
return inner;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the metrics for a given symbol and font, after transformation
|
|
||||||
var getMetrics = function(symbol, font) {
|
|
||||||
if (symbols["math"][symbol] && symbols["math"][symbol].replace) {
|
|
||||||
return fontMetrics.getCharacterMetrics(
|
|
||||||
symbols["math"][symbol].replace, font);
|
|
||||||
} else {
|
|
||||||
return fontMetrics.getCharacterMetrics(
|
|
||||||
symbol, font);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var original = group.value.value;
|
|
||||||
|
|
||||||
if (utils.contains(normalDelimiters, original)) {
|
|
||||||
// These delimiters can be created by simply using the size1-size4
|
|
||||||
// fonts, so they don't require special treatment
|
|
||||||
if (original === "<") {
|
|
||||||
original = "\\langle";
|
|
||||||
} else if (original === ">") {
|
|
||||||
original = "\\rangle";
|
|
||||||
}
|
|
||||||
|
|
||||||
var size = "size" + group.value.size;
|
|
||||||
var inner = mathrmSize(
|
|
||||||
original, group.value.size, group.mode);
|
|
||||||
|
|
||||||
var node = makeSpan(
|
|
||||||
[options.style.reset(), Style.TEXT.cls(),
|
|
||||||
groupToType[group.value.type]],
|
|
||||||
[makeSpan(
|
|
||||||
["delimsizing", size, groupToType[group.value.type]],
|
|
||||||
[inner], options.getColor())]);
|
|
||||||
|
|
||||||
var multiplier = Style.TEXT.sizeMultiplier /
|
|
||||||
options.style.sizeMultiplier;
|
|
||||||
|
|
||||||
node.height *= multiplier;
|
|
||||||
node.depth *= multiplier;
|
|
||||||
node.maxFontSize = 1.0;
|
|
||||||
|
|
||||||
return node;
|
|
||||||
} else if (utils.contains(stackDelimiters, original)) {
|
|
||||||
// These delimiters can be created by stacking other delimiters on
|
|
||||||
// top of each other to create the correct size
|
|
||||||
|
|
||||||
// There are three parts, the top, a repeated middle, and a bottom.
|
|
||||||
var top = middle = bottom = original;
|
|
||||||
var font = "Size1-Regular";
|
|
||||||
var overlap = false;
|
|
||||||
|
|
||||||
// We set the parts and font based on the symbol. Note that we use
|
|
||||||
// '\u23d0' instead of '|' and '\u2016' instead of '\\|' for the
|
|
||||||
// middles of the arrows
|
|
||||||
if (original === "\\uparrow") {
|
|
||||||
middle = bottom = "\u23d0";
|
|
||||||
} else if (original === "\\Uparrow") {
|
|
||||||
middle = bottom = "\u2016";
|
|
||||||
} else if (original === "\\downarrow") {
|
|
||||||
top = middle = "\u23d0";
|
|
||||||
} else if (original === "\\Downarrow") {
|
|
||||||
top = middle = "\u2016";
|
|
||||||
} else if (original === "\\updownarrow") {
|
|
||||||
top = "\\uparrow";
|
|
||||||
middle = "\u23d0";
|
|
||||||
bottom = "\\downarrow";
|
|
||||||
} else if (original === "\\Updownarrow") {
|
|
||||||
top = "\\Uparrow";
|
|
||||||
middle = "\u2016";
|
|
||||||
bottom = "\\Downarrow";
|
|
||||||
} else if (original === "|" || original === "\\vert") {
|
|
||||||
overlap = true;
|
|
||||||
} else if (original === "\\|" || original === "\\Vert") {
|
|
||||||
overlap = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the metrics of the final symbol
|
|
||||||
var metrics = sizeToMetrics[group.value.size];
|
|
||||||
var heightTotal = metrics.height + metrics.depth;
|
|
||||||
|
|
||||||
// Get the metrics of the three sections
|
|
||||||
var topMetrics = getMetrics(top, font);
|
|
||||||
var topHeightTotal = topMetrics.height + topMetrics.depth;
|
|
||||||
var middleMetrics = getMetrics(middle, font);
|
|
||||||
var middleHeightTotal = middleMetrics.height + middleMetrics.depth;
|
|
||||||
var bottomMetrics = getMetrics(bottom, font);
|
|
||||||
var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth;
|
|
||||||
|
|
||||||
var middleHeight = heightTotal - topHeightTotal - bottomHeightTotal;
|
|
||||||
var symbolCount = Math.ceil(middleHeight / middleHeightTotal);
|
|
||||||
|
|
||||||
if (overlap) {
|
|
||||||
// 2 * overlapAmount + middleHeight =
|
|
||||||
// (symbolCount - 1) * (middleHeightTotal - overlapAmount) +
|
|
||||||
// middleHeightTotal
|
|
||||||
var overlapAmount = (symbolCount * middleHeightTotal -
|
|
||||||
middleHeight) / (symbolCount + 1);
|
|
||||||
} else {
|
|
||||||
var overlapAmount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep a list of the inner spans
|
|
||||||
var inners = [];
|
|
||||||
|
|
||||||
// Add the top symbol
|
|
||||||
inners.push(
|
|
||||||
makeInner(top, topMetrics.height - metrics.height, font));
|
|
||||||
|
|
||||||
// Add middle symbols until there's only space for the bottom symbol
|
|
||||||
var curr_height = metrics.height - topHeightTotal + overlapAmount;
|
|
||||||
for (var i = 0; i < symbolCount; i++) {
|
|
||||||
inners.push(
|
|
||||||
makeInner(middle, middleMetrics.height - curr_height, font));
|
|
||||||
curr_height -= middleHeightTotal - overlapAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the bottom symbol
|
|
||||||
inners.push(
|
|
||||||
makeInner(bottom, metrics.depth - bottomMetrics.depth, font));
|
|
||||||
|
|
||||||
var fixIE = makeSpan(["fix-ie"], [new domTree.textNode("\u00a0")]);
|
|
||||||
inners.push(fixIE);
|
|
||||||
|
|
||||||
var node = makeSpan(
|
|
||||||
[options.style.reset(), Style.TEXT.cls(),
|
|
||||||
groupToType[group.value.type]],
|
|
||||||
[makeSpan(["delimsizing", "mult"],
|
|
||||||
inners, options.getColor())]);
|
|
||||||
|
|
||||||
var multiplier = Style.TEXT.sizeMultiplier /
|
|
||||||
options.style.sizeMultiplier;
|
|
||||||
|
|
||||||
node.height *= multiplier;
|
|
||||||
node.depth *= multiplier;
|
|
||||||
node.maxFontSize = 1.0;
|
|
||||||
|
|
||||||
return node;
|
|
||||||
} else {
|
|
||||||
throw new ParseError("Illegal delimiter: '" + original + "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return delimiter.sizedDelim(
|
||||||
|
delim, group.value.size, options, group.mode);
|
||||||
|
},
|
||||||
|
|
||||||
|
leftright: function(group, options, prev) {
|
||||||
|
var inner = buildExpression(group.value.body, options.reset());
|
||||||
|
|
||||||
|
var innerHeight = 0;
|
||||||
|
var innerDepth = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < inner.length; i++) {
|
||||||
|
innerHeight = Math.max(inner[i].height, innerHeight);
|
||||||
|
innerDepth = Math.max(inner[i].depth, innerDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
innerHeight *= options.style.sizeMultiplier;
|
||||||
|
innerDepth *= options.style.sizeMultiplier;
|
||||||
|
|
||||||
|
var leftDelim;
|
||||||
|
if (group.value.left === ".") {
|
||||||
|
leftDelim = makeSpan(["nulldelimiter"]);
|
||||||
|
} else {
|
||||||
|
leftDelim = delimiter.leftRightDelim(
|
||||||
|
group.value.left, innerHeight, innerDepth, options,
|
||||||
|
group.mode);
|
||||||
|
}
|
||||||
|
inner.unshift(leftDelim);
|
||||||
|
|
||||||
|
var rightDelim;
|
||||||
|
if (group.value.right === ".") {
|
||||||
|
rightDelim = makeSpan(["nulldelimiter"]);
|
||||||
|
} else {
|
||||||
|
rightDelim = delimiter.leftRightDelim(
|
||||||
|
group.value.right, innerHeight, innerDepth, options,
|
||||||
|
group.mode);
|
||||||
|
}
|
||||||
|
inner.push(rightDelim);
|
||||||
|
|
||||||
|
return makeSpan(["minner"], inner, options.getColor());
|
||||||
},
|
},
|
||||||
|
|
||||||
rule: function(group, options, prev) {
|
rule: function(group, options, prev) {
|
||||||
|
@ -772,47 +602,6 @@ var buildGroup = function(group, options, prev) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeText = function(value, style, mode) {
|
|
||||||
if (symbols[mode][value] && symbols[mode][value].replace) {
|
|
||||||
value = symbols[mode][value].replace;
|
|
||||||
}
|
|
||||||
|
|
||||||
var metrics = fontMetrics.getCharacterMetrics(value, style);
|
|
||||||
|
|
||||||
if (metrics) {
|
|
||||||
var textNode = new domTree.textNode(value, metrics.height,
|
|
||||||
metrics.depth);
|
|
||||||
if (metrics.italic > 0) {
|
|
||||||
var span = makeSpan([], [textNode]);
|
|
||||||
span.style.marginRight = metrics.italic + "em";
|
|
||||||
|
|
||||||
return span;
|
|
||||||
} else {
|
|
||||||
return textNode;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console && console.warn("No character metrics for '" + value +
|
|
||||||
"' in style '" + style + "'");
|
|
||||||
return new domTree.textNode(value, 0, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var mathit = function(value, mode) {
|
|
||||||
return makeSpan(["mathit"], [makeText(value, "Math-Italic", mode)]);
|
|
||||||
};
|
|
||||||
|
|
||||||
var mathrm = function(value, mode) {
|
|
||||||
if (symbols[mode][value].font === "main") {
|
|
||||||
return makeText(value, "Main-Regular", mode);
|
|
||||||
} else {
|
|
||||||
return makeSpan(["amsrm"], [makeText(value, "AMS-Regular", mode)]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var mathrmSize = function(value, size, mode) {
|
|
||||||
return makeText(value, "Size" + size + "-Regular", mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
var buildTree = function(tree) {
|
var buildTree = function(tree) {
|
||||||
// Setup the default options
|
// Setup the default options
|
||||||
var options = new Options(Style.TEXT, "size5", "");
|
var options = new Options(Style.TEXT, "size5", "");
|
||||||
|
|
475
delimiter.js
Normal file
|
@ -0,0 +1,475 @@
|
||||||
|
var Options = require("./Options");
|
||||||
|
var ParseError = require("./ParseError");
|
||||||
|
var Style = require("./Style");
|
||||||
|
|
||||||
|
var domTree = require("./domTree");
|
||||||
|
var fontMetrics = require("./fontMetrics");
|
||||||
|
var parseTree = require("./parseTree");
|
||||||
|
var utils = require("./utils");
|
||||||
|
var symbols = require("./symbols");
|
||||||
|
var buildCommon = require("./buildCommon");
|
||||||
|
var makeSpan = require("./buildCommon").makeSpan;
|
||||||
|
|
||||||
|
// Get the metrics for a given symbol and font, after transformation (i.e.
|
||||||
|
// after following replacement from symbols.js)
|
||||||
|
var getMetrics = function(symbol, font) {
|
||||||
|
if (symbols["math"][symbol] && symbols["math"][symbol].replace) {
|
||||||
|
return fontMetrics.getCharacterMetrics(
|
||||||
|
symbols["math"][symbol].replace, font);
|
||||||
|
} else {
|
||||||
|
return fontMetrics.getCharacterMetrics(
|
||||||
|
symbol, font);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var mathrmSize = function(value, size, mode) {
|
||||||
|
return buildCommon.makeText(value, "Size" + size + "-Regular", mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
var styleWrap = function(delim, toStyle, options) {
|
||||||
|
var span = makeSpan(["style-wrap", options.style.reset(), toStyle.cls()], [delim]);
|
||||||
|
|
||||||
|
var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
|
||||||
|
|
||||||
|
span.height *= multiplier;
|
||||||
|
span.depth *= multiplier;
|
||||||
|
span.maxFontSize = toStyle.sizeMultiplier;
|
||||||
|
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeSmallDelim = function(delim, style, center, options, mode) {
|
||||||
|
var text = buildCommon.makeText(delim, "Main-Regular", mode);
|
||||||
|
|
||||||
|
var span = styleWrap(text, style, options);
|
||||||
|
|
||||||
|
if (center) {
|
||||||
|
var shift =
|
||||||
|
(1 - options.style.sizeMultiplier / style.sizeMultiplier) *
|
||||||
|
fontMetrics.metrics.axisHeight;
|
||||||
|
|
||||||
|
span.style.top = shift + "em";
|
||||||
|
span.height -= shift;
|
||||||
|
span.depth += shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeLargeDelim = function(delim, size, center, options, mode) {
|
||||||
|
var inner = mathrmSize(delim, size, mode);
|
||||||
|
|
||||||
|
var span = styleWrap(
|
||||||
|
makeSpan(["delimsizing", "size" + size],
|
||||||
|
[inner], options.getColor()),
|
||||||
|
Style.TEXT, options);
|
||||||
|
|
||||||
|
if (center) {
|
||||||
|
var shift = (1 - options.style.sizeMultiplier) *
|
||||||
|
fontMetrics.metrics.axisHeight;
|
||||||
|
|
||||||
|
span.style.top = shift + "em";
|
||||||
|
span.height -= shift;
|
||||||
|
span.depth += shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make an inner span with the given offset and in the given font
|
||||||
|
var makeInner = function(symbol, offset, font, mode) {
|
||||||
|
var sizeClass;
|
||||||
|
if (font === "Size1-Regular") {
|
||||||
|
sizeClass = "size1";
|
||||||
|
} else if (font === "Size4-Regular") {
|
||||||
|
sizeClass = "size4";
|
||||||
|
}
|
||||||
|
|
||||||
|
var inner = makeSpan(
|
||||||
|
["delimsizinginner", sizeClass],
|
||||||
|
[makeSpan([], [buildCommon.makeText(symbol, font, mode)])]);
|
||||||
|
|
||||||
|
inner.style.top = offset + "em";
|
||||||
|
inner.height -= offset;
|
||||||
|
inner.depth += offset;
|
||||||
|
|
||||||
|
return inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
|
||||||
|
// There are four parts, the top, a middle, a repeated part, and a bottom.
|
||||||
|
var top, middle, repeat, bottom;
|
||||||
|
top = repeat = bottom = delim;
|
||||||
|
middle = null;
|
||||||
|
var font = "Size1-Regular";
|
||||||
|
var overlap = false;
|
||||||
|
|
||||||
|
// We set the parts and font based on the symbol. Note that we use
|
||||||
|
// '\u23d0' instead of '|' and '\u2016' instead of '\\|' for the
|
||||||
|
// repeats of the arrows
|
||||||
|
if (delim === "\\uparrow") {
|
||||||
|
repeat = bottom = "\u23d0";
|
||||||
|
} else if (delim === "\\Uparrow") {
|
||||||
|
repeat = bottom = "\u2016";
|
||||||
|
} else if (delim === "\\downarrow") {
|
||||||
|
top = repeat = "\u23d0";
|
||||||
|
} else if (delim === "\\Downarrow") {
|
||||||
|
top = repeat = "\u2016";
|
||||||
|
} else if (delim === "\\updownarrow") {
|
||||||
|
top = "\\uparrow";
|
||||||
|
repeat = "\u23d0";
|
||||||
|
bottom = "\\downarrow";
|
||||||
|
} else if (delim === "\\Updownarrow") {
|
||||||
|
top = "\\Uparrow";
|
||||||
|
repeat = "\u2016";
|
||||||
|
bottom = "\\Downarrow";
|
||||||
|
|
||||||
|
// For some reason, the sizes of this one delimiter don't work out
|
||||||
|
// right, so we shrink it a bit to make it now add an extraneous
|
||||||
|
// repeating part
|
||||||
|
if (height + depth <= 1.21) {
|
||||||
|
height -= 0.01;
|
||||||
|
depth -= 0.01;
|
||||||
|
}
|
||||||
|
} else if (delim === "|" || delim === "\\vert") {
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "\\|" || delim === "\\Vert") {
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "[" || delim === "\\lbrack") {
|
||||||
|
top = "\u23a1";
|
||||||
|
repeat = "\u23a2";
|
||||||
|
bottom = "\u23a3";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "]" || delim === "\\rbrack") {
|
||||||
|
top = "\u23a4";
|
||||||
|
repeat = "\u23a5";
|
||||||
|
bottom = "\u23a6";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "\\lfloor") {
|
||||||
|
repeat = top = "\u23a2";
|
||||||
|
bottom = "\u23a3";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "\\lceil") {
|
||||||
|
top = "\u23a1";
|
||||||
|
repeat = bottom = "\u23a2";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "\\rfloor") {
|
||||||
|
repeat = top = "\u23a5";
|
||||||
|
bottom = "\u23a6";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "\\rceil") {
|
||||||
|
top = "\u23a4";
|
||||||
|
repeat = bottom = "\u23a5";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "(") {
|
||||||
|
top = "\u239b";
|
||||||
|
repeat = "\u239c";
|
||||||
|
bottom = "\u239d";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === ")") {
|
||||||
|
top = "\u239e";
|
||||||
|
repeat = "\u239f";
|
||||||
|
bottom = "\u23a0";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
overlap = true;
|
||||||
|
} else if (delim === "\\{" || delim === "\\lbrace") {
|
||||||
|
top = "\u23a7";
|
||||||
|
middle = "\u23a8";
|
||||||
|
bottom = "\u23a9";
|
||||||
|
repeat = "\u23aa";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
} else if (delim === "\\}" || delim === "\\rbrace") {
|
||||||
|
top = "\u23ab";
|
||||||
|
middle = "\u23ac";
|
||||||
|
bottom = "\u23ad";
|
||||||
|
repeat = "\u23aa";
|
||||||
|
font = "Size4-Regular";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the metrics of the three sections
|
||||||
|
var topMetrics = getMetrics(top, font);
|
||||||
|
var topHeightTotal = topMetrics.height + topMetrics.depth;
|
||||||
|
var repeatMetrics = getMetrics(repeat, font);
|
||||||
|
var repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth;
|
||||||
|
var bottomMetrics = getMetrics(bottom, font);
|
||||||
|
var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth;
|
||||||
|
var middleMetrics, middleHeightTotal;
|
||||||
|
if (middle !== null) {
|
||||||
|
middleMetrics = getMetrics(middle, font);
|
||||||
|
middleHeightTotal = middleMetrics.height + middleMetrics.depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
var realHeightTotal = topHeightTotal + bottomHeightTotal;
|
||||||
|
if (middle !== null) {
|
||||||
|
realHeightTotal += middleHeightTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (realHeightTotal < heightTotal) {
|
||||||
|
realHeightTotal += repeatHeightTotal;
|
||||||
|
if (middle !== null) {
|
||||||
|
realHeightTotal += repeatHeightTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var axisHeight = fontMetrics.metrics.axisHeight;
|
||||||
|
if (center) {
|
||||||
|
axisHeight *= options.style.sizeMultiplier;
|
||||||
|
}
|
||||||
|
var height = realHeightTotal / 2 + axisHeight;
|
||||||
|
var depth = realHeightTotal / 2 - axisHeight;
|
||||||
|
|
||||||
|
// Keep a list of the inner spans
|
||||||
|
var inners = [];
|
||||||
|
|
||||||
|
// Add the top symbol
|
||||||
|
inners.push(
|
||||||
|
makeInner(top, topMetrics.height - height, font, mode));
|
||||||
|
|
||||||
|
if (middle === null) {
|
||||||
|
var repeatHeight = realHeightTotal - topHeightTotal - bottomHeightTotal;
|
||||||
|
var symbolCount = Math.ceil(repeatHeight / repeatHeightTotal);
|
||||||
|
|
||||||
|
var overlapAmount;
|
||||||
|
if (overlap) {
|
||||||
|
// 2 * overlapAmount + repeatHeight =
|
||||||
|
// (symbolCount - 1) * (repeatHeightTotal - overlapAmount) +
|
||||||
|
// repeatHeightTotal
|
||||||
|
overlapAmount = (symbolCount * repeatHeightTotal -
|
||||||
|
repeatHeight) / (symbolCount + 1);
|
||||||
|
} else {
|
||||||
|
overlapAmount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add repeat symbols until there's only space for the bottom symbol
|
||||||
|
var currHeight = height - topHeightTotal + overlapAmount;
|
||||||
|
for (var i = 0; i < symbolCount; i++) {
|
||||||
|
inners.push(
|
||||||
|
makeInner(repeat,
|
||||||
|
repeatMetrics.height - currHeight, font, mode));
|
||||||
|
currHeight -= repeatHeightTotal - overlapAmount;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When there is a middle bit, we need the middle part and two repeated
|
||||||
|
// sections
|
||||||
|
|
||||||
|
// Calculate the number of symbols needed for the top and bottom
|
||||||
|
// repeated parts
|
||||||
|
var topRepeatHeight =
|
||||||
|
realHeightTotal / 2 - topHeightTotal - middleHeightTotal / 2;
|
||||||
|
var topSymbolCount = Math.ceil(topRepeatHeight / repeatHeightTotal);
|
||||||
|
|
||||||
|
var bottomRepeatHeight =
|
||||||
|
realHeightTotal / 2 - topHeightTotal - middleHeightTotal / 2;
|
||||||
|
var bottomSymbolCount =
|
||||||
|
Math.ceil(bottomRepeatHeight / repeatHeightTotal);
|
||||||
|
|
||||||
|
// Add the top repeated part
|
||||||
|
var currHeight = height - topHeightTotal;
|
||||||
|
for (var i = 0; i < topSymbolCount; i++) {
|
||||||
|
inners.push(
|
||||||
|
makeInner(repeat,
|
||||||
|
repeatMetrics.height - currHeight, font, mode));
|
||||||
|
currHeight -= repeatHeightTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the middle piece
|
||||||
|
var midPoint = realHeightTotal / 2 - depth;
|
||||||
|
inners.push(
|
||||||
|
makeInner(middle,
|
||||||
|
middleMetrics.height - midPoint - middleHeightTotal / 2,
|
||||||
|
font, mode));
|
||||||
|
|
||||||
|
// Add the bottom repeated part
|
||||||
|
currHeight = midPoint - middleHeightTotal / 2;
|
||||||
|
for (var i = 0; i < bottomSymbolCount; i++) {
|
||||||
|
inners.push(
|
||||||
|
makeInner(repeat,
|
||||||
|
repeatMetrics.height - currHeight, font, mode));
|
||||||
|
currHeight -= repeatHeightTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the bottom symbol
|
||||||
|
inners.push(
|
||||||
|
makeInner(bottom, depth - bottomMetrics.depth, font, mode));
|
||||||
|
|
||||||
|
var fixIE = makeSpan(["fix-ie"], [new domTree.textNode("\u00a0")]);
|
||||||
|
inners.push(fixIE);
|
||||||
|
|
||||||
|
return styleWrap(
|
||||||
|
makeSpan(["delimsizing", "mult"], inners, options.getColor()),
|
||||||
|
Style.TEXT, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
var normalDelimiters = [
|
||||||
|
"(", ")", "[", "\\lbrack", "]", "\\rbrack",
|
||||||
|
"\\{", "\\lbrace", "\\}", "\\rbrace",
|
||||||
|
"\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
|
||||||
|
"<", ">", "\\langle", "\\rangle", "/", "\\backslash"
|
||||||
|
];
|
||||||
|
|
||||||
|
var stackDelimiters = [
|
||||||
|
"\\uparrow", "\\downarrow", "\\updownarrow",
|
||||||
|
"\\Uparrow", "\\Downarrow", "\\Updownarrow",
|
||||||
|
"|", "\\|", "\\vert", "\\Vert"
|
||||||
|
];
|
||||||
|
|
||||||
|
var onlyNormalDelimiters = [
|
||||||
|
"<", ">", "\\langle", "\\rangle", "/", "\\backslash"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Metrics of the different sizes. Found by looking at TeX's output of
|
||||||
|
// $\bigl| \Bigl| \biggl| \Biggl| \showlists$
|
||||||
|
var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
|
||||||
|
|
||||||
|
var makeSizedDelim = function(delim, size, options, mode) {
|
||||||
|
if (delim === "<") {
|
||||||
|
delim = "\\langle";
|
||||||
|
} else if (delim === ">") {
|
||||||
|
delim = "\\rangle";
|
||||||
|
}
|
||||||
|
|
||||||
|
var retDelim;
|
||||||
|
|
||||||
|
if (utils.contains(normalDelimiters, delim)) {
|
||||||
|
return makeLargeDelim(delim, size, false, options, mode);
|
||||||
|
} else if (utils.contains(stackDelimiters, delim)) {
|
||||||
|
return makeStackedDelim(
|
||||||
|
delim, sizeToMaxHeight[size], false, options, mode);
|
||||||
|
} else {
|
||||||
|
throw new ParseError("Illegal delimiter: '" + delim + "'");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var normalDelimiterSequence = [
|
||||||
|
{type: "small", style: Style.SCRIPTSCRIPT},
|
||||||
|
{type: "small", style: Style.SCRIPT},
|
||||||
|
{type: "small", style: Style.TEXT},
|
||||||
|
{type: "large", size: 1},
|
||||||
|
{type: "large", size: 2},
|
||||||
|
{type: "large", size: 3},
|
||||||
|
{type: "large", size: 4}
|
||||||
|
];
|
||||||
|
|
||||||
|
var stackAlwaysDelimiterSequence = [
|
||||||
|
{type: "small", style: Style.SCRIPTSCRIPT},
|
||||||
|
{type: "small", style: Style.SCRIPT},
|
||||||
|
{type: "small", style: Style.TEXT},
|
||||||
|
{type: "stack"}
|
||||||
|
];
|
||||||
|
|
||||||
|
var stackLargeDelimiterSequence = [
|
||||||
|
{type: "small", style: Style.SCRIPTSCRIPT},
|
||||||
|
{type: "small", style: Style.SCRIPT},
|
||||||
|
{type: "small", style: Style.TEXT},
|
||||||
|
{type: "large", size: 1},
|
||||||
|
{type: "large", size: 2},
|
||||||
|
{type: "large", size: 3},
|
||||||
|
{type: "large", size: 4},
|
||||||
|
{type: "stack"}
|
||||||
|
];
|
||||||
|
|
||||||
|
var delimTypeToFont = function(type) {
|
||||||
|
if (type.type === "small") {
|
||||||
|
return "Main-Regular";
|
||||||
|
} else if (type.type === "large") {
|
||||||
|
return "Size" + type.size + "-Regular";
|
||||||
|
} else if (type.type === "stack") {
|
||||||
|
return "Size4-Regular";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var traverseSequence = function(delim, height, sequence, options) {
|
||||||
|
// Here, we choose the index we should start at in the sequences. In smaller
|
||||||
|
// sizes (which correspond to larger numbers in style.size) we start earlier
|
||||||
|
// in the sequence. Thus, scriptscript starts at index 3-3=0, script starts
|
||||||
|
// at index 3-2=1, text starts at 3-1=2, and display starts at min(2,3-0)=2
|
||||||
|
var start = Math.min(2, 3 - options.style.size);
|
||||||
|
for (var i = start; i < sequence.length; i++) {
|
||||||
|
if (sequence[i].type === "stack") {
|
||||||
|
// This is always the last delimiter, so we just break the loop now.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var metrics = getMetrics(delim, delimTypeToFont(sequence[i]));
|
||||||
|
|
||||||
|
var heightDepth = metrics.height + metrics.depth;
|
||||||
|
|
||||||
|
if (sequence[i].type === "small") {
|
||||||
|
heightDepth *= sequence[i].style.sizeMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heightDepth > height) {
|
||||||
|
return sequence[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequence[sequence.length - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeCustomSizedDelim = function(delim, height, center, options, mode) {
|
||||||
|
if (delim === "<") {
|
||||||
|
delim = "\\langle";
|
||||||
|
} else if (delim === ">") {
|
||||||
|
delim = "\\rangle";
|
||||||
|
}
|
||||||
|
|
||||||
|
var sequence;
|
||||||
|
if (utils.contains(onlyNormalDelimiters, delim)) {
|
||||||
|
sequence = normalDelimiterSequence;
|
||||||
|
} else if (utils.contains(normalDelimiters, delim)) {
|
||||||
|
sequence = stackLargeDelimiterSequence;
|
||||||
|
} else {
|
||||||
|
sequence = stackAlwaysDelimiterSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
var delimType = traverseSequence(delim, height, sequence, options);
|
||||||
|
|
||||||
|
if (delimType.type === "small") {
|
||||||
|
return makeSmallDelim(delim, delimType.style, center, options, mode);
|
||||||
|
} else if (delimType.type === "large") {
|
||||||
|
return makeLargeDelim(delim, delimType.size, center, options, mode);
|
||||||
|
} else if (delimType.type === "stack") {
|
||||||
|
return makeStackedDelim(delim, height, center, options, mode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeLeftRightDelim = function(delim, height, depth, options, mode) {
|
||||||
|
var axisHeight =
|
||||||
|
fontMetrics.metrics.axisHeight * options.style.sizeMultiplier;
|
||||||
|
|
||||||
|
// Taken from TeX source, tex.web, function make_left_right
|
||||||
|
var delimiterFactor = 901;
|
||||||
|
var delimiterExtend = 5.0 / fontMetrics.metrics.ptPerEm;
|
||||||
|
|
||||||
|
var maxDistFromAxis = Math.max(
|
||||||
|
height - axisHeight, depth + axisHeight);
|
||||||
|
|
||||||
|
var totalHeight = Math.max(
|
||||||
|
// In real TeX, calculations are done using integral values which are
|
||||||
|
// 65536 per pt, or 655360 per em. So, the division here truncates in
|
||||||
|
// TeX but doesn't here, producing different results. If we wanted to
|
||||||
|
// exactly match TeX's calculation, we could do
|
||||||
|
// Math.floor(655360 * maxDistFromAxis / 500) *
|
||||||
|
// delimiterFactor / 655360
|
||||||
|
// (To see the difference, compare
|
||||||
|
// x^{x^{\left(\rule{0.1em}{0.68em}\right)}}
|
||||||
|
// in TeX and KaTeX)
|
||||||
|
maxDistFromAxis / 500 * delimiterFactor,
|
||||||
|
2 * maxDistFromAxis - delimiterExtend);
|
||||||
|
|
||||||
|
return makeCustomSizedDelim(delim, totalHeight, true, options, mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sizedDelim: makeSizedDelim,
|
||||||
|
customSizedDelim: makeCustomSizedDelim,
|
||||||
|
leftRightDelim: makeLeftRightDelim
|
||||||
|
};
|
|
@ -37,10 +37,11 @@ span.prototype.toDOM = function() {
|
||||||
return span;
|
return span;
|
||||||
};
|
};
|
||||||
|
|
||||||
function documentFragment(children, height, depth) {
|
function documentFragment(children, height, depth, maxFontSize) {
|
||||||
this.children = children || [];
|
this.children = children || [];
|
||||||
this.height = height || 0;
|
this.height = height || 0;
|
||||||
this.depth = depth || 0;
|
this.depth = depth || 0;
|
||||||
|
this.maxFontSize = maxFontSize || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
documentFragment.prototype.toDOM = function() {
|
documentFragment.prototype.toDOM = function() {
|
||||||
|
|
|
@ -53,9 +53,9 @@ def main():
|
||||||
|
|
||||||
tfm_char = font_name_to_tfm[font].get_char_metrics(tex_char_num)
|
tfm_char = font_name_to_tfm[font].get_char_metrics(tex_char_num)
|
||||||
|
|
||||||
height = round(tfm_char.height + yshift / 1000.0, 3)
|
height = round(tfm_char.height + yshift / 1000.0, 5)
|
||||||
depth = round(tfm_char.depth - yshift / 1000.0, 3)
|
depth = round(tfm_char.depth - yshift / 1000.0, 5)
|
||||||
italic = round(tfm_char.italic_correction, 3)
|
italic = round(tfm_char.italic_correction, 5)
|
||||||
|
|
||||||
families[family][char_num] = {
|
families[family][char_num] = {
|
||||||
'height': height,
|
'height': height,
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
<link href="main.css" rel="stylesheet" type="text/css">
|
<link href="main.css" rel="stylesheet" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<input type="text" value="\blue\dfrac{\frac{\phi^2}{3}-G_a^{x^3}}{2\times3+4}+\orange\dfrac{(x^2+y^2)^\frac{1}{2}}{\tan\psi^\tau+2/3}" id="input" />
|
<input type="text"
|
||||||
|
value="(\left( x \right) \left( x^2 \right) \left( \frac{a}{b} \right) \left( \frac{a^2}{b} \right) \left( \dfrac{a}{b} \right) \left( \dfrac{a^2}{b} \right)"
|
||||||
|
id="input" />
|
||||||
<div id="math"></div>
|
<div id="math"></div>
|
||||||
<input id="permalink" type="button" value="permalink">
|
<input id="permalink" type="button" value="permalink">
|
||||||
<script src="main.js" type="text/javascript"></script>
|
<script src="main.js" type="text/javascript"></script>
|
||||||
|
|
|
@ -39,6 +39,11 @@ big parens
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This value is also used in fontMetrics.js, if you change it make sure the
|
||||||
|
// values match.
|
||||||
|
@ptperem: 10.0;
|
||||||
|
@nulldelimiterspace: 1.2em / @ptperem;
|
||||||
|
|
||||||
@thinspace: 0.16667em;
|
@thinspace: 0.16667em;
|
||||||
@mediumspace: 0.22222em;
|
@mediumspace: 0.22222em;
|
||||||
@thickspace: 0.27778em;
|
@thickspace: 0.27778em;
|
||||||
|
@ -162,6 +167,10 @@ big parens
|
||||||
.reset-scriptscriptstyle.scriptstyle { font-size: 1.4em; }
|
.reset-scriptscriptstyle.scriptstyle { font-size: 1.4em; }
|
||||||
.reset-scriptscriptstyle.scriptscriptstyle { font-size: 1em; }
|
.reset-scriptscriptstyle.scriptscriptstyle { font-size: 1em; }
|
||||||
|
|
||||||
|
.style-wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.baseline-align-hack-outer() {
|
.baseline-align-hack-outer() {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -392,12 +401,18 @@ big parens
|
||||||
.baseline-align-hack-middle;
|
.baseline-align-hack-middle;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.size1 {
|
&.size1 > span {
|
||||||
> span {
|
font-family: Katex_Size1;
|
||||||
font-family: KaTeX_Size1;
|
}
|
||||||
}
|
&.size4 > span {
|
||||||
|
font-family: Katex_Size4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nulldelimiter {
|
||||||
|
display: inline-block;
|
||||||
|
width: @nulldelimiterspace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -131,9 +131,27 @@
|
||||||
"url": "http://localhost:7936/test/huxley/test.html?m=\\rule{1em}{0.5em}\\rule{1ex}{2ex}\\rule{1em}{1ex}\\rule{1em}{0.431ex}"
|
"url": "http://localhost:7936/test/huxley/test.html?m=\\rule{1em}{0.5em}\\rule{1ex}{2ex}\\rule{1em}{1ex}\\rule{1em}{0.431ex}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "LeftRight",
|
||||||
|
"screenSize": [1024, 768],
|
||||||
|
"url": "http://localhost:7936/test/huxley/test.html?m=\\left( x^2 \\right) \\left\\{ x^{x^{x^{x^x}}} \\right."
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "LeftRightStyleSizing",
|
||||||
|
"screenSize": [1024, 768],
|
||||||
|
"url": "http://localhost:7936/test/huxley/test.html?m=+\\left\\{\\rule{0.1em}{1em}\\right.x^{+\\left\\{\\rule{0.1em}{1em}\\right.x^{+\\left\\{\\rule{0.1em}{1em}\\right.}}"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "PrimeSpacing",
|
"name": "PrimeSpacing",
|
||||||
"screenSize": [1024, 768],
|
"screenSize": [1024, 768],
|
||||||
"url": "http://localhost:7936/test/huxley/test.html?m=f'+f_2'+f^{f'}"
|
"url": "http://localhost:7936/test/huxley/test.html?m=f'+f_2'+f^{f'}"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "NullDelimiterInteraction",
|
||||||
|
"screenSize": [1024, 768],
|
||||||
|
"url": "http://localhost:7936/test/huxley/test.html?m=a \\bigl. + 2 \\quad \\left. + a \\right)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
BIN
test/huxley/LeftRight.hux/firefox-1.png
Normal file
After Width: | Height: | Size: 17 KiB |
5
test/huxley/LeftRight.hux/record.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"action": "screenshot"
|
||||||
|
}
|
||||||
|
]
|
BIN
test/huxley/LeftRightStyleSizing.hux/firefox-1.png
Normal file
After Width: | Height: | Size: 15 KiB |
5
test/huxley/LeftRightStyleSizing.hux/record.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"action": "screenshot"
|
||||||
|
}
|
||||||
|
]
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
BIN
test/huxley/NullDelimiterInteraction.hux/firefox-1.png
Normal file
After Width: | Height: | Size: 12 KiB |
5
test/huxley/NullDelimiterInteraction.hux/record.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"action": "screenshot"
|
||||||
|
}
|
||||||
|
]
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
@ -739,3 +739,62 @@ describe("A rule parser", function() {
|
||||||
expect(hardNumberParse.value.height.number).toBeCloseTo(2.45);
|
expect(hardNumberParse.value.height.number).toBeCloseTo(2.45);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("A left/right parser", function() {
|
||||||
|
var normalLeftRight = "\\left( \\dfrac{x}{y} \\right)";
|
||||||
|
var emptyRight = "\\left( \\dfrac{x}{y} \\right.";
|
||||||
|
|
||||||
|
it("should not fail", function() {
|
||||||
|
expect(function() {
|
||||||
|
parseTree(normalLeftRight);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should produce a leftright", function() {
|
||||||
|
var parse = parseTree(normalLeftRight)[0];
|
||||||
|
|
||||||
|
expect(parse.type).toMatch("leftright");
|
||||||
|
expect(parse.value.left).toMatch("\\(");
|
||||||
|
expect(parse.value.right).toMatch("\\)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should error when it is mismatched", function() {
|
||||||
|
var unmatchedLeft = "\\left( \\dfrac{x}{y}";
|
||||||
|
var unmatchedRight = "\\dfrac{x}{y} \\right)";
|
||||||
|
|
||||||
|
expect(function() {
|
||||||
|
parseTree(unmatchedLeft);
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
|
expect(function() {
|
||||||
|
parseTree(unmatchedRight);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should error when braces are mismatched", function() {
|
||||||
|
var unmatched = "{ \\left( \\dfrac{x}{y} } \\right)";
|
||||||
|
expect(function() {
|
||||||
|
parseTree(unmatched);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should error when non-delimiters are provided", function() {
|
||||||
|
var nonDelimiter = "\\left$ \\dfrac{x}{y} \\right)";
|
||||||
|
expect(function() {
|
||||||
|
parseTree(nonDelimiter);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse the empty '.' delimiter", function() {
|
||||||
|
expect(function() {
|
||||||
|
parseTree(emptyRight);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse the '.' delimiter with normal sizes", function() {
|
||||||
|
var normalEmpty = "\\Bigl .";
|
||||||
|
expect(function() {
|
||||||
|
parseTree(normalEmpty);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|