extract sqrt, enclose, and verb into their own files (#1040)

* extract sqrt, enclose, and verb into their own files

* add returns types in utils.js and update the  comment

* rebase, fix location of  comment
This commit is contained in:
Kevin Barabash
2017-12-29 13:37:09 -07:00
committed by GitHub
parent 5cc795eaaa
commit 357d6783a4
9 changed files with 546 additions and 444 deletions

188
src/functions/enclose.js Normal file
View File

@@ -0,0 +1,188 @@
// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import utils from "../utils";
import stretchy from "../stretchy";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
const htmlBuilder = (group, options) => {
// \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox
const inner = html.buildGroup(group.value.body, options);
const label = group.value.label.substr(1);
const scale = options.sizeMultiplier;
let img;
let imgShift = 0;
const isColorbox = /color/.test(label);
if (label === "sout") {
img = buildCommon.makeSpan(["stretchy", "sout"]);
img.height = options.fontMetrics().defaultRuleThickness / scale;
imgShift = -0.5 * options.fontMetrics().xHeight;
} else {
// Add horizontal padding
inner.classes.push(/cancel/.test(label) ? "cancel-pad" : "boxpad");
// Add vertical padding
let vertPad = 0;
// ref: LaTeX source2e: \fboxsep = 3pt; \fboxrule = .4pt
// ref: cancel package: \advance\totalheight2\p@ % "+2"
if (/box/.test(label)) {
vertPad = label === "colorbox" ? 0.3 : 0.34;
} else {
vertPad = utils.isCharacterBox(group.value.body) ? 0.2 : 0;
}
img = stretchy.encloseSpan(inner, label, vertPad, options);
imgShift = inner.depth + vertPad;
if (isColorbox) {
img.style.backgroundColor = group.value.backgroundColor.value;
if (label === "fcolorbox") {
img.style.borderColor = group.value.borderColor.value;
}
}
}
let vlist;
if (isColorbox) {
vlist = buildCommon.makeVList({
positionType: "individualShift",
children: [
// Put the color background behind inner;
{type: "elem", elem: img, shift: imgShift},
{type: "elem", elem: inner, shift: 0},
],
}, options);
} else {
vlist = buildCommon.makeVList({
positionType: "individualShift",
children: [
// Write the \cancel stroke on top of inner.
{
type: "elem",
elem: inner,
shift: 0,
},
{
type: "elem",
elem: img,
shift: imgShift,
wrapperClasses: /cancel/.test(label) ? ["svg-align"] : [],
},
],
}, options);
}
if (/cancel/.test(label)) {
// cancel does not create horiz space for its line extension.
// That is, not when adjacent to a mord.
return buildCommon.makeSpan(["mord", "cancel-lap"], [vlist], options);
} else {
return buildCommon.makeSpan(["mord"], [vlist], options);
}
};
const mathmlBuilder = (group, options) => {
const node = new mathMLTree.MathNode(
"menclose", [mml.buildGroup(group.value.body, options)]);
switch (group.value.label) {
case "\\cancel":
node.setAttribute("notation", "updiagonalstrike");
break;
case "\\bcancel":
node.setAttribute("notation", "downdiagonalstrike");
break;
case "\\sout":
node.setAttribute("notation", "horizontalstrike");
break;
case "\\fbox":
node.setAttribute("notation", "box");
break;
case "\\colorbox":
node.setAttribute("mathbackground",
group.value.backgroundColor.value);
break;
case "\\fcolorbox":
node.setAttribute("mathbackground",
group.value.backgroundColor.value);
// TODO(ron): I don't know any way to set the border color.
node.setAttribute("notation", "box");
break;
default:
// xcancel
node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
}
return node;
};
defineFunction({
type: "enclose",
names: ["\\colorbox"],
props: {
numArgs: 2,
allowedInText: true,
greediness: 3,
argTypes: ["color", "text"],
},
handler(context, args, optArgs) {
const color = args[0];
const body = args[1];
return {
type: "enclose",
label: context.funcName,
backgroundColor: color,
body: body,
};
},
htmlBuilder,
mathmlBuilder,
});
defineFunction({
type: "enclose",
names: ["\\fcolorbox"],
props: {
numArgs: 3,
allowedInText: true,
greediness: 3,
argTypes: ["color", "color", "text"],
},
handler(context, args, optArgs) {
const borderColor = args[0];
const backgroundColor = args[1];
const body = args[2];
return {
type: "enclose",
label: context.funcName,
backgroundColor: backgroundColor,
borderColor: borderColor,
body: body,
};
},
htmlBuilder,
mathmlBuilder,
});
defineFunction({
type: "enclose",
names: ["\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\fbox"],
props: {
numArgs: 1,
},
handler(context, args, optArgs) {
const body = args[0];
return {
type: "enclose",
label: context.funcName,
body: body,
};
},
htmlBuilder,
mathmlBuilder,
});

130
src/functions/sqrt.js Normal file
View File

@@ -0,0 +1,130 @@
// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import domTree from "../domTree";
import mathMLTree from "../mathMLTree";
import delimiter from "../delimiter";
import Style from "../Style";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
defineFunction({
type: "sqrt",
names: ["\\sqrt"],
props: {
numArgs: 1,
numOptionalArgs: 1,
},
handler(context, args, optArgs) {
const index = optArgs[0];
const body = args[0];
return {
type: "sqrt",
body: body,
index: index,
};
},
htmlBuilder(group, options) {
// Square roots are handled in the TeXbook pg. 443, Rule 11.
// First, we do the same steps as in overline to build the inner group
// and line
let inner = html.buildGroup(group.value.body, options.havingCrampedStyle());
if (inner.height === 0) {
// Render a small surd.
inner.height = options.fontMetrics().xHeight;
}
// Some groups can return document fragments. Handle those by wrapping
// them in a span.
if (inner instanceof domTree.documentFragment) {
inner = buildCommon.makeSpan([], [inner], options);
}
// Calculate the minimum size for the \surd delimiter
const metrics = options.fontMetrics();
const theta = metrics.defaultRuleThickness;
let phi = theta;
if (options.style.id < Style.TEXT.id) {
phi = options.fontMetrics().xHeight;
}
// Calculate the clearance between the body and line
let lineClearance = theta + phi / 4;
const minDelimiterHeight = (inner.height + inner.depth +
lineClearance + theta) * options.sizeMultiplier;
// Create a sqrt SVG of the required minimum size
const {span: img, ruleWidth} =
delimiter.sqrtImage(minDelimiterHeight, options);
const delimDepth = img.height - ruleWidth;
// Adjust the clearance based on the delimiter size
if (delimDepth > inner.height + inner.depth + lineClearance) {
lineClearance =
(lineClearance + delimDepth - inner.height - inner.depth) / 2;
}
// Shift the sqrt image
const imgShift = img.height - inner.height - lineClearance - ruleWidth;
inner.style.paddingLeft = img.advanceWidth + "em";
// Overlay the image and the argument.
const body = buildCommon.makeVList({
positionType: "firstBaseline",
children: [
{type: "elem", elem: inner, wrapperClasses: ["svg-align"]},
{type: "kern", size: -(inner.height + imgShift)},
{type: "elem", elem: img},
{type: "kern", size: ruleWidth},
],
}, options);
if (!group.value.index) {
return buildCommon.makeSpan(["mord", "sqrt"], [body], options);
} else {
// Handle the optional root index
// The index is always in scriptscript style
const newOptions = options.havingStyle(Style.SCRIPTSCRIPT);
const rootm = html.buildGroup(group.value.index, newOptions, options);
// The amount the index is shifted by. This is taken from the TeX
// source, in the definition of `\r@@t`.
const toShift = 0.6 * (body.height - body.depth);
// Build a VList with the superscript shifted up correctly
const rootVList = buildCommon.makeVList({
positionType: "shift",
positionData: -toShift,
children: [{type: "elem", elem: rootm}],
}, options);
// Add a class surrounding it so we can add on the appropriate
// kerning
const rootVListWrap = buildCommon.makeSpan(["root"], [rootVList]);
return buildCommon.makeSpan(["mord", "sqrt"],
[rootVListWrap, body], options);
}
},
mathmlBuilder(group, options) {
let node;
if (group.value.index) {
node = new mathMLTree.MathNode(
"mroot", [
mml.buildGroup(group.value.body, options),
mml.buildGroup(group.value.index, options),
]);
} else {
node = new mathMLTree.MathNode(
"msqrt", [mml.buildGroup(group.value.body, options)]);
}
return node;
},
});

55
src/functions/verb.js Normal file
View File

@@ -0,0 +1,55 @@
// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import ParseError from "../ParseError";
defineFunction({
type: "verb",
names: ["\\verb"],
props: {
numArgs: 0,
allowedInText: true,
},
handler(context, args, optArgs) {
// \verb and \verb* are dealt with directly in Parser.js.
// If we end up here, it's because of a failure to match the two delimiters
// in the regex in Lexer.js. LaTeX raises the following error when \verb is
// terminated by end of line (or file).
throw new ParseError(
"\\verb ended by end of line instead of matching delimiter");
},
htmlBuilder(group, options) {
const text = buildCommon.makeVerb(group, options);
const body = [];
// \verb enters text mode and therefore is sized like \textstyle
const newOptions = options.havingStyle(options.style.text());
for (let i = 0; i < text.length; i++) {
if (text[i] === '\xA0') { // spaces appear as nonbreaking space
// The space character isn't in the Typewriter-Regular font,
// so we implement it as a kern of the same size as a character.
// 0.525 is the width of a texttt character in LaTeX.
// It automatically gets scaled by the font size.
const rule = buildCommon.makeSpan(["mord", "rule"], [], newOptions);
rule.style.marginLeft = "0.525em";
body.push(rule);
} else {
body.push(buildCommon.makeSymbol(text[i], "Typewriter-Regular",
group.mode, newOptions, ["mathtt"]));
}
}
buildCommon.tryCombineChars(body);
return buildCommon.makeSpan(
["mord", "text"].concat(newOptions.sizingClasses(options)),
// tryCombinChars expects CombinableDomNode[] while makeSpan expects
// DomChildNode[].
// $FlowFixMe: CombinableDomNode[] is not compatible with DomChildNode[]
body, newOptions);
},
mathmlBuilder(group, options) {
const text = new mathMLTree.TextNode(buildCommon.makeVerb(group, options));
const node = new mathMLTree.MathNode("mtext", [text]);
node.setAttribute("mathvariant", buildCommon.fontMap["mathtt"].variant);
return node;
},
});