temml 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/contrib/auto-render/README.md +89 -0
  4. package/contrib/auto-render/auto-render.js +128 -0
  5. package/contrib/auto-render/dist/auto-render.js +217 -0
  6. package/contrib/auto-render/dist/auto-render.min.js +1 -0
  7. package/contrib/auto-render/splitAtDelimiters.js +84 -0
  8. package/contrib/auto-render/test/auto-render-spec.js +234 -0
  9. package/contrib/auto-render/test/auto-render.js +217 -0
  10. package/contrib/auto-render/test/test_page.html +59 -0
  11. package/contrib/mhchem/README.md +26 -0
  12. package/contrib/mhchem/mhchem.js +1705 -0
  13. package/contrib/mhchem/mhchem.min.js +1 -0
  14. package/contrib/physics/README.md +20 -0
  15. package/contrib/physics/physics.js +131 -0
  16. package/contrib/texvc/README.md +23 -0
  17. package/contrib/texvc/texvc.js +61 -0
  18. package/dist/Temml-Asana.css +201 -0
  19. package/dist/Temml-Latin-Modern.css +216 -0
  20. package/dist/Temml-Libertinus.css +214 -0
  21. package/dist/Temml-Local.css +194 -0
  22. package/dist/Temml-STIX2.css +203 -0
  23. package/dist/Temml.woff2 +0 -0
  24. package/dist/temml.cjs +13122 -0
  25. package/dist/temml.js +11225 -0
  26. package/dist/temml.min.js +1 -0
  27. package/dist/temml.mjs +13120 -0
  28. package/dist/temmlPostProcess.js +70 -0
  29. package/package.json +34 -0
  30. package/src/Lexer.js +121 -0
  31. package/src/MacroExpander.js +437 -0
  32. package/src/Namespace.js +107 -0
  33. package/src/ParseError.js +64 -0
  34. package/src/Parser.js +977 -0
  35. package/src/Settings.js +49 -0
  36. package/src/SourceLocation.js +29 -0
  37. package/src/Style.js +144 -0
  38. package/src/Token.js +40 -0
  39. package/src/buildMathML.js +235 -0
  40. package/src/constants.js +25 -0
  41. package/src/defineEnvironment.js +25 -0
  42. package/src/defineFunction.js +69 -0
  43. package/src/defineMacro.js +11 -0
  44. package/src/domTree.js +185 -0
  45. package/src/environments/array.js +791 -0
  46. package/src/environments/cd.js +252 -0
  47. package/src/environments.js +8 -0
  48. package/src/functions/accent.js +127 -0
  49. package/src/functions/accentunder.js +38 -0
  50. package/src/functions/arrow.js +204 -0
  51. package/src/functions/cancelto.js +36 -0
  52. package/src/functions/char.js +33 -0
  53. package/src/functions/color.js +253 -0
  54. package/src/functions/cr.js +46 -0
  55. package/src/functions/def.js +259 -0
  56. package/src/functions/delimsizing.js +304 -0
  57. package/src/functions/enclose.js +193 -0
  58. package/src/functions/envTag.js +38 -0
  59. package/src/functions/environment.js +59 -0
  60. package/src/functions/font.js +123 -0
  61. package/src/functions/genfrac.js +333 -0
  62. package/src/functions/hbox.js +29 -0
  63. package/src/functions/horizBrace.js +32 -0
  64. package/src/functions/href.js +90 -0
  65. package/src/functions/html.js +95 -0
  66. package/src/functions/includegraphics.js +131 -0
  67. package/src/functions/kern.js +75 -0
  68. package/src/functions/label.js +29 -0
  69. package/src/functions/lap.js +75 -0
  70. package/src/functions/math.js +40 -0
  71. package/src/functions/mathchoice.js +41 -0
  72. package/src/functions/mclass.js +201 -0
  73. package/src/functions/multiscript.js +91 -0
  74. package/src/functions/not.js +46 -0
  75. package/src/functions/op.js +338 -0
  76. package/src/functions/operatorname.js +139 -0
  77. package/src/functions/ordgroup.js +9 -0
  78. package/src/functions/phantom.js +73 -0
  79. package/src/functions/pmb.js +31 -0
  80. package/src/functions/raise.js +68 -0
  81. package/src/functions/ref.js +28 -0
  82. package/src/functions/relax.js +16 -0
  83. package/src/functions/rule.js +52 -0
  84. package/src/functions/sizing.js +64 -0
  85. package/src/functions/smash.js +66 -0
  86. package/src/functions/sqrt.js +31 -0
  87. package/src/functions/styling.js +58 -0
  88. package/src/functions/supsub.js +135 -0
  89. package/src/functions/symbolsOp.js +53 -0
  90. package/src/functions/symbolsOrd.js +102 -0
  91. package/src/functions/symbolsSpacing.js +53 -0
  92. package/src/functions/tag.js +8 -0
  93. package/src/functions/text.js +75 -0
  94. package/src/functions/tip.js +63 -0
  95. package/src/functions/toggle.js +13 -0
  96. package/src/functions/verb.js +33 -0
  97. package/src/functions.js +57 -0
  98. package/src/linebreaking.js +159 -0
  99. package/src/macros.js +708 -0
  100. package/src/mathMLTree.js +175 -0
  101. package/src/parseNode.js +42 -0
  102. package/src/parseTree.js +40 -0
  103. package/src/postProcess.js +57 -0
  104. package/src/replace.js +225 -0
  105. package/src/stretchy.js +66 -0
  106. package/src/svg.js +110 -0
  107. package/src/symbols.js +972 -0
  108. package/src/tree.js +50 -0
  109. package/src/unicodeAccents.js +16 -0
  110. package/src/unicodeScripts.js +119 -0
  111. package/src/unicodeSupOrSub.js +108 -0
  112. package/src/unicodeSymbolBuilder.js +31 -0
  113. package/src/unicodeSymbols.js +320 -0
  114. package/src/units.js +109 -0
  115. package/src/utils.js +109 -0
  116. package/src/variant.js +103 -0
  117. package/temml.js +181 -0
