diff --git a/src/buildHTML.js b/src/buildHTML.js index 8a4fe9e4a..aee601df7 100644 --- a/src/buildHTML.js +++ b/src/buildHTML.js @@ -812,7 +812,37 @@ var groupTypes = { ], "firstBaseline", null, options); } - return makeSpan(["sqrt", "mord"], [delim, body]); + if (!group.value.index) { + return makeSpan(["sqrt", "mord"], [delim, body]); + } else { + // Handle the optional root index + + // The index is always in scriptscript style + var root = buildGroup( + group.value.index, + options.withStyle(Style.SCRIPTSCRIPT)); + var rootWrap = makeSpan( + [options.style.reset(), Style.SCRIPTSCRIPT.cls()], + [root]); + + // Figure out the height and depth of the inner part + var innerHeight = Math.max(delim.height, body.height); + var innerDepth = Math.max(delim.depth, body.depth); + + // The amount the index is shifted by. This is taken from the TeX + // source, in the definition of `\r@@t`. + var toShift = 0.6 * (innerHeight - innerDepth); + + // Build a VList with the superscript shifted up correctly + var rootVList = buildCommon.makeVList( + [{type: "elem", elem: rootWrap}], + "shift", -toShift, options); + // Add a class surrounding it so we can add on the appropriate + // kerning + var rootVListWrap = makeSpan(["root"], [rootVList]); + + return makeSpan(["sqrt", "mord"], [rootVListWrap, delim, body]); + } }, sizing: function(group, options, prev) { diff --git a/src/buildMathML.js b/src/buildMathML.js index 129b26d60..a681decaa 100644 --- a/src/buildMathML.js +++ b/src/buildMathML.js @@ -187,8 +187,17 @@ var groupTypes = { }, sqrt: function(group) { - var node = new mathMLTree.MathNode( - "msqrt", [buildGroup(group.value.body)]); + var node; + if (group.value.index) { + node = new mathMLTree.MathNode( + "mroot", [ + buildGroup(group.value.body), + buildGroup(group.value.index) + ]); + } else { + node = new mathMLTree.MathNode( + "msqrt", [buildGroup(group.value.body)]); + } return node; }, diff --git a/src/functions.js b/src/functions.js index 5bdbe0245..a00dd3f29 100644 --- a/src/functions.js +++ b/src/functions.js @@ -71,16 +71,11 @@ var functions = { "\\sqrt": { numArgs: 1, numOptionalArgs: 1, - handler: function(func, optional, body, positions) { - if (optional != null) { - throw new ParseError( - "Optional arguments to \\sqrt aren't supported yet", - this.lexer, positions[1] - 1); - } - + handler: function(func, index, body, positions) { return { type: "sqrt", - body: body + body: body, + index: index }; } }, diff --git a/static/katex.less b/static/katex.less index ff29f422f..44bc34536 100644 --- a/static/katex.less +++ b/static/katex.less @@ -1,5 +1,8 @@ @import "fonts.less"; +// The mu unit is defined as 1/18 em +@mu: 1.0/18.0em; + .katex-display { display: block; margin: 1em 0; @@ -359,6 +362,13 @@ margin-top: -1px; } } + + > .root { + // These values are taken from the definition of `\r@@t`, + // `\mkern 5mu` and `\mkern -10mu`. + margin-left: 5*@mu; + margin-right: -10*@mu; + } } .sizing, .fontsize-ensurer { diff --git a/test/katex-spec.js b/test/katex-spec.js index e89ccdc0b..ed102cb07 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -1210,8 +1210,8 @@ describe("An optional argument parser", function() { expect("\\rule[0.2em]{1em}{1em}").toParse(); }); - it("should fail on sqrts for now", function() { - expect("\\sqrt[3]{2}").toNotParse(); + it("should work with sqrts with optional arguments", function() { + expect("\\sqrt[3]{2}").toParse(); }); it("should work when the optional argument is missing", function() { diff --git a/test/screenshotter/images/SqrtRoot-firefox.png b/test/screenshotter/images/SqrtRoot-firefox.png new file mode 100644 index 000000000..09b51fe93 Binary files /dev/null and b/test/screenshotter/images/SqrtRoot-firefox.png differ diff --git a/test/screenshotter/ss_data.json b/test/screenshotter/ss_data.json index 62ff0cb68..5f0416f48 100644 --- a/test/screenshotter/ss_data.json +++ b/test/screenshotter/ss_data.json @@ -29,6 +29,7 @@ "Sizing": "http://localhost:7936/test/screenshotter/test.html?m={\\Huge x}{\\LARGE y}{\\normalsize z}{\\scriptsize w}", "Spacing": "http://localhost:7936/test/screenshotter/test.html?m=^3+[-1][1-1]1=1(=1)\\lvert a\\rvert~b", "Sqrt": "http://localhost:7936/test/screenshotter/test.html?m=\\sqrt{\\sqrt{\\sqrt{x}}}_{\\sqrt{\\sqrt{x}}}^{\\sqrt{\\sqrt{\\sqrt{x}}}^{\\sqrt{\\sqrt{\\sqrt{x}}}}}", + "SqrtRoot": "http://localhost:7936/test/screenshotter/test.html?m=1+\\sqrt[3]{2}+\\sqrt[1923^234]{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^2}}}}}}}}}}}", "SupSubCharacterBox": "http://localhost:7936/test/screenshotter/test.html?m=a_2f_2{f}_2{aa}_2{af}_2", "SupSubHorizSpacing": "http://localhost:7936/test/screenshotter/test.html?m=x^{x^{x}}\\Big|x_{x_{x_{x_{x}}}}\\bigg|x^{x^{x_{x_{x_{x_{x}}}}}}\\bigg|", "SupSubOffsets": "http://localhost:7936/test/screenshotter/test.html?m=\\displaystyle \\int_{2+3}x f^{2+3}+3\\lim_{2+3+4+5}f",