From fd18f6979ed315a7165a438962a41dfd544d518e Mon Sep 17 00:00:00 2001 From: Emily Eisenberg Date: Thu, 19 Feb 2015 15:26:57 -0800 Subject: [PATCH] Add an optional settings argument to render calls Summary: Add the ability to pass in options to the render calls which contain information about the parse. This information is passed around to the parser and builder, which parse and render differently depending on the options. Currently, this includes an option to render the math in display mode (i.e. centered, block level, and in displaystyle). Also added some changes to make it easier to add new data to functions (now that new data doesn't need to be copied into the ParseFuncOrArg data structure, it is looked up when it is needed) and has more sane support for the `'original'` argType (as suggested by pull request #93). Test Plan: - Make sure tests and lint pass - Make sure huxley screenshots didn't change, and new screenshot looks correct Reviewers: alpert Reviewed By: alpert Differential Revision: https://phabricator.khanacademy.org/D13810 --- README.md | 18 ++- katex.js | 17 +- src/Parser.js | 57 +++---- src/Settings.js | 26 +++ src/buildTree.js | 15 +- src/functions.js | 13 +- src/parseTree.js | 4 +- static/katex.less | 10 ++ test/huxley/Huxleyfile.json | 6 + .../DisplayMode.hux/firefox-1.png | Bin 0 -> 22294 bytes .../Huxleyfolder/DisplayMode.record.json | 5 + .../Huxleyfolder/Spacing.hux/firefox-1.png | Bin 8787 -> 16091 bytes test/huxley/test.html | 12 +- test/katex-spec.js | 151 +++++++++--------- 14 files changed, 190 insertions(+), 144 deletions(-) create mode 100644 src/Settings.js create mode 100644 test/huxley/Huxleyfolder/DisplayMode.hux/firefox-1.png create mode 100644 test/huxley/Huxleyfolder/DisplayMode.record.json diff --git a/README.md b/README.md index fdc633bd3..9a1c4df0d 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,17 @@ You can [download KaTeX](https://github.com/khan/katex/releases) and host it on ``` +#### In-browser rendering + Call `katex.render` with a TeX expression and a DOM element to render into: ```js katex.render("c = \\pm\\sqrt{a^2 + b^2}", element); ``` -To generate HTML on the server, you can use `katex.renderToString`: +#### Server side rendering or rendering to a string + +To generate HTML on the server or to generate an HTML string of the rendered math, you can use `katex.renderToString`: ```js var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}"); @@ -33,12 +37,16 @@ var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}"); Make sure to include the CSS and font files, but there is no need to include the JavaScript. -These APIs default to inline math typesetting; for display math you can prepend `\displaystyle` ([#66](https://github.com/Khan/KaTeX/issues/66)): +#### Rendering options + +You can provide an object of options as the last argument to `katex.render` and `katex.renderToString`. Available options are: + +- `displayMode`: `boolean`. If `true` the math will be rendered in display mode, which will put the math in display style (so `\int` and `\sum` are large, for example), and will center the math on the page on its own line. If `false` the math will be rendered in inline mode. (default: `false`) + +For example: ```js -katex.render("\\displaystyle {" + formula + "}", element); -// OR -var html = katex.renderToString("\\displaystyle {" + formula + "}"); +katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, { displayMode: true }); ``` ## Contributing diff --git a/katex.js b/katex.js index b6b96f3cb..368a9b924 100644 --- a/katex.js +++ b/katex.js @@ -7,6 +7,7 @@ */ var ParseError = require("./src/ParseError"); +var Settings = require("./src/Settings"); var buildTree = require("./src/buildTree"); var parseTree = require("./src/parseTree"); @@ -16,11 +17,13 @@ var utils = require("./src/utils"); * Parse and build an expression, and place that expression in the DOM node * given. */ -var render = function(toParse, baseNode) { +var render = function(toParse, baseNode, options) { utils.clearNode(baseNode); - var tree = parseTree(toParse); - var node = buildTree(tree).toNode(); + var settings = new Settings(options); + + var tree = parseTree(toParse, settings); + var node = buildTree(tree, settings).toNode(); baseNode.appendChild(node); }; @@ -42,9 +45,11 @@ if (typeof document !== "undefined") { /** * Parse and build an expression, and return the markup for that. */ -var renderToString = function(toParse) { - var tree = parseTree(toParse); - return buildTree(tree).toMarkup(); +var renderToString = function(toParse, options) { + var settings = new Settings(options); + + var tree = parseTree(toParse, settings); + return buildTree(tree, settings).toMarkup(); }; module.exports = { diff --git a/src/Parser.js b/src/Parser.js index f23e7fd96..5a1cbb5ea 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -43,9 +43,11 @@ var ParseError = require("./ParseError"); /** * Main Parser class */ -function Parser(input) { +function Parser(input, settings) { // Make a new lexer this.lexer = new Lexer(input); + // Store the settings for use in parsing + this.settings = settings; } /** @@ -69,18 +71,10 @@ function ParseResult(result, newPosition) { * An initial function (without its arguments), or an argument to a function. * The `result` argument should be a ParseResult. */ -function ParseFuncOrArgument(result, isFunction, allowedInText, numArgs, numOptionalArgs, argTypes) { +function ParseFuncOrArgument(result, isFunction) { this.result = result; // Is this a function (i.e. is it something defined in functions.js)? this.isFunction = isFunction; - // Is it allowed in text mode? - this.allowedInText = allowedInText; - // How many arguments? - this.numArgs = numArgs; - // How many optional arguments? - this.numOptionalArgs = numOptionalArgs; - // What types of arguments? - this.argTypes = argTypes; } /** @@ -217,10 +211,10 @@ Parser.prototype.handleSupSubscript = function(pos, mode, symbol, name) { if (!group) { throw new ParseError( "Expected group after '" + symbol + "'", this.lexer, pos); - } else if (group.numArgs > 0) { + } else if (group.isFunction) { // ^ and _ have a greediness, so handle interactions with functions' // greediness - var funcGreediness = functions.getGreediness(group.result.result); + var funcGreediness = functions.funcs[group.result.result].greediness; if (funcGreediness > SUPSUB_GREEDINESS) { return this.parseFunction(pos, mode); } else { @@ -419,7 +413,8 @@ Parser.prototype.parseFunction = function(pos, mode) { if (baseGroup) { if (baseGroup.isFunction) { var func = baseGroup.result.result; - if (mode === "text" && !baseGroup.allowedInText) { + var funcData = functions.funcs[func]; + if (mode === "text" && !funcData.allowedInText) { throw new ParseError( "Can't use function '" + func + "' in text mode", this.lexer, baseGroup.position); @@ -428,17 +423,17 @@ Parser.prototype.parseFunction = function(pos, mode) { var newPos = baseGroup.result.position; var result; - var totalArgs = baseGroup.numArgs + baseGroup.numOptionalArgs; + var totalArgs = funcData.numArgs + funcData.numOptionalArgs; if (totalArgs > 0) { - var baseGreediness = functions.getGreediness(func); + var baseGreediness = funcData.greediness; var args = [func]; var positions = [newPos]; for (var i = 0; i < totalArgs; i++) { - var argType = baseGroup.argTypes && baseGroup.argTypes[i]; + var argType = funcData.argTypes && funcData.argTypes[i]; var arg; - if (i < baseGroup.numOptionalArgs) { + if (i < funcData.numOptionalArgs) { if (argType) { arg = this.parseSpecialGroup(newPos, argType, mode, true); } else { @@ -463,8 +458,9 @@ Parser.prototype.parseFunction = function(pos, mode) { } } var argNode; - if (arg.numArgs > 0) { - var argGreediness = functions.getGreediness(arg.result.result); + if (arg.isFunction) { + var argGreediness = + functions.funcs[arg.result.result].greediness; if (argGreediness > baseGreediness) { argNode = this.parseFunction(newPos, mode); } else { @@ -507,6 +503,11 @@ Parser.prototype.parseFunction = function(pos, mode) { * @return {?ParseFuncOrArgument} */ Parser.prototype.parseSpecialGroup = function(pos, mode, outerMode, optional) { + // Handle `original` argTypes + if (mode === "original") { + mode = outerMode; + } + if (mode === "color" || mode === "size") { // color and size modes are special because they should have braces and // should only lex a single symbol inside @@ -605,23 +606,11 @@ Parser.prototype.parseSymbol = function(pos, mode) { var nucleus = this.lexer.lex(pos, mode); if (functions.funcs[nucleus.text]) { - // If there is a function with this name, we use its data - var func = functions.funcs[nucleus.text]; - - // Here, we replace "original" argTypes with the current mode - var argTypes = func.argTypes; - if (argTypes) { - argTypes = argTypes.slice(); - for (var i = 0; i < argTypes.length; i++) { - if (argTypes[i] === "original") { - argTypes[i] = mode; - } - } - } - + // If there exists a function with this name, we return the function and + // say that it is a function. return new ParseFuncOrArgument( new ParseResult(nucleus.text, nucleus.position), - true, func.allowedInText, func.numArgs, func.numOptionalArgs, argTypes); + true); } else if (symbols[mode][nucleus.text]) { // Otherwise if this is a no-argument function, find the type it // corresponds to in the symbols map diff --git a/src/Settings.js b/src/Settings.js new file mode 100644 index 000000000..49395d9e9 --- /dev/null +++ b/src/Settings.js @@ -0,0 +1,26 @@ +/** + * This is a module for storing settings passed into KaTeX. It correctly handles + * default settings. + */ + +/** + * Helper function for getting a default value if the value is undefined + */ +function get(option, defaultValue) { + return option === undefined ? defaultValue : option; +} + +/** + * The main Settings object + * + * The current options stored are: + * - displayMode: Whether the expression should be typeset by default in + * textstyle or displaystyle (default false) + */ +function Settings(options) { + // allow null options + options = options || {}; + this.displayMode = get(options.displayMode, false); +} + +module.exports = Settings; diff --git a/src/buildTree.js b/src/buildTree.js index 1939331b3..464e6c2f9 100644 --- a/src/buildTree.js +++ b/src/buildTree.js @@ -1135,9 +1135,14 @@ var buildGroup = function(group, options, prev) { /** * Take an entire parse tree, and build it into an appropriate set of nodes. */ -var buildTree = function(tree) { +var buildTree = function(tree, settings) { + var startStyle = Style.TEXT; + if (settings.displayMode) { + startStyle = Style.DISPLAY; + } + // Setup the default options - var options = new Options(Style.TEXT, "size5", ""); + var options = new Options(startStyle, "size5", ""); // Build the expression contained in the tree var expression = buildExpression(tree, options); @@ -1161,7 +1166,11 @@ var buildTree = function(tree) { makeSpan(["katex-inner"], [topStrut, bottomStrut, body]) ]); - return katexNode; + if (settings.displayMode) { + return makeSpan(["katex-display"], [katexNode]); + } else { + return katexNode; + } }; module.exports = buildTree; diff --git a/src/functions.js b/src/functions.js index ec702a8dc..40600a40e 100644 --- a/src/functions.js +++ b/src/functions.js @@ -506,16 +506,6 @@ for (var i = 0; i < duplicatedFunctions.length; i++) { addFuncsWithData(duplicatedFunctions[i].funcs, duplicatedFunctions[i].data); } -// Returns the greediness of a given function. Since greediness is optional, we -// use this function to put in the default value if it is undefined. -var getGreediness = function(func) { - if (functions[func].greediness === undefined) { - return 1; - } else { - return functions[func].greediness; - } -}; - // Set default values of functions for (var f in functions) { if (functions.hasOwnProperty(f)) { @@ -534,6 +524,5 @@ for (var f in functions) { } module.exports = { - funcs: functions, - getGreediness: getGreediness + funcs: functions }; diff --git a/src/parseTree.js b/src/parseTree.js index a778e2c4d..3adba8248 100644 --- a/src/parseTree.js +++ b/src/parseTree.js @@ -8,8 +8,8 @@ var Parser = require("./Parser"); /** * Parses an expression using a Parser, then returns the parsed result. */ -var parseTree = function(toParse) { - var parser = new Parser(toParse); +var parseTree = function(toParse, settings) { + var parser = new Parser(toParse, settings); return parser.parse(); }; diff --git a/static/katex.less b/static/katex.less index fe18a0aff..e394bf730 100644 --- a/static/katex.less +++ b/static/katex.less @@ -1,5 +1,15 @@ @import "fonts.less"; +.katex-display { + display: block; + margin: 1em 0; + text-align: center; + + > .katex { + display: inline-block; + } +} + .katex { font: normal 1.21em KaTeX_Main; line-height: 1.2; diff --git a/test/huxley/Huxleyfile.json b/test/huxley/Huxleyfile.json index 673b58927..06aff0f10 100644 --- a/test/huxley/Huxleyfile.json +++ b/test/huxley/Huxleyfile.json @@ -195,5 +195,11 @@ "name": "Accents", "screenSize": [1024, 768], "url": "http://localhost:7936/test/huxley/test.html?m=\\vec{A}\\vec{x}\\vec x^2\\vec{x}_2^2\\vec{A}^2\\vec{xA}^2" + }, + + { + "name": "DisplayMode", + "screenSize": [1024, 768], + "url": "http://localhost:7936/test/huxley/test.html?m=\\sum_{i=0}^\\infty \\frac{1}{i}&pre=pre&post=post&display=1" } ] diff --git a/test/huxley/Huxleyfolder/DisplayMode.hux/firefox-1.png b/test/huxley/Huxleyfolder/DisplayMode.hux/firefox-1.png new file mode 100644 index 0000000000000000000000000000000000000000..e560a1f0efac97ab0e509e800fe770596fb6b5ed GIT binary patch literal 22294 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 C4Xcp= diff --git a/test/huxley/test.html b/test/huxley/test.html index 6ff6a2281..2b8b4c9fb 100644 --- a/test/huxley/test.html +++ b/test/huxley/test.html @@ -16,13 +16,15 @@ diff --git a/test/katex-spec.js b/test/katex-spec.js index 64d30294c..310cfbef6 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -1,17 +1,26 @@ -var katex = require("../katex"); var buildTree = require("../src/buildTree"); -var parseTree = require("../src/parseTree"); +var katex = require("../katex"); var ParseError = require("../src/ParseError"); +var parseTree = require("../src/parseTree"); +var Settings = require("../src/Settings"); + +var defaultSettings = new Settings({}); var getBuilt = function(expr) { expect(expr).toBuild(); - var built = buildTree(parseTree(expr)); + var built = buildTree(parseTree(expr), defaultSettings); // Remove the outer .katex and .katex-inner layers return built.children[0].children[2].children; }; +var getParsed = function(expr) { + expect(expr).toParse(); + + return parseTree(expr, defaultSettings); +}; + beforeEach(function() { jasmine.addMatchers({ toParse: function() { @@ -23,7 +32,7 @@ beforeEach(function() { }; try { - parseTree(actual); + parseTree(actual, defaultSettings); } catch (e) { result.pass = false; if (e instanceof ParseError) { @@ -50,7 +59,7 @@ beforeEach(function() { }; try { - parseTree(actual); + parseTree(actual, defaultSettings); } catch (e) { if (e instanceof ParseError) { result.pass = true; @@ -78,7 +87,7 @@ beforeEach(function() { expect(actual).toParse(); try { - buildTree(parseTree(actual)); + buildTree(parseTree(actual), defaultSettings); } catch (e) { result.pass = false; if (e instanceof ParseError) { @@ -103,8 +112,8 @@ describe("A parser", function() { }); it("should ignore whitespace", function() { - var parseA = parseTree(" x y "); - var parseB = parseTree("xy"); + var parseA = getParsed(" x y "); + var parseB = getParsed("xy"); expect(parseA).toEqual(parseB); }); }); @@ -117,7 +126,7 @@ describe("An ord parser", function() { }); it("should build a list of ords", function() { - var parse = parseTree(expression); + var parse = getParsed(expression); expect(parse).toBeTruthy(); @@ -128,7 +137,7 @@ describe("An ord parser", function() { }); it("should parse the right number of ords", function() { - var parse = parseTree(expression); + var parse = getParsed(expression); expect(parse.length).toBe(expression.length); }); @@ -142,7 +151,7 @@ describe("A bin parser", function() { }); it("should build a list of bins", function() { - var parse = parseTree(expression); + var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { @@ -160,7 +169,7 @@ describe("A rel parser", function() { }); it("should build a list of rels", function() { - var parse = parseTree(expression); + var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { @@ -178,7 +187,7 @@ describe("A punct parser", function() { }); it("should build a list of puncts", function() { - var parse = parseTree(expression); + var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { @@ -196,7 +205,7 @@ describe("An open parser", function() { }); it("should build a list of opens", function() { - var parse = parseTree(expression); + var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { @@ -214,7 +223,7 @@ describe("A close parser", function() { }); it("should build a list of closes", function() { - var parse = parseTree(expression); + var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { @@ -253,7 +262,7 @@ describe("A subscript and superscript parser", function() { }); it("should produce supsubs for superscript", function() { - var parse = parseTree("x^2")[0]; + var parse = getParsed("x^2")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); @@ -262,7 +271,7 @@ describe("A subscript and superscript parser", function() { }); it("should produce supsubs for subscript", function() { - var parse = parseTree("x_3")[0]; + var parse = getParsed("x_3")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); @@ -271,7 +280,7 @@ describe("A subscript and superscript parser", function() { }); it("should produce supsubs for ^_", function() { - var parse = parseTree("x^2_3")[0]; + var parse = getParsed("x^2_3")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); @@ -280,7 +289,7 @@ describe("A subscript and superscript parser", function() { }); it("should produce supsubs for _^", function() { - var parse = parseTree("x_3^2")[0]; + var parse = getParsed("x_3^2")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); @@ -289,8 +298,8 @@ describe("A subscript and superscript parser", function() { }); it("should produce the same thing regardless of order", function() { - var parseA = parseTree("x^2_3"); - var parseB = parseTree("x_3^2"); + var parseA = getParsed("x^2_3"); + var parseB = getParsed("x_3^2"); expect(parseA).toEqual(parseB); }); @@ -350,7 +359,7 @@ describe("A group parser", function() { }); it("should produce a single ord", function() { - var parse = parseTree("{xy}"); + var parse = getParsed("{xy}"); expect(parse.length).toBe(1); @@ -368,7 +377,7 @@ describe("An implicit group parser", function() { }); it("should produce a single object", function() { - var parse = parseTree("\\Large abc"); + var parse = getParsed("\\Large abc"); expect(parse.length).toBe(1); @@ -379,7 +388,7 @@ describe("An implicit group parser", function() { }); it("should apply only after the function", function() { - var parse = parseTree("a \\Large abc"); + var parse = getParsed("a \\Large abc"); expect(parse.length).toBe(2); @@ -390,7 +399,7 @@ describe("An implicit group parser", function() { }); it("should stop at the ends of groups", function() { - var parse = parseTree("a { b \\Large c } d"); + var parse = getParsed("a { b \\Large c } d"); var group = parse[1]; var sizing = group.value[1]; @@ -446,7 +455,7 @@ describe("A frac parser", function() { }); it("should produce a frac", function() { - var parse = parseTree(expression)[0]; + var parse = getParsed(expression)[0]; expect(parse.type).toMatch("frac"); expect(parse.value.numer).toBeDefined(); @@ -460,13 +469,13 @@ describe("A frac parser", function() { }); it("should parse dfrac and tfrac as fracs", function() { - var dfracParse = parseTree(dfracExpression)[0]; + var dfracParse = getParsed(dfracExpression)[0]; expect(dfracParse.type).toMatch("frac"); expect(dfracParse.value.numer).toBeDefined(); expect(dfracParse.value.denom).toBeDefined(); - var tfracParse = parseTree(tfracExpression)[0]; + var tfracParse = getParsed(tfracExpression)[0]; expect(tfracParse.type).toMatch("frac"); expect(tfracParse.value.numer).toBeDefined(); @@ -486,13 +495,13 @@ describe("An over parser", function() { it("should produce a frac", function() { var parse; - parse = parseTree(simpleOver)[0]; + parse = getParsed(simpleOver)[0]; expect(parse.type).toMatch("frac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); - parse = parseTree(complexOver)[0]; + parse = getParsed(complexOver)[0]; expect(parse.type).toMatch("frac"); expect(parse.value.numer).toBeDefined(); @@ -500,14 +509,14 @@ describe("An over parser", function() { }); it("should create a numerator from the atoms before \\over", function () { - var parse = parseTree(complexOver)[0]; + var parse = getParsed(complexOver)[0]; var numer = parse.value.numer; expect(numer.value.length).toEqual(4); }); it("should create a demonimator from the atoms after \\over", function () { - var parse = parseTree(complexOver)[0]; + var parse = getParsed(complexOver)[0]; var denom = parse.value.numer; expect(denom.value.length).toEqual(4); @@ -515,9 +524,7 @@ describe("An over parser", function() { it("should handle empty numerators", function () { var emptyNumerator = "\\over x"; - expect(emptyNumerator).toParse(); - - var parse = parseTree(emptyNumerator)[0]; + var parse = getParsed(emptyNumerator)[0]; expect(parse.type).toMatch("frac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); @@ -525,9 +532,7 @@ describe("An over parser", function() { it("should handle empty denominators", function () { var emptyDenominator = "1 \\over"; - expect(emptyDenominator).toParse(); - - var parse = parseTree(emptyDenominator)[0]; + var parse = getParsed(emptyDenominator)[0]; expect(parse.type).toMatch("frac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); @@ -535,9 +540,7 @@ describe("An over parser", function() { it("should handle \\displaystyle correctly", function () { var displaystyleExpression = "\\displaystyle 1 \\over 2"; - expect(displaystyleExpression).toParse(); - - var parse = parseTree(displaystyleExpression)[0]; + var parse = getParsed(displaystyleExpression)[0]; expect(parse.type).toMatch("frac"); expect(parse.value.numer.value[0].type).toMatch("styling"); expect(parse.value.denom).toBeDefined(); @@ -545,9 +548,7 @@ describe("An over parser", function() { it("should handle nested factions", function () { var nestedOverExpression = "{1 \\over 2} \\over 3"; - expect(nestedOverExpression).toParse(); - - var parse = parseTree(nestedOverExpression)[0]; + var parse = getParsed(nestedOverExpression)[0]; expect(parse.type).toMatch("frac"); expect(parse.value.numer.value[0].type).toMatch("frac"); expect(parse.value.numer.value[0].value.numer.value[0].value).toMatch(1); @@ -574,7 +575,7 @@ describe("A sizing parser", function() { }); it("should produce a sizing node", function() { - var parse = parseTree(sizeExpression)[0]; + var parse = getParsed(sizeExpression)[0]; expect(parse.type).toMatch("sizing"); expect(parse.value).toBeDefined(); @@ -596,14 +597,14 @@ describe("A text parser", function() { }); it("should produce a text", function() { - var parse = parseTree(textExpression)[0]; + var parse = getParsed(textExpression)[0]; expect(parse.type).toMatch("text"); expect(parse.value).toBeDefined(); }); it("should produce textords instead of mathords", function() { - var parse = parseTree(textExpression)[0]; + var parse = getParsed(textExpression)[0]; var group = parse.value.body; expect(group[0].type).toMatch("textord"); @@ -626,7 +627,7 @@ describe("A text parser", function() { }); it("should contract spaces", function() { - var parse = parseTree(spaceTextExpression)[0]; + var parse = getParsed(spaceTextExpression)[0]; var group = parse.value.body; expect(group[0].type).toMatch("spacing"); @@ -636,7 +637,7 @@ describe("A text parser", function() { }); it("should ignore a space before the text group", function() { - var parse = parseTree(leadingSpaceTextExpression)[0]; + var parse = getParsed(leadingSpaceTextExpression)[0]; // [m, o, o] expect(parse.value.body.length).toBe(3); expect( @@ -655,7 +656,7 @@ describe("A color parser", function() { }); it("should build a color node", function() { - var parse = parseTree(colorExpression)[0]; + var parse = getParsed(colorExpression)[0]; expect(parse.type).toMatch("color"); expect(parse.value.color).toBeDefined(); @@ -667,7 +668,7 @@ describe("A color parser", function() { }); it("should correctly extract the custom color", function() { - var parse = parseTree(customColorExpression)[0]; + var parse = getParsed(customColorExpression)[0]; expect(parse.value.color).toMatch("#fA6"); }); @@ -690,20 +691,20 @@ describe("A tie parser", function() { }); it("should produce spacing in math mode", function() { - var parse = parseTree(mathTie); + var parse = getParsed(mathTie); expect(parse[1].type).toMatch("spacing"); }); it("should produce spacing in text mode", function() { - var text = parseTree(textTie)[0]; + var text = getParsed(textTie)[0]; var parse = text.value.body; expect(parse[1].type).toMatch("spacing"); }); it("should not contract with spaces in text mode", function() { - var text = parseTree(textTie)[0]; + var text = getParsed(textTie)[0]; var parse = text.value.body; expect(parse[2].type).toMatch("spacing"); @@ -725,22 +726,22 @@ describe("A delimiter sizing parser", function() { }); it("should produce a delimsizing", function() { - var parse = parseTree(normalDelim)[0]; + var parse = getParsed(normalDelim)[0]; expect(parse.type).toMatch("delimsizing"); }); it("should produce the correct direction delimiter", function() { - var leftParse = parseTree(normalDelim)[0]; - var rightParse = parseTree(bigDelim)[0]; + var leftParse = getParsed(normalDelim)[0]; + var rightParse = getParsed(bigDelim)[0]; expect(leftParse.value.delimType).toMatch("open"); expect(rightParse.value.delimType).toMatch("close"); }); it("should parse the correct size delimiter", function() { - var smallParse = parseTree(normalDelim)[0]; - var bigParse = parseTree(bigDelim)[0]; + var smallParse = getParsed(normalDelim)[0]; + var bigParse = getParsed(bigDelim)[0]; expect(smallParse.value.size).toEqual(1); expect(bigParse.value.size).toEqual(4); @@ -755,7 +756,7 @@ describe("An overline parser", function() { }); it("should produce an overline", function() { - var parse = parseTree(overline)[0]; + var parse = getParsed(overline)[0]; expect(parse.type).toMatch("overline"); }); @@ -785,14 +786,14 @@ describe("A rule parser", function() { }); it("should produce a rule", function() { - var parse = parseTree(emRule)[0]; + var parse = getParsed(emRule)[0]; expect(parse.type).toMatch("rule"); }); it("should list the correct units", function() { - var emParse = parseTree(emRule)[0]; - var exParse = parseTree(exRule)[0]; + var emParse = getParsed(emRule)[0]; + var exParse = getParsed(exRule)[0]; expect(emParse.value.width.unit).toMatch("em"); expect(emParse.value.height.unit).toMatch("em"); @@ -802,16 +803,14 @@ describe("A rule parser", function() { }); it("should parse the number correctly", function() { - var hardNumberParse = parseTree(hardNumberRule)[0]; + var hardNumberParse = getParsed(hardNumberRule)[0]; expect(hardNumberParse.value.width.number).toBeCloseTo(1.24); expect(hardNumberParse.value.height.number).toBeCloseTo(2.45); }); it("should parse negative sizes", function() { - expect("\\rule{-1em}{- 0.2em}").toParse(); - - var parse = parseTree("\\rule{-1em}{- 0.2em}")[0]; + var parse = getParsed("\\rule{-1em}{- 0.2em}")[0]; expect(parse.value.width.number).toBeCloseTo(-1); expect(parse.value.height.number).toBeCloseTo(-0.2); @@ -827,7 +826,7 @@ describe("A left/right parser", function() { }); it("should produce a leftright", function() { - var parse = parseTree(normalLeftRight)[0]; + var parse = getParsed(normalLeftRight)[0]; expect(parse.type).toMatch("leftright"); expect(parse.value.left).toMatch("\\("); @@ -876,7 +875,7 @@ describe("A sqrt parser", function() { }); it("should produce sqrts", function() { - var parse = parseTree(sqrt)[0]; + var parse = getParsed(sqrt)[0]; expect(parse.type).toMatch("sqrt"); }); @@ -1036,18 +1035,16 @@ describe("A style change parser", function() { }); it("should produce the correct style", function() { - var displayParse = parseTree("\\displaystyle x")[0]; + var displayParse = getParsed("\\displaystyle x")[0]; expect(displayParse.value.style).toMatch("display"); - var scriptscriptParse = parseTree("\\scriptscriptstyle x")[0]; + var scriptscriptParse = getParsed("\\scriptscriptstyle x")[0]; expect(scriptscriptParse.value.style).toMatch("scriptscript"); }); it("should only change the style within its group", function() { var text = "a b { c d \\displaystyle e f } g h"; - expect(text).toParse(); - - var parse = parseTree(text); + var parse = getParsed(text); var displayNode = parse[2].value[2]; @@ -1108,13 +1105,13 @@ describe("An accent parser", function() { }); it("should produce accents", function() { - var parse = parseTree("\\vec x")[0]; + var parse = getParsed("\\vec x")[0]; expect(parse.type).toMatch("accent"); }); it("should be grouped more tightly than supsubs", function() { - var parse = parseTree("\\vec x^2")[0]; + var parse = getParsed("\\vec x^2")[0]; expect(parse.type).toMatch("supsub"); }); @@ -1144,7 +1141,7 @@ describe("An accent builder", function() { describe("A parser error", function () { it("should report the position of an error", function () { try { - parseTree("\\sqrt}"); + parseTree("\\sqrt}", defaultSettings); } catch (e) { expect(e.position).toEqual(5); }