diff options
Diffstat (limited to 'webapp/non_npm_dependencies/katex/src/environments.js')
-rw-r--r-- | webapp/non_npm_dependencies/katex/src/environments.js | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/webapp/non_npm_dependencies/katex/src/environments.js b/webapp/non_npm_dependencies/katex/src/environments.js new file mode 100644 index 000000000..f0fff42a5 --- /dev/null +++ b/webapp/non_npm_dependencies/katex/src/environments.js @@ -0,0 +1,221 @@ +/* eslint no-constant-condition:0 */ +var fontMetrics = require("./fontMetrics"); +var parseData = require("./parseData"); +var ParseError = require("./ParseError"); + +var ParseNode = parseData.ParseNode; + +/** + * Parse the body of the environment, with rows delimited by \\ and + * columns delimited by &, and create a nested list in row-major order + * with one group per cell. + */ +function parseArray(parser, result) { + var row = []; + var body = [row]; + var rowGaps = []; + while (true) { + var cell = parser.parseExpression(false, null); + row.push(new ParseNode("ordgroup", cell, parser.mode)); + var next = parser.nextToken.text; + if (next === "&") { + parser.consume(); + } else if (next === "\\end") { + break; + } else if (next === "\\\\" || next === "\\cr") { + var cr = parser.parseFunction(); + rowGaps.push(cr.value.size); + row = []; + body.push(row); + } else { + // TODO: Clean up the following hack once #385 got merged + var pos = Math.min(parser.pos + 1, parser.lexer._input.length); + throw new ParseError("Expected & or \\\\ or \\end", + parser.lexer, pos); + } + } + result.body = body; + result.rowGaps = rowGaps; + return new ParseNode(result.type, result, parser.mode); +} + +/* + * An environment definition is very similar to a function definition: + * it is declared with a name or a list of names, a set of properties + * and a handler containing the actual implementation. + * + * The properties include: + * - numArgs: The number of arguments after the \begin{name} function. + * - argTypes: (optional) Just like for a function + * - allowedInText: (optional) Whether or not the environment is allowed inside + * text mode (default false) (not enforced yet) + * - numOptionalArgs: (optional) Just like for a function + * A bare number instead of that object indicates the numArgs value. + * + * The handler function will receive two arguments + * - context: information and references provided by the parser + * - args: an array of arguments passed to \begin{name} + * The context contains the following properties: + * - envName: the name of the environment, one of the listed names. + * - parser: the parser object + * - lexer: the lexer object + * - positions: the positions associated with these arguments from args. + * The handler must return a ParseResult. + */ + +function defineEnvironment(names, props, handler) { + if (typeof names === "string") { + names = [names]; + } + if (typeof props === "number") { + props = { numArgs: props }; + } + // Set default values of environments + var data = { + numArgs: props.numArgs || 0, + argTypes: props.argTypes, + greediness: 1, + allowedInText: !!props.allowedInText, + numOptionalArgs: props.numOptionalArgs || 0, + handler: handler, + }; + for (var i = 0; i < names.length; ++i) { + module.exports[names[i]] = data; + } +} + +// Arrays are part of LaTeX, defined in lttab.dtx so its documentation +// is part of the source2e.pdf file of LaTeX2e source documentation. +defineEnvironment("array", { + numArgs: 1, +}, function(context, args) { + var colalign = args[0]; + colalign = colalign.value.map ? colalign.value : [colalign]; + var cols = colalign.map(function(node) { + var ca = node.value; + if ("lcr".indexOf(ca) !== -1) { + return { + type: "align", + align: ca, + }; + } else if (ca === "|") { + return { + type: "separator", + separator: "|", + }; + } + throw new ParseError( + "Unknown column alignment: " + node.value, + context.lexer, context.positions[1]); + }); + var res = { + type: "array", + cols: cols, + hskipBeforeAndAfter: true, // \@preamble in lttab.dtx + }; + res = parseArray(context.parser, res); + return res; +}); + +// The matrix environments of amsmath builds on the array environment +// of LaTeX, which is discussed above. +defineEnvironment([ + "matrix", + "pmatrix", + "bmatrix", + "Bmatrix", + "vmatrix", + "Vmatrix", +], { +}, function(context) { + var delimiters = { + "matrix": null, + "pmatrix": ["(", ")"], + "bmatrix": ["[", "]"], + "Bmatrix": ["\\{", "\\}"], + "vmatrix": ["|", "|"], + "Vmatrix": ["\\Vert", "\\Vert"], + }[context.envName]; + var res = { + type: "array", + hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath + }; + res = parseArray(context.parser, res); + if (delimiters) { + res = new ParseNode("leftright", { + body: [res], + left: delimiters[0], + right: delimiters[1], + }, context.mode); + } + return res; +}); + +// A cases environment (in amsmath.sty) is almost equivalent to +// \def\arraystretch{1.2}% +// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. +defineEnvironment("cases", { +}, function(context) { + var res = { + type: "array", + arraystretch: 1.2, + cols: [{ + type: "align", + align: "l", + pregap: 0, + postgap: fontMetrics.metrics.quad, + }, { + type: "align", + align: "l", + pregap: 0, + postgap: 0, + }], + }; + res = parseArray(context.parser, res); + res = new ParseNode("leftright", { + body: [res], + left: "\\{", + right: ".", + }, context.mode); + return res; +}); + +// An aligned environment is like the align* environment +// except it operates within math mode. +// Note that we assume \nomallineskiplimit to be zero, +// so that \strut@ is the same as \strut. +defineEnvironment("aligned", { +}, function(context) { + var res = { + type: "array", + cols: [], + }; + res = parseArray(context.parser, res); + var emptyGroup = new ParseNode("ordgroup", [], context.mode); + var numCols = 0; + res.value.body.forEach(function(row) { + var i; + for (i = 1; i < row.length; i += 2) { + row[i].value.unshift(emptyGroup); + } + if (numCols < row.length) { + numCols = row.length; + } + }); + for (var i = 0; i < numCols; ++i) { + var align = "r"; + var pregap = 0; + if (i % 2 === 1) { + align = "l"; + } else if (i > 0) { + pregap = 2; // one \qquad between columns + } + res.value.cols[i] = { + type: "align", + align: align, + pregap: pregap, + postgap: 0, + }; + } + return res; +}); |