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
@@ -0,0 +1,234 @@
1
+ /* global beforeEach: false */
2
+ /* global expect: false */
3
+ /* global it: false */
4
+ /* global describe: false */
5
+
6
+ import splitAtDelimiters from "../splitAtDelimiters";
7
+ import renderMathInElement from "../auto-render";
8
+
9
+ beforeEach(function() {
10
+ expect.extend({
11
+ toSplitInto: function(actual, left, right, result) {
12
+ const message = {
13
+ pass: true,
14
+ message: "'" + actual + "' split correctly"
15
+ };
16
+
17
+ const startData = [{ type: "text", data: actual }];
18
+
19
+ const split = splitAtDelimiters(startData, left, right, false);
20
+
21
+ if (split.length !== result.length) {
22
+ message.pass = false;
23
+ message.message =
24
+ "Different number of splits: " +
25
+ split.length +
26
+ " vs. " +
27
+ result.length +
28
+ " (" +
29
+ JSON.stringify(split) +
30
+ " vs. " +
31
+ JSON.stringify(result) +
32
+ ")";
33
+ return message;
34
+ }
35
+
36
+ for (let i = 0; i < split.length; i++) {
37
+ const real = split[i];
38
+ const correct = result[i];
39
+
40
+ let good = true;
41
+ let diff;
42
+
43
+ if (real.type !== correct.type) {
44
+ good = false;
45
+ diff = "type";
46
+ } else if (real.data !== correct.data) {
47
+ good = false;
48
+ diff = "data";
49
+ } else if (real.display !== correct.display) {
50
+ good = false;
51
+ diff = "display";
52
+ }
53
+
54
+ if (!good) {
55
+ message.pass = false;
56
+ message.message =
57
+ "Difference at split " +
58
+ (i + 1) +
59
+ ": " +
60
+ JSON.stringify(real) +
61
+ " vs. " +
62
+ JSON.stringify(correct) +
63
+ " (" +
64
+ diff +
65
+ " differs)";
66
+ break;
67
+ }
68
+ }
69
+
70
+ return message;
71
+ }
72
+ });
73
+ });
74
+
75
+ describe("A delimiter splitter", function() {
76
+ it("doesn't split when there are no delimiters", function() {
77
+ expect("hello").toSplitInto("(", ")", [{ type: "text", data: "hello" }]);
78
+ });
79
+
80
+ it("doesn't create a math node with only one left delimiter", function() {
81
+ expect("hello ( world").toSplitInto("(", ")", [
82
+ { type: "text", data: "hello " },
83
+ { type: "text", data: "( world" }
84
+ ]);
85
+ });
86
+
87
+ it("doesn't split when there's only a right delimiter", function() {
88
+ expect("hello ) world").toSplitInto("(", ")", [{ type: "text", data: "hello ) world" }]);
89
+ });
90
+
91
+ it("splits when there are both delimiters", function() {
92
+ expect("hello ( world ) boo").toSplitInto("(", ")", [
93
+ { type: "text", data: "hello " },
94
+ { type: "math", data: " world ", rawData: "( world )", display: false },
95
+ { type: "text", data: " boo" }
96
+ ]);
97
+ });
98
+
99
+ it("splits on multi-character delimiters", function() {
100
+ expect("hello [[ world ]] boo").toSplitInto("[[", "]]", [
101
+ { type: "text", data: "hello " },
102
+ { type: "math", data: " world ", rawData: "[[ world ]]", display: false },
103
+ { type: "text", data: " boo" }
104
+ ]);
105
+ });
106
+
107
+ it("splits mutliple times", function() {
108
+ expect("hello ( world ) boo ( more ) stuff").toSplitInto("(", ")", [
109
+ { type: "text", data: "hello " },
110
+ { type: "math", data: " world ", rawData: "( world )", display: false },
111
+ { type: "text", data: " boo " },
112
+ { type: "math", data: " more ", rawData: "( more )", display: false },
113
+ { type: "text", data: " stuff" }
114
+ ]);
115
+ });
116
+
117
+ it("leaves the ending when there's only a left delimiter", function() {
118
+ expect("hello ( world ) boo ( left").toSplitInto("(", ")", [
119
+ { type: "text", data: "hello " },
120
+ { type: "math", data: " world ", rawData: "( world )", display: false },
121
+ { type: "text", data: " boo " },
122
+ { type: "text", data: "( left" }
123
+ ]);
124
+ });
125
+
126
+ it("doesn't split when close delimiters are in {}s", function() {
127
+ expect("hello ( world { ) } ) boo").toSplitInto("(", ")", [
128
+ { type: "text", data: "hello " },
129
+ { type: "math", data: " world { ) } ", rawData: "( world { ) } )", display: false },
130
+ { type: "text", data: " boo" }
131
+ ]);
132
+
133
+ expect("hello ( world { { } ) } ) boo").toSplitInto("(", ")", [
134
+ { type: "text", data: "hello " },
135
+ { type: "math", data: " world { { } ) } ", rawData: "( world { { } ) } )", display: false },
136
+ { type: "text", data: " boo" }
137
+ ]);
138
+ });
139
+
140
+ it("doesn't split at escaped delimiters", function() {
141
+ expect("hello ( world \\) ) boo").toSplitInto("(", ")", [
142
+ { type: "text", data: "hello " },
143
+ { type: "math", data: " world \\) ", rawData: "( world \\) )", display: false },
144
+ { type: "text", data: " boo" }
145
+ ]);
146
+
147
+ /* TODO(emily): make this work maybe?
148
+ expect("hello \\( ( world ) boo").toSplitInto(
149
+ "(", ")",
150
+ [
151
+ {type: "text", data: "hello \\( "},
152
+ {type: "math", data: " world ",
153
+ rawData: "( world )", display: false},
154
+ {type: "text", data: " boo"},
155
+ ]);
156
+ */
157
+ });
158
+
159
+ it("splits when the right and left delimiters are the same", function() {
160
+ expect("hello $ world $ boo").toSplitInto("$", "$", [
161
+ { type: "text", data: "hello " },
162
+ { type: "math", data: " world ", rawData: "$ world $", display: false },
163
+ { type: "text", data: " boo" }
164
+ ]);
165
+ });
166
+
167
+ it("remembers which delimiters are display-mode", function() {
168
+ const startData = [{ type: "text", data: "hello ( world ) boo" }];
169
+
170
+ expect(splitAtDelimiters(startData, "(", ")", true)).toEqual([
171
+ { type: "text", data: "hello " },
172
+ { type: "math", data: " world ", rawData: "( world )", display: true },
173
+ { type: "text", data: " boo" }
174
+ ]);
175
+ });
176
+
177
+ it("works with more than one start datum", function() {
178
+ const startData = [
179
+ { type: "text", data: "hello ( world ) boo" },
180
+ { type: "math", data: "math", rawData: "(math)", display: true },
181
+ { type: "text", data: "hello ( world ) boo" }
182
+ ];
183
+
184
+ expect(splitAtDelimiters(startData, "(", ")", false)).toEqual([
185
+ { type: "text", data: "hello " },
186
+ { type: "math", data: " world ", rawData: "( world )", display: false },
187
+ { type: "text", data: " boo" },
188
+ { type: "math", data: "math", rawData: "(math)", display: true },
189
+ { type: "text", data: "hello " },
190
+ { type: "math", data: " world ", rawData: "( world )", display: false },
191
+ { type: "text", data: " boo" }
192
+ ]);
193
+ });
194
+
195
+ it("doesn't do splitting inside of math nodes", function() {
196
+ const startData = [
197
+ { type: "text", data: "hello ( world ) boo" },
198
+ {
199
+ type: "math",
200
+ data: "hello ( world ) boo",
201
+ rawData: "(hello ( world ) boo)",
202
+ display: true
203
+ }
204
+ ];
205
+
206
+ expect(splitAtDelimiters(startData, "(", ")", false)).toEqual([
207
+ { type: "text", data: "hello " },
208
+ { type: "math", data: " world ", rawData: "( world )", display: false },
209
+ { type: "text", data: " boo" },
210
+ {
211
+ type: "math",
212
+ data: "hello ( world ) boo",
213
+ rawData: "(hello ( world ) boo)",
214
+ display: true
215
+ }
216
+ ]);
217
+ });
218
+ });
219
+
220
+ describe("Pre-process callback", function() {
221
+ it("replace `-squared` with `^2 `", function() {
222
+ const el1 = document.createElement("div");
223
+ el1.textContent = "Circle equation: $x-squared + y-squared = r-squared$.";
224
+ const el2 = document.createElement("div");
225
+ el2.textContent = "Circle equation: $x^2 + y^2 = r^2$.";
226
+ const delimiters = [{ left: "$", right: "$", display: false }];
227
+ renderMathInElement(el1, {
228
+ delimiters,
229
+ preProcess: (math) => math.replace(/-squared/g, "^2")
230
+ });
231
+ renderMathInElement(el2, { delimiters });
232
+ expect(el1.innerHTML).toEqual(el2.innerHTML);
233
+ });
234
+ });
@@ -0,0 +1,217 @@
1
+ var renderMathInElement = (function (temml) {
2
+ 'use strict';
3
+
4
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
5
+
6
+ var temml__default = /*#__PURE__*/_interopDefaultLegacy(temml);
7
+
8
+ /* eslint no-constant-condition:0 */
9
+ const findEndOfMath = function(delimiter, text, startIndex) {
10
+ // Adapted from
11
+ // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
12
+ let index = startIndex;
13
+ let braceLevel = 0;
14
+
15
+ const delimLength = delimiter.length;
16
+
17
+ while (index < text.length) {
18
+ const character = text[index];
19
+
20
+ if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
21
+ return index;
22
+ } else if (character === "\\") {
23
+ index++;
24
+ } else if (character === "{") {
25
+ braceLevel++;
26
+ } else if (character === "}") {
27
+ braceLevel--;
28
+ }
29
+
30
+ index++;
31
+ }
32
+
33
+ return -1;
34
+ };
35
+
36
+ const escapeRegex = function(string) {
37
+ return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
38
+ };
39
+
40
+ const amsRegex = /^\\begin{/;
41
+
42
+ const splitAtDelimiters = function(text, delimiters) {
43
+ let index;
44
+ const data = [];
45
+
46
+ const regexLeft = new RegExp(
47
+ "(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")"
48
+ );
49
+
50
+ while (true) {
51
+ index = text.search(regexLeft);
52
+ if (index === -1) {
53
+ break;
54
+ }
55
+ if (index > 0) {
56
+ data.push({
57
+ type: "text",
58
+ data: text.slice(0, index)
59
+ });
60
+ text = text.slice(index); // now text starts with delimiter
61
+ }
62
+ // ... so this always succeeds:
63
+ const i = delimiters.findIndex((delim) => text.startsWith(delim.left));
64
+ index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
65
+ if (index === -1) {
66
+ break;
67
+ }
68
+ const rawData = text.slice(0, index + delimiters[i].right.length);
69
+ const math = amsRegex.test(rawData)
70
+ ? rawData
71
+ : text.slice(delimiters[i].left.length, index);
72
+ data.push({
73
+ type: "math",
74
+ data: math,
75
+ rawData,
76
+ display: delimiters[i].display
77
+ });
78
+ text = text.slice(index + delimiters[i].right.length);
79
+ }
80
+
81
+ if (text !== "") {
82
+ data.push({
83
+ type: "text",
84
+ data: text
85
+ });
86
+ }
87
+
88
+ return data;
89
+ };
90
+
91
+ /* eslint no-console:0 */
92
+
93
+ /* Note: optionsCopy is mutated by this method. If it is ever exposed in the
94
+ * API, we should copy it before mutating.
95
+ */
96
+ const renderMathInText = function(text, optionsCopy) {
97
+ const data = splitAtDelimiters(text, optionsCopy.delimiters);
98
+ if (data.length === 1 && data[0].type === "text") {
99
+ // There is no formula in the text.
100
+ // Let's return null which means there is no need to replace
101
+ // the current text node with a new one.
102
+ return null;
103
+ }
104
+
105
+ const fragment = document.createDocumentFragment();
106
+
107
+ for (let i = 0; i < data.length; i++) {
108
+ if (data[i].type === "text") {
109
+ fragment.appendChild(document.createTextNode(data[i].data));
110
+ } else {
111
+ const span = document.createElement("span");
112
+ let math = data[i].data;
113
+ // Override any display mode defined in the settings with that
114
+ // defined by the text itself
115
+ optionsCopy.displayMode = data[i].display;
116
+ try {
117
+ if (optionsCopy.preProcess) {
118
+ math = optionsCopy.preProcess(math);
119
+ }
120
+ temml__default["default"].render(math, span, optionsCopy);
121
+ } catch (e) {
122
+ if (!(e instanceof temml__default["default"].ParseError)) {
123
+ throw e;
124
+ }
125
+ optionsCopy.errorCallback(
126
+ "Temml auto-render: Failed to parse `" + data[i].data + "` with ",
127
+ e
128
+ );
129
+ fragment.appendChild(document.createTextNode(data[i].rawData));
130
+ continue;
131
+ }
132
+ fragment.appendChild(span);
133
+ }
134
+ }
135
+
136
+ return fragment;
137
+ };
138
+
139
+ const renderElem = function(elem, optionsCopy) {
140
+ for (let i = 0; i < elem.childNodes.length; i++) {
141
+ const childNode = elem.childNodes[i];
142
+ if (childNode.nodeType === 3) {
143
+ // Text node
144
+ const frag = renderMathInText(childNode.textContent, optionsCopy);
145
+ if (frag) {
146
+ i += frag.childNodes.length - 1;
147
+ elem.replaceChild(frag, childNode);
148
+ }
149
+ } else if (childNode.nodeType === 1) {
150
+ // Element node
151
+ const className = " " + childNode.className + " ";
152
+ const shouldRender =
153
+ optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 &&
154
+ optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1);
155
+
156
+ if (shouldRender) {
157
+ renderElem(childNode, optionsCopy);
158
+ }
159
+ }
160
+ // Otherwise, it's something else, and ignore it.
161
+ }
162
+ };
163
+
164
+ const renderMathInElement = function(elem, options) {
165
+ if (!elem) {
166
+ throw new Error("No element provided to render");
167
+ }
168
+
169
+ const optionsCopy = {};
170
+
171
+ // Object.assign(optionsCopy, option)
172
+ for (const option in options) {
173
+ if (Object.prototype.hasOwnProperty.call(options, option)) {
174
+ optionsCopy[option] = options[option];
175
+ }
176
+ }
177
+
178
+ // default options
179
+ optionsCopy.delimiters = optionsCopy.delimiters || [
180
+ { left: "$$", right: "$$", display: true },
181
+ { left: "\\(", right: "\\)", display: false },
182
+ // LaTeX uses $…$, but it ruins the display of normal `$` in text:
183
+ // {left: "$", right: "$", display: false},
184
+ // $ must come after $$
185
+
186
+ // Render AMS environments even if outside $$…$$ delimiters.
187
+ { left: "\\begin{equation}", right: "\\end{equation}", display: true },
188
+ { left: "\\begin{align}", right: "\\end{align}", display: true },
189
+ { left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
190
+ { left: "\\begin{gather}", right: "\\end{gather}", display: true },
191
+ { left: "\\begin{CD}", right: "\\end{CD}", display: true },
192
+
193
+ { left: "\\[", right: "\\]", display: true }
194
+ ];
195
+ optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
196
+ "script",
197
+ "noscript",
198
+ "style",
199
+ "textarea",
200
+ "pre",
201
+ "code",
202
+ "option"
203
+ ];
204
+ optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
205
+ optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
206
+
207
+ // Enable sharing of global macros defined via `\gdef` between different
208
+ // math elements within a single call to `renderMathInElement`.
209
+ optionsCopy.macros = optionsCopy.macros || {};
210
+
211
+ renderElem(elem, optionsCopy);
212
+ temml__default["default"].postProcess(elem);
213
+ };
214
+
215
+ return renderMathInElement;
216
+
217
+ })(temml);
@@ -0,0 +1,59 @@
1
+ <!DOCTYPE html>
2
+ <!--To run this example from a clone of the repository, run `yarn start`
3
+ in the root Temml directory and then visit with your web browser:
4
+ http://localhost:7936/contrib/auto-render/index.html
5
+ -->
6
+ <html>
7
+ <head>
8
+ <meta charset="UTF-8">
9
+ <title>Auto-render test</title>
10
+ <link rel="stylesheet" href="../../../site/temml/temml.css">
11
+ <script src="../../../site/temml/temml.min.js" type="text/javascript"></script>
12
+ <script src="./auto-render.js" type="text/javascript"></script>
13
+ <style type="text/css">
14
+ body {
15
+ margin: 0px;
16
+ padding: 0px;
17
+ font-size: 36px;
18
+ }
19
+
20
+ #test > .blue {
21
+ color: blue;
22
+ }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <div id="test">
27
+ This is some text $math \frac12$ other text
28
+ <span class="blue">
29
+ Other node \[ displaymath \frac{1}{2} \] blah $$ \int_2^3 $$
30
+ </span>
31
+ and some <!-- comment --> more text \(and math\) blah. And $math with a
32
+ \$ sign$.
33
+ <pre>
34
+ Stuff in a $pre tag$
35
+ </pre>
36
+ <p>An AMS environment without <code>$$…$$</code> delimiters.<p>
37
+ <p>\begin{equation}\begin{split} a &=b+c\\ &=e+f \end{split} \end{equation}</p>
38
+ <p>$\unsupported$</p>
39
+ </div>
40
+ <script>
41
+ renderMathInElement(
42
+ document.getElementById("test"),
43
+ {
44
+ delimiters: [
45
+ { left: "$$", right: "$$", display: true },
46
+ { left: "\\(", right: "\\)", display: false },
47
+ { left: "\\begin{equation}", right: "\\end{equation}", display: true },
48
+ { left: "\\begin{align}", right: "\\end{align}", display: true },
49
+ { left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
50
+ { left: "\\begin{gather}", right: "\\end{gather}", display: true },
51
+ { left: "\\begin{CD}", right: "\\end{CD}", display: true },
52
+ { left: "\\begin{multline}", right: "\\end{multline}", display: true },
53
+ { left: "\\[", right: "\\]", display: true }
54
+ ]
55
+ }
56
+ );
57
+ </script>
58
+ </body>
59
+ </html>
@@ -0,0 +1,26 @@
1
+ # mhchem extension
2
+
3
+ This extension adds to Temml the `\ce` and `\pu` functions from the [mhchem](https://mhchem.github.io/MathJax-mhchem/) package.
4
+
5
+ You can download the `mhchem.min.js` file from this repository.
6
+
7
+ ### Usage
8
+
9
+ This extension isn't part of core Temml, so the script should be separately included. Write the following line into the HTML page's `<head>`. Place it _after_ the line that calls `temml.js`.
10
+
11
+ ```html
12
+ <script src="./temml.min.js"></script>
13
+ <script src="./mhchem.min.js"></script>
14
+ ```
15
+
16
+ If you are working sever-side, just use `temml.cjs`. It already includes all the functions in `mhchem.js`.
17
+
18
+ ### Syntax
19
+
20
+ See the [mhchem Manual](https://mhchem.github.io/MathJax-mhchem/) for a full explanation of the input syntax, with working examples. The manual also includes a demonstration box.
21
+
22
+ Note that old versions of `mhchem.sty` used `\cf` for chemical formula and `\ce` for chemical equations, but `\cf` has been deprecated in place of `\ce`. This extension supports only `\ce`. You can define a macro mapping `\cf` to `\ce` if needed.
23
+
24
+ ### Browser Support
25
+
26
+ This extension has been tested on Chrome, Firefox, Opera, and Edge.