diff --git a/Parser.js b/Parser.js index 1bc2aede2..3116e3ebd 100644 --- a/Parser.js +++ b/Parser.js @@ -466,6 +466,19 @@ Parser.prototype.parseNucleus = function(pos, mode) { new ParseNode("katex", null, mode), nucleus.position ); + } else if (mode === "math" && nucleus.type === "\\overline") { + // If this is an overline, parse its argument and return + var group = this.parseGroup(nucleus.position, mode); + if (group) { + return new ParseResult( + new ParseNode("overline", group, mode), + group.position); + } else { + throw new ParseError("Expected group after '" + + nucleus.type + "'", + this.lexer, nucleus.position + ); + } } 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/Style.js b/Style.js index 8b1206723..94d1a7e0e 100644 --- a/Style.js +++ b/Style.js @@ -21,6 +21,10 @@ Style.prototype.fracDen = function() { return styles[fracDen[this.id]]; }; +Style.prototype.cramp = function() { + return styles[cramp[this.id]]; +}; + // HTML class name, like "displaystyle cramped" Style.prototype.cls = function() { return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped"); @@ -69,6 +73,7 @@ var sup = [S, Sc, S, Sc, SS, SSc, SS, SSc]; var sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc]; var fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc]; var fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc]; +var cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc]; module.exports = { DISPLAY: styles[D], diff --git a/buildTree.js b/buildTree.js index 43e345c28..bea447364 100644 --- a/buildTree.js +++ b/buildTree.js @@ -55,7 +55,8 @@ var groupToType = { punct: "mpunct", ordgroup: "mord", namedfn: "mop", - katex: "mord" + katex: "mord", + overline: "mord" }; var getTypeOfGroup = function(group) { @@ -419,6 +420,27 @@ var groupTypes = { return makeSpan(["katex-logo"], [k, a, t, e, x], options.getColor()); }, + overline: function(group, options, prev) { + var innerGroup = buildGroup(group.value.result, + options.withStyle(options.style.cramp()).deepen()); + + // The theta variable in the TeXbook + var lineWidth = fontMetrics.metrics.defaultRuleThickness; + + var line = makeSpan(["overline-line"], [makeSpan([])]); + var inner = makeSpan(["overline-inner"], [innerGroup]); + var fixIE = makeSpan(["fix-ie"], []); + + line.style.top = (-inner.height - 3 * lineWidth) + "em"; + // The line is supposed to have 1 extra line width above it in height + // (TeXbook pg. 443, nr. 9) + line.height = inner.height + 5 * lineWidth; + + return makeSpan(["overline", "mord"], [ + line, inner, fixIE + ], options.getColor()); + }, + sizing: function(group, options, prev) { var inner = buildGroup(group.value.value, options.withSize(group.value.size), prev); diff --git a/static/katex.less b/static/katex.less index 73bef0c4b..541f9c6b3 100644 --- a/static/katex.less +++ b/static/katex.less @@ -308,6 +308,45 @@ big parens } } + .overline { + .baseline-align-hack-outer; + + > .overline-line, + > .overline-inner, + > .fix-ie { + .baseline-align-hack-middle; + position: relative; + text-align: center; + + > span { + .baseline-align-hack-inner; + } + } + + > .fix-ie { + display: inline-block; + } + + > .overline-line > span { + width: 100%; + + &:before { + border-bottom-style: solid; + border-bottom-width: 1px; + content: ""; + display: block; + } + + &:after { + border-bottom-style: solid; + border-bottom-width: 0.04em; + content: ""; + display: block; + margin-top: -1px; + } + } + } + .sizing { display: inline-block; diff --git a/test/huxley/Huxleyfile.json b/test/huxley/Huxleyfile.json index 42a80e516..d265f333e 100644 --- a/test/huxley/Huxleyfile.json +++ b/test/huxley/Huxleyfile.json @@ -99,5 +99,11 @@ "name": "DelimiterSizing", "screenSize": [1024, 768], "url": "http://localhost:7936/test/huxley/test.html?m=\\bigl\\uparrow\\Bigl\\downarrow\\biggl\\updownarrow\\Biggl\\Uparrow\\Biggr\\Downarrow\\biggr\\langle\\Bigr\\}\\bigr\\rfloor" + }, + + { + "name": "Overline", + "screenSize": [1024, 768], + "url": "http://localhost:7936/test/huxley/test.html?m=\\overline{x}\\overline{x}\\overline{x^{x^{x^x}}} \\blue\\overline{y}" } ] diff --git a/test/huxley/Overline.hux/firefox-1.png b/test/huxley/Overline.hux/firefox-1.png new file mode 100644 index 000000000..d16429077 Binary files /dev/null and b/test/huxley/Overline.hux/firefox-1.png differ diff --git a/test/huxley/Overline.hux/record.json b/test/huxley/Overline.hux/record.json new file mode 100644 index 000000000..3cae6ac65 --- /dev/null +++ b/test/huxley/Overline.hux/record.json @@ -0,0 +1,5 @@ +[ + { + "action": "screenshot" + } +] diff --git a/test/katex-tests.js b/test/katex-tests.js index d645e0e1f..97a4faded 100644 --- a/test/katex-tests.js +++ b/test/katex-tests.js @@ -667,3 +667,19 @@ describe("A delimiter sizing parser", function() { expect(bigParse.value.size).toEqual(4); }); }); + +describe("An overline parser", function() { + var overline = "\\overline{x}"; + + it("should not fail", function() { + expect(function() { + parseTree(overline); + }).not.toThrow(); + }); + + it("should produce an overline", function() { + var parse = parseTree(overline)[0]; + + expect(parse.type).toMatch("overline"); + }); +});