package/src/units.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * This file does conversion between units. In particular, it provides
3
+ * calculateSize to convert other units into CSS units.
4
+ */
5
+
6
+ import ParseError from "./ParseError"
7
+ import utils from "./utils"
8
+
9
+ const ptPerUnit = {
10
+ // Convert to CSS (Postscipt) points, not TeX points
11
+ // https://en.wikibooks.org/wiki/LaTeX/Lengths and
12
+ // https://tex.stackexchange.com/a/8263
13
+ pt: 800 / 803, // convert TeX point to CSS (Postscript) point
14
+ pc: (12 * 800) / 803, // pica
15
+ dd: ((1238 / 1157) * 800) / 803, // didot
16
+ cc: ((14856 / 1157) * 800) / 803, // cicero (12 didot)
17
+ nd: ((685 / 642) * 800) / 803, // new didot
18
+ nc: ((1370 / 107) * 800) / 803, // new cicero (12 new didot)
19
+ sp: ((1 / 65536) * 800) / 803, // scaled point (TeX's internal smallest unit)
20
+ mm: (25.4 / 72),
21
+ cm: (2.54 / 72),
22
+ in: (1 / 72),
23
+ px: (96 / 72)
24
+ }
25
+
26
+ /**
27
+ * Determine whether the specified unit (either a string defining the unit
28
+ * or a "size" parse node containing a unit field) is valid.
29
+ */
30
+ const validUnits = [
31
+ "em",
32
+ "ex",
33
+ "mu",
34
+ "pt",
35
+ "mm",
36
+ "cm",
37
+ "in",
38
+ "px",
39
+ "bp",
40
+ "pc",
41
+ "dd",
42
+ "cc",
43
+ "nd",
44
+ "nc",
45
+ "sp"
46
+ ]
47
+
48
+ export const validUnit = function(unit) {
49
+ if (typeof unit !== "string") {
50
+ unit = unit.unit
51
+ }
52
+ return validUnits.indexOf(unit) > -1
53
+ }
54
+
55
+ export const emScale = styleLevel => {
56
+ const scriptLevel = Math.max(styleLevel - 1, 0)
57
+ return [1, 0.7, 0.5][scriptLevel]
58
+ };
59
+
60
+ /*
61
+ * Convert a "size" parse node (with numeric "number" and string "unit" fields,
62
+ * as parsed by functions.js argType "size") into a CSS value.
63
+ */
64
+ export const calculateSize = function(sizeValue, style) {
65
+ let number = sizeValue.number
66
+ if (style.maxSize[0] < 0 && number > 0) {
67
+ return { number: 0, unit: "em" }
68
+ }
69
+ const unit = sizeValue.unit
70
+ switch (unit) {
71
+ case "mm":
72
+ case "cm":
73
+ case "in":
74
+ case "px": {
75
+ const numInCssPts = number * ptPerUnit[unit];
76
+ if (numInCssPts > style.maxSize[1]) {
77
+ return { number: style.maxSize[1], unit: "pt" }
78
+ }
79
+ return { number, unit }; // absolute CSS units.
80
+ }
81
+ case "em":
82
+ case "ex": {
83
+ // In TeX, em and ex do not change size in \scriptstyle.
84
+ if (unit === "ex") { number *= 0.431 }
85
+ number = Math.min(number / emScale(style.level), style.maxSize[0])
86
+ return { number: utils.round(number), unit: "em" };
87
+ }
88
+ case "bp": {
89
+ if (number > style.maxSize[1]) { number = style.maxSize[1] }
90
+ return { number, unit: "pt" }; // TeX bp is a CSS pt. (1/72 inch).
91
+ }
92
+ case "pt":
93
+ case "pc":
94
+ case "dd":
95
+ case "cc":
96
+ case "nd":
97
+ case "nc":
98
+ case "sp": {
99
+ number = Math.min(number * ptPerUnit[unit], style.maxSize[1])
100
+ return { number: utils.round(number), unit: "pt" }
101
+ }
102
+ case "mu": {
103
+ number = Math.min(number / 18, style.maxSize[0])
104
+ return { number: utils.round(number), unit: "em" }
105
+ }
106
+ default:
107
+ throw new ParseError("Invalid unit: '" + unit + "'")
108
+ }
109
+ }
package/src/utils.js ADDED
@@ -0,0 +1,109 @@
1
+ //
2
+ /**
3
+ * This file contains a list of utility functions which are useful in other
4
+ * files.
5
+ */
6
+
7
+ /**
8
+ * Provide a default value if a setting is undefined
9
+ */
10
+ const deflt = function(setting, defaultIfUndefined) {
11
+ return setting === undefined ? defaultIfUndefined : setting;
12
+ };
13
+
14
+ // hyphenate and escape adapted from Facebook's React under Apache 2 license
15
+
16
+ const uppercase = /([A-Z])/g;
17
+ const hyphenate = function(str) {
18
+ return str.replace(uppercase, "-$1").toLowerCase();
19
+ };
20
+
21
+ const ESCAPE_LOOKUP = {
22
+ "&": "&amp;",
23
+ ">": "&gt;",
24
+ "<": "&lt;",
25
+ '"': "&quot;",
26
+ "'": "&#x27;"
27
+ };
28
+
29
+ const ESCAPE_REGEX = /[&><"']/g;
30
+
31
+ /**
32
+ * Escapes text to prevent scripting attacks.
33
+ */
34
+ function escape(text) {
35
+ return String(text).replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
36
+ }
37
+
38
+ /**
39
+ * Sometimes we want to pull out the innermost element of a group. In most
40
+ * cases, this will just be the group itself, but when ordgroups and colors have
41
+ * a single element, we want to pull that out.
42
+ */
43
+ const getBaseElem = function(group) {
44
+ if (group.type === "ordgroup") {
45
+ if (group.body.length === 1) {
46
+ return getBaseElem(group.body[0]);
47
+ } else {
48
+ return group;
49
+ }
50
+ } else if (group.type === "color") {
51
+ if (group.body.length === 1) {
52
+ return getBaseElem(group.body[0]);
53
+ } else {
54
+ return group;
55
+ }
56
+ } else if (group.type === "font") {
57
+ return getBaseElem(group.body);
58
+ } else {
59
+ return group;
60
+ }
61
+ };
62
+
63
+ /**
64
+ * TeXbook algorithms often reference "character boxes", which are simply groups
65
+ * with a single character in them. To decide if something is a character box,
66
+ * we find its innermost group, and see if it is a single character.
67
+ */
68
+ const isCharacterBox = function(group) {
69
+ const baseElem = getBaseElem(group);
70
+
71
+ // These are all the types of groups which hold single characters
72
+ return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom"
73
+ };
74
+
75
+ export const assert = function(value) {
76
+ if (!value) {
77
+ throw new Error("Expected non-null, but got " + String(value));
78
+ }
79
+ return value;
80
+ };
81
+
82
+ /**
83
+ * Return the protocol of a URL, or "_relative" if the URL does not specify a
84
+ * protocol (and thus is relative).
85
+ */
86
+ export const protocolFromUrl = function(url) {
87
+ const protocol = /^\s*([^\\/#]*?)(?::|&#0*58|&#x0*3a)/i.exec(url);
88
+ return protocol != null ? protocol[1] : "_relative";
89
+ };
90
+
91
+ /**
92
+ * Round `n` to 4 decimal places, or to the nearest 1/10,000th em. The TeXbook
93
+ * gives an acceptable rounding error of 100sp (which would be the nearest
94
+ * 1/6551.6em with our ptPerEm = 10):
95
+ * http://www.ctex.org/documents/shredder/src/texbook.pdf#page=69
96
+ */
97
+ const round = function(n) {
98
+ return +n.toFixed(4);
99
+ };
100
+
101
+ export default {
102
+ deflt,
103
+ escape,
104
+ hyphenate,
105
+ getBaseElem,
106
+ isCharacterBox,
107
+ protocolFromUrl,
108
+ round
109
+ };
package/src/variant.js ADDED
@@ -0,0 +1,103 @@
1
+ import symbols from "./symbols";
2
+
3
+ /**
4
+ * Maps TeX font commands to "mathvariant" attribute in buildMathML.js
5
+ */
6
+ const fontMap = {
7
+ // styles
8
+ mathbf: "bold",
9
+ mathrm: "normal",
10
+ textit: "italic",
11
+ mathit: "italic",
12
+ mathnormal: "italic",
13
+
14
+ // families
15
+ mathbb: "double-struck",
16
+ mathcal: "script",
17
+ mathfrak: "fraktur",
18
+ mathscr: "script",
19
+ mathsf: "sans-serif",
20
+ mathtt: "monospace",
21
+ oldstylenums: "oldstylenums"
22
+ }
23
+
24
+ /**
25
+ * Returns the math variant as a string or null if none is required.
26
+ */
27
+ export const getVariant = function(group, style) {
28
+ // Handle font specifiers as best we can.
29
+ // Chromium does not support the MathML mathvariant attribute.
30
+ // So we'll use Unicode replacement characters instead.
31
+ // But first, determine the math variant.
32
+
33
+ // Deal with the \textit, \textbf, etc., functions.
34
+ if (style.fontFamily === "texttt") {
35
+ return "monospace"
36
+ } else if (style.fontFamily === "textsc") {
37
+ return "normal"; // handled via character substitution in symbolsOrd.js.
38
+ } else if (style.fontFamily === "textsf") {
39
+ if (style.fontShape === "textit" && style.fontWeight === "textbf") {
40
+ return "sans-serif-bold-italic"
41
+ } else if (style.fontShape === "textit") {
42
+ return "sans-serif-italic"
43
+ } else if (style.fontWeight === "textbf") {
44
+ return "sans-serif-bold"
45
+ } else {
46
+ return "sans-serif"
47
+ }
48
+ } else if (style.fontShape === "textit" && style.fontWeight === "textbf") {
49
+ return "bold-italic"
50
+ } else if (style.fontShape === "textit") {
51
+ return "italic"
52
+ } else if (style.fontWeight === "textbf") {
53
+ return "bold"
54
+ }
55
+
56
+ // Deal with the \mathit, mathbf, etc, functions.
57
+ const font = style.font
58
+ if (!font || font === "mathnormal") {
59
+ return null
60
+ }
61
+
62
+ const mode = group.mode;
63
+ switch (font) {
64
+ case "mathit":
65
+ return "italic"
66
+ case "mathrm": {
67
+ const codePoint = group.text.codePointAt(0)
68
+ // LaTeX \mathrm returns italic for Greek characters.
69
+ return (0x03ab < codePoint && codePoint < 0x03cf) ? "italic" : "normal"
70
+ }
71
+ case "greekItalic":
72
+ return "italic"
73
+ case "up@greek":
74
+ return "normal"
75
+ case "boldsymbol":
76
+ case "mathboldsymbol":
77
+ return "bold-italic"
78
+ case "mathbf":
79
+ return "bold"
80
+ case "mathbb":
81
+ return "double-struck"
82
+ case "mathfrak":
83
+ return "fraktur"
84
+ case "mathscr":
85
+ case "mathcal":
86
+ return "script"
87
+ case "mathsf":
88
+ return "sans-serif"
89
+ case "mathtt":
90
+ return "monospace"
91
+ case "oldstylenums":
92
+ return "oldstylenums"
93
+ default:
94
+ break
95
+ }
96
+
97
+ let text = group.text;
98
+ if (symbols[mode][text] && symbols[mode][text].replace) {
99
+ text = symbols[mode][text].replace
100
+ }
101
+
102
+ return Object.prototype.hasOwnProperty.call(fontMap, font) ? fontMap[font] : null
103
+ };
package/temml.js ADDED
@@ -0,0 +1,181 @@
1
+ /* eslint no-console:0 */
2
+ /**
3
+ * This is the main entry point for Temml. Here, we expose functions for
4
+ * rendering expressions either to DOM nodes or to markup strings.
5
+ *
6
+ * We also expose the ParseError class to check if errors thrown from Temml are
7
+ * errors in the expression, or errors in javascript handling.
8
+ */
9
+
10
+ import ParseError from "./src/ParseError";
11
+ import Settings from "./src/Settings";
12
+
13
+ import Parser from "./src/Parser";
14
+ import parseTree from "./src/parseTree";
15
+ import buildMathML from "./src/buildMathML";
16
+ import { StyleLevel } from "./src/constants";
17
+ import Style from "./src/Style";
18
+ import { Span, TextNode } from "./src/domTree";
19
+ import { defineSymbol } from "./src/symbols";
20
+ import defineMacro from "./src/defineMacro";
21
+ import { postProcess, version } from "./src/postProcess";
22
+
23
+ /**
24
+ * Parse and build an expression, and place that expression in the DOM node
25
+ * given.
26
+ */
27
+ let render = function(expression, baseNode, options) {
28
+ baseNode.textContent = "";
29
+ const alreadyInMathElement = baseNode.tagName === "MATH"
30
+ if (alreadyInMathElement) { options.wrap = "none" }
31
+ const math = renderToMathMLTree(expression, options)
32
+ if (alreadyInMathElement) {
33
+ // The <math> element already exists. Populate it.
34
+ baseNode.textContent = ""
35
+ math.children.forEach(e => { baseNode.appendChild(e.toNode()) })
36
+ } else if (math.children.length > 1) {
37
+ baseNode.textContent = ""
38
+ math.children.forEach(e => { baseNode.appendChild(e.toNode()) })
39
+ } else {
40
+ baseNode.appendChild(math.toNode())
41
+ }
42
+ };
43
+
44
+ // Temml's styles don't work properly in quirks mode. Print out an error, and
45
+ // disable rendering.
46
+ if (typeof document !== "undefined") {
47
+ if (document.compatMode !== "CSS1Compat") {
48
+ typeof console !== "undefined" &&
49
+ console.warn(
50
+ "Warning: Temml doesn't work in quirks mode. Make sure your " +
51
+ "website has a suitable doctype."
52
+ );
53
+
54
+ render = function() {
55
+ throw new ParseError("Temml doesn't work in quirks mode.");
56
+ };
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Parse and build an expression, and return the markup for that.
62
+ */
63
+ const renderToString = function(expression, options) {
64
+ const markup = renderToMathMLTree(expression, options).toMarkup();
65
+ return markup;
66
+ };
67
+
68
+ /**
69
+ * Parse an expression and return the parse tree.
70
+ */
71
+ const generateParseTree = function(expression, options) {
72
+ const settings = new Settings(options);
73
+ return parseTree(expression, settings);
74
+ };
75
+
76
+ /**
77
+ * Take an expression which contains a preamble.
78
+ * Parse it and return the macros.
79
+ */
80
+ const definePreamble = function(expression, options) {
81
+ const settings = new Settings(options);
82
+ settings.macros = {};
83
+ if (!(typeof expression === "string" || expression instanceof String)) {
84
+ throw new TypeError("Temml can only parse string typed expression")
85
+ }
86
+ const parser = new Parser(expression, settings, true)
87
+ // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors
88
+ delete parser.gullet.macros.current["\\df@tag"]
89
+ const macros = parser.parse()
90
+ return macros
91
+ };
92
+
93
+ /**
94
+ * If the given error is a Temml ParseError,
95
+ * renders the invalid LaTeX as a span with hover title giving the Temml
96
+ * error message. Otherwise, simply throws the error.
97
+ */
98
+ const renderError = function(error, expression, options) {
99
+ if (options.throwOnError || !(error instanceof ParseError)) {
100
+ throw error;
101
+ }
102
+ const node = new Span(["temml-error"], [new TextNode(expression + "\n" + error.toString())]);
103
+ node.style.color = options.errorColor
104
+ node.style.whiteSpace = "pre-line"
105
+ return node;
106
+ };
107
+
108
+ /**
109
+ * Generates and returns the Temml build tree. This is used for advanced
110
+ * use cases (like rendering to custom output).
111
+ */
112
+ const renderToMathMLTree = function(expression, options) {
113
+ const settings = new Settings(options);
114
+ try {
115
+ const tree = parseTree(expression, settings);
116
+ const style = new Style({
117
+ level: settings.displayMode ? StyleLevel.DISPLAY : StyleLevel.TEXT,
118
+ maxSize: settings.maxSize
119
+ });
120
+ return buildMathML(tree, expression, style, settings);
121
+ } catch (error) {
122
+ return renderError(error, expression, settings);
123
+ }
124
+ };
125
+
126
+ export default {
127
+ /**
128
+ * Current Temml version
129
+ */
130
+ version: version,
131
+ /**
132
+ * Renders the given LaTeX into MathML, and adds
133
+ * it as a child to the specified DOM node.
134
+ */
135
+ render,
136
+ /**
137
+ * Renders the given LaTeX into MathML string,
138
+ * for sending to the client.
139
+ */
140
+ renderToString,
141
+ /**
142
+ * Post-process an entire HTML block.
143
+ * Writes AMS auto-numbers and implements \ref{}.
144
+ * Typcally called once, after a loop has rendered many individual spans.
145
+ */
146
+ postProcess,
147
+ /**
148
+ * Temml error, usually during parsing.
149
+ */
150
+ ParseError,
151
+ /**
152
+ * Creates a set of macros with document-wide scope.
153
+ */
154
+ definePreamble,
155
+ /**
156
+ * Parses the given LaTeX into Temml's internal parse tree structure,
157
+ * without rendering to HTML or MathML.
158
+ *
159
+ * NOTE: This method is not currently recommended for public use.
160
+ * The internal tree representation is unstable and is very likely
161
+ * to change. Use at your own risk.
162
+ */
163
+ __parse: generateParseTree,
164
+ /**
165
+ * Renders the given LaTeX into a MathML internal DOM tree
166
+ * representation, without flattening that representation to a string.
167
+ *
168
+ * NOTE: This method is not currently recommended for public use.
169
+ * The internal tree representation is unstable and is very likely
170
+ * to change. Use at your own risk.
171
+ */
172
+ __renderToMathMLTree: renderToMathMLTree,
173
+ /**
174
+ * adds a new symbol to builtin symbols table
175
+ */
176
+ __defineSymbol: defineSymbol,
177
+ /**
178
+ * adds a new macro to builtin macro list
179
+ */
180
+ __defineMacro: defineMacro
181
+ }