Move HTML and MathML builders for symbol groups into src/functions/{symbolsOp,symbolsOrd}.js. (#1334)

* Move HTML and MathML builders for symbol groups into src/functions/{symbolsOp,symbolsOrd}.js.
This commit is contained in:
Ashish Myles
2018-05-20 22:07:35 -04:00
committed by GitHub
parent ff8ca60584
commit 34cf5c2f81
8 changed files with 152 additions and 123 deletions

View File

@@ -222,10 +222,10 @@ const boldsymbol = function(
/**
* Makes either a mathord or textord in the correct font and color.
*/
const makeOrd = function(
group: ParseNode<*>,
const makeOrd = function<NODETYPE: "textord" | "mathord">(
group: ParseNode<NODETYPE>,
options: Options,
type: NodeType,
type: NODETYPE,
): domTree.symbolNode {
const mode = group.mode;
const value = group.value;

View File

@@ -242,28 +242,6 @@ export const makeNullDelimiter = function(options, classes) {
* Simpler types come at the beginning, while complicated types come afterwards.
*/
export const groupTypes = {
mathord: (group, options) => buildCommon.makeOrd(group, options, "mathord"),
textord: (group, options) => buildCommon.makeOrd(group, options, "textord"),
bin: (group, options) =>
buildCommon.mathsym(group.value, group.mode, options, ["mbin"]),
rel: (group, options) =>
buildCommon.mathsym(group.value, group.mode, options, ["mrel"]),
open: (group, options) =>
buildCommon.mathsym(group.value, group.mode, options, ["mopen"]),
close: (group, options) =>
buildCommon.mathsym(group.value, group.mode, options, ["mclose"]),
inner: (group, options) =>
buildCommon.mathsym(group.value, group.mode, options, ["minner"]),
punct: (group, options) =>
buildCommon.mathsym(group.value, group.mode, options, ["mpunct"]),
ordgroup: (group, options) => makeSpan(
["mord"], buildExpression(group.value, options, true), options),

View File

@@ -60,7 +60,7 @@ export const makeTextRow = function(body, options) {
/**
* Returns the math variant as a string or null if none is required.
*/
const getVariant = function(group, options) {
export const getVariant = function(group, options) {
const font = options.font;
if (!font) {
return null;
@@ -96,97 +96,6 @@ const getVariant = function(group, options) {
*/
export const groupTypes = {};
const defaultVariant = {
"mi": "italic",
"mn": "normal",
"mtext": "normal",
};
groupTypes.mathord = function(group, options) {
const node = new mathMLTree.MathNode(
"mi",
[makeText(group.value, group.mode)]);
const variant = getVariant(group, options) || "italic";
if (variant !== defaultVariant[node.type]) {
node.setAttribute("mathvariant", variant);
}
return node;
};
groupTypes.textord = function(group, options) {
const text = makeText(group.value, group.mode);
const variant = getVariant(group, options) || "normal";
let node;
if (group.mode === 'text') {
node = new mathMLTree.MathNode("mtext", [text]);
} else if (/[0-9]/.test(group.value)) {
// TODO(kevinb) merge adjacent <mn> nodes
// do it as a post processing step
node = new mathMLTree.MathNode("mn", [text]);
} else if (group.value === "\\prime") {
node = new mathMLTree.MathNode("mo", [text]);
} else {
node = new mathMLTree.MathNode("mi", [text]);
}
if (variant !== defaultVariant[node.type]) {
node.setAttribute("mathvariant", variant);
}
return node;
};
groupTypes.bin = function(group, options) {
const node = new mathMLTree.MathNode(
"mo", [makeText(group.value, group.mode)]);
const variant = getVariant(group, options);
if (variant === "bold-italic") {
node.setAttribute("mathvariant", variant);
}
return node;
};
groupTypes.rel = function(group) {
const node = new mathMLTree.MathNode(
"mo", [makeText(group.value, group.mode)]);
return node;
};
groupTypes.open = function(group) {
const node = new mathMLTree.MathNode(
"mo", [makeText(group.value, group.mode)]);
return node;
};
groupTypes.close = function(group) {
const node = new mathMLTree.MathNode(
"mo", [makeText(group.value, group.mode)]);
return node;
};
groupTypes.inner = function(group) {
const node = new mathMLTree.MathNode(
"mo", [makeText(group.value, group.mode)]);
return node;
};
groupTypes.punct = function(group) {
const node = new mathMLTree.MathNode(
"mo", [makeText(group.value, group.mode)]);
node.setAttribute("separator", "true");
return node;
};
groupTypes.ordgroup = function(group, options) {
const inner = buildExpression(group.value, options);

View File

@@ -26,6 +26,12 @@ export type FunctionHandler<NODETYPE: NodeType> = (
optArgs: (?ParseNode<*>)[],
) => NodeValue<NODETYPE>;
export type HtmlBuilder<NODETYPE> = (ParseNode<NODETYPE>, Options) => HtmlDomNode;
export type MathMLBuilder<NODETYPE> = (
group: ParseNode<NODETYPE>,
options: Options,
) => MathNode | TextNode | domTree.documentFragment;
export type FunctionPropSpec = {
// The number of arguments the function takes.
numArgs: number,
@@ -106,16 +112,13 @@ type FunctionDefSpec<NODETYPE: NodeType> = {|
// This function returns an object representing the DOM structure to be
// created when rendering the defined LaTeX function.
htmlBuilder?: (group: ParseNode<NODETYPE>, options: Options) => HtmlDomNode,
htmlBuilder?: HtmlBuilder<NODETYPE>,
// TODO: Currently functions/op.js returns documentFragment. Refactor it
// and update the return type of this function.
// This function returns an object representing the MathML structure to be
// created when rendering the defined LaTeX function.
mathmlBuilder?: (
group: ParseNode<NODETYPE>,
options: Options,
) => MathNode | TextNode | domTree.documentFragment,
mathmlBuilder?: MathMLBuilder<NODETYPE>,
|};
/**
@@ -198,6 +201,28 @@ export default function defineFunction<NODETYPE: NodeType>({
}
}
/**
* Use this to register only the HTML and MathML builders for a function (e.g.
* if the function's ParseNode is generated in Parser.js rather than via a
* stand-alone handler provided to `defineFunction`).
*/
export function defineFunctionBuilders<NODETYPE: NodeType>({
type, htmlBuilder, mathmlBuilder,
}: {
type: NODETYPE,
htmlBuilder: HtmlBuilder<NODETYPE>,
mathmlBuilder: MathMLBuilder<NODETYPE>,
}) {
defineFunction({
type,
names: [],
props: {numArgs: 0},
handler() { throw new Error('Should never be called.'); },
htmlBuilder,
mathmlBuilder,
});
}
// Since the corresponding buildHTML/buildMathML function expects a
// list of elements, we normalize for different kinds of arguments
export const ordargument = function(arg: ParseNode<*>): ParseNode<*>[] {

View File

@@ -35,6 +35,8 @@ import "./functions/sizing";
import "./functions/smash";
import "./functions/sqrt";
import "./functions/styling";
import "./functions/symbolsOp";
import "./functions/symbolsOrd";
import "./functions/text";
import "./functions/underline";
import "./functions/verb";

View File

@@ -0,0 +1,52 @@
// @flow
import {defineFunctionBuilders} from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import * as mml from "../buildMathML";
import type Options from "../Options";
import type ParseNode from "../ParseNode";
import type {Group} from "../symbols";
// Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js.
// NOTE: `NODETYPE` is constrained by `Group` instead of `NodeType`. This
// guarantees that `group.value` is a string as required by buildCommon.mathsym.
function defineOpFunction<NODETYPE: Group>(
type: NODETYPE,
mathmlNodePostProcessor?: (
mathMLTree.MathNode,
ParseNode<NODETYPE>,
Options) => *,
) {
defineFunctionBuilders({
type,
htmlBuilder(group: ParseNode<NODETYPE>, options) {
const groupValue: string = group.value;
return buildCommon.mathsym(
groupValue, group.mode, options, ["m" + type]);
},
mathmlBuilder(group: ParseNode<NODETYPE>, options) {
const node = new mathMLTree.MathNode(
"mo", [mml.makeText(group.value, group.mode)]);
if (mathmlNodePostProcessor) {
mathmlNodePostProcessor(node, group, options);
}
return node;
},
});
}
defineOpFunction("bin", (mathNode, group, options) => {
const variant = mml.getVariant(group, options);
if (variant === "bold-italic") {
mathNode.setAttribute("mathvariant", variant);
}
});
defineOpFunction("rel");
defineOpFunction("open");
defineOpFunction("close");
defineOpFunction("inner");
defineOpFunction("punct", mathNode => mathNode.setAttribute("separator", "true"));

View File

@@ -0,0 +1,63 @@
// @flow
import {defineFunctionBuilders} from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import * as mml from "../buildMathML";
// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
// src/symbols.js.
const defaultVariant = {
"mi": "italic",
"mn": "normal",
"mtext": "normal",
};
defineFunctionBuilders({
type: "mathord",
htmlBuilder(group, options) {
return buildCommon.makeOrd(group, options, "mathord");
},
mathmlBuilder(group, options) {
const node = new mathMLTree.MathNode(
"mi",
[mml.makeText(group.value, group.mode)]);
const variant = mml.getVariant(group, options) || "italic";
if (variant !== defaultVariant[node.type]) {
node.setAttribute("mathvariant", variant);
}
return node;
},
});
defineFunctionBuilders({
type: "textord",
htmlBuilder(group, options) {
return buildCommon.makeOrd(group, options, "textord");
},
mathmlBuilder(group, options) {
const text = mml.makeText(group.value, group.mode);
const variant = mml.getVariant(group, options) || "normal";
let node;
if (group.mode === 'text') {
node = new mathMLTree.MathNode("mtext", [text]);
} else if (/[0-9]/.test(group.value)) {
// TODO(kevinb) merge adjacent <mn> nodes
// do it as a post processing step
node = new mathMLTree.MathNode("mn", [text]);
} else if (group.value === "\\prime") {
node = new mathMLTree.MathNode("mo", [text]);
} else {
node = new mathMLTree.MathNode("mi", [text]);
}
if (variant !== defaultVariant[node.type]) {
node.setAttribute("mathvariant", variant);
}
return node;
},
});

View File

@@ -24,7 +24,7 @@ type Font = "main" | "ams"
// types for raw text tokens, and we want to avoid conflicts with higher-level
// `ParseNode` types. These `ParseNode`s are constructed within `Parser` by
// looking up the `symbols` map.
type Group =
export type Group =
"accent-token" | "bin" | "close" | "inner" | "mathord" |
"op-token" | "open" | "punct" | "rel" | "spacing" | "textord";
type CharInfoMap = {[string]: {font: Font, group: Group, replace: ?string}};