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
package/src/Settings.js
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
/**
|
2
|
+
* This is a module for storing settings passed into Temml. It correctly handles
|
3
|
+
* default settings.
|
4
|
+
*/
|
5
|
+
|
6
|
+
import utils from "./utils";
|
7
|
+
|
8
|
+
/**
|
9
|
+
* The main Settings object
|
10
|
+
*/
|
11
|
+
export default class Settings {
|
12
|
+
constructor(options) {
|
13
|
+
// allow null options
|
14
|
+
options = options || {};
|
15
|
+
this.displayMode = utils.deflt(options.displayMode, false); // boolean
|
16
|
+
this.annotate = utils.deflt(options.annotate, false) // boolean
|
17
|
+
this.leqno = utils.deflt(options.leqno, false); // boolean
|
18
|
+
this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
|
19
|
+
this.macros = options.macros || {};
|
20
|
+
this.wrap = utils.deflt(options.wrap, "none") // "none" | "tex" | "="
|
21
|
+
this.xml = utils.deflt(options.xml, false); // boolean
|
22
|
+
this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
|
23
|
+
this.strict = utils.deflt(options.strict, false); // boolean
|
24
|
+
this.trust = utils.deflt(options.trust, false); // trust context. See html.js.
|
25
|
+
this.maxSize = (options.maxSize === undefined
|
26
|
+
? [Infinity, Infinity]
|
27
|
+
: Array.isArray(options.maxSize)
|
28
|
+
? options.maxSize
|
29
|
+
: [Infinity, Infinity]
|
30
|
+
)
|
31
|
+
this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000)); // number
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Check whether to test potentially dangerous input, and return
|
36
|
+
* `true` (trusted) or `false` (untrusted). The sole argument `context`
|
37
|
+
* should be an object with `command` field specifying the relevant LaTeX
|
38
|
+
* command (as a string starting with `\`), and any other arguments, etc.
|
39
|
+
* If `context` has a `url` field, a `protocol` field will automatically
|
40
|
+
* get added by this function (changing the specified object).
|
41
|
+
*/
|
42
|
+
isTrusted(context) {
|
43
|
+
if (context.url && !context.protocol) {
|
44
|
+
context.protocol = utils.protocolFromUrl(context.url);
|
45
|
+
}
|
46
|
+
const trust = typeof this.trust === "function" ? this.trust(context) : this.trust;
|
47
|
+
return Boolean(trust);
|
48
|
+
}
|
49
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/**
|
2
|
+
* Lexing or parsing positional information for error reporting.
|
3
|
+
* This object is immutable.
|
4
|
+
*/
|
5
|
+
export default class SourceLocation {
|
6
|
+
constructor(lexer, start, end) {
|
7
|
+
this.lexer = lexer; // Lexer holding the input string.
|
8
|
+
this.start = start; // Start offset, zero-based inclusive.
|
9
|
+
this.end = end; // End offset, zero-based exclusive.
|
10
|
+
}
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Merges two `SourceLocation`s from location providers, given they are
|
14
|
+
* provided in order of appearance.
|
15
|
+
* - Returns the first one's location if only the first is provided.
|
16
|
+
* - Returns a merged range of the first and the last if both are provided
|
17
|
+
* and their lexers match.
|
18
|
+
* - Otherwise, returns null.
|
19
|
+
*/
|
20
|
+
static range(first, second) {
|
21
|
+
if (!second) {
|
22
|
+
return first && first.loc;
|
23
|
+
} else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) {
|
24
|
+
return null;
|
25
|
+
} else {
|
26
|
+
return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
package/src/Style.js
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
/**
|
2
|
+
* This file contains information about the style that the mathmlBuilder carries
|
3
|
+
* around with it. Data is held in an `Style` object, and when
|
4
|
+
* recursing, a new `Style` object can be created with the `.with*` functions.
|
5
|
+
*/
|
6
|
+
|
7
|
+
const subOrSupLevel = [2, 2, 3, 3];
|
8
|
+
|
9
|
+
/**
|
10
|
+
* This is the main Style class. It contains the current style.level, color, and font.
|
11
|
+
*
|
12
|
+
* Style objects should not be modified. To create a new Style with
|
13
|
+
* different properties, call a `.with*` method.
|
14
|
+
*/
|
15
|
+
class Style {
|
16
|
+
constructor(data) {
|
17
|
+
// Style.level can be 0 | 1 | 2 | 3, which correspond to
|
18
|
+
// displaystyle, textstyle, scriptstyle, and scriptscriptstyle.
|
19
|
+
// style.level does not directly set MathML's script level. MathML does that itself.
|
20
|
+
// We use style.level to track, not set, math style so that we can get the
|
21
|
+
// correct scriptlevel when needed in supsub.js, mathchoice.js, or for dimensions in em.
|
22
|
+
this.level = data.level;
|
23
|
+
this.color = data.color; // string | void
|
24
|
+
// A font family applies to a group of fonts (i.e. SansSerif), while a font
|
25
|
+
// represents a specific font (i.e. SansSerif Bold).
|
26
|
+
// See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm
|
27
|
+
this.font = data.font || ""; // string
|
28
|
+
this.fontFamily = data.fontFamily || ""; // string
|
29
|
+
this.fontSize = data.fontSize || 1.0; // number
|
30
|
+
this.fontWeight = data.fontWeight || "";
|
31
|
+
this.fontShape = data.fontShape || "";
|
32
|
+
this.maxSize = data.maxSize; // [number, number]
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Returns a new style object with the same properties as "this". Properties
|
37
|
+
* from "extension" will be copied to the new style object.
|
38
|
+
*/
|
39
|
+
extend(extension) {
|
40
|
+
const data = {
|
41
|
+
level: this.level,
|
42
|
+
color: this.color,
|
43
|
+
font: this.font,
|
44
|
+
fontFamily: this.fontFamily,
|
45
|
+
fontSize: this.fontSize,
|
46
|
+
fontWeight: this.fontWeight,
|
47
|
+
fontShape: this.fontShape,
|
48
|
+
maxSize: this.maxSize
|
49
|
+
};
|
50
|
+
|
51
|
+
for (const key in extension) {
|
52
|
+
if (Object.prototype.hasOwnProperty.call(extension, key)) {
|
53
|
+
data[key] = extension[key];
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
return new Style(data);
|
58
|
+
}
|
59
|
+
|
60
|
+
withLevel(n) {
|
61
|
+
return this.extend({
|
62
|
+
level: n
|
63
|
+
});
|
64
|
+
}
|
65
|
+
|
66
|
+
incrementLevel() {
|
67
|
+
return this.extend({
|
68
|
+
level: Math.min(this.level + 1, 3)
|
69
|
+
});
|
70
|
+
}
|
71
|
+
|
72
|
+
inSubOrSup() {
|
73
|
+
return this.extend({
|
74
|
+
level: subOrSupLevel[this.level]
|
75
|
+
})
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Create a new style object with the given color.
|
80
|
+
*/
|
81
|
+
withColor(color) {
|
82
|
+
return this.extend({
|
83
|
+
color: color
|
84
|
+
});
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Creates a new style object with the given math font or old text font.
|
89
|
+
* @type {[type]}
|
90
|
+
*/
|
91
|
+
withFont(font) {
|
92
|
+
return this.extend({
|
93
|
+
font
|
94
|
+
});
|
95
|
+
}
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Create a new style objects with the given fontFamily.
|
99
|
+
*/
|
100
|
+
withTextFontFamily(fontFamily) {
|
101
|
+
return this.extend({
|
102
|
+
fontFamily,
|
103
|
+
font: ""
|
104
|
+
});
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Creates a new style object with the given font size
|
109
|
+
*/
|
110
|
+
withFontSize(num) {
|
111
|
+
return this.extend({
|
112
|
+
fontSize: num
|
113
|
+
});
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Creates a new style object with the given font weight
|
118
|
+
*/
|
119
|
+
withTextFontWeight(fontWeight) {
|
120
|
+
return this.extend({
|
121
|
+
fontWeight,
|
122
|
+
font: ""
|
123
|
+
});
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* Creates a new style object with the given font weight
|
128
|
+
*/
|
129
|
+
withTextFontShape(fontShape) {
|
130
|
+
return this.extend({
|
131
|
+
fontShape,
|
132
|
+
font: ""
|
133
|
+
});
|
134
|
+
}
|
135
|
+
|
136
|
+
/**
|
137
|
+
* Gets the CSS color of the current style object
|
138
|
+
*/
|
139
|
+
getColor() {
|
140
|
+
return this.color;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
export default Style;
|
package/src/Token.js
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
import SourceLocation from "./SourceLocation";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Interface required to break circular dependency between Token, Lexer, and
|
5
|
+
* ParseError.
|
6
|
+
*/
|
7
|
+
|
8
|
+
/**
|
9
|
+
* The resulting token returned from `lex`.
|
10
|
+
*
|
11
|
+
* It consists of the token text plus some position information.
|
12
|
+
* The position information is essentially a range in an input string,
|
13
|
+
* but instead of referencing the bare input string, we refer to the lexer.
|
14
|
+
* That way it is possible to attach extra metadata to the input string,
|
15
|
+
* like for example a file name or similar.
|
16
|
+
*
|
17
|
+
* The position information is optional, so it is OK to construct synthetic
|
18
|
+
* tokens if appropriate. Not providing available position information may
|
19
|
+
* lead to degraded error reporting, though.
|
20
|
+
*/
|
21
|
+
export class Token {
|
22
|
+
constructor(
|
23
|
+
text, // the text of this token
|
24
|
+
loc
|
25
|
+
) {
|
26
|
+
this.text = text;
|
27
|
+
this.loc = loc;
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Given a pair of tokens (this and endToken), compute a `Token` encompassing
|
32
|
+
* the whole input range enclosed by these two.
|
33
|
+
*/
|
34
|
+
range(
|
35
|
+
endToken, // last token of the range, inclusive
|
36
|
+
text // the text of the newly constructed token
|
37
|
+
) {
|
38
|
+
return new Token(text, SourceLocation.range(this, endToken));
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,235 @@
|
|
1
|
+
/**
|
2
|
+
* This file converts a parse tree into a cooresponding MathML tree. The main
|
3
|
+
* entry point is the `buildMathML` function, which takes a parse tree from the
|
4
|
+
* parser.
|
5
|
+
*/
|
6
|
+
|
7
|
+
import mathMLTree from "./mathMLTree"
|
8
|
+
import ParseError from "./ParseError"
|
9
|
+
import symbols, { ligatures } from "./symbols"
|
10
|
+
import { _mathmlGroupBuilders as groupBuilders } from "./defineFunction"
|
11
|
+
import { MathNode } from "./mathMLTree"
|
12
|
+
import setLineBreaks from "./linebreaking"
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Takes a symbol and converts it into a MathML text node after performing
|
16
|
+
* optional replacement from symbols.js.
|
17
|
+
*/
|
18
|
+
export const makeText = function(text, mode, style) {
|
19
|
+
if (
|
20
|
+
symbols[mode][text] &&
|
21
|
+
symbols[mode][text].replace &&
|
22
|
+
text.charCodeAt(0) !== 0xd835 &&
|
23
|
+
!(
|
24
|
+
Object.prototype.hasOwnProperty.call(ligatures, text) &&
|
25
|
+
style &&
|
26
|
+
((style.fontFamily && style.fontFamily.slice(4, 6) === "tt") ||
|
27
|
+
(style.font && style.font.slice(4, 6) === "tt"))
|
28
|
+
)
|
29
|
+
) {
|
30
|
+
text = symbols[mode][text].replace;
|
31
|
+
}
|
32
|
+
|
33
|
+
return new mathMLTree.TextNode(text);
|
34
|
+
};
|
35
|
+
|
36
|
+
export const consolidateText = mrow => {
|
37
|
+
// If possible, consolidate adjacent <mtext> elements into a single element.
|
38
|
+
if (mrow.type !== "mrow") { return mrow }
|
39
|
+
if (mrow.children.length === 0) { return mrow } // empty group, e.g., \text{}
|
40
|
+
if (!mrow.children[0].attributes || mrow.children[0].type !== "mtext") { return mrow }
|
41
|
+
const variant = mrow.children[0].attributes.mathvariant || ""
|
42
|
+
const mtext = new mathMLTree.MathNode(
|
43
|
+
"mtext",
|
44
|
+
[new mathMLTree.TextNode(mrow.children[0].children[0].text)]
|
45
|
+
)
|
46
|
+
for (let i = 1; i < mrow.children.length; i++) {
|
47
|
+
// Check each child and, if possible, copy the character into child[0].
|
48
|
+
const localVariant = mrow.children[i].attributes.mathvariant || ""
|
49
|
+
if (mrow.children[i].type === "mrow") {
|
50
|
+
const childRow = mrow.children[i]
|
51
|
+
for (let j = 0; j < childRow.children.length; j++) {
|
52
|
+
// We'll also check the children of a mrow. One level only. No recursion.
|
53
|
+
const childVariant = childRow.children[j].attributes.mathvariant || ""
|
54
|
+
if (childVariant !== variant || childRow.children[j].type !== "mtext") {
|
55
|
+
return mrow // At least one element cannot be consolidated. Get out.
|
56
|
+
} else {
|
57
|
+
mtext.children[0].text += childRow.children[j].children[0].text
|
58
|
+
}
|
59
|
+
}
|
60
|
+
} else if (localVariant !== variant || mrow.children[i].type !== "mtext") {
|
61
|
+
return mrow
|
62
|
+
} else {
|
63
|
+
mtext.children[0].text += mrow.children[i].children[0].text
|
64
|
+
}
|
65
|
+
}
|
66
|
+
// Firefox does not render a space at either end of an <mtext> string.
|
67
|
+
// To get proper rendering, we replace leading or trailing spaces with no-break spaces.
|
68
|
+
if (mtext.children[0].text.charAt(0) === " ") {
|
69
|
+
mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1)
|
70
|
+
}
|
71
|
+
const L = mtext.children[0].text.length
|
72
|
+
if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") {
|
73
|
+
mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0"
|
74
|
+
}
|
75
|
+
return mtext
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Wrap the given array of nodes in an <mrow> node if needed, i.e.,
|
80
|
+
* unless the array has length 1. Always returns a single node.
|
81
|
+
*/
|
82
|
+
export const makeRow = function(body) {
|
83
|
+
if (body.length === 1) {
|
84
|
+
return body[0];
|
85
|
+
} else {
|
86
|
+
return new mathMLTree.MathNode("mrow", body);
|
87
|
+
}
|
88
|
+
};
|
89
|
+
|
90
|
+
const isRel = item => {
|
91
|
+
return (item.type === "atom" && item.family === "rel") ||
|
92
|
+
(item.type === "mclass" && item.mclass === "mrel")
|
93
|
+
}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Takes a list of nodes, builds them, and returns a list of the generated
|
97
|
+
* MathML nodes. Also do a couple chores along the way:
|
98
|
+
* (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
|
99
|
+
* (2) Suppress spacing between two adjacent relations.
|
100
|
+
*/
|
101
|
+
export const buildExpression = function(expression, style, isOrdgroup) {
|
102
|
+
if (expression.length === 1) {
|
103
|
+
const group = buildGroup(expression[0], style);
|
104
|
+
if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
|
105
|
+
// When TeX writers want to suppress spacing on an operator,
|
106
|
+
// they often put the operator by itself inside braces.
|
107
|
+
group.setAttribute("lspace", "0em");
|
108
|
+
group.setAttribute("rspace", "0em");
|
109
|
+
}
|
110
|
+
return [group];
|
111
|
+
}
|
112
|
+
|
113
|
+
const groups = [];
|
114
|
+
for (let i = 0; i < expression.length; i++) {
|
115
|
+
const group = buildGroup(expression[i], style);
|
116
|
+
// Suppress spacing between adjacent relations
|
117
|
+
if (i < expression.length - 1 && isRel(expression[i]) && isRel(expression[i + 1])) {
|
118
|
+
group.setAttribute("rspace", "0em")
|
119
|
+
}
|
120
|
+
if (i > 0 && isRel(expression[i]) && isRel(expression[i - 1])) {
|
121
|
+
group.setAttribute("lspace", "0em")
|
122
|
+
}
|
123
|
+
groups.push(group);
|
124
|
+
}
|
125
|
+
return groups;
|
126
|
+
};
|
127
|
+
|
128
|
+
/**
|
129
|
+
* Equivalent to buildExpression, but wraps the elements in an <mrow>
|
130
|
+
* if there's more than one. Returns a single node instead of an array.
|
131
|
+
*/
|
132
|
+
export const buildExpressionRow = function(expression, style, isOrdgroup) {
|
133
|
+
return makeRow(buildExpression(expression, style, isOrdgroup));
|
134
|
+
};
|
135
|
+
|
136
|
+
/**
|
137
|
+
* Takes a group from the parser and calls the appropriate groupBuilders function
|
138
|
+
* on it to produce a MathML node.
|
139
|
+
*/
|
140
|
+
export const buildGroup = function(group, style) {
|
141
|
+
if (!group) {
|
142
|
+
return new mathMLTree.MathNode("mrow");
|
143
|
+
}
|
144
|
+
|
145
|
+
if (groupBuilders[group.type]) {
|
146
|
+
// Call the groupBuilders function
|
147
|
+
const result = groupBuilders[group.type](group, style);
|
148
|
+
return result;
|
149
|
+
} else {
|
150
|
+
throw new ParseError("Got group of unknown type: '" + group.type + "'");
|
151
|
+
}
|
152
|
+
};
|
153
|
+
|
154
|
+
const glue = _ => {
|
155
|
+
return new mathMLTree.MathNode("mtd", [], [], { padding: "0", width: "50%" })
|
156
|
+
}
|
157
|
+
|
158
|
+
const taggedExpression = (expression, tag, style, leqno) => {
|
159
|
+
tag = buildExpressionRow(tag[0].body, style)
|
160
|
+
tag = consolidateText(tag)
|
161
|
+
tag.classes.push("tml-tag")
|
162
|
+
|
163
|
+
expression = new mathMLTree.MathNode("mtd", [expression])
|
164
|
+
const rowArray = [glue(), expression, glue()]
|
165
|
+
if (leqno) {
|
166
|
+
rowArray[0].children.push(tag)
|
167
|
+
rowArray[0].style.textAlign = "-webkit-left"
|
168
|
+
} else {
|
169
|
+
rowArray[2].children.push(tag)
|
170
|
+
rowArray[2].style.textAlign = "-webkit-right"
|
171
|
+
}
|
172
|
+
const mtr = new mathMLTree.MathNode("mtr", rowArray, ["tml-tageqn"])
|
173
|
+
const table = new mathMLTree.MathNode("mtable", [mtr])
|
174
|
+
table.style.width = "100%"
|
175
|
+
table.setAttribute("displaystyle", "true")
|
176
|
+
return table
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Takes a full parse tree and settings and builds a MathML representation of
|
181
|
+
* it.
|
182
|
+
*/
|
183
|
+
export default function buildMathML(tree, texExpression, style, settings) {
|
184
|
+
// Strip off outer tag wrapper for processing below.
|
185
|
+
let tag = null
|
186
|
+
if (tree.length === 1 && tree[0].type === "tag") {
|
187
|
+
tag = tree[0].tag
|
188
|
+
tree = tree[0].body
|
189
|
+
}
|
190
|
+
|
191
|
+
const expression = buildExpression(tree, style);
|
192
|
+
const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap
|
193
|
+
|
194
|
+
const n1 = expression.length === 0 ? null : expression[0]
|
195
|
+
let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode)
|
196
|
+
&& !(n1.type === "mstyle" && n1.attributes.mathcolor)
|
197
|
+
? expression[0]
|
198
|
+
: expression.length > 1 && wrap === "none"
|
199
|
+
? new mathMLTree.MathNode("mrow", expression)
|
200
|
+
: setLineBreaks(expression, wrap, settings.displayMode)
|
201
|
+
|
202
|
+
if (tag) {
|
203
|
+
wrapper = taggedExpression(wrapper, tag, style, settings.leqno)
|
204
|
+
}
|
205
|
+
|
206
|
+
if (settings.annotate) {
|
207
|
+
// Build a TeX annotation of the source
|
208
|
+
const annotation = new mathMLTree.MathNode(
|
209
|
+
"annotation", [new mathMLTree.TextNode(texExpression)]);
|
210
|
+
annotation.setAttribute("encoding", "application/x-tex");
|
211
|
+
wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
|
212
|
+
}
|
213
|
+
|
214
|
+
if (wrap !== "none") {
|
215
|
+
const maths = []
|
216
|
+
for (let i = 0; i < wrapper.children.length; i++) {
|
217
|
+
const math = new mathMLTree.MathNode("math", [wrapper.children[i]])
|
218
|
+
if (settings.xml) {
|
219
|
+
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML")
|
220
|
+
}
|
221
|
+
maths.push(math)
|
222
|
+
}
|
223
|
+
return mathMLTree.newDocumentFragment(maths)
|
224
|
+
}
|
225
|
+
|
226
|
+
const math = new mathMLTree.MathNode("math", [wrapper])
|
227
|
+
|
228
|
+
if (settings.xml) {
|
229
|
+
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML")
|
230
|
+
}
|
231
|
+
if (settings.displayMode) {
|
232
|
+
math.setAttribute("display", "block");
|
233
|
+
}
|
234
|
+
return math;
|
235
|
+
}
|
package/src/constants.js
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
// In TeX, there are actually three sets of dimensions, one for each of
|
2
|
+
// textstyle, scriptstyle, and scriptscriptstyle. These are
|
3
|
+
// provided in the the arrays below, in that order.
|
4
|
+
//
|
5
|
+
export const metrics = {
|
6
|
+
quad: [1.0, 1.171, 1.472], // Extracted from TeX
|
7
|
+
|
8
|
+
// The TeX default rule thickness, 0.04 em, often disappears from a browser screen.
|
9
|
+
// So we use a thicker rule.
|
10
|
+
defaultRuleThickness: [0.06, 0.074, 0.074],
|
11
|
+
|
12
|
+
// The space between adjacent `|` columns in an array definition.
|
13
|
+
doubleRuleSep: [0.3, 0.3, 0.3],
|
14
|
+
|
15
|
+
// The width of separator lines in {array} environments.
|
16
|
+
arrayRuleWidth: [0.06, 0.06, 0.04]
|
17
|
+
};
|
18
|
+
|
19
|
+
// Math style is not quite the same thing as script level.
|
20
|
+
export const StyleLevel = {
|
21
|
+
DISPLAY: 0,
|
22
|
+
TEXT: 1,
|
23
|
+
SCRIPT: 2,
|
24
|
+
SCRIPTSCRIPT: 3
|
25
|
+
};
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { _mathmlGroupBuilders } from "./defineFunction";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* All registered environments.
|
5
|
+
* `environments.js` exports this same dictionary again and makes it public.
|
6
|
+
* `Parser.js` requires this dictionary via `environments.js`.
|
7
|
+
*/
|
8
|
+
export const _environments = {};
|
9
|
+
|
10
|
+
export default function defineEnvironment({ type, names, props, handler, mathmlBuilder }) {
|
11
|
+
// Set default values of environments.
|
12
|
+
const data = {
|
13
|
+
type,
|
14
|
+
numArgs: props.numArgs || 0,
|
15
|
+
allowedInText: false,
|
16
|
+
numOptionalArgs: 0,
|
17
|
+
handler
|
18
|
+
};
|
19
|
+
for (let i = 0; i < names.length; ++i) {
|
20
|
+
_environments[names[i]] = data;
|
21
|
+
}
|
22
|
+
if (mathmlBuilder) {
|
23
|
+
_mathmlGroupBuilders[type] = mathmlBuilder;
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
/**
|
2
|
+
* All registered functions.
|
3
|
+
* `functions.js` just exports this same dictionary again and makes it public.
|
4
|
+
* `Parser.js` requires this dictionary.
|
5
|
+
*/
|
6
|
+
export const _functions = {}
|
7
|
+
|
8
|
+
/**
|
9
|
+
* All MathML builders. Should be only used in the `define*` and the `build*ML`
|
10
|
+
* functions.
|
11
|
+
*/
|
12
|
+
export const _mathmlGroupBuilders = {}
|
13
|
+
|
14
|
+
export default function defineFunction({
|
15
|
+
type,
|
16
|
+
names,
|
17
|
+
props,
|
18
|
+
handler,
|
19
|
+
mathmlBuilder
|
20
|
+
}) {
|
21
|
+
// Set default values of functions
|
22
|
+
const data = {
|
23
|
+
type,
|
24
|
+
numArgs: props.numArgs,
|
25
|
+
argTypes: props.argTypes,
|
26
|
+
allowedInArgument: !!props.allowedInArgument,
|
27
|
+
allowedInText: !!props.allowedInText,
|
28
|
+
allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath,
|
29
|
+
numOptionalArgs: props.numOptionalArgs || 0,
|
30
|
+
infix: !!props.infix,
|
31
|
+
primitive: !!props.primitive,
|
32
|
+
handler: handler
|
33
|
+
}
|
34
|
+
for (let i = 0; i < names.length; ++i) {
|
35
|
+
_functions[names[i]] = data
|
36
|
+
}
|
37
|
+
if (type) {
|
38
|
+
if (mathmlBuilder) {
|
39
|
+
_mathmlGroupBuilders[type] = mathmlBuilder
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Use this to register only the MathML builder for a function(e.g.
|
46
|
+
* if the function's ParseNode is generated in Parser.js rather than via a
|
47
|
+
* stand-alone handler provided to `defineFunction`).
|
48
|
+
*/
|
49
|
+
export function defineFunctionBuilders({ type, mathmlBuilder }) {
|
50
|
+
defineFunction({
|
51
|
+
type,
|
52
|
+
names: [],
|
53
|
+
props: { numArgs: 0 },
|
54
|
+
handler() {
|
55
|
+
throw new Error("Should never be called.")
|
56
|
+
},
|
57
|
+
mathmlBuilder
|
58
|
+
})
|
59
|
+
}
|
60
|
+
|
61
|
+
export const normalizeArgument = function(arg) {
|
62
|
+
return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg
|
63
|
+
}
|
64
|
+
|
65
|
+
// Since the corresponding buildMathML function expects a
|
66
|
+
// list of elements, we normalize for different kinds of arguments
|
67
|
+
export const ordargument = function(arg) {
|
68
|
+
return arg.type === "ordgroup" ? arg.body : [arg]
|
69
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
/**
|
2
|
+
* All registered global/built-in macros.
|
3
|
+
* `macros.js` exports this same dictionary again and makes it public.
|
4
|
+
* `Parser.js` requires this dictionary via `macros.js`.
|
5
|
+
*/
|
6
|
+
export const _macros = {};
|
7
|
+
|
8
|
+
// This function might one day accept an additional argument and do more things.
|
9
|
+
export default function defineMacro(name, body) {
|
10
|
+
_macros[name] = body;
|
11
|
+
}
|