temml 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/contrib/auto-render/README.md +89 -0
- package/contrib/auto-render/auto-render.js +128 -0
- package/contrib/auto-render/dist/auto-render.js +217 -0
- package/contrib/auto-render/dist/auto-render.min.js +1 -0
- package/contrib/auto-render/splitAtDelimiters.js +84 -0
- package/contrib/auto-render/test/auto-render-spec.js +234 -0
- package/contrib/auto-render/test/auto-render.js +217 -0
- package/contrib/auto-render/test/test_page.html +59 -0
- package/contrib/mhchem/README.md +26 -0
- package/contrib/mhchem/mhchem.js +1705 -0
- package/contrib/mhchem/mhchem.min.js +1 -0
- package/contrib/physics/README.md +20 -0
- package/contrib/physics/physics.js +131 -0
- package/contrib/texvc/README.md +23 -0
- package/contrib/texvc/texvc.js +61 -0
- package/dist/Temml-Asana.css +201 -0
- package/dist/Temml-Latin-Modern.css +216 -0
- package/dist/Temml-Libertinus.css +214 -0
- package/dist/Temml-Local.css +194 -0
- package/dist/Temml-STIX2.css +203 -0
- package/dist/Temml.woff2 +0 -0
- package/dist/temml.cjs +13122 -0
- package/dist/temml.js +11225 -0
- package/dist/temml.min.js +1 -0
- package/dist/temml.mjs +13120 -0
- package/dist/temmlPostProcess.js +70 -0
- package/package.json +34 -0
- package/src/Lexer.js +121 -0
- package/src/MacroExpander.js +437 -0
- package/src/Namespace.js +107 -0
- package/src/ParseError.js +64 -0
- package/src/Parser.js +977 -0
- package/src/Settings.js +49 -0
- package/src/SourceLocation.js +29 -0
- package/src/Style.js +144 -0
- package/src/Token.js +40 -0
- package/src/buildMathML.js +235 -0
- package/src/constants.js +25 -0
- package/src/defineEnvironment.js +25 -0
- package/src/defineFunction.js +69 -0
- package/src/defineMacro.js +11 -0
- package/src/domTree.js +185 -0
- package/src/environments/array.js +791 -0
- package/src/environments/cd.js +252 -0
- package/src/environments.js +8 -0
- package/src/functions/accent.js +127 -0
- package/src/functions/accentunder.js +38 -0
- package/src/functions/arrow.js +204 -0
- package/src/functions/cancelto.js +36 -0
- package/src/functions/char.js +33 -0
- package/src/functions/color.js +253 -0
- package/src/functions/cr.js +46 -0
- package/src/functions/def.js +259 -0
- package/src/functions/delimsizing.js +304 -0
- package/src/functions/enclose.js +193 -0
- package/src/functions/envTag.js +38 -0
- package/src/functions/environment.js +59 -0
- package/src/functions/font.js +123 -0
- package/src/functions/genfrac.js +333 -0
- package/src/functions/hbox.js +29 -0
- package/src/functions/horizBrace.js +32 -0
- package/src/functions/href.js +90 -0
- package/src/functions/html.js +95 -0
- package/src/functions/includegraphics.js +131 -0
- package/src/functions/kern.js +75 -0
- package/src/functions/label.js +29 -0
- package/src/functions/lap.js +75 -0
- package/src/functions/math.js +40 -0
- package/src/functions/mathchoice.js +41 -0
- package/src/functions/mclass.js +201 -0
- package/src/functions/multiscript.js +91 -0
- package/src/functions/not.js +46 -0
- package/src/functions/op.js +338 -0
- package/src/functions/operatorname.js +139 -0
- package/src/functions/ordgroup.js +9 -0
- package/src/functions/phantom.js +73 -0
- package/src/functions/pmb.js +31 -0
- package/src/functions/raise.js +68 -0
- package/src/functions/ref.js +28 -0
- package/src/functions/relax.js +16 -0
- package/src/functions/rule.js +52 -0
- package/src/functions/sizing.js +64 -0
- package/src/functions/smash.js +66 -0
- package/src/functions/sqrt.js +31 -0
- package/src/functions/styling.js +58 -0
- package/src/functions/supsub.js +135 -0
- package/src/functions/symbolsOp.js +53 -0
- package/src/functions/symbolsOrd.js +102 -0
- package/src/functions/symbolsSpacing.js +53 -0
- package/src/functions/tag.js +8 -0
- package/src/functions/text.js +75 -0
- package/src/functions/tip.js +63 -0
- package/src/functions/toggle.js +13 -0
- package/src/functions/verb.js +33 -0
- package/src/functions.js +57 -0
- package/src/linebreaking.js +159 -0
- package/src/macros.js +708 -0
- package/src/mathMLTree.js +175 -0
- package/src/parseNode.js +42 -0
- package/src/parseTree.js +40 -0
- package/src/postProcess.js +57 -0
- package/src/replace.js +225 -0
- package/src/stretchy.js +66 -0
- package/src/svg.js +110 -0
- package/src/symbols.js +972 -0
- package/src/tree.js +50 -0
- package/src/unicodeAccents.js +16 -0
- package/src/unicodeScripts.js +119 -0
- package/src/unicodeSupOrSub.js +108 -0
- package/src/unicodeSymbolBuilder.js +31 -0
- package/src/unicodeSymbols.js +320 -0
- package/src/units.js +109 -0
- package/src/utils.js +109 -0
- package/src/variant.js +103 -0
- package/temml.js +181 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import defineFunction from "../defineFunction";
|
|
2
|
+
import mathMLTree from "../mathMLTree";
|
|
3
|
+
import { invalidIdRegEx } from "./label";
|
|
4
|
+
|
|
5
|
+
defineFunction({
|
|
6
|
+
type: "ref",
|
|
7
|
+
names: ["\\ref", "\\eqref"],
|
|
8
|
+
props: {
|
|
9
|
+
numArgs: 1,
|
|
10
|
+
argTypes: ["raw"]
|
|
11
|
+
},
|
|
12
|
+
handler({ parser, funcName }, args) {
|
|
13
|
+
return {
|
|
14
|
+
type: "ref",
|
|
15
|
+
mode: parser.mode,
|
|
16
|
+
funcName,
|
|
17
|
+
string: args[0].string.replace(invalidIdRegEx, "")
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
mathmlBuilder(group, style) {
|
|
21
|
+
// Create an empty text node. Set a class and an href.
|
|
22
|
+
// The post-processor will populate with the target's tag or equation number.
|
|
23
|
+
const classes = group.funcName === "\\ref" ? ["tml-ref"] : ["tml-ref", "tml-eqref"]
|
|
24
|
+
const node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("")], classes)
|
|
25
|
+
node.setAttribute("href", "#" + group.string)
|
|
26
|
+
return node
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import defineFunction from "../defineFunction"
|
|
2
|
+
|
|
3
|
+
defineFunction({
|
|
4
|
+
type: "internal",
|
|
5
|
+
names: ["\\relax"],
|
|
6
|
+
props: {
|
|
7
|
+
numArgs: 0,
|
|
8
|
+
allowedInText: true
|
|
9
|
+
},
|
|
10
|
+
handler({ parser }) {
|
|
11
|
+
return {
|
|
12
|
+
type: "internal",
|
|
13
|
+
mode: parser.mode
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import defineFunction from "../defineFunction";
|
|
2
|
+
import mathMLTree from "../mathMLTree";
|
|
3
|
+
import { assertNodeType } from "../parseNode";
|
|
4
|
+
import { calculateSize } from "../units";
|
|
5
|
+
|
|
6
|
+
defineFunction({
|
|
7
|
+
type: "rule",
|
|
8
|
+
names: ["\\rule"],
|
|
9
|
+
props: {
|
|
10
|
+
numArgs: 2,
|
|
11
|
+
numOptionalArgs: 1,
|
|
12
|
+
argTypes: ["size", "size", "size"]
|
|
13
|
+
},
|
|
14
|
+
handler({ parser }, args, optArgs) {
|
|
15
|
+
const shift = optArgs[0];
|
|
16
|
+
const width = assertNodeType(args[0], "size");
|
|
17
|
+
const height = assertNodeType(args[1], "size");
|
|
18
|
+
return {
|
|
19
|
+
type: "rule",
|
|
20
|
+
mode: parser.mode,
|
|
21
|
+
shift: shift && assertNodeType(shift, "size").value,
|
|
22
|
+
width: width.value,
|
|
23
|
+
height: height.value
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
mathmlBuilder(group, style) {
|
|
27
|
+
const width = calculateSize(group.width, style);
|
|
28
|
+
const height = calculateSize(group.height, style);
|
|
29
|
+
const shift = group.shift
|
|
30
|
+
? calculateSize(group.shift, style)
|
|
31
|
+
: { number: 0, unit: "em" };
|
|
32
|
+
const color = (style.color && style.getColor()) || "black";
|
|
33
|
+
|
|
34
|
+
const rule = new mathMLTree.MathNode("mspace");
|
|
35
|
+
if (width.number > 0 && height.number > 0) {
|
|
36
|
+
rule.setAttribute("mathbackground", color);
|
|
37
|
+
}
|
|
38
|
+
rule.setAttribute("width", width.number + width.unit);
|
|
39
|
+
rule.setAttribute("height", height.number + height.unit);
|
|
40
|
+
if (shift.number === 0) { return rule }
|
|
41
|
+
|
|
42
|
+
const wrapper = new mathMLTree.MathNode("mpadded", [rule]);
|
|
43
|
+
if (shift.number >= 0) {
|
|
44
|
+
wrapper.setAttribute("height", "+" + shift.number + shift.unit);
|
|
45
|
+
} else {
|
|
46
|
+
wrapper.setAttribute("height", shift.number + shift.unit);
|
|
47
|
+
wrapper.setAttribute("depth", "+" + -shift.number + shift.unit);
|
|
48
|
+
}
|
|
49
|
+
wrapper.setAttribute("voffset", shift.number + shift.unit);
|
|
50
|
+
return wrapper;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import defineFunction from "../defineFunction";
|
|
2
|
+
import { wrapWithMstyle } from "../mathMLTree"
|
|
3
|
+
import * as mml from "../buildMathML";
|
|
4
|
+
|
|
5
|
+
// The size mappings are taken from TeX with \normalsize=10pt.
|
|
6
|
+
// We don't have to track script level. MathML does that.
|
|
7
|
+
const sizeMap = {
|
|
8
|
+
"\\tiny": 0.5,
|
|
9
|
+
"\\sixptsize": 0.6,
|
|
10
|
+
"\\Tiny": 0.6,
|
|
11
|
+
"\\scriptsize": 0.7,
|
|
12
|
+
"\\footnotesize": 0.8,
|
|
13
|
+
"\\small": 0.9,
|
|
14
|
+
"\\normalsize": 1.0,
|
|
15
|
+
"\\large": 1.2,
|
|
16
|
+
"\\Large": 1.44,
|
|
17
|
+
"\\LARGE": 1.728,
|
|
18
|
+
"\\huge": 2.074,
|
|
19
|
+
"\\Huge": 2.488
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
defineFunction({
|
|
23
|
+
type: "sizing",
|
|
24
|
+
names: [
|
|
25
|
+
"\\tiny",
|
|
26
|
+
"\\sixptsize",
|
|
27
|
+
"\\Tiny",
|
|
28
|
+
"\\scriptsize",
|
|
29
|
+
"\\footnotesize",
|
|
30
|
+
"\\small",
|
|
31
|
+
"\\normalsize",
|
|
32
|
+
"\\large",
|
|
33
|
+
"\\Large",
|
|
34
|
+
"\\LARGE",
|
|
35
|
+
"\\huge",
|
|
36
|
+
"\\Huge"
|
|
37
|
+
],
|
|
38
|
+
props: {
|
|
39
|
+
numArgs: 0,
|
|
40
|
+
allowedInText: true
|
|
41
|
+
},
|
|
42
|
+
handler: ({ breakOnTokenText, funcName, parser }, args) => {
|
|
43
|
+
if (parser.settings.strict && parser.mode === "math") {
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`)
|
|
46
|
+
}
|
|
47
|
+
const body = parser.parseExpression(false, breakOnTokenText);
|
|
48
|
+
return {
|
|
49
|
+
type: "sizing",
|
|
50
|
+
mode: parser.mode,
|
|
51
|
+
funcName,
|
|
52
|
+
body
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
mathmlBuilder: (group, style) => {
|
|
56
|
+
const newStyle = style.withFontSize(sizeMap[group.funcName]);
|
|
57
|
+
const inner = mml.buildExpression(group.body, newStyle);
|
|
58
|
+
// Wrap with an <mstyle> element.
|
|
59
|
+
const node = wrapWithMstyle(inner)
|
|
60
|
+
const factor = (sizeMap[group.funcName] / style.fontSize).toFixed(4)
|
|
61
|
+
node.setAttribute("mathsize", factor + "em");
|
|
62
|
+
return node;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// smash, with optional [tb], as in AMS
|
|
2
|
+
import defineFunction from "../defineFunction";
|
|
3
|
+
import mathMLTree from "../mathMLTree";
|
|
4
|
+
import { assertNodeType } from "../parseNode";
|
|
5
|
+
|
|
6
|
+
import * as mml from "../buildMathML";
|
|
7
|
+
|
|
8
|
+
defineFunction({
|
|
9
|
+
type: "smash",
|
|
10
|
+
names: ["\\smash"],
|
|
11
|
+
props: {
|
|
12
|
+
numArgs: 1,
|
|
13
|
+
numOptionalArgs: 1,
|
|
14
|
+
allowedInText: true
|
|
15
|
+
},
|
|
16
|
+
handler: ({ parser }, args, optArgs) => {
|
|
17
|
+
let smashHeight = false;
|
|
18
|
+
let smashDepth = false;
|
|
19
|
+
const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup");
|
|
20
|
+
if (tbArg) {
|
|
21
|
+
// Optional [tb] argument is engaged.
|
|
22
|
+
// ref: amsmath: \renewcommand{\smash}[1][tb]{%
|
|
23
|
+
// def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}%
|
|
24
|
+
let letter = "";
|
|
25
|
+
for (let i = 0; i < tbArg.body.length; ++i) {
|
|
26
|
+
const node = tbArg.body[i];
|
|
27
|
+
// TODO: Write an AssertSymbolNode
|
|
28
|
+
letter = node.text;
|
|
29
|
+
if (letter === "t") {
|
|
30
|
+
smashHeight = true;
|
|
31
|
+
} else if (letter === "b") {
|
|
32
|
+
smashDepth = true;
|
|
33
|
+
} else {
|
|
34
|
+
smashHeight = false;
|
|
35
|
+
smashDepth = false;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
smashHeight = true;
|
|
41
|
+
smashDepth = true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const body = args[0];
|
|
45
|
+
return {
|
|
46
|
+
type: "smash",
|
|
47
|
+
mode: parser.mode,
|
|
48
|
+
body,
|
|
49
|
+
smashHeight,
|
|
50
|
+
smashDepth
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
mathmlBuilder: (group, style) => {
|
|
54
|
+
const node = new mathMLTree.MathNode("mpadded", [mml.buildGroup(group.body, style)]);
|
|
55
|
+
|
|
56
|
+
if (group.smashHeight) {
|
|
57
|
+
node.setAttribute("height", "0px");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (group.smashDepth) {
|
|
61
|
+
node.setAttribute("depth", "0px");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return node;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import defineFunction from "../defineFunction";
|
|
2
|
+
import mathMLTree from "../mathMLTree";
|
|
3
|
+
import * as mml from "../buildMathML";
|
|
4
|
+
|
|
5
|
+
defineFunction({
|
|
6
|
+
type: "sqrt",
|
|
7
|
+
names: ["\\sqrt"],
|
|
8
|
+
props: {
|
|
9
|
+
numArgs: 1,
|
|
10
|
+
numOptionalArgs: 1
|
|
11
|
+
},
|
|
12
|
+
handler({ parser }, args, optArgs) {
|
|
13
|
+
const index = optArgs[0];
|
|
14
|
+
const body = args[0];
|
|
15
|
+
return {
|
|
16
|
+
type: "sqrt",
|
|
17
|
+
mode: parser.mode,
|
|
18
|
+
body,
|
|
19
|
+
index
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
mathmlBuilder(group, style) {
|
|
23
|
+
const { body, index } = group;
|
|
24
|
+
return index
|
|
25
|
+
? new mathMLTree.MathNode("mroot", [
|
|
26
|
+
mml.buildGroup(body, style),
|
|
27
|
+
mml.buildGroup(index, style.incrementLevel())
|
|
28
|
+
])
|
|
29
|
+
: new mathMLTree.MathNode("msqrt", [mml.buildGroup(body, style)]);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import defineFunction from "../defineFunction";
|
|
2
|
+
import { wrapWithMstyle } from "../mathMLTree"
|
|
3
|
+
import * as mml from "../buildMathML";
|
|
4
|
+
|
|
5
|
+
const styleMap = {
|
|
6
|
+
display: 0,
|
|
7
|
+
text: 1,
|
|
8
|
+
script: 2,
|
|
9
|
+
scriptscript: 3
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const styleAttributes = {
|
|
13
|
+
display: ["0", "true"],
|
|
14
|
+
text: ["0", "false"],
|
|
15
|
+
script: ["1", "false"],
|
|
16
|
+
scriptscript: ["2", "false"]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
defineFunction({
|
|
20
|
+
type: "styling",
|
|
21
|
+
names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"],
|
|
22
|
+
props: {
|
|
23
|
+
numArgs: 0,
|
|
24
|
+
allowedInText: true,
|
|
25
|
+
primitive: true
|
|
26
|
+
},
|
|
27
|
+
handler({ breakOnTokenText, funcName, parser }, args) {
|
|
28
|
+
// parse out the implicit body
|
|
29
|
+
const body = parser.parseExpression(true, breakOnTokenText);
|
|
30
|
+
|
|
31
|
+
const scriptLevel = funcName.slice(1, funcName.length - 5);
|
|
32
|
+
return {
|
|
33
|
+
type: "styling",
|
|
34
|
+
mode: parser.mode,
|
|
35
|
+
// Figure out what scriptLevel to use by pulling out the scriptLevel from
|
|
36
|
+
// the function name
|
|
37
|
+
scriptLevel,
|
|
38
|
+
body
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
mathmlBuilder(group, style) {
|
|
42
|
+
// Figure out what scriptLevel we're changing to.
|
|
43
|
+
const newStyle = style.withLevel(styleMap[group.scriptLevel]);
|
|
44
|
+
// The style argument in the next line does NOT directly set a MathML script level.
|
|
45
|
+
// It just tracks the style level, in case we need to know it for supsub or mathchoice.
|
|
46
|
+
const inner = mml.buildExpression(group.body, newStyle);
|
|
47
|
+
// Wrap with an <mstyle> element.
|
|
48
|
+
const node = wrapWithMstyle(inner)
|
|
49
|
+
|
|
50
|
+
const attr = styleAttributes[group.scriptLevel];
|
|
51
|
+
|
|
52
|
+
// Here is where we set the MathML script level.
|
|
53
|
+
node.setAttribute("scriptlevel", attr[0]);
|
|
54
|
+
node.setAttribute("displaystyle", attr[1]);
|
|
55
|
+
|
|
56
|
+
return node;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { defineFunctionBuilders } from "../defineFunction"
|
|
2
|
+
import { StyleLevel } from "../constants"
|
|
3
|
+
import mathMLTree from "../mathMLTree"
|
|
4
|
+
import * as mml from "../buildMathML"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sometimes, groups perform special rules when they have superscripts or
|
|
8
|
+
* subscripts attached to them. This function lets the `supsub` group know that
|
|
9
|
+
* Sometimes, groups perform special rules when they have superscripts or
|
|
10
|
+
* its inner element should handle the superscripts and subscripts instead of
|
|
11
|
+
* handling them itself.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Helpers
|
|
15
|
+
const symbolRegEx = /^m(over|under|underover)$/
|
|
16
|
+
|
|
17
|
+
// Super scripts and subscripts, whose precise placement can depend on other
|
|
18
|
+
// functions that precede them.
|
|
19
|
+
defineFunctionBuilders({
|
|
20
|
+
type: "supsub",
|
|
21
|
+
mathmlBuilder(group, style) {
|
|
22
|
+
// Is the inner group a relevant horizonal brace?
|
|
23
|
+
let isBrace = false
|
|
24
|
+
let isOver
|
|
25
|
+
let isSup
|
|
26
|
+
let appendApplyFunction = false
|
|
27
|
+
let needsLeadingSpace = false
|
|
28
|
+
|
|
29
|
+
if (group.base && group.base.type === "horizBrace") {
|
|
30
|
+
isSup = !!group.sup
|
|
31
|
+
if (isSup === group.base.isOver) {
|
|
32
|
+
isBrace = true
|
|
33
|
+
isOver = group.base.isOver
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (group.base && !group.base.stack &&
|
|
38
|
+
(group.base.type === "op" || group.base.type === "operatorname")) {
|
|
39
|
+
group.base.parentIsSupSub = true
|
|
40
|
+
appendApplyFunction = !group.base.symbol
|
|
41
|
+
needsLeadingSpace = group.base.needsLeadingSpace
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const children = group.base && group.base.stack
|
|
45
|
+
? [mml.buildGroup(group.base.body[0], style)]
|
|
46
|
+
: [mml.buildGroup(group.base, style)]
|
|
47
|
+
|
|
48
|
+
const childStyle = style.inSubOrSup()
|
|
49
|
+
if (group.sub) {
|
|
50
|
+
children.push(mml.buildGroup(group.sub, childStyle))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (group.sup) {
|
|
54
|
+
children.push(mml.buildGroup(group.sup, childStyle))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let nodeType;
|
|
58
|
+
if (isBrace) {
|
|
59
|
+
nodeType = isOver ? "mover" : "munder"
|
|
60
|
+
} else if (!group.sub) {
|
|
61
|
+
const base = group.base
|
|
62
|
+
if (
|
|
63
|
+
base &&
|
|
64
|
+
base.type === "op" &&
|
|
65
|
+
base.limits &&
|
|
66
|
+
(style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
|
|
67
|
+
) {
|
|
68
|
+
nodeType = "mover"
|
|
69
|
+
} else if (
|
|
70
|
+
base &&
|
|
71
|
+
base.type === "operatorname" &&
|
|
72
|
+
base.alwaysHandleSupSub &&
|
|
73
|
+
(base.limits || style.level === StyleLevel.DISPLAY)
|
|
74
|
+
) {
|
|
75
|
+
nodeType = "mover"
|
|
76
|
+
} else {
|
|
77
|
+
nodeType = "msup"
|
|
78
|
+
}
|
|
79
|
+
} else if (!group.sup) {
|
|
80
|
+
const base = group.base;
|
|
81
|
+
if (
|
|
82
|
+
base &&
|
|
83
|
+
base.type === "op" &&
|
|
84
|
+
base.limits &&
|
|
85
|
+
(style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
|
|
86
|
+
) {
|
|
87
|
+
nodeType = "munder";
|
|
88
|
+
} else if (
|
|
89
|
+
base &&
|
|
90
|
+
base.type === "operatorname" &&
|
|
91
|
+
base.alwaysHandleSupSub &&
|
|
92
|
+
(base.limits || style.level === StyleLevel.DISPLAY)
|
|
93
|
+
) {
|
|
94
|
+
nodeType = "munder"
|
|
95
|
+
} else {
|
|
96
|
+
nodeType = "msub"
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
const base = group.base;
|
|
100
|
+
if (base && ((base.type === "op" && base.limits) || base.type === "multiscript") &&
|
|
101
|
+
(style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
|
|
102
|
+
) {
|
|
103
|
+
nodeType = "munderover"
|
|
104
|
+
} else if (
|
|
105
|
+
base &&
|
|
106
|
+
base.type === "operatorname" &&
|
|
107
|
+
base.alwaysHandleSupSub &&
|
|
108
|
+
(style.level === StyleLevel.DISPLAY || base.limits)
|
|
109
|
+
) {
|
|
110
|
+
nodeType = "munderover"
|
|
111
|
+
} else {
|
|
112
|
+
nodeType = "msubsup"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let node = new mathMLTree.MathNode(nodeType, children)
|
|
117
|
+
if (appendApplyFunction) {
|
|
118
|
+
// Append an <mo>⁡</mo>.
|
|
119
|
+
// ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
|
|
120
|
+
const operator = new mathMLTree.MathNode("mo", [mml.makeText("\u2061", "text")])
|
|
121
|
+
if (needsLeadingSpace) {
|
|
122
|
+
const space = new mathMLTree.MathNode("mspace")
|
|
123
|
+
space.setAttribute("width", "0.1667em") // thin space.
|
|
124
|
+
node = mathMLTree.newDocumentFragment([space, node, operator])
|
|
125
|
+
} else {
|
|
126
|
+
node = mathMLTree.newDocumentFragment([node, operator])
|
|
127
|
+
}
|
|
128
|
+
} else if (symbolRegEx.test(nodeType)) {
|
|
129
|
+
// Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
|
|
130
|
+
node = new mathMLTree.MathNode("mrow", [node])
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return node
|
|
134
|
+
}
|
|
135
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineFunctionBuilders } from "../defineFunction";
|
|
2
|
+
import mathMLTree from "../mathMLTree";
|
|
3
|
+
import * as mml from "../buildMathML";
|
|
4
|
+
|
|
5
|
+
// Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js.
|
|
6
|
+
|
|
7
|
+
const short = ["\\shortmid", "\\nshortmid", "\\shortparallel",
|
|
8
|
+
"\\nshortparallel", "\\smallsetminus"]
|
|
9
|
+
|
|
10
|
+
const arrows = ["\\Rsh", "\\Lsh", "\\restriction"]
|
|
11
|
+
|
|
12
|
+
const isArrow = str => {
|
|
13
|
+
if (str.length === 1) {
|
|
14
|
+
const codePoint = str.codePointAt(0)
|
|
15
|
+
return (0x218f < codePoint && codePoint < 0x2200)
|
|
16
|
+
}
|
|
17
|
+
return str.indexOf("arrow") > -1 || str.indexOf("harpoon") > -1 || arrows.includes(str)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
defineFunctionBuilders({
|
|
21
|
+
type: "atom",
|
|
22
|
+
mathmlBuilder(group, style) {
|
|
23
|
+
const node = new mathMLTree.MathNode("mo", [mml.makeText(group.text, group.mode)]);
|
|
24
|
+
if (group.family === "punct") {
|
|
25
|
+
node.setAttribute("separator", "true");
|
|
26
|
+
} else if (group.family === "open" || group.family === "close") {
|
|
27
|
+
// Delims built here should not stretch vertically.
|
|
28
|
+
// See delimsizing.js for stretchy delims.
|
|
29
|
+
if (group.family === "open") {
|
|
30
|
+
node.setAttribute("form", "prefix")
|
|
31
|
+
// Set an explicit attribute for stretch. Otherwise Firefox may do it wrong.
|
|
32
|
+
node.setAttribute("stretchy", "false")
|
|
33
|
+
} else if (group.family === "close") {
|
|
34
|
+
node.setAttribute("form", "postfix");
|
|
35
|
+
node.setAttribute("stretchy", "false")
|
|
36
|
+
}
|
|
37
|
+
} else if (group.text === "\\mid") {
|
|
38
|
+
// Firefox messes up this spacing if at the end of an <mrow>. See it explicitly.
|
|
39
|
+
node.setAttribute("lspace", "0.22em") // medium space
|
|
40
|
+
node.setAttribute("rspace", "0.22em")
|
|
41
|
+
node.setAttribute("stretchy", "false")
|
|
42
|
+
} else if (group.family === "rel" && isArrow(group.text)) {
|
|
43
|
+
node.setAttribute("stretchy", "false")
|
|
44
|
+
} else if (short.includes(group.text)) {
|
|
45
|
+
node.setAttribute("mathsize", "70%")
|
|
46
|
+
} else if (group.text === ":") {
|
|
47
|
+
// ":" is not in the MathML operator dictionary. Give it BIN spacing.
|
|
48
|
+
node.attributes.lspace = "0.2222em"
|
|
49
|
+
node.attributes.rspace = "0.2222em"
|
|
50
|
+
}
|
|
51
|
+
return node;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { defineFunctionBuilders } from "../defineFunction"
|
|
2
|
+
import { getVariant } from "../variant"
|
|
3
|
+
import { variantChar, smallCaps } from "../replace"
|
|
4
|
+
import mathMLTree from "../mathMLTree"
|
|
5
|
+
import * as mml from "../buildMathML"
|
|
6
|
+
|
|
7
|
+
// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
|
|
8
|
+
// src/symbols.js.
|
|
9
|
+
|
|
10
|
+
const numberRegEx = /^\d(?:[\d,.]*\d)?$/ // Keep in sync with numberRegEx in Parser.js
|
|
11
|
+
const latinRegEx = /[A-Ba-z]/
|
|
12
|
+
|
|
13
|
+
const italicNumber = (text, variant) => {
|
|
14
|
+
const mn = new mathMLTree.MathNode("mn", [text])
|
|
15
|
+
const wrapper = new mathMLTree.MathNode("mstyle", [mn])
|
|
16
|
+
wrapper.style["font-style"] = "italic"
|
|
17
|
+
wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif"
|
|
18
|
+
if (variant === "bold-italic") { wrapper.style["font-weight"] = "bold" }
|
|
19
|
+
return wrapper
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
defineFunctionBuilders({
|
|
23
|
+
type: "mathord",
|
|
24
|
+
mathmlBuilder(group, style) {
|
|
25
|
+
const text = mml.makeText(group.text, group.mode, style)
|
|
26
|
+
const codePoint = text.text.codePointAt(0)
|
|
27
|
+
// Test for upper-case Greek
|
|
28
|
+
const defaultVariant = (0x0390 < codePoint && codePoint < 0x03aa) ? "normal" : "italic"
|
|
29
|
+
const variant = getVariant(group, style) || defaultVariant
|
|
30
|
+
if (variant === "script") {
|
|
31
|
+
text.text = variantChar(text.text, variant)
|
|
32
|
+
return new mathMLTree.MathNode("mi", [text], [style.font])
|
|
33
|
+
} else if (variant !== "italic") {
|
|
34
|
+
text.text = variantChar(text.text, variant)
|
|
35
|
+
}
|
|
36
|
+
let node = new mathMLTree.MathNode("mi", [text])
|
|
37
|
+
// TODO: Handle U+1D49C - U+1D4CF per https://www.unicode.org/charts/PDF/U1D400.pdf
|
|
38
|
+
if (variant === "normal") {
|
|
39
|
+
node.setAttribute("mathvariant", "normal")
|
|
40
|
+
if (text.text.length === 1) {
|
|
41
|
+
// A Firefox bug will apply spacing here, but there should be none. Fix it.
|
|
42
|
+
node = new mathMLTree.MathNode("mrow", [node])
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return node
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
defineFunctionBuilders({
|
|
50
|
+
type: "textord",
|
|
51
|
+
mathmlBuilder(group, style) {
|
|
52
|
+
let ch = group.text
|
|
53
|
+
const codePoint = ch.codePointAt(0)
|
|
54
|
+
if (style.fontFamily === "textsc") {
|
|
55
|
+
// Convert small latin letters to small caps.
|
|
56
|
+
if (96 < codePoint && codePoint < 123) {
|
|
57
|
+
ch = smallCaps[ch]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const text = mml.makeText(ch, group.mode, style)
|
|
61
|
+
const variant = getVariant(group, style) || "normal"
|
|
62
|
+
|
|
63
|
+
let node
|
|
64
|
+
if (group.mode === "text") {
|
|
65
|
+
if (variant === "italic" || variant === "bold-italic") {
|
|
66
|
+
if (numberRegEx.test(group.text)) {
|
|
67
|
+
return italicNumber(text, variant)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (variant !== "normal") {
|
|
71
|
+
text.text = variantChar(text.text, variant)
|
|
72
|
+
}
|
|
73
|
+
node = new mathMLTree.MathNode("mtext", [text])
|
|
74
|
+
} else if (numberRegEx.test(group.text)) {
|
|
75
|
+
if (variant === "oldstylenums") {
|
|
76
|
+
const ms = new mathMLTree.MathNode("mstyle", [text], ["oldstylenums"])
|
|
77
|
+
node = new mathMLTree.MathNode("mn", [ms])
|
|
78
|
+
} else if (variant === "italic" || variant === "bold-italic") {
|
|
79
|
+
return italicNumber(text, variant)
|
|
80
|
+
} else {
|
|
81
|
+
if (variant !== "normal") {
|
|
82
|
+
text.text = text.text.split("").map(c => variantChar(c, variant)).join("")
|
|
83
|
+
}
|
|
84
|
+
node = new mathMLTree.MathNode("mn", [text])
|
|
85
|
+
}
|
|
86
|
+
} else if (group.text === "\\prime") {
|
|
87
|
+
node = new mathMLTree.MathNode("mo", [text])
|
|
88
|
+
// TODO: If/when Chromium uses ssty variant for prime, remove the next line.
|
|
89
|
+
node.classes.push("tml-prime")
|
|
90
|
+
} else {
|
|
91
|
+
const origText = text.text
|
|
92
|
+
if (variant !== "italic") {
|
|
93
|
+
text.text = variantChar(text.text, variant)
|
|
94
|
+
}
|
|
95
|
+
node = new mathMLTree.MathNode("mi", [text])
|
|
96
|
+
if (text.text === origText && latinRegEx.test(origText)) {
|
|
97
|
+
node.setAttribute("mathvariant", "italic")
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return node
|
|
101
|
+
}
|
|
102
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineFunctionBuilders } from "../defineFunction"
|
|
2
|
+
import mathMLTree from "../mathMLTree"
|
|
3
|
+
import ParseError from "../ParseError"
|
|
4
|
+
|
|
5
|
+
// A map of CSS-based spacing functions to their CSS class.
|
|
6
|
+
const cssSpace = {
|
|
7
|
+
"\\nobreak": "nobreak",
|
|
8
|
+
"\\allowbreak": "allowbreak"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// A lookup table to determine whether a spacing function/symbol should be
|
|
12
|
+
// treated like a regular space character. If a symbol or command is a key
|
|
13
|
+
// in this table, then it should be a regular space character. Furthermore,
|
|
14
|
+
// the associated value may have a `className` specifying an extra CSS class
|
|
15
|
+
// to add to the created `span`.
|
|
16
|
+
const regularSpace = {
|
|
17
|
+
" ": {},
|
|
18
|
+
"\\ ": {},
|
|
19
|
+
"~": {
|
|
20
|
+
className: "nobreak"
|
|
21
|
+
},
|
|
22
|
+
"\\space": {},
|
|
23
|
+
"\\nobreakspace": {
|
|
24
|
+
className: "nobreak"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in
|
|
29
|
+
// src/symbols.js.
|
|
30
|
+
defineFunctionBuilders({
|
|
31
|
+
type: "spacing",
|
|
32
|
+
mathmlBuilder(group, style) {
|
|
33
|
+
let node
|
|
34
|
+
|
|
35
|
+
if (Object.prototype.hasOwnProperty.call(regularSpace, group.text)) {
|
|
36
|
+
// Firefox does not render a space in a <mtext> </mtext>. So write a no-break space.
|
|
37
|
+
// TODO: If Firefox fixes that bug, uncomment the next line and write ch into the node.
|
|
38
|
+
//const ch = (regularSpace[group.text].className === "nobreak") ? "\u00a0" : " "
|
|
39
|
+
node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("\u00a0")])
|
|
40
|
+
} else if (Object.prototype.hasOwnProperty.call(cssSpace, group.text)) {
|
|
41
|
+
// MathML 3.0 calls for nobreak to occur in an <mo>, not an <mtext>
|
|
42
|
+
// Ref: https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs
|
|
43
|
+
node = new mathMLTree.MathNode("mo")
|
|
44
|
+
if (group.text === "\\nobreak") {
|
|
45
|
+
node.setAttribute("linebreak", "nobreak")
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
throw new ParseError(`Unknown type of space "${group.text}"`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return node
|
|
52
|
+
}
|
|
53
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineFunctionBuilders } from "../defineFunction";
|
|
2
|
+
|
|
3
|
+
defineFunctionBuilders({
|
|
4
|
+
type: "tag"
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
// For a \tag, the work usually done in a mathmlBuilder is instead done in buildMathML.js.
|
|
8
|
+
// That way, a \tag can be pulled out of the parse tree and wrapped around the outer node.
|