Handle \middle.
This commit is contained in:
parent
7433638fda
commit
e449b2d61a
|
@ -52,6 +52,8 @@ function Parser(input, settings) {
|
|||
this.gullet = new MacroExpander(input, settings.macros);
|
||||
// Store the settings for use in parsing
|
||||
this.settings = settings;
|
||||
// Count leftright depth (for \middle errors)
|
||||
this.leftrightDepth = 0;
|
||||
}
|
||||
|
||||
var ParseNode = parseData.ParseNode;
|
||||
|
@ -411,7 +413,9 @@ Parser.prototype.parseImplicitGroup = function() {
|
|||
// Parse the entire left function (including the delimiter)
|
||||
var left = this.parseFunction(start);
|
||||
// Parse out the implicit body
|
||||
++this.leftrightDepth;
|
||||
body = this.parseExpression(false);
|
||||
--this.leftrightDepth;
|
||||
// Check the next token
|
||||
this.expect("\\right", false);
|
||||
var right = this.parseFunction();
|
||||
|
|
|
@ -1186,11 +1186,16 @@ groupTypes.leftright = function(group, options) {
|
|||
|
||||
var innerHeight = 0;
|
||||
var innerDepth = 0;
|
||||
var hadMiddle = false;
|
||||
|
||||
// Calculate its height and depth
|
||||
for (var i = 0; i < inner.length; i++) {
|
||||
innerHeight = Math.max(inner[i].height, innerHeight);
|
||||
innerDepth = Math.max(inner[i].depth, innerDepth);
|
||||
if (inner[i].isMiddle) {
|
||||
hadMiddle = true;
|
||||
} else {
|
||||
innerHeight = Math.max(inner[i].height, innerHeight);
|
||||
innerDepth = Math.max(inner[i].depth, innerDepth);
|
||||
}
|
||||
}
|
||||
|
||||
var style = options.style;
|
||||
|
@ -1215,6 +1220,18 @@ groupTypes.leftright = function(group, options) {
|
|||
// Add it to the beginning of the expression
|
||||
inner.unshift(leftDelim);
|
||||
|
||||
// Handle middle delimiters
|
||||
if (hadMiddle) {
|
||||
for (i = 1; i < inner.length; i++) {
|
||||
if (inner[i].isMiddle) {
|
||||
// Apply the options that were active when \middle was called
|
||||
inner[i] = delimiter.leftRightDelim(
|
||||
inner[i].isMiddle.value, innerHeight, innerDepth,
|
||||
inner[i].isMiddle.options, group.mode, []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rightDelim;
|
||||
// Same for the right delimiter
|
||||
if (group.value.right === ".") {
|
||||
|
@ -1231,6 +1248,19 @@ groupTypes.leftright = function(group, options) {
|
|||
["minner", style.cls()], inner, options);
|
||||
};
|
||||
|
||||
groupTypes.middle = function(group, options) {
|
||||
var middleDelim;
|
||||
if (group.value.value === ".") {
|
||||
middleDelim = makeNullDelimiter(options, []);
|
||||
} else {
|
||||
middleDelim = delimiter.sizedDelim(
|
||||
group.value.value, 1, options,
|
||||
group.mode, []);
|
||||
middleDelim.isMiddle = {value: group.value.value, options: options};
|
||||
}
|
||||
return middleDelim;
|
||||
};
|
||||
|
||||
groupTypes.rule = function(group, options) {
|
||||
// Make an empty span for the rule
|
||||
var rule = makeSpan(["mord", "rule"], [], options);
|
||||
|
|
|
@ -285,6 +285,13 @@ groupTypes.leftright = function(group, options) {
|
|||
return outerNode;
|
||||
};
|
||||
|
||||
groupTypes.middle = function(group, options) {
|
||||
var middleNode = new mathMLTree.MathNode(
|
||||
"mo", [makeText(group.value.middle, group.mode)]);
|
||||
middleNode.setAttribute("fence", "true");
|
||||
return middleNode;
|
||||
};
|
||||
|
||||
groupTypes.accent = function(group, options) {
|
||||
var accentNode = new mathMLTree.MathNode(
|
||||
"mo", [makeText(group.value.accent, group.mode)]);
|
||||
|
|
|
@ -496,37 +496,61 @@ defineFunction(["\\llap", "\\rlap"], {
|
|||
});
|
||||
|
||||
// Delimiter functions
|
||||
var checkDelimiter = function(delim, context) {
|
||||
if (utils.contains(delimiters, delim.value)) {
|
||||
return delim;
|
||||
} else {
|
||||
throw new ParseError(
|
||||
"Invalid delimiter: '" + delim.value + "' after '" +
|
||||
context.funcName + "'", delim);
|
||||
}
|
||||
};
|
||||
|
||||
defineFunction([
|
||||
"\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
|
||||
"\\bigr", "\\Bigr", "\\biggr", "\\Biggr",
|
||||
"\\bigm", "\\Bigm", "\\biggm", "\\Biggm",
|
||||
"\\big", "\\Big", "\\bigg", "\\Bigg",
|
||||
], {
|
||||
numArgs: 1,
|
||||
}, function(context, args) {
|
||||
var delim = checkDelimiter(args[0], context);
|
||||
|
||||
return {
|
||||
type: "delimsizing",
|
||||
size: delimiterSizes[context.funcName].size,
|
||||
mclass: delimiterSizes[context.funcName].mclass,
|
||||
value: delim.value,
|
||||
};
|
||||
});
|
||||
|
||||
defineFunction([
|
||||
"\\left", "\\right",
|
||||
], {
|
||||
numArgs: 1,
|
||||
}, function(context, args) {
|
||||
var delim = args[0];
|
||||
if (!utils.contains(delimiters, delim.value)) {
|
||||
throw new ParseError(
|
||||
"Invalid delimiter: '" + delim.value + "' after '" +
|
||||
context.funcName + "'", delim);
|
||||
}
|
||||
var delim = checkDelimiter(args[0], context);
|
||||
|
||||
// \left and \right are caught somewhere in Parser.js, which is
|
||||
// why this data doesn't match what is in buildHTML.
|
||||
if (context.funcName === "\\left" || context.funcName === "\\right") {
|
||||
return {
|
||||
type: "leftright",
|
||||
value: delim.value,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: "delimsizing",
|
||||
size: delimiterSizes[context.funcName].size,
|
||||
mclass: delimiterSizes[context.funcName].mclass,
|
||||
value: delim.value,
|
||||
};
|
||||
return {
|
||||
type: "leftright",
|
||||
value: delim.value,
|
||||
};
|
||||
});
|
||||
|
||||
defineFunction("\\middle", {
|
||||
numArgs: 1,
|
||||
}, function(context, args) {
|
||||
var delim = checkDelimiter(args[0], context);
|
||||
if (!context.parser.leftrightDepth) {
|
||||
throw new ParseError("\\middle without preceding \\left", delim);
|
||||
}
|
||||
|
||||
return {
|
||||
type: "middle",
|
||||
value: delim.value,
|
||||
};
|
||||
});
|
||||
|
||||
// Sizing functions (handled in Parser.js explicitly, hence no handler)
|
||||
|
|
|
@ -1042,6 +1042,26 @@ describe("A left/right parser", function() {
|
|||
var normalEmpty = "\\Bigl .";
|
||||
expect(normalEmpty).toParse();
|
||||
});
|
||||
|
||||
it("should handle \\middle", function() {
|
||||
var normalMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\right)";
|
||||
expect(normalMiddle).toParse();
|
||||
});
|
||||
|
||||
it("should handle multiple \\middles", function() {
|
||||
var multiMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\middle/ \\dfrac{z}{q} \\right)";
|
||||
expect(multiMiddle).toParse();
|
||||
});
|
||||
|
||||
it("should handle nested \\middles", function() {
|
||||
var nestedMiddle = "\\left( a^2 \\middle| \\left( b \\middle/ c \\right) \\right)";
|
||||
expect(nestedMiddle).toParse();
|
||||
});
|
||||
|
||||
it("should error when \\middle is not in \\left...\\right", function() {
|
||||
var unmatchedMiddle = "(\\middle|\\dfrac{x}{y})";
|
||||
expect(unmatchedMiddle).toNotParse();
|
||||
});
|
||||
});
|
||||
|
||||
describe("A begin/end parser", function() {
|
||||
|
|
BIN
test/screenshotter/images/LeftRightMiddle-chrome.png
Normal file
BIN
test/screenshotter/images/LeftRightMiddle-chrome.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
test/screenshotter/images/LeftRightMiddle-firefox.png
Normal file
BIN
test/screenshotter/images/LeftRightMiddle-firefox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -72,6 +72,7 @@ Kern:
|
|||
Lap: ab\llap{f}cd\rlap{g}h
|
||||
LeftRight: \left( x^2 \right) \left\{ x^{x^{x^{x^x}}} \right.
|
||||
LeftRightListStyling: a+\left(x+y\right)-x
|
||||
LeftRightMiddle: \left( x^2 \middle/ \right) \left\{ x^{x^{x^{x^x}}} \middle/ y \right.
|
||||
LeftRightStyleSizing: |
|
||||
+\left\{\rule{0.1em}{1em}\right.
|
||||
x^{+\left\{\rule{0.1em}{1em}\right.
|
||||
|
|
Loading…
Reference in New Issue
Block a user