temml 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|