From a51dc4b0727dad43187e8cf006cf1aeb32cb4de3 Mon Sep 17 00:00:00 2001 From: ylemkimon Date: Sat, 13 Oct 2018 11:26:13 +0900 Subject: [PATCH] Wrap document fragment where classes, attributes, or styles are applied (#1707) * Wrap document fragment group where classes or styles are applied * Wrap non-math node where attributes are applied in \href --- src/buildCommon.js | 15 +++++++++++++++ src/functions/arrow.js | 8 ++++++-- src/functions/enclose.js | 5 ++++- src/functions/href.js | 8 +++++--- src/functions/sqrt.js | 5 +---- src/utils.js | 12 ------------ test/katex-spec.js | 19 ++++++++++++++++++- 7 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/buildCommon.js b/src/buildCommon.js index aec619f8..06d734d1 100644 --- a/src/buildCommon.js +++ b/src/buildCommon.js @@ -459,6 +459,20 @@ const makeFragment = function( return fragment; }; +/** + * Wraps group in a span if it's a document fragment, allowing to apply classes + * and styles + */ +const wrapFragment = function( + group: HtmlDomNode, + options: Options, +): HtmlDomNode { + if (group instanceof DocumentFragment) { + return makeSpan([], [group], options); + } + return group; +}; + // These are exact object types to catch typos in the names of the optional fields. export type VListElem = {| @@ -812,6 +826,7 @@ export default { makeLineSpan, makeAnchor, makeFragment, + wrapFragment, makeVList, makeOrd, makeGlue, diff --git a/src/functions/arrow.js b/src/functions/arrow.js index 898dd87c..b96e97c1 100644 --- a/src/functions/arrow.js +++ b/src/functions/arrow.js @@ -44,15 +44,19 @@ defineFunction({ // Build the argument groups in the appropriate style. // Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}% + // Some groups can return document fragments. Handle those by wrapping + // them in a span. let newOptions = options.havingStyle(style.sup()); - const upperGroup = html.buildGroup(group.body, newOptions, options); + const upperGroup = buildCommon.wrapFragment( + html.buildGroup(group.body, newOptions, options), options); upperGroup.classes.push("x-arrow-pad"); let lowerGroup; if (group.below) { // Build the lower group newOptions = options.havingStyle(style.sub()); - lowerGroup = html.buildGroup(group.below, newOptions, options); + lowerGroup = buildCommon.wrapFragment( + html.buildGroup(group.below, newOptions, options), options); lowerGroup.classes.push("x-arrow-pad"); } diff --git a/src/functions/enclose.js b/src/functions/enclose.js index 487ebf2d..f1cad883 100644 --- a/src/functions/enclose.js +++ b/src/functions/enclose.js @@ -12,7 +12,10 @@ import * as mml from "../buildMathML"; const htmlBuilder = (group, options) => { // \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox - const inner = html.buildGroup(group.body, options); + // Some groups can return document fragments. Handle those by wrapping + // them in a span. + const inner = buildCommon.wrapFragment( + html.buildGroup(group.body, options), options); const label = group.label.substr(1); const scale = options.sizeMultiplier; diff --git a/src/functions/href.js b/src/functions/href.js index 4be210a3..17001fb4 100644 --- a/src/functions/href.js +++ b/src/functions/href.js @@ -2,7 +2,6 @@ import defineFunction, {ordargument} from "../defineFunction"; import buildCommon from "../buildCommon"; import {assertNodeType} from "../parseNode"; -import {assertType} from "../utils"; import {MathNode} from "../mathMLTree"; import * as html from "../buildHTML"; @@ -31,8 +30,11 @@ defineFunction({ return buildCommon.makeAnchor(group.href, [], elements, options); }, mathmlBuilder: (group, options) => { - const math = mml.buildExpressionRow(group.body, options); - assertType(math, MathNode).setAttribute("href", group.href); + let math = mml.buildExpressionRow(group.body, options); + if (!(math instanceof MathNode)) { + math = new MathNode("mrow", [math]); + } + math.setAttribute("href", group.href); return math; }, }); diff --git a/src/functions/sqrt.js b/src/functions/sqrt.js index 11098dbe..5bd9d902 100644 --- a/src/functions/sqrt.js +++ b/src/functions/sqrt.js @@ -5,7 +5,6 @@ import mathMLTree from "../mathMLTree"; import delimiter from "../delimiter"; import Style from "../Style"; -import {DocumentFragment} from "../tree"; import * as html from "../buildHTML"; import * as mml from "../buildMathML"; @@ -39,9 +38,7 @@ defineFunction({ // Some groups can return document fragments. Handle those by wrapping // them in a span. - if (inner instanceof DocumentFragment) { - inner = buildCommon.makeSpan([], [inner], options); - } + inner = buildCommon.wrapFragment(inner, options); // Calculate the minimum size for the \surd delimiter const metrics = options.fontMetrics(); diff --git a/src/utils.js b/src/utils.js index 87c2cdb8..b364ad8d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -91,18 +91,6 @@ export const assert = function(value: ?T): T { return value; }; -export const assertType = function(val: mixed, Cls: Class): T { - if (val instanceof Cls) { - return val; - } - - // $FlowFixMe: Get constructor name if possible. - const expected = String(Cls.name || Cls); - // $FlowFixMe: Get constructor name if possible; else stringify value. - const actual = String(val.constructor.name || val); - throw new Error(`Expected ${expected} but got ${actual}.`); -}; - export default { contains, deflt, diff --git a/test/katex-spec.js b/test/katex-spec.js index d2b0753f..b4cb6ba9 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -2415,6 +2415,23 @@ describe("A smash builder", function() { }); }); +describe("A document fragment", function() { + it("should have paddings applied inside an extensible arrow", function() { + const markup = katex.renderToString("\\tiny\\xrightarrow\\textcolor{red}{x}"); + expect(markup).toContain("x-arrow-pad"); + }); + + it("should have paddings applied inside an enclose", function() { + const markup = katex.renderToString(r`\fbox\textcolor{red}{x}`); + expect(markup).toContain("boxpad"); + }); + + it("should have paddings applied inside a square root", function() { + const markup = katex.renderToString(r`\sqrt\textcolor{red}{x}`); + expect(markup).toContain("padding-left"); + }); +}); + describe("A parser error", function() { it("should report the position of an error", function() { try { @@ -2519,7 +2536,7 @@ describe("href and url commands", function() { // We can't use raw strings for \url because \u is for Unicode escapes. it("should parse its input", function() { - expect`\href{http://example.com/}{example here}`.toBuild(); + expect`\href{http://example.com/}{\sin}`.toBuild(); expect("\\url{http://example.com/}").toBuild(); });