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
This commit is contained in:
ylemkimon
2018-10-13 11:26:13 +09:00
committed by Kevin Barabash
parent 3514d48856
commit a51dc4b072
7 changed files with 49 additions and 23 deletions

View File

@@ -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,

View File

@@ -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");
}

View File

@@ -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;

View File

@@ -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;
},
});

View File

@@ -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();

View File

@@ -91,18 +91,6 @@ export const assert = function<T>(value: ?T): T {
return value;
};
export const assertType = function<T>(val: mixed, Cls: Class<T>): 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,

View File

@@ -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();
});