temml 0.10.34 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/temml.cjs +4757 -4265
- package/dist/temml.d.ts +3 -0
- package/dist/temml.js +3107 -2615
- package/dist/temml.min.js +1 -1
- package/dist/temml.mjs +4757 -4265
- package/dist/temmlPostProcess.js +2 -1
- package/package.json +2 -2
- package/src/ParseError.js +1 -1
- package/src/auto-render.js +263 -0
- package/src/environments/array.js +81 -34
- package/src/environments/borderTree.js +139 -0
- package/src/functions/bordermatrix.js +42 -0
- package/src/functions.js +1 -0
- package/src/linebreaking.js +1 -1
- package/src/postProcess.js +2 -1
- package/temml.js +7 -1
- package/contrib/auto-render/README.md +0 -89
- package/contrib/auto-render/auto-render.js +0 -128
- package/contrib/auto-render/dist/auto-render.js +0 -214
- package/contrib/auto-render/dist/auto-render.min.js +0 -1
- package/contrib/auto-render/splitAtDelimiters.js +0 -84
- package/contrib/auto-render/test/auto-render-spec.js +0 -234
- package/contrib/auto-render/test/auto-render.js +0 -214
- package/contrib/auto-render/test/test_page.html +0 -59
@@ -1,234 +0,0 @@
|
|
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 multiple 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
|
-
});
|
@@ -1,214 +0,0 @@
|
|
1
|
-
var renderMathInElement = (function (temml) {
|
2
|
-
'use strict';
|
3
|
-
|
4
|
-
/* eslint no-constant-condition:0 */
|
5
|
-
const findEndOfMath = function(delimiter, text, startIndex) {
|
6
|
-
// Adapted from
|
7
|
-
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
8
|
-
let index = startIndex;
|
9
|
-
let braceLevel = 0;
|
10
|
-
|
11
|
-
const delimLength = delimiter.length;
|
12
|
-
|
13
|
-
while (index < text.length) {
|
14
|
-
const character = text[index];
|
15
|
-
|
16
|
-
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
17
|
-
return index;
|
18
|
-
} else if (character === "\\") {
|
19
|
-
index++;
|
20
|
-
} else if (character === "{") {
|
21
|
-
braceLevel++;
|
22
|
-
} else if (character === "}") {
|
23
|
-
braceLevel--;
|
24
|
-
}
|
25
|
-
|
26
|
-
index++;
|
27
|
-
}
|
28
|
-
|
29
|
-
return -1;
|
30
|
-
};
|
31
|
-
|
32
|
-
const escapeRegex = function(string) {
|
33
|
-
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
34
|
-
};
|
35
|
-
|
36
|
-
const amsRegex = /^\\begin{/;
|
37
|
-
|
38
|
-
const splitAtDelimiters = function(text, delimiters) {
|
39
|
-
let index;
|
40
|
-
const data = [];
|
41
|
-
|
42
|
-
const regexLeft = new RegExp(
|
43
|
-
"(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")"
|
44
|
-
);
|
45
|
-
|
46
|
-
while (true) {
|
47
|
-
index = text.search(regexLeft);
|
48
|
-
if (index === -1) {
|
49
|
-
break;
|
50
|
-
}
|
51
|
-
if (index > 0) {
|
52
|
-
data.push({
|
53
|
-
type: "text",
|
54
|
-
data: text.slice(0, index)
|
55
|
-
});
|
56
|
-
text = text.slice(index); // now text starts with delimiter
|
57
|
-
}
|
58
|
-
// ... so this always succeeds:
|
59
|
-
const i = delimiters.findIndex((delim) => text.startsWith(delim.left));
|
60
|
-
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
61
|
-
if (index === -1) {
|
62
|
-
break;
|
63
|
-
}
|
64
|
-
const rawData = text.slice(0, index + delimiters[i].right.length);
|
65
|
-
const math = amsRegex.test(rawData)
|
66
|
-
? rawData
|
67
|
-
: text.slice(delimiters[i].left.length, index);
|
68
|
-
data.push({
|
69
|
-
type: "math",
|
70
|
-
data: math,
|
71
|
-
rawData,
|
72
|
-
display: delimiters[i].display
|
73
|
-
});
|
74
|
-
text = text.slice(index + delimiters[i].right.length);
|
75
|
-
}
|
76
|
-
|
77
|
-
if (text !== "") {
|
78
|
-
data.push({
|
79
|
-
type: "text",
|
80
|
-
data: text
|
81
|
-
});
|
82
|
-
}
|
83
|
-
|
84
|
-
return data;
|
85
|
-
};
|
86
|
-
|
87
|
-
/* eslint no-console:0 */
|
88
|
-
|
89
|
-
|
90
|
-
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
91
|
-
* API, we should copy it before mutating.
|
92
|
-
*/
|
93
|
-
const renderMathInText = function(text, optionsCopy) {
|
94
|
-
const data = splitAtDelimiters(text, optionsCopy.delimiters);
|
95
|
-
if (data.length === 1 && data[0].type === "text") {
|
96
|
-
// There is no formula in the text.
|
97
|
-
// Let's return null which means there is no need to replace
|
98
|
-
// the current text node with a new one.
|
99
|
-
return null;
|
100
|
-
}
|
101
|
-
|
102
|
-
const fragment = document.createDocumentFragment();
|
103
|
-
|
104
|
-
for (let i = 0; i < data.length; i++) {
|
105
|
-
if (data[i].type === "text") {
|
106
|
-
fragment.appendChild(document.createTextNode(data[i].data));
|
107
|
-
} else {
|
108
|
-
const span = document.createElement("span");
|
109
|
-
let math = data[i].data;
|
110
|
-
// Override any display mode defined in the settings with that
|
111
|
-
// defined by the text itself
|
112
|
-
optionsCopy.displayMode = data[i].display;
|
113
|
-
try {
|
114
|
-
if (optionsCopy.preProcess) {
|
115
|
-
math = optionsCopy.preProcess(math);
|
116
|
-
}
|
117
|
-
temml.render(math, span, optionsCopy);
|
118
|
-
} catch (e) {
|
119
|
-
if (!(e instanceof temml.ParseError)) {
|
120
|
-
throw e;
|
121
|
-
}
|
122
|
-
optionsCopy.errorCallback(
|
123
|
-
"Temml auto-render: Failed to parse `" + data[i].data + "` with ",
|
124
|
-
e
|
125
|
-
);
|
126
|
-
fragment.appendChild(document.createTextNode(data[i].rawData));
|
127
|
-
continue;
|
128
|
-
}
|
129
|
-
fragment.appendChild(span);
|
130
|
-
}
|
131
|
-
}
|
132
|
-
|
133
|
-
return fragment;
|
134
|
-
};
|
135
|
-
|
136
|
-
const renderElem = function(elem, optionsCopy) {
|
137
|
-
for (let i = 0; i < elem.childNodes.length; i++) {
|
138
|
-
const childNode = elem.childNodes[i];
|
139
|
-
if (childNode.nodeType === 3) {
|
140
|
-
// Text node
|
141
|
-
const frag = renderMathInText(childNode.textContent, optionsCopy);
|
142
|
-
if (frag) {
|
143
|
-
i += frag.childNodes.length - 1;
|
144
|
-
elem.replaceChild(frag, childNode);
|
145
|
-
}
|
146
|
-
} else if (childNode.nodeType === 1) {
|
147
|
-
// Element node
|
148
|
-
const className = " " + childNode.className + " ";
|
149
|
-
const shouldRender =
|
150
|
-
optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 &&
|
151
|
-
optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1);
|
152
|
-
|
153
|
-
if (shouldRender) {
|
154
|
-
renderElem(childNode, optionsCopy);
|
155
|
-
}
|
156
|
-
}
|
157
|
-
// Otherwise, it's something else, and ignore it.
|
158
|
-
}
|
159
|
-
};
|
160
|
-
|
161
|
-
const renderMathInElement = function(elem, options) {
|
162
|
-
if (!elem) {
|
163
|
-
throw new Error("No element provided to render");
|
164
|
-
}
|
165
|
-
|
166
|
-
const optionsCopy = {};
|
167
|
-
|
168
|
-
// Object.assign(optionsCopy, option)
|
169
|
-
for (const option in options) {
|
170
|
-
if (Object.prototype.hasOwnProperty.call(options, option)) {
|
171
|
-
optionsCopy[option] = options[option];
|
172
|
-
}
|
173
|
-
}
|
174
|
-
|
175
|
-
// default options
|
176
|
-
optionsCopy.delimiters = optionsCopy.delimiters || [
|
177
|
-
{ left: "$$", right: "$$", display: true },
|
178
|
-
{ left: "\\(", right: "\\)", display: false },
|
179
|
-
// LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
180
|
-
// {left: "$", right: "$", display: false},
|
181
|
-
// $ must come after $$
|
182
|
-
|
183
|
-
// Render AMS environments even if outside $$…$$ delimiters.
|
184
|
-
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
185
|
-
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
186
|
-
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
187
|
-
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
188
|
-
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
189
|
-
|
190
|
-
{ left: "\\[", right: "\\]", display: true }
|
191
|
-
];
|
192
|
-
optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
|
193
|
-
"script",
|
194
|
-
"noscript",
|
195
|
-
"style",
|
196
|
-
"textarea",
|
197
|
-
"pre",
|
198
|
-
"code",
|
199
|
-
"option"
|
200
|
-
];
|
201
|
-
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
202
|
-
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
|
203
|
-
|
204
|
-
// Enable sharing of global macros defined via `\gdef` between different
|
205
|
-
// math elements within a single call to `renderMathInElement`.
|
206
|
-
optionsCopy.macros = optionsCopy.macros || {};
|
207
|
-
|
208
|
-
renderElem(elem, optionsCopy);
|
209
|
-
temml.postProcess(elem);
|
210
|
-
};
|
211
|
-
|
212
|
-
return renderMathInElement;
|
213
|
-
|
214
|
-
})(temml);
|
@@ -1,59 +0,0 @@
|
|
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>
|