Diff: STRATO-apps/wordpress_03/app/wp-includes/js/dist/latex-to-mathml.js
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
/******/ (() => { // webpackBootstrap
2
+
/******/ "use strict";
3
+
/******/ // The require scope
4
+
/******/ var __webpack_require__ = {};
5
+
/******/
6
+
/************************************************************************/
7
+
/******/ /* webpack/runtime/define property getters */
8
+
/******/ (() => {
9
+
/******/ // define getter functions for harmony exports
10
+
/******/ __webpack_require__.d = (exports, definition) => {
11
+
/******/ for(var key in definition) {
12
+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
13
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
14
+
/******/ }
15
+
/******/ }
16
+
/******/ };
17
+
/******/ })();
18
+
/******/
19
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
20
+
/******/ (() => {
21
+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
22
+
/******/ })();
23
+
/******/
24
+
/******/ /* webpack/runtime/make namespace object */
25
+
/******/ (() => {
26
+
/******/ // define __esModule on exports
27
+
/******/ __webpack_require__.r = (exports) => {
28
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
29
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
30
+
/******/ }
31
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
32
+
/******/ };
33
+
/******/ })();
34
+
/******/
35
+
/************************************************************************/
36
+
var __webpack_exports__ = {};
37
+
// ESM COMPAT FLAG
38
+
__webpack_require__.r(__webpack_exports__);
39
+
40
+
// EXPORTS
41
+
__webpack_require__.d(__webpack_exports__, {
42
+
"default": () => (/* binding */ latexToMathML)
43
+
});
44
+
45
+
;// ./node_modules/temml/dist/temml.mjs
46
+
/**
47
+
* This is the ParseError class, which is the main error thrown by Temml
48
+
* functions when something has gone wrong. This is used to distinguish internal
49
+
* errors from errors in the expression that the user provided.
50
+
*
51
+
* If possible, a caller should provide a Token or ParseNode with information
52
+
* about where in the source string the problem occurred.
53
+
*/
54
+
class ParseError {
55
+
constructor(
56
+
message, // The error message
57
+
token // An object providing position information
58
+
) {
59
+
let error = " " + message;
60
+
let start;
61
+
62
+
const loc = token && token.loc;
63
+
if (loc && loc.start <= loc.end) {
64
+
// If we have the input and a position, make the error a bit fancier
65
+
66
+
// Get the input
67
+
const input = loc.lexer.input;
68
+
69
+
// Prepend some information
70
+
start = loc.start;
71
+
const end = loc.end;
72
+
if (start === input.length) {
73
+
error += " at end of input: ";
74
+
} else {
75
+
error += " at position " + (start + 1) + ": ";
76
+
}
77
+
78
+
// Underline token in question using combining underscores
79
+
const underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332");
80
+
81
+
// Extract some context from the input and add it to the error
82
+
let left;
83
+
if (start > 15) {
84
+
left = "…" + input.slice(start - 15, start);
85
+
} else {
86
+
left = input.slice(0, start);
87
+
}
88
+
let right;
89
+
if (end + 15 < input.length) {
90
+
right = input.slice(end, end + 15) + "…";
91
+
} else {
92
+
right = input.slice(end);
93
+
}
94
+
error += left + underlined + right;
95
+
}
96
+
97
+
// Some hackery to make ParseError a prototype of Error
98
+
// See http://stackoverflow.com/a/8460753
99
+
const self = new Error(error);
100
+
self.name = "ParseError";
101
+
self.__proto__ = ParseError.prototype;
102
+
self.position = start;
103
+
return self;
104
+
}
105
+
}
106
+
107
+
ParseError.prototype.__proto__ = Error.prototype;
108
+
109
+
//
110
+
/**
111
+
* This file contains a list of utility functions which are useful in other
112
+
* files.
113
+
*/
114
+
115
+
/**
116
+
* Provide a default value if a setting is undefined
117
+
*/
118
+
const deflt = function(setting, defaultIfUndefined) {
119
+
return setting === undefined ? defaultIfUndefined : setting;
120
+
};
121
+
122
+
// hyphenate and escape adapted from Facebook's React under Apache 2 license
123
+
124
+
const uppercase = /([A-Z])/g;
125
+
const hyphenate = function(str) {
126
+
return str.replace(uppercase, "-$1").toLowerCase();
127
+
};
128
+
129
+
const ESCAPE_LOOKUP = {
130
+
"&": "&",
131
+
">": ">",
132
+
"<": "<",
133
+
'"': """,
134
+
"'": "'"
135
+
};
136
+
137
+
const ESCAPE_REGEX = /[&><"']/g;
138
+
139
+
/**
140
+
* Escapes text to prevent scripting attacks.
141
+
*/
142
+
function temml_escape(text) {
143
+
return String(text).replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
144
+
}
145
+
146
+
/**
147
+
* Sometimes we want to pull out the innermost element of a group. In most
148
+
* cases, this will just be the group itself, but when ordgroups and colors have
149
+
* a single element, we want to pull that out.
150
+
*/
151
+
const getBaseElem = function(group) {
152
+
if (group.type === "ordgroup") {
153
+
if (group.body.length === 1) {
154
+
return getBaseElem(group.body[0]);
155
+
} else {
156
+
return group;
157
+
}
158
+
} else if (group.type === "color") {
159
+
if (group.body.length === 1) {
160
+
return getBaseElem(group.body[0]);
161
+
} else {
162
+
return group;
163
+
}
164
+
} else if (group.type === "font") {
165
+
return getBaseElem(group.body);
166
+
} else {
167
+
return group;
168
+
}
169
+
};
170
+
171
+
/**
172
+
* TeXbook algorithms often reference "character boxes", which are simply groups
173
+
* with a single character in them. To decide if something is a character box,
174
+
* we find its innermost group, and see if it is a single character.
175
+
*/
176
+
const isCharacterBox = function(group) {
177
+
const baseElem = getBaseElem(group);
178
+
179
+
// These are all the types of groups which hold single characters
180
+
return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom"
181
+
};
182
+
183
+
const assert = function(value) {
184
+
if (!value) {
185
+
throw new Error("Expected non-null, but got " + String(value));
186
+
}
187
+
return value;
188
+
};
189
+
190
+
/**
191
+
* Return the protocol of a URL, or "_relative" if the URL does not specify a
192
+
* protocol (and thus is relative), or `null` if URL has invalid protocol
193
+
* (so should be outright rejected).
194
+
*/
195
+
const protocolFromUrl = function(url) {
196
+
// Check for possible leading protocol.
197
+
// https://url.spec.whatwg.org/#url-parsing strips leading whitespace
198
+
// (\x00) or C0 control (\x00-\x1F) characters.
199
+
// eslint-disable-next-line no-control-regex
200
+
const protocol = /^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(url);
201
+
if (!protocol) {
202
+
return "_relative";
203
+
}
204
+
// Reject weird colons
205
+
if (protocol[2] !== ":") {
206
+
return null;
207
+
}
208
+
// Reject invalid characters in scheme according to
209
+
// https://datatracker.ietf.org/doc/html/rfc3986#section-3.1
210
+
if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(protocol[1])) {
211
+
return null;
212
+
}
213
+
// Lowercase the protocol
214
+
return protocol[1].toLowerCase();
215
+
};
216
+
217
+
/**
218
+
* Round `n` to 4 decimal places, or to the nearest 1/10,000th em. The TeXbook
219
+
* gives an acceptable rounding error of 100sp (which would be the nearest
220
+
* 1/6551.6em with our ptPerEm = 10):
221
+
* http://www.ctex.org/documents/shredder/src/texbook.pdf#page=69
222
+
*/
223
+
const round = function(n) {
224
+
return +n.toFixed(4);
225
+
};
226
+
227
+
var utils = {
228
+
deflt,
229
+
escape: temml_escape,
230
+
hyphenate,
231
+
getBaseElem,
232
+
isCharacterBox,
233
+
protocolFromUrl,
234
+
round
235
+
};
236
+
237
+
/**
238
+
* This is a module for storing settings passed into Temml. It correctly handles
239
+
* default settings.
240
+
*/
241
+
242
+
243
+
/**
244
+
* The main Settings object
245
+
*/
246
+
class Settings {
247
+
constructor(options) {
248
+
// allow null options
249
+
options = options || {};
250
+
this.displayMode = utils.deflt(options.displayMode, false); // boolean
251
+
this.annotate = utils.deflt(options.annotate, false); // boolean
252
+
this.leqno = utils.deflt(options.leqno, false); // boolean
253
+
this.throwOnError = utils.deflt(options.throwOnError, false); // boolean
254
+
this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
255
+
this.macros = options.macros || {};
256
+
this.wrap = utils.deflt(options.wrap, "tex"); // "tex" | "="
257
+
this.xml = utils.deflt(options.xml, false); // boolean
258
+
this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
259
+
this.strict = utils.deflt(options.strict, false); // boolean
260
+
this.trust = utils.deflt(options.trust, false); // trust context. See html.js.
261
+
this.maxSize = (options.maxSize === undefined
262
+
? [Infinity, Infinity]
263
+
: Array.isArray(options.maxSize)
264
+
? options.maxSize
265
+
: [Infinity, Infinity]
266
+
);
267
+
this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000)); // number
268
+
}
269
+
270
+
/**
271
+
* Check whether to test potentially dangerous input, and return
272
+
* `true` (trusted) or `false` (untrusted). The sole argument `context`
273
+
* should be an object with `command` field specifying the relevant LaTeX
274
+
* command (as a string starting with `\`), and any other arguments, etc.
275
+
* If `context` has a `url` field, a `protocol` field will automatically
276
+
* get added by this function (changing the specified object).
277
+
*/
278
+
isTrusted(context) {
279
+
if (context.url && !context.protocol) {
280
+
const protocol = utils.protocolFromUrl(context.url);
281
+
if (protocol == null) {
282
+
return false
283
+
}
284
+
context.protocol = protocol;
285
+
}
286
+
const trust = typeof this.trust === "function" ? this.trust(context) : this.trust;
287
+
return Boolean(trust);
288
+
}
289
+
}
290
+
291
+
/**
292
+
* All registered functions.
293
+
* `functions.js` just exports this same dictionary again and makes it public.
294
+
* `Parser.js` requires this dictionary.
295
+
*/
296
+
const _functions = {};
297
+
298
+
/**
299
+
* All MathML builders. Should be only used in the `define*` and the `build*ML`
300
+
* functions.
301
+
*/
302
+
const _mathmlGroupBuilders = {};
303
+
304
+
function defineFunction({
305
+
type,
306
+
names,
307
+
props,
308
+
handler,
309
+
mathmlBuilder
310
+
}) {
311
+
// Set default values of functions
312
+
const data = {
313
+
type,
314
+
numArgs: props.numArgs,
315
+
argTypes: props.argTypes,
316
+
allowedInArgument: !!props.allowedInArgument,
317
+
allowedInText: !!props.allowedInText,
318
+
allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath,
319
+
numOptionalArgs: props.numOptionalArgs || 0,
320
+
infix: !!props.infix,
321
+
primitive: !!props.primitive,
322
+
handler: handler
323
+
};
324
+
for (let i = 0; i < names.length; ++i) {
325
+
_functions[names[i]] = data;
326
+
}
327
+
if (type) {
328
+
if (mathmlBuilder) {
329
+
_mathmlGroupBuilders[type] = mathmlBuilder;
330
+
}
331
+
}
332
+
}
333
+
334
+
/**
335
+
* Use this to register only the MathML builder for a function(e.g.
336
+
* if the function's ParseNode is generated in Parser.js rather than via a
337
+
* stand-alone handler provided to `defineFunction`).
338
+
*/
339
+
function defineFunctionBuilders({ type, mathmlBuilder }) {
340
+
defineFunction({
341
+
type,
342
+
names: [],
343
+
props: { numArgs: 0 },
344
+
handler() {
345
+
throw new Error("Should never be called.")
346
+
},
347
+
mathmlBuilder
348
+
});
349
+
}
350
+
351
+
const normalizeArgument = function(arg) {
352
+
return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg
353
+
};
354
+
355
+
// Since the corresponding buildMathML function expects a
356
+
// list of elements, we normalize for different kinds of arguments
357
+
const ordargument = function(arg) {
358
+
return arg.type === "ordgroup" ? arg.body : [arg]
359
+
};
360
+
361
+
/**
362
+
* This node represents a document fragment, which contains elements, but when
363
+
* placed into the DOM doesn't have any representation itself. It only contains
364
+
* children and doesn't have any DOM node properties.
365
+
*/
366
+
class DocumentFragment {
367
+
constructor(children) {
368
+
this.children = children;
369
+
this.classes = [];
370
+
this.style = {};
371
+
}
372
+
373
+
hasClass(className) {
374
+
return this.classes.includes(className);
375
+
}
376
+
377
+
/** Convert the fragment into a node. */
378
+
toNode() {
379
+
const frag = document.createDocumentFragment();
380
+
381
+
for (let i = 0; i < this.children.length; i++) {
382
+
frag.appendChild(this.children[i].toNode());
383
+
}
384
+
385
+
return frag;
386
+
}
387
+
388
+
/** Convert the fragment into HTML markup. */
389
+
toMarkup() {
390
+
let markup = "";
391
+
392
+
// Simply concatenate the markup for the children together.
393
+
for (let i = 0; i < this.children.length; i++) {
394
+
markup += this.children[i].toMarkup();
395
+
}
396
+
397
+
return markup;
398
+
}
399
+
400
+
/**
401
+
* Converts the math node into a string, similar to innerText. Applies to
402
+
* MathDomNode's only.
403
+
*/
404
+
toText() {
405
+
// To avoid this, we would subclass documentFragment separately for
406
+
// MathML, but polyfills for subclassing is expensive per PR 1469.
407
+
const toText = (child) => child.toText();
408
+
return this.children.map(toText).join("");
409
+
}
410
+
}
411
+
412
+
/**
413
+
* These objects store the data about the DOM nodes we create, as well as some
414
+
* extra data. They can then be transformed into real DOM nodes with the
415
+
* `toNode` function or HTML markup using `toMarkup`. They are useful for both
416
+
* storing extra properties on the nodes, as well as providing a way to easily
417
+
* work with the DOM.
418
+
*
419
+
* Similar functions for working with MathML nodes exist in mathMLTree.js.
420
+
*
421
+
*/
422
+
423
+
/**
424
+
* Create an HTML className based on a list of classes. In addition to joining
425
+
* with spaces, we also remove empty classes.
426
+
*/
427
+
const createClass = function(classes) {
428
+
return classes.filter((cls) => cls).join(" ");
429
+
};
430
+
431
+
const initNode = function(classes, style) {
432
+
this.classes = classes || [];
433
+
this.attributes = {};
434
+
this.style = style || {};
435
+
};
436
+
437
+
/**
438
+
* Convert into an HTML node
439
+
*/
440
+
const toNode = function(tagName) {
441
+
const node = document.createElement(tagName);
442
+
443
+
// Apply the class
444
+
node.className = createClass(this.classes);
445
+
446
+
// Apply inline styles
447
+
for (const style in this.style) {
448
+
if (Object.prototype.hasOwnProperty.call(this.style, style )) {
449
+
node.style[style] = this.style[style];
450
+
}
451
+
}
452
+
453
+
// Apply attributes
454
+
for (const attr in this.attributes) {
455
+
if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) {
456
+
node.setAttribute(attr, this.attributes[attr]);
457
+
}
458
+
}
459
+
460
+
// Append the children, also as HTML nodes
461
+
for (let i = 0; i < this.children.length; i++) {
462
+
node.appendChild(this.children[i].toNode());
463
+
}
464
+
465
+
return node;
466
+
};
467
+
468
+
/**
469
+
* Convert into an HTML markup string
470
+
*/
471
+
const toMarkup = function(tagName) {
472
+
let markup = `<${tagName}`;
473
+
474
+
// Add the class
475
+
if (this.classes.length) {
476
+
markup += ` class="${utils.escape(createClass(this.classes))}"`;
477
+
}
478
+
479
+
let styles = "";
480
+
481
+
// Add the styles, after hyphenation
482
+
for (const style in this.style) {
483
+
if (Object.prototype.hasOwnProperty.call(this.style, style )) {
484
+
styles += `${utils.hyphenate(style)}:${this.style[style]};`;
485
+
}
486
+
}
487
+
488
+
if (styles) {
489
+
markup += ` style="${styles}"`;
490
+
}
491
+
492
+
// Add the attributes
493
+
for (const attr in this.attributes) {
494
+
if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) {
495
+
markup += ` ${attr}="${utils.escape(this.attributes[attr])}"`;
496
+
}
497
+
}
498
+
499
+
markup += ">";
500
+
501
+
// Add the markup of the children, also as markup
502
+
for (let i = 0; i < this.children.length; i++) {
503
+
markup += this.children[i].toMarkup();
504
+
}
505
+
506
+
markup += `</${tagName}>`;
507
+
508
+
return markup;
509
+
};
510
+
511
+
/**
512
+
* This node represents a span node, with a className, a list of children, and
513
+
* an inline style.
514
+
*
515
+
*/
516
+
class Span {
517
+
constructor(classes, children, style) {
518
+
initNode.call(this, classes, style);
519
+
this.children = children || [];
520
+
}
521
+
522
+
setAttribute(attribute, value) {
523
+
this.attributes[attribute] = value;
524
+
}
525
+
526
+
toNode() {
527
+
return toNode.call(this, "span");
528
+
}
529
+
530
+
toMarkup() {
531
+
return toMarkup.call(this, "span");
532
+
}
533
+
}
534
+
535
+
let TextNode$1 = class TextNode {
536
+
constructor(text) {
537
+
this.text = text;
538
+
}
539
+
toNode() {
540
+
return document.createTextNode(this.text);
541
+
}
542
+
toMarkup() {
543
+
return utils.escape(this.text);
544
+
}
545
+
};
546
+
547
+
// Create an <a href="…"> node.
548
+
class AnchorNode {
549
+
constructor(href, classes, children) {
550
+
this.href = href;
551
+
this.classes = classes;
552
+
this.children = children || [];
553
+
}
554
+
555
+
toNode() {
556
+
const node = document.createElement("a");
557
+
node.setAttribute("href", this.href);
558
+
if (this.classes.length > 0) {
559
+
node.className = createClass(this.classes);
560
+
}
561
+
for (let i = 0; i < this.children.length; i++) {
562
+
node.appendChild(this.children[i].toNode());
563
+
}
564
+
return node
565
+
}
566
+
567
+
toMarkup() {
568
+
let markup = `<a href='${utils.escape(this.href)}'`;
569
+
if (this.classes.length > 0) {
570
+
markup += ` class="${utils.escape(createClass(this.classes))}"`;
571
+
}
572
+
markup += ">";
573
+
for (let i = 0; i < this.children.length; i++) {
574
+
markup += this.children[i].toMarkup();
575
+
}
576
+
markup += "</a>";
577
+
return markup
578
+
}
579
+
}
580
+
581
+
/*
582
+
* This node represents an image embed (<img>) element.
583
+
*/
584
+
class Img {
585
+
constructor(src, alt, style) {
586
+
this.alt = alt;
587
+
this.src = src;
588
+
this.classes = ["mord"];
589
+
this.style = style;
590
+
}
591
+
592
+
hasClass(className) {
593
+
return this.classes.includes(className);
594
+
}
595
+
596
+
toNode() {
597
+
const node = document.createElement("img");
598
+
node.src = this.src;
599
+
node.alt = this.alt;
600
+
node.className = "mord";
601
+
602
+
// Apply inline styles
603
+
for (const style in this.style) {
604
+
if (Object.prototype.hasOwnProperty.call(this.style, style )) {
605
+
node.style[style] = this.style[style];
606
+
}
607
+
}
608
+
609
+
return node;
610
+
}
611
+
612
+
toMarkup() {
613
+
let markup = `<img src='${this.src}' alt='${this.alt}'`;
614
+
615
+
// Add the styles, after hyphenation
616
+
let styles = "";
617
+
for (const style in this.style) {
618
+
if (Object.prototype.hasOwnProperty.call(this.style, style )) {
619
+
styles += `${utils.hyphenate(style)}:${this.style[style]};`;
620
+
}
621
+
}
622
+
if (styles) {
623
+
markup += ` style="${utils.escape(styles)}"`;
624
+
}
625
+
626
+
markup += ">";
627
+
return markup;
628
+
}
629
+
}
630
+
631
+
//
632
+
/**
633
+
* These objects store data about MathML nodes.
634
+
* The `toNode` and `toMarkup` functions create namespaced DOM nodes and
635
+
* HTML text markup respectively.
636
+
*/
637
+
638
+
639
+
function newDocumentFragment(children) {
640
+
return new DocumentFragment(children);
641
+
}
642
+
643
+
/**
644
+
* This node represents a general purpose MathML node of any type,
645
+
* for example, `"mo"` or `"mspace"`, corresponding to `<mo>` and
646
+
* `<mspace>` tags).
647
+
*/
648
+
class MathNode {
649
+
constructor(type, children, classes, style) {
650
+
this.type = type;
651
+
this.attributes = {};
652
+
this.children = children || [];
653
+
this.classes = classes || [];
654
+
this.style = style || {}; // Used for <mstyle> elements
655
+
this.label = "";
656
+
}
657
+
658
+
/**
659
+
* Sets an attribute on a MathML node. MathML depends on attributes to convey a
660
+
* semantic content, so this is used heavily.
661
+
*/
662
+
setAttribute(name, value) {
663
+
this.attributes[name] = value;
664
+
}
665
+
666
+
/**
667
+
* Gets an attribute on a MathML node.
668
+
*/
669
+
getAttribute(name) {
670
+
return this.attributes[name];
671
+
}
672
+
673
+
setLabel(value) {
674
+
this.label = value;
675
+
}
676
+
677
+
/**
678
+
* Converts the math node into a MathML-namespaced DOM element.
679
+
*/
680
+
toNode() {
681
+
const node = document.createElementNS("http://www.w3.org/1998/Math/MathML", this.type);
682
+
683
+
for (const attr in this.attributes) {
684
+
if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
685
+
node.setAttribute(attr, this.attributes[attr]);
686
+
}
687
+
}
688
+
689
+
if (this.classes.length > 0) {
690
+
node.className = createClass(this.classes);
691
+
}
692
+
693
+
// Apply inline styles
694
+
for (const style in this.style) {
695
+
if (Object.prototype.hasOwnProperty.call(this.style, style )) {
696
+
node.style[style] = this.style[style];
697
+
}
698
+
}
699
+
700
+
for (let i = 0; i < this.children.length; i++) {
701
+
node.appendChild(this.children[i].toNode());
702
+
}
703
+
704
+
return node;
705
+
}
706
+
707
+
/**
708
+
* Converts the math node into an HTML markup string.
709
+
*/
710
+
toMarkup() {
711
+
let markup = "<" + this.type;
712
+
713
+
// Add the attributes
714
+
for (const attr in this.attributes) {
715
+
if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
716
+
markup += " " + attr + '="';
717
+
markup += utils.escape(this.attributes[attr]);
718
+
markup += '"';
719
+
}
720
+
}
721
+
722
+
if (this.classes.length > 0) {
723
+
markup += ` class="${utils.escape(createClass(this.classes))}"`;
724
+
}
725
+
726
+
let styles = "";
727
+
728
+
// Add the styles, after hyphenation
729
+
for (const style in this.style) {
730
+
if (Object.prototype.hasOwnProperty.call(this.style, style )) {
731
+
styles += `${utils.hyphenate(style)}:${this.style[style]};`;
732
+
}
733
+
}
734
+
735
+
if (styles) {
736
+
markup += ` style="${styles}"`;
737
+
}
738
+
739
+
markup += ">";
740
+
741
+
for (let i = 0; i < this.children.length; i++) {
742
+
markup += this.children[i].toMarkup();
743
+
}
744
+
745
+
markup += "</" + this.type + ">";
746
+
747
+
return markup;
748
+
}
749
+
750
+
/**
751
+
* Converts the math node into a string, similar to innerText, but escaped.
752
+
*/
753
+
toText() {
754
+
return this.children.map((child) => child.toText()).join("");
755
+
}
756
+
}
757
+
758
+
/**
759
+
* This node represents a piece of text.
760
+
*/
761
+
class TextNode {
762
+
constructor(text) {
763
+
this.text = text;
764
+
}
765
+
766
+
/**
767
+
* Converts the text node into a DOM text node.
768
+
*/
769
+
toNode() {
770
+
return document.createTextNode(this.text);
771
+
}
772
+
773
+
/**
774
+
* Converts the text node into escaped HTML markup
775
+
* (representing the text itself).
776
+
*/
777
+
toMarkup() {
778
+
return utils.escape(this.toText());
779
+
}
780
+
781
+
/**
782
+
* Converts the text node into a string
783
+
* (representing the text itself).
784
+
*/
785
+
toText() {
786
+
return this.text;
787
+
}
788
+
}
789
+
790
+
// Do not make an <mrow> the only child of a <mstyle>.
791
+
// An <mstyle> acts as its own implicit <mrow>.
792
+
const wrapWithMstyle = expression => {
793
+
let node;
794
+
if (expression.length === 1 && expression[0].type === "mrow") {
795
+
node = expression.pop();
796
+
node.type = "mstyle";
797
+
} else {
798
+
node = new MathNode("mstyle", expression);
799
+
}
800
+
return node
801
+
};
802
+
803
+
var mathMLTree = {
804
+
MathNode,
805
+
TextNode,
806
+
newDocumentFragment
807
+
};
808
+
809
+
/**
810
+
* This file provides support for building horizontal stretchy elements.
811
+
*/
812
+
813
+
814
+
// TODO: Remove when Chromium stretches \widetilde & \widehat
815
+
const estimatedWidth = node => {
816
+
let width = 0;
817
+
if (node.body) {
818
+
for (const item of node.body) {
819
+
width += estimatedWidth(item);
820
+
}
821
+
} else if (node.type === "supsub") {
822
+
width += estimatedWidth(node.base);
823
+
if (node.sub) { width += 0.7 * estimatedWidth(node.sub); }
824
+
if (node.sup) { width += 0.7 * estimatedWidth(node.sup); }
825
+
} else if (node.type === "mathord" || node.type === "textord") {
826
+
for (const ch of node.text.split('')) {
827
+
const codePoint = ch.codePointAt(0);
828
+
if ((0x60 < codePoint && codePoint < 0x7B) || (0x03B0 < codePoint && codePoint < 0x3CA)) {
829
+
width += 0.56; // lower case latin or greek. Use advance width of letter n
830
+
} else if (0x2F < codePoint && codePoint < 0x3A) {
831
+
width += 0.50; // numerals.
832
+
} else {
833
+
width += 0.92; // advance width of letter M
834
+
}
835
+
}
836
+
} else {
837
+
width += 1.0;
838
+
}
839
+
return width
840
+
};
841
+
842
+
const stretchyCodePoint = {
843
+
widehat: "^",
844
+
widecheck: "ˇ",
845
+
widetilde: "~",
846
+
wideparen: "⏜", // \u23dc
847
+
utilde: "~",
848
+
overleftarrow: "\u2190",
849
+
underleftarrow: "\u2190",
850
+
xleftarrow: "\u2190",
851
+
overrightarrow: "\u2192",
852
+
underrightarrow: "\u2192",
853
+
xrightarrow: "\u2192",
854
+
underbrace: "\u23df",
855
+
overbrace: "\u23de",
856
+
overgroup: "\u23e0",
857
+
overparen: "⏜",
858
+
undergroup: "\u23e1",
859
+
underparen: "\u23dd",
860
+
overleftrightarrow: "\u2194",
861
+
underleftrightarrow: "\u2194",
862
+
xleftrightarrow: "\u2194",
863
+
Overrightarrow: "\u21d2",
864
+
xRightarrow: "\u21d2",
865
+
overleftharpoon: "\u21bc",
866
+
xleftharpoonup: "\u21bc",
867
+
overrightharpoon: "\u21c0",
868
+
xrightharpoonup: "\u21c0",
869
+
xLeftarrow: "\u21d0",
870
+
xLeftrightarrow: "\u21d4",
871
+
xhookleftarrow: "\u21a9",
872
+
xhookrightarrow: "\u21aa",
873
+
xmapsto: "\u21a6",
874
+
xrightharpoondown: "\u21c1",
875
+
xleftharpoondown: "\u21bd",
876
+
xtwoheadleftarrow: "\u219e",
877
+
xtwoheadrightarrow: "\u21a0",
878
+
xlongequal: "=",
879
+
xrightleftarrows: "\u21c4",
880
+
yields: "\u2192",
881
+
yieldsLeft: "\u2190",
882
+
mesomerism: "\u2194",
883
+
longrightharpoonup: "\u21c0",
884
+
longleftharpoondown: "\u21bd",
885
+
eqrightharpoonup: "\u21c0",
886
+
eqleftharpoondown: "\u21bd",
887
+
"\\cdrightarrow": "\u2192",
888
+
"\\cdleftarrow": "\u2190",
889
+
"\\cdlongequal": "="
890
+
};
891
+
892
+
const mathMLnode = function(label) {
893
+
const child = new mathMLTree.TextNode(stretchyCodePoint[label.slice(1)]);
894
+
const node = new mathMLTree.MathNode("mo", [child]);
895
+
node.setAttribute("stretchy", "true");
896
+
return node
897
+
};
898
+
899
+
const crookedWides = ["\\widetilde", "\\widehat", "\\widecheck", "\\utilde"];
900
+
901
+
// TODO: Remove when Chromium stretches \widetilde & \widehat
902
+
const accentNode = (group) => {
903
+
const mo = mathMLnode(group.label);
904
+
if (crookedWides.includes(group.label)) {
905
+
const width = estimatedWidth(group.base);
906
+
if (1 < width && width < 1.6) {
907
+
mo.classes.push("tml-crooked-2");
908
+
} else if (1.6 <= width && width < 2.5) {
909
+
mo.classes.push("tml-crooked-3");
910
+
} else if (2.5 <= width) {
911
+
mo.classes.push("tml-crooked-4");
912
+
}
913
+
}
914
+
return mo
915
+
};
916
+
917
+
var stretchy = {
918
+
mathMLnode,
919
+
accentNode
920
+
};
921
+
922
+
/**
923
+
* This file holds a list of all no-argument functions and single-character
924
+
* symbols (like 'a' or ';').
925
+
*
926
+
* For each of the symbols, there are two properties they can have:
927
+
* - group (required): the ParseNode group type the symbol should have (i.e.
928
+
"textord", "mathord", etc).
929
+
* - replace: the character that this symbol or function should be
930
+
* replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi
931
+
* character in the main font).
932
+
*
933
+
* The outermost map in the table indicates what mode the symbols should be
934
+
* accepted in (e.g. "math" or "text").
935
+
*/
936
+
937
+
// Some of these have a "-token" suffix since these are also used as `ParseNode`
938
+
// types for raw text tokens, and we want to avoid conflicts with higher-level
939
+
// `ParseNode` types. These `ParseNode`s are constructed within `Parser` by
940
+
// looking up the `symbols` map.
941
+
const ATOMS = {
942
+
bin: 1,
943
+
close: 1,
944
+
inner: 1,
945
+
open: 1,
946
+
punct: 1,
947
+
rel: 1
948
+
};
949
+
const NON_ATOMS = {
950
+
"accent-token": 1,
951
+
mathord: 1,
952
+
"op-token": 1,
953
+
spacing: 1,
954
+
textord: 1
955
+
};
956
+
957
+
const symbols = {
958
+
math: {},
959
+
text: {}
960
+
};
961
+
962
+
/** `acceptUnicodeChar = true` is only applicable if `replace` is set. */
963
+
function defineSymbol(mode, group, replace, name, acceptUnicodeChar) {
964
+
symbols[mode][name] = { group, replace };
965
+
966
+
if (acceptUnicodeChar && replace) {
967
+
symbols[mode][replace] = symbols[mode][name];
968
+
}
969
+
}
970
+
971
+
// Some abbreviations for commonly used strings.
972
+
// This helps minify the code, and also spotting typos using jshint.
973
+
974
+
// modes:
975
+
const math = "math";
976
+
const temml_text = "text";
977
+
978
+
// groups:
979
+
const accent = "accent-token";
980
+
const bin = "bin";
981
+
const temml_close = "close";
982
+
const inner = "inner";
983
+
const mathord = "mathord";
984
+
const op = "op-token";
985
+
const temml_open = "open";
986
+
const punct = "punct";
987
+
const rel = "rel";
988
+
const spacing = "spacing";
989
+
const textord = "textord";
990
+
991
+
// Now comes the symbol table
992
+
993
+
// Relation Symbols
994
+
defineSymbol(math, rel, "\u2261", "\\equiv", true);
995
+
defineSymbol(math, rel, "\u227a", "\\prec", true);
996
+
defineSymbol(math, rel, "\u227b", "\\succ", true);
997
+
defineSymbol(math, rel, "\u223c", "\\sim", true);
998
+
defineSymbol(math, rel, "\u27c2", "\\perp", true);
999
+
defineSymbol(math, rel, "\u2aaf", "\\preceq", true);
1000
+
defineSymbol(math, rel, "\u2ab0", "\\succeq", true);
1001
+
defineSymbol(math, rel, "\u2243", "\\simeq", true);
1002
+
defineSymbol(math, rel, "\u224c", "\\backcong", true);
1003
+
defineSymbol(math, rel, "|", "\\mid", true);
1004
+
defineSymbol(math, rel, "\u226a", "\\ll", true);
1005
+
defineSymbol(math, rel, "\u226b", "\\gg", true);
1006
+
defineSymbol(math, rel, "\u224d", "\\asymp", true);
1007
+
defineSymbol(math, rel, "\u2225", "\\parallel");
1008
+
defineSymbol(math, rel, "\u2323", "\\smile", true);
1009
+
defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true);
1010
+
defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true);
1011
+
defineSymbol(math, rel, "\u2250", "\\doteq", true);
1012
+
defineSymbol(math, rel, "\u2322", "\\frown", true);
1013
+
defineSymbol(math, rel, "\u220b", "\\ni", true);
1014
+
defineSymbol(math, rel, "\u220c", "\\notni", true);
1015
+
defineSymbol(math, rel, "\u221d", "\\propto", true);
1016
+
defineSymbol(math, rel, "\u22a2", "\\vdash", true);
1017
+
defineSymbol(math, rel, "\u22a3", "\\dashv", true);
1018
+
defineSymbol(math, rel, "\u220b", "\\owns");
1019
+
defineSymbol(math, rel, "\u2258", "\\arceq", true);
1020
+
defineSymbol(math, rel, "\u2259", "\\wedgeq", true);
1021
+
defineSymbol(math, rel, "\u225a", "\\veeeq", true);
1022
+
defineSymbol(math, rel, "\u225b", "\\stareq", true);
1023
+
defineSymbol(math, rel, "\u225d", "\\eqdef", true);
1024
+
defineSymbol(math, rel, "\u225e", "\\measeq", true);
1025
+
defineSymbol(math, rel, "\u225f", "\\questeq", true);
1026
+
defineSymbol(math, rel, "\u2260", "\\ne", true);
1027
+
defineSymbol(math, rel, "\u2260", "\\neq");
1028
+
// unicodemath
1029
+
defineSymbol(math, rel, "\u2a75", "\\eqeq", true);
1030
+
defineSymbol(math, rel, "\u2a76", "\\eqeqeq", true);
1031
+
// mathtools.sty
1032
+
defineSymbol(math, rel, "\u2237", "\\dblcolon", true);
1033
+
defineSymbol(math, rel, "\u2254", "\\coloneqq", true);
1034
+
defineSymbol(math, rel, "\u2255", "\\eqqcolon", true);
1035
+
defineSymbol(math, rel, "\u2239", "\\eqcolon", true);
1036
+
defineSymbol(math, rel, "\u2A74", "\\Coloneqq", true);
1037
+
1038
+
// Punctuation
1039
+
defineSymbol(math, punct, "\u002e", "\\ldotp");
1040
+
defineSymbol(math, punct, "\u00b7", "\\cdotp");
1041
+
1042
+
// Misc Symbols
1043
+
defineSymbol(math, textord, "\u0023", "\\#");
1044
+
defineSymbol(temml_text, textord, "\u0023", "\\#");
1045
+
defineSymbol(math, textord, "\u0026", "\\&");
1046
+
defineSymbol(temml_text, textord, "\u0026", "\\&");
1047
+
defineSymbol(math, textord, "\u2135", "\\aleph", true);
1048
+
defineSymbol(math, textord, "\u2200", "\\forall", true);
1049
+
defineSymbol(math, textord, "\u210f", "\\hbar", true);
1050
+
defineSymbol(math, textord, "\u2203", "\\exists", true);
1051
+
// ∇ is actually a unary operator, not binary. But this works.
1052
+
defineSymbol(math, bin, "\u2207", "\\nabla", true);
1053
+
defineSymbol(math, textord, "\u266d", "\\flat", true);
1054
+
defineSymbol(math, textord, "\u2113", "\\ell", true);
1055
+
defineSymbol(math, textord, "\u266e", "\\natural", true);
1056
+
defineSymbol(math, textord, "Å", "\\Angstrom", true);
1057
+
defineSymbol(temml_text, textord, "Å", "\\Angstrom", true);
1058
+
defineSymbol(math, textord, "\u2663", "\\clubsuit", true);
1059
+
defineSymbol(math, textord, "\u2667", "\\varclubsuit", true);
1060
+
defineSymbol(math, textord, "\u2118", "\\wp", true);
1061
+
defineSymbol(math, textord, "\u266f", "\\sharp", true);
1062
+
defineSymbol(math, textord, "\u2662", "\\diamondsuit", true);
1063
+
defineSymbol(math, textord, "\u2666", "\\vardiamondsuit", true);
1064
+
defineSymbol(math, textord, "\u211c", "\\Re", true);
1065
+
defineSymbol(math, textord, "\u2661", "\\heartsuit", true);
1066
+
defineSymbol(math, textord, "\u2665", "\\varheartsuit", true);
1067
+
defineSymbol(math, textord, "\u2111", "\\Im", true);
1068
+
defineSymbol(math, textord, "\u2660", "\\spadesuit", true);
1069
+
defineSymbol(math, textord, "\u2664", "\\varspadesuit", true);
1070
+
defineSymbol(math, textord, "\u2640", "\\female", true);
1071
+
defineSymbol(math, textord, "\u2642", "\\male", true);
1072
+
defineSymbol(math, textord, "\u00a7", "\\S", true);
1073
+
defineSymbol(temml_text, textord, "\u00a7", "\\S");
1074
+
defineSymbol(math, textord, "\u00b6", "\\P", true);
1075
+
defineSymbol(temml_text, textord, "\u00b6", "\\P");
1076
+
defineSymbol(temml_text, textord, "\u263a", "\\smiley", true);
1077
+
defineSymbol(math, textord, "\u263a", "\\smiley", true);
1078
+
1079
+
// Math and Text
1080
+
defineSymbol(math, textord, "\u2020", "\\dag");
1081
+
defineSymbol(temml_text, textord, "\u2020", "\\dag");
1082
+
defineSymbol(temml_text, textord, "\u2020", "\\textdagger");
1083
+
defineSymbol(math, textord, "\u2021", "\\ddag");
1084
+
defineSymbol(temml_text, textord, "\u2021", "\\ddag");
1085
+
defineSymbol(temml_text, textord, "\u2021", "\\textdaggerdbl");
1086
+
1087
+
// Large Delimiters
1088
+
defineSymbol(math, temml_close, "\u23b1", "\\rmoustache", true);
1089
+
defineSymbol(math, temml_open, "\u23b0", "\\lmoustache", true);
1090
+
defineSymbol(math, temml_close, "\u27ef", "\\rgroup", true);
1091
+
defineSymbol(math, temml_open, "\u27ee", "\\lgroup", true);
1092
+
1093
+
// Binary Operators
1094
+
defineSymbol(math, bin, "\u2213", "\\mp", true);
1095
+
defineSymbol(math, bin, "\u2296", "\\ominus", true);
1096
+
defineSymbol(math, bin, "\u228e", "\\uplus", true);
1097
+
defineSymbol(math, bin, "\u2293", "\\sqcap", true);
1098
+
defineSymbol(math, bin, "\u2217", "\\ast");
1099
+
defineSymbol(math, bin, "\u2294", "\\sqcup", true);
1100
+
defineSymbol(math, bin, "\u25ef", "\\bigcirc", true);
1101
+
defineSymbol(math, bin, "\u2219", "\\bullet", true);
1102
+
defineSymbol(math, bin, "\u2021", "\\ddagger");
1103
+
defineSymbol(math, bin, "\u2240", "\\wr", true);
1104
+
defineSymbol(math, bin, "\u2a3f", "\\amalg");
1105
+
defineSymbol(math, bin, "\u0026", "\\And"); // from amsmath
1106
+
defineSymbol(math, bin, "\u2AFD", "\\sslash", true); // from stmaryrd
1107
+
1108
+
// Arrow Symbols
1109
+
defineSymbol(math, rel, "\u27f5", "\\longleftarrow", true);
1110
+
defineSymbol(math, rel, "\u21d0", "\\Leftarrow", true);
1111
+
defineSymbol(math, rel, "\u27f8", "\\Longleftarrow", true);
1112
+
defineSymbol(math, rel, "\u27f6", "\\longrightarrow", true);
1113
+
defineSymbol(math, rel, "\u21d2", "\\Rightarrow", true);
1114
+
defineSymbol(math, rel, "\u27f9", "\\Longrightarrow", true);
1115
+
defineSymbol(math, rel, "\u2194", "\\leftrightarrow", true);
1116
+
defineSymbol(math, rel, "\u27f7", "\\longleftrightarrow", true);
1117
+
defineSymbol(math, rel, "\u21d4", "\\Leftrightarrow", true);
1118
+
defineSymbol(math, rel, "\u27fa", "\\Longleftrightarrow", true);
1119
+
defineSymbol(math, rel, "\u21a4", "\\mapsfrom", true);
1120
+
defineSymbol(math, rel, "\u21a6", "\\mapsto", true);
1121
+
defineSymbol(math, rel, "\u27fc", "\\longmapsto", true);
1122
+
defineSymbol(math, rel, "\u2197", "\\nearrow", true);
1123
+
defineSymbol(math, rel, "\u21a9", "\\hookleftarrow", true);
1124
+
defineSymbol(math, rel, "\u21aa", "\\hookrightarrow", true);
1125
+
defineSymbol(math, rel, "\u2198", "\\searrow", true);
1126
+
defineSymbol(math, rel, "\u21bc", "\\leftharpoonup", true);
1127
+
defineSymbol(math, rel, "\u21c0", "\\rightharpoonup", true);
1128
+
defineSymbol(math, rel, "\u2199", "\\swarrow", true);
1129
+
defineSymbol(math, rel, "\u21bd", "\\leftharpoondown", true);
1130
+
defineSymbol(math, rel, "\u21c1", "\\rightharpoondown", true);
1131
+
defineSymbol(math, rel, "\u2196", "\\nwarrow", true);
1132
+
defineSymbol(math, rel, "\u21cc", "\\rightleftharpoons", true);
1133
+
defineSymbol(math, mathord, "\u21af", "\\lightning", true);
1134
+
defineSymbol(math, mathord, "\u220E", "\\QED", true);
1135
+
defineSymbol(math, mathord, "\u2030", "\\permil", true);
1136
+
defineSymbol(temml_text, textord, "\u2030", "\\permil");
1137
+
defineSymbol(math, mathord, "\u2609", "\\astrosun", true);
1138
+
defineSymbol(math, mathord, "\u263c", "\\sun", true);
1139
+
defineSymbol(math, mathord, "\u263e", "\\leftmoon", true);
1140
+
defineSymbol(math, mathord, "\u263d", "\\rightmoon", true);
1141
+
defineSymbol(math, mathord, "\u2295", "\\Earth");
1142
+
1143
+
// AMS Negated Binary Relations
1144
+
defineSymbol(math, rel, "\u226e", "\\nless", true);
1145
+
// Symbol names preceeded by "@" each have a corresponding macro.
1146
+
defineSymbol(math, rel, "\u2a87", "\\lneq", true);
1147
+
defineSymbol(math, rel, "\u2268", "\\lneqq", true);
1148
+
defineSymbol(math, rel, "\u2268\ufe00", "\\lvertneqq");
1149
+
defineSymbol(math, rel, "\u22e6", "\\lnsim", true);
1150
+
defineSymbol(math, rel, "\u2a89", "\\lnapprox", true);
1151
+
defineSymbol(math, rel, "\u2280", "\\nprec", true);
1152
+
// unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym.
1153
+
defineSymbol(math, rel, "\u22e0", "\\npreceq", true);
1154
+
defineSymbol(math, rel, "\u22e8", "\\precnsim", true);
1155
+
defineSymbol(math, rel, "\u2ab9", "\\precnapprox", true);
1156
+
defineSymbol(math, rel, "\u2241", "\\nsim", true);
1157
+
defineSymbol(math, rel, "\u2224", "\\nmid", true);
1158
+
defineSymbol(math, rel, "\u2224", "\\nshortmid");
1159
+
defineSymbol(math, rel, "\u22ac", "\\nvdash", true);
1160
+
defineSymbol(math, rel, "\u22ad", "\\nvDash", true);
1161
+
defineSymbol(math, rel, "\u22ea", "\\ntriangleleft");
1162
+
defineSymbol(math, rel, "\u22ec", "\\ntrianglelefteq", true);
1163
+
defineSymbol(math, rel, "\u2284", "\\nsubset", true);
1164
+
defineSymbol(math, rel, "\u2285", "\\nsupset", true);
1165
+
defineSymbol(math, rel, "\u228a", "\\subsetneq", true);
1166
+
defineSymbol(math, rel, "\u228a\ufe00", "\\varsubsetneq");
1167
+
defineSymbol(math, rel, "\u2acb", "\\subsetneqq", true);
1168
+
defineSymbol(math, rel, "\u2acb\ufe00", "\\varsubsetneqq");
1169
+
defineSymbol(math, rel, "\u226f", "\\ngtr", true);
1170
+
defineSymbol(math, rel, "\u2a88", "\\gneq", true);
1171
+
defineSymbol(math, rel, "\u2269", "\\gneqq", true);
1172
+
defineSymbol(math, rel, "\u2269\ufe00", "\\gvertneqq");
1173
+
defineSymbol(math, rel, "\u22e7", "\\gnsim", true);
1174
+
defineSymbol(math, rel, "\u2a8a", "\\gnapprox", true);
1175
+
defineSymbol(math, rel, "\u2281", "\\nsucc", true);
1176
+
// unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym.
1177
+
defineSymbol(math, rel, "\u22e1", "\\nsucceq", true);
1178
+
defineSymbol(math, rel, "\u22e9", "\\succnsim", true);
1179
+
defineSymbol(math, rel, "\u2aba", "\\succnapprox", true);
1180
+
// unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym.
1181
+
defineSymbol(math, rel, "\u2246", "\\ncong", true);
1182
+
defineSymbol(math, rel, "\u2226", "\\nparallel", true);
1183
+
defineSymbol(math, rel, "\u2226", "\\nshortparallel");
1184
+
defineSymbol(math, rel, "\u22af", "\\nVDash", true);
1185
+
defineSymbol(math, rel, "\u22eb", "\\ntriangleright");
1186
+
defineSymbol(math, rel, "\u22ed", "\\ntrianglerighteq", true);
1187
+
defineSymbol(math, rel, "\u228b", "\\supsetneq", true);
1188
+
defineSymbol(math, rel, "\u228b", "\\varsupsetneq");
1189
+
defineSymbol(math, rel, "\u2acc", "\\supsetneqq", true);
1190
+
defineSymbol(math, rel, "\u2acc\ufe00", "\\varsupsetneqq");
1191
+
defineSymbol(math, rel, "\u22ae", "\\nVdash", true);
1192
+
defineSymbol(math, rel, "\u2ab5", "\\precneqq", true);
1193
+
defineSymbol(math, rel, "\u2ab6", "\\succneqq", true);
1194
+
defineSymbol(math, bin, "\u22b4", "\\unlhd");
1195
+
defineSymbol(math, bin, "\u22b5", "\\unrhd");
1196
+
1197
+
// AMS Negated Arrows
1198
+
defineSymbol(math, rel, "\u219a", "\\nleftarrow", true);
1199
+
defineSymbol(math, rel, "\u219b", "\\nrightarrow", true);
1200
+
defineSymbol(math, rel, "\u21cd", "\\nLeftarrow", true);
1201
+
defineSymbol(math, rel, "\u21cf", "\\nRightarrow", true);
1202
+
defineSymbol(math, rel, "\u21ae", "\\nleftrightarrow", true);
1203
+
defineSymbol(math, rel, "\u21ce", "\\nLeftrightarrow", true);
1204
+
1205
+
// AMS Misc
1206
+
defineSymbol(math, rel, "\u25b3", "\\vartriangle");
1207
+
defineSymbol(math, textord, "\u210f", "\\hslash");
1208
+
defineSymbol(math, textord, "\u25bd", "\\triangledown");
1209
+
defineSymbol(math, textord, "\u25ca", "\\lozenge");
1210
+
defineSymbol(math, textord, "\u24c8", "\\circledS");
1211
+
defineSymbol(math, textord, "\u00ae", "\\circledR", true);
1212
+
defineSymbol(temml_text, textord, "\u00ae", "\\circledR");
1213
+
defineSymbol(temml_text, textord, "\u00ae", "\\textregistered");
1214
+
defineSymbol(math, textord, "\u2221", "\\measuredangle", true);
1215
+
defineSymbol(math, textord, "\u2204", "\\nexists");
1216
+
defineSymbol(math, textord, "\u2127", "\\mho");
1217
+
defineSymbol(math, textord, "\u2132", "\\Finv", true);
1218
+
defineSymbol(math, textord, "\u2141", "\\Game", true);
1219
+
defineSymbol(math, textord, "\u2035", "\\backprime");
1220
+
defineSymbol(math, textord, "\u2036", "\\backdprime");
1221
+
defineSymbol(math, textord, "\u2037", "\\backtrprime");
1222
+
defineSymbol(math, textord, "\u25b2", "\\blacktriangle");
1223
+
defineSymbol(math, textord, "\u25bc", "\\blacktriangledown");
1224
+
defineSymbol(math, textord, "\u25a0", "\\blacksquare");
1225
+
defineSymbol(math, textord, "\u29eb", "\\blacklozenge");
1226
+
defineSymbol(math, textord, "\u2605", "\\bigstar");
1227
+
defineSymbol(math, textord, "\u2222", "\\sphericalangle", true);
1228
+
defineSymbol(math, textord, "\u2201", "\\complement", true);
1229
+
// unicode-math maps U+F0 to \matheth. We map to AMS function \eth
1230
+
defineSymbol(math, textord, "\u00f0", "\\eth", true);
1231
+
defineSymbol(temml_text, textord, "\u00f0", "\u00f0");
1232
+
defineSymbol(math, textord, "\u2571", "\\diagup");
1233
+
defineSymbol(math, textord, "\u2572", "\\diagdown");
1234
+
defineSymbol(math, textord, "\u25a1", "\\square");
1235
+
defineSymbol(math, textord, "\u25a1", "\\Box");
1236
+
defineSymbol(math, textord, "\u25ca", "\\Diamond");
1237
+
// unicode-math maps U+A5 to \mathyen. We map to AMS function \yen
1238
+
defineSymbol(math, textord, "\u00a5", "\\yen", true);
1239
+
defineSymbol(temml_text, textord, "\u00a5", "\\yen", true);
1240
+
defineSymbol(math, textord, "\u2713", "\\checkmark", true);
1241
+
defineSymbol(temml_text, textord, "\u2713", "\\checkmark");
1242
+
defineSymbol(math, textord, "\u2717", "\\ballotx", true);
1243
+
defineSymbol(temml_text, textord, "\u2717", "\\ballotx");
1244
+
defineSymbol(temml_text, textord, "\u2022", "\\textbullet");
1245
+
1246
+
// AMS Hebrew
1247
+
defineSymbol(math, textord, "\u2136", "\\beth", true);
1248
+
defineSymbol(math, textord, "\u2138", "\\daleth", true);
1249
+
defineSymbol(math, textord, "\u2137", "\\gimel", true);
1250
+
1251
+
// AMS Greek
1252
+
defineSymbol(math, textord, "\u03dd", "\\digamma", true);
1253
+
defineSymbol(math, textord, "\u03f0", "\\varkappa");
1254
+
1255
+
// AMS Delimiters
1256
+
defineSymbol(math, temml_open, "\u231C", "\\ulcorner", true);
1257
+
defineSymbol(math, temml_close, "\u231D", "\\urcorner", true);
1258
+
defineSymbol(math, temml_open, "\u231E", "\\llcorner", true);
1259
+
defineSymbol(math, temml_close, "\u231F", "\\lrcorner", true);
1260
+
1261
+
// AMS Binary Relations
1262
+
defineSymbol(math, rel, "\u2266", "\\leqq", true);
1263
+
defineSymbol(math, rel, "\u2a7d", "\\leqslant", true);
1264
+
defineSymbol(math, rel, "\u2a95", "\\eqslantless", true);
1265
+
defineSymbol(math, rel, "\u2272", "\\lesssim", true);
1266
+
defineSymbol(math, rel, "\u2a85", "\\lessapprox", true);
1267
+
defineSymbol(math, rel, "\u224a", "\\approxeq", true);
1268
+
defineSymbol(math, bin, "\u22d6", "\\lessdot");
1269
+
defineSymbol(math, rel, "\u22d8", "\\lll", true);
1270
+
defineSymbol(math, rel, "\u2276", "\\lessgtr", true);
1271
+
defineSymbol(math, rel, "\u22da", "\\lesseqgtr", true);
1272
+
defineSymbol(math, rel, "\u2a8b", "\\lesseqqgtr", true);
1273
+
defineSymbol(math, rel, "\u2251", "\\doteqdot");
1274
+
defineSymbol(math, rel, "\u2253", "\\risingdotseq", true);
1275
+
defineSymbol(math, rel, "\u2252", "\\fallingdotseq", true);
1276
+
defineSymbol(math, rel, "\u223d", "\\backsim", true);
1277
+
defineSymbol(math, rel, "\u22cd", "\\backsimeq", true);
1278
+
defineSymbol(math, rel, "\u2ac5", "\\subseteqq", true);
1279
+
defineSymbol(math, rel, "\u22d0", "\\Subset", true);
1280
+
defineSymbol(math, rel, "\u228f", "\\sqsubset", true);
1281
+
defineSymbol(math, rel, "\u227c", "\\preccurlyeq", true);
1282
+
defineSymbol(math, rel, "\u22de", "\\curlyeqprec", true);
1283
+
defineSymbol(math, rel, "\u227e", "\\precsim", true);
1284
+
defineSymbol(math, rel, "\u2ab7", "\\precapprox", true);
1285
+
defineSymbol(math, rel, "\u22b2", "\\vartriangleleft");
1286
+
defineSymbol(math, rel, "\u22b4", "\\trianglelefteq");
1287
+
defineSymbol(math, rel, "\u22a8", "\\vDash", true);
1288
+
defineSymbol(math, rel, "\u22ab", "\\VDash", true);
1289
+
defineSymbol(math, rel, "\u22aa", "\\Vvdash", true);
1290
+
defineSymbol(math, rel, "\u2323", "\\smallsmile");
1291
+
defineSymbol(math, rel, "\u2322", "\\smallfrown");
1292
+
defineSymbol(math, rel, "\u224f", "\\bumpeq", true);
1293
+
defineSymbol(math, rel, "\u224e", "\\Bumpeq", true);
1294
+
defineSymbol(math, rel, "\u2267", "\\geqq", true);
1295
+
defineSymbol(math, rel, "\u2a7e", "\\geqslant", true);
1296
+
defineSymbol(math, rel, "\u2a96", "\\eqslantgtr", true);
1297
+
defineSymbol(math, rel, "\u2273", "\\gtrsim", true);
1298
+
defineSymbol(math, rel, "\u2a86", "\\gtrapprox", true);
1299
+
defineSymbol(math, bin, "\u22d7", "\\gtrdot");
1300
+
defineSymbol(math, rel, "\u22d9", "\\ggg", true);
1301
+
defineSymbol(math, rel, "\u2277", "\\gtrless", true);
1302
+
defineSymbol(math, rel, "\u22db", "\\gtreqless", true);
1303
+
defineSymbol(math, rel, "\u2a8c", "\\gtreqqless", true);
1304
+
defineSymbol(math, rel, "\u2256", "\\eqcirc", true);
1305
+
defineSymbol(math, rel, "\u2257", "\\circeq", true);
1306
+
defineSymbol(math, rel, "\u225c", "\\triangleq", true);
1307
+
defineSymbol(math, rel, "\u223c", "\\thicksim");
1308
+
defineSymbol(math, rel, "\u2248", "\\thickapprox");
1309
+
defineSymbol(math, rel, "\u2ac6", "\\supseteqq", true);
1310
+
defineSymbol(math, rel, "\u22d1", "\\Supset", true);
1311
+
defineSymbol(math, rel, "\u2290", "\\sqsupset", true);
1312
+
defineSymbol(math, rel, "\u227d", "\\succcurlyeq", true);
1313
+
defineSymbol(math, rel, "\u22df", "\\curlyeqsucc", true);
1314
+
defineSymbol(math, rel, "\u227f", "\\succsim", true);
1315
+
defineSymbol(math, rel, "\u2ab8", "\\succapprox", true);
1316
+
defineSymbol(math, rel, "\u22b3", "\\vartriangleright");
1317
+
defineSymbol(math, rel, "\u22b5", "\\trianglerighteq");
1318
+
defineSymbol(math, rel, "\u22a9", "\\Vdash", true);
1319
+
defineSymbol(math, rel, "\u2223", "\\shortmid");
1320
+
defineSymbol(math, rel, "\u2225", "\\shortparallel");
1321
+
defineSymbol(math, rel, "\u226c", "\\between", true);
1322
+
defineSymbol(math, rel, "\u22d4", "\\pitchfork", true);
1323
+
defineSymbol(math, rel, "\u221d", "\\varpropto");
1324
+
defineSymbol(math, rel, "\u25c0", "\\blacktriangleleft");
1325
+
// unicode-math says that \therefore is a mathord atom.
1326
+
// We kept the amssymb atom type, which is rel.
1327
+
defineSymbol(math, rel, "\u2234", "\\therefore", true);
1328
+
defineSymbol(math, rel, "\u220d", "\\backepsilon");
1329
+
defineSymbol(math, rel, "\u25b6", "\\blacktriangleright");
1330
+
// unicode-math says that \because is a mathord atom.
1331
+
// We kept the amssymb atom type, which is rel.
1332
+
defineSymbol(math, rel, "\u2235", "\\because", true);
1333
+
defineSymbol(math, rel, "\u22d8", "\\llless");
1334
+
defineSymbol(math, rel, "\u22d9", "\\gggtr");
1335
+
defineSymbol(math, bin, "\u22b2", "\\lhd");
1336
+
defineSymbol(math, bin, "\u22b3", "\\rhd");
1337
+
defineSymbol(math, rel, "\u2242", "\\eqsim", true);
1338
+
defineSymbol(math, rel, "\u2251", "\\Doteq", true);
1339
+
defineSymbol(math, rel, "\u297d", "\\strictif", true);
1340
+
defineSymbol(math, rel, "\u297c", "\\strictfi", true);
1341
+
1342
+
// AMS Binary Operators
1343
+
defineSymbol(math, bin, "\u2214", "\\dotplus", true);
1344
+
defineSymbol(math, bin, "\u2216", "\\smallsetminus");
1345
+
defineSymbol(math, bin, "\u22d2", "\\Cap", true);
1346
+
defineSymbol(math, bin, "\u22d3", "\\Cup", true);
1347
+
defineSymbol(math, bin, "\u2a5e", "\\doublebarwedge", true);
1348
+
defineSymbol(math, bin, "\u229f", "\\boxminus", true);
1349
+
defineSymbol(math, bin, "\u229e", "\\boxplus", true);
1350
+
defineSymbol(math, bin, "\u29C4", "\\boxslash", true);
1351
+
defineSymbol(math, bin, "\u22c7", "\\divideontimes", true);
1352
+
defineSymbol(math, bin, "\u22c9", "\\ltimes", true);
1353
+
defineSymbol(math, bin, "\u22ca", "\\rtimes", true);
1354
+
defineSymbol(math, bin, "\u22cb", "\\leftthreetimes", true);
1355
+
defineSymbol(math, bin, "\u22cc", "\\rightthreetimes", true);
1356
+
defineSymbol(math, bin, "\u22cf", "\\curlywedge", true);
1357
+
defineSymbol(math, bin, "\u22ce", "\\curlyvee", true);
1358
+
defineSymbol(math, bin, "\u229d", "\\circleddash", true);
1359
+
defineSymbol(math, bin, "\u229b", "\\circledast", true);
1360
+
defineSymbol(math, bin, "\u22ba", "\\intercal", true);
1361
+
defineSymbol(math, bin, "\u22d2", "\\doublecap");
1362
+
defineSymbol(math, bin, "\u22d3", "\\doublecup");
1363
+
defineSymbol(math, bin, "\u22a0", "\\boxtimes", true);
1364
+
defineSymbol(math, bin, "\u22c8", "\\bowtie", true);
1365
+
defineSymbol(math, bin, "\u22c8", "\\Join");
1366
+
defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true);
1367
+
defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true);
1368
+
defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true);
1369
+
1370
+
// stix Binary Operators
1371
+
defineSymbol(math, bin, "\u2238", "\\dotminus", true);
1372
+
defineSymbol(math, bin, "\u27D1", "\\wedgedot", true);
1373
+
defineSymbol(math, bin, "\u27C7", "\\veedot", true);
1374
+
defineSymbol(math, bin, "\u2A62", "\\doublebarvee", true);
1375
+
defineSymbol(math, bin, "\u2A63", "\\veedoublebar", true);
1376
+
defineSymbol(math, bin, "\u2A5F", "\\wedgebar", true);
1377
+
defineSymbol(math, bin, "\u2A60", "\\wedgedoublebar", true);
1378
+
defineSymbol(math, bin, "\u2A54", "\\Vee", true);
1379
+
defineSymbol(math, bin, "\u2A53", "\\Wedge", true);
1380
+
defineSymbol(math, bin, "\u2A43", "\\barcap", true);
1381
+
defineSymbol(math, bin, "\u2A42", "\\barcup", true);
1382
+
defineSymbol(math, bin, "\u2A48", "\\capbarcup", true);
1383
+
defineSymbol(math, bin, "\u2A40", "\\capdot", true);
1384
+
defineSymbol(math, bin, "\u2A47", "\\capovercup", true);
1385
+
defineSymbol(math, bin, "\u2A46", "\\cupovercap", true);
1386
+
defineSymbol(math, bin, "\u2A4D", "\\closedvarcap", true);
1387
+
defineSymbol(math, bin, "\u2A4C", "\\closedvarcup", true);
1388
+
defineSymbol(math, bin, "\u2A2A", "\\minusdot", true);
1389
+
defineSymbol(math, bin, "\u2A2B", "\\minusfdots", true);
1390
+
defineSymbol(math, bin, "\u2A2C", "\\minusrdots", true);
1391
+
defineSymbol(math, bin, "\u22BB", "\\Xor", true);
1392
+
defineSymbol(math, bin, "\u22BC", "\\Nand", true);
1393
+
defineSymbol(math, bin, "\u22BD", "\\Nor", true);
1394
+
defineSymbol(math, bin, "\u22BD", "\\barvee");
1395
+
defineSymbol(math, bin, "\u2AF4", "\\interleave", true);
1396
+
defineSymbol(math, bin, "\u29E2", "\\shuffle", true);
1397
+
defineSymbol(math, bin, "\u2AF6", "\\threedotcolon", true);
1398
+
defineSymbol(math, bin, "\u2982", "\\typecolon", true);
1399
+
defineSymbol(math, bin, "\u223E", "\\invlazys", true);
1400
+
defineSymbol(math, bin, "\u2A4B", "\\twocaps", true);
1401
+
defineSymbol(math, bin, "\u2A4A", "\\twocups", true);
1402
+
defineSymbol(math, bin, "\u2A4E", "\\Sqcap", true);
1403
+
defineSymbol(math, bin, "\u2A4F", "\\Sqcup", true);
1404
+
defineSymbol(math, bin, "\u2A56", "\\veeonvee", true);
1405
+
defineSymbol(math, bin, "\u2A55", "\\wedgeonwedge", true);
1406
+
defineSymbol(math, bin, "\u29D7", "\\blackhourglass", true);
1407
+
defineSymbol(math, bin, "\u29C6", "\\boxast", true);
1408
+
defineSymbol(math, bin, "\u29C8", "\\boxbox", true);
1409
+
defineSymbol(math, bin, "\u29C7", "\\boxcircle", true);
1410
+
defineSymbol(math, bin, "\u229C", "\\circledequal", true);
1411
+
defineSymbol(math, bin, "\u29B7", "\\circledparallel", true);
1412
+
defineSymbol(math, bin, "\u29B6", "\\circledvert", true);
1413
+
defineSymbol(math, bin, "\u29B5", "\\circlehbar", true);
1414
+
defineSymbol(math, bin, "\u27E1", "\\concavediamond", true);
1415
+
defineSymbol(math, bin, "\u27E2", "\\concavediamondtickleft", true);
1416
+
defineSymbol(math, bin, "\u27E3", "\\concavediamondtickright", true);
1417
+
defineSymbol(math, bin, "\u22C4", "\\diamond", true);
1418
+
defineSymbol(math, bin, "\u29D6", "\\hourglass", true);
1419
+
defineSymbol(math, bin, "\u27E0", "\\lozengeminus", true);
1420
+
defineSymbol(math, bin, "\u233D", "\\obar", true);
1421
+
defineSymbol(math, bin, "\u29B8", "\\obslash", true);
1422
+
defineSymbol(math, bin, "\u2A38", "\\odiv", true);
1423
+
defineSymbol(math, bin, "\u29C1", "\\ogreaterthan", true);
1424
+
defineSymbol(math, bin, "\u29C0", "\\olessthan", true);
1425
+
defineSymbol(math, bin, "\u29B9", "\\operp", true);
1426
+
defineSymbol(math, bin, "\u2A37", "\\Otimes", true);
1427
+
defineSymbol(math, bin, "\u2A36", "\\otimeshat", true);
1428
+
defineSymbol(math, bin, "\u22C6", "\\star", true);
1429
+
defineSymbol(math, bin, "\u25B3", "\\triangle", true);
1430
+
defineSymbol(math, bin, "\u2A3A", "\\triangleminus", true);
1431
+
defineSymbol(math, bin, "\u2A39", "\\triangleplus", true);
1432
+
defineSymbol(math, bin, "\u2A3B", "\\triangletimes", true);
1433
+
defineSymbol(math, bin, "\u27E4", "\\whitesquaretickleft", true);
1434
+
defineSymbol(math, bin, "\u27E5", "\\whitesquaretickright", true);
1435
+
defineSymbol(math, bin, "\u2A33", "\\smashtimes", true);
1436
+
1437
+
// AMS Arrows
1438
+
// Note: unicode-math maps \u21e2 to their own function \rightdasharrow.
1439
+
// We'll map it to AMS function \dashrightarrow. It produces the same atom.
1440
+
defineSymbol(math, rel, "\u21e2", "\\dashrightarrow", true);
1441
+
// unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym.
1442
+
defineSymbol(math, rel, "\u21e0", "\\dashleftarrow", true);
1443
+
defineSymbol(math, rel, "\u21c7", "\\leftleftarrows", true);
1444
+
defineSymbol(math, rel, "\u21c6", "\\leftrightarrows", true);
1445
+
defineSymbol(math, rel, "\u21da", "\\Lleftarrow", true);
1446
+
defineSymbol(math, rel, "\u219e", "\\twoheadleftarrow", true);
1447
+
defineSymbol(math, rel, "\u21a2", "\\leftarrowtail", true);
1448
+
defineSymbol(math, rel, "\u21ab", "\\looparrowleft", true);
1449
+
defineSymbol(math, rel, "\u21cb", "\\leftrightharpoons", true);
1450
+
defineSymbol(math, rel, "\u21b6", "\\curvearrowleft", true);
1451
+
// unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym.
1452
+
defineSymbol(math, rel, "\u21ba", "\\circlearrowleft", true);
1453
+
defineSymbol(math, rel, "\u21b0", "\\Lsh", true);
1454
+
defineSymbol(math, rel, "\u21c8", "\\upuparrows", true);
1455
+
defineSymbol(math, rel, "\u21bf", "\\upharpoonleft", true);
1456
+
defineSymbol(math, rel, "\u21c3", "\\downharpoonleft", true);
1457
+
defineSymbol(math, rel, "\u22b6", "\\origof", true);
1458
+
defineSymbol(math, rel, "\u22b7", "\\imageof", true);
1459
+
defineSymbol(math, rel, "\u22b8", "\\multimap", true);
1460
+
defineSymbol(math, rel, "\u21ad", "\\leftrightsquigarrow", true);
1461
+
defineSymbol(math, rel, "\u21c9", "\\rightrightarrows", true);
1462
+
defineSymbol(math, rel, "\u21c4", "\\rightleftarrows", true);
1463
+
defineSymbol(math, rel, "\u21a0", "\\twoheadrightarrow", true);
1464
+
defineSymbol(math, rel, "\u21a3", "\\rightarrowtail", true);
1465
+
defineSymbol(math, rel, "\u21ac", "\\looparrowright", true);
1466
+
defineSymbol(math, rel, "\u21b7", "\\curvearrowright", true);
1467
+
// unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym.
1468
+
defineSymbol(math, rel, "\u21bb", "\\circlearrowright", true);
1469
+
defineSymbol(math, rel, "\u21b1", "\\Rsh", true);
1470
+
defineSymbol(math, rel, "\u21ca", "\\downdownarrows", true);
1471
+
defineSymbol(math, rel, "\u21be", "\\upharpoonright", true);
1472
+
defineSymbol(math, rel, "\u21c2", "\\downharpoonright", true);
1473
+
defineSymbol(math, rel, "\u21dd", "\\rightsquigarrow", true);
1474
+
defineSymbol(math, rel, "\u21dd", "\\leadsto");
1475
+
defineSymbol(math, rel, "\u21db", "\\Rrightarrow", true);
1476
+
defineSymbol(math, rel, "\u21be", "\\restriction");
1477
+
1478
+
defineSymbol(math, textord, "\u2018", "`");
1479
+
defineSymbol(math, textord, "$", "\\$");
1480
+
defineSymbol(temml_text, textord, "$", "\\$");
1481
+
defineSymbol(temml_text, textord, "$", "\\textdollar");
1482
+
defineSymbol(math, textord, "¢", "\\cent");
1483
+
defineSymbol(temml_text, textord, "¢", "\\cent");
1484
+
defineSymbol(math, textord, "%", "\\%");
1485
+
defineSymbol(temml_text, textord, "%", "\\%");
1486
+
defineSymbol(math, textord, "_", "\\_");
1487
+
defineSymbol(temml_text, textord, "_", "\\_");
1488
+
defineSymbol(temml_text, textord, "_", "\\textunderscore");
1489
+
defineSymbol(temml_text, textord, "\u2423", "\\textvisiblespace", true);
1490
+
defineSymbol(math, textord, "\u2220", "\\angle", true);
1491
+
defineSymbol(math, textord, "\u221e", "\\infty", true);
1492
+
defineSymbol(math, textord, "\u2032", "\\prime");
1493
+
defineSymbol(math, textord, "\u2033", "\\dprime");
1494
+
defineSymbol(math, textord, "\u2034", "\\trprime");
1495
+
defineSymbol(math, textord, "\u2057", "\\qprime");
1496
+
defineSymbol(math, textord, "\u25b3", "\\triangle");
1497
+
defineSymbol(temml_text, textord, "\u0391", "\\Alpha", true);
1498
+
defineSymbol(temml_text, textord, "\u0392", "\\Beta", true);
1499
+
defineSymbol(temml_text, textord, "\u0393", "\\Gamma", true);
1500
+
defineSymbol(temml_text, textord, "\u0394", "\\Delta", true);
1501
+
defineSymbol(temml_text, textord, "\u0395", "\\Epsilon", true);
1502
+
defineSymbol(temml_text, textord, "\u0396", "\\Zeta", true);
1503
+
defineSymbol(temml_text, textord, "\u0397", "\\Eta", true);
1504
+
defineSymbol(temml_text, textord, "\u0398", "\\Theta", true);
1505
+
defineSymbol(temml_text, textord, "\u0399", "\\Iota", true);
1506
+
defineSymbol(temml_text, textord, "\u039a", "\\Kappa", true);
1507
+
defineSymbol(temml_text, textord, "\u039b", "\\Lambda", true);
1508
+
defineSymbol(temml_text, textord, "\u039c", "\\Mu", true);
1509
+
defineSymbol(temml_text, textord, "\u039d", "\\Nu", true);
1510
+
defineSymbol(temml_text, textord, "\u039e", "\\Xi", true);
1511
+
defineSymbol(temml_text, textord, "\u039f", "\\Omicron", true);
1512
+
defineSymbol(temml_text, textord, "\u03a0", "\\Pi", true);
1513
+
defineSymbol(temml_text, textord, "\u03a1", "\\Rho", true);
1514
+
defineSymbol(temml_text, textord, "\u03a3", "\\Sigma", true);
1515
+
defineSymbol(temml_text, textord, "\u03a4", "\\Tau", true);
1516
+
defineSymbol(temml_text, textord, "\u03a5", "\\Upsilon", true);
1517
+
defineSymbol(temml_text, textord, "\u03a6", "\\Phi", true);
1518
+
defineSymbol(temml_text, textord, "\u03a7", "\\Chi", true);
1519
+
defineSymbol(temml_text, textord, "\u03a8", "\\Psi", true);
1520
+
defineSymbol(temml_text, textord, "\u03a9", "\\Omega", true);
1521
+
defineSymbol(math, mathord, "\u0391", "\\Alpha", true);
1522
+
defineSymbol(math, mathord, "\u0392", "\\Beta", true);
1523
+
defineSymbol(math, mathord, "\u0393", "\\Gamma", true);
1524
+
defineSymbol(math, mathord, "\u0394", "\\Delta", true);
1525
+
defineSymbol(math, mathord, "\u0395", "\\Epsilon", true);
1526
+
defineSymbol(math, mathord, "\u0396", "\\Zeta", true);
1527
+
defineSymbol(math, mathord, "\u0397", "\\Eta", true);
1528
+
defineSymbol(math, mathord, "\u0398", "\\Theta", true);
1529
+
defineSymbol(math, mathord, "\u0399", "\\Iota", true);
1530
+
defineSymbol(math, mathord, "\u039a", "\\Kappa", true);
1531
+
defineSymbol(math, mathord, "\u039b", "\\Lambda", true);
1532
+
defineSymbol(math, mathord, "\u039c", "\\Mu", true);
1533
+
defineSymbol(math, mathord, "\u039d", "\\Nu", true);
1534
+
defineSymbol(math, mathord, "\u039e", "\\Xi", true);
1535
+
defineSymbol(math, mathord, "\u039f", "\\Omicron", true);
1536
+
defineSymbol(math, mathord, "\u03a0", "\\Pi", true);
1537
+
defineSymbol(math, mathord, "\u03a1", "\\Rho", true);
1538
+
defineSymbol(math, mathord, "\u03a3", "\\Sigma", true);
1539
+
defineSymbol(math, mathord, "\u03a4", "\\Tau", true);
1540
+
defineSymbol(math, mathord, "\u03a5", "\\Upsilon", true);
1541
+
defineSymbol(math, mathord, "\u03a6", "\\Phi", true);
1542
+
defineSymbol(math, mathord, "\u03a7", "\\Chi", true);
1543
+
defineSymbol(math, mathord, "\u03a8", "\\Psi", true);
1544
+
defineSymbol(math, mathord, "\u03a9", "\\Omega", true);
1545
+
defineSymbol(math, temml_open, "\u00ac", "\\neg", true);
1546
+
defineSymbol(math, temml_open, "\u00ac", "\\lnot");
1547
+
defineSymbol(math, textord, "\u22a4", "\\top");
1548
+
defineSymbol(math, textord, "\u22a5", "\\bot");
1549
+
defineSymbol(math, textord, "\u2205", "\\emptyset");
1550
+
defineSymbol(math, textord, "\u2300", "\\varnothing");
1551
+
defineSymbol(math, mathord, "\u03b1", "\\alpha", true);
1552
+
defineSymbol(math, mathord, "\u03b2", "\\beta", true);
1553
+
defineSymbol(math, mathord, "\u03b3", "\\gamma", true);
1554
+
defineSymbol(math, mathord, "\u03b4", "\\delta", true);
1555
+
defineSymbol(math, mathord, "\u03f5", "\\epsilon", true);
1556
+
defineSymbol(math, mathord, "\u03b6", "\\zeta", true);
1557
+
defineSymbol(math, mathord, "\u03b7", "\\eta", true);
1558
+
defineSymbol(math, mathord, "\u03b8", "\\theta", true);
1559
+
defineSymbol(math, mathord, "\u03b9", "\\iota", true);
1560
+
defineSymbol(math, mathord, "\u03ba", "\\kappa", true);
1561
+
defineSymbol(math, mathord, "\u03bb", "\\lambda", true);
1562
+
defineSymbol(math, mathord, "\u03bc", "\\mu", true);
1563
+
defineSymbol(math, mathord, "\u03bd", "\\nu", true);
1564
+
defineSymbol(math, mathord, "\u03be", "\\xi", true);
1565
+
defineSymbol(math, mathord, "\u03bf", "\\omicron", true);
1566
+
defineSymbol(math, mathord, "\u03c0", "\\pi", true);
1567
+
defineSymbol(math, mathord, "\u03c1", "\\rho", true);
1568
+
defineSymbol(math, mathord, "\u03c3", "\\sigma", true);
1569
+
defineSymbol(math, mathord, "\u03c4", "\\tau", true);
1570
+
defineSymbol(math, mathord, "\u03c5", "\\upsilon", true);
1571
+
defineSymbol(math, mathord, "\u03d5", "\\phi", true);
1572
+
defineSymbol(math, mathord, "\u03c7", "\\chi", true);
1573
+
defineSymbol(math, mathord, "\u03c8", "\\psi", true);
1574
+
defineSymbol(math, mathord, "\u03c9", "\\omega", true);
1575
+
defineSymbol(math, mathord, "\u03b5", "\\varepsilon", true);
1576
+
defineSymbol(math, mathord, "\u03d1", "\\vartheta", true);
1577
+
defineSymbol(math, mathord, "\u03d6", "\\varpi", true);
1578
+
defineSymbol(math, mathord, "\u03f1", "\\varrho", true);
1579
+
defineSymbol(math, mathord, "\u03c2", "\\varsigma", true);
1580
+
defineSymbol(math, mathord, "\u03c6", "\\varphi", true);
1581
+
defineSymbol(math, mathord, "\u03d8", "\\Coppa", true);
1582
+
defineSymbol(math, mathord, "\u03d9", "\\coppa", true);
1583
+
defineSymbol(math, mathord, "\u03d9", "\\varcoppa", true);
1584
+
defineSymbol(math, mathord, "\u03de", "\\Koppa", true);
1585
+
defineSymbol(math, mathord, "\u03df", "\\koppa", true);
1586
+
defineSymbol(math, mathord, "\u03e0", "\\Sampi", true);
1587
+
defineSymbol(math, mathord, "\u03e1", "\\sampi", true);
1588
+
defineSymbol(math, mathord, "\u03da", "\\Stigma", true);
1589
+
defineSymbol(math, mathord, "\u03db", "\\stigma", true);
1590
+
defineSymbol(math, mathord, "\u2aeb", "\\Bot");
1591
+
defineSymbol(math, bin, "\u2217", "\u2217", true);
1592
+
defineSymbol(math, bin, "+", "+");
1593
+
defineSymbol(math, bin, "\u2217", "*");
1594
+
defineSymbol(math, bin, "\u2044", "/", true);
1595
+
defineSymbol(math, bin, "\u2044", "\u2044");
1596
+
defineSymbol(math, bin, "\u2212", "-", true);
1597
+
defineSymbol(math, bin, "\u22c5", "\\cdot", true);
1598
+
defineSymbol(math, bin, "\u2218", "\\circ", true);
1599
+
defineSymbol(math, bin, "\u00f7", "\\div", true);
1600
+
defineSymbol(math, bin, "\u00b1", "\\pm", true);
1601
+
defineSymbol(math, bin, "\u00d7", "\\times", true);
1602
+
defineSymbol(math, bin, "\u2229", "\\cap", true);
1603
+
defineSymbol(math, bin, "\u222a", "\\cup", true);
1604
+
defineSymbol(math, bin, "\u2216", "\\setminus", true);
1605
+
defineSymbol(math, bin, "\u2227", "\\land");
1606
+
defineSymbol(math, bin, "\u2228", "\\lor");
1607
+
defineSymbol(math, bin, "\u2227", "\\wedge", true);
1608
+
defineSymbol(math, bin, "\u2228", "\\vee", true);
1609
+
defineSymbol(math, temml_open, "\u27e6", "\\llbracket", true); // stmaryrd/semantic packages
1610
+
defineSymbol(math, temml_close, "\u27e7", "\\rrbracket", true);
1611
+
defineSymbol(math, temml_open, "\u27e8", "\\langle", true);
1612
+
defineSymbol(math, temml_open, "\u27ea", "\\lAngle", true);
1613
+
defineSymbol(math, temml_open, "\u2989", "\\llangle", true);
1614
+
defineSymbol(math, temml_open, "|", "\\lvert");
1615
+
defineSymbol(math, temml_open, "\u2016", "\\lVert", true);
1616
+
defineSymbol(math, textord, "!", "\\oc"); // cmll package
1617
+
defineSymbol(math, textord, "?", "\\wn");
1618
+
defineSymbol(math, textord, "\u2193", "\\shpos");
1619
+
defineSymbol(math, textord, "\u2195", "\\shift");
1620
+
defineSymbol(math, textord, "\u2191", "\\shneg");
1621
+
defineSymbol(math, temml_close, "?", "?");
1622
+
defineSymbol(math, temml_close, "!", "!");
1623
+
defineSymbol(math, temml_close, "‼", "‼");
1624
+
defineSymbol(math, temml_close, "\u27e9", "\\rangle", true);
1625
+
defineSymbol(math, temml_close, "\u27eb", "\\rAngle", true);
1626
+
defineSymbol(math, temml_close, "\u298a", "\\rrangle", true);
1627
+
defineSymbol(math, temml_close, "|", "\\rvert");
1628
+
defineSymbol(math, temml_close, "\u2016", "\\rVert");
1629
+
defineSymbol(math, temml_open, "\u2983", "\\lBrace", true); // stmaryrd/semantic packages
1630
+
defineSymbol(math, temml_close, "\u2984", "\\rBrace", true);
1631
+
defineSymbol(math, rel, "=", "\\equal", true);
1632
+
defineSymbol(math, rel, ":", ":");
1633
+
defineSymbol(math, rel, "\u2248", "\\approx", true);
1634
+
defineSymbol(math, rel, "\u2245", "\\cong", true);
1635
+
defineSymbol(math, rel, "\u2265", "\\ge");
1636
+
defineSymbol(math, rel, "\u2265", "\\geq", true);
1637
+
defineSymbol(math, rel, "\u2190", "\\gets");
1638
+
defineSymbol(math, rel, ">", "\\gt", true);
1639
+
defineSymbol(math, rel, "\u2208", "\\in", true);
1640
+
defineSymbol(math, rel, "\u2209", "\\notin", true);
1641
+
defineSymbol(math, rel, "\ue020", "\\@not");
1642
+
defineSymbol(math, rel, "\u2282", "\\subset", true);
1643
+
defineSymbol(math, rel, "\u2283", "\\supset", true);
1644
+
defineSymbol(math, rel, "\u2286", "\\subseteq", true);
1645
+
defineSymbol(math, rel, "\u2287", "\\supseteq", true);
1646
+
defineSymbol(math, rel, "\u2288", "\\nsubseteq", true);
1647
+
defineSymbol(math, rel, "\u2288", "\\nsubseteqq");
1648
+
defineSymbol(math, rel, "\u2289", "\\nsupseteq", true);
1649
+
defineSymbol(math, rel, "\u2289", "\\nsupseteqq");
1650
+
defineSymbol(math, rel, "\u22a8", "\\models");
1651
+
defineSymbol(math, rel, "\u2190", "\\leftarrow", true);
1652
+
defineSymbol(math, rel, "\u2264", "\\le");
1653
+
defineSymbol(math, rel, "\u2264", "\\leq", true);
1654
+
defineSymbol(math, rel, "<", "\\lt", true);
1655
+
defineSymbol(math, rel, "\u2192", "\\rightarrow", true);
1656
+
defineSymbol(math, rel, "\u2192", "\\to");
1657
+
defineSymbol(math, rel, "\u2271", "\\ngeq", true);
1658
+
defineSymbol(math, rel, "\u2271", "\\ngeqq");
1659
+
defineSymbol(math, rel, "\u2271", "\\ngeqslant");
1660
+
defineSymbol(math, rel, "\u2270", "\\nleq", true);
1661
+
defineSymbol(math, rel, "\u2270", "\\nleqq");
1662
+
defineSymbol(math, rel, "\u2270", "\\nleqslant");
1663
+
defineSymbol(math, rel, "\u2aeb", "\\Perp", true); //cmll package
1664
+
defineSymbol(math, spacing, "\u00a0", "\\ ");
1665
+
defineSymbol(math, spacing, "\u00a0", "\\space");
1666
+
// Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{%
1667
+
defineSymbol(math, spacing, "\u00a0", "\\nobreakspace");
1668
+
defineSymbol(temml_text, spacing, "\u00a0", "\\ ");
1669
+
defineSymbol(temml_text, spacing, "\u00a0", " ");
1670
+
defineSymbol(temml_text, spacing, "\u00a0", "\\space");
1671
+
defineSymbol(temml_text, spacing, "\u00a0", "\\nobreakspace");
1672
+
defineSymbol(math, spacing, null, "\\nobreak");
1673
+
defineSymbol(math, spacing, null, "\\allowbreak");
1674
+
defineSymbol(math, punct, ",", ",");
1675
+
defineSymbol(temml_text, punct, ":", ":");
1676
+
defineSymbol(math, punct, ";", ";");
1677
+
defineSymbol(math, bin, "\u22bc", "\\barwedge");
1678
+
defineSymbol(math, bin, "\u22bb", "\\veebar");
1679
+
defineSymbol(math, bin, "\u2299", "\\odot", true);
1680
+
// Firefox turns ⊕ into an emoji. So append \uFE0E. Define Unicode character in macros, not here.
1681
+
defineSymbol(math, bin, "\u2295\uFE0E", "\\oplus");
1682
+
defineSymbol(math, bin, "\u2297", "\\otimes", true);
1683
+
defineSymbol(math, textord, "\u2202", "\\partial", true);
1684
+
defineSymbol(math, bin, "\u2298", "\\oslash", true);
1685
+
defineSymbol(math, bin, "\u229a", "\\circledcirc", true);
1686
+
defineSymbol(math, bin, "\u22a1", "\\boxdot", true);
1687
+
defineSymbol(math, bin, "\u25b3", "\\bigtriangleup");
1688
+
defineSymbol(math, bin, "\u25bd", "\\bigtriangledown");
1689
+
defineSymbol(math, bin, "\u2020", "\\dagger");
1690
+
defineSymbol(math, bin, "\u22c4", "\\diamond");
1691
+
defineSymbol(math, bin, "\u25c3", "\\triangleleft");
1692
+
defineSymbol(math, bin, "\u25b9", "\\triangleright");
1693
+
defineSymbol(math, temml_open, "{", "\\{");
1694
+
defineSymbol(temml_text, textord, "{", "\\{");
1695
+
defineSymbol(temml_text, textord, "{", "\\textbraceleft");
1696
+
defineSymbol(math, temml_close, "}", "\\}");
1697
+
defineSymbol(temml_text, textord, "}", "\\}");
1698
+
defineSymbol(temml_text, textord, "}", "\\textbraceright");
1699
+
defineSymbol(math, temml_open, "{", "\\lbrace");
1700
+
defineSymbol(math, temml_close, "}", "\\rbrace");
1701
+
defineSymbol(math, temml_open, "[", "\\lbrack", true);
1702
+
defineSymbol(temml_text, textord, "[", "\\lbrack", true);
1703
+
defineSymbol(math, temml_close, "]", "\\rbrack", true);
1704
+
defineSymbol(temml_text, textord, "]", "\\rbrack", true);
1705
+
defineSymbol(math, temml_open, "(", "\\lparen", true);
1706
+
defineSymbol(math, temml_close, ")", "\\rparen", true);
1707
+
defineSymbol(math, temml_open, "⦇", "\\llparenthesis", true);
1708
+
defineSymbol(math, temml_close, "⦈", "\\rrparenthesis", true);
1709
+
defineSymbol(temml_text, textord, "<", "\\textless", true); // in T1 fontenc
1710
+
defineSymbol(temml_text, textord, ">", "\\textgreater", true); // in T1 fontenc
1711
+
defineSymbol(math, temml_open, "\u230a", "\\lfloor", true);
1712
+
defineSymbol(math, temml_close, "\u230b", "\\rfloor", true);
1713
+
defineSymbol(math, temml_open, "\u2308", "\\lceil", true);
1714
+
defineSymbol(math, temml_close, "\u2309", "\\rceil", true);
1715
+
defineSymbol(math, textord, "\\", "\\backslash");
1716
+
defineSymbol(math, textord, "|", "|");
1717
+
defineSymbol(math, textord, "|", "\\vert");
1718
+
defineSymbol(temml_text, textord, "|", "\\textbar", true); // in T1 fontenc
1719
+
defineSymbol(math, textord, "\u2016", "\\|");
1720
+
defineSymbol(math, textord, "\u2016", "\\Vert");
1721
+
defineSymbol(temml_text, textord, "\u2016", "\\textbardbl");
1722
+
defineSymbol(temml_text, textord, "~", "\\textasciitilde");
1723
+
defineSymbol(temml_text, textord, "\\", "\\textbackslash");
1724
+
defineSymbol(temml_text, textord, "^", "\\textasciicircum");
1725
+
defineSymbol(math, rel, "\u2191", "\\uparrow", true);
1726
+
defineSymbol(math, rel, "\u21d1", "\\Uparrow", true);
1727
+
defineSymbol(math, rel, "\u2193", "\\downarrow", true);
1728
+
defineSymbol(math, rel, "\u21d3", "\\Downarrow", true);
1729
+
defineSymbol(math, rel, "\u2195", "\\updownarrow", true);
1730
+
defineSymbol(math, rel, "\u21d5", "\\Updownarrow", true);
1731
+
defineSymbol(math, op, "\u2210", "\\coprod");
1732
+
defineSymbol(math, op, "\u22c1", "\\bigvee");
1733
+
defineSymbol(math, op, "\u22c0", "\\bigwedge");
1734
+
defineSymbol(math, op, "\u2a04", "\\biguplus");
1735
+
defineSymbol(math, op, "\u2a04", "\\bigcupplus");
1736
+
defineSymbol(math, op, "\u2a03", "\\bigcupdot");
1737
+
defineSymbol(math, op, "\u2a07", "\\bigdoublevee");
1738
+
defineSymbol(math, op, "\u2a08", "\\bigdoublewedge");
1739
+
defineSymbol(math, op, "\u22c2", "\\bigcap");
1740
+
defineSymbol(math, op, "\u22c3", "\\bigcup");
1741
+
defineSymbol(math, op, "\u222b", "\\int");
1742
+
defineSymbol(math, op, "\u222b", "\\intop");
1743
+
defineSymbol(math, op, "\u222c", "\\iint");
1744
+
defineSymbol(math, op, "\u222d", "\\iiint");
1745
+
defineSymbol(math, op, "\u220f", "\\prod");
1746
+
defineSymbol(math, op, "\u2211", "\\sum");
1747
+
defineSymbol(math, op, "\u2a02", "\\bigotimes");
1748
+
defineSymbol(math, op, "\u2a01", "\\bigoplus");
1749
+
defineSymbol(math, op, "\u2a00", "\\bigodot");
1750
+
defineSymbol(math, op, "\u2a09", "\\bigtimes");
1751
+
defineSymbol(math, op, "\u222e", "\\oint");
1752
+
defineSymbol(math, op, "\u222f", "\\oiint");
1753
+
defineSymbol(math, op, "\u2230", "\\oiiint");
1754
+
defineSymbol(math, op, "\u2231", "\\intclockwise");
1755
+
defineSymbol(math, op, "\u2232", "\\varointclockwise");
1756
+
defineSymbol(math, op, "\u2a0c", "\\iiiint");
1757
+
defineSymbol(math, op, "\u2a0d", "\\intbar");
1758
+
defineSymbol(math, op, "\u2a0e", "\\intBar");
1759
+
defineSymbol(math, op, "\u2a0f", "\\fint");
1760
+
defineSymbol(math, op, "\u2a12", "\\rppolint");
1761
+
defineSymbol(math, op, "\u2a13", "\\scpolint");
1762
+
defineSymbol(math, op, "\u2a15", "\\pointint");
1763
+
defineSymbol(math, op, "\u2a16", "\\sqint");
1764
+
defineSymbol(math, op, "\u2a17", "\\intlarhk");
1765
+
defineSymbol(math, op, "\u2a18", "\\intx");
1766
+
defineSymbol(math, op, "\u2a19", "\\intcap");
1767
+
defineSymbol(math, op, "\u2a1a", "\\intcup");
1768
+
defineSymbol(math, op, "\u2a05", "\\bigsqcap");
1769
+
defineSymbol(math, op, "\u2a06", "\\bigsqcup");
1770
+
defineSymbol(math, op, "\u222b", "\\smallint");
1771
+
defineSymbol(temml_text, inner, "\u2026", "\\textellipsis");
1772
+
defineSymbol(math, inner, "\u2026", "\\mathellipsis");
1773
+
defineSymbol(temml_text, inner, "\u2026", "\\ldots", true);
1774
+
defineSymbol(math, inner, "\u2026", "\\ldots", true);
1775
+
defineSymbol(math, inner, "\u22f0", "\\iddots", true);
1776
+
defineSymbol(math, inner, "\u22ef", "\\@cdots", true);
1777
+
defineSymbol(math, inner, "\u22f1", "\\ddots", true);
1778
+
defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro
1779
+
defineSymbol(temml_text, textord, "\u22ee", "\\varvdots");
1780
+
defineSymbol(math, accent, "\u02ca", "\\acute");
1781
+
defineSymbol(math, accent, "\u0060", "\\grave");
1782
+
defineSymbol(math, accent, "\u00a8", "\\ddot");
1783
+
defineSymbol(math, accent, "\u2026", "\\dddot");
1784
+
defineSymbol(math, accent, "\u2026\u002e", "\\ddddot");
1785
+
defineSymbol(math, accent, "\u007e", "\\tilde");
1786
+
defineSymbol(math, accent, "\u203e", "\\bar");
1787
+
defineSymbol(math, accent, "\u02d8", "\\breve");
1788
+
defineSymbol(math, accent, "\u02c7", "\\check");
1789
+
defineSymbol(math, accent, "\u005e", "\\hat");
1790
+
defineSymbol(math, accent, "\u2192", "\\vec");
1791
+
defineSymbol(math, accent, "\u02d9", "\\dot");
1792
+
defineSymbol(math, accent, "\u02da", "\\mathring");
1793
+
defineSymbol(math, mathord, "\u0131", "\\imath", true);
1794
+
defineSymbol(math, mathord, "\u0237", "\\jmath", true);
1795
+
defineSymbol(math, textord, "\u0131", "\u0131");
1796
+
defineSymbol(math, textord, "\u0237", "\u0237");
1797
+
defineSymbol(temml_text, textord, "\u0131", "\\i", true);
1798
+
defineSymbol(temml_text, textord, "\u0237", "\\j", true);
1799
+
defineSymbol(temml_text, textord, "\u00df", "\\ss", true);
1800
+
defineSymbol(temml_text, textord, "\u00e6", "\\ae", true);
1801
+
defineSymbol(temml_text, textord, "\u0153", "\\oe", true);
1802
+
defineSymbol(temml_text, textord, "\u00f8", "\\o", true);
1803
+
defineSymbol(math, mathord, "\u00f8", "\\o", true);
1804
+
defineSymbol(temml_text, textord, "\u00c6", "\\AE", true);
1805
+
defineSymbol(temml_text, textord, "\u0152", "\\OE", true);
1806
+
defineSymbol(temml_text, textord, "\u00d8", "\\O", true);
1807
+
defineSymbol(math, mathord, "\u00d8", "\\O", true);
1808
+
defineSymbol(temml_text, accent, "\u02ca", "\\'"); // acute
1809
+
defineSymbol(temml_text, accent, "\u02cb", "\\`"); // grave
1810
+
defineSymbol(temml_text, accent, "\u02c6", "\\^"); // circumflex
1811
+
defineSymbol(temml_text, accent, "\u02dc", "\\~"); // tilde
1812
+
defineSymbol(temml_text, accent, "\u02c9", "\\="); // macron
1813
+
defineSymbol(temml_text, accent, "\u02d8", "\\u"); // breve
1814
+
defineSymbol(temml_text, accent, "\u02d9", "\\."); // dot above
1815
+
defineSymbol(temml_text, accent, "\u00b8", "\\c"); // cedilla
1816
+
defineSymbol(temml_text, accent, "\u02da", "\\r"); // ring above
1817
+
defineSymbol(temml_text, accent, "\u02c7", "\\v"); // caron
1818
+
defineSymbol(temml_text, accent, "\u00a8", '\\"'); // diaresis
1819
+
defineSymbol(temml_text, accent, "\u02dd", "\\H"); // double acute
1820
+
defineSymbol(math, accent, "\u02ca", "\\'"); // acute
1821
+
defineSymbol(math, accent, "\u02cb", "\\`"); // grave
1822
+
defineSymbol(math, accent, "\u02c6", "\\^"); // circumflex
1823
+
defineSymbol(math, accent, "\u02dc", "\\~"); // tilde
1824
+
defineSymbol(math, accent, "\u02c9", "\\="); // macron
1825
+
defineSymbol(math, accent, "\u02d8", "\\u"); // breve
1826
+
defineSymbol(math, accent, "\u02d9", "\\."); // dot above
1827
+
defineSymbol(math, accent, "\u00b8", "\\c"); // cedilla
1828
+
defineSymbol(math, accent, "\u02da", "\\r"); // ring above
1829
+
defineSymbol(math, accent, "\u02c7", "\\v"); // caron
1830
+
defineSymbol(math, accent, "\u00a8", '\\"'); // diaresis
1831
+
defineSymbol(math, accent, "\u02dd", "\\H"); // double acute
1832
+
1833
+
// These ligatures are detected and created in Parser.js's `formLigatures`.
1834
+
const ligatures = {
1835
+
"--": true,
1836
+
"---": true,
1837
+
"``": true,
1838
+
"''": true
1839
+
};
1840
+
1841
+
defineSymbol(temml_text, textord, "\u2013", "--", true);
1842
+
defineSymbol(temml_text, textord, "\u2013", "\\textendash");
1843
+
defineSymbol(temml_text, textord, "\u2014", "---", true);
1844
+
defineSymbol(temml_text, textord, "\u2014", "\\textemdash");
1845
+
defineSymbol(temml_text, textord, "\u2018", "`", true);
1846
+
defineSymbol(temml_text, textord, "\u2018", "\\textquoteleft");
1847
+
defineSymbol(temml_text, textord, "\u2019", "'", true);
1848
+
defineSymbol(temml_text, textord, "\u2019", "\\textquoteright");
1849
+
defineSymbol(temml_text, textord, "\u201c", "``", true);
1850
+
defineSymbol(temml_text, textord, "\u201c", "\\textquotedblleft");
1851
+
defineSymbol(temml_text, textord, "\u201d", "''", true);
1852
+
defineSymbol(temml_text, textord, "\u201d", "\\textquotedblright");
1853
+
// \degree from gensymb package
1854
+
defineSymbol(math, textord, "\u00b0", "\\degree", true);
1855
+
defineSymbol(temml_text, textord, "\u00b0", "\\degree");
1856
+
// \textdegree from inputenc package
1857
+
defineSymbol(temml_text, textord, "\u00b0", "\\textdegree", true);
1858
+
// TODO: In LaTeX, \pounds can generate a different character in text and math
1859
+
// mode, but among our fonts, only Main-Regular defines this character "163".
1860
+
defineSymbol(math, textord, "\u00a3", "\\pounds");
1861
+
defineSymbol(math, textord, "\u00a3", "\\mathsterling", true);
1862
+
defineSymbol(temml_text, textord, "\u00a3", "\\pounds");
1863
+
defineSymbol(temml_text, textord, "\u00a3", "\\textsterling", true);
1864
+
defineSymbol(math, textord, "\u2720", "\\maltese");
1865
+
defineSymbol(temml_text, textord, "\u2720", "\\maltese");
1866
+
defineSymbol(math, textord, "\u20ac", "\\euro", true);
1867
+
defineSymbol(temml_text, textord, "\u20ac", "\\euro", true);
1868
+
defineSymbol(temml_text, textord, "\u20ac", "\\texteuro");
1869
+
defineSymbol(math, textord, "\u00a9", "\\copyright", true);
1870
+
defineSymbol(temml_text, textord, "\u00a9", "\\textcopyright");
1871
+
defineSymbol(math, textord, "\u2300", "\\diameter", true);
1872
+
defineSymbol(temml_text, textord, "\u2300", "\\diameter");
1873
+
1874
+
// Italic Greek
1875
+
defineSymbol(math, textord, "𝛤", "\\varGamma");
1876
+
defineSymbol(math, textord, "𝛥", "\\varDelta");
1877
+
defineSymbol(math, textord, "𝛩", "\\varTheta");
1878
+
defineSymbol(math, textord, "𝛬", "\\varLambda");
1879
+
defineSymbol(math, textord, "𝛯", "\\varXi");
1880
+
defineSymbol(math, textord, "𝛱", "\\varPi");
1881
+
defineSymbol(math, textord, "𝛴", "\\varSigma");
1882
+
defineSymbol(math, textord, "𝛶", "\\varUpsilon");
1883
+
defineSymbol(math, textord, "𝛷", "\\varPhi");
1884
+
defineSymbol(math, textord, "𝛹", "\\varPsi");
1885
+
defineSymbol(math, textord, "𝛺", "\\varOmega");
1886
+
defineSymbol(temml_text, textord, "𝛤", "\\varGamma");
1887
+
defineSymbol(temml_text, textord, "𝛥", "\\varDelta");
1888
+
defineSymbol(temml_text, textord, "𝛩", "\\varTheta");
1889
+
defineSymbol(temml_text, textord, "𝛬", "\\varLambda");
1890
+
defineSymbol(temml_text, textord, "𝛯", "\\varXi");
1891
+
defineSymbol(temml_text, textord, "𝛱", "\\varPi");
1892
+
defineSymbol(temml_text, textord, "𝛴", "\\varSigma");
1893
+
defineSymbol(temml_text, textord, "𝛶", "\\varUpsilon");
1894
+
defineSymbol(temml_text, textord, "𝛷", "\\varPhi");
1895
+
defineSymbol(temml_text, textord, "𝛹", "\\varPsi");
1896
+
defineSymbol(temml_text, textord, "𝛺", "\\varOmega");
1897
+
1898
+
1899
+
// There are lots of symbols which are the same, so we add them in afterwards.
1900
+
// All of these are textords in math mode
1901
+
const mathTextSymbols = '0123456789/@."';
1902
+
for (let i = 0; i < mathTextSymbols.length; i++) {
1903
+
const ch = mathTextSymbols.charAt(i);
1904
+
defineSymbol(math, textord, ch, ch);
1905
+
}
1906
+
1907
+
// All of these are textords in text mode
1908
+
const textSymbols = '0123456789!@*()-=+";:?/.,';
1909
+
for (let i = 0; i < textSymbols.length; i++) {
1910
+
const ch = textSymbols.charAt(i);
1911
+
defineSymbol(temml_text, textord, ch, ch);
1912
+
}
1913
+
1914
+
// All of these are textords in text mode, and mathords in math mode
1915
+
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1916
+
for (let i = 0; i < letters.length; i++) {
1917
+
const ch = letters.charAt(i);
1918
+
defineSymbol(math, mathord, ch, ch);
1919
+
defineSymbol(temml_text, textord, ch, ch);
1920
+
}
1921
+
1922
+
// Some more letters in Unicode Basic Multilingual Plane.
1923
+
const narrow = "ÇÐÞçþℂℍℕℙℚℝℤℎℏℊℋℌℐℑℒℓ℘ℛℜℬℰℱℳℭℨ";
1924
+
for (let i = 0; i < narrow.length; i++) {
1925
+
const ch = narrow.charAt(i);
1926
+
defineSymbol(math, mathord, ch, ch);
1927
+
defineSymbol(temml_text, textord, ch, ch);
1928
+
}
1929
+
1930
+
// The next loop loads wide (surrogate pair) characters.
1931
+
// We support some letters in the Unicode range U+1D400 to U+1D7FF,
1932
+
// Mathematical Alphanumeric Symbols.
1933
+
let wideChar = "";
1934
+
for (let i = 0; i < letters.length; i++) {
1935
+
// The hex numbers in the next line are a surrogate pair.
1936
+
// 0xD835 is the high surrogate for all letters in the range we support.
1937
+
// 0xDC00 is the low surrogate for bold A.
1938
+
wideChar = String.fromCharCode(0xd835, 0xdc00 + i); // A-Z a-z bold
1939
+
defineSymbol(math, mathord, wideChar, wideChar);
1940
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1941
+
1942
+
wideChar = String.fromCharCode(0xd835, 0xdc34 + i); // A-Z a-z italic
1943
+
defineSymbol(math, mathord, wideChar, wideChar);
1944
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1945
+
1946
+
wideChar = String.fromCharCode(0xd835, 0xdc68 + i); // A-Z a-z bold italic
1947
+
defineSymbol(math, mathord, wideChar, wideChar);
1948
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1949
+
1950
+
wideChar = String.fromCharCode(0xd835, 0xdd04 + i); // A-Z a-z Fractur
1951
+
defineSymbol(math, mathord, wideChar, wideChar);
1952
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1953
+
1954
+
wideChar = String.fromCharCode(0xd835, 0xdda0 + i); // A-Z a-z sans-serif
1955
+
defineSymbol(math, mathord, wideChar, wideChar);
1956
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1957
+
1958
+
wideChar = String.fromCharCode(0xd835, 0xddd4 + i); // A-Z a-z sans bold
1959
+
defineSymbol(math, mathord, wideChar, wideChar);
1960
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1961
+
1962
+
wideChar = String.fromCharCode(0xd835, 0xde08 + i); // A-Z a-z sans italic
1963
+
defineSymbol(math, mathord, wideChar, wideChar);
1964
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1965
+
1966
+
wideChar = String.fromCharCode(0xd835, 0xde70 + i); // A-Z a-z monospace
1967
+
defineSymbol(math, mathord, wideChar, wideChar);
1968
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1969
+
1970
+
wideChar = String.fromCharCode(0xd835, 0xdd38 + i); // A-Z a-z double struck
1971
+
defineSymbol(math, mathord, wideChar, wideChar);
1972
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1973
+
1974
+
const ch = letters.charAt(i);
1975
+
wideChar = String.fromCharCode(0xd835, 0xdc9c + i); // A-Z a-z calligraphic
1976
+
defineSymbol(math, mathord, ch, wideChar);
1977
+
defineSymbol(temml_text, textord, ch, wideChar);
1978
+
}
1979
+
1980
+
// Next, some wide character numerals
1981
+
for (let i = 0; i < 10; i++) {
1982
+
wideChar = String.fromCharCode(0xd835, 0xdfce + i); // 0-9 bold
1983
+
defineSymbol(math, mathord, wideChar, wideChar);
1984
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1985
+
1986
+
wideChar = String.fromCharCode(0xd835, 0xdfe2 + i); // 0-9 sans serif
1987
+
defineSymbol(math, mathord, wideChar, wideChar);
1988
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1989
+
1990
+
wideChar = String.fromCharCode(0xd835, 0xdfec + i); // 0-9 bold sans
1991
+
defineSymbol(math, mathord, wideChar, wideChar);
1992
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1993
+
1994
+
wideChar = String.fromCharCode(0xd835, 0xdff6 + i); // 0-9 monospace
1995
+
defineSymbol(math, mathord, wideChar, wideChar);
1996
+
defineSymbol(temml_text, textord, wideChar, wideChar);
1997
+
}
1998
+
1999
+
/*
2000
+
* Neither Firefox nor Chrome support hard line breaks or soft line breaks.
2001
+
* (Despite https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs)
2002
+
* So Temml has work-arounds for both hard and soft breaks.
2003
+
* The work-arounds sadly do not work simultaneously. Any top-level hard
2004
+
* break makes soft line breaks impossible.
2005
+
*
2006
+
* Hard breaks are simulated by creating a <mtable> and putting each line in its own <mtr>.
2007
+
*
2008
+
* To create soft line breaks, Temml avoids using the <semantics> and <annotation> tags.
2009
+
* Then the top level of a <math> element can be occupied by <mrow> elements, and the browser
2010
+
* will break after a <mrow> if the expression extends beyond the container limit.
2011
+
*
2012
+
* The default is for soft line breaks after each top-level binary or
2013
+
* relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that
2014
+
* each <mrow> ends in a binary or relational operator.
2015
+
*
2016
+
* An option is for soft line breaks before an "=" sign. That changes the <mrow>s.
2017
+
*
2018
+
* Soft line breaks will not work in Chromium and Safari, only Firefox.
2019
+
*
2020
+
* Hopefully browsers will someday do their own linebreaking and we will be able to delete
2021
+
* much of this module.
2022
+
*/
2023
+
2024
+
const openDelims = "([{⌊⌈⟨⟮⎰⟦⦃";
2025
+
const closeDelims = ")]}⌋⌉⟩⟯⎱⟦⦄";
2026
+
2027
+
function setLineBreaks(expression, wrapMode, isDisplayMode) {
2028
+
const mtrs = [];
2029
+
let mrows = [];
2030
+
let block = [];
2031
+
let numTopLevelEquals = 0;
2032
+
let i = 0;
2033
+
let level = 0;
2034
+
while (i < expression.length) {
2035
+
while (expression[i] instanceof DocumentFragment) {
2036
+
expression.splice(i, 1, ...expression[i].children); // Expand the fragment.
2037
+
}
2038
+
const node = expression[i];
2039
+
if (node.attributes && node.attributes.linebreak &&
2040
+
node.attributes.linebreak === "newline") {
2041
+
// A hard line break. Create a <mtr> for the current block.
2042
+
if (block.length > 0) {
2043
+
mrows.push(new mathMLTree.MathNode("mrow", block));
2044
+
}
2045
+
mrows.push(node);
2046
+
block = [];
2047
+
const mtd = new mathMLTree.MathNode("mtd", mrows);
2048
+
mtd.style.textAlign = "left";
2049
+
mtrs.push(new mathMLTree.MathNode("mtr", [mtd]));
2050
+
mrows = [];
2051
+
i += 1;
2052
+
continue
2053
+
}
2054
+
block.push(node);
2055
+
if (node.type && node.type === "mo" && node.children.length === 1 &&
2056
+
!Object.hasOwn(node.attributes, "movablelimits")) {
2057
+
const ch = node.children[0].text;
2058
+
if (openDelims.indexOf(ch) > -1) {
2059
+
level += 1;
2060
+
} else if (closeDelims.indexOf(ch) > -1) {
2061
+
level -= 1;
2062
+
} else if (level === 0 && wrapMode === "=" && ch === "=") {
2063
+
numTopLevelEquals += 1;
2064
+
if (numTopLevelEquals > 1) {
2065
+
block.pop();
2066
+
// Start a new block. (Insert a soft linebreak.)
2067
+
const element = new mathMLTree.MathNode("mrow", block);
2068
+
mrows.push(element);
2069
+
block = [node];
2070
+
}
2071
+
} else if (level === 0 && wrapMode === "tex" && ch !== "∇") {
2072
+
// Check if the following node is a \nobreak text node, e.g. "~""
2073
+
const next = i < expression.length - 1 ? expression[i + 1] : null;
2074
+
let glueIsFreeOfNobreak = true;
2075
+
if (
2076
+
!(
2077
+
next &&
2078
+
next.type === "mtext" &&
2079
+
next.attributes.linebreak &&
2080
+
next.attributes.linebreak === "nobreak"
2081
+
)
2082
+
) {
2083
+
// We may need to start a new block.
2084
+
// First, put any post-operator glue on same line as operator.
2085
+
for (let j = i + 1; j < expression.length; j++) {
2086
+
const nd = expression[j];
2087
+
if (
2088
+
nd.type &&
2089
+
nd.type === "mspace" &&
2090
+
!(nd.attributes.linebreak && nd.attributes.linebreak === "newline")
2091
+
) {
2092
+
block.push(nd);
2093
+
i += 1;
2094
+
if (
2095
+
nd.attributes &&
2096
+
nd.attributes.linebreak &&
2097
+
nd.attributes.linebreak === "nobreak"
2098
+
) {
2099
+
glueIsFreeOfNobreak = false;
2100
+
}
2101
+
} else {
2102
+
break;
2103
+
}
2104
+
}
2105
+
}
2106
+
if (glueIsFreeOfNobreak) {
2107
+
// Start a new block. (Insert a soft linebreak.)
2108
+
const element = new mathMLTree.MathNode("mrow", block);
2109
+
mrows.push(element);
2110
+
block = [];
2111
+
}
2112
+
}
2113
+
}
2114
+
i += 1;
2115
+
}
2116
+
if (block.length > 0) {
2117
+
const element = new mathMLTree.MathNode("mrow", block);
2118
+
mrows.push(element);
2119
+
}
2120
+
if (mtrs.length > 0) {
2121
+
const mtd = new mathMLTree.MathNode("mtd", mrows);
2122
+
mtd.style.textAlign = "left";
2123
+
const mtr = new mathMLTree.MathNode("mtr", [mtd]);
2124
+
mtrs.push(mtr);
2125
+
const mtable = new mathMLTree.MathNode("mtable", mtrs);
2126
+
if (!isDisplayMode) {
2127
+
mtable.setAttribute("columnalign", "left");
2128
+
mtable.setAttribute("rowspacing", "0em");
2129
+
}
2130
+
return mtable
2131
+
}
2132
+
return mathMLTree.newDocumentFragment(mrows);
2133
+
}
2134
+
2135
+
/**
2136
+
* This file converts a parse tree into a corresponding MathML tree. The main
2137
+
* entry point is the `buildMathML` function, which takes a parse tree from the
2138
+
* parser.
2139
+
*/
2140
+
2141
+
2142
+
/**
2143
+
* Takes a symbol and converts it into a MathML text node after performing
2144
+
* optional replacement from symbols.js.
2145
+
*/
2146
+
const makeText = function(text, mode, style) {
2147
+
if (
2148
+
symbols[mode][text] &&
2149
+
symbols[mode][text].replace &&
2150
+
text.charCodeAt(0) !== 0xd835 &&
2151
+
!(
2152
+
Object.prototype.hasOwnProperty.call(ligatures, text) &&
2153
+
style &&
2154
+
((style.fontFamily && style.fontFamily.slice(4, 6) === "tt") ||
2155
+
(style.font && style.font.slice(4, 6) === "tt"))
2156
+
)
2157
+
) {
2158
+
text = symbols[mode][text].replace;
2159
+
}
2160
+
2161
+
return new mathMLTree.TextNode(text);
2162
+
};
2163
+
2164
+
const copyChar = (newRow, child) => {
2165
+
if (newRow.children.length === 0 ||
2166
+
newRow.children[newRow.children.length - 1].type !== "mtext") {
2167
+
const mtext = new mathMLTree.MathNode(
2168
+
"mtext",
2169
+
[new mathMLTree.TextNode(child.children[0].text)]
2170
+
);
2171
+
newRow.children.push(mtext);
2172
+
} else {
2173
+
newRow.children[newRow.children.length - 1].children[0].text += child.children[0].text;
2174
+
}
2175
+
};
2176
+
2177
+
const consolidateText = mrow => {
2178
+
// If possible, consolidate adjacent <mtext> elements into a single element.
2179
+
if (mrow.type !== "mrow" && mrow.type !== "mstyle") { return mrow }
2180
+
if (mrow.children.length === 0) { return mrow } // empty group, e.g., \text{}
2181
+
const newRow = new mathMLTree.MathNode("mrow");
2182
+
for (let i = 0; i < mrow.children.length; i++) {
2183
+
const child = mrow.children[i];
2184
+
if (child.type === "mtext" && Object.keys(child.attributes).length === 0) {
2185
+
copyChar(newRow, child);
2186
+
} else if (child.type === "mrow") {
2187
+
// We'll also check the children of an mrow. One level only. No recursion.
2188
+
let canConsolidate = true;
2189
+
for (let j = 0; j < child.children.length; j++) {
2190
+
const grandChild = child.children[j];
2191
+
if (grandChild.type !== "mtext" || Object.keys(child.attributes).length !== 0) {
2192
+
canConsolidate = false;
2193
+
break
2194
+
}
2195
+
}
2196
+
if (canConsolidate) {
2197
+
for (let j = 0; j < child.children.length; j++) {
2198
+
const grandChild = child.children[j];
2199
+
copyChar(newRow, grandChild);
2200
+
}
2201
+
} else {
2202
+
newRow.children.push(child);
2203
+
}
2204
+
} else {
2205
+
newRow.children.push(child);
2206
+
}
2207
+
}
2208
+
for (let i = 0; i < newRow.children.length; i++) {
2209
+
if (newRow.children[i].type === "mtext") {
2210
+
const mtext = newRow.children[i];
2211
+
// Firefox does not render a space at either end of an <mtext> string.
2212
+
// To get proper rendering, we replace leading or trailing spaces with no-break spaces.
2213
+
if (mtext.children[0].text.charAt(0) === " ") {
2214
+
mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1);
2215
+
}
2216
+
const L = mtext.children[0].text.length;
2217
+
if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") {
2218
+
mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0";
2219
+
}
2220
+
for (const [key, value] of Object.entries(mrow.attributes)) {
2221
+
mtext.attributes[key] = value;
2222
+
}
2223
+
}
2224
+
}
2225
+
if (newRow.children.length === 1 && newRow.children[0].type === "mtext") {
2226
+
return newRow.children[0]; // A consolidated <mtext>
2227
+
} else {
2228
+
return newRow
2229
+
}
2230
+
};
2231
+
2232
+
/**
2233
+
* Wrap the given array of nodes in an <mrow> node if needed, i.e.,
2234
+
* unless the array has length 1. Always returns a single node.
2235
+
*/
2236
+
const makeRow = function(body, semisimple = false) {
2237
+
if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
2238
+
return body[0];
2239
+
} else if (!semisimple) {
2240
+
// Suppress spacing on <mo> nodes at both ends of the row.
2241
+
if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
2242
+
body[0].attributes.lspace = "0em";
2243
+
body[0].attributes.rspace = "0em";
2244
+
}
2245
+
const end = body.length - 1;
2246
+
if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
2247
+
body[end].attributes.lspace = "0em";
2248
+
body[end].attributes.rspace = "0em";
2249
+
}
2250
+
}
2251
+
return new mathMLTree.MathNode("mrow", body);
2252
+
};
2253
+
2254
+
/**
2255
+
* Check for <mi>.</mi> which is how a dot renders in MathML,
2256
+
* or <mo separator="true" lspace="0em" rspace="0em">,</mo>
2257
+
* which is how a braced comma {,} renders in MathML
2258
+
*/
2259
+
function isNumberPunctuation(group) {
2260
+
if (!group) {
2261
+
return false
2262
+
}
2263
+
if (group.type === 'mi' && group.children.length === 1) {
2264
+
const child = group.children[0];
2265
+
return child instanceof TextNode && child.text === '.'
2266
+
} else if (group.type === "mtext" && group.children.length === 1) {
2267
+
const child = group.children[0];
2268
+
return child instanceof TextNode && child.text === '\u2008' // punctuation space
2269
+
} else if (group.type === 'mo' && group.children.length === 1 &&
2270
+
group.getAttribute('separator') === 'true' &&
2271
+
group.getAttribute('lspace') === '0em' &&
2272
+
group.getAttribute('rspace') === '0em') {
2273
+
const child = group.children[0];
2274
+
return child instanceof TextNode && child.text === ','
2275
+
} else {
2276
+
return false
2277
+
}
2278
+
}
2279
+
const isComma = (expression, i) => {
2280
+
const node = expression[i];
2281
+
const followingNode = expression[i + 1];
2282
+
return (node.type === "atom" && node.text === ",") &&
2283
+
// Don't consolidate if there is a space after the comma.
2284
+
node.loc && followingNode.loc && node.loc.end === followingNode.loc.start
2285
+
};
2286
+
2287
+
const isRel = item => {
2288
+
return (item.type === "atom" && item.family === "rel") ||
2289
+
(item.type === "mclass" && item.mclass === "mrel")
2290
+
};
2291
+
2292
+
/**
2293
+
* Takes a list of nodes, builds them, and returns a list of the generated
2294
+
* MathML nodes. Also do a couple chores along the way:
2295
+
* (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
2296
+
* (2) Suppress spacing between two adjacent relations.
2297
+
*/
2298
+
const buildExpression = function(expression, style, semisimple = false) {
2299
+
if (!semisimple && expression.length === 1) {
2300
+
const group = buildGroup$1(expression[0], style);
2301
+
if (group instanceof MathNode && group.type === "mo") {
2302
+
// When TeX writers want to suppress spacing on an operator,
2303
+
// they often put the operator by itself inside braces.
2304
+
group.setAttribute("lspace", "0em");
2305
+
group.setAttribute("rspace", "0em");
2306
+
}
2307
+
return [group];
2308
+
}
2309
+
2310
+
const groups = [];
2311
+
const groupArray = [];
2312
+
let lastGroup;
2313
+
for (let i = 0; i < expression.length; i++) {
2314
+
groupArray.push(buildGroup$1(expression[i], style));
2315
+
}
2316
+
2317
+
for (let i = 0; i < groupArray.length; i++) {
2318
+
const group = groupArray[i];
2319
+
2320
+
// Suppress spacing between adjacent relations
2321
+
if (i < expression.length - 1 && isRel(expression[i]) && isRel(expression[i + 1])) {
2322
+
group.setAttribute("rspace", "0em");
2323
+
}
2324
+
if (i > 0 && isRel(expression[i]) && isRel(expression[i - 1])) {
2325
+
group.setAttribute("lspace", "0em");
2326
+
}
2327
+
2328
+
// Concatenate numbers
2329
+
if (group.type === 'mn' && lastGroup && lastGroup.type === 'mn') {
2330
+
// Concatenate <mn>...</mn> followed by <mi>.</mi>
2331
+
lastGroup.children.push(...group.children);
2332
+
continue
2333
+
} else if (isNumberPunctuation(group) && lastGroup && lastGroup.type === 'mn') {
2334
+
// Concatenate <mn>...</mn> followed by <mi>.</mi>
2335
+
lastGroup.children.push(...group.children);
2336
+
continue
2337
+
} else if (lastGroup && lastGroup.type === "mn" && i < groupArray.length - 1 &&
2338
+
groupArray[i + 1].type === "mn" && isComma(expression, i)) {
2339
+
lastGroup.children.push(...group.children);
2340
+
continue
2341
+
} else if (group.type === 'mn' && isNumberPunctuation(lastGroup)) {
2342
+
// Concatenate <mi>.</mi> followed by <mn>...</mn>
2343
+
group.children = [...lastGroup.children, ...group.children];
2344
+
groups.pop();
2345
+
} else if ((group.type === 'msup' || group.type === 'msub') &&
2346
+
group.children.length >= 1 && lastGroup &&
2347
+
(lastGroup.type === 'mn' || isNumberPunctuation(lastGroup))) {
2348
+
// Put preceding <mn>...</mn> or <mi>.</mi> inside base of
2349
+
// <msup><mn>...base...</mn>...exponent...</msup> (or <msub>)
2350
+
const base = group.children[0];
2351
+
if (base instanceof MathNode && base.type === 'mn' && lastGroup) {
2352
+
base.children = [...lastGroup.children, ...base.children];
2353
+
groups.pop();
2354
+
}
2355
+
}
2356
+
groups.push(group);
2357
+
lastGroup = group;
2358
+
}
2359
+
return groups
2360
+
};
2361
+
2362
+
/**
2363
+
* Equivalent to buildExpression, but wraps the elements in an <mrow>
2364
+
* if there's more than one. Returns a single node instead of an array.
2365
+
*/
2366
+
const buildExpressionRow = function(expression, style, semisimple = false) {
2367
+
return makeRow(buildExpression(expression, style, semisimple), semisimple);
2368
+
};
2369
+
2370
+
/**
2371
+
* Takes a group from the parser and calls the appropriate groupBuilders function
2372
+
* on it to produce a MathML node.
2373
+
*/
2374
+
const buildGroup$1 = function(group, style) {
2375
+
if (!group) {
2376
+
return new mathMLTree.MathNode("mrow");
2377
+
}
2378
+
2379
+
if (_mathmlGroupBuilders[group.type]) {
2380
+
// Call the groupBuilders function
2381
+
const result = _mathmlGroupBuilders[group.type](group, style);
2382
+
return result;
2383
+
} else {
2384
+
throw new ParseError("Got group of unknown type: '" + group.type + "'");
2385
+
}
2386
+
};
2387
+
2388
+
const glue$1 = _ => {
2389
+
return new mathMLTree.MathNode("mtd", [], [], { padding: "0", width: "50%" })
2390
+
};
2391
+
2392
+
const labelContainers = ["mrow", "mtd", "mtable", "mtr"];
2393
+
const getLabel = parent => {
2394
+
for (const node of parent.children) {
2395
+
if (node.type && labelContainers.includes(node.type)) {
2396
+
if (node.classes && node.classes[0] === "tml-label") {
2397
+
const label = node.label;
2398
+
return label
2399
+
} else {
2400
+
const label = getLabel(node);
2401
+
if (label) { return label }
2402
+
}
2403
+
} else if (!node.type) {
2404
+
const label = getLabel(node);
2405
+
if (label) { return label }
2406
+
}
2407
+
}
2408
+
};
2409
+
2410
+
const taggedExpression = (expression, tag, style, leqno) => {
2411
+
tag = buildExpressionRow(tag[0].body, style);
2412
+
tag = consolidateText(tag);
2413
+
tag.classes.push("tml-tag");
2414
+
2415
+
const label = getLabel(expression); // from a \label{} function.
2416
+
expression = new mathMLTree.MathNode("mtd", [expression]);
2417
+
const rowArray = [glue$1(), expression, glue$1()];
2418
+
rowArray[leqno ? 0 : 2].classes.push(leqno ? "tml-left" : "tml-right");
2419
+
rowArray[leqno ? 0 : 2].children.push(tag);
2420
+
const mtr = new mathMLTree.MathNode("mtr", rowArray, ["tml-tageqn"]);
2421
+
if (label) { mtr.setAttribute("id", label); }
2422
+
const table = new mathMLTree.MathNode("mtable", [mtr]);
2423
+
table.style.width = "100%";
2424
+
table.setAttribute("displaystyle", "true");
2425
+
return table
2426
+
};
2427
+
2428
+
/**
2429
+
* Takes a full parse tree and settings and builds a MathML representation of
2430
+
* it.
2431
+
*/
2432
+
function buildMathML(tree, texExpression, style, settings) {
2433
+
// Strip off outer tag wrapper for processing below.
2434
+
let tag = null;
2435
+
if (tree.length === 1 && tree[0].type === "tag") {
2436
+
tag = tree[0].tag;
2437
+
tree = tree[0].body;
2438
+
}
2439
+
2440
+
const expression = buildExpression(tree, style);
2441
+
2442
+
if (expression.length === 1 && expression[0] instanceof AnchorNode) {
2443
+
return expression[0]
2444
+
}
2445
+
2446
+
const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap;
2447
+
2448
+
const n1 = expression.length === 0 ? null : expression[0];
2449
+
let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode)
2450
+
? expression[0]
2451
+
: setLineBreaks(expression, wrap, settings.displayMode);
2452
+
2453
+
if (tag) {
2454
+
wrapper = taggedExpression(wrapper, tag, style, settings.leqno);
2455
+
}
2456
+
2457
+
if (settings.annotate) {
2458
+
// Build a TeX annotation of the source
2459
+
const annotation = new mathMLTree.MathNode(
2460
+
"annotation", [new mathMLTree.TextNode(texExpression)]);
2461
+
annotation.setAttribute("encoding", "application/x-tex");
2462
+
wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
2463
+
}
2464
+
2465
+
const math = new mathMLTree.MathNode("math", [wrapper]);
2466
+
2467
+
if (settings.xml) {
2468
+
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
2469
+
}
2470
+
if (wrapper.style.width) {
2471
+
math.style.width = "100%";
2472
+
}
2473
+
if (settings.displayMode) {
2474
+
math.setAttribute("display", "block");
2475
+
math.style.display = "block math"; // necessary in Chromium.
2476
+
// Firefox and Safari do not recognize display: "block math".
2477
+
// Set a class so that the CSS file can set display: block.
2478
+
math.classes = ["tml-display"];
2479
+
}
2480
+
return math;
2481
+
}
2482
+
2483
+
const smalls = "acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚𝐜𝐞𝐠𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐮𝐯𝐰𝐱𝐲𝐳";
2484
+
const talls = "ABCDEFGHIJKLMNOPQRSTUVWXYZbdfhkltΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩβδλζφθψ"
2485
+
+ "𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐛𝐝𝐟𝐡𝐤𝐥𝐭";
2486
+
const longSmalls = new Set(["\\alpha", "\\gamma", "\\delta", "\\epsilon", "\\eta", "\\iota",
2487
+
"\\kappa", "\\mu", "\\nu", "\\pi", "\\rho", "\\sigma", "\\tau", "\\upsilon", "\\chi", "\\psi",
2488
+
"\\omega", "\\imath", "\\jmath"]);
2489
+
const longTalls = new Set(["\\Gamma", "\\Delta", "\\Sigma", "\\Omega", "\\beta", "\\delta",
2490
+
"\\lambda", "\\theta", "\\psi"]);
2491
+
2492
+
const mathmlBuilder$a = (group, style) => {
2493
+
const accentNode = group.isStretchy
2494
+
? stretchy.accentNode(group)
2495
+
: new mathMLTree.MathNode("mo", [makeText(group.label, group.mode)]);
2496
+
2497
+
if (group.label === "\\vec") {
2498
+
accentNode.style.transform = "scale(0.75) translate(10%, 30%)";
2499
+
} else {
2500
+
accentNode.style.mathStyle = "normal";
2501
+
accentNode.style.mathDepth = "0";
2502
+
if (needWebkitShift.has(group.label) && utils.isCharacterBox(group.base)) {
2503
+
let shift = "";
2504
+
const ch = group.base.text;
2505
+
if (smalls.indexOf(ch) > -1 || longSmalls.has(ch)) { shift = "tml-xshift"; }
2506
+
if (talls.indexOf(ch) > -1 || longTalls.has(ch)) { shift = "tml-capshift"; }
2507
+
if (shift) { accentNode.classes.push(shift); }
2508
+
}
2509
+
}
2510
+
if (!group.isStretchy) {
2511
+
accentNode.setAttribute("stretchy", "false");
2512
+
}
2513
+
2514
+
const node = new mathMLTree.MathNode((group.label === "\\c" ? "munder" : "mover"),
2515
+
[buildGroup$1(group.base, style), accentNode]
2516
+
);
2517
+
2518
+
return node;
2519
+
};
2520
+
2521
+
const nonStretchyAccents = new Set([
2522
+
"\\acute",
2523
+
"\\grave",
2524
+
"\\ddot",
2525
+
"\\dddot",
2526
+
"\\ddddot",
2527
+
"\\tilde",
2528
+
"\\bar",
2529
+
"\\breve",
2530
+
"\\check",
2531
+
"\\hat",
2532
+
"\\vec",
2533
+
"\\dot",
2534
+
"\\mathring"
2535
+
]);
2536
+
2537
+
const needWebkitShift = new Set([
2538
+
"\\acute",
2539
+
"\\bar",
2540
+
"\\breve",
2541
+
"\\check",
2542
+
"\\dot",
2543
+
"\\ddot",
2544
+
"\\grave",
2545
+
"\\hat",
2546
+
"\\mathring",
2547
+
"\\'", "\\^", "\\~", "\\=", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"
2548
+
]);
2549
+
2550
+
const combiningChar = {
2551
+
"\\`": "\u0300",
2552
+
"\\'": "\u0301",
2553
+
"\\^": "\u0302",
2554
+
"\\~": "\u0303",
2555
+
"\\=": "\u0304",
2556
+
"\\u": "\u0306",
2557
+
"\\.": "\u0307",
2558
+
'\\"': "\u0308",
2559
+
"\\r": "\u030A",
2560
+
"\\H": "\u030B",
2561
+
"\\v": "\u030C"
2562
+
};
2563
+
2564
+
// Accents
2565
+
defineFunction({
2566
+
type: "accent",
2567
+
names: [
2568
+
"\\acute",
2569
+
"\\grave",
2570
+
"\\ddot",
2571
+
"\\dddot",
2572
+
"\\ddddot",
2573
+
"\\tilde",
2574
+
"\\bar",
2575
+
"\\breve",
2576
+
"\\check",
2577
+
"\\hat",
2578
+
"\\vec",
2579
+
"\\dot",
2580
+
"\\mathring",
2581
+
"\\overparen",
2582
+
"\\widecheck",
2583
+
"\\widehat",
2584
+
"\\wideparen",
2585
+
"\\widetilde",
2586
+
"\\overrightarrow",
2587
+
"\\overleftarrow",
2588
+
"\\Overrightarrow",
2589
+
"\\overleftrightarrow",
2590
+
"\\overgroup",
2591
+
"\\overleftharpoon",
2592
+
"\\overrightharpoon"
2593
+
],
2594
+
props: {
2595
+
numArgs: 1
2596
+
},
2597
+
handler: (context, args) => {
2598
+
const base = normalizeArgument(args[0]);
2599
+
2600
+
const isStretchy = !nonStretchyAccents.has(context.funcName);
2601
+
2602
+
return {
2603
+
type: "accent",
2604
+
mode: context.parser.mode,
2605
+
label: context.funcName,
2606
+
isStretchy: isStretchy,
2607
+
base: base
2608
+
};
2609
+
},
2610
+
mathmlBuilder: mathmlBuilder$a
2611
+
});
2612
+
2613
+
// Text-mode accents
2614
+
defineFunction({
2615
+
type: "accent",
2616
+
names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\c", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"],
2617
+
props: {
2618
+
numArgs: 1,
2619
+
allowedInText: true,
2620
+
allowedInMath: true,
2621
+
argTypes: ["primitive"]
2622
+
},
2623
+
handler: (context, args) => {
2624
+
const base = normalizeArgument(args[0]);
2625
+
const mode = context.parser.mode;
2626
+
2627
+
if (mode === "math" && context.parser.settings.strict) {
2628
+
// LaTeX only writes a warning. It doesn't stop. We'll issue the same warning.
2629
+
// eslint-disable-next-line no-console
2630
+
console.log(`Temml parse error: Command ${context.funcName} is invalid in math mode.`);
2631
+
}
2632
+
2633
+
if (mode === "text" && base.text && base.text.length === 1
2634
+
&& context.funcName in combiningChar && smalls.indexOf(base.text) > -1) {
2635
+
// Return a combining accent character
2636
+
return {
2637
+
type: "textord",
2638
+
mode: "text",
2639
+
text: base.text + combiningChar[context.funcName]
2640
+
}
2641
+
} else {
2642
+
// Build up the accent
2643
+
return {
2644
+
type: "accent",
2645
+
mode: mode,
2646
+
label: context.funcName,
2647
+
isStretchy: false,
2648
+
base: base
2649
+
}
2650
+
}
2651
+
},
2652
+
mathmlBuilder: mathmlBuilder$a
2653
+
});
2654
+
2655
+
defineFunction({
2656
+
type: "accentUnder",
2657
+
names: [
2658
+
"\\underleftarrow",
2659
+
"\\underrightarrow",
2660
+
"\\underleftrightarrow",
2661
+
"\\undergroup",
2662
+
"\\underparen",
2663
+
"\\utilde"
2664
+
],
2665
+
props: {
2666
+
numArgs: 1
2667
+
},
2668
+
handler: ({ parser, funcName }, args) => {
2669
+
const base = args[0];
2670
+
return {
2671
+
type: "accentUnder",
2672
+
mode: parser.mode,
2673
+
label: funcName,
2674
+
base: base
2675
+
};
2676
+
},
2677
+
mathmlBuilder: (group, style) => {
2678
+
const accentNode = stretchy.accentNode(group);
2679
+
accentNode.style["math-depth"] = 0;
2680
+
const node = new mathMLTree.MathNode("munder", [
2681
+
buildGroup$1(group.base, style),
2682
+
accentNode
2683
+
]);
2684
+
return node;
2685
+
}
2686
+
});
2687
+
2688
+
/**
2689
+
* This file does conversion between units. In particular, it provides
2690
+
* calculateSize to convert other units into CSS units.
2691
+
*/
2692
+
2693
+
2694
+
const ptPerUnit = {
2695
+
// Convert to CSS (Postscipt) points, not TeX points
2696
+
// https://en.wikibooks.org/wiki/LaTeX/Lengths and
2697
+
// https://tex.stackexchange.com/a/8263
2698
+
pt: 800 / 803, // convert TeX point to CSS (Postscript) point
2699
+
pc: (12 * 800) / 803, // pica
2700
+
dd: ((1238 / 1157) * 800) / 803, // didot
2701
+
cc: ((14856 / 1157) * 800) / 803, // cicero (12 didot)
2702
+
nd: ((685 / 642) * 800) / 803, // new didot
2703
+
nc: ((1370 / 107) * 800) / 803, // new cicero (12 new didot)
2704
+
sp: ((1 / 65536) * 800) / 803, // scaled point (TeX's internal smallest unit)
2705
+
mm: (25.4 / 72),
2706
+
cm: (2.54 / 72),
2707
+
in: (1 / 72),
2708
+
px: (96 / 72)
2709
+
};
2710
+
2711
+
/**
2712
+
* Determine whether the specified unit (either a string defining the unit
2713
+
* or a "size" parse node containing a unit field) is valid.
2714
+
*/
2715
+
const validUnits = [
2716
+
"em",
2717
+
"ex",
2718
+
"mu",
2719
+
"pt",
2720
+
"mm",
2721
+
"cm",
2722
+
"in",
2723
+
"px",
2724
+
"bp",
2725
+
"pc",
2726
+
"dd",
2727
+
"cc",
2728
+
"nd",
2729
+
"nc",
2730
+
"sp"
2731
+
];
2732
+
2733
+
const validUnit = function(unit) {
2734
+
if (typeof unit !== "string") {
2735
+
unit = unit.unit;
2736
+
}
2737
+
return validUnits.indexOf(unit) > -1
2738
+
};
2739
+
2740
+
const emScale = styleLevel => {
2741
+
const scriptLevel = Math.max(styleLevel - 1, 0);
2742
+
return [1, 0.7, 0.5][scriptLevel]
2743
+
};
2744
+
2745
+
/*
2746
+
* Convert a "size" parse node (with numeric "number" and string "unit" fields,
2747
+
* as parsed by functions.js argType "size") into a CSS value.
2748
+
*/
2749
+
const calculateSize = function(sizeValue, style) {
2750
+
let number = sizeValue.number;
2751
+
if (style.maxSize[0] < 0 && number > 0) {
2752
+
return { number: 0, unit: "em" }
2753
+
}
2754
+
const unit = sizeValue.unit;
2755
+
switch (unit) {
2756
+
case "mm":
2757
+
case "cm":
2758
+
case "in":
2759
+
case "px": {
2760
+
const numInCssPts = number * ptPerUnit[unit];
2761
+
if (numInCssPts > style.maxSize[1]) {
2762
+
return { number: style.maxSize[1], unit: "pt" }
2763
+
}
2764
+
return { number, unit }; // absolute CSS units.
2765
+
}
2766
+
case "em":
2767
+
case "ex": {
2768
+
// In TeX, em and ex do not change size in \scriptstyle.
2769
+
if (unit === "ex") { number *= 0.431; }
2770
+
number = Math.min(number / emScale(style.level), style.maxSize[0]);
2771
+
return { number: utils.round(number), unit: "em" };
2772
+
}
2773
+
case "bp": {
2774
+
if (number > style.maxSize[1]) { number = style.maxSize[1]; }
2775
+
return { number, unit: "pt" }; // TeX bp is a CSS pt. (1/72 inch).
2776
+
}
2777
+
case "pt":
2778
+
case "pc":
2779
+
case "dd":
2780
+
case "cc":
2781
+
case "nd":
2782
+
case "nc":
2783
+
case "sp": {
2784
+
number = Math.min(number * ptPerUnit[unit], style.maxSize[1]);
2785
+
return { number: utils.round(number), unit: "pt" }
2786
+
}
2787
+
case "mu": {
2788
+
number = Math.min(number / 18, style.maxSize[0]);
2789
+
return { number: utils.round(number), unit: "em" }
2790
+
}
2791
+
default:
2792
+
throw new ParseError("Invalid unit: '" + unit + "'")
2793
+
}
2794
+
};
2795
+
2796
+
// Helper functions
2797
+
2798
+
const padding$2 = width => {
2799
+
const node = new mathMLTree.MathNode("mspace");
2800
+
node.setAttribute("width", width + "em");
2801
+
return node
2802
+
};
2803
+
2804
+
const paddedNode = (group, lspace = 0.3, rspace = 0, mustSmash = false) => {
2805
+
if (group == null && rspace === 0) { return padding$2(lspace) }
2806
+
const row = group ? [group] : [];
2807
+
if (lspace !== 0) { row.unshift(padding$2(lspace)); }
2808
+
if (rspace > 0) { row.push(padding$2(rspace)); }
2809
+
if (mustSmash) {
2810
+
// Used for the bottom arrow in a {CD} environment
2811
+
const mpadded = new mathMLTree.MathNode("mpadded", row);
2812
+
mpadded.setAttribute("height", "0");
2813
+
return mpadded
2814
+
} else {
2815
+
return new mathMLTree.MathNode("mrow", row)
2816
+
}
2817
+
};
2818
+
2819
+
const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel);
2820
+
2821
+
const munderoverNode = (fName, body, below, style) => {
2822
+
const arrowNode = stretchy.mathMLnode(fName);
2823
+
// Is this the short part of a mhchem equilibrium arrow?
2824
+
const isEq = fName.slice(1, 3) === "eq";
2825
+
const minWidth = fName.charAt(1) === "x"
2826
+
? "1.75" // mathtools extensible arrows are ≥ 1.75em long
2827
+
: fName.slice(2, 4) === "cd"
2828
+
? "3.0" // cd package arrows
2829
+
: isEq
2830
+
? "1.0" // The shorter harpoon of a mhchem equilibrium arrow
2831
+
: "2.0"; // other mhchem arrows
2832
+
// TODO: When Firefox supports minsize, use the next line.
2833
+
//arrowNode.setAttribute("minsize", String(minWidth) + "em")
2834
+
arrowNode.setAttribute("lspace", "0");
2835
+
arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0"));
2836
+
2837
+
// <munderover> upper and lower labels are set to scriptlevel by MathML
2838
+
// So we have to adjust our label dimensions accordingly.
2839
+
const labelStyle = style.withLevel(style.level < 2 ? 2 : 3);
2840
+
const minArrowWidth = labelSize(minWidth, labelStyle.level);
2841
+
// The dummyNode will be inside a <mover> inside a <mover>
2842
+
// So it will be at scriptlevel 3
2843
+
const dummyWidth = labelSize(minWidth, 3);
2844
+
const emptyLabel = paddedNode(null, minArrowWidth.toFixed(4), 0);
2845
+
const dummyNode = paddedNode(null, dummyWidth.toFixed(4), 0);
2846
+
// The arrow is a little longer than the label. Set a spacer length.
2847
+
const space = labelSize((isEq ? 0 : 0.3), labelStyle.level).toFixed(4);
2848
+
let upperNode;
2849
+
let lowerNode;
2850
+
2851
+
const gotUpper = (body && body.body &&
2852
+
// \hphantom visible content
2853
+
(body.body.body || body.body.length > 0));
2854
+
if (gotUpper) {
2855
+
let label = buildGroup$1(body, labelStyle);
2856
+
const mustSmash = (fName === "\\\\cdrightarrow" || fName === "\\\\cdleftarrow");
2857
+
label = paddedNode(label, space, space, mustSmash);
2858
+
// Since Firefox does not support minsize, stack a invisible node
2859
+
// on top of the label. Its width will serve as a min-width.
2860
+
// TODO: Refactor this after Firefox supports minsize.
2861
+
upperNode = new mathMLTree.MathNode("mover", [label, dummyNode]);
2862
+
}
2863
+
const gotLower = (below && below.body &&
2864
+
(below.body.body || below.body.length > 0));
2865
+
if (gotLower) {
2866
+
let label = buildGroup$1(below, labelStyle);
2867
+
label = paddedNode(label, space, space);
2868
+
lowerNode = new mathMLTree.MathNode("munder", [label, dummyNode]);
2869
+
}
2870
+
2871
+
let node;
2872
+
if (!gotUpper && !gotLower) {
2873
+
node = new mathMLTree.MathNode("mover", [arrowNode, emptyLabel]);
2874
+
} else if (gotUpper && gotLower) {
2875
+
node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]);
2876
+
} else if (gotUpper) {
2877
+
node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]);
2878
+
} else {
2879
+
node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]);
2880
+
}
2881
+
if (minWidth === "3.0") { node.style.height = "1em"; } // CD environment
2882
+
node.setAttribute("accent", "false"); // Necessary for MS Word
2883
+
return node
2884
+
};
2885
+
2886
+
// Stretchy arrows with an optional argument
2887
+
defineFunction({
2888
+
type: "xArrow",
2889
+
names: [
2890
+
"\\xleftarrow",
2891
+
"\\xrightarrow",
2892
+
"\\xLeftarrow",
2893
+
"\\xRightarrow",
2894
+
"\\xleftrightarrow",
2895
+
"\\xLeftrightarrow",
2896
+
"\\xhookleftarrow",
2897
+
"\\xhookrightarrow",
2898
+
"\\xmapsto",
2899
+
"\\xrightharpoondown",
2900
+
"\\xrightharpoonup",
2901
+
"\\xleftharpoondown",
2902
+
"\\xleftharpoonup",
2903
+
"\\xlongequal",
2904
+
"\\xtwoheadrightarrow",
2905
+
"\\xtwoheadleftarrow",
2906
+
// The next 5 functions are here only to support mhchem
2907
+
"\\yields",
2908
+
"\\yieldsLeft",
2909
+
"\\mesomerism",
2910
+
"\\longrightharpoonup",
2911
+
"\\longleftharpoondown",
2912
+
// The next 3 functions are here only to support the {CD} environment.
2913
+
"\\\\cdrightarrow",
2914
+
"\\\\cdleftarrow",
2915
+
"\\\\cdlongequal"
2916
+
],
2917
+
props: {
2918
+
numArgs: 1,
2919
+
numOptionalArgs: 1
2920
+
},
2921
+
handler({ parser, funcName }, args, optArgs) {
2922
+
return {
2923
+
type: "xArrow",
2924
+
mode: parser.mode,
2925
+
name: funcName,
2926
+
body: args[0],
2927
+
below: optArgs[0]
2928
+
};
2929
+
},
2930
+
mathmlBuilder(group, style) {
2931
+
// Build the arrow and its labels.
2932
+
const node = munderoverNode(group.name, group.body, group.below, style);
2933
+
// Create operator spacing for a relation.
2934
+
const row = [node];
2935
+
row.unshift(padding$2(0.2778));
2936
+
row.push(padding$2(0.2778));
2937
+
return new mathMLTree.MathNode("mrow", row)
2938
+
}
2939
+
});
2940
+
2941
+
const arrowComponent = {
2942
+
"\\xtofrom": ["\\xrightarrow", "\\xleftarrow"],
2943
+
"\\xleftrightharpoons": ["\\xleftharpoonup", "\\xrightharpoondown"],
2944
+
"\\xrightleftharpoons": ["\\xrightharpoonup", "\\xleftharpoondown"],
2945
+
"\\yieldsLeftRight": ["\\yields", "\\yieldsLeft"],
2946
+
// The next three all get the same harpoon glyphs. Only the lengths and paddings differ.
2947
+
"\\equilibrium": ["\\longrightharpoonup", "\\longleftharpoondown"],
2948
+
"\\equilibriumRight": ["\\longrightharpoonup", "\\eqleftharpoondown"],
2949
+
"\\equilibriumLeft": ["\\eqrightharpoonup", "\\longleftharpoondown"]
2950
+
};
2951
+
2952
+
// Browsers are not good at stretching a glyph that contains a pair of stacked arrows such as ⇄.
2953
+
// So we stack a pair of single arrows.
2954
+
defineFunction({
2955
+
type: "stackedArrow",
2956
+
names: [
2957
+
"\\xtofrom", // expfeil
2958
+
"\\xleftrightharpoons", // mathtools
2959
+
"\\xrightleftharpoons", // mathtools
2960
+
"\\yieldsLeftRight", // mhchem
2961
+
"\\equilibrium", // mhchem
2962
+
"\\equilibriumRight",
2963
+
"\\equilibriumLeft"
2964
+
],
2965
+
props: {
2966
+
numArgs: 1,
2967
+
numOptionalArgs: 1
2968
+
},
2969
+
handler({ parser, funcName }, args, optArgs) {
2970
+
const lowerArrowBody = args[0]
2971
+
? {
2972
+
type: "hphantom",
2973
+
mode: parser.mode,
2974
+
body: args[0]
2975
+
}
2976
+
: null;
2977
+
const upperArrowBelow = optArgs[0]
2978
+
? {
2979
+
type: "hphantom",
2980
+
mode: parser.mode,
2981
+
body: optArgs[0]
2982
+
}
2983
+
: null;
2984
+
return {
2985
+
type: "stackedArrow",
2986
+
mode: parser.mode,
2987
+
name: funcName,
2988
+
body: args[0],
2989
+
upperArrowBelow,
2990
+
lowerArrowBody,
2991
+
below: optArgs[0]
2992
+
};
2993
+
},
2994
+
mathmlBuilder(group, style) {
2995
+
const topLabel = arrowComponent[group.name][0];
2996
+
const botLabel = arrowComponent[group.name][1];
2997
+
const topArrow = munderoverNode(topLabel, group.body, group.upperArrowBelow, style);
2998
+
const botArrow = munderoverNode(botLabel, group.lowerArrowBody, group.below, style);
2999
+
let wrapper;
3000
+
3001
+
const raiseNode = new mathMLTree.MathNode("mpadded", [topArrow]);
3002
+
raiseNode.setAttribute("voffset", "0.3em");
3003
+
raiseNode.setAttribute("height", "+0.3em");
3004
+
raiseNode.setAttribute("depth", "-0.3em");
3005
+
// One of the arrows is given ~zero width. so the other has the same horzontal alignment.
3006
+
if (group.name === "\\equilibriumLeft") {
3007
+
const botNode = new mathMLTree.MathNode("mpadded", [botArrow]);
3008
+
botNode.setAttribute("width", "0.5em");
3009
+
wrapper = new mathMLTree.MathNode(
3010
+
"mpadded",
3011
+
[padding$2(0.2778), botNode, raiseNode, padding$2(0.2778)]
3012
+
);
3013
+
} else {
3014
+
raiseNode.setAttribute("width", (group.name === "\\equilibriumRight" ? "0.5em" : "0"));
3015
+
wrapper = new mathMLTree.MathNode(
3016
+
"mpadded",
3017
+
[padding$2(0.2778), raiseNode, botArrow, padding$2(0.2778)]
3018
+
);
3019
+
}
3020
+
3021
+
wrapper.setAttribute("voffset", "-0.18em");
3022
+
wrapper.setAttribute("height", "-0.18em");
3023
+
wrapper.setAttribute("depth", "+0.18em");
3024
+
return wrapper
3025
+
}
3026
+
});
3027
+
3028
+
/**
3029
+
* Asserts that the node is of the given type and returns it with stricter
3030
+
* typing. Throws if the node's type does not match.
3031
+
*/
3032
+
function assertNodeType(node, type) {
3033
+
if (!node || node.type !== type) {
3034
+
throw new Error(
3035
+
`Expected node of type ${type}, but got ` +
3036
+
(node ? `node of type ${node.type}` : String(node))
3037
+
);
3038
+
}
3039
+
return node;
3040
+
}
3041
+
3042
+
/**
3043
+
* Returns the node more strictly typed iff it is of the given type. Otherwise,
3044
+
* returns null.
3045
+
*/
3046
+
function assertSymbolNodeType(node) {
3047
+
const typedNode = checkSymbolNodeType(node);
3048
+
if (!typedNode) {
3049
+
throw new Error(
3050
+
`Expected node of symbol group type, but got ` +
3051
+
(node ? `node of type ${node.type}` : String(node))
3052
+
);
3053
+
}
3054
+
return typedNode;
3055
+
}
3056
+
3057
+
/**
3058
+
* Returns the node more strictly typed iff it is of the given type. Otherwise,
3059
+
* returns null.
3060
+
*/
3061
+
function checkSymbolNodeType(node) {
3062
+
if (node && (node.type === "atom" ||
3063
+
Object.prototype.hasOwnProperty.call(NON_ATOMS, node.type))) {
3064
+
return node;
3065
+
}
3066
+
return null;
3067
+
}
3068
+
3069
+
const cdArrowFunctionName = {
3070
+
">": "\\\\cdrightarrow",
3071
+
"<": "\\\\cdleftarrow",
3072
+
"=": "\\\\cdlongequal",
3073
+
A: "\\uparrow",
3074
+
V: "\\downarrow",
3075
+
"|": "\\Vert",
3076
+
".": "no arrow"
3077
+
};
3078
+
3079
+
const newCell = () => {
3080
+
// Create an empty cell, to be filled below with parse nodes.
3081
+
return { type: "styling", body: [], mode: "math", scriptLevel: "display" };
3082
+
};
3083
+
3084
+
const isStartOfArrow = (node) => {
3085
+
return node.type === "textord" && node.text === "@";
3086
+
};
3087
+
3088
+
const isLabelEnd = (node, endChar) => {
3089
+
return (node.type === "mathord" || node.type === "atom") && node.text === endChar;
3090
+
};
3091
+
3092
+
function cdArrow(arrowChar, labels, parser) {
3093
+
// Return a parse tree of an arrow and its labels.
3094
+
// This acts in a way similar to a macro expansion.
3095
+
const funcName = cdArrowFunctionName[arrowChar];
3096
+
switch (funcName) {
3097
+
case "\\\\cdrightarrow":
3098
+
case "\\\\cdleftarrow":
3099
+
return parser.callFunction(funcName, [labels[0]], [labels[1]]);
3100
+
case "\\uparrow":
3101
+
case "\\downarrow": {
3102
+
const leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []);
3103
+
const bareArrow = {
3104
+
type: "atom",
3105
+
text: funcName,
3106
+
mode: "math",
3107
+
family: "rel"
3108
+
};
3109
+
const sizedArrow = parser.callFunction("\\Big", [bareArrow], []);
3110
+
const rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []);
3111
+
const arrowGroup = {
3112
+
type: "ordgroup",
3113
+
mode: "math",
3114
+
body: [leftLabel, sizedArrow, rightLabel],
3115
+
semisimple: true
3116
+
};
3117
+
return parser.callFunction("\\\\cdparent", [arrowGroup], []);
3118
+
}
3119
+
case "\\\\cdlongequal":
3120
+
return parser.callFunction("\\\\cdlongequal", [], []);
3121
+
case "\\Vert": {
3122
+
const arrow = { type: "textord", text: "\\Vert", mode: "math" };
3123
+
return parser.callFunction("\\Big", [arrow], []);
3124
+
}
3125
+
default:
3126
+
return { type: "textord", text: " ", mode: "math" };
3127
+
}
3128
+
}
3129
+
3130
+
function parseCD(parser) {
3131
+
// Get the array's parse nodes with \\ temporarily mapped to \cr.
3132
+
const parsedRows = [];
3133
+
parser.gullet.beginGroup();
3134
+
parser.gullet.macros.set("\\cr", "\\\\\\relax");
3135
+
parser.gullet.beginGroup();
3136
+
while (true) {
3137
+
// Get the parse nodes for the next row.
3138
+
parsedRows.push(parser.parseExpression(false, "\\\\"));
3139
+
parser.gullet.endGroup();
3140
+
parser.gullet.beginGroup();
3141
+
const next = parser.fetch().text;
3142
+
if (next === "&" || next === "\\\\") {
3143
+
parser.consume();
3144
+
} else if (next === "\\end") {
3145
+
if (parsedRows[parsedRows.length - 1].length === 0) {
3146
+
parsedRows.pop(); // final row ended in \\
3147
+
}
3148
+
break;
3149
+
} else {
3150
+
throw new ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken);
3151
+
}
3152
+
}
3153
+
3154
+
let row = [];
3155
+
const body = [row];
3156
+
3157
+
// Loop thru the parse nodes. Collect them into cells and arrows.
3158
+
for (let i = 0; i < parsedRows.length; i++) {
3159
+
// Start a new row.
3160
+
const rowNodes = parsedRows[i];
3161
+
// Create the first cell.
3162
+
let cell = newCell();
3163
+
3164
+
for (let j = 0; j < rowNodes.length; j++) {
3165
+
if (!isStartOfArrow(rowNodes[j])) {
3166
+
// If a parseNode is not an arrow, it goes into a cell.
3167
+
cell.body.push(rowNodes[j]);
3168
+
} else {
3169
+
// Parse node j is an "@", the start of an arrow.
3170
+
// Before starting on the arrow, push the cell into `row`.
3171
+
row.push(cell);
3172
+
3173
+
// Now collect parseNodes into an arrow.
3174
+
// The character after "@" defines the arrow type.
3175
+
j += 1;
3176
+
const arrowChar = assertSymbolNodeType(rowNodes[j]).text;
3177
+
3178
+
// Create two empty label nodes. We may or may not use them.
3179
+
const labels = new Array(2);
3180
+
labels[0] = { type: "ordgroup", mode: "math", body: [] };
3181
+
labels[1] = { type: "ordgroup", mode: "math", body: [] };
3182
+
3183
+
// Process the arrow.
3184
+
if ("=|.".indexOf(arrowChar) > -1) ; else if ("<>AV".indexOf(arrowChar) > -1) {
3185
+
// Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take
3186
+
// two optional labels. E.g. the right-point arrow syntax is
3187
+
// really: @>{optional label}>{optional label}>
3188
+
// Collect parseNodes into labels.
3189
+
for (let labelNum = 0; labelNum < 2; labelNum++) {
3190
+
let inLabel = true;
3191
+
for (let k = j + 1; k < rowNodes.length; k++) {
3192
+
if (isLabelEnd(rowNodes[k], arrowChar)) {
3193
+
inLabel = false;
3194
+
j = k;
3195
+
break;
3196
+
}
3197
+
if (isStartOfArrow(rowNodes[k])) {
3198
+
throw new ParseError(
3199
+
"Missing a " + arrowChar + " character to complete a CD arrow.",
3200
+
rowNodes[k]
3201
+
);
3202
+
}
3203
+
3204
+
labels[labelNum].body.push(rowNodes[k]);
3205
+
}
3206
+
if (inLabel) {
3207
+
// isLabelEnd never returned a true.
3208
+
throw new ParseError(
3209
+
"Missing a " + arrowChar + " character to complete a CD arrow.",
3210
+
rowNodes[j]
3211
+
);
3212
+
}
3213
+
}
3214
+
} else {
3215
+
throw new ParseError(`Expected one of "<>AV=|." after @.`);
3216
+
}
3217
+
3218
+
// Now join the arrow to its labels.
3219
+
const arrow = cdArrow(arrowChar, labels, parser);
3220
+
3221
+
// Wrap the arrow in a styling node
3222
+
row.push(arrow);
3223
+
// In CD's syntax, cells are implicit. That is, everything that
3224
+
// is not an arrow gets collected into a cell. So create an empty
3225
+
// cell now. It will collect upcoming parseNodes.
3226
+
cell = newCell();
3227
+
}
3228
+
}
3229
+
if (i % 2 === 0) {
3230
+
// Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell
3231
+
// The last cell is not yet pushed into `row`, so:
3232
+
row.push(cell);
3233
+
} else {
3234
+
// Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow
3235
+
// Remove the empty cell that was placed at the beginning of `row`.
3236
+
row.shift();
3237
+
}
3238
+
row = [];
3239
+
body.push(row);
3240
+
}
3241
+
body.pop();
3242
+
3243
+
// End row group
3244
+
parser.gullet.endGroup();
3245
+
// End array group defining \\
3246
+
parser.gullet.endGroup();
3247
+
3248
+
return {
3249
+
type: "array",
3250
+
mode: "math",
3251
+
body,
3252
+
tags: null,
3253
+
labels: new Array(body.length + 1).fill(""),
3254
+
envClasses: ["jot", "cd"],
3255
+
cols: [],
3256
+
hLinesBeforeRow: new Array(body.length + 1).fill([])
3257
+
};
3258
+
}
3259
+
3260
+
// The functions below are not available for general use.
3261
+
// They are here only for internal use by the {CD} environment in placing labels
3262
+
// next to vertical arrows.
3263
+
3264
+
// We don't need any such functions for horizontal arrows because we can reuse
3265
+
// the functionality that already exists for extensible arrows.
3266
+
3267
+
defineFunction({
3268
+
type: "cdlabel",
3269
+
names: ["\\\\cdleft", "\\\\cdright"],
3270
+
props: {
3271
+
numArgs: 1
3272
+
},
3273
+
handler({ parser, funcName }, args) {
3274
+
return {
3275
+
type: "cdlabel",
3276
+
mode: parser.mode,
3277
+
side: funcName.slice(4),
3278
+
label: args[0]
3279
+
};
3280
+
},
3281
+
mathmlBuilder(group, style) {
3282
+
if (group.label.body.length === 0) {
3283
+
return new mathMLTree.MathNode("mrow", style) // empty label
3284
+
}
3285
+
// Abuse an <mtable> to create vertically centered content.
3286
+
const mtd = new mathMLTree.MathNode("mtd", [buildGroup$1(group.label, style)]);
3287
+
mtd.style.padding = "0";
3288
+
const mtr = new mathMLTree.MathNode("mtr", [mtd]);
3289
+
const mtable = new mathMLTree.MathNode("mtable", [mtr]);
3290
+
const label = new mathMLTree.MathNode("mpadded", [mtable]);
3291
+
// Set the label width to zero so that the arrow will be centered under the corner cell.
3292
+
label.setAttribute("width", "0");
3293
+
label.setAttribute("displaystyle", "false");
3294
+
label.setAttribute("scriptlevel", "1");
3295
+
if (group.side === "left") {
3296
+
label.style.display = "flex";
3297
+
label.style.justifyContent = "flex-end";
3298
+
}
3299
+
return label;
3300
+
}
3301
+
});
3302
+
3303
+
defineFunction({
3304
+
type: "cdlabelparent",
3305
+
names: ["\\\\cdparent"],
3306
+
props: {
3307
+
numArgs: 1
3308
+
},
3309
+
handler({ parser }, args) {
3310
+
return {
3311
+
type: "cdlabelparent",
3312
+
mode: parser.mode,
3313
+
fragment: args[0]
3314
+
};
3315
+
},
3316
+
mathmlBuilder(group, style) {
3317
+
return new mathMLTree.MathNode("mrow", [buildGroup$1(group.fragment, style)]);
3318
+
}
3319
+
});
3320
+
3321
+
// \@char is an internal function that takes a grouped decimal argument like
3322
+
// {123} and converts into symbol with code 123. It is used by the *macro*
3323
+
// \char defined in macros.js.
3324
+
defineFunction({
3325
+
type: "textord",
3326
+
names: ["\\@char"],
3327
+
props: {
3328
+
numArgs: 1,
3329
+
allowedInText: true
3330
+
},
3331
+
handler({ parser, token }, args) {
3332
+
const arg = assertNodeType(args[0], "ordgroup");
3333
+
const group = arg.body;
3334
+
let number = "";
3335
+
for (let i = 0; i < group.length; i++) {
3336
+
const node = assertNodeType(group[i], "textord");
3337
+
number += node.text;
3338
+
}
3339
+
const code = parseInt(number);
3340
+
if (isNaN(code)) {
3341
+
throw new ParseError(`\\@char has non-numeric argument ${number}`, token)
3342
+
}
3343
+
return {
3344
+
type: "textord",
3345
+
mode: parser.mode,
3346
+
text: String.fromCodePoint(code)
3347
+
}
3348
+
}
3349
+
});
3350
+
3351
+
// Helpers
3352
+
const htmlRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6})$/i;
3353
+
const htmlOrNameRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i;
3354
+
const RGBregEx = /^ *\d{1,3} *(?:, *\d{1,3} *){2}$/;
3355
+
const rgbRegEx = /^ *[10](?:\.\d*)? *(?:, *[10](?:\.\d*)? *){2}$/;
3356
+
const xcolorHtmlRegEx = /^[a-f0-9]{6}$/i;
3357
+
const toHex = num => {
3358
+
let str = num.toString(16);
3359
+
if (str.length === 1) { str = "0" + str; }
3360
+
return str
3361
+
};
3362
+
3363
+
// Colors from Tables 4.1 and 4.2 of the xcolor package.
3364
+
// Table 4.1 (lower case) RGB values are taken from chroma and xcolor.dtx.
3365
+
// Table 4.2 (Capitalizzed) values were sampled, because Chroma contains a unreliable
3366
+
// conversion from cmyk to RGB. See https://tex.stackexchange.com/a/537274.
3367
+
const xcolors = JSON.parse(`{
3368
+
"Apricot": "#ffb484",
3369
+
"Aquamarine": "#08b4bc",
3370
+
"Bittersweet": "#c84c14",
3371
+
"blue": "#0000FF",
3372
+
"Blue": "#303494",
3373
+
"BlueGreen": "#08b4bc",
3374
+
"BlueViolet": "#503c94",
3375
+
"BrickRed": "#b8341c",
3376
+
"brown": "#BF8040",
3377
+
"Brown": "#802404",
3378
+
"BurntOrange": "#f8941c",
3379
+
"CadetBlue": "#78749c",
3380
+
"CarnationPink": "#f884b4",
3381
+
"Cerulean": "#08a4e4",
3382
+
"CornflowerBlue": "#40ace4",
3383
+
"cyan": "#00FFFF",
3384
+
"Cyan": "#08acec",
3385
+
"Dandelion": "#ffbc44",
3386
+
"darkgray": "#404040",
3387
+
"DarkOrchid": "#a8548c",
3388
+
"Emerald": "#08ac9c",
3389
+
"ForestGreen": "#089c54",
3390
+
"Fuchsia": "#90348c",
3391
+
"Goldenrod": "#ffdc44",
3392
+
"gray": "#808080",
3393
+
"Gray": "#98949c",
3394
+
"green": "#00FF00",
3395
+
"Green": "#08a44c",
3396
+
"GreenYellow": "#e0e474",
3397
+
"JungleGreen": "#08ac9c",
3398
+
"Lavender": "#f89cc4",
3399
+
"lightgray": "#c0c0c0",
3400
+
"lime": "#BFFF00",
3401
+
"LimeGreen": "#90c43c",
3402
+
"magenta": "#FF00FF",
3403
+
"Magenta": "#f0048c",
3404
+
"Mahogany": "#b0341c",
3405
+
"Maroon": "#b03434",
3406
+
"Melon": "#f89c7c",
3407
+
"MidnightBlue": "#086494",
3408
+
"Mulberry": "#b03c94",
3409
+
"NavyBlue": "#086cbc",
3410
+
"olive": "#7F7F00",
3411
+
"OliveGreen": "#407c34",
3412
+
"orange": "#FF8000",
3413
+
"Orange": "#f8843c",
3414
+
"OrangeRed": "#f0145c",
3415
+
"Orchid": "#b074ac",
3416
+
"Peach": "#f8945c",
3417
+
"Periwinkle": "#8074bc",
3418
+
"PineGreen": "#088c74",
3419
+
"pink": "#ff7f7f",
3420
+
"Plum": "#98248c",
3421
+
"ProcessBlue": "#08b4ec",
3422
+
"purple": "#BF0040",
3423
+
"Purple": "#a0449c",
3424
+
"RawSienna": "#983c04",
3425
+
"red": "#ff0000",
3426
+
"Red": "#f01c24",
3427
+
"RedOrange": "#f86434",
3428
+
"RedViolet": "#a0246c",
3429
+
"Rhodamine": "#f0549c",
3430
+
"Royallue": "#0874bc",
3431
+
"RoyalPurple": "#683c9c",
3432
+
"RubineRed": "#f0047c",
3433
+
"Salmon": "#f8948c",
3434
+
"SeaGreen": "#30bc9c",
3435
+
"Sepia": "#701404",
3436
+
"SkyBlue": "#48c4dc",
3437
+
"SpringGreen": "#c8dc64",
3438
+
"Tan": "#e09c74",
3439
+
"teal": "#007F7F",
3440
+
"TealBlue": "#08acb4",
3441
+
"Thistle": "#d884b4",
3442
+
"Turquoise": "#08b4cc",
3443
+
"violet": "#800080",
3444
+
"Violet": "#60449c",
3445
+
"VioletRed": "#f054a4",
3446
+
"WildStrawberry": "#f0246c",
3447
+
"yellow": "#FFFF00",
3448
+
"Yellow": "#fff404",
3449
+
"YellowGreen": "#98cc6c",
3450
+
"YellowOrange": "#ffa41c"
3451
+
}`);
3452
+
3453
+
const colorFromSpec = (model, spec) => {
3454
+
let color = "";
3455
+
if (model === "HTML") {
3456
+
if (!htmlRegEx.test(spec)) {
3457
+
throw new ParseError("Invalid HTML input.")
3458
+
}
3459
+
color = spec;
3460
+
} else if (model === "RGB") {
3461
+
if (!RGBregEx.test(spec)) {
3462
+
throw new ParseError("Invalid RGB input.")
3463
+
}
3464
+
spec.split(",").map(e => { color += toHex(Number(e.trim())); });
3465
+
} else {
3466
+
if (!rgbRegEx.test(spec)) {
3467
+
throw new ParseError("Invalid rbg input.")
3468
+
}
3469
+
spec.split(",").map(e => {
3470
+
const num = Number(e.trim());
3471
+
if (num > 1) { throw new ParseError("Color rgb input must be < 1.") }
3472
+
color += toHex(Number((num * 255).toFixed(0)));
3473
+
});
3474
+
}
3475
+
if (color.charAt(0) !== "#") { color = "#" + color; }
3476
+
return color
3477
+
};
3478
+
3479
+
const validateColor = (color, macros, token) => {
3480
+
const macroName = `\\\\color@${color}`; // from \defineColor.
3481
+
const match = htmlOrNameRegEx.exec(color);
3482
+
if (!match) { throw new ParseError("Invalid color: '" + color + "'", token) }
3483
+
// We allow a 6-digit HTML color spec without a leading "#".
3484
+
// This follows the xcolor package's HTML color model.
3485
+
// Predefined color names are all missed by this RegEx pattern.
3486
+
if (xcolorHtmlRegEx.test(color)) {
3487
+
return "#" + color
3488
+
} else if (color.charAt(0) === "#") {
3489
+
return color
3490
+
} else if (macros.has(macroName)) {
3491
+
color = macros.get(macroName).tokens[0].text;
3492
+
} else if (xcolors[color]) {
3493
+
color = xcolors[color];
3494
+
}
3495
+
return color
3496
+
};
3497
+
3498
+
const mathmlBuilder$9 = (group, style) => {
3499
+
// In LaTeX, color is not supposed to change the spacing of any node.
3500
+
// So instead of wrapping the group in an <mstyle>, we apply
3501
+
// the color individually to each node and return a document fragment.
3502
+
let expr = buildExpression(group.body, style.withColor(group.color));
3503
+
expr = expr.map(e => {
3504
+
e.style.color = group.color;
3505
+
return e
3506
+
});
3507
+
return mathMLTree.newDocumentFragment(expr)
3508
+
};
3509
+
3510
+
defineFunction({
3511
+
type: "color",
3512
+
names: ["\\textcolor"],
3513
+
props: {
3514
+
numArgs: 2,
3515
+
numOptionalArgs: 1,
3516
+
allowedInText: true,
3517
+
argTypes: ["raw", "raw", "original"]
3518
+
},
3519
+
handler({ parser, token }, args, optArgs) {
3520
+
const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string;
3521
+
let color = "";
3522
+
if (model) {
3523
+
const spec = assertNodeType(args[0], "raw").string;
3524
+
color = colorFromSpec(model, spec);
3525
+
} else {
3526
+
color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token);
3527
+
}
3528
+
const body = args[1];
3529
+
return {
3530
+
type: "color",
3531
+
mode: parser.mode,
3532
+
color,
3533
+
isTextColor: true,
3534
+
body: ordargument(body)
3535
+
}
3536
+
},
3537
+
mathmlBuilder: mathmlBuilder$9
3538
+
});
3539
+
3540
+
defineFunction({
3541
+
type: "color",
3542
+
names: ["\\color"],
3543
+
props: {
3544
+
numArgs: 1,
3545
+
numOptionalArgs: 1,
3546
+
allowedInText: true,
3547
+
argTypes: ["raw", "raw"]
3548
+
},
3549
+
handler({ parser, breakOnTokenText, token }, args, optArgs) {
3550
+
const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string;
3551
+
let color = "";
3552
+
if (model) {
3553
+
const spec = assertNodeType(args[0], "raw").string;
3554
+
color = colorFromSpec(model, spec);
3555
+
} else {
3556
+
color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token);
3557
+
}
3558
+
3559
+
// Parse out the implicit body that should be colored.
3560
+
const body = parser.parseExpression(true, breakOnTokenText, true);
3561
+
3562
+
return {
3563
+
type: "color",
3564
+
mode: parser.mode,
3565
+
color,
3566
+
isTextColor: false,
3567
+
body
3568
+
}
3569
+
},
3570
+
mathmlBuilder: mathmlBuilder$9
3571
+
});
3572
+
3573
+
defineFunction({
3574
+
type: "color",
3575
+
names: ["\\definecolor"],
3576
+
props: {
3577
+
numArgs: 3,
3578
+
allowedInText: true,
3579
+
argTypes: ["raw", "raw", "raw"]
3580
+
},
3581
+
handler({ parser, funcName, token }, args) {
3582
+
const name = assertNodeType(args[0], "raw").string;
3583
+
if (!/^[A-Za-z]+$/.test(name)) {
3584
+
throw new ParseError("Color name must be latin letters.", token)
3585
+
}
3586
+
const model = assertNodeType(args[1], "raw").string;
3587
+
if (!["HTML", "RGB", "rgb"].includes(model)) {
3588
+
throw new ParseError("Color model must be HTML, RGB, or rgb.", token)
3589
+
}
3590
+
const spec = assertNodeType(args[2], "raw").string;
3591
+
const color = colorFromSpec(model, spec);
3592
+
parser.gullet.macros.set(`\\\\color@${name}`, { tokens: [{ text: color }], numArgs: 0 });
3593
+
return { type: "internal", mode: parser.mode }
3594
+
}
3595
+
// No mathmlBuilder. The point of \definecolor is to set a macro.
3596
+
});
3597
+
3598
+
// Row breaks within tabular environments, and line breaks at top level
3599
+
3600
+
3601
+
// \DeclareRobustCommand\\{...\@xnewline}
3602
+
defineFunction({
3603
+
type: "cr",
3604
+
names: ["\\\\"],
3605
+
props: {
3606
+
numArgs: 0,
3607
+
numOptionalArgs: 0,
3608
+
allowedInText: true
3609
+
},
3610
+
3611
+
handler({ parser }, args, optArgs) {
3612
+
const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null;
3613
+
const newLine = !parser.settings.displayMode;
3614
+
return {
3615
+
type: "cr",
3616
+
mode: parser.mode,
3617
+
newLine,
3618
+
size: size && assertNodeType(size, "size").value
3619
+
}
3620
+
},
3621
+
3622
+
// The following builder is called only at the top level,
3623
+
// not within tabular/array environments.
3624
+
3625
+
mathmlBuilder(group, style) {
3626
+
// MathML 3.0 calls for newline to occur in an <mo> or an <mspace>.
3627
+
// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.linebreaking
3628
+
const node = new mathMLTree.MathNode("mo");
3629
+
if (group.newLine) {
3630
+
node.setAttribute("linebreak", "newline");
3631
+
if (group.size) {
3632
+
const size = calculateSize(group.size, style);
3633
+
node.setAttribute("height", size.number + size.unit);
3634
+
}
3635
+
}
3636
+
return node
3637
+
}
3638
+
});
3639
+
3640
+
const globalMap = {
3641
+
"\\global": "\\global",
3642
+
"\\long": "\\\\globallong",
3643
+
"\\\\globallong": "\\\\globallong",
3644
+
"\\def": "\\gdef",
3645
+
"\\gdef": "\\gdef",
3646
+
"\\edef": "\\xdef",
3647
+
"\\xdef": "\\xdef",
3648
+
"\\let": "\\\\globallet",
3649
+
"\\futurelet": "\\\\globalfuture"
3650
+
};
3651
+
3652
+
const checkControlSequence = (tok) => {
3653
+
const name = tok.text;
3654
+
if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) {
3655
+
throw new ParseError("Expected a control sequence", tok);
3656
+
}
3657
+
return name;
3658
+
};
3659
+
3660
+
const getRHS = (parser) => {
3661
+
let tok = parser.gullet.popToken();
3662
+
if (tok.text === "=") {
3663
+
// consume optional equals
3664
+
tok = parser.gullet.popToken();
3665
+
if (tok.text === " ") {
3666
+
// consume one optional space
3667
+
tok = parser.gullet.popToken();
3668
+
}
3669
+
}
3670
+
return tok;
3671
+
};
3672
+
3673
+
const letCommand = (parser, name, tok, global) => {
3674
+
let macro = parser.gullet.macros.get(tok.text);
3675
+
if (macro == null) {
3676
+
// don't expand it later even if a macro with the same name is defined
3677
+
// e.g., \let\foo=\frac \def\frac{\relax} \frac12
3678
+
tok.noexpand = true;
3679
+
macro = {
3680
+
tokens: [tok],
3681
+
numArgs: 0,
3682
+
// reproduce the same behavior in expansion
3683
+
unexpandable: !parser.gullet.isExpandable(tok.text)
3684
+
};
3685
+
}
3686
+
parser.gullet.macros.set(name, macro, global);
3687
+
};
3688
+
3689
+
// <assignment> -> <non-macro assignment>|<macro assignment>
3690
+
// <non-macro assignment> -> <simple assignment>|\global<non-macro assignment>
3691
+
// <macro assignment> -> <definition>|<prefix><macro assignment>
3692
+
// <prefix> -> \global|\long|\outer
3693
+
defineFunction({
3694
+
type: "internal",
3695
+
names: [
3696
+
"\\global",
3697
+
"\\long",
3698
+
"\\\\globallong" // can’t be entered directly
3699
+
],
3700
+
props: {
3701
+
numArgs: 0,
3702
+
allowedInText: true
3703
+
},
3704
+
handler({ parser, funcName }) {
3705
+
parser.consumeSpaces();
3706
+
const token = parser.fetch();
3707
+
if (globalMap[token.text]) {
3708
+
// Temml doesn't have \par, so ignore \long
3709
+
if (funcName === "\\global" || funcName === "\\\\globallong") {
3710
+
token.text = globalMap[token.text];
3711
+
}
3712
+
return assertNodeType(parser.parseFunction(), "internal");
3713
+
}
3714
+
throw new ParseError(`Invalid token after macro prefix`, token);
3715
+
}
3716
+
});
3717
+
3718
+
// Basic support for macro definitions: \def, \gdef, \edef, \xdef
3719
+
// <definition> -> <def><control sequence><definition text>
3720
+
// <def> -> \def|\gdef|\edef|\xdef
3721
+
// <definition text> -> <parameter text><left brace><balanced text><right brace>
3722
+
defineFunction({
3723
+
type: "internal",
3724
+
names: ["\\def", "\\gdef", "\\edef", "\\xdef"],
3725
+
props: {
3726
+
numArgs: 0,
3727
+
allowedInText: true,
3728
+
primitive: true
3729
+
},
3730
+
handler({ parser, funcName }) {
3731
+
let tok = parser.gullet.popToken();
3732
+
const name = tok.text;
3733
+
if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) {
3734
+
throw new ParseError("Expected a control sequence", tok);
3735
+
}
3736
+
3737
+
let numArgs = 0;
3738
+
let insert;
3739
+
const delimiters = [[]];
3740
+
// <parameter text> contains no braces
3741
+
while (parser.gullet.future().text !== "{") {
3742
+
tok = parser.gullet.popToken();
3743
+
if (tok.text === "#") {
3744
+
// If the very last character of the <parameter text> is #, so that
3745
+
// this # is immediately followed by {, TeX will behave as if the {
3746
+
// had been inserted at the right end of both the parameter text
3747
+
// and the replacement text.
3748
+
if (parser.gullet.future().text === "{") {
3749
+
insert = parser.gullet.future();
3750
+
delimiters[numArgs].push("{");
3751
+
break;
3752
+
}
3753
+
3754
+
// A parameter, the first appearance of # must be followed by 1,
3755
+
// the next by 2, and so on; up to nine #’s are allowed
3756
+
tok = parser.gullet.popToken();
3757
+
if (!/^[1-9]$/.test(tok.text)) {
3758
+
throw new ParseError(`Invalid argument number "${tok.text}"`);
3759
+
}
3760
+
if (parseInt(tok.text) !== numArgs + 1) {
3761
+
throw new ParseError(`Argument number "${tok.text}" out of order`);
3762
+
}
3763
+
numArgs++;
3764
+
delimiters.push([]);
3765
+
} else if (tok.text === "EOF") {
3766
+
throw new ParseError("Expected a macro definition");
3767
+
} else {
3768
+
delimiters[numArgs].push(tok.text);
3769
+
}
3770
+
}
3771
+
// replacement text, enclosed in '{' and '}' and properly nested
3772
+
let { tokens } = parser.gullet.consumeArg();
3773
+
if (insert) {
3774
+
tokens.unshift(insert);
3775
+
}
3776
+
3777
+
if (funcName === "\\edef" || funcName === "\\xdef") {
3778
+
tokens = parser.gullet.expandTokens(tokens);
3779
+
if (tokens.length > parser.gullet.settings.maxExpand) {
3780
+
throw new ParseError("Too many expansions in an " + funcName);
3781
+
}
3782
+
tokens.reverse(); // to fit in with stack order
3783
+
}
3784
+
// Final arg is the expansion of the macro
3785
+
parser.gullet.macros.set(
3786
+
name,
3787
+
{ tokens, numArgs, delimiters },
3788
+
funcName === globalMap[funcName]
3789
+
);
3790
+
return { type: "internal", mode: parser.mode };
3791
+
}
3792
+
});
3793
+
3794
+
// <simple assignment> -> <let assignment>
3795
+
// <let assignment> -> \futurelet<control sequence><token><token>
3796
+
// | \let<control sequence><equals><one optional space><token>
3797
+
// <equals> -> <optional spaces>|<optional spaces>=
3798
+
defineFunction({
3799
+
type: "internal",
3800
+
names: [
3801
+
"\\let",
3802
+
"\\\\globallet" // can’t be entered directly
3803
+
],
3804
+
props: {
3805
+
numArgs: 0,
3806
+
allowedInText: true,
3807
+
primitive: true
3808
+
},
3809
+
handler({ parser, funcName }) {
3810
+
const name = checkControlSequence(parser.gullet.popToken());
3811
+
parser.gullet.consumeSpaces();
3812
+
const tok = getRHS(parser);
3813
+
letCommand(parser, name, tok, funcName === "\\\\globallet");
3814
+
return { type: "internal", mode: parser.mode };
3815
+
}
3816
+
});
3817
+
3818
+
// ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf
3819
+
defineFunction({
3820
+
type: "internal",
3821
+
names: [
3822
+
"\\futurelet",
3823
+
"\\\\globalfuture" // can’t be entered directly
3824
+
],
3825
+
props: {
3826
+
numArgs: 0,
3827
+
allowedInText: true,
3828
+
primitive: true
3829
+
},
3830
+
handler({ parser, funcName }) {
3831
+
const name = checkControlSequence(parser.gullet.popToken());
3832
+
const middle = parser.gullet.popToken();
3833
+
const tok = parser.gullet.popToken();
3834
+
letCommand(parser, name, tok, funcName === "\\\\globalfuture");
3835
+
parser.gullet.pushToken(tok);
3836
+
parser.gullet.pushToken(middle);
3837
+
return { type: "internal", mode: parser.mode };
3838
+
}
3839
+
});
3840
+
3841
+
defineFunction({
3842
+
type: "internal",
3843
+
names: ["\\newcommand", "\\renewcommand", "\\providecommand"],
3844
+
props: {
3845
+
numArgs: 0,
3846
+
allowedInText: true,
3847
+
primitive: true
3848
+
},
3849
+
handler({ parser, funcName }) {
3850
+
let name = "";
3851
+
const tok = parser.gullet.popToken();
3852
+
if (tok.text === "{") {
3853
+
name = checkControlSequence(parser.gullet.popToken());
3854
+
parser.gullet.popToken();
3855
+
} else {
3856
+
name = checkControlSequence(tok);
3857
+
}
3858
+
3859
+
const exists = parser.gullet.isDefined(name);
3860
+
if (exists && funcName === "\\newcommand") {
3861
+
throw new ParseError(
3862
+
`\\newcommand{${name}} attempting to redefine ${name}; use \\renewcommand`
3863
+
);
3864
+
}
3865
+
if (!exists && funcName === "\\renewcommand") {
3866
+
throw new ParseError(
3867
+
`\\renewcommand{${name}} when command ${name} does not yet exist; use \\newcommand`
3868
+
);
3869
+
}
3870
+
3871
+
let numArgs = 0;
3872
+
if (parser.gullet.future().text === "[") {
3873
+
let tok = parser.gullet.popToken();
3874
+
tok = parser.gullet.popToken();
3875
+
if (!/^[0-9]$/.test(tok.text)) {
3876
+
throw new ParseError(`Invalid number of arguments: "${tok.text}"`);
3877
+
}
3878
+
numArgs = parseInt(tok.text);
3879
+
tok = parser.gullet.popToken();
3880
+
if (tok.text !== "]") {
3881
+
throw new ParseError(`Invalid argument "${tok.text}"`);
3882
+
}
3883
+
}
3884
+
3885
+
// replacement text, enclosed in '{' and '}' and properly nested
3886
+
const { tokens } = parser.gullet.consumeArg();
3887
+
3888
+
if (!(funcName === "\\providecommand" && parser.gullet.macros.has(name))) {
3889
+
// Ignore \providecommand
3890
+
parser.gullet.macros.set(
3891
+
name,
3892
+
{ tokens, numArgs }
3893
+
);
3894
+
}
3895
+
3896
+
return { type: "internal", mode: parser.mode };
3897
+
3898
+
}
3899
+
});
3900
+
3901
+
// Extra data needed for the delimiter handler down below
3902
+
const delimiterSizes = {
3903
+
"\\bigl": { mclass: "mopen", size: 1 },
3904
+
"\\Bigl": { mclass: "mopen", size: 2 },
3905
+
"\\biggl": { mclass: "mopen", size: 3 },
3906
+
"\\Biggl": { mclass: "mopen", size: 4 },
3907
+
"\\bigr": { mclass: "mclose", size: 1 },
3908
+
"\\Bigr": { mclass: "mclose", size: 2 },
3909
+
"\\biggr": { mclass: "mclose", size: 3 },
3910
+
"\\Biggr": { mclass: "mclose", size: 4 },
3911
+
"\\bigm": { mclass: "mrel", size: 1 },
3912
+
"\\Bigm": { mclass: "mrel", size: 2 },
3913
+
"\\biggm": { mclass: "mrel", size: 3 },
3914
+
"\\Biggm": { mclass: "mrel", size: 4 },
3915
+
"\\big": { mclass: "mord", size: 1 },
3916
+
"\\Big": { mclass: "mord", size: 2 },
3917
+
"\\bigg": { mclass: "mord", size: 3 },
3918
+
"\\Bigg": { mclass: "mord", size: 4 }
3919
+
};
3920
+
3921
+
const delimiters = [
3922
+
"(",
3923
+
"\\lparen",
3924
+
")",
3925
+
"\\rparen",
3926
+
"[",
3927
+
"\\lbrack",
3928
+
"]",
3929
+
"\\rbrack",
3930
+
"\\{",
3931
+
"\\lbrace",
3932
+
"\\}",
3933
+
"\\rbrace",
3934
+
"⦇",
3935
+
"\\llparenthesis",
3936
+
"⦈",
3937
+
"\\rrparenthesis",
3938
+
"\\lfloor",
3939
+
"\\rfloor",
3940
+
"\u230a",
3941
+
"\u230b",
3942
+
"\\lceil",
3943
+
"\\rceil",
3944
+
"\u2308",
3945
+
"\u2309",
3946
+
"<",
3947
+
">",
3948
+
"\\langle",
3949
+
"\u27e8",
3950
+
"\\rangle",
3951
+
"\u27e9",
3952
+
"\\lAngle",
3953
+
"\u27ea",
3954
+
"\\rAngle",
3955
+
"\u27eb",
3956
+
"\\llangle",
3957
+
"⦉",
3958
+
"\\rrangle",
3959
+
"⦊",
3960
+
"\\lt",
3961
+
"\\gt",
3962
+
"\\lvert",
3963
+
"\\rvert",
3964
+
"\\lVert",
3965
+
"\\rVert",
3966
+
"\\lgroup",
3967
+
"\\rgroup",
3968
+
"\u27ee",
3969
+
"\u27ef",
3970
+
"\\lmoustache",
3971
+
"\\rmoustache",
3972
+
"\u23b0",
3973
+
"\u23b1",
3974
+
"\\llbracket",
3975
+
"\\rrbracket",
3976
+
"\u27e6",
3977
+
"\u27e6",
3978
+
"\\lBrace",
3979
+
"\\rBrace",
3980
+
"\u2983",
3981
+
"\u2984",
3982
+
"/",
3983
+
"\\backslash",
3984
+
"|",
3985
+
"\\vert",
3986
+
"\\|",
3987
+
"\\Vert",
3988
+
"\u2016",
3989
+
"\\uparrow",
3990
+
"\\Uparrow",
3991
+
"\\downarrow",
3992
+
"\\Downarrow",
3993
+
"\\updownarrow",
3994
+
"\\Updownarrow",
3995
+
"."
3996
+
];
3997
+
3998
+
// Export isDelimiter for benefit of parser.
3999
+
const dels = ["}", "\\left", "\\middle", "\\right"];
4000
+
const isDelimiter = str => str.length > 0 &&
4001
+
(delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
4002
+
4003
+
// Metrics of the different sizes. Found by looking at TeX's output of
4004
+
// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
4005
+
// Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
4006
+
const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
4007
+
4008
+
// Delimiter functions
4009
+
function checkDelimiter(delim, context) {
4010
+
const symDelim = checkSymbolNodeType(delim);
4011
+
if (symDelim && delimiters.includes(symDelim.text)) {
4012
+
// If a character is not in the MathML operator dictionary, it will not stretch.
4013
+
// Replace such characters w/characters that will stretch.
4014
+
if (["<", "\\lt"].includes(symDelim.text)) { symDelim.text = "⟨"; }
4015
+
if ([">", "\\gt"].includes(symDelim.text)) { symDelim.text = "⟩"; }
4016
+
return symDelim;
4017
+
} else if (symDelim) {
4018
+
throw new ParseError(`Invalid delimiter '${symDelim.text}' after '${context.funcName}'`, delim);
4019
+
} else {
4020
+
throw new ParseError(`Invalid delimiter type '${delim.type}'`, delim);
4021
+
}
4022
+
}
4023
+
4024
+
// / \
4025
+
const needExplicitStretch = ["\u002F", "\u005C", "\\backslash", "\\vert", "|"];
4026
+
4027
+
defineFunction({
4028
+
type: "delimsizing",
4029
+
names: [
4030
+
"\\bigl",
4031
+
"\\Bigl",
4032
+
"\\biggl",
4033
+
"\\Biggl",
4034
+
"\\bigr",
4035
+
"\\Bigr",
4036
+
"\\biggr",
4037
+
"\\Biggr",
4038
+
"\\bigm",
4039
+
"\\Bigm",
4040
+
"\\biggm",
4041
+
"\\Biggm",
4042
+
"\\big",
4043
+
"\\Big",
4044
+
"\\bigg",
4045
+
"\\Bigg"
4046
+
],
4047
+
props: {
4048
+
numArgs: 1,
4049
+
argTypes: ["primitive"]
4050
+
},
4051
+
handler: (context, args) => {
4052
+
const delim = checkDelimiter(args[0], context);
4053
+
4054
+
return {
4055
+
type: "delimsizing",
4056
+
mode: context.parser.mode,
4057
+
size: delimiterSizes[context.funcName].size,
4058
+
mclass: delimiterSizes[context.funcName].mclass,
4059
+
delim: delim.text
4060
+
};
4061
+
},
4062
+
mathmlBuilder: (group) => {
4063
+
const children = [];
4064
+
4065
+
if (group.delim === ".") { group.delim = ""; }
4066
+
children.push(makeText(group.delim, group.mode));
4067
+
4068
+
const node = new mathMLTree.MathNode("mo", children);
4069
+
4070
+
if (group.mclass === "mopen" || group.mclass === "mclose") {
4071
+
// Only some of the delimsizing functions act as fences, and they
4072
+
// return "mopen" or "mclose" mclass.
4073
+
node.setAttribute("fence", "true");
4074
+
} else {
4075
+
// Explicitly disable fencing if it's not a fence, to override the
4076
+
// defaults.
4077
+
node.setAttribute("fence", "false");
4078
+
}
4079
+
if (needExplicitStretch.includes(group.delim) || group.delim.indexOf("arrow") > -1) {
4080
+
// We have to explicitly set stretchy to true.
4081
+
node.setAttribute("stretchy", "true");
4082
+
}
4083
+
node.setAttribute("symmetric", "true"); // Needed for tall arrows in Firefox.
4084
+
node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em");
4085
+
node.setAttribute("maxsize", sizeToMaxHeight[group.size] + "em");
4086
+
return node;
4087
+
}
4088
+
});
4089
+
4090
+
function assertParsed(group) {
4091
+
if (!group.body) {
4092
+
throw new Error("Bug: The leftright ParseNode wasn't fully parsed.");
4093
+
}
4094
+
}
4095
+
4096
+
defineFunction({
4097
+
type: "leftright-right",
4098
+
names: ["\\right"],
4099
+
props: {
4100
+
numArgs: 1,
4101
+
argTypes: ["primitive"]
4102
+
},
4103
+
handler: (context, args) => {
4104
+
return {
4105
+
type: "leftright-right",
4106
+
mode: context.parser.mode,
4107
+
delim: checkDelimiter(args[0], context).text
4108
+
};
4109
+
}
4110
+
});
4111
+
4112
+
defineFunction({
4113
+
type: "leftright",
4114
+
names: ["\\left"],
4115
+
props: {
4116
+
numArgs: 1,
4117
+
argTypes: ["primitive"]
4118
+
},
4119
+
handler: (context, args) => {
4120
+
const delim = checkDelimiter(args[0], context);
4121
+
4122
+
const parser = context.parser;
4123
+
// Parse out the implicit body
4124
+
++parser.leftrightDepth;
4125
+
// parseExpression stops before '\\right' or `\\middle`
4126
+
let body = parser.parseExpression(false, null, true);
4127
+
let nextToken = parser.fetch();
4128
+
while (nextToken.text === "\\middle") {
4129
+
// `\middle`, from the ε-TeX package, ends one group and starts another group.
4130
+
// We had to parse this expression with `breakOnMiddle` enabled in order
4131
+
// to get TeX-compliant parsing of \over.
4132
+
// But we do not want, at this point, to end on \middle, so continue
4133
+
// to parse until we fetch a `\right`.
4134
+
parser.consume();
4135
+
const middle = parser.fetch().text;
4136
+
if (!symbols.math[middle]) {
4137
+
throw new ParseError(`Invalid delimiter '${middle}' after '\\middle'`);
4138
+
}
4139
+
checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" });
4140
+
body.push({ type: "middle", mode: "math", delim: middle });
4141
+
parser.consume();
4142
+
body = body.concat(parser.parseExpression(false, null, true));
4143
+
nextToken = parser.fetch();
4144
+
}
4145
+
--parser.leftrightDepth;
4146
+
// Check the next token
4147
+
parser.expect("\\right", false);
4148
+
const right = assertNodeType(parser.parseFunction(), "leftright-right");
4149
+
return {
4150
+
type: "leftright",
4151
+
mode: parser.mode,
4152
+
body,
4153
+
left: delim.text,
4154
+
right: right.delim
4155
+
};
4156
+
},
4157
+
mathmlBuilder: (group, style) => {
4158
+
assertParsed(group);
4159
+
const inner = buildExpression(group.body, style);
4160
+
4161
+
if (group.left === ".") { group.left = ""; }
4162
+
const leftNode = new mathMLTree.MathNode("mo", [makeText(group.left, group.mode)]);
4163
+
leftNode.setAttribute("fence", "true");
4164
+
leftNode.setAttribute("form", "prefix");
4165
+
if (group.left === "/" || group.left === "\u005C" || group.left.indexOf("arrow") > -1) {
4166
+
leftNode.setAttribute("stretchy", "true");
4167
+
}
4168
+
inner.unshift(leftNode);
4169
+
4170
+
if (group.right === ".") { group.right = ""; }
4171
+
const rightNode = new mathMLTree.MathNode("mo", [makeText(group.right, group.mode)]);
4172
+
rightNode.setAttribute("fence", "true");
4173
+
rightNode.setAttribute("form", "postfix");
4174
+
if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) {
4175
+
rightNode.setAttribute("stretchy", "true");
4176
+
}
4177
+
if (group.body.length > 0) {
4178
+
const lastElement = group.body[group.body.length - 1];
4179
+
if (lastElement.type === "color" && !lastElement.isTextColor) {
4180
+
// \color is a switch. If the last element is of type "color" then
4181
+
// the user set the \color switch and left it on.
4182
+
// A \right delimiter turns the switch off, but the delimiter itself gets the color.
4183
+
rightNode.setAttribute("mathcolor", lastElement.color);
4184
+
}
4185
+
}
4186
+
inner.push(rightNode);
4187
+
4188
+
return makeRow(inner);
4189
+
}
4190
+
});
4191
+
4192
+
defineFunction({
4193
+
type: "middle",
4194
+
names: ["\\middle"],
4195
+
props: {
4196
+
numArgs: 1,
4197
+
argTypes: ["primitive"]
4198
+
},
4199
+
handler: (context, args) => {
4200
+
const delim = checkDelimiter(args[0], context);
4201
+
if (!context.parser.leftrightDepth) {
4202
+
throw new ParseError("\\middle without preceding \\left", delim);
4203
+
}
4204
+
4205
+
return {
4206
+
type: "middle",
4207
+
mode: context.parser.mode,
4208
+
delim: delim.text
4209
+
};
4210
+
},
4211
+
mathmlBuilder: (group, style) => {
4212
+
const textNode = makeText(group.delim, group.mode);
4213
+
const middleNode = new mathMLTree.MathNode("mo", [textNode]);
4214
+
middleNode.setAttribute("fence", "true");
4215
+
if (group.delim.indexOf("arrow") > -1) {
4216
+
middleNode.setAttribute("stretchy", "true");
4217
+
}
4218
+
// The next line is not semantically correct, but
4219
+
// Chromium fails to stretch if it is not there.
4220
+
middleNode.setAttribute("form", "prefix");
4221
+
// MathML gives 5/18em spacing to each <mo> element.
4222
+
// \middle should get delimiter spacing instead.
4223
+
middleNode.setAttribute("lspace", "0.05em");
4224
+
middleNode.setAttribute("rspace", "0.05em");
4225
+
return middleNode;
4226
+
}
4227
+
});
4228
+
4229
+
const padding$1 = _ => {
4230
+
const node = new mathMLTree.MathNode("mspace");
4231
+
node.setAttribute("width", "3pt");
4232
+
return node
4233
+
};
4234
+
4235
+
const mathmlBuilder$8 = (group, style) => {
4236
+
let node;
4237
+
if (group.label.indexOf("colorbox") > -1 || group.label === "\\boxed") {
4238
+
// MathML core does not support +width attribute in <mpadded>.
4239
+
// Firefox does not reliably add side padding.
4240
+
// Insert <mspace>
4241
+
node = new mathMLTree.MathNode("mrow", [
4242
+
padding$1(),
4243
+
buildGroup$1(group.body, style),
4244
+
padding$1()
4245
+
]);
4246
+
} else {
4247
+
node = new mathMLTree.MathNode("menclose", [buildGroup$1(group.body, style)]);
4248
+
}
4249
+
switch (group.label) {
4250
+
case "\\overline":
4251
+
node.setAttribute("notation", "top"); // for Firefox & WebKit
4252
+
node.classes.push("tml-overline"); // for Chromium
4253
+
break
4254
+
case "\\underline":
4255
+
node.setAttribute("notation", "bottom");
4256
+
node.classes.push("tml-underline");
4257
+
break
4258
+
case "\\cancel":
4259
+
node.setAttribute("notation", "updiagonalstrike");
4260
+
node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "upstrike"]));
4261
+
break
4262
+
case "\\bcancel":
4263
+
node.setAttribute("notation", "downdiagonalstrike");
4264
+
node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "downstrike"]));
4265
+
break
4266
+
case "\\sout":
4267
+
node.setAttribute("notation", "horizontalstrike");
4268
+
node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "sout"]));
4269
+
break
4270
+
case "\\xcancel":
4271
+
node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
4272
+
node.classes.push("tml-xcancel");
4273
+
break
4274
+
case "\\longdiv":
4275
+
node.setAttribute("notation", "longdiv");
4276
+
node.classes.push("longdiv-top");
4277
+
node.children.push(new mathMLTree.MathNode("mrow", [], ["longdiv-arc"]));
4278
+
break
4279
+
case "\\phase":
4280
+
node.setAttribute("notation", "phasorangle");
4281
+
node.classes.push("phasor-bottom");
4282
+
node.children.push(new mathMLTree.MathNode("mrow", [], ["phasor-angle"]));
4283
+
break
4284
+
case "\\textcircled":
4285
+
node.setAttribute("notation", "circle");
4286
+
node.classes.push("circle-pad");
4287
+
node.children.push(new mathMLTree.MathNode("mrow", [], ["textcircle"]));
4288
+
break
4289
+
case "\\angl":
4290
+
node.setAttribute("notation", "actuarial");
4291
+
node.classes.push("actuarial");
4292
+
break
4293
+
case "\\boxed":
4294
+
// \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty
4295
+
node.setAttribute("notation", "box");
4296
+
node.classes.push("tml-box");
4297
+
node.setAttribute("scriptlevel", "0");
4298
+
node.setAttribute("displaystyle", "true");
4299
+
break
4300
+
case "\\fbox":
4301
+
node.setAttribute("notation", "box");
4302
+
node.classes.push("tml-fbox");
4303
+
break
4304
+
case "\\fcolorbox":
4305
+
case "\\colorbox": {
4306
+
// <menclose> doesn't have a good notation option for \colorbox.
4307
+
// So use <mpadded> instead. Set some attributes that come
4308
+
// included with <menclose>.
4309
+
//const fboxsep = 3; // 3 pt from LaTeX source2e
4310
+
//node.setAttribute("height", `+${2 * fboxsep}pt`)
4311
+
//node.setAttribute("voffset", `${fboxsep}pt`)
4312
+
const style = { padding: "3pt 0 3pt 0" };
4313
+
4314
+
if (group.label === "\\fcolorbox") {
4315
+
style.border = "0.0667em solid " + String(group.borderColor);
4316
+
}
4317
+
node.style = style;
4318
+
break
4319
+
}
4320
+
}
4321
+
if (group.backgroundColor) {
4322
+
node.setAttribute("mathbackground", group.backgroundColor);
4323
+
}
4324
+
return node;
4325
+
};
4326
+
4327
+
defineFunction({
4328
+
type: "enclose",
4329
+
names: ["\\colorbox"],
4330
+
props: {
4331
+
numArgs: 2,
4332
+
numOptionalArgs: 1,
4333
+
allowedInText: true,
4334
+
argTypes: ["raw", "raw", "text"]
4335
+
},
4336
+
handler({ parser, funcName }, args, optArgs) {
4337
+
const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string;
4338
+
let color = "";
4339
+
if (model) {
4340
+
const spec = assertNodeType(args[0], "raw").string;
4341
+
color = colorFromSpec(model, spec);
4342
+
} else {
4343
+
color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros);
4344
+
}
4345
+
const body = args[1];
4346
+
return {
4347
+
type: "enclose",
4348
+
mode: parser.mode,
4349
+
label: funcName,
4350
+
backgroundColor: color,
4351
+
body
4352
+
};
4353
+
},
4354
+
mathmlBuilder: mathmlBuilder$8
4355
+
});
4356
+
4357
+
defineFunction({
4358
+
type: "enclose",
4359
+
names: ["\\fcolorbox"],
4360
+
props: {
4361
+
numArgs: 3,
4362
+
numOptionalArgs: 1,
4363
+
allowedInText: true,
4364
+
argTypes: ["raw", "raw", "raw", "text"]
4365
+
},
4366
+
handler({ parser, funcName }, args, optArgs) {
4367
+
const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string;
4368
+
let borderColor = "";
4369
+
let backgroundColor;
4370
+
if (model) {
4371
+
const borderSpec = assertNodeType(args[0], "raw").string;
4372
+
const backgroundSpec = assertNodeType(args[0], "raw").string;
4373
+
borderColor = colorFromSpec(model, borderSpec);
4374
+
backgroundColor = colorFromSpec(model, backgroundSpec);
4375
+
} else {
4376
+
borderColor = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros);
4377
+
backgroundColor = validateColor(assertNodeType(args[1], "raw").string, parser.gullet.macros);
4378
+
}
4379
+
const body = args[2];
4380
+
return {
4381
+
type: "enclose",
4382
+
mode: parser.mode,
4383
+
label: funcName,
4384
+
backgroundColor,
4385
+
borderColor,
4386
+
body
4387
+
};
4388
+
},
4389
+
mathmlBuilder: mathmlBuilder$8
4390
+
});
4391
+
4392
+
defineFunction({
4393
+
type: "enclose",
4394
+
names: ["\\fbox"],
4395
+
props: {
4396
+
numArgs: 1,
4397
+
argTypes: ["hbox"],
4398
+
allowedInText: true
4399
+
},
4400
+
handler({ parser }, args) {
4401
+
return {
4402
+
type: "enclose",
4403
+
mode: parser.mode,
4404
+
label: "\\fbox",
4405
+
body: args[0]
4406
+
};
4407
+
}
4408
+
});
4409
+
4410
+
defineFunction({
4411
+
type: "enclose",
4412
+
names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline",
4413
+
"\\boxed", "\\longdiv", "\\phase"],
4414
+
props: {
4415
+
numArgs: 1
4416
+
},
4417
+
handler({ parser, funcName }, args) {
4418
+
const body = args[0];
4419
+
return {
4420
+
type: "enclose",
4421
+
mode: parser.mode,
4422
+
label: funcName,
4423
+
body
4424
+
};
4425
+
},
4426
+
mathmlBuilder: mathmlBuilder$8
4427
+
});
4428
+
4429
+
defineFunction({
4430
+
type: "enclose",
4431
+
names: ["\\underline"],
4432
+
props: {
4433
+
numArgs: 1,
4434
+
allowedInText: true
4435
+
},
4436
+
handler({ parser, funcName }, args) {
4437
+
const body = args[0];
4438
+
return {
4439
+
type: "enclose",
4440
+
mode: parser.mode,
4441
+
label: funcName,
4442
+
body
4443
+
};
4444
+
},
4445
+
mathmlBuilder: mathmlBuilder$8
4446
+
});
4447
+
4448
+
4449
+
defineFunction({
4450
+
type: "enclose",
4451
+
names: ["\\textcircled"],
4452
+
props: {
4453
+
numArgs: 1,
4454
+
argTypes: ["text"],
4455
+
allowedInArgument: true,
4456
+
allowedInText: true
4457
+
},
4458
+
handler({ parser, funcName }, args) {
4459
+
const body = args[0];
4460
+
return {
4461
+
type: "enclose",
4462
+
mode: parser.mode,
4463
+
label: funcName,
4464
+
body
4465
+
};
4466
+
},
4467
+
mathmlBuilder: mathmlBuilder$8
4468
+
});
4469
+
4470
+
/**
4471
+
* All registered environments.
4472
+
* `environments.js` exports this same dictionary again and makes it public.
4473
+
* `Parser.js` requires this dictionary via `environments.js`.
4474
+
*/
4475
+
const _environments = {};
4476
+
4477
+
function defineEnvironment({ type, names, props, handler, mathmlBuilder }) {
4478
+
// Set default values of environments.
4479
+
const data = {
4480
+
type,
4481
+
numArgs: props.numArgs || 0,
4482
+
allowedInText: false,
4483
+
numOptionalArgs: 0,
4484
+
handler
4485
+
};
4486
+
for (let i = 0; i < names.length; ++i) {
4487
+
_environments[names[i]] = data;
4488
+
}
4489
+
if (mathmlBuilder) {
4490
+
_mathmlGroupBuilders[type] = mathmlBuilder;
4491
+
}
4492
+
}
4493
+
4494
+
/**
4495
+
* Lexing or parsing positional information for error reporting.
4496
+
* This object is immutable.
4497
+
*/
4498
+
class SourceLocation {
4499
+
constructor(lexer, start, end) {
4500
+
this.lexer = lexer; // Lexer holding the input string.
4501
+
this.start = start; // Start offset, zero-based inclusive.
4502
+
this.end = end; // End offset, zero-based exclusive.
4503
+
}
4504
+
4505
+
/**
4506
+
* Merges two `SourceLocation`s from location providers, given they are
4507
+
* provided in order of appearance.
4508
+
* - Returns the first one's location if only the first is provided.
4509
+
* - Returns a merged range of the first and the last if both are provided
4510
+
* and their lexers match.
4511
+
* - Otherwise, returns null.
4512
+
*/
4513
+
static range(first, second) {
4514
+
if (!second) {
4515
+
return first && first.loc;
4516
+
} else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) {
4517
+
return null;
4518
+
} else {
4519
+
return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end);
4520
+
}
4521
+
}
4522
+
}
4523
+
4524
+
/**
4525
+
* Interface required to break circular dependency between Token, Lexer, and
4526
+
* ParseError.
4527
+
*/
4528
+
4529
+
/**
4530
+
* The resulting token returned from `lex`.
4531
+
*
4532
+
* It consists of the token text plus some position information.
4533
+
* The position information is essentially a range in an input string,
4534
+
* but instead of referencing the bare input string, we refer to the lexer.
4535
+
* That way it is possible to attach extra metadata to the input string,
4536
+
* like for example a file name or similar.
4537
+
*
4538
+
* The position information is optional, so it is OK to construct synthetic
4539
+
* tokens if appropriate. Not providing available position information may
4540
+
* lead to degraded error reporting, though.
4541
+
*/
4542
+
class Token {
4543
+
constructor(
4544
+
text, // the text of this token
4545
+
loc
4546
+
) {
4547
+
this.text = text;
4548
+
this.loc = loc;
4549
+
}
4550
+
4551
+
/**
4552
+
* Given a pair of tokens (this and endToken), compute a `Token` encompassing
4553
+
* the whole input range enclosed by these two.
4554
+
*/
4555
+
range(
4556
+
endToken, // last token of the range, inclusive
4557
+
text // the text of the newly constructed token
4558
+
) {
4559
+
return new Token(text, SourceLocation.range(this, endToken));
4560
+
}
4561
+
}
4562
+
4563
+
// In TeX, there are actually three sets of dimensions, one for each of
4564
+
// textstyle, scriptstyle, and scriptscriptstyle. These are
4565
+
// provided in the the arrays below, in that order.
4566
+
//
4567
+
4568
+
// Math style is not quite the same thing as script level.
4569
+
const StyleLevel = {
4570
+
DISPLAY: 0,
4571
+
TEXT: 1,
4572
+
SCRIPT: 2,
4573
+
SCRIPTSCRIPT: 3
4574
+
};
4575
+
4576
+
/**
4577
+
* All registered global/built-in macros.
4578
+
* `macros.js` exports this same dictionary again and makes it public.
4579
+
* `Parser.js` requires this dictionary via `macros.js`.
4580
+
*/
4581
+
const _macros = {};
4582
+
4583
+
// This function might one day accept an additional argument and do more things.
4584
+
function defineMacro(name, body) {
4585
+
_macros[name] = body;
4586
+
}
4587
+
4588
+
/**
4589
+
* Predefined macros for Temml.
4590
+
* This can be used to define some commands in terms of others.
4591
+
*/
4592
+
4593
+
const macros = _macros;
4594
+
4595
+
//////////////////////////////////////////////////////////////////////
4596
+
// macro tools
4597
+
4598
+
defineMacro("\\noexpand", function(context) {
4599
+
// The expansion is the token itself; but that token is interpreted
4600
+
// as if its meaning were ‘\relax’ if it is a control sequence that
4601
+
// would ordinarily be expanded by TeX’s expansion rules.
4602
+
const t = context.popToken();
4603
+
if (context.isExpandable(t.text)) {
4604
+
t.noexpand = true;
4605
+
t.treatAsRelax = true;
4606
+
}
4607
+
return { tokens: [t], numArgs: 0 };
4608
+
});
4609
+
4610
+
defineMacro("\\expandafter", function(context) {
4611
+
// TeX first reads the token that comes immediately after \expandafter,
4612
+
// without expanding it; let’s call this token t. Then TeX reads the
4613
+
// token that comes after t (and possibly more tokens, if that token
4614
+
// has an argument), replacing it by its expansion. Finally TeX puts
4615
+
// t back in front of that expansion.
4616
+
const t = context.popToken();
4617
+
context.expandOnce(true); // expand only an expandable token
4618
+
return { tokens: [t], numArgs: 0 };
4619
+
});
4620
+
4621
+
// LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2
4622
+
// TeX source: \long\def\@firstoftwo#1#2{#1}
4623
+
defineMacro("\\@firstoftwo", function(context) {
4624
+
const args = context.consumeArgs(2);
4625
+
return { tokens: args[0], numArgs: 0 };
4626
+
});
4627
+
4628
+
// LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1
4629
+
// TeX source: \long\def\@secondoftwo#1#2{#2}
4630
+
defineMacro("\\@secondoftwo", function(context) {
4631
+
const args = context.consumeArgs(2);
4632
+
return { tokens: args[1], numArgs: 0 };
4633
+
});
4634
+
4635
+
// LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded)
4636
+
// symbol that isn't a space, consuming any spaces but not consuming the
4637
+
// first nonspace character. If that nonspace character matches #1, then
4638
+
// the macro expands to #2; otherwise, it expands to #3.
4639
+
defineMacro("\\@ifnextchar", function(context) {
4640
+
const args = context.consumeArgs(3); // symbol, if, else
4641
+
context.consumeSpaces();
4642
+
const nextToken = context.future();
4643
+
if (args[0].length === 1 && args[0][0].text === nextToken.text) {
4644
+
return { tokens: args[1], numArgs: 0 };
4645
+
} else {
4646
+
return { tokens: args[2], numArgs: 0 };
4647
+
}
4648
+
});
4649
+
4650
+
// LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol.
4651
+
// If it is `*`, then it consumes the symbol, and the macro expands to #1;
4652
+
// otherwise, the macro expands to #2 (without consuming the symbol).
4653
+
// TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}}
4654
+
defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}");
4655
+
4656
+
// LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode
4657
+
defineMacro("\\TextOrMath", function(context) {
4658
+
const args = context.consumeArgs(2);
4659
+
if (context.mode === "text") {
4660
+
return { tokens: args[0], numArgs: 0 };
4661
+
} else {
4662
+
return { tokens: args[1], numArgs: 0 };
4663
+
}
4664
+
});
4665
+
4666
+
const stringFromArg = arg => {
4667
+
// Reverse the order of the arg and return a string.
4668
+
let str = "";
4669
+
for (let i = arg.length - 1; i > -1; i--) {
4670
+
str += arg[i].text;
4671
+
}
4672
+
return str
4673
+
};
4674
+
4675
+
// Lookup table for parsing numbers in base 8 through 16
4676
+
const digitToNumber = {
4677
+
0: 0,
4678
+
1: 1,
4679
+
2: 2,
4680
+
3: 3,
4681
+
4: 4,
4682
+
5: 5,
4683
+
6: 6,
4684
+
7: 7,
4685
+
8: 8,
4686
+
9: 9,
4687
+
a: 10,
4688
+
A: 10,
4689
+
b: 11,
4690
+
B: 11,
4691
+
c: 12,
4692
+
C: 12,
4693
+
d: 13,
4694
+
D: 13,
4695
+
e: 14,
4696
+
E: 14,
4697
+
f: 15,
4698
+
F: 15
4699
+
};
4700
+
4701
+
const nextCharNumber = context => {
4702
+
const numStr = context.future().text;
4703
+
if (numStr === "EOF") { return [null, ""] }
4704
+
return [digitToNumber[numStr.charAt(0)], numStr]
4705
+
};
4706
+
4707
+
const appendCharNumbers = (number, numStr, base) => {
4708
+
for (let i = 1; i < numStr.length; i++) {
4709
+
const digit = digitToNumber[numStr.charAt(i)];
4710
+
number *= base;
4711
+
number += digit;
4712
+
}
4713
+
return number
4714
+
};
4715
+
4716
+
// TeX \char makes a literal character (catcode 12) using the following forms:
4717
+
// (see The TeXBook, p. 43)
4718
+
// \char123 -- decimal
4719
+
// \char'123 -- octal
4720
+
// \char"123 -- hex
4721
+
// \char`x -- character that can be written (i.e. isn't active)
4722
+
// \char`\x -- character that cannot be written (e.g. %)
4723
+
// These all refer to characters from the font, so we turn them into special
4724
+
// calls to a function \@char dealt with in the Parser.
4725
+
defineMacro("\\char", function(context) {
4726
+
let token = context.popToken();
4727
+
let base;
4728
+
let number = "";
4729
+
if (token.text === "'") {
4730
+
base = 8;
4731
+
token = context.popToken();
4732
+
} else if (token.text === '"') {
4733
+
base = 16;
4734
+
token = context.popToken();
4735
+
} else if (token.text === "`") {
4736
+
token = context.popToken();
4737
+
if (token.text[0] === "\\") {
4738
+
number = token.text.charCodeAt(1);
4739
+
} else if (token.text === "EOF") {
4740
+
throw new ParseError("\\char` missing argument");
4741
+
} else {
4742
+
number = token.text.charCodeAt(0);
4743
+
}
4744
+
} else {
4745
+
base = 10;
4746
+
}
4747
+
if (base) {
4748
+
// Parse a number in the given base, starting with first `token`.
4749
+
let numStr = token.text;
4750
+
number = digitToNumber[numStr.charAt(0)];
4751
+
if (number == null || number >= base) {
4752
+
throw new ParseError(`Invalid base-${base} digit ${token.text}`);
4753
+
}
4754
+
number = appendCharNumbers(number, numStr, base);
4755
+
let digit;
4756
+
[digit, numStr] = nextCharNumber(context);
4757
+
while (digit != null && digit < base) {
4758
+
number *= base;
4759
+
number += digit;
4760
+
number = appendCharNumbers(number, numStr, base);
4761
+
context.popToken();
4762
+
[digit, numStr] = nextCharNumber(context);
4763
+
}
4764
+
}
4765
+
return `\\@char{${number}}`;
4766
+
});
4767
+
4768
+
function recreateArgStr(context) {
4769
+
// Recreate the macro's original argument string from the array of parse tokens.
4770
+
const tokens = context.consumeArgs(1)[0];
4771
+
let str = "";
4772
+
let expectedLoc = tokens[tokens.length - 1].loc.start;
4773
+
for (let i = tokens.length - 1; i >= 0; i--) {
4774
+
const actualLoc = tokens[i].loc.start;
4775
+
if (actualLoc > expectedLoc) {
4776
+
// context.consumeArgs has eaten a space.
4777
+
str += " ";
4778
+
expectedLoc = actualLoc;
4779
+
}
4780
+
str += tokens[i].text;
4781
+
expectedLoc += tokens[i].text.length;
4782
+
}
4783
+
return str
4784
+
}
4785
+
4786
+
// The Latin Modern font renders <mi>√</mi> at the wrong vertical alignment.
4787
+
// This macro provides a better rendering.
4788
+
defineMacro("\\surd", '\\sqrt{\\vphantom{|}}');
4789
+
4790
+
// See comment for \oplus in symbols.js.
4791
+
defineMacro("\u2295", "\\oplus");
4792
+
4793
+
// Since Temml has no \par, ignore \long.
4794
+
defineMacro("\\long", "");
4795
+
4796
+
//////////////////////////////////////////////////////////////////////
4797
+
// Grouping
4798
+
// \let\bgroup={ \let\egroup=}
4799
+
defineMacro("\\bgroup", "{");
4800
+
defineMacro("\\egroup", "}");
4801
+
4802
+
// Symbols from latex.ltx:
4803
+
// \def~{\nobreakspace{}}
4804
+
// \def\lq{`}
4805
+
// \def\rq{'}
4806
+
// \def \aa {\r a}
4807
+
defineMacro("~", "\\nobreakspace");
4808
+
defineMacro("\\lq", "`");
4809
+
defineMacro("\\rq", "'");
4810
+
defineMacro("\\aa", "\\r a");
4811
+
4812
+
defineMacro("\\Bbbk", "\\Bbb{k}");
4813
+
4814
+
// \mathstrut from the TeXbook, p 360
4815
+
defineMacro("\\mathstrut", "\\vphantom{(}");
4816
+
4817
+
// \underbar from TeXbook p 353
4818
+
defineMacro("\\underbar", "\\underline{\\text{#1}}");
4819
+
4820
+
//////////////////////////////////////////////////////////////////////
4821
+
// LaTeX_2ε
4822
+
4823
+
// \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@
4824
+
// \kern6\p@\hbox{.}\hbox{.}\hbox{.}}}
4825
+
// We'll call \varvdots, which gets a glyph from symbols.js.
4826
+
// The zero-width rule gets us an equivalent to the vertical 6pt kern.
4827
+
defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}");
4828
+
defineMacro("\u22ee", "\\vdots");
4829
+
4830
+
// {array} environment gaps
4831
+
defineMacro("\\arraystretch", "1"); // line spacing factor times 12pt
4832
+
defineMacro("\\arraycolsep", "6pt"); // half the width separating columns
4833
+
4834
+
//////////////////////////////////////////////////////////////////////
4835
+
// amsmath.sty
4836
+
// http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf
4837
+
4838
+
//\newcommand{\substack}[1]{\subarray{c}#1\endsubarray}
4839
+
defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}");
4840
+
4841
+
// \def\iff{\DOTSB\;\Longleftrightarrow\;}
4842
+
// \def\implies{\DOTSB\;\Longrightarrow\;}
4843
+
// \def\impliedby{\DOTSB\;\Longleftarrow\;}
4844
+
defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;");
4845
+
defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;");
4846
+
defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;");
4847
+
4848
+
// AMSMath's automatic \dots, based on \mdots@@ macro.
4849
+
const dotsByToken = {
4850
+
",": "\\dotsc",
4851
+
"\\not": "\\dotsb",
4852
+
// \keybin@ checks for the following:
4853
+
"+": "\\dotsb",
4854
+
"=": "\\dotsb",
4855
+
"<": "\\dotsb",
4856
+
">": "\\dotsb",
4857
+
"-": "\\dotsb",
4858
+
"*": "\\dotsb",
4859
+
":": "\\dotsb",
4860
+
// Symbols whose definition starts with \DOTSB:
4861
+
"\\DOTSB": "\\dotsb",
4862
+
"\\coprod": "\\dotsb",
4863
+
"\\bigvee": "\\dotsb",
4864
+
"\\bigwedge": "\\dotsb",
4865
+
"\\biguplus": "\\dotsb",
4866
+
"\\bigcap": "\\dotsb",
4867
+
"\\bigcup": "\\dotsb",
4868
+
"\\prod": "\\dotsb",
4869
+
"\\sum": "\\dotsb",
4870
+
"\\bigotimes": "\\dotsb",
4871
+
"\\bigoplus": "\\dotsb",
4872
+
"\\bigodot": "\\dotsb",
4873
+
"\\bigsqcap": "\\dotsb",
4874
+
"\\bigsqcup": "\\dotsb",
4875
+
"\\bigtimes": "\\dotsb",
4876
+
"\\And": "\\dotsb",
4877
+
"\\longrightarrow": "\\dotsb",
4878
+
"\\Longrightarrow": "\\dotsb",
4879
+
"\\longleftarrow": "\\dotsb",
4880
+
"\\Longleftarrow": "\\dotsb",
4881
+
"\\longleftrightarrow": "\\dotsb",
4882
+
"\\Longleftrightarrow": "\\dotsb",
4883
+
"\\mapsto": "\\dotsb",
4884
+
"\\longmapsto": "\\dotsb",
4885
+
"\\hookrightarrow": "\\dotsb",
4886
+
"\\doteq": "\\dotsb",
4887
+
// Symbols whose definition starts with \mathbin:
4888
+
"\\mathbin": "\\dotsb",
4889
+
// Symbols whose definition starts with \mathrel:
4890
+
"\\mathrel": "\\dotsb",
4891
+
"\\relbar": "\\dotsb",
4892
+
"\\Relbar": "\\dotsb",
4893
+
"\\xrightarrow": "\\dotsb",
4894
+
"\\xleftarrow": "\\dotsb",
4895
+
// Symbols whose definition starts with \DOTSI:
4896
+
"\\DOTSI": "\\dotsi",
4897
+
"\\int": "\\dotsi",
4898
+
"\\oint": "\\dotsi",
4899
+
"\\iint": "\\dotsi",
4900
+
"\\iiint": "\\dotsi",
4901
+
"\\iiiint": "\\dotsi",
4902
+
"\\idotsint": "\\dotsi",
4903
+
// Symbols whose definition starts with \DOTSX:
4904
+
"\\DOTSX": "\\dotsx"
4905
+
};
4906
+
4907
+
defineMacro("\\dots", function(context) {
4908
+
// TODO: If used in text mode, should expand to \textellipsis.
4909
+
// However, in Temml, \textellipsis and \ldots behave the same
4910
+
// (in text mode), and it's unlikely we'd see any of the math commands
4911
+
// that affect the behavior of \dots when in text mode. So fine for now
4912
+
// (until we support \ifmmode ... \else ... \fi).
4913
+
let thedots = "\\dotso";
4914
+
const next = context.expandAfterFuture().text;
4915
+
if (next in dotsByToken) {
4916
+
thedots = dotsByToken[next];
4917
+
} else if (next.slice(0, 4) === "\\not") {
4918
+
thedots = "\\dotsb";
4919
+
} else if (next in symbols.math) {
4920
+
if (["bin", "rel"].includes(symbols.math[next].group)) {
4921
+
thedots = "\\dotsb";
4922
+
}
4923
+
}
4924
+
return thedots;
4925
+
});
4926
+
4927
+
const spaceAfterDots = {
4928
+
// \rightdelim@ checks for the following:
4929
+
")": true,
4930
+
"]": true,
4931
+
"\\rbrack": true,
4932
+
"\\}": true,
4933
+
"\\rbrace": true,
4934
+
"\\rangle": true,
4935
+
"\\rceil": true,
4936
+
"\\rfloor": true,
4937
+
"\\rgroup": true,
4938
+
"\\rmoustache": true,
4939
+
"\\right": true,
4940
+
"\\bigr": true,
4941
+
"\\biggr": true,
4942
+
"\\Bigr": true,
4943
+
"\\Biggr": true,
4944
+
// \extra@ also tests for the following:
4945
+
$: true,
4946
+
// \extrap@ checks for the following:
4947
+
";": true,
4948
+
".": true,
4949
+
",": true
4950
+
};
4951
+
4952
+
defineMacro("\\dotso", function(context) {
4953
+
const next = context.future().text;
4954
+
if (next in spaceAfterDots) {
4955
+
return "\\ldots\\,";
4956
+
} else {
4957
+
return "\\ldots";
4958
+
}
4959
+
});
4960
+
4961
+
defineMacro("\\dotsc", function(context) {
4962
+
const next = context.future().text;
4963
+
// \dotsc uses \extra@ but not \extrap@, instead specially checking for
4964
+
// ';' and '.', but doesn't check for ','.
4965
+
if (next in spaceAfterDots && next !== ",") {
4966
+
return "\\ldots\\,";
4967
+
} else {
4968
+
return "\\ldots";
4969
+
}
4970
+
});
4971
+
4972
+
defineMacro("\\cdots", function(context) {
4973
+
const next = context.future().text;
4974
+
if (next in spaceAfterDots) {
4975
+
return "\\@cdots\\,";
4976
+
} else {
4977
+
return "\\@cdots";
4978
+
}
4979
+
});
4980
+
4981
+
defineMacro("\\dotsb", "\\cdots");
4982
+
defineMacro("\\dotsm", "\\cdots");
4983
+
defineMacro("\\dotsi", "\\!\\cdots");
4984
+
defineMacro("\\idotsint", "\\dotsi");
4985
+
// amsmath doesn't actually define \dotsx, but \dots followed by a macro
4986
+
// starting with \DOTSX implies \dotso, and then \extra@ detects this case
4987
+
// and forces the added `\,`.
4988
+
defineMacro("\\dotsx", "\\ldots\\,");
4989
+
4990
+
// \let\DOTSI\relax
4991
+
// \let\DOTSB\relax
4992
+
// \let\DOTSX\relax
4993
+
defineMacro("\\DOTSI", "\\relax");
4994
+
defineMacro("\\DOTSB", "\\relax");
4995
+
defineMacro("\\DOTSX", "\\relax");
4996
+
4997
+
// Spacing, based on amsmath.sty's override of LaTeX defaults
4998
+
// \DeclareRobustCommand{\tmspace}[3]{%
4999
+
// \ifmmode\mskip#1#2\else\kern#1#3\fi\relax}
5000
+
defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax");
5001
+
// \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}}
5002
+
// TODO: math mode should use \thinmuskip
5003
+
defineMacro("\\,", "{\\tmspace+{3mu}{.1667em}}");
5004
+
// \let\thinspace\,
5005
+
defineMacro("\\thinspace", "\\,");
5006
+
// \def\>{\mskip\medmuskip}
5007
+
// \renewcommand{\:}{\tmspace+\medmuskip{.2222em}}
5008
+
// TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu
5009
+
defineMacro("\\>", "\\mskip{4mu}");
5010
+
defineMacro("\\:", "{\\tmspace+{4mu}{.2222em}}");
5011
+
// \let\medspace\:
5012
+
defineMacro("\\medspace", "\\:");
5013
+
// \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}}
5014
+
// TODO: math mode should use \thickmuskip = 5mu plus 5mu
5015
+
defineMacro("\\;", "{\\tmspace+{5mu}{.2777em}}");
5016
+
// \let\thickspace\;
5017
+
defineMacro("\\thickspace", "\\;");
5018
+
// \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}}
5019
+
// TODO: math mode should use \thinmuskip
5020
+
defineMacro("\\!", "{\\tmspace-{3mu}{.1667em}}");
5021
+
// \let\negthinspace\!
5022
+
defineMacro("\\negthinspace", "\\!");
5023
+
// \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}}
5024
+
// TODO: math mode should use \medmuskip
5025
+
defineMacro("\\negmedspace", "{\\tmspace-{4mu}{.2222em}}");
5026
+
// \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}}
5027
+
// TODO: math mode should use \thickmuskip
5028
+
defineMacro("\\negthickspace", "{\\tmspace-{5mu}{.277em}}");
5029
+
// \def\enspace{\kern.5em }
5030
+
defineMacro("\\enspace", "\\kern.5em ");
5031
+
// \def\enskip{\hskip.5em\relax}
5032
+
defineMacro("\\enskip", "\\hskip.5em\\relax");
5033
+
// \def\quad{\hskip1em\relax}
5034
+
defineMacro("\\quad", "\\hskip1em\\relax");
5035
+
// \def\qquad{\hskip2em\relax}
5036
+
defineMacro("\\qquad", "\\hskip2em\\relax");
5037
+
5038
+
defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax");
5039
+
5040
+
// \tag@in@display form of \tag
5041
+
defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren");
5042
+
defineMacro("\\tag@paren", "\\tag@literal{({#1})}");
5043
+
defineMacro("\\tag@literal", (context) => {
5044
+
if (context.macros.get("\\df@tag")) {
5045
+
throw new ParseError("Multiple \\tag");
5046
+
}
5047
+
return "\\gdef\\df@tag{\\text{#1}}";
5048
+
});
5049
+
defineMacro("\\notag", "\\nonumber");
5050
+
defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}");
5051
+
5052
+
// \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin
5053
+
// {\operator@font mod}\penalty900
5054
+
// \mkern5mu\nonscript\mskip-\medmuskip}
5055
+
// \newcommand{\pod}[1]{\allowbreak
5056
+
// \if@display\mkern18mu\else\mkern8mu\fi(#1)}
5057
+
// \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}}
5058
+
// \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu
5059
+
// \else\mkern12mu\fi{\operator@font mod}\,\,#1}
5060
+
// TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu
5061
+
defineMacro("\\bmod", "\\mathbin{\\text{mod}}");
5062
+
defineMacro(
5063
+
"\\pod",
5064
+
"\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"
5065
+
);
5066
+
defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}");
5067
+
defineMacro(
5068
+
"\\mod",
5069
+
"\\allowbreak" +
5070
+
"\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" +
5071
+
"{\\rm mod}\\,\\,#1"
5072
+
);
5073
+
5074
+
//////////////////////////////////////////////////////////////////////
5075
+
// LaTeX source2e
5076
+
5077
+
// \expandafter\let\expandafter\@normalcr
5078
+
// \csname\expandafter\@gobble\string\\ \endcsname
5079
+
// \DeclareRobustCommand\newline{\@normalcr\relax}
5080
+
defineMacro("\\newline", "\\\\\\relax");
5081
+
5082
+
// \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
5083
+
// TODO: Doesn't normally work in math mode because \@ fails.
5084
+
defineMacro("\\TeX", "\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}");
5085
+
5086
+
defineMacro(
5087
+
"\\LaTeX",
5088
+
"\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX"
5089
+
);
5090
+
5091
+
defineMacro(
5092
+
"\\Temml",
5093
+
// eslint-disable-next-line max-len
5094
+
"\\textrm{T}\\kern-0.2em\\lower{0.2em}{\\textrm{E}}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}"
5095
+
);
5096
+
5097
+
// \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace}
5098
+
// \def\@hspace#1{\hskip #1\relax}
5099
+
// \def\@hspacer#1{\vrule \@width\z@\nobreak
5100
+
// \hskip #1\hskip \z@skip}
5101
+
defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace");
5102
+
defineMacro("\\@hspace", "\\hskip #1\\relax");
5103
+
defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax");
5104
+
5105
+
defineMacro("\\colon", `\\mathpunct{\\char"3a}`);
5106
+
5107
+
//////////////////////////////////////////////////////////////////////
5108
+
// mathtools.sty
5109
+
5110
+
defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}");
5111
+
5112
+
//\providecommand\ordinarycolon{:}
5113
+
defineMacro("\\ordinarycolon", `\\char"3a`);
5114
+
// Raise to center on the math axis, as closely as possible.
5115
+
defineMacro("\\vcentcolon", "\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}");
5116
+
// \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}}
5117
+
defineMacro("\\coloneq", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}');
5118
+
// \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}}
5119
+
defineMacro("\\Coloneq", '\\mathrel{\\char"2237\\char"2212}');
5120
+
// \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon}
5121
+
defineMacro("\\Eqqcolon", '\\mathrel{\\char"3d\\char"2237}');
5122
+
// \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon}
5123
+
defineMacro("\\Eqcolon", '\\mathrel{\\char"2212\\char"2237}');
5124
+
// \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx}
5125
+
defineMacro("\\colonapprox", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}');
5126
+
// \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx}
5127
+
defineMacro("\\Colonapprox", '\\mathrel{\\char"2237\\char"2248}');
5128
+
// \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim}
5129
+
defineMacro("\\colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}');
5130
+
// \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim}
5131
+
defineMacro("\\Colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}');
5132
+
5133
+
//////////////////////////////////////////////////////////////////////
5134
+
// colonequals.sty
5135
+
5136
+
// Alternate names for mathtools's macros:
5137
+
defineMacro("\\ratio", "\\vcentcolon");
5138
+
defineMacro("\\coloncolon", "\\dblcolon");
5139
+
defineMacro("\\colonequals", "\\coloneqq");
5140
+
defineMacro("\\coloncolonequals", "\\Coloneqq");
5141
+
defineMacro("\\equalscolon", "\\eqqcolon");
5142
+
defineMacro("\\equalscoloncolon", "\\Eqqcolon");
5143
+
defineMacro("\\colonminus", "\\coloneq");
5144
+
defineMacro("\\coloncolonminus", "\\Coloneq");
5145
+
defineMacro("\\minuscolon", "\\eqcolon");
5146
+
defineMacro("\\minuscoloncolon", "\\Eqcolon");
5147
+
// \colonapprox name is same in mathtools and colonequals.
5148
+
defineMacro("\\coloncolonapprox", "\\Colonapprox");
5149
+
// \colonsim name is same in mathtools and colonequals.
5150
+
defineMacro("\\coloncolonsim", "\\Colonsim");
5151
+
5152
+
// Present in newtxmath, pxfonts and txfonts
5153
+
defineMacro("\\notni", "\\mathrel{\\char`\u220C}");
5154
+
defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}");
5155
+
defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}");
5156
+
5157
+
//////////////////////////////////////////////////////////////////////
5158
+
// From amsopn.sty
5159
+
defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}");
5160
+
defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}");
5161
+
defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\text{lim}}}");
5162
+
defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\text{lim}}}");
5163
+
defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}");
5164
+
defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}");
5165
+
5166
+
defineMacro("\\centerdot", "{\\medspace\\rule{0.167em}{0.189em}\\medspace}");
5167
+
5168
+
//////////////////////////////////////////////////////////////////////
5169
+
// statmath.sty
5170
+
// https://ctan.math.illinois.edu/macros/latex/contrib/statmath/statmath.pdf
5171
+
5172
+
defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}");
5173
+
defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}");
5174
+
defineMacro("\\plim", "\\DOTSB\\operatorname*{plim}");
5175
+
5176
+
//////////////////////////////////////////////////////////////////////
5177
+
// MnSymbol.sty
5178
+
5179
+
defineMacro("\\leftmodels", "\\mathop{\\reflectbox{$\\models$}}");
5180
+
5181
+
//////////////////////////////////////////////////////////////////////
5182
+
// braket.sty
5183
+
// http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf
5184
+
5185
+
defineMacro("\\bra", "\\mathinner{\\langle{#1}|}");
5186
+
defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}");
5187
+
defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}");
5188
+
defineMacro("\\Bra", "\\left\\langle#1\\right|");
5189
+
defineMacro("\\Ket", "\\left|#1\\right\\rangle");
5190
+
// A helper for \Braket and \Set
5191
+
const replaceVert = (argStr, match) => {
5192
+
const ch = match[0] === "|" ? "\\vert" : "\\Vert";
5193
+
const replaceStr = `}\\,\\middle${ch}\\,{`;
5194
+
return argStr.slice(0, match.index) + replaceStr + argStr.slice(match.index + match[0].length)
5195
+
};
5196
+
defineMacro("\\Braket", function(context) {
5197
+
let argStr = recreateArgStr(context);
5198
+
const regEx = /\|\||\||\\\|/g;
5199
+
let match;
5200
+
while ((match = regEx.exec(argStr)) !== null) {
5201
+
argStr = replaceVert(argStr, match);
5202
+
}
5203
+
return "\\left\\langle{" + argStr + "}\\right\\rangle"
5204
+
});
5205
+
defineMacro("\\Set", function(context) {
5206
+
let argStr = recreateArgStr(context);
5207
+
const match = /\|\||\||\\\|/.exec(argStr);
5208
+
if (match) {
5209
+
argStr = replaceVert(argStr, match);
5210
+
}
5211
+
return "\\left\\{\\:{" + argStr + "}\\:\\right\\}"
5212
+
});
5213
+
defineMacro("\\set", function(context) {
5214
+
const argStr = recreateArgStr(context);
5215
+
return "\\{{" + argStr.replace(/\|/, "}\\mid{") + "}\\}"
5216
+
});
5217
+
5218
+
//////////////////////////////////////////////////////////////////////
5219
+
// actuarialangle.dtx
5220
+
defineMacro("\\angln", "{\\angl n}");
5221
+
5222
+
//////////////////////////////////////////////////////////////////////
5223
+
// derivative.sty
5224
+
defineMacro("\\odv", "\\@ifstar\\odv@next\\odv@numerator");
5225
+
defineMacro("\\odv@numerator", "\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}");
5226
+
defineMacro("\\odv@next", "\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1");
5227
+
defineMacro("\\pdv", "\\@ifstar\\pdv@next\\pdv@numerator");
5228
+
5229
+
const pdvHelper = args => {
5230
+
const numerator = args[0][0].text;
5231
+
const denoms = stringFromArg(args[1]).split(",");
5232
+
const power = String(denoms.length);
5233
+
const numOp = power === "1" ? "\\partial" : `\\partial^${power}`;
5234
+
let denominator = "";
5235
+
denoms.map(e => { denominator += "\\partial " + e.trim() + "\\,";});
5236
+
return [numerator, numOp, denominator.replace(/\\,$/, "")]
5237
+
};
5238
+
defineMacro("\\pdv@numerator", function(context) {
5239
+
const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2));
5240
+
return `\\frac{${numOp} ${numerator}}{${denominator}}`
5241
+
});
5242
+
defineMacro("\\pdv@next", function(context) {
5243
+
const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2));
5244
+
return `\\frac{${numOp}}{${denominator}} ${numerator}`
5245
+
});
5246
+
5247
+
//////////////////////////////////////////////////////////////////////
5248
+
// upgreek.dtx
5249
+
defineMacro("\\upalpha", "\\up@greek{\\alpha}");
5250
+
defineMacro("\\upbeta", "\\up@greek{\\beta}");
5251
+
defineMacro("\\upgamma", "\\up@greek{\\gamma}");
5252
+
defineMacro("\\updelta", "\\up@greek{\\delta}");
5253
+
defineMacro("\\upepsilon", "\\up@greek{\\epsilon}");
5254
+
defineMacro("\\upzeta", "\\up@greek{\\zeta}");
5255
+
defineMacro("\\upeta", "\\up@greek{\\eta}");
5256
+
defineMacro("\\uptheta", "\\up@greek{\\theta}");
5257
+
defineMacro("\\upiota", "\\up@greek{\\iota}");
5258
+
defineMacro("\\upkappa", "\\up@greek{\\kappa}");
5259
+
defineMacro("\\uplambda", "\\up@greek{\\lambda}");
5260
+
defineMacro("\\upmu", "\\up@greek{\\mu}");
5261
+
defineMacro("\\upnu", "\\up@greek{\\nu}");
5262
+
defineMacro("\\upxi", "\\up@greek{\\xi}");
5263
+
defineMacro("\\upomicron", "\\up@greek{\\omicron}");
5264
+
defineMacro("\\uppi", "\\up@greek{\\pi}");
5265
+
defineMacro("\\upalpha", "\\up@greek{\\alpha}");
5266
+
defineMacro("\\uprho", "\\up@greek{\\rho}");
5267
+
defineMacro("\\upsigma", "\\up@greek{\\sigma}");
5268
+
defineMacro("\\uptau", "\\up@greek{\\tau}");
5269
+
defineMacro("\\upupsilon", "\\up@greek{\\upsilon}");
5270
+
defineMacro("\\upphi", "\\up@greek{\\phi}");
5271
+
defineMacro("\\upchi", "\\up@greek{\\chi}");
5272
+
defineMacro("\\uppsi", "\\up@greek{\\psi}");
5273
+
defineMacro("\\upomega", "\\up@greek{\\omega}");
5274
+
5275
+
//////////////////////////////////////////////////////////////////////
5276
+
// cmll package
5277
+
defineMacro("\\invamp", '\\mathbin{\\char"214b}');
5278
+
defineMacro("\\parr", '\\mathbin{\\char"214b}');
5279
+
defineMacro("\\with", '\\mathbin{\\char"26}');
5280
+
defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}');
5281
+
defineMacro("\\multimapboth", '\\mathrel{\\char"29df}');
5282
+
defineMacro("\\scoh", '{\\mkern5mu\\char"2322\\mkern5mu}');
5283
+
defineMacro("\\sincoh", '{\\mkern5mu\\char"2323\\mkern5mu}');
5284
+
defineMacro("\\coh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}}
5285
+
{\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}`);
5286
+
defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}}
5287
+
{\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}`);
5288
+
5289
+
5290
+
//////////////////////////////////////////////////////////////////////
5291
+
// chemstyle package
5292
+
defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}");
5293
+
5294
+
/* eslint-disable */
5295
+
/* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
5296
+
/* vim: set ts=2 et sw=2 tw=80: */
5297
+
5298
+
/*************************************************************
5299
+
*
5300
+
* Temml mhchem.js
5301
+
*
5302
+
* This file implements a Temml version of mhchem version 3.3.0.
5303
+
* It is adapted from MathJax/extensions/TeX/mhchem.js
5304
+
* It differs from the MathJax version as follows:
5305
+
* 1. The interface is changed so that it can be called from Temml, not MathJax.
5306
+
* 2. \rlap and \llap are replaced with \mathrlap and \mathllap.
5307
+
* 3. The reaction arrow code is simplified. All reaction arrows are rendered
5308
+
* using Temml extensible arrows instead of building non-extensible arrows.
5309
+
* 4. The ~bond forms are composed entirely of \rule elements.
5310
+
* 5. Two dashes in _getBond are wrapped in braces to suppress spacing. i.e., {-}
5311
+
* 6. The electron dot uses \textbullet instead of \bullet.
5312
+
* 7. \smash[T] has been removed. (WebKit hides anything inside \smash{…})
5313
+
*
5314
+
* This code, as other Temml code, is released under the MIT license.
5315
+
*
5316
+
* /*************************************************************
5317
+
*
5318
+
* MathJax/extensions/TeX/mhchem.js
5319
+
*
5320
+
* Implements the \ce command for handling chemical formulas
5321
+
* from the mhchem LaTeX package.
5322
+
*
5323
+
* ---------------------------------------------------------------------
5324
+
*
5325
+
* Copyright (c) 2011-2015 The MathJax Consortium
5326
+
* Copyright (c) 2015-2018 Martin Hensel
5327
+
*
5328
+
* Licensed under the Apache License, Version 2.0 (the "License");
5329
+
* you may not use this file except in compliance with the License.
5330
+
* You may obtain a copy of the License at
5331
+
*
5332
+
* http://www.apache.org/licenses/LICENSE-2.0
5333
+
*
5334
+
* Unless required by applicable law or agreed to in writing, software
5335
+
* distributed under the License is distributed on an "AS IS" BASIS,
5336
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5337
+
* See the License for the specific language governing permissions and
5338
+
* limitations under the License.
5339
+
*/
5340
+
5341
+
//
5342
+
// Coding Style
5343
+
// - use '' for identifiers that can by minified/uglified
5344
+
// - use "" for strings that need to stay untouched
5345
+
5346
+
// version: "3.3.0" for MathJax and Temml
5347
+
5348
+
5349
+
// Add \ce, \pu, and \tripleDash to the Temml macros.
5350
+
5351
+
defineMacro("\\ce", function(context) {
5352
+
return chemParse(context.consumeArgs(1)[0], "ce")
5353
+
});
5354
+
5355
+
defineMacro("\\pu", function(context) {
5356
+
return chemParse(context.consumeArgs(1)[0], "pu");
5357
+
});
5358
+
5359
+
// Math fonts do not include glyphs for the ~ form of bonds. So we'll send path geometry
5360
+
// So we'll compose characters built from \rule elements.
5361
+
defineMacro("\\uniDash", `{\\rule{0.672em}{0.06em}}`)
5362
+
defineMacro("\\triDash", `{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}`)
5363
+
defineMacro("\\tripleDash", `\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em`)
5364
+
defineMacro("\\tripleDashOverLine", `\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em`)
5365
+
defineMacro("\\tripleDashOverDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`)
5366
+
defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\uniDash}}\\raise0.27em{\\triDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`)
5367
+
5368
+
//
5369
+
// This is the main function for handing the \ce and \pu commands.
5370
+
// It takes the argument to \ce or \pu and returns the corresponding TeX string.
5371
+
//
5372
+
5373
+
var chemParse = function (tokens, stateMachine) {
5374
+
// Recreate the argument string from Temml's array of tokens.
5375
+
var str = "";
5376
+
var expectedLoc = tokens.length && tokens[tokens.length - 1].loc.start
5377
+
for (var i = tokens.length - 1; i >= 0; i--) {
5378
+
if(tokens[i].loc.start > expectedLoc) {
5379
+
// context.consumeArgs has eaten a space.
5380
+
str += " ";
5381
+
expectedLoc = tokens[i].loc.start;
5382
+
}
5383
+
str += tokens[i].text;
5384
+
expectedLoc += tokens[i].text.length;
5385
+
}
5386
+
// Call the mhchem core parser.
5387
+
var tex = texify.go(mhchemParser.go(str, stateMachine));
5388
+
return tex;
5389
+
};
5390
+
5391
+
//
5392
+
// Core parser for mhchem syntax (recursive)
5393
+
//
5394
+
/** @type {MhchemParser} */
5395
+
var mhchemParser = {
5396
+
//
5397
+
// Parses mchem \ce syntax
5398
+
//
5399
+
// Call like
5400
+
// go("H2O");
5401
+
//
5402
+
go: function (input, stateMachine) {
5403
+
if (!input) { return []; }
5404
+
if (stateMachine === undefined) { stateMachine = 'ce'; }
5405
+
var state = '0';
5406
+
5407
+
//
5408
+
// String buffers for parsing:
5409
+
//
5410
+
// buffer.a == amount
5411
+
// buffer.o == element
5412
+
// buffer.b == left-side superscript
5413
+
// buffer.p == left-side subscript
5414
+
// buffer.q == right-side subscript
5415
+
// buffer.d == right-side superscript
5416
+
//
5417
+
// buffer.r == arrow
5418
+
// buffer.rdt == arrow, script above, type
5419
+
// buffer.rd == arrow, script above, content
5420
+
// buffer.rqt == arrow, script below, type
5421
+
// buffer.rq == arrow, script below, content
5422
+
//
5423
+
// buffer.text_
5424
+
// buffer.rm
5425
+
// etc.
5426
+
//
5427
+
// buffer.parenthesisLevel == int, starting at 0
5428
+
// buffer.sb == bool, space before
5429
+
// buffer.beginsWithBond == bool
5430
+
//
5431
+
// These letters are also used as state names.
5432
+
//
5433
+
// Other states:
5434
+
// 0 == begin of main part (arrow/operator unlikely)
5435
+
// 1 == next entity
5436
+
// 2 == next entity (arrow/operator unlikely)
5437
+
// 3 == next atom
5438
+
// c == macro
5439
+
//
5440
+
/** @type {Buffer} */
5441
+
var buffer = {};
5442
+
buffer['parenthesisLevel'] = 0;
5443
+
5444
+
input = input.replace(/\n/g, " ");
5445
+
input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-");
5446
+
input = input.replace(/[\u2026]/g, "...");
5447
+
5448
+
//
5449
+
// Looks through mhchemParser.transitions, to execute a matching action
5450
+
// (recursive)
5451
+
//
5452
+
var lastInput;
5453
+
var watchdog = 10;
5454
+
/** @type {ParserOutput[]} */
5455
+
var output = [];
5456
+
while (true) {
5457
+
if (lastInput !== input) {
5458
+
watchdog = 10;
5459
+
lastInput = input;
5460
+
} else {
5461
+
watchdog--;
5462
+
}
5463
+
//
5464
+
// Find actions in transition table
5465
+
//
5466
+
var machine = mhchemParser.stateMachines[stateMachine];
5467
+
var t = machine.transitions[state] || machine.transitions['*'];
5468
+
iterateTransitions:
5469
+
for (var i=0; i<t.length; i++) {
5470
+
var matches = mhchemParser.patterns.match_(t[i].pattern, input);
5471
+
if (matches) {
5472
+
//
5473
+
// Execute actions
5474
+
//
5475
+
var task = t[i].task;
5476
+
for (var iA=0; iA<task.action_.length; iA++) {
5477
+
var o;
5478
+
//
5479
+
// Find and execute action
5480
+
//
5481
+
if (machine.actions[task.action_[iA].type_]) {
5482
+
o = machine.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option);
5483
+
} else if (mhchemParser.actions[task.action_[iA].type_]) {
5484
+
o = mhchemParser.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option);
5485
+
} else {
5486
+
throw ["MhchemBugA", "mhchem bug A. Please report. (" + task.action_[iA].type_ + ")"]; // Trying to use non-existing action
5487
+
}
5488
+
//
5489
+
// Add output
5490
+
//
5491
+
mhchemParser.concatArray(output, o);
5492
+
}
5493
+
//
5494
+
// Set next state,
5495
+
// Shorten input,
5496
+
// Continue with next character
5497
+
// (= apply only one transition per position)
5498
+
//
5499
+
state = task.nextState || state;
5500
+
if (input.length > 0) {
5501
+
if (!task.revisit) {
5502
+
input = matches.remainder;
5503
+
}
5504
+
if (!task.toContinue) {
5505
+
break iterateTransitions;
5506
+
}
5507
+
} else {
5508
+
return output;
5509
+
}
5510
+
}
5511
+
}
5512
+
//
5513
+
// Prevent infinite loop
5514
+
//
5515
+
if (watchdog <= 0) {
5516
+
throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character
5517
+
}
5518
+
}
5519
+
},
5520
+
concatArray: function (a, b) {
5521
+
if (b) {
5522
+
if (Array.isArray(b)) {
5523
+
for (var iB=0; iB<b.length; iB++) {
5524
+
a.push(b[iB]);
5525
+
}
5526
+
} else {
5527
+
a.push(b);
5528
+
}
5529
+
}
5530
+
},
5531
+
5532
+
patterns: {
5533
+
//
5534
+
// Matching patterns
5535
+
// either regexps or function that return null or {match_:"a", remainder:"bc"}
5536
+
//
5537
+
patterns: {
5538
+
// property names must not look like integers ("2") for correct property traversal order, later on
5539
+
'empty': /^$/,
5540
+
'else': /^./,
5541
+
'else2': /^./,
5542
+
'space': /^\s/,
5543
+
'space A': /^\s(?=[A-Z\\$])/,
5544
+
'space$': /^\s$/,
5545
+
'a-z': /^[a-z]/,
5546
+
'x': /^x/,
5547
+
'x$': /^x$/,
5548
+
'i$': /^i$/,
5549
+
'letters': /^(?:[a-zA-Z\u03B1-\u03C9\u0391-\u03A9?@]|(?:\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/,
5550
+
'\\greek': /^\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))/,
5551
+
'one lowercase latin letter $': /^(?:([a-z])(?:$|[^a-zA-Z]))$/,
5552
+
'$one lowercase latin letter$ $': /^\$(?:([a-z])(?:$|[^a-zA-Z]))\$$/,
5553
+
'one lowercase greek letter $': /^(?:\$?[\u03B1-\u03C9]\$?|\$?\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\s*\$?)(?:\s+|\{\}|(?![a-zA-Z]))$/,
5554
+
'digits': /^[0-9]+/,
5555
+
'-9.,9': /^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/,
5556
+
'-9.,9 no missing 0': /^[+\-]?[0-9]+(?:[.,][0-9]+)?/,
5557
+
'(-)(9.,9)(e)(99)': function (input) {
5558
+
var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))?(\((?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))\))?(?:([eE]|\s*(\*|x|\\times|\u00D7)\s*10\^)([+\-]?[0-9]+|\{[+\-]?[0-9]+\}))?/);
5559
+
if (m && m[0]) {
5560
+
return { match_: m.splice(1), remainder: input.substr(m[0].length) };
5561
+
}
5562
+
return null;
5563
+
},
5564
+
'(-)(9)^(-9)': function (input) {
5565
+
var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/);
5566
+
if (m && m[0]) {
5567
+
return { match_: m.splice(1), remainder: input.substr(m[0].length) };
5568
+
}
5569
+
return null;
5570
+
},
5571
+
'state of aggregation $': function (input) { // ... or crystal system
5572
+
var a = mhchemParser.patterns.findObserveGroups(input, "", /^\([a-z]{1,3}(?=[\),])/, ")", ""); // (aq), (aq,$\infty$), (aq, sat)
5573
+
if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) { return a; } // AND end of 'phrase'
5574
+
var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/); // OR crystal system ($o$) (\ca$c$)
5575
+
if (m) {
5576
+
return { match_: m[0], remainder: input.substr(m[0].length) };
5577
+
}
5578
+
return null;
5579
+
},
5580
+
'_{(state of aggregation)}$': /^_\{(\([a-z]{1,3}\))\}/,
5581
+
'{[(': /^(?:\\\{|\[|\()/,
5582
+
')]}': /^(?:\)|\]|\\\})/,
5583
+
', ': /^[,;]\s*/,
5584
+
',': /^[,;]/,
5585
+
'.': /^[.]/,
5586
+
'. ': /^([.\u22C5\u00B7\u2022])\s*/,
5587
+
'...': /^\.\.\.(?=$|[^.])/,
5588
+
'* ': /^([*])\s*/,
5589
+
'^{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^{", "", "", "}"); },
5590
+
'^($...$)': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^", "$", "$", ""); },
5591
+
'^a': /^\^([0-9]+|[^\\_])/,
5592
+
'^\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); },
5593
+
'^\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^", /^\\[a-zA-Z]+\{/, "}", ""); },
5594
+
'^\\x': /^\^(\\[a-zA-Z]+)\s*/,
5595
+
'^(-1)': /^\^(-?\d+)/,
5596
+
'\'': /^'/,
5597
+
'_{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_{", "", "", "}"); },
5598
+
'_($...$)': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_", "$", "$", ""); },
5599
+
'_9': /^_([+\-]?[0-9]+|[^\\])/,
5600
+
'_\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); },
5601
+
'_\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_", /^\\[a-zA-Z]+\{/, "}", ""); },
5602
+
'_\\x': /^_(\\[a-zA-Z]+)\s*/,
5603
+
'^_': /^(?:\^(?=_)|\_(?=\^)|[\^_]$)/,
5604
+
'{}': /^\{\}/,
5605
+
'{...}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", "{", "}", ""); },
5606
+
'{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "{", "", "", "}"); },
5607
+
'$...$': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", "$", "$", ""); },
5608
+
'${(...)}$': function (input) { return mhchemParser.patterns.findObserveGroups(input, "${", "", "", "}$"); },
5609
+
'$(...)$': function (input) { return mhchemParser.patterns.findObserveGroups(input, "$", "", "", "$"); },
5610
+
'=<>': /^[=<>]/,
5611
+
'#': /^[#\u2261]/,
5612
+
'+': /^\+/,
5613
+
'-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, // -space -, -; -] -/ -$ -state-of-aggregation
5614
+
'-9': /^-(?=[0-9])/,
5615
+
'- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,
5616
+
'-': /^-/,
5617
+
'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,
5618
+
'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,
5619
+
'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,
5620
+
'\\bond{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\bond{", "", "", "}"); },
5621
+
'->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,
5622
+
'CMT': /^[CMT](?=\[)/,
5623
+
'[(...)]': function (input) { return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]"); },
5624
+
'1st-level escape': /^(&|\\\\|\\hline)\s*/,
5625
+
'\\,': /^(?:\\[,\ ;:])/, // \\x - but output no space before
5626
+
'\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); },
5627
+
'\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", ""); },
5628
+
'\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/,
5629
+
'\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/,
5630
+
'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, // only those with numbers in front, because the others will be formatted correctly anyway
5631
+
'others': /^[\/~|]/,
5632
+
'\\frac{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\frac{", "", "", "}", "{", "", "", "}"); },
5633
+
'\\overset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\overset{", "", "", "}", "{", "", "", "}"); },
5634
+
'\\underset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underset{", "", "", "}", "{", "", "", "}"); },
5635
+
'\\underbrace{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underbrace{", "", "", "}_", "{", "", "", "}"); },
5636
+
'\\color{(...)}0': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}"); },
5637
+
'\\color{(...)}{(...)}1': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}", "{", "", "", "}"); },
5638
+
'\\color(...){(...)}2': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}"); },
5639
+
'\\ce{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\ce{", "", "", "}"); },
5640
+
'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,
5641
+
'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, // 0 could be oxidation or charge
5642
+
'roman numeral': /^[IVX]+/,
5643
+
'1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,
5644
+
'amount': function (input) {
5645
+
var match;
5646
+
// e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing
5647
+
match = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/);
5648
+
if (match) {
5649
+
return { match_: match[0], remainder: input.substr(match[0].length) };
5650
+
}
5651
+
var a = mhchemParser.patterns.findObserveGroups(input, "", "$", "$", "");
5652
+
if (a) { // e.g. $2n-1$, $-$
5653
+
match = a.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/);
5654
+
if (match) {
5655
+
return { match_: match[0], remainder: input.substr(match[0].length) };
5656
+
}
5657
+
}
5658
+
return null;
5659
+
},
5660
+
'amount2': function (input) { return this['amount'](input); },
5661
+
'(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/,
5662
+
'formula$': function (input) {
5663
+
if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula
5664
+
var match = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/);
5665
+
if (match) {
5666
+
return { match_: match[0], remainder: input.substr(match[0].length) };
5667
+
}
5668
+
return null;
5669
+
},
5670
+
'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,
5671
+
'/': /^\s*(\/)\s*/,
5672
+
'//': /^\s*(\/\/)\s*/,
5673
+
'*': /^\s*[*.]\s*/
5674
+
},
5675
+
findObserveGroups: function (input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) {
5676
+
/** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */
5677
+
var _match = function (input, pattern) {
5678
+
if (typeof pattern === "string") {
5679
+
if (input.indexOf(pattern) !== 0) { return null; }
5680
+
return pattern;
5681
+
} else {
5682
+
var match = input.match(pattern);
5683
+
if (!match) { return null; }
5684
+
return match[0];
5685
+
}
5686
+
};
5687
+
/** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */
5688
+
var _findObserveGroups = function (input, i, endChars) {
5689
+
var braces = 0;
5690
+
while (i < input.length) {
5691
+
var a = input.charAt(i);
5692
+
var match = _match(input.substr(i), endChars);
5693
+
if (match !== null && braces === 0) {
5694
+
return { endMatchBegin: i, endMatchEnd: i + match.length };
5695
+
} else if (a === "{") {
5696
+
braces++;
5697
+
} else if (a === "}") {
5698
+
if (braces === 0) {
5699
+
throw ["ExtraCloseMissingOpen", "Extra close brace or missing open brace"];
5700
+
} else {
5701
+
braces--;
5702
+
}
5703
+
}
5704
+
i++;
5705
+
}
5706
+
if (braces > 0) {
5707
+
return null;
5708
+
}
5709
+
return null;
5710
+
};
5711
+
var match = _match(input, begExcl);
5712
+
if (match === null) { return null; }
5713
+
input = input.substr(match.length);
5714
+
match = _match(input, begIncl);
5715
+
if (match === null) { return null; }
5716
+
var e = _findObserveGroups(input, match.length, endIncl || endExcl);
5717
+
if (e === null) { return null; }
5718
+
var match1 = input.substring(0, (endIncl ? e.endMatchEnd : e.endMatchBegin));
5719
+
if (!(beg2Excl || beg2Incl)) {
5720
+
return {
5721
+
match_: match1,
5722
+
remainder: input.substr(e.endMatchEnd)
5723
+
};
5724
+
} else {
5725
+
var group2 = this.findObserveGroups(input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl);
5726
+
if (group2 === null) { return null; }
5727
+
/** @type {string[]} */
5728
+
var matchRet = [match1, group2.match_];
5729
+
return {
5730
+
match_: (combine ? matchRet.join("") : matchRet),
5731
+
remainder: group2.remainder
5732
+
};
5733
+
}
5734
+
},
5735
+
5736
+
//
5737
+
// Matching function
5738
+
// e.g. match("a", input) will look for the regexp called "a" and see if it matches
5739
+
// returns null or {match_:"a", remainder:"bc"}
5740
+
//
5741
+
match_: function (m, input) {
5742
+
var pattern = mhchemParser.patterns.patterns[m];
5743
+
if (pattern === undefined) {
5744
+
throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern
5745
+
} else if (typeof pattern === "function") {
5746
+
return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser
5747
+
} else { // RegExp
5748
+
var match = input.match(pattern);
5749
+
if (match) {
5750
+
var mm;
5751
+
if (match[2]) {
5752
+
mm = [ match[1], match[2] ];
5753
+
} else if (match[1]) {
5754
+
mm = match[1];
5755
+
} else {
5756
+
mm = match[0];
5757
+
}
5758
+
return { match_: mm, remainder: input.substr(match[0].length) };
5759
+
}
5760
+
return null;
5761
+
}
5762
+
}
5763
+
},
5764
+
5765
+
//
5766
+
// Generic state machine actions
5767
+
//
5768
+
actions: {
5769
+
'a=': function (buffer, m) { buffer.a = (buffer.a || "") + m; },
5770
+
'b=': function (buffer, m) { buffer.b = (buffer.b || "") + m; },
5771
+
'p=': function (buffer, m) { buffer.p = (buffer.p || "") + m; },
5772
+
'o=': function (buffer, m) { buffer.o = (buffer.o || "") + m; },
5773
+
'q=': function (buffer, m) { buffer.q = (buffer.q || "") + m; },
5774
+
'd=': function (buffer, m) { buffer.d = (buffer.d || "") + m; },
5775
+
'rm=': function (buffer, m) { buffer.rm = (buffer.rm || "") + m; },
5776
+
'text=': function (buffer, m) { buffer.text_ = (buffer.text_ || "") + m; },
5777
+
'insert': function (buffer, m, a) { return { type_: a }; },
5778
+
'insert+p1': function (buffer, m, a) { return { type_: a, p1: m }; },
5779
+
'insert+p1+p2': function (buffer, m, a) { return { type_: a, p1: m[0], p2: m[1] }; },
5780
+
'copy': function (buffer, m) { return m; },
5781
+
'rm': function (buffer, m) { return { type_: 'rm', p1: m || ""}; },
5782
+
'text': function (buffer, m) { return mhchemParser.go(m, 'text'); },
5783
+
'{text}': function (buffer, m) {
5784
+
var ret = [ "{" ];
5785
+
mhchemParser.concatArray(ret, mhchemParser.go(m, 'text'));
5786
+
ret.push("}");
5787
+
return ret;
5788
+
},
5789
+
'tex-math': function (buffer, m) { return mhchemParser.go(m, 'tex-math'); },
5790
+
'tex-math tight': function (buffer, m) { return mhchemParser.go(m, 'tex-math tight'); },
5791
+
'bond': function (buffer, m, k) { return { type_: 'bond', kind_: k || m }; },
5792
+
'color0-output': function (buffer, m) { return { type_: 'color0', color: m[0] }; },
5793
+
'ce': function (buffer, m) { return mhchemParser.go(m); },
5794
+
'1/2': function (buffer, m) {
5795
+
/** @type {ParserOutput[]} */
5796
+
var ret = [];
5797
+
if (m.match(/^[+\-]/)) {
5798
+
ret.push(m.substr(0, 1));
5799
+
m = m.substr(1);
5800
+
}
5801
+
var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/);
5802
+
n[1] = n[1].replace(/\$/g, "");
5803
+
ret.push({ type_: 'frac', p1: n[1], p2: n[2] });
5804
+
if (n[3]) {
5805
+
n[3] = n[3].replace(/\$/g, "");
5806
+
ret.push({ type_: 'tex-math', p1: n[3] });
5807
+
}
5808
+
return ret;
5809
+
},
5810
+
'9,9': function (buffer, m) { return mhchemParser.go(m, '9,9'); }
5811
+
},
5812
+
//
5813
+
// createTransitions
5814
+
// convert { 'letter': { 'state': { action_: 'output' } } } to { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] }
5815
+
// with expansion of 'a|b' to 'a' and 'b' (at 2 places)
5816
+
//
5817
+
createTransitions: function (o) {
5818
+
var pattern, state;
5819
+
/** @type {string[]} */
5820
+
var stateArray;
5821
+
var i;
5822
+
//
5823
+
// 1. Collect all states
5824
+
//
5825
+
/** @type {Transitions} */
5826
+
var transitions = {};
5827
+
for (pattern in o) {
5828
+
for (state in o[pattern]) {
5829
+
stateArray = state.split("|");
5830
+
o[pattern][state].stateArray = stateArray;
5831
+
for (i=0; i<stateArray.length; i++) {
5832
+
transitions[stateArray[i]] = [];
5833
+
}
5834
+
}
5835
+
}
5836
+
//
5837
+
// 2. Fill states
5838
+
//
5839
+
for (pattern in o) {
5840
+
for (state in o[pattern]) {
5841
+
stateArray = o[pattern][state].stateArray || [];
5842
+
for (i=0; i<stateArray.length; i++) {
5843
+
//
5844
+
// 2a. Normalize actions into array: 'text=' ==> [{type_:'text='}]
5845
+
// (Note to myself: Resolving the function here would be problematic. It would need .bind (for *this*) and currying (for *option*).)
5846
+
//
5847
+
/** @type {any} */
5848
+
var p = o[pattern][state];
5849
+
if (p.action_) {
5850
+
p.action_ = [].concat(p.action_);
5851
+
for (var k=0; k<p.action_.length; k++) {
5852
+
if (typeof p.action_[k] === "string") {
5853
+
p.action_[k] = { type_: p.action_[k] };
5854
+
}
5855
+
}
5856
+
} else {
5857
+
p.action_ = [];
5858
+
}
5859
+
//
5860
+
// 2.b Multi-insert
5861
+
//
5862
+
var patternArray = pattern.split("|");
5863
+
for (var j=0; j<patternArray.length; j++) {
5864
+
if (stateArray[i] === '*') { // insert into all
5865
+
for (var t in transitions) {
5866
+
transitions[t].push({ pattern: patternArray[j], task: p });
5867
+
}
5868
+
} else {
5869
+
transitions[stateArray[i]].push({ pattern: patternArray[j], task: p });
5870
+
}
5871
+
}
5872
+
}
5873
+
}
5874
+
}
5875
+
return transitions;
5876
+
},
5877
+
stateMachines: {}
5878
+
};
5879
+
5880
+
//
5881
+
// Definition of state machines
5882
+
//
5883
+
mhchemParser.stateMachines = {
5884
+
//
5885
+
// \ce state machines
5886
+
//
5887
+
//#region ce
5888
+
'ce': { // main parser
5889
+
transitions: mhchemParser.createTransitions({
5890
+
'empty': {
5891
+
'*': { action_: 'output' } },
5892
+
'else': {
5893
+
'0|1|2': { action_: 'beginsWithBond=false', revisit: true, toContinue: true } },
5894
+
'oxidation$': {
5895
+
'0': { action_: 'oxidation-output' } },
5896
+
'CMT': {
5897
+
'r': { action_: 'rdt=', nextState: 'rt' },
5898
+
'rd': { action_: 'rqt=', nextState: 'rdt' } },
5899
+
'arrowUpDown': {
5900
+
'0|1|2|as': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '1' } },
5901
+
'uprightEntities': {
5902
+
'0|1|2': { action_: [ 'o=', 'output' ], nextState: '1' } },
5903
+
'orbital': {
5904
+
'0|1|2|3': { action_: 'o=', nextState: 'o' } },
5905
+
'->': {
5906
+
'0|1|2|3': { action_: 'r=', nextState: 'r' },
5907
+
'a|as': { action_: [ 'output', 'r=' ], nextState: 'r' },
5908
+
'*': { action_: [ 'output', 'r=' ], nextState: 'r' } },
5909
+
'+': {
5910
+
'o': { action_: 'd= kv', nextState: 'd' },
5911
+
'd|D': { action_: 'd=', nextState: 'd' },
5912
+
'q': { action_: 'd=', nextState: 'qd' },
5913
+
'qd|qD': { action_: 'd=', nextState: 'qd' },
5914
+
'dq': { action_: [ 'output', 'd=' ], nextState: 'd' },
5915
+
'3': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } },
5916
+
'amount': {
5917
+
'0|2': { action_: 'a=', nextState: 'a' } },
5918
+
'pm-operator': {
5919
+
'0|1|2|a|as': { action_: [ 'sb=false', 'output', { type_: 'operator', option: '\\pm' } ], nextState: '0' } },
5920
+
'operator': {
5921
+
'0|1|2|a|as': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } },
5922
+
'-$': {
5923
+
'o|q': { action_: [ 'charge or bond', 'output' ], nextState: 'qd' },
5924
+
'd': { action_: 'd=', nextState: 'd' },
5925
+
'D': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' },
5926
+
'q': { action_: 'd=', nextState: 'qd' },
5927
+
'qd': { action_: 'd=', nextState: 'qd' },
5928
+
'qD|dq': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } },
5929
+
'-9': {
5930
+
'3|o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '3' } },
5931
+
'- orbital overlap': {
5932
+
'o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' },
5933
+
'd': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' } },
5934
+
'-': {
5935
+
'0|1|2': { action_: [ { type_: 'output', option: 1 }, 'beginsWithBond=true', { type_: 'bond', option: "-" } ], nextState: '3' },
5936
+
'3': { action_: { type_: 'bond', option: "-" } },
5937
+
'a': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' },
5938
+
'as': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "-" } ], nextState: '3' },
5939
+
'b': { action_: 'b=' },
5940
+
'o': { action_: { type_: '- after o/d', option: false }, nextState: '2' },
5941
+
'q': { action_: { type_: '- after o/d', option: false }, nextState: '2' },
5942
+
'd|qd|dq': { action_: { type_: '- after o/d', option: true }, nextState: '2' },
5943
+
'D|qD|p': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } },
5944
+
'amount2': {
5945
+
'1|3': { action_: 'a=', nextState: 'a' } },
5946
+
'letters': {
5947
+
'0|1|2|3|a|as|b|p|bp|o': { action_: 'o=', nextState: 'o' },
5948
+
'q|dq': { action_: ['output', 'o='], nextState: 'o' },
5949
+
'd|D|qd|qD': { action_: 'o after d', nextState: 'o' } },
5950
+
'digits': {
5951
+
'o': { action_: 'q=', nextState: 'q' },
5952
+
'd|D': { action_: 'q=', nextState: 'dq' },
5953
+
'q': { action_: [ 'output', 'o=' ], nextState: 'o' },
5954
+
'a': { action_: 'o=', nextState: 'o' } },
5955
+
'space A': {
5956
+
'b|p|bp': {} },
5957
+
'space': {
5958
+
'a': { nextState: 'as' },
5959
+
'0': { action_: 'sb=false' },
5960
+
'1|2': { action_: 'sb=true' },
5961
+
'r|rt|rd|rdt|rdq': { action_: 'output', nextState: '0' },
5962
+
'*': { action_: [ 'output', 'sb=true' ], nextState: '1'} },
5963
+
'1st-level escape': {
5964
+
'1|2': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ] },
5965
+
'*': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ], nextState: '0' } },
5966
+
'[(...)]': {
5967
+
'r|rt': { action_: 'rd=', nextState: 'rd' },
5968
+
'rd|rdt': { action_: 'rq=', nextState: 'rdq' } },
5969
+
'...': {
5970
+
'o|d|D|dq|qd|qD': { action_: [ 'output', { type_: 'bond', option: "..." } ], nextState: '3' },
5971
+
'*': { action_: [ { type_: 'output', option: 1 }, { type_: 'insert', option: 'ellipsis' } ], nextState: '1' } },
5972
+
'. |* ': {
5973
+
'*': { action_: [ 'output', { type_: 'insert', option: 'addition compound' } ], nextState: '1' } },
5974
+
'state of aggregation $': {
5975
+
'*': { action_: [ 'output', 'state of aggregation' ], nextState: '1' } },
5976
+
'{[(': {
5977
+
'a|as|o': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' },
5978
+
'0|1|2|3': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' },
5979
+
'*': { action_: [ 'output', 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' } },
5980
+
')]}': {
5981
+
'0|1|2|3|b|p|bp|o': { action_: [ 'o=', 'parenthesisLevel--' ], nextState: 'o' },
5982
+
'a|as|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=', 'parenthesisLevel--' ], nextState: 'o' } },
5983
+
', ': {
5984
+
'*': { action_: [ 'output', 'comma' ], nextState: '0' } },
5985
+
'^_': { // ^ and _ without a sensible argument
5986
+
'*': { } },
5987
+
'^{(...)}|^($...$)': {
5988
+
'0|1|2|as': { action_: 'b=', nextState: 'b' },
5989
+
'p': { action_: 'b=', nextState: 'bp' },
5990
+
'3|o': { action_: 'd= kv', nextState: 'D' },
5991
+
'q': { action_: 'd=', nextState: 'qD' },
5992
+
'd|D|qd|qD|dq': { action_: [ 'output', 'd=' ], nextState: 'D' } },
5993
+
'^a|^\\x{}{}|^\\x{}|^\\x|\'': {
5994
+
'0|1|2|as': { action_: 'b=', nextState: 'b' },
5995
+
'p': { action_: 'b=', nextState: 'bp' },
5996
+
'3|o': { action_: 'd= kv', nextState: 'd' },
5997
+
'q': { action_: 'd=', nextState: 'qd' },
5998
+
'd|qd|D|qD': { action_: 'd=' },
5999
+
'dq': { action_: [ 'output', 'd=' ], nextState: 'd' } },
6000
+
'_{(state of aggregation)}$': {
6001
+
'd|D|q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } },
6002
+
'_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': {
6003
+
'0|1|2|as': { action_: 'p=', nextState: 'p' },
6004
+
'b': { action_: 'p=', nextState: 'bp' },
6005
+
'3|o': { action_: 'q=', nextState: 'q' },
6006
+
'd|D': { action_: 'q=', nextState: 'dq' },
6007
+
'q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } },
6008
+
'=<>': {
6009
+
'0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: '3' } },
6010
+
'#': {
6011
+
'0|1|2|3|a|as|o': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "#" } ], nextState: '3' } },
6012
+
'{}': {
6013
+
'*': { action_: { type_: 'output', option: 1 }, nextState: '1' } },
6014
+
'{...}': {
6015
+
'0|1|2|3|a|as|b|p|bp': { action_: 'o=', nextState: 'o' },
6016
+
'o|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } },
6017
+
'$...$': {
6018
+
'a': { action_: 'a=' }, // 2$n$
6019
+
'0|1|2|3|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, // not 'amount'
6020
+
'as|o': { action_: 'o=' },
6021
+
'q|d|D|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } },
6022
+
'\\bond{(...)}': {
6023
+
'*': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: "3" } },
6024
+
'\\frac{(...)}': {
6025
+
'*': { action_: [ { type_: 'output', option: 1 }, 'frac-output' ], nextState: '3' } },
6026
+
'\\overset{(...)}': {
6027
+
'*': { action_: [ { type_: 'output', option: 2 }, 'overset-output' ], nextState: '3' } },
6028
+
'\\underset{(...)}': {
6029
+
'*': { action_: [ { type_: 'output', option: 2 }, 'underset-output' ], nextState: '3' } },
6030
+
'\\underbrace{(...)}': {
6031
+
'*': { action_: [ { type_: 'output', option: 2 }, 'underbrace-output' ], nextState: '3' } },
6032
+
'\\color{(...)}{(...)}1|\\color(...){(...)}2': {
6033
+
'*': { action_: [ { type_: 'output', option: 2 }, 'color-output' ], nextState: '3' } },
6034
+
'\\color{(...)}0': {
6035
+
'*': { action_: [ { type_: 'output', option: 2 }, 'color0-output' ] } },
6036
+
'\\ce{(...)}': {
6037
+
'*': { action_: [ { type_: 'output', option: 2 }, 'ce' ], nextState: '3' } },
6038
+
'\\,': {
6039
+
'*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '1' } },
6040
+
'\\x{}{}|\\x{}|\\x': {
6041
+
'0|1|2|3|a|as|b|p|bp|o|c0': { action_: [ 'o=', 'output' ], nextState: '3' },
6042
+
'*': { action_: ['output', 'o=', 'output' ], nextState: '3' } },
6043
+
'others': {
6044
+
'*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '3' } },
6045
+
'else2': {
6046
+
'a': { action_: 'a to o', nextState: 'o', revisit: true },
6047
+
'as': { action_: [ 'output', 'sb=true' ], nextState: '1', revisit: true },
6048
+
'r|rt|rd|rdt|rdq': { action_: [ 'output' ], nextState: '0', revisit: true },
6049
+
'*': { action_: [ 'output', 'copy' ], nextState: '3' } }
6050
+
}),
6051
+
actions: {
6052
+
'o after d': function (buffer, m) {
6053
+
var ret;
6054
+
if ((buffer.d || "").match(/^[0-9]+$/)) {
6055
+
var tmp = buffer.d;
6056
+
buffer.d = undefined;
6057
+
ret = this['output'](buffer);
6058
+
buffer.b = tmp;
6059
+
} else {
6060
+
ret = this['output'](buffer);
6061
+
}
6062
+
mhchemParser.actions['o='](buffer, m);
6063
+
return ret;
6064
+
},
6065
+
'd= kv': function (buffer, m) {
6066
+
buffer.d = m;
6067
+
buffer.dType = 'kv';
6068
+
},
6069
+
'charge or bond': function (buffer, m) {
6070
+
if (buffer['beginsWithBond']) {
6071
+
/** @type {ParserOutput[]} */
6072
+
var ret = [];
6073
+
mhchemParser.concatArray(ret, this['output'](buffer));
6074
+
mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-"));
6075
+
return ret;
6076
+
} else {
6077
+
buffer.d = m;
6078
+
}
6079
+
},
6080
+
'- after o/d': function (buffer, m, isAfterD) {
6081
+
var c1 = mhchemParser.patterns.match_('orbital', buffer.o || "");
6082
+
var c2 = mhchemParser.patterns.match_('one lowercase greek letter $', buffer.o || "");
6083
+
var c3 = mhchemParser.patterns.match_('one lowercase latin letter $', buffer.o || "");
6084
+
var c4 = mhchemParser.patterns.match_('$one lowercase latin letter$ $', buffer.o || "");
6085
+
var hyphenFollows = m==="-" && ( c1 && c1.remainder==="" || c2 || c3 || c4 );
6086
+
if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) {
6087
+
buffer.o = '$' + buffer.o + '$';
6088
+
}
6089
+
/** @type {ParserOutput[]} */
6090
+
var ret = [];
6091
+
if (hyphenFollows) {
6092
+
mhchemParser.concatArray(ret, this['output'](buffer));
6093
+
ret.push({ type_: 'hyphen' });
6094
+
} else {
6095
+
c1 = mhchemParser.patterns.match_('digits', buffer.d || "");
6096
+
if (isAfterD && c1 && c1.remainder==='') {
6097
+
mhchemParser.concatArray(ret, mhchemParser.actions['d='](buffer, m));
6098
+
mhchemParser.concatArray(ret, this['output'](buffer));
6099
+
} else {
6100
+
mhchemParser.concatArray(ret, this['output'](buffer));
6101
+
mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-"));
6102
+
}
6103
+
}
6104
+
return ret;
6105
+
},
6106
+
'a to o': function (buffer) {
6107
+
buffer.o = buffer.a;
6108
+
buffer.a = undefined;
6109
+
},
6110
+
'sb=true': function (buffer) { buffer.sb = true; },
6111
+
'sb=false': function (buffer) { buffer.sb = false; },
6112
+
'beginsWithBond=true': function (buffer) { buffer['beginsWithBond'] = true; },
6113
+
'beginsWithBond=false': function (buffer) { buffer['beginsWithBond'] = false; },
6114
+
'parenthesisLevel++': function (buffer) { buffer['parenthesisLevel']++; },
6115
+
'parenthesisLevel--': function (buffer) { buffer['parenthesisLevel']--; },
6116
+
'state of aggregation': function (buffer, m) {
6117
+
return { type_: 'state of aggregation', p1: mhchemParser.go(m, 'o') };
6118
+
},
6119
+
'comma': function (buffer, m) {
6120
+
var a = m.replace(/\s*$/, '');
6121
+
var withSpace = (a !== m);
6122
+
if (withSpace && buffer['parenthesisLevel'] === 0) {
6123
+
return { type_: 'comma enumeration L', p1: a };
6124
+
} else {
6125
+
return { type_: 'comma enumeration M', p1: a };
6126
+
}
6127
+
},
6128
+
'output': function (buffer, m, entityFollows) {
6129
+
// entityFollows:
6130
+
// undefined = if we have nothing else to output, also ignore the just read space (buffer.sb)
6131
+
// 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1)
6132
+
// 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as)
6133
+
/** @type {ParserOutput | ParserOutput[]} */
6134
+
var ret;
6135
+
if (!buffer.r) {
6136
+
ret = [];
6137
+
if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) {
6138
+
//ret = [];
6139
+
} else {
6140
+
if (buffer.sb) {
6141
+
ret.push({ type_: 'entitySkip' });
6142
+
}
6143
+
if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows!==2) {
6144
+
buffer.o = buffer.a;
6145
+
buffer.a = undefined;
6146
+
} else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) {
6147
+
buffer.o = buffer.a;
6148
+
buffer.d = buffer.b;
6149
+
buffer.q = buffer.p;
6150
+
buffer.a = buffer.b = buffer.p = undefined;
6151
+
} else {
6152
+
if (buffer.o && buffer.dType==='kv' && mhchemParser.patterns.match_('d-oxidation$', buffer.d || "")) {
6153
+
buffer.dType = 'oxidation';
6154
+
} else if (buffer.o && buffer.dType==='kv' && !buffer.q) {
6155
+
buffer.dType = undefined;
6156
+
}
6157
+
}
6158
+
ret.push({
6159
+
type_: 'chemfive',
6160
+
a: mhchemParser.go(buffer.a, 'a'),
6161
+
b: mhchemParser.go(buffer.b, 'bd'),
6162
+
p: mhchemParser.go(buffer.p, 'pq'),
6163
+
o: mhchemParser.go(buffer.o, 'o'),
6164
+
q: mhchemParser.go(buffer.q, 'pq'),
6165
+
d: mhchemParser.go(buffer.d, (buffer.dType === 'oxidation' ? 'oxidation' : 'bd')),
6166
+
dType: buffer.dType
6167
+
});
6168
+
}
6169
+
} else { // r
6170
+
/** @type {ParserOutput[]} */
6171
+
var rd;
6172
+
if (buffer.rdt === 'M') {
6173
+
rd = mhchemParser.go(buffer.rd, 'tex-math');
6174
+
} else if (buffer.rdt === 'T') {
6175
+
rd = [ { type_: 'text', p1: buffer.rd || "" } ];
6176
+
} else {
6177
+
rd = mhchemParser.go(buffer.rd);
6178
+
}
6179
+
/** @type {ParserOutput[]} */
6180
+
var rq;
6181
+
if (buffer.rqt === 'M') {
6182
+
rq = mhchemParser.go(buffer.rq, 'tex-math');
6183
+
} else if (buffer.rqt === 'T') {
6184
+
rq = [ { type_: 'text', p1: buffer.rq || ""} ];
6185
+
} else {
6186
+
rq = mhchemParser.go(buffer.rq);
6187
+
}
6188
+
ret = {
6189
+
type_: 'arrow',
6190
+
r: buffer.r,
6191
+
rd: rd,
6192
+
rq: rq
6193
+
};
6194
+
}
6195
+
for (var p in buffer) {
6196
+
if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') {
6197
+
delete buffer[p];
6198
+
}
6199
+
}
6200
+
return ret;
6201
+
},
6202
+
'oxidation-output': function (buffer, m) {
6203
+
var ret = [ "{" ];
6204
+
mhchemParser.concatArray(ret, mhchemParser.go(m, 'oxidation'));
6205
+
ret.push("}");
6206
+
return ret;
6207
+
},
6208
+
'frac-output': function (buffer, m) {
6209
+
return { type_: 'frac-ce', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
6210
+
},
6211
+
'overset-output': function (buffer, m) {
6212
+
return { type_: 'overset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
6213
+
},
6214
+
'underset-output': function (buffer, m) {
6215
+
return { type_: 'underset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
6216
+
},
6217
+
'underbrace-output': function (buffer, m) {
6218
+
return { type_: 'underbrace', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
6219
+
},
6220
+
'color-output': function (buffer, m) {
6221
+
return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1]) };
6222
+
},
6223
+
'r=': function (buffer, m) { buffer.r = m; },
6224
+
'rdt=': function (buffer, m) { buffer.rdt = m; },
6225
+
'rd=': function (buffer, m) { buffer.rd = m; },
6226
+
'rqt=': function (buffer, m) { buffer.rqt = m; },
6227
+
'rq=': function (buffer, m) { buffer.rq = m; },
6228
+
'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; }
6229
+
}
6230
+
},
6231
+
'a': {
6232
+
transitions: mhchemParser.createTransitions({
6233
+
'empty': {
6234
+
'*': {} },
6235
+
'1/2$': {
6236
+
'0': { action_: '1/2' } },
6237
+
'else': {
6238
+
'0': { nextState: '1', revisit: true } },
6239
+
'$(...)$': {
6240
+
'*': { action_: 'tex-math tight', nextState: '1' } },
6241
+
',': {
6242
+
'*': { action_: { type_: 'insert', option: 'commaDecimal' } } },
6243
+
'else2': {
6244
+
'*': { action_: 'copy' } }
6245
+
}),
6246
+
actions: {}
6247
+
},
6248
+
'o': {
6249
+
transitions: mhchemParser.createTransitions({
6250
+
'empty': {
6251
+
'*': {} },
6252
+
'1/2$': {
6253
+
'0': { action_: '1/2' } },
6254
+
'else': {
6255
+
'0': { nextState: '1', revisit: true } },
6256
+
'letters': {
6257
+
'*': { action_: 'rm' } },
6258
+
'\\ca': {
6259
+
'*': { action_: { type_: 'insert', option: 'circa' } } },
6260
+
'\\x{}{}|\\x{}|\\x': {
6261
+
'*': { action_: 'copy' } },
6262
+
'${(...)}$|$(...)$': {
6263
+
'*': { action_: 'tex-math' } },
6264
+
'{(...)}': {
6265
+
'*': { action_: '{text}' } },
6266
+
'else2': {
6267
+
'*': { action_: 'copy' } }
6268
+
}),
6269
+
actions: {}
6270
+
},
6271
+
'text': {
6272
+
transitions: mhchemParser.createTransitions({
6273
+
'empty': {
6274
+
'*': { action_: 'output' } },
6275
+
'{...}': {
6276
+
'*': { action_: 'text=' } },
6277
+
'${(...)}$|$(...)$': {
6278
+
'*': { action_: 'tex-math' } },
6279
+
'\\greek': {
6280
+
'*': { action_: [ 'output', 'rm' ] } },
6281
+
'\\,|\\x{}{}|\\x{}|\\x': {
6282
+
'*': { action_: [ 'output', 'copy' ] } },
6283
+
'else': {
6284
+
'*': { action_: 'text=' } }
6285
+
}),
6286
+
actions: {
6287
+
'output': function (buffer) {
6288
+
if (buffer.text_) {
6289
+
/** @type {ParserOutput} */
6290
+
var ret = { type_: 'text', p1: buffer.text_ };
6291
+
for (var p in buffer) { delete buffer[p]; }
6292
+
return ret;
6293
+
}
6294
+
}
6295
+
}
6296
+
},
6297
+
'pq': {
6298
+
transitions: mhchemParser.createTransitions({
6299
+
'empty': {
6300
+
'*': {} },
6301
+
'state of aggregation $': {
6302
+
'*': { action_: 'state of aggregation' } },
6303
+
'i$': {
6304
+
'0': { nextState: '!f', revisit: true } },
6305
+
'(KV letters),': {
6306
+
'0': { action_: 'rm', nextState: '0' } },
6307
+
'formula$': {
6308
+
'0': { nextState: 'f', revisit: true } },
6309
+
'1/2$': {
6310
+
'0': { action_: '1/2' } },
6311
+
'else': {
6312
+
'0': { nextState: '!f', revisit: true } },
6313
+
'${(...)}$|$(...)$': {
6314
+
'*': { action_: 'tex-math' } },
6315
+
'{(...)}': {
6316
+
'*': { action_: 'text' } },
6317
+
'a-z': {
6318
+
'f': { action_: 'tex-math' } },
6319
+
'letters': {
6320
+
'*': { action_: 'rm' } },
6321
+
'-9.,9': {
6322
+
'*': { action_: '9,9' } },
6323
+
',': {
6324
+
'*': { action_: { type_: 'insert+p1', option: 'comma enumeration S' } } },
6325
+
'\\color{(...)}{(...)}1|\\color(...){(...)}2': {
6326
+
'*': { action_: 'color-output' } },
6327
+
'\\color{(...)}0': {
6328
+
'*': { action_: 'color0-output' } },
6329
+
'\\ce{(...)}': {
6330
+
'*': { action_: 'ce' } },
6331
+
'\\,|\\x{}{}|\\x{}|\\x': {
6332
+
'*': { action_: 'copy' } },
6333
+
'else2': {
6334
+
'*': { action_: 'copy' } }
6335
+
}),
6336
+
actions: {
6337
+
'state of aggregation': function (buffer, m) {
6338
+
return { type_: 'state of aggregation subscript', p1: mhchemParser.go(m, 'o') };
6339
+
},
6340
+
'color-output': function (buffer, m) {
6341
+
return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'pq') };
6342
+
}
6343
+
}
6344
+
},
6345
+
'bd': {
6346
+
transitions: mhchemParser.createTransitions({
6347
+
'empty': {
6348
+
'*': {} },
6349
+
'x$': {
6350
+
'0': { nextState: '!f', revisit: true } },
6351
+
'formula$': {
6352
+
'0': { nextState: 'f', revisit: true } },
6353
+
'else': {
6354
+
'0': { nextState: '!f', revisit: true } },
6355
+
'-9.,9 no missing 0': {
6356
+
'*': { action_: '9,9' } },
6357
+
'.': {
6358
+
'*': { action_: { type_: 'insert', option: 'electron dot' } } },
6359
+
'a-z': {
6360
+
'f': { action_: 'tex-math' } },
6361
+
'x': {
6362
+
'*': { action_: { type_: 'insert', option: 'KV x' } } },
6363
+
'letters': {
6364
+
'*': { action_: 'rm' } },
6365
+
'\'': {
6366
+
'*': { action_: { type_: 'insert', option: 'prime' } } },
6367
+
'${(...)}$|$(...)$': {
6368
+
'*': { action_: 'tex-math' } },
6369
+
'{(...)}': {
6370
+
'*': { action_: 'text' } },
6371
+
'\\color{(...)}{(...)}1|\\color(...){(...)}2': {
6372
+
'*': { action_: 'color-output' } },
6373
+
'\\color{(...)}0': {
6374
+
'*': { action_: 'color0-output' } },
6375
+
'\\ce{(...)}': {
6376
+
'*': { action_: 'ce' } },
6377
+
'\\,|\\x{}{}|\\x{}|\\x': {
6378
+
'*': { action_: 'copy' } },
6379
+
'else2': {
6380
+
'*': { action_: 'copy' } }
6381
+
}),
6382
+
actions: {
6383
+
'color-output': function (buffer, m) {
6384
+
return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'bd') };
6385
+
}
6386
+
}
6387
+
},
6388
+
'oxidation': {
6389
+
transitions: mhchemParser.createTransitions({
6390
+
'empty': {
6391
+
'*': {} },
6392
+
'roman numeral': {
6393
+
'*': { action_: 'roman-numeral' } },
6394
+
'${(...)}$|$(...)$': {
6395
+
'*': { action_: 'tex-math' } },
6396
+
'else': {
6397
+
'*': { action_: 'copy' } }
6398
+
}),
6399
+
actions: {
6400
+
'roman-numeral': function (buffer, m) { return { type_: 'roman numeral', p1: m || "" }; }
6401
+
}
6402
+
},
6403
+
'tex-math': {
6404
+
transitions: mhchemParser.createTransitions({
6405
+
'empty': {
6406
+
'*': { action_: 'output' } },
6407
+
'\\ce{(...)}': {
6408
+
'*': { action_: [ 'output', 'ce' ] } },
6409
+
'{...}|\\,|\\x{}{}|\\x{}|\\x': {
6410
+
'*': { action_: 'o=' } },
6411
+
'else': {
6412
+
'*': { action_: 'o=' } }
6413
+
}),
6414
+
actions: {
6415
+
'output': function (buffer) {
6416
+
if (buffer.o) {
6417
+
/** @type {ParserOutput} */
6418
+
var ret = { type_: 'tex-math', p1: buffer.o };
6419
+
for (var p in buffer) { delete buffer[p]; }
6420
+
return ret;
6421
+
}
6422
+
}
6423
+
}
6424
+
},
6425
+
'tex-math tight': {
6426
+
transitions: mhchemParser.createTransitions({
6427
+
'empty': {
6428
+
'*': { action_: 'output' } },
6429
+
'\\ce{(...)}': {
6430
+
'*': { action_: [ 'output', 'ce' ] } },
6431
+
'{...}|\\,|\\x{}{}|\\x{}|\\x': {
6432
+
'*': { action_: 'o=' } },
6433
+
'-|+': {
6434
+
'*': { action_: 'tight operator' } },
6435
+
'else': {
6436
+
'*': { action_: 'o=' } }
6437
+
}),
6438
+
actions: {
6439
+
'tight operator': function (buffer, m) { buffer.o = (buffer.o || "") + "{"+m+"}"; },
6440
+
'output': function (buffer) {
6441
+
if (buffer.o) {
6442
+
/** @type {ParserOutput} */
6443
+
var ret = { type_: 'tex-math', p1: buffer.o };
6444
+
for (var p in buffer) { delete buffer[p]; }
6445
+
return ret;
6446
+
}
6447
+
}
6448
+
}
6449
+
},
6450
+
'9,9': {
6451
+
transitions: mhchemParser.createTransitions({
6452
+
'empty': {
6453
+
'*': {} },
6454
+
',': {
6455
+
'*': { action_: 'comma' } },
6456
+
'else': {
6457
+
'*': { action_: 'copy' } }
6458
+
}),
6459
+
actions: {
6460
+
'comma': function () { return { type_: 'commaDecimal' }; }
6461
+
}
6462
+
},
6463
+
//#endregion
6464
+
//
6465
+
// \pu state machines
6466
+
//
6467
+
//#region pu
6468
+
'pu': {
6469
+
transitions: mhchemParser.createTransitions({
6470
+
'empty': {
6471
+
'*': { action_: 'output' } },
6472
+
'space$': {
6473
+
'*': { action_: [ 'output', 'space' ] } },
6474
+
'{[(|)]}': {
6475
+
'0|a': { action_: 'copy' } },
6476
+
'(-)(9)^(-9)': {
6477
+
'0': { action_: 'number^', nextState: 'a' } },
6478
+
'(-)(9.,9)(e)(99)': {
6479
+
'0': { action_: 'enumber', nextState: 'a' } },
6480
+
'space': {
6481
+
'0|a': {} },
6482
+
'pm-operator': {
6483
+
'0|a': { action_: { type_: 'operator', option: '\\pm' }, nextState: '0' } },
6484
+
'operator': {
6485
+
'0|a': { action_: 'copy', nextState: '0' } },
6486
+
'//': {
6487
+
'd': { action_: 'o=', nextState: '/' } },
6488
+
'/': {
6489
+
'd': { action_: 'o=', nextState: '/' } },
6490
+
'{...}|else': {
6491
+
'0|d': { action_: 'd=', nextState: 'd' },
6492
+
'a': { action_: [ 'space', 'd=' ], nextState: 'd' },
6493
+
'/|q': { action_: 'q=', nextState: 'q' } }
6494
+
}),
6495
+
actions: {
6496
+
'enumber': function (buffer, m) {
6497
+
/** @type {ParserOutput[]} */
6498
+
var ret = [];
6499
+
if (m[0] === "+-" || m[0] === "+/-") {
6500
+
ret.push("\\pm ");
6501
+
} else if (m[0]) {
6502
+
ret.push(m[0]);
6503
+
}
6504
+
if (m[1]) {
6505
+
mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9'));
6506
+
if (m[2]) {
6507
+
if (m[2].match(/[,.]/)) {
6508
+
mhchemParser.concatArray(ret, mhchemParser.go(m[2], 'pu-9,9'));
6509
+
} else {
6510
+
ret.push(m[2]);
6511
+
}
6512
+
}
6513
+
m[3] = m[4] || m[3];
6514
+
if (m[3]) {
6515
+
m[3] = m[3].trim();
6516
+
if (m[3] === "e" || m[3].substr(0, 1) === "*") {
6517
+
ret.push({ type_: 'cdot' });
6518
+
} else {
6519
+
ret.push({ type_: 'times' });
6520
+
}
6521
+
}
6522
+
}
6523
+
if (m[3]) {
6524
+
ret.push("10^{"+m[5]+"}");
6525
+
}
6526
+
return ret;
6527
+
},
6528
+
'number^': function (buffer, m) {
6529
+
/** @type {ParserOutput[]} */
6530
+
var ret = [];
6531
+
if (m[0] === "+-" || m[0] === "+/-") {
6532
+
ret.push("\\pm ");
6533
+
} else if (m[0]) {
6534
+
ret.push(m[0]);
6535
+
}
6536
+
mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9'));
6537
+
ret.push("^{"+m[2]+"}");
6538
+
return ret;
6539
+
},
6540
+
'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; },
6541
+
'space': function () { return { type_: 'pu-space-1' }; },
6542
+
'output': function (buffer) {
6543
+
/** @type {ParserOutput | ParserOutput[]} */
6544
+
var ret;
6545
+
var md = mhchemParser.patterns.match_('{(...)}', buffer.d || "");
6546
+
if (md && md.remainder === '') { buffer.d = md.match_; }
6547
+
var mq = mhchemParser.patterns.match_('{(...)}', buffer.q || "");
6548
+
if (mq && mq.remainder === '') { buffer.q = mq.match_; }
6549
+
if (buffer.d) {
6550
+
buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
6551
+
buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
6552
+
}
6553
+
if (buffer.q) { // fraction
6554
+
buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
6555
+
buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
6556
+
var b5 = {
6557
+
d: mhchemParser.go(buffer.d, 'pu'),
6558
+
q: mhchemParser.go(buffer.q, 'pu')
6559
+
};
6560
+
if (buffer.o === '//') {
6561
+
ret = { type_: 'pu-frac', p1: b5.d, p2: b5.q };
6562
+
} else {
6563
+
ret = b5.d;
6564
+
if (b5.d.length > 1 || b5.q.length > 1) {
6565
+
ret.push({ type_: ' / ' });
6566
+
} else {
6567
+
ret.push({ type_: '/' });
6568
+
}
6569
+
mhchemParser.concatArray(ret, b5.q);
6570
+
}
6571
+
} else { // no fraction
6572
+
ret = mhchemParser.go(buffer.d, 'pu-2');
6573
+
}
6574
+
for (var p in buffer) { delete buffer[p]; }
6575
+
return ret;
6576
+
}
6577
+
}
6578
+
},
6579
+
'pu-2': {
6580
+
transitions: mhchemParser.createTransitions({
6581
+
'empty': {
6582
+
'*': { action_: 'output' } },
6583
+
'*': {
6584
+
'*': { action_: [ 'output', 'cdot' ], nextState: '0' } },
6585
+
'\\x': {
6586
+
'*': { action_: 'rm=' } },
6587
+
'space': {
6588
+
'*': { action_: [ 'output', 'space' ], nextState: '0' } },
6589
+
'^{(...)}|^(-1)': {
6590
+
'1': { action_: '^(-1)' } },
6591
+
'-9.,9': {
6592
+
'0': { action_: 'rm=', nextState: '0' },
6593
+
'1': { action_: '^(-1)', nextState: '0' } },
6594
+
'{...}|else': {
6595
+
'*': { action_: 'rm=', nextState: '1' } }
6596
+
}),
6597
+
actions: {
6598
+
'cdot': function () { return { type_: 'tight cdot' }; },
6599
+
'^(-1)': function (buffer, m) { buffer.rm += "^{"+m+"}"; },
6600
+
'space': function () { return { type_: 'pu-space-2' }; },
6601
+
'output': function (buffer) {
6602
+
/** @type {ParserOutput | ParserOutput[]} */
6603
+
var ret = [];
6604
+
if (buffer.rm) {
6605
+
var mrm = mhchemParser.patterns.match_('{(...)}', buffer.rm || "");
6606
+
if (mrm && mrm.remainder === '') {
6607
+
ret = mhchemParser.go(mrm.match_, 'pu');
6608
+
} else {
6609
+
ret = { type_: 'rm', p1: buffer.rm };
6610
+
}
6611
+
}
6612
+
for (var p in buffer) { delete buffer[p]; }
6613
+
return ret;
6614
+
}
6615
+
}
6616
+
},
6617
+
'pu-9,9': {
6618
+
transitions: mhchemParser.createTransitions({
6619
+
'empty': {
6620
+
'0': { action_: 'output-0' },
6621
+
'o': { action_: 'output-o' } },
6622
+
',': {
6623
+
'0': { action_: [ 'output-0', 'comma' ], nextState: 'o' } },
6624
+
'.': {
6625
+
'0': { action_: [ 'output-0', 'copy' ], nextState: 'o' } },
6626
+
'else': {
6627
+
'*': { action_: 'text=' } }
6628
+
}),
6629
+
actions: {
6630
+
'comma': function () { return { type_: 'commaDecimal' }; },
6631
+
'output-0': function (buffer) {
6632
+
/** @type {ParserOutput[]} */
6633
+
var ret = [];
6634
+
buffer.text_ = buffer.text_ || "";
6635
+
if (buffer.text_.length > 4) {
6636
+
var a = buffer.text_.length % 3;
6637
+
if (a === 0) { a = 3; }
6638
+
for (var i=buffer.text_.length-3; i>0; i-=3) {
6639
+
ret.push(buffer.text_.substr(i, 3));
6640
+
ret.push({ type_: '1000 separator' });
6641
+
}
6642
+
ret.push(buffer.text_.substr(0, a));
6643
+
ret.reverse();
6644
+
} else {
6645
+
ret.push(buffer.text_);
6646
+
}
6647
+
for (var p in buffer) { delete buffer[p]; }
6648
+
return ret;
6649
+
},
6650
+
'output-o': function (buffer) {
6651
+
/** @type {ParserOutput[]} */
6652
+
var ret = [];
6653
+
buffer.text_ = buffer.text_ || "";
6654
+
if (buffer.text_.length > 4) {
6655
+
var a = buffer.text_.length - 3;
6656
+
for (var i=0; i<a; i+=3) {
6657
+
ret.push(buffer.text_.substr(i, 3));
6658
+
ret.push({ type_: '1000 separator' });
6659
+
}
6660
+
ret.push(buffer.text_.substr(i));
6661
+
} else {
6662
+
ret.push(buffer.text_);
6663
+
}
6664
+
for (var p in buffer) { delete buffer[p]; }
6665
+
return ret;
6666
+
}
6667
+
}
6668
+
}
6669
+
//#endregion
6670
+
};
6671
+
6672
+
//
6673
+
// texify: Take MhchemParser output and convert it to TeX
6674
+
//
6675
+
/** @type {Texify} */
6676
+
var texify = {
6677
+
go: function (input, isInner) { // (recursive, max 4 levels)
6678
+
if (!input) { return ""; }
6679
+
var res = "";
6680
+
var cee = false;
6681
+
for (var i=0; i < input.length; i++) {
6682
+
var inputi = input[i];
6683
+
if (typeof inputi === "string") {
6684
+
res += inputi;
6685
+
} else {
6686
+
res += texify._go2(inputi);
6687
+
if (inputi.type_ === '1st-level escape') { cee = true; }
6688
+
}
6689
+
}
6690
+
if (!isInner && !cee && res) {
6691
+
res = "{" + res + "}";
6692
+
}
6693
+
return res;
6694
+
},
6695
+
_goInner: function (input) {
6696
+
if (!input) { return input; }
6697
+
return texify.go(input, true);
6698
+
},
6699
+
_go2: function (buf) {
6700
+
/** @type {undefined | string} */
6701
+
var res;
6702
+
switch (buf.type_) {
6703
+
case 'chemfive':
6704
+
res = "";
6705
+
var b5 = {
6706
+
a: texify._goInner(buf.a),
6707
+
b: texify._goInner(buf.b),
6708
+
p: texify._goInner(buf.p),
6709
+
o: texify._goInner(buf.o),
6710
+
q: texify._goInner(buf.q),
6711
+
d: texify._goInner(buf.d)
6712
+
};
6713
+
//
6714
+
// a
6715
+
//
6716
+
if (b5.a) {
6717
+
if (b5.a.match(/^[+\-]/)) { b5.a = "{"+b5.a+"}"; }
6718
+
res += b5.a + "\\,";
6719
+
}
6720
+
//
6721
+
// b and p
6722
+
//
6723
+
if (b5.b || b5.p) {
6724
+
res += "{\\vphantom{X}}";
6725
+
res += "^{\\hphantom{"+(b5.b||"")+"}}_{\\hphantom{"+(b5.p||"")+"}}";
6726
+
res += "{\\vphantom{X}}";
6727
+
// In the next two lines, I've removed \smash[t] (ron)
6728
+
// TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0"
6729
+
//res += "^{\\smash[t]{\\vphantom{2}}\\mathllap{"+(b5.b||"")+"}}";
6730
+
res += "^{\\vphantom{2}\\mathllap{"+(b5.b||"")+"}}";
6731
+
//res += "_{\\vphantom{2}\\mathllap{\\smash[t]{"+(b5.p||"")+"}}}";
6732
+
res += "_{\\vphantom{2}\\mathllap{"+(b5.p||"")+"}}";
6733
+
}
6734
+
//
6735
+
// o
6736
+
//
6737
+
if (b5.o) {
6738
+
if (b5.o.match(/^[+\-]/)) { b5.o = "{"+b5.o+"}"; }
6739
+
res += b5.o;
6740
+
}
6741
+
//
6742
+
// q and d
6743
+
//
6744
+
if (buf.dType === 'kv') {
6745
+
if (b5.d || b5.q) {
6746
+
res += "{\\vphantom{X}}";
6747
+
}
6748
+
if (b5.d) {
6749
+
res += "^{"+b5.d+"}";
6750
+
}
6751
+
if (b5.q) {
6752
+
// In the next line, I've removed \smash[t] (ron)
6753
+
// TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0"
6754
+
//res += "_{\\smash[t]{"+b5.q+"}}";
6755
+
res += "_{"+b5.q+"}";
6756
+
}
6757
+
} else if (buf.dType === 'oxidation') {
6758
+
if (b5.d) {
6759
+
res += "{\\vphantom{X}}";
6760
+
res += "^{"+b5.d+"}";
6761
+
}
6762
+
if (b5.q) {
6763
+
// A Firefox bug adds a bogus depth to <mphantom>, so we change \vphantom{X} to {}
6764
+
// TODO: Reinstate \vphantom{X} when the Firefox bug is fixed.
6765
+
// res += "{\\vphantom{X}}";
6766
+
res += "{{}}";
6767
+
// In the next line, I've removed \smash[t] (ron)
6768
+
// TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0"
6769
+
//res += "_{\\smash[t]{"+b5.q+"}}";
6770
+
res += "_{"+b5.q+"}";
6771
+
}
6772
+
} else {
6773
+
if (b5.q) {
6774
+
// TODO: Reinstate \vphantom{X} when the Firefox bug is fixed.
6775
+
// res += "{\\vphantom{X}}";
6776
+
res += "{{}}";
6777
+
// In the next line, I've removed \smash[t] (ron)
6778
+
// TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0"
6779
+
//res += "_{\\smash[t]{"+b5.q+"}}";
6780
+
res += "_{"+b5.q+"}";
6781
+
}
6782
+
if (b5.d) {
6783
+
// TODO: Reinstate \vphantom{X} when the Firefox bug is fixed.
6784
+
// res += "{\\vphantom{X}}";
6785
+
res += "{{}}";
6786
+
res += "^{"+b5.d+"}";
6787
+
}
6788
+
}
6789
+
break;
6790
+
case 'rm':
6791
+
res = "\\mathrm{"+buf.p1+"}";
6792
+
break;
6793
+
case 'text':
6794
+
if (buf.p1.match(/[\^_]/)) {
6795
+
buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}");
6796
+
res = "\\mathrm{"+buf.p1+"}";
6797
+
} else {
6798
+
res = "\\text{"+buf.p1+"}";
6799
+
}
6800
+
break;
6801
+
case 'roman numeral':
6802
+
res = "\\mathrm{"+buf.p1+"}";
6803
+
break;
6804
+
case 'state of aggregation':
6805
+
res = "\\mskip2mu "+texify._goInner(buf.p1);
6806
+
break;
6807
+
case 'state of aggregation subscript':
6808
+
res = "\\mskip1mu "+texify._goInner(buf.p1);
6809
+
break;
6810
+
case 'bond':
6811
+
res = texify._getBond(buf.kind_);
6812
+
if (!res) {
6813
+
throw ["MhchemErrorBond", "mhchem Error. Unknown bond type (" + buf.kind_ + ")"];
6814
+
}
6815
+
break;
6816
+
case 'frac':
6817
+
var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}";
6818
+
res = "\\mathchoice{\\textstyle"+c+"}{"+c+"}{"+c+"}{"+c+"}";
6819
+
break;
6820
+
case 'pu-frac':
6821
+
var d = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
6822
+
res = "\\mathchoice{\\textstyle"+d+"}{"+d+"}{"+d+"}{"+d+"}";
6823
+
break;
6824
+
case 'tex-math':
6825
+
res = buf.p1 + " ";
6826
+
break;
6827
+
case 'frac-ce':
6828
+
res = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
6829
+
break;
6830
+
case 'overset':
6831
+
res = "\\overset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
6832
+
break;
6833
+
case 'underset':
6834
+
res = "\\underset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
6835
+
break;
6836
+
case 'underbrace':
6837
+
res = "\\underbrace{" + texify._goInner(buf.p1) + "}_{" + texify._goInner(buf.p2) + "}";
6838
+
break;
6839
+
case 'color':
6840
+
res = "{\\color{" + buf.color1 + "}{" + texify._goInner(buf.color2) + "}}";
6841
+
break;
6842
+
case 'color0':
6843
+
res = "\\color{" + buf.color + "}";
6844
+
break;
6845
+
case 'arrow':
6846
+
var b6 = {
6847
+
rd: texify._goInner(buf.rd),
6848
+
rq: texify._goInner(buf.rq)
6849
+
};
6850
+
var arrow = texify._getArrow(buf.r);
6851
+
if (b6.rq) { arrow += "[{\\rm " + b6.rq + "}]"; }
6852
+
if (b6.rd) {
6853
+
arrow += "{\\rm " + b6.rd + "}";
6854
+
} else {
6855
+
arrow += "{}";
6856
+
}
6857
+
res = arrow;
6858
+
break;
6859
+
case 'operator':
6860
+
res = texify._getOperator(buf.kind_);
6861
+
break;
6862
+
case '1st-level escape':
6863
+
res = buf.p1+" "; // &, \\\\, \\hlin
6864
+
break;
6865
+
case 'space':
6866
+
res = " ";
6867
+
break;
6868
+
case 'entitySkip':
6869
+
res = "~";
6870
+
break;
6871
+
case 'pu-space-1':
6872
+
res = "~";
6873
+
break;
6874
+
case 'pu-space-2':
6875
+
res = "\\mkern3mu ";
6876
+
break;
6877
+
case '1000 separator':
6878
+
res = "\\mkern2mu ";
6879
+
break;
6880
+
case 'commaDecimal':
6881
+
res = "{,}";
6882
+
break;
6883
+
case 'comma enumeration L':
6884
+
res = "{"+buf.p1+"}\\mkern6mu ";
6885
+
break;
6886
+
case 'comma enumeration M':
6887
+
res = "{"+buf.p1+"}\\mkern3mu ";
6888
+
break;
6889
+
case 'comma enumeration S':
6890
+
res = "{"+buf.p1+"}\\mkern1mu ";
6891
+
break;
6892
+
case 'hyphen':
6893
+
res = "\\text{-}";
6894
+
break;
6895
+
case 'addition compound':
6896
+
res = "\\,{\\cdot}\\,";
6897
+
break;
6898
+
case 'electron dot':
6899
+
res = "\\mkern1mu \\text{\\textbullet}\\mkern1mu ";
6900
+
break;
6901
+
case 'KV x':
6902
+
res = "{\\times}";
6903
+
break;
6904
+
case 'prime':
6905
+
res = "\\prime ";
6906
+
break;
6907
+
case 'cdot':
6908
+
res = "\\cdot ";
6909
+
break;
6910
+
case 'tight cdot':
6911
+
res = "\\mkern1mu{\\cdot}\\mkern1mu ";
6912
+
break;
6913
+
case 'times':
6914
+
res = "\\times ";
6915
+
break;
6916
+
case 'circa':
6917
+
res = "{\\sim}";
6918
+
break;
6919
+
case '^':
6920
+
res = "uparrow";
6921
+
break;
6922
+
case 'v':
6923
+
res = "downarrow";
6924
+
break;
6925
+
case 'ellipsis':
6926
+
res = "\\ldots ";
6927
+
break;
6928
+
case '/':
6929
+
res = "/";
6930
+
break;
6931
+
case ' / ':
6932
+
res = "\\,/\\,";
6933
+
break;
6934
+
default:
6935
+
assertNever(buf);
6936
+
throw ["MhchemBugT", "mhchem bug T. Please report."]; // Missing texify rule or unknown MhchemParser output
6937
+
}
6938
+
assertString(res);
6939
+
return res;
6940
+
},
6941
+
_getArrow: function (a) {
6942
+
switch (a) {
6943
+
case "->": return "\\yields";
6944
+
case "\u2192": return "\\yields";
6945
+
case "\u27F6": return "\\yields";
6946
+
case "<-": return "\\yieldsLeft";
6947
+
case "<->": return "\\mesomerism";
6948
+
case "<-->": return "\\yieldsLeftRight";
6949
+
case "<=>": return "\\equilibrium";
6950
+
case "\u21CC": return "\\equilibrium";
6951
+
case "<=>>": return "\\equilibriumRight";
6952
+
case "<<=>": return "\\equilibriumLeft";
6953
+
default:
6954
+
assertNever(a);
6955
+
throw ["MhchemBugT", "mhchem bug T. Please report."];
6956
+
}
6957
+
},
6958
+
_getBond: function (a) {
6959
+
switch (a) {
6960
+
case "-": return "{-}";
6961
+
case "1": return "{-}";
6962
+
case "=": return "{=}";
6963
+
case "2": return "{=}";
6964
+
case "#": return "{\\equiv}";
6965
+
case "3": return "{\\equiv}";
6966
+
case "~": return "{\\tripleDash}";
6967
+
case "~-": return "{\\tripleDashOverLine}";
6968
+
case "~=": return "{\\tripleDashOverDoubleLine}";
6969
+
case "~--": return "{\\tripleDashOverDoubleLine}";
6970
+
case "-~-": return "{\\tripleDashBetweenDoubleLine}";
6971
+
case "...": return "{{\\cdot}{\\cdot}{\\cdot}}";
6972
+
case "....": return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}";
6973
+
case "->": return "{\\rightarrow}";
6974
+
case "<-": return "{\\leftarrow}";
6975
+
case "<": return "{<}";
6976
+
case ">": return "{>}";
6977
+
default:
6978
+
assertNever(a);
6979
+
throw ["MhchemBugT", "mhchem bug T. Please report."];
6980
+
}
6981
+
},
6982
+
_getOperator: function (a) {
6983
+
switch (a) {
6984
+
case "+": return " {}+{} ";
6985
+
case "-": return " {}-{} ";
6986
+
case "=": return " {}={} ";
6987
+
case "<": return " {}<{} ";
6988
+
case ">": return " {}>{} ";
6989
+
case "<<": return " {}\\ll{} ";
6990
+
case ">>": return " {}\\gg{} ";
6991
+
case "\\pm": return " {}\\pm{} ";
6992
+
case "\\approx": return " {}\\approx{} ";
6993
+
case "$\\approx$": return " {}\\approx{} ";
6994
+
case "v": return " \\downarrow{} ";
6995
+
case "(v)": return " \\downarrow{} ";
6996
+
case "^": return " \\uparrow{} ";
6997
+
case "(^)": return " \\uparrow{} ";
6998
+
default:
6999
+
assertNever(a);
7000
+
throw ["MhchemBugT", "mhchem bug T. Please report."];
7001
+
}
7002
+
}
7003
+
};
7004
+
7005
+
//
7006
+
// Helpers for code analysis
7007
+
// Will show type error at calling position
7008
+
//
7009
+
/** @param {number} a */
7010
+
function assertNever(a) {}
7011
+
/** @param {string} a */
7012
+
function assertString(a) {}
7013
+
7014
+
/* eslint-disable no-undef */
7015
+
7016
+
//////////////////////////////////////////////////////////////////////
7017
+
// texvc.sty
7018
+
7019
+
// The texvc package contains macros available in mediawiki pages.
7020
+
// We omit the functions deprecated at
7021
+
// https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax
7022
+
7023
+
// We also omit texvc's \O, which conflicts with \text{\O}
7024
+
7025
+
defineMacro("\\darr", "\\downarrow");
7026
+
defineMacro("\\dArr", "\\Downarrow");
7027
+
defineMacro("\\Darr", "\\Downarrow");
7028
+
defineMacro("\\lang", "\\langle");
7029
+
defineMacro("\\rang", "\\rangle");
7030
+
defineMacro("\\uarr", "\\uparrow");
7031
+
defineMacro("\\uArr", "\\Uparrow");
7032
+
defineMacro("\\Uarr", "\\Uparrow");
7033
+
defineMacro("\\N", "\\mathbb{N}");
7034
+
defineMacro("\\R", "\\mathbb{R}");
7035
+
defineMacro("\\Z", "\\mathbb{Z}");
7036
+
defineMacro("\\alef", "\\aleph");
7037
+
defineMacro("\\alefsym", "\\aleph");
7038
+
defineMacro("\\bull", "\\bullet");
7039
+
defineMacro("\\clubs", "\\clubsuit");
7040
+
defineMacro("\\cnums", "\\mathbb{C}");
7041
+
defineMacro("\\Complex", "\\mathbb{C}");
7042
+
defineMacro("\\Dagger", "\\ddagger");
7043
+
defineMacro("\\diamonds", "\\diamondsuit");
7044
+
defineMacro("\\empty", "\\emptyset");
7045
+
defineMacro("\\exist", "\\exists");
7046
+
defineMacro("\\harr", "\\leftrightarrow");
7047
+
defineMacro("\\hArr", "\\Leftrightarrow");
7048
+
defineMacro("\\Harr", "\\Leftrightarrow");
7049
+
defineMacro("\\hearts", "\\heartsuit");
7050
+
defineMacro("\\image", "\\Im");
7051
+
defineMacro("\\infin", "\\infty");
7052
+
defineMacro("\\isin", "\\in");
7053
+
defineMacro("\\larr", "\\leftarrow");
7054
+
defineMacro("\\lArr", "\\Leftarrow");
7055
+
defineMacro("\\Larr", "\\Leftarrow");
7056
+
defineMacro("\\lrarr", "\\leftrightarrow");
7057
+
defineMacro("\\lrArr", "\\Leftrightarrow");
7058
+
defineMacro("\\Lrarr", "\\Leftrightarrow");
7059
+
defineMacro("\\natnums", "\\mathbb{N}");
7060
+
defineMacro("\\plusmn", "\\pm");
7061
+
defineMacro("\\rarr", "\\rightarrow");
7062
+
defineMacro("\\rArr", "\\Rightarrow");
7063
+
defineMacro("\\Rarr", "\\Rightarrow");
7064
+
defineMacro("\\real", "\\Re");
7065
+
defineMacro("\\reals", "\\mathbb{R}");
7066
+
defineMacro("\\Reals", "\\mathbb{R}");
7067
+
defineMacro("\\sdot", "\\cdot");
7068
+
defineMacro("\\sect", "\\S");
7069
+
defineMacro("\\spades", "\\spadesuit");
7070
+
defineMacro("\\sub", "\\subset");
7071
+
defineMacro("\\sube", "\\subseteq");
7072
+
defineMacro("\\supe", "\\supseteq");
7073
+
defineMacro("\\thetasym", "\\vartheta");
7074
+
defineMacro("\\weierp", "\\wp");
7075
+
7076
+
/* eslint-disable no-undef */
7077
+
7078
+
/****************************************************
7079
+
*
7080
+
* physics.js
7081
+
*
7082
+
* Implements the Physics Package for LaTeX input.
7083
+
*
7084
+
* ---------------------------------------------------------------------
7085
+
*
7086
+
* The original version of this file is licensed as follows:
7087
+
* Copyright (c) 2015-2016 Kolen Cheung <https://github.com/ickc/MathJax-third-party-extensions>.
7088
+
*
7089
+
* Licensed under the Apache License, Version 2.0 (the "License");
7090
+
* you may not use this file except in compliance with the License.
7091
+
* You may obtain a copy of the License at
7092
+
*
7093
+
* http://www.apache.org/licenses/LICENSE-2.0
7094
+
*
7095
+
* Unless required by applicable law or agreed to in writing, software
7096
+
* distributed under the License is distributed on an "AS IS" BASIS,
7097
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7098
+
* See the License for the specific language governing permissions and
7099
+
* limitations under the License.
7100
+
*
7101
+
* ---------------------------------------------------------------------
7102
+
*
7103
+
* This file has been revised from the original in the following ways:
7104
+
* 1. The interface is changed so that it can be called from Temml, not MathJax.
7105
+
* 2. \Re and \Im are not used, to avoid conflict with existing LaTeX letters.
7106
+
*
7107
+
* This revision of the file is released under the MIT license.
7108
+
* https://mit-license.org/
7109
+
*/
7110
+
defineMacro("\\quantity", "{\\left\\{ #1 \\right\\}}");
7111
+
defineMacro("\\qty", "{\\left\\{ #1 \\right\\}}");
7112
+
defineMacro("\\pqty", "{\\left( #1 \\right)}");
7113
+
defineMacro("\\bqty", "{\\left[ #1 \\right]}");
7114
+
defineMacro("\\vqty", "{\\left\\vert #1 \\right\\vert}");
7115
+
defineMacro("\\Bqty", "{\\left\\{ #1 \\right\\}}");
7116
+
defineMacro("\\absolutevalue", "{\\left\\vert #1 \\right\\vert}");
7117
+
defineMacro("\\abs", "{\\left\\vert #1 \\right\\vert}");
7118
+
defineMacro("\\norm", "{\\left\\Vert #1 \\right\\Vert}");
7119
+
defineMacro("\\evaluated", "{\\left.#1 \\right\\vert}");
7120
+
defineMacro("\\eval", "{\\left.#1 \\right\\vert}");
7121
+
defineMacro("\\order", "{\\mathcal{O} \\left( #1 \\right)}");
7122
+
defineMacro("\\commutator", "{\\left[ #1 , #2 \\right]}");
7123
+
defineMacro("\\comm", "{\\left[ #1 , #2 \\right]}");
7124
+
defineMacro("\\anticommutator", "{\\left\\{ #1 , #2 \\right\\}}");
7125
+
defineMacro("\\acomm", "{\\left\\{ #1 , #2 \\right\\}}");
7126
+
defineMacro("\\poissonbracket", "{\\left\\{ #1 , #2 \\right\\}}");
7127
+
defineMacro("\\pb", "{\\left\\{ #1 , #2 \\right\\}}");
7128
+
defineMacro("\\vectorbold", "{\\boldsymbol{ #1 }}");
7129
+
defineMacro("\\vb", "{\\boldsymbol{ #1 }}");
7130
+
defineMacro("\\vectorarrow", "{\\vec{\\boldsymbol{ #1 }}}");
7131
+
defineMacro("\\va", "{\\vec{\\boldsymbol{ #1 }}}");
7132
+
defineMacro("\\vectorunit", "{{\\boldsymbol{\\hat{ #1 }}}}");
7133
+
defineMacro("\\vu", "{{\\boldsymbol{\\hat{ #1 }}}}");
7134
+
defineMacro("\\dotproduct", "\\mathbin{\\boldsymbol\\cdot}");
7135
+
defineMacro("\\vdot", "{\\boldsymbol\\cdot}");
7136
+
defineMacro("\\crossproduct", "\\mathbin{\\boldsymbol\\times}");
7137
+
defineMacro("\\cross", "\\mathbin{\\boldsymbol\\times}");
7138
+
defineMacro("\\cp", "\\mathbin{\\boldsymbol\\times}");
7139
+
defineMacro("\\gradient", "{\\boldsymbol\\nabla}");
7140
+
defineMacro("\\grad", "{\\boldsymbol\\nabla}");
7141
+
defineMacro("\\divergence", "{\\grad\\vdot}");
7142
+
//defineMacro("\\div", "{\\grad\\vdot}"); Not included in Temml. Conflicts w/LaTeX \div
7143
+
defineMacro("\\curl", "{\\grad\\cross}");
7144
+
defineMacro("\\laplacian", "\\nabla^2");
7145
+
defineMacro("\\tr", "{\\operatorname{tr}}");
7146
+
defineMacro("\\Tr", "{\\operatorname{Tr}}");
7147
+
defineMacro("\\rank", "{\\operatorname{rank}}");
7148
+
defineMacro("\\erf", "{\\operatorname{erf}}");
7149
+
defineMacro("\\Res", "{\\operatorname{Res}}");
7150
+
defineMacro("\\principalvalue", "{\\mathcal{P}}");
7151
+
defineMacro("\\pv", "{\\mathcal{P}}");
7152
+
defineMacro("\\PV", "{\\operatorname{P.V.}}");
7153
+
// Temml does not use the next two lines. They conflict with LaTeX letters.
7154
+
//defineMacro("\\Re", "{\\operatorname{Re} \\left\\{ #1 \\right\\}}");
7155
+
//defineMacro("\\Im", "{\\operatorname{Im} \\left\\{ #1 \\right\\}}");
7156
+
defineMacro("\\qqtext", "{\\quad\\text{ #1 }\\quad}");
7157
+
defineMacro("\\qq", "{\\quad\\text{ #1 }\\quad}");
7158
+
defineMacro("\\qcomma", "{\\text{,}\\quad}");
7159
+
defineMacro("\\qc", "{\\text{,}\\quad}");
7160
+
defineMacro("\\qcc", "{\\quad\\text{c.c.}\\quad}");
7161
+
defineMacro("\\qif", "{\\quad\\text{if}\\quad}");
7162
+
defineMacro("\\qthen", "{\\quad\\text{then}\\quad}");
7163
+
defineMacro("\\qelse", "{\\quad\\text{else}\\quad}");
7164
+
defineMacro("\\qotherwise", "{\\quad\\text{otherwise}\\quad}");
7165
+
defineMacro("\\qunless", "{\\quad\\text{unless}\\quad}");
7166
+
defineMacro("\\qgiven", "{\\quad\\text{given}\\quad}");
7167
+
defineMacro("\\qusing", "{\\quad\\text{using}\\quad}");
7168
+
defineMacro("\\qassume", "{\\quad\\text{assume}\\quad}");
7169
+
defineMacro("\\qsince", "{\\quad\\text{since}\\quad}");
7170
+
defineMacro("\\qlet", "{\\quad\\text{let}\\quad}");
7171
+
defineMacro("\\qfor", "{\\quad\\text{for}\\quad}");
7172
+
defineMacro("\\qall", "{\\quad\\text{all}\\quad}");
7173
+
defineMacro("\\qeven", "{\\quad\\text{even}\\quad}");
7174
+
defineMacro("\\qodd", "{\\quad\\text{odd}\\quad}");
7175
+
defineMacro("\\qinteger", "{\\quad\\text{integer}\\quad}");
7176
+
defineMacro("\\qand", "{\\quad\\text{and}\\quad}");
7177
+
defineMacro("\\qor", "{\\quad\\text{or}\\quad}");
7178
+
defineMacro("\\qas", "{\\quad\\text{as}\\quad}");
7179
+
defineMacro("\\qin", "{\\quad\\text{in}\\quad}");
7180
+
defineMacro("\\differential", "{\\text{d}}");
7181
+
defineMacro("\\dd", "{\\text{d}}");
7182
+
defineMacro("\\derivative", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}");
7183
+
defineMacro("\\dv", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}");
7184
+
defineMacro("\\partialderivative", "{\\frac{\\partial{ #1 }}{\\partial{ #2 }}}");
7185
+
defineMacro("\\variation", "{\\delta}");
7186
+
defineMacro("\\var", "{\\delta}");
7187
+
defineMacro("\\functionalderivative", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}");
7188
+
defineMacro("\\fdv", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}");
7189
+
defineMacro("\\innerproduct", "{\\left\\langle {#1} \\mid { #2} \\right\\rangle}");
7190
+
defineMacro("\\outerproduct",
7191
+
"{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7192
+
defineMacro("\\dyad",
7193
+
"{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7194
+
defineMacro("\\ketbra",
7195
+
"{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7196
+
defineMacro("\\op",
7197
+
"{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7198
+
defineMacro("\\expectationvalue", "{\\left\\langle {#1 } \\right\\rangle}");
7199
+
defineMacro("\\expval", "{\\left\\langle {#1 } \\right\\rangle}");
7200
+
defineMacro("\\ev", "{\\left\\langle {#1 } \\right\\rangle}");
7201
+
defineMacro("\\matrixelement",
7202
+
"{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}");
7203
+
defineMacro("\\matrixel",
7204
+
"{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}");
7205
+
defineMacro("\\mel",
7206
+
"{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}");
7207
+
7208
+
// Helper functions
7209
+
function getHLines(parser) {
7210
+
// Return an array. The array length = number of hlines.
7211
+
// Each element in the array tells if the line is dashed.
7212
+
const hlineInfo = [];
7213
+
parser.consumeSpaces();
7214
+
let nxt = parser.fetch().text;
7215
+
if (nxt === "\\relax") {
7216
+
parser.consume();
7217
+
parser.consumeSpaces();
7218
+
nxt = parser.fetch().text;
7219
+
}
7220
+
while (nxt === "\\hline" || nxt === "\\hdashline") {
7221
+
parser.consume();
7222
+
hlineInfo.push(nxt === "\\hdashline");
7223
+
parser.consumeSpaces();
7224
+
nxt = parser.fetch().text;
7225
+
}
7226
+
return hlineInfo;
7227
+
}
7228
+
7229
+
const validateAmsEnvironmentContext = context => {
7230
+
const settings = context.parser.settings;
7231
+
if (!settings.displayMode) {
7232
+
throw new ParseError(`{${context.envName}} can be used only in display mode.`);
7233
+
}
7234
+
};
7235
+
7236
+
const sizeRegEx$1 = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/;
7237
+
const arrayGaps = macros => {
7238
+
let arraystretch = macros.get("\\arraystretch");
7239
+
if (typeof arraystretch !== "string") {
7240
+
arraystretch = stringFromArg(arraystretch.tokens);
7241
+
}
7242
+
arraystretch = isNaN(arraystretch) ? null : Number(arraystretch);
7243
+
let arraycolsepStr = macros.get("\\arraycolsep");
7244
+
if (typeof arraycolsepStr !== "string") {
7245
+
arraycolsepStr = stringFromArg(arraycolsepStr.tokens);
7246
+
}
7247
+
const match = sizeRegEx$1.exec(arraycolsepStr);
7248
+
const arraycolsep = match
7249
+
? { number: +(match[1] + match[2]), unit: match[3] }
7250
+
: null;
7251
+
return [arraystretch, arraycolsep]
7252
+
};
7253
+
7254
+
const checkCellForLabels = cell => {
7255
+
// Check if the author wrote a \tag{} inside this cell.
7256
+
let rowLabel = "";
7257
+
for (let i = 0; i < cell.length; i++) {
7258
+
if (cell[i].type === "label") {
7259
+
if (rowLabel) { throw new ParseError(("Multiple \\labels in one row")) }
7260
+
rowLabel = cell[i].string;
7261
+
}
7262
+
}
7263
+
return rowLabel
7264
+
};
7265
+
7266
+
// autoTag (an argument to parseArray) can be one of three values:
7267
+
// * undefined: Regular (not-top-level) array; no tags on each row
7268
+
// * true: Automatic equation numbering, overridable by \tag
7269
+
// * false: Tags allowed on each row, but no automatic numbering
7270
+
// This function *doesn't* work with the "split" environment name.
7271
+
function getAutoTag(name) {
7272
+
if (name.indexOf("ed") === -1) {
7273
+
return name.indexOf("*") === -1;
7274
+
}
7275
+
// return undefined;
7276
+
}
7277
+
7278
+
/**
7279
+
* Parse the body of the environment, with rows delimited by \\ and
7280
+
* columns delimited by &, and create a nested list in row-major order
7281
+
* with one group per cell. If given an optional argument scriptLevel
7282
+
* ("text", "display", etc.), then each cell is cast into that scriptLevel.
7283
+
*/
7284
+
function parseArray(
7285
+
parser,
7286
+
{
7287
+
cols, // [{ type: string , align: l|c|r|null }]
7288
+
envClasses, // align(ed|at|edat) | array | cases | cd | small | multline
7289
+
autoTag, // boolean
7290
+
singleRow, // boolean
7291
+
emptySingleRow, // boolean
7292
+
maxNumCols, // number
7293
+
leqno, // boolean
7294
+
arraystretch, // number | null
7295
+
arraycolsep // size value | null
7296
+
},
7297
+
scriptLevel
7298
+
) {
7299
+
parser.gullet.beginGroup();
7300
+
if (!singleRow) {
7301
+
// \cr is equivalent to \\ without the optional size argument (see below)
7302
+
// TODO: provide helpful error when \cr is used outside array environment
7303
+
parser.gullet.macros.set("\\cr", "\\\\\\relax");
7304
+
}
7305
+
7306
+
// Start group for first cell
7307
+
parser.gullet.beginGroup();
7308
+
7309
+
let row = [];
7310
+
const body = [row];
7311
+
const rowGaps = [];
7312
+
const labels = [];
7313
+
7314
+
const hLinesBeforeRow = [];
7315
+
7316
+
const tags = (autoTag != null ? [] : undefined);
7317
+
7318
+
// amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent
7319
+
// whether this row should have an equation number. Simulate this with
7320
+
// a \@eqnsw macro set to 1 or 0.
7321
+
function beginRow() {
7322
+
if (autoTag) {
7323
+
parser.gullet.macros.set("\\@eqnsw", "1", true);
7324
+
}
7325
+
}
7326
+
function endRow() {
7327
+
if (tags) {
7328
+
if (parser.gullet.macros.get("\\df@tag")) {
7329
+
tags.push(parser.subparse([new Token("\\df@tag")]));
7330
+
parser.gullet.macros.set("\\df@tag", undefined, true);
7331
+
} else {
7332
+
tags.push(Boolean(autoTag) &&
7333
+
parser.gullet.macros.get("\\@eqnsw") === "1");
7334
+
}
7335
+
}
7336
+
}
7337
+
beginRow();
7338
+
7339
+
// Test for \hline at the top of the array.
7340
+
hLinesBeforeRow.push(getHLines(parser));
7341
+
7342
+
while (true) {
7343
+
// Parse each cell in its own group (namespace)
7344
+
let cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\");
7345
+
parser.gullet.endGroup();
7346
+
parser.gullet.beginGroup();
7347
+
7348
+
cell = {
7349
+
type: "ordgroup",
7350
+
mode: parser.mode,
7351
+
body: cell,
7352
+
semisimple: true
7353
+
};
7354
+
row.push(cell);
7355
+
const next = parser.fetch().text;
7356
+
if (next === "&") {
7357
+
if (maxNumCols && row.length === maxNumCols) {
7358
+
if (envClasses.includes("array")) {
7359
+
if (parser.settings.strict) {
7360
+
throw new ParseError("Too few columns " + "specified in the {array} column argument.",
7361
+
parser.nextToken)
7362
+
}
7363
+
} else if (maxNumCols === 2) {
7364
+
throw new ParseError("The split environment accepts no more than two columns",
7365
+
parser.nextToken);
7366
+
} else {
7367
+
throw new ParseError("The equation environment accepts only one column",
7368
+
parser.nextToken)
7369
+
}
7370
+
}
7371
+
parser.consume();
7372
+
} else if (next === "\\end") {
7373
+
endRow();
7374
+
// Arrays terminate newlines with `\crcr` which consumes a `\cr` if
7375
+
// the last line is empty. However, AMS environments keep the
7376
+
// empty row if it's the only one.
7377
+
// NOTE: Currently, `cell` is the last item added into `row`.
7378
+
if (row.length === 1 && cell.body.length === 0 && (body.length > 1 || !emptySingleRow)) {
7379
+
body.pop();
7380
+
}
7381
+
labels.push(checkCellForLabels(cell.body));
7382
+
if (hLinesBeforeRow.length < body.length + 1) {
7383
+
hLinesBeforeRow.push([]);
7384
+
}
7385
+
break;
7386
+
} else if (next === "\\\\") {
7387
+
parser.consume();
7388
+
let size;
7389
+
// \def\Let@{\let\\\math@cr}
7390
+
// \def\math@cr{...\math@cr@}
7391
+
// \def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}}
7392
+
// \def\math@cr@@[#1]{...\math@cr@@@...}
7393
+
// \def\math@cr@@@{\cr}
7394
+
if (parser.gullet.future().text !== " ") {
7395
+
size = parser.parseSizeGroup(true);
7396
+
}
7397
+
rowGaps.push(size ? size.value : null);
7398
+
endRow();
7399
+
7400
+
labels.push(checkCellForLabels(cell.body));
7401
+
7402
+
// check for \hline(s) following the row separator
7403
+
hLinesBeforeRow.push(getHLines(parser));
7404
+
7405
+
row = [];
7406
+
body.push(row);
7407
+
beginRow();
7408
+
} else {
7409
+
throw new ParseError("Expected & or \\\\ or \\cr or \\end", parser.nextToken);
7410
+
}
7411
+
}
7412
+
7413
+
// End cell group
7414
+
parser.gullet.endGroup();
7415
+
// End array group defining \cr
7416
+
parser.gullet.endGroup();
7417
+
7418
+
return {
7419
+
type: "array",
7420
+
mode: parser.mode,
7421
+
body,
7422
+
cols,
7423
+
rowGaps,
7424
+
hLinesBeforeRow,
7425
+
envClasses,
7426
+
autoTag,
7427
+
scriptLevel,
7428
+
tags,
7429
+
labels,
7430
+
leqno,
7431
+
arraystretch,
7432
+
arraycolsep
7433
+
};
7434
+
}
7435
+
7436
+
// Decides on a scriptLevel for cells in an array according to whether the given
7437
+
// environment name starts with the letter 'd'.
7438
+
function dCellStyle(envName) {
7439
+
return envName.slice(0, 1) === "d" ? "display" : "text"
7440
+
}
7441
+
7442
+
const alignMap = {
7443
+
c: "center ",
7444
+
l: "left ",
7445
+
r: "right "
7446
+
};
7447
+
7448
+
const glue = group => {
7449
+
const glueNode = new mathMLTree.MathNode("mtd", []);
7450
+
glueNode.style = { padding: "0", width: "50%" };
7451
+
if (group.envClasses.includes("multline")) {
7452
+
glueNode.style.width = "7.5%";
7453
+
}
7454
+
return glueNode
7455
+
};
7456
+
7457
+
const mathmlBuilder$7 = function(group, style) {
7458
+
const tbl = [];
7459
+
const numRows = group.body.length;
7460
+
const hlines = group.hLinesBeforeRow;
7461
+
7462
+
for (let i = 0; i < numRows; i++) {
7463
+
const rw = group.body[i];
7464
+
const row = [];
7465
+
const cellLevel = group.scriptLevel === "text"
7466
+
? StyleLevel.TEXT
7467
+
: group.scriptLevel === "script"
7468
+
? StyleLevel.SCRIPT
7469
+
: StyleLevel.DISPLAY;
7470
+
7471
+
for (let j = 0; j < rw.length; j++) {
7472
+
const mtd = new mathMLTree.MathNode(
7473
+
"mtd",
7474
+
[buildGroup$1(rw[j], style.withLevel(cellLevel))]
7475
+
);
7476
+
7477
+
if (group.envClasses.includes("multline")) {
7478
+
const align = i === 0 ? "left" : i === numRows - 1 ? "right" : "center";
7479
+
mtd.setAttribute("columnalign", align);
7480
+
if (align !== "center") {
7481
+
mtd.classes.push("tml-" + align);
7482
+
}
7483
+
}
7484
+
row.push(mtd);
7485
+
}
7486
+
const numColumns = group.body[0].length;
7487
+
// Fill out a short row with empty <mtd> elements.
7488
+
for (let k = 0; k < numColumns - rw.length; k++) {
7489
+
row.push(new mathMLTree.MathNode("mtd", [], style));
7490
+
}
7491
+
if (group.autoTag) {
7492
+
const tag = group.tags[i];
7493
+
let tagElement;
7494
+
if (tag === true) { // automatic numbering
7495
+
tagElement = new mathMLTree.MathNode("mtext", [new Span(["tml-eqn"])]);
7496
+
} else if (tag === false) {
7497
+
// \nonumber/\notag or starred environment
7498
+
tagElement = new mathMLTree.MathNode("mtext", [], []);
7499
+
} else { // manual \tag
7500
+
tagElement = buildExpressionRow(tag[0].body, style.withLevel(cellLevel), true);
7501
+
tagElement = consolidateText(tagElement);
7502
+
tagElement.classes = ["tml-tag"];
7503
+
}
7504
+
if (tagElement) {
7505
+
row.unshift(glue(group));
7506
+
row.push(glue(group));
7507
+
if (group.leqno) {
7508
+
row[0].children.push(tagElement);
7509
+
row[0].classes.push("tml-left");
7510
+
} else {
7511
+
row[row.length - 1].children.push(tagElement);
7512
+
row[row.length - 1].classes.push("tml-right");
7513
+
}
7514
+
}
7515
+
}
7516
+
const mtr = new mathMLTree.MathNode("mtr", row, []);
7517
+
const label = group.labels.shift();
7518
+
if (label && group.tags && group.tags[i]) {
7519
+
mtr.setAttribute("id", label);
7520
+
if (Array.isArray(group.tags[i])) { mtr.classes.push("tml-tageqn"); }
7521
+
}
7522
+
7523
+
// Write horizontal rules
7524
+
if (i === 0 && hlines[0].length > 0) {
7525
+
if (hlines[0].length === 2) {
7526
+
mtr.children.forEach(cell => { cell.style.borderTop = "0.15em double"; });
7527
+
} else {
7528
+
mtr.children.forEach(cell => {
7529
+
cell.style.borderTop = hlines[0][0] ? "0.06em dashed" : "0.06em solid";
7530
+
});
7531
+
}
7532
+
}
7533
+
if (hlines[i + 1].length > 0) {
7534
+
if (hlines[i + 1].length === 2) {
7535
+
mtr.children.forEach(cell => { cell.style.borderBottom = "0.15em double"; });
7536
+
} else {
7537
+
mtr.children.forEach(cell => {
7538
+
cell.style.borderBottom = hlines[i + 1][0] ? "0.06em dashed" : "0.06em solid";
7539
+
});
7540
+
}
7541
+
}
7542
+
tbl.push(mtr);
7543
+
}
7544
+
7545
+
if (group.envClasses.length > 0) {
7546
+
if (group.arraystretch && group.arraystretch !== 1) {
7547
+
// In LaTeX, \arraystretch is a factor applied to a 12pt strut height.
7548
+
// It defines a baseline to baseline distance.
7549
+
// Here, we do an approximation of that approach.
7550
+
const pad = String(1.4 * group.arraystretch - 0.8) + "ex";
7551
+
for (let i = 0; i < tbl.length; i++) {
7552
+
for (let j = 0; j < tbl[i].children.length; j++) {
7553
+
tbl[i].children[j].style.paddingTop = pad;
7554
+
tbl[i].children[j].style.paddingBottom = pad;
7555
+
}
7556
+
}
7557
+
}
7558
+
let sidePadding = group.envClasses.includes("abut")
7559
+
? "0"
7560
+
: group.envClasses.includes("cases")
7561
+
? "0"
7562
+
: group.envClasses.includes("small")
7563
+
? "0.1389"
7564
+
: group.envClasses.includes("cd")
7565
+
? "0.25"
7566
+
: "0.4"; // default side padding
7567
+
let sidePadUnit = "em";
7568
+
if (group.arraycolsep) {
7569
+
const arraySidePad = calculateSize(group.arraycolsep, style);
7570
+
sidePadding = arraySidePad.number.toFixed(4);
7571
+
sidePadUnit = arraySidePad.unit;
7572
+
}
7573
+
7574
+
const numCols = tbl.length === 0 ? 0 : tbl[0].children.length;
7575
+
7576
+
const sidePad = (j, hand) => {
7577
+
if (j === 0 && hand === 0) { return "0" }
7578
+
if (j === numCols - 1 && hand === 1) { return "0" }
7579
+
if (group.envClasses[0] !== "align") { return sidePadding }
7580
+
if (hand === 1) { return "0" }
7581
+
if (group.autoTag) {
7582
+
return (j % 2) ? "1" : "0"
7583
+
} else {
7584
+
return (j % 2) ? "0" : "1"
7585
+
}
7586
+
};
7587
+
7588
+
// Side padding
7589
+
for (let i = 0; i < tbl.length; i++) {
7590
+
for (let j = 0; j < tbl[i].children.length; j++) {
7591
+
tbl[i].children[j].style.paddingLeft = `${sidePad(j, 0)}${sidePadUnit}`;
7592
+
tbl[i].children[j].style.paddingRight = `${sidePad(j, 1)}${sidePadUnit}`;
7593
+
}
7594
+
}
7595
+
7596
+
// Justification
7597
+
const align = group.envClasses.includes("align") || group.envClasses.includes("alignat");
7598
+
for (let i = 0; i < tbl.length; i++) {
7599
+
const row = tbl[i];
7600
+
if (align) {
7601
+
for (let j = 0; j < row.children.length; j++) {
7602
+
// Chromium does not recognize text-align: left. Use -webkit-
7603
+
// TODO: Remove -webkit- when Chromium no longer needs it.
7604
+
row.children[j].classes = ["tml-" + (j % 2 ? "left" : "right")];
7605
+
}
7606
+
if (group.autoTag) {
7607
+
const k = group.leqno ? 0 : row.children.length - 1;
7608
+
row.children[k].classes = ["tml-" + (group.leqno ? "left" : "right")];
7609
+
}
7610
+
}
7611
+
if (row.children.length > 1 && group.envClasses.includes("cases")) {
7612
+
row.children[1].style.paddingLeft = "1em";
7613
+
}
7614
+
7615
+
if (group.envClasses.includes("cases") || group.envClasses.includes("subarray")) {
7616
+
for (const cell of row.children) {
7617
+
cell.classes.push("tml-left");
7618
+
}
7619
+
}
7620
+
}
7621
+
} else {
7622
+
// Set zero padding on side of the matrix
7623
+
for (let i = 0; i < tbl.length; i++) {
7624
+
tbl[i].children[0].style.paddingLeft = "0em";
7625
+
if (tbl[i].children.length === tbl[0].children.length) {
7626
+
tbl[i].children[tbl[i].children.length - 1].style.paddingRight = "0em";
7627
+
}
7628
+
}
7629
+
}
7630
+
7631
+
let table = new mathMLTree.MathNode("mtable", tbl);
7632
+
if (group.envClasses.length > 0) {
7633
+
// Top & bottom padding
7634
+
if (group.envClasses.includes("jot")) {
7635
+
table.classes.push("tml-jot");
7636
+
} else if (group.envClasses.includes("small")) {
7637
+
table.classes.push("tml-small");
7638
+
}
7639
+
}
7640
+
if (group.scriptLevel === "display") { table.setAttribute("displaystyle", "true"); }
7641
+
7642
+
if (group.autoTag || group.envClasses.includes("multline")) {
7643
+
table.style.width = "100%";
7644
+
}
7645
+
7646
+
// Column separator lines and column alignment
7647
+
let align = "";
7648
+
7649
+
if (group.cols && group.cols.length > 0) {
7650
+
const cols = group.cols;
7651
+
let prevTypeWasAlign = false;
7652
+
let iStart = 0;
7653
+
let iEnd = cols.length;
7654
+
7655
+
while (cols[iStart].type === "separator") {
7656
+
iStart += 1;
7657
+
}
7658
+
while (cols[iEnd - 1].type === "separator") {
7659
+
iEnd -= 1;
7660
+
}
7661
+
7662
+
if (cols[0].type === "separator") {
7663
+
const sep = cols[1].type === "separator"
7664
+
? "0.15em double"
7665
+
: cols[0].separator === "|"
7666
+
? "0.06em solid "
7667
+
: "0.06em dashed ";
7668
+
for (const row of table.children) {
7669
+
row.children[0].style.borderLeft = sep;
7670
+
}
7671
+
}
7672
+
let iCol = group.autoTag ? 0 : -1;
7673
+
for (let i = iStart; i < iEnd; i++) {
7674
+
if (cols[i].type === "align") {
7675
+
const colAlign = alignMap[cols[i].align];
7676
+
align += colAlign;
7677
+
iCol += 1;
7678
+
for (const row of table.children) {
7679
+
if (colAlign.trim() !== "center" && iCol < row.children.length) {
7680
+
row.children[iCol].classes = ["tml-" + colAlign.trim()];
7681
+
}
7682
+
}
7683
+
prevTypeWasAlign = true;
7684
+
} else if (cols[i].type === "separator") {
7685
+
// MathML accepts only single lines between cells.
7686
+
// So we read only the first of consecutive separators.
7687
+
if (prevTypeWasAlign) {
7688
+
const sep = cols[i + 1].type === "separator"
7689
+
? "0.15em double"
7690
+
: cols[i].separator === "|"
7691
+
? "0.06em solid"
7692
+
: "0.06em dashed";
7693
+
for (const row of table.children) {
7694
+
if (iCol < row.children.length) {
7695
+
row.children[iCol].style.borderRight = sep;
7696
+
}
7697
+
}
7698
+
}
7699
+
prevTypeWasAlign = false;
7700
+
}
7701
+
}
7702
+
if (cols[cols.length - 1].type === "separator") {
7703
+
const sep = cols[cols.length - 2].type === "separator"
7704
+
? "0.15em double"
7705
+
: cols[cols.length - 1].separator === "|"
7706
+
? "0.06em solid"
7707
+
: "0.06em dashed";
7708
+
for (const row of table.children) {
7709
+
row.children[row.children.length - 1].style.borderRight = sep;
7710
+
row.children[row.children.length - 1].style.paddingRight = "0.4em";
7711
+
}
7712
+
}
7713
+
}
7714
+
if (group.autoTag) {
7715
+
// allow for glue cells on each side
7716
+
align = "left " + (align.length > 0 ? align : "center ") + "right ";
7717
+
}
7718
+
if (align) {
7719
+
// Firefox reads this attribute, not the -webkit-left|right written above.
7720
+
// TODO: When Chrome no longer needs "-webkit-", use CSS and delete the next line.
7721
+
table.setAttribute("columnalign", align.trim());
7722
+
}
7723
+
7724
+
if (group.envClasses.includes("small")) {
7725
+
// A small array. Wrap in scriptstyle.
7726
+
table = new mathMLTree.MathNode("mstyle", [table]);
7727
+
table.setAttribute("scriptlevel", "1");
7728
+
}
7729
+
7730
+
return table
7731
+
};
7732
+
7733
+
// Convenience function for align, align*, aligned, alignat, alignat*, alignedat, split.
7734
+
const alignedHandler = function(context, args) {
7735
+
if (context.envName.indexOf("ed") === -1) {
7736
+
validateAmsEnvironmentContext(context);
7737
+
}
7738
+
const isSplit = context.envName === "split";
7739
+
const cols = [];
7740
+
const res = parseArray(
7741
+
context.parser,
7742
+
{
7743
+
cols,
7744
+
emptySingleRow: true,
7745
+
autoTag: isSplit ? undefined : getAutoTag(context.envName),
7746
+
envClasses: ["abut", "jot"], // set row spacing & provisional column spacing
7747
+
maxNumCols: context.envName === "split" ? 2 : undefined,
7748
+
leqno: context.parser.settings.leqno
7749
+
},
7750
+
"display"
7751
+
);
7752
+
7753
+
// Determining number of columns.
7754
+
// 1. If the first argument is given, we use it as a number of columns,
7755
+
// and makes sure that each row doesn't exceed that number.
7756
+
// 2. Otherwise, just count number of columns = maximum number
7757
+
// of cells in each row ("aligned" mode -- isAligned will be true).
7758
+
//
7759
+
// At the same time, prepend empty group {} at beginning of every second
7760
+
// cell in each row (starting with second cell) so that operators become
7761
+
// binary. This behavior is implemented in amsmath's \start@aligned.
7762
+
let numMaths;
7763
+
let numCols = 0;
7764
+
const isAlignedAt = context.envName.indexOf("at") > -1;
7765
+
if (args[0] && isAlignedAt) {
7766
+
// alignat environment takes an argument w/ number of columns
7767
+
let arg0 = "";
7768
+
for (let i = 0; i < args[0].body.length; i++) {
7769
+
const textord = assertNodeType(args[0].body[i], "textord");
7770
+
arg0 += textord.text;
7771
+
}
7772
+
if (isNaN(arg0)) {
7773
+
throw new ParseError("The alignat enviroment requires a numeric first argument.")
7774
+
}
7775
+
numMaths = Number(arg0);
7776
+
numCols = numMaths * 2;
7777
+
}
7778
+
res.body.forEach(function(row) {
7779
+
if (isAlignedAt) {
7780
+
// Case 1
7781
+
const curMaths = row.length / 2;
7782
+
if (numMaths < curMaths) {
7783
+
throw new ParseError(
7784
+
"Too many math in a row: " + `expected ${numMaths}, but got ${curMaths}`,
7785
+
row[0]
7786
+
);
7787
+
}
7788
+
} else if (numCols < row.length) {
7789
+
// Case 2
7790
+
numCols = row.length;
7791
+
}
7792
+
});
7793
+
7794
+
// Adjusting alignment.
7795
+
// In aligned mode, we add one \qquad between columns;
7796
+
// otherwise we add nothing.
7797
+
for (let i = 0; i < numCols; ++i) {
7798
+
let align = "r";
7799
+
if (i % 2 === 1) {
7800
+
align = "l";
7801
+
}
7802
+
cols[i] = {
7803
+
type: "align",
7804
+
align: align
7805
+
};
7806
+
}
7807
+
if (context.envName === "split") ; else if (isAlignedAt) {
7808
+
res.envClasses.push("alignat"); // Sets justification
7809
+
} else {
7810
+
res.envClasses[0] = "align"; // Sets column spacing & justification
7811
+
}
7812
+
return res;
7813
+
};
7814
+
7815
+
// Arrays are part of LaTeX, defined in lttab.dtx so its documentation
7816
+
// is part of the source2e.pdf file of LaTeX2e source documentation.
7817
+
// {darray} is an {array} environment where cells are set in \displaystyle,
7818
+
// as defined in nccmath.sty.
7819
+
defineEnvironment({
7820
+
type: "array",
7821
+
names: ["array", "darray"],
7822
+
props: {
7823
+
numArgs: 1
7824
+
},
7825
+
handler(context, args) {
7826
+
// Since no types are specified above, the two possibilities are
7827
+
// - The argument is wrapped in {} or [], in which case Parser's
7828
+
// parseGroup() returns an "ordgroup" wrapping some symbol node.
7829
+
// - The argument is a bare symbol node.
7830
+
const symNode = checkSymbolNodeType(args[0]);
7831
+
const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body;
7832
+
const cols = colalign.map(function(nde) {
7833
+
const node = assertSymbolNodeType(nde);
7834
+
const ca = node.text;
7835
+
if ("lcr".indexOf(ca) !== -1) {
7836
+
return {
7837
+
type: "align",
7838
+
align: ca
7839
+
};
7840
+
} else if (ca === "|") {
7841
+
return {
7842
+
type: "separator",
7843
+
separator: "|"
7844
+
};
7845
+
} else if (ca === ":") {
7846
+
return {
7847
+
type: "separator",
7848
+
separator: ":"
7849
+
};
7850
+
}
7851
+
throw new ParseError("Unknown column alignment: " + ca, nde);
7852
+
});
7853
+
const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros);
7854
+
const res = {
7855
+
cols,
7856
+
envClasses: ["array"],
7857
+
maxNumCols: cols.length,
7858
+
arraystretch,
7859
+
arraycolsep
7860
+
};
7861
+
return parseArray(context.parser, res, dCellStyle(context.envName));
7862
+
},
7863
+
mathmlBuilder: mathmlBuilder$7
7864
+
});
7865
+
7866
+
// The matrix environments of amsmath builds on the array environment
7867
+
// of LaTeX, which is discussed above.
7868
+
// The mathtools package adds starred versions of the same environments.
7869
+
// These have an optional argument to choose left|center|right justification.
7870
+
defineEnvironment({
7871
+
type: "array",
7872
+
names: [
7873
+
"matrix",
7874
+
"pmatrix",
7875
+
"bmatrix",
7876
+
"Bmatrix",
7877
+
"vmatrix",
7878
+
"Vmatrix",
7879
+
"matrix*",
7880
+
"pmatrix*",
7881
+
"bmatrix*",
7882
+
"Bmatrix*",
7883
+
"vmatrix*",
7884
+
"Vmatrix*"
7885
+
],
7886
+
props: {
7887
+
numArgs: 0
7888
+
},
7889
+
handler(context) {
7890
+
const delimiters = {
7891
+
matrix: null,
7892
+
pmatrix: ["(", ")"],
7893
+
bmatrix: ["[", "]"],
7894
+
Bmatrix: ["\\{", "\\}"],
7895
+
vmatrix: ["|", "|"],
7896
+
Vmatrix: ["\\Vert", "\\Vert"]
7897
+
}[context.envName.replace("*", "")];
7898
+
// \hskip -\arraycolsep in amsmath
7899
+
let colAlign = "c";
7900
+
const payload = {
7901
+
envClasses: [],
7902
+
cols: []
7903
+
};
7904
+
if (context.envName.charAt(context.envName.length - 1) === "*") {
7905
+
// It's one of the mathtools starred functions.
7906
+
// Parse the optional alignment argument.
7907
+
const parser = context.parser;
7908
+
parser.consumeSpaces();
7909
+
if (parser.fetch().text === "[") {
7910
+
parser.consume();
7911
+
parser.consumeSpaces();
7912
+
colAlign = parser.fetch().text;
7913
+
if ("lcr".indexOf(colAlign) === -1) {
7914
+
throw new ParseError("Expected l or c or r", parser.nextToken);
7915
+
}
7916
+
parser.consume();
7917
+
parser.consumeSpaces();
7918
+
parser.expect("]");
7919
+
parser.consume();
7920
+
payload.cols = [];
7921
+
}
7922
+
}
7923
+
const res = parseArray(context.parser, payload, "text");
7924
+
res.cols = new Array(res.body[0].length).fill({ type: "align", align: colAlign });
7925
+
const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros);
7926
+
return delimiters
7927
+
? {
7928
+
type: "leftright",
7929
+
mode: context.mode,
7930
+
body: [res],
7931
+
left: delimiters[0],
7932
+
right: delimiters[1],
7933
+
rightColor: undefined, // \right uninfluenced by \color in array
7934
+
arraystretch,
7935
+
arraycolsep
7936
+
}
7937
+
: res;
7938
+
},
7939
+
mathmlBuilder: mathmlBuilder$7
7940
+
});
7941
+
7942
+
defineEnvironment({
7943
+
type: "array",
7944
+
names: ["smallmatrix"],
7945
+
props: {
7946
+
numArgs: 0
7947
+
},
7948
+
handler(context) {
7949
+
const payload = { type: "small" };
7950
+
const res = parseArray(context.parser, payload, "script");
7951
+
res.envClasses = ["small"];
7952
+
return res;
7953
+
},
7954
+
mathmlBuilder: mathmlBuilder$7
7955
+
});
7956
+
7957
+
defineEnvironment({
7958
+
type: "array",
7959
+
names: ["subarray"],
7960
+
props: {
7961
+
numArgs: 1
7962
+
},
7963
+
handler(context, args) {
7964
+
// Parsing of {subarray} is similar to {array}
7965
+
const symNode = checkSymbolNodeType(args[0]);
7966
+
const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body;
7967
+
const cols = colalign.map(function(nde) {
7968
+
const node = assertSymbolNodeType(nde);
7969
+
const ca = node.text;
7970
+
// {subarray} only recognizes "l" & "c"
7971
+
if ("lc".indexOf(ca) !== -1) {
7972
+
return {
7973
+
type: "align",
7974
+
align: ca
7975
+
};
7976
+
}
7977
+
throw new ParseError("Unknown column alignment: " + ca, nde);
7978
+
});
7979
+
if (cols.length > 1) {
7980
+
throw new ParseError("{subarray} can contain only one column");
7981
+
}
7982
+
let res = {
7983
+
cols,
7984
+
envClasses: ["small"]
7985
+
};
7986
+
res = parseArray(context.parser, res, "script");
7987
+
if (res.body.length > 0 && res.body[0].length > 1) {
7988
+
throw new ParseError("{subarray} can contain only one column");
7989
+
}
7990
+
return res;
7991
+
},
7992
+
mathmlBuilder: mathmlBuilder$7
7993
+
});
7994
+
7995
+
// A cases environment (in amsmath.sty) is almost equivalent to
7996
+
// \def
7997
+
// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
7998
+
// {dcases} is a {cases} environment where cells are set in \displaystyle,
7999
+
// as defined in mathtools.sty.
8000
+
// {rcases} is another mathtools environment. It's brace is on the right side.
8001
+
defineEnvironment({
8002
+
type: "array",
8003
+
names: ["cases", "dcases", "rcases", "drcases"],
8004
+
props: {
8005
+
numArgs: 0
8006
+
},
8007
+
handler(context) {
8008
+
const payload = {
8009
+
cols: [],
8010
+
envClasses: ["cases"]
8011
+
};
8012
+
const res = parseArray(context.parser, payload, dCellStyle(context.envName));
8013
+
return {
8014
+
type: "leftright",
8015
+
mode: context.mode,
8016
+
body: [res],
8017
+
left: context.envName.indexOf("r") > -1 ? "." : "\\{",
8018
+
right: context.envName.indexOf("r") > -1 ? "\\}" : ".",
8019
+
rightColor: undefined
8020
+
};
8021
+
},
8022
+
mathmlBuilder: mathmlBuilder$7
8023
+
});
8024
+
8025
+
// In the align environment, one uses ampersands, &, to specify number of
8026
+
// columns in each row, and to locate spacing between each column.
8027
+
// align gets automatic numbering. align* and aligned do not.
8028
+
// The alignedat environment can be used in math mode.
8029
+
defineEnvironment({
8030
+
type: "array",
8031
+
names: ["align", "align*", "aligned", "split"],
8032
+
props: {
8033
+
numArgs: 0
8034
+
},
8035
+
handler: alignedHandler,
8036
+
mathmlBuilder: mathmlBuilder$7
8037
+
});
8038
+
8039
+
// alignat environment is like an align environment, but one must explicitly
8040
+
// specify maximum number of columns in each row, and can adjust where spacing occurs.
8041
+
defineEnvironment({
8042
+
type: "array",
8043
+
names: ["alignat", "alignat*", "alignedat"],
8044
+
props: {
8045
+
numArgs: 1
8046
+
},
8047
+
handler: alignedHandler,
8048
+
mathmlBuilder: mathmlBuilder$7
8049
+
});
8050
+
8051
+
// A gathered environment is like an array environment with one centered
8052
+
// column, but where rows are considered lines so get \jot line spacing
8053
+
// and contents are set in \displaystyle.
8054
+
defineEnvironment({
8055
+
type: "array",
8056
+
names: ["gathered", "gather", "gather*"],
8057
+
props: {
8058
+
numArgs: 0
8059
+
},
8060
+
handler(context) {
8061
+
if (context.envName !== "gathered") {
8062
+
validateAmsEnvironmentContext(context);
8063
+
}
8064
+
const res = {
8065
+
cols: [],
8066
+
envClasses: ["abut", "jot"],
8067
+
autoTag: getAutoTag(context.envName),
8068
+
emptySingleRow: true,
8069
+
leqno: context.parser.settings.leqno
8070
+
};
8071
+
return parseArray(context.parser, res, "display");
8072
+
},
8073
+
mathmlBuilder: mathmlBuilder$7
8074
+
});
8075
+
8076
+
defineEnvironment({
8077
+
type: "array",
8078
+
names: ["equation", "equation*"],
8079
+
props: {
8080
+
numArgs: 0
8081
+
},
8082
+
handler(context) {
8083
+
validateAmsEnvironmentContext(context);
8084
+
const res = {
8085
+
autoTag: getAutoTag(context.envName),
8086
+
emptySingleRow: true,
8087
+
singleRow: true,
8088
+
maxNumCols: 1,
8089
+
envClasses: ["align"],
8090
+
leqno: context.parser.settings.leqno
8091
+
};
8092
+
return parseArray(context.parser, res, "display");
8093
+
},
8094
+
mathmlBuilder: mathmlBuilder$7
8095
+
});
8096
+
8097
+
defineEnvironment({
8098
+
type: "array",
8099
+
names: ["multline", "multline*"],
8100
+
props: {
8101
+
numArgs: 0
8102
+
},
8103
+
handler(context) {
8104
+
validateAmsEnvironmentContext(context);
8105
+
const res = {
8106
+
autoTag: context.envName === "multline",
8107
+
maxNumCols: 1,
8108
+
envClasses: ["jot", "multline"],
8109
+
leqno: context.parser.settings.leqno
8110
+
};
8111
+
return parseArray(context.parser, res, "display");
8112
+
},
8113
+
mathmlBuilder: mathmlBuilder$7
8114
+
});
8115
+
8116
+
defineEnvironment({
8117
+
type: "array",
8118
+
names: ["CD"],
8119
+
props: {
8120
+
numArgs: 0
8121
+
},
8122
+
handler(context) {
8123
+
validateAmsEnvironmentContext(context);
8124
+
return parseCD(context.parser);
8125
+
},
8126
+
mathmlBuilder: mathmlBuilder$7
8127
+
});
8128
+
8129
+
// Catch \hline outside array environment
8130
+
defineFunction({
8131
+
type: "text", // Doesn't matter what this is.
8132
+
names: ["\\hline", "\\hdashline"],
8133
+
props: {
8134
+
numArgs: 0,
8135
+
allowedInText: true,
8136
+
allowedInMath: true
8137
+
},
8138
+
handler(context, args) {
8139
+
throw new ParseError(`${context.funcName} valid only within array environment`);
8140
+
}
8141
+
});
8142
+
8143
+
const environments = _environments;
8144
+
8145
+
// Environment delimiters. HTML/MathML rendering is defined in the corresponding
8146
+
// defineEnvironment definitions.
8147
+
defineFunction({
8148
+
type: "environment",
8149
+
names: ["\\begin", "\\end"],
8150
+
props: {
8151
+
numArgs: 1,
8152
+
argTypes: ["text"]
8153
+
},
8154
+
handler({ parser, funcName }, args) {
8155
+
const nameGroup = args[0];
8156
+
if (nameGroup.type !== "ordgroup") {
8157
+
throw new ParseError("Invalid environment name", nameGroup);
8158
+
}
8159
+
let envName = "";
8160
+
for (let i = 0; i < nameGroup.body.length; ++i) {
8161
+
envName += assertNodeType(nameGroup.body[i], "textord").text;
8162
+
}
8163
+
8164
+
if (funcName === "\\begin") {
8165
+
// begin...end is similar to left...right
8166
+
if (!Object.prototype.hasOwnProperty.call(environments, envName )) {
8167
+
throw new ParseError("No such environment: " + envName, nameGroup);
8168
+
}
8169
+
// Build the environment object. Arguments and other information will
8170
+
// be made available to the begin and end methods using properties.
8171
+
const env = environments[envName];
8172
+
const { args, optArgs } = parser.parseArguments("\\begin{" + envName + "}", env);
8173
+
const context = {
8174
+
mode: parser.mode,
8175
+
envName,
8176
+
parser
8177
+
};
8178
+
const result = env.handler(context, args, optArgs);
8179
+
parser.expect("\\end", false);
8180
+
const endNameToken = parser.nextToken;
8181
+
const end = assertNodeType(parser.parseFunction(), "environment");
8182
+
if (end.name !== envName) {
8183
+
throw new ParseError(
8184
+
`Mismatch: \\begin{${envName}} matched by \\end{${end.name}}`,
8185
+
endNameToken
8186
+
);
8187
+
}
8188
+
return result;
8189
+
}
8190
+
8191
+
return {
8192
+
type: "environment",
8193
+
mode: parser.mode,
8194
+
name: envName,
8195
+
nameGroup
8196
+
};
8197
+
}
8198
+
});
8199
+
8200
+
defineFunction({
8201
+
type: "envTag",
8202
+
names: ["\\env@tag"],
8203
+
props: {
8204
+
numArgs: 1,
8205
+
argTypes: ["math"]
8206
+
},
8207
+
handler({ parser }, args) {
8208
+
return {
8209
+
type: "envTag",
8210
+
mode: parser.mode,
8211
+
body: args[0]
8212
+
};
8213
+
},
8214
+
mathmlBuilder(group, style) {
8215
+
return new mathMLTree.MathNode("mrow");
8216
+
}
8217
+
});
8218
+
8219
+
defineFunction({
8220
+
type: "noTag",
8221
+
names: ["\\env@notag"],
8222
+
props: {
8223
+
numArgs: 0
8224
+
},
8225
+
handler({ parser }) {
8226
+
return {
8227
+
type: "noTag",
8228
+
mode: parser.mode
8229
+
};
8230
+
},
8231
+
mathmlBuilder(group, style) {
8232
+
return new mathMLTree.MathNode("mrow");
8233
+
}
8234
+
});
8235
+
8236
+
const isLongVariableName = (group, font) => {
8237
+
if (font !== "mathrm" || group.body.type !== "ordgroup" || group.body.body.length === 1) {
8238
+
return false
8239
+
}
8240
+
if (group.body.body[0].type !== "mathord") { return false }
8241
+
for (let i = 1; i < group.body.body.length; i++) {
8242
+
const parseNodeType = group.body.body[i].type;
8243
+
if (!(parseNodeType === "mathord" ||
8244
+
(parseNodeType === "textord" && !isNaN(group.body.body[i].text)))) {
8245
+
return false
8246
+
}
8247
+
}
8248
+
return true
8249
+
};
8250
+
8251
+
const mathmlBuilder$6 = (group, style) => {
8252
+
const font = group.font;
8253
+
const newStyle = style.withFont(font);
8254
+
const mathGroup = buildGroup$1(group.body, newStyle);
8255
+
8256
+
if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
8257
+
if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
8258
+
mathGroup.style.fontWeight = "bold";
8259
+
return mathGroup
8260
+
}
8261
+
// Check if it is possible to consolidate elements into a single <mi> element.
8262
+
if (isLongVariableName(group, font)) {
8263
+
// This is a \mathrm{…} group. It gets special treatment because symbolsOrd.js
8264
+
// wraps <mi> elements with <mrow>s to work around a Firefox bug.
8265
+
const mi = mathGroup.children[0].children[0];
8266
+
delete mi.attributes.mathvariant;
8267
+
for (let i = 1; i < mathGroup.children.length; i++) {
8268
+
mi.children[0].text += mathGroup.children[i].type === "mn"
8269
+
? mathGroup.children[i].children[0].text
8270
+
: mathGroup.children[i].children[0].children[0].text;
8271
+
}
8272
+
// Wrap in a <mrow> to prevent the same Firefox bug.
8273
+
const bogus = new mathMLTree.MathNode("mtext", new mathMLTree.TextNode("\u200b"));
8274
+
return new mathMLTree.MathNode("mrow", [bogus, mi])
8275
+
}
8276
+
let canConsolidate = mathGroup.children[0].type === "mo";
8277
+
for (let i = 1; i < mathGroup.children.length; i++) {
8278
+
if (mathGroup.children[i].type === "mo" && font === "boldsymbol") {
8279
+
mathGroup.children[i].style.fontWeight = "bold";
8280
+
}
8281
+
if (mathGroup.children[i].type !== "mi") { canConsolidate = false; }
8282
+
const localVariant = mathGroup.children[i].attributes &&
8283
+
mathGroup.children[i].attributes.mathvariant || "";
8284
+
if (localVariant !== "normal") { canConsolidate = false; }
8285
+
}
8286
+
if (!canConsolidate) { return mathGroup }
8287
+
// Consolidate the <mi> elements.
8288
+
const mi = mathGroup.children[0];
8289
+
for (let i = 1; i < mathGroup.children.length; i++) {
8290
+
mi.children.push(mathGroup.children[i].children[0]);
8291
+
}
8292
+
if (mi.attributes.mathvariant && mi.attributes.mathvariant === "normal") {
8293
+
// Workaround for a Firefox bug that renders spurious space around
8294
+
// a <mi mathvariant="normal">
8295
+
// Ref: https://bugs.webkit.org/show_bug.cgi?id=129097
8296
+
// We insert a text node that contains a zero-width space and wrap in an mrow.
8297
+
// TODO: Get rid of this <mi> workaround when the Firefox bug is fixed.
8298
+
const bogus = new mathMLTree.MathNode("mtext", new mathMLTree.TextNode("\u200b"));
8299
+
return new mathMLTree.MathNode("mrow", [bogus, mi])
8300
+
}
8301
+
return mi
8302
+
};
8303
+
8304
+
const fontAliases = {
8305
+
"\\Bbb": "\\mathbb",
8306
+
"\\bold": "\\mathbf",
8307
+
"\\frak": "\\mathfrak",
8308
+
"\\bm": "\\boldsymbol"
8309
+
};
8310
+
8311
+
defineFunction({
8312
+
type: "font",
8313
+
names: [
8314
+
// styles
8315
+
"\\mathrm",
8316
+
"\\mathit",
8317
+
"\\mathbf",
8318
+
"\\mathnormal",
8319
+
"\\up@greek",
8320
+
"\\boldsymbol",
8321
+
8322
+
// families
8323
+
"\\mathbb",
8324
+
"\\mathcal",
8325
+
"\\mathfrak",
8326
+
"\\mathscr",
8327
+
"\\mathsf",
8328
+
"\\mathsfit",
8329
+
"\\mathtt",
8330
+
8331
+
// aliases
8332
+
"\\Bbb",
8333
+
"\\bm",
8334
+
"\\bold",
8335
+
"\\frak"
8336
+
],
8337
+
props: {
8338
+
numArgs: 1,
8339
+
allowedInArgument: true
8340
+
},
8341
+
handler: ({ parser, funcName }, args) => {
8342
+
const body = normalizeArgument(args[0]);
8343
+
let func = funcName;
8344
+
if (func in fontAliases) {
8345
+
func = fontAliases[func];
8346
+
}
8347
+
return {
8348
+
type: "font",
8349
+
mode: parser.mode,
8350
+
font: func.slice(1),
8351
+
body
8352
+
};
8353
+
},
8354
+
mathmlBuilder: mathmlBuilder$6
8355
+
});
8356
+
8357
+
// Old font changing functions
8358
+
defineFunction({
8359
+
type: "font",
8360
+
names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"],
8361
+
props: {
8362
+
numArgs: 0,
8363
+
allowedInText: true
8364
+
},
8365
+
handler: ({ parser, funcName, breakOnTokenText }, args) => {
8366
+
const { mode } = parser;
8367
+
const body = parser.parseExpression(true, breakOnTokenText, true);
8368
+
const fontStyle = `math${funcName.slice(1)}`;
8369
+
8370
+
return {
8371
+
type: "font",
8372
+
mode: mode,
8373
+
font: fontStyle,
8374
+
body: {
8375
+
type: "ordgroup",
8376
+
mode: parser.mode,
8377
+
body
8378
+
}
8379
+
};
8380
+
},
8381
+
mathmlBuilder: mathmlBuilder$6
8382
+
});
8383
+
8384
+
const stylArray = ["display", "text", "script", "scriptscript"];
8385
+
const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 };
8386
+
8387
+
const mathmlBuilder$5 = (group, style) => {
8388
+
// Track the scriptLevel of the numerator and denominator.
8389
+
// We may need that info for \mathchoice or for adjusting em dimensions.
8390
+
const childOptions = group.scriptLevel === "auto"
8391
+
? style.incrementLevel()
8392
+
: group.scriptLevel === "display"
8393
+
? style.withLevel(StyleLevel.TEXT)
8394
+
: group.scriptLevel === "text"
8395
+
? style.withLevel(StyleLevel.SCRIPT)
8396
+
: style.withLevel(StyleLevel.SCRIPTSCRIPT);
8397
+
8398
+
// Chromium (wrongly) continues to shrink fractions beyond scriptscriptlevel.
8399
+
// So we check for levels that Chromium shrinks too small.
8400
+
// If necessary, set an explicit fraction depth.
8401
+
const numer = buildGroup$1(group.numer, childOptions);
8402
+
const denom = buildGroup$1(group.denom, childOptions);
8403
+
if (style.level === 3) {
8404
+
numer.style.mathDepth = "2";
8405
+
numer.setAttribute("scriptlevel", "2");
8406
+
denom.style.mathDepth = "2";
8407
+
denom.setAttribute("scriptlevel", "2");
8408
+
}
8409
+
8410
+
let node = new mathMLTree.MathNode("mfrac", [numer, denom]);
8411
+
8412
+
if (!group.hasBarLine) {
8413
+
node.setAttribute("linethickness", "0px");
8414
+
} else if (group.barSize) {
8415
+
const ruleWidth = calculateSize(group.barSize, style);
8416
+
node.setAttribute("linethickness", ruleWidth.number + ruleWidth.unit);
8417
+
}
8418
+
8419
+
if (group.leftDelim != null || group.rightDelim != null) {
8420
+
const withDelims = [];
8421
+
8422
+
if (group.leftDelim != null) {
8423
+
const leftOp = new mathMLTree.MathNode("mo", [
8424
+
new mathMLTree.TextNode(group.leftDelim.replace("\\", ""))
8425
+
]);
8426
+
leftOp.setAttribute("fence", "true");
8427
+
withDelims.push(leftOp);
8428
+
}
8429
+
8430
+
withDelims.push(node);
8431
+
8432
+
if (group.rightDelim != null) {
8433
+
const rightOp = new mathMLTree.MathNode("mo", [
8434
+
new mathMLTree.TextNode(group.rightDelim.replace("\\", ""))
8435
+
]);
8436
+
rightOp.setAttribute("fence", "true");
8437
+
withDelims.push(rightOp);
8438
+
}
8439
+
8440
+
node = makeRow(withDelims);
8441
+
}
8442
+
8443
+
if (group.scriptLevel !== "auto") {
8444
+
node = new mathMLTree.MathNode("mstyle", [node]);
8445
+
node.setAttribute("displaystyle", String(group.scriptLevel === "display"));
8446
+
node.setAttribute("scriptlevel", scriptLevel[group.scriptLevel]);
8447
+
}
8448
+
8449
+
return node;
8450
+
};
8451
+
8452
+
defineFunction({
8453
+
type: "genfrac",
8454
+
names: [
8455
+
"\\dfrac",
8456
+
"\\frac",
8457
+
"\\tfrac",
8458
+
"\\dbinom",
8459
+
"\\binom",
8460
+
"\\tbinom",
8461
+
"\\\\atopfrac", // can’t be entered directly
8462
+
"\\\\bracefrac",
8463
+
"\\\\brackfrac" // ditto
8464
+
],
8465
+
props: {
8466
+
numArgs: 2,
8467
+
allowedInArgument: true
8468
+
},
8469
+
handler: ({ parser, funcName }, args) => {
8470
+
const numer = args[0];
8471
+
const denom = args[1];
8472
+
let hasBarLine = false;
8473
+
let leftDelim = null;
8474
+
let rightDelim = null;
8475
+
let scriptLevel = "auto";
8476
+
8477
+
switch (funcName) {
8478
+
case "\\dfrac":
8479
+
case "\\frac":
8480
+
case "\\tfrac":
8481
+
hasBarLine = true;
8482
+
break;
8483
+
case "\\\\atopfrac":
8484
+
hasBarLine = false;
8485
+
break;
8486
+
case "\\dbinom":
8487
+
case "\\binom":
8488
+
case "\\tbinom":
8489
+
leftDelim = "(";
8490
+
rightDelim = ")";
8491
+
break;
8492
+
case "\\\\bracefrac":
8493
+
leftDelim = "\\{";
8494
+
rightDelim = "\\}";
8495
+
break;
8496
+
case "\\\\brackfrac":
8497
+
leftDelim = "[";
8498
+
rightDelim = "]";
8499
+
break;
8500
+
default:
8501
+
throw new Error("Unrecognized genfrac command");
8502
+
}
8503
+
8504
+
switch (funcName) {
8505
+
case "\\dfrac":
8506
+
case "\\dbinom":
8507
+
scriptLevel = "display";
8508
+
break;
8509
+
case "\\tfrac":
8510
+
case "\\tbinom":
8511
+
scriptLevel = "text";
8512
+
break;
8513
+
}
8514
+
8515
+
return {
8516
+
type: "genfrac",
8517
+
mode: parser.mode,
8518
+
continued: false,
8519
+
numer,
8520
+
denom,
8521
+
hasBarLine,
8522
+
leftDelim,
8523
+
rightDelim,
8524
+
scriptLevel,
8525
+
barSize: null
8526
+
};
8527
+
},
8528
+
mathmlBuilder: mathmlBuilder$5
8529
+
});
8530
+
8531
+
defineFunction({
8532
+
type: "genfrac",
8533
+
names: ["\\cfrac"],
8534
+
props: {
8535
+
numArgs: 2
8536
+
},
8537
+
handler: ({ parser, funcName }, args) => {
8538
+
const numer = args[0];
8539
+
const denom = args[1];
8540
+
8541
+
return {
8542
+
type: "genfrac",
8543
+
mode: parser.mode,
8544
+
continued: true,
8545
+
numer,
8546
+
denom,
8547
+
hasBarLine: true,
8548
+
leftDelim: null,
8549
+
rightDelim: null,
8550
+
scriptLevel: "display",
8551
+
barSize: null
8552
+
};
8553
+
}
8554
+
});
8555
+
8556
+
// Infix generalized fractions -- these are not rendered directly, but replaced
8557
+
// immediately by one of the variants above.
8558
+
defineFunction({
8559
+
type: "infix",
8560
+
names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"],
8561
+
props: {
8562
+
numArgs: 0,
8563
+
infix: true
8564
+
},
8565
+
handler({ parser, funcName, token }) {
8566
+
let replaceWith;
8567
+
switch (funcName) {
8568
+
case "\\over":
8569
+
replaceWith = "\\frac";
8570
+
break;
8571
+
case "\\choose":
8572
+
replaceWith = "\\binom";
8573
+
break;
8574
+
case "\\atop":
8575
+
replaceWith = "\\\\atopfrac";
8576
+
break;
8577
+
case "\\brace":
8578
+
replaceWith = "\\\\bracefrac";
8579
+
break;
8580
+
case "\\brack":
8581
+
replaceWith = "\\\\brackfrac";
8582
+
break;
8583
+
default:
8584
+
throw new Error("Unrecognized infix genfrac command");
8585
+
}
8586
+
return {
8587
+
type: "infix",
8588
+
mode: parser.mode,
8589
+
replaceWith,
8590
+
token
8591
+
};
8592
+
}
8593
+
});
8594
+
8595
+
const delimFromValue = function(delimString) {
8596
+
let delim = null;
8597
+
if (delimString.length > 0) {
8598
+
delim = delimString;
8599
+
delim = delim === "." ? null : delim;
8600
+
}
8601
+
return delim;
8602
+
};
8603
+
8604
+
defineFunction({
8605
+
type: "genfrac",
8606
+
names: ["\\genfrac"],
8607
+
props: {
8608
+
numArgs: 6,
8609
+
allowedInArgument: true,
8610
+
argTypes: ["math", "math", "size", "text", "math", "math"]
8611
+
},
8612
+
handler({ parser }, args) {
8613
+
const numer = args[4];
8614
+
const denom = args[5];
8615
+
8616
+
// Look into the parse nodes to get the desired delimiters.
8617
+
const leftNode = normalizeArgument(args[0]);
8618
+
const leftDelim = leftNode.type === "atom" && leftNode.family === "open"
8619
+
? delimFromValue(leftNode.text)
8620
+
: null;
8621
+
const rightNode = normalizeArgument(args[1]);
8622
+
const rightDelim =
8623
+
rightNode.type === "atom" && rightNode.family === "close"
8624
+
? delimFromValue(rightNode.text)
8625
+
: null;
8626
+
8627
+
const barNode = assertNodeType(args[2], "size");
8628
+
let hasBarLine;
8629
+
let barSize = null;
8630
+
if (barNode.isBlank) {
8631
+
// \genfrac acts differently than \above.
8632
+
// \genfrac treats an empty size group as a signal to use a
8633
+
// standard bar size. \above would see size = 0 and omit the bar.
8634
+
hasBarLine = true;
8635
+
} else {
8636
+
barSize = barNode.value;
8637
+
hasBarLine = barSize.number > 0;
8638
+
}
8639
+
8640
+
// Find out if we want displaystyle, textstyle, etc.
8641
+
let scriptLevel = "auto";
8642
+
let styl = args[3];
8643
+
if (styl.type === "ordgroup") {
8644
+
if (styl.body.length > 0) {
8645
+
const textOrd = assertNodeType(styl.body[0], "textord");
8646
+
scriptLevel = stylArray[Number(textOrd.text)];
8647
+
}
8648
+
} else {
8649
+
styl = assertNodeType(styl, "textord");
8650
+
scriptLevel = stylArray[Number(styl.text)];
8651
+
}
8652
+
8653
+
return {
8654
+
type: "genfrac",
8655
+
mode: parser.mode,
8656
+
numer,
8657
+
denom,
8658
+
continued: false,
8659
+
hasBarLine,
8660
+
barSize,
8661
+
leftDelim,
8662
+
rightDelim,
8663
+
scriptLevel
8664
+
};
8665
+
},
8666
+
mathmlBuilder: mathmlBuilder$5
8667
+
});
8668
+
8669
+
// \above is an infix fraction that also defines a fraction bar size.
8670
+
defineFunction({
8671
+
type: "infix",
8672
+
names: ["\\above"],
8673
+
props: {
8674
+
numArgs: 1,
8675
+
argTypes: ["size"],
8676
+
infix: true
8677
+
},
8678
+
handler({ parser, funcName, token }, args) {
8679
+
return {
8680
+
type: "infix",
8681
+
mode: parser.mode,
8682
+
replaceWith: "\\\\abovefrac",
8683
+
barSize: assertNodeType(args[0], "size").value,
8684
+
token
8685
+
};
8686
+
}
8687
+
});
8688
+
8689
+
defineFunction({
8690
+
type: "genfrac",
8691
+
names: ["\\\\abovefrac"],
8692
+
props: {
8693
+
numArgs: 3,
8694
+
argTypes: ["math", "size", "math"]
8695
+
},
8696
+
handler: ({ parser, funcName }, args) => {
8697
+
const numer = args[0];
8698
+
const barSize = assert(assertNodeType(args[1], "infix").barSize);
8699
+
const denom = args[2];
8700
+
8701
+
const hasBarLine = barSize.number > 0;
8702
+
return {
8703
+
type: "genfrac",
8704
+
mode: parser.mode,
8705
+
numer,
8706
+
denom,
8707
+
continued: false,
8708
+
hasBarLine,
8709
+
barSize,
8710
+
leftDelim: null,
8711
+
rightDelim: null,
8712
+
scriptLevel: "auto"
8713
+
};
8714
+
},
8715
+
8716
+
mathmlBuilder: mathmlBuilder$5
8717
+
});
8718
+
8719
+
// \hbox is provided for compatibility with LaTeX functions that act on a box.
8720
+
// This function by itself doesn't do anything but set scriptlevel to \textstyle
8721
+
// and prevent a soft line break.
8722
+
8723
+
defineFunction({
8724
+
type: "hbox",
8725
+
names: ["\\hbox"],
8726
+
props: {
8727
+
numArgs: 1,
8728
+
argTypes: ["hbox"],
8729
+
allowedInArgument: true,
8730
+
allowedInText: false
8731
+
},
8732
+
handler({ parser }, args) {
8733
+
return {
8734
+
type: "hbox",
8735
+
mode: parser.mode,
8736
+
body: ordargument(args[0])
8737
+
};
8738
+
},
8739
+
mathmlBuilder(group, style) {
8740
+
const newStyle = style.withLevel(StyleLevel.TEXT);
8741
+
const mrow = buildExpressionRow(group.body, newStyle);
8742
+
return consolidateText(mrow)
8743
+
}
8744
+
});
8745
+
8746
+
const mathmlBuilder$4 = (group, style) => {
8747
+
const accentNode = stretchy.mathMLnode(group.label);
8748
+
accentNode.style["math-depth"] = 0;
8749
+
return new mathMLTree.MathNode(group.isOver ? "mover" : "munder", [
8750
+
buildGroup$1(group.base, style),
8751
+
accentNode
8752
+
]);
8753
+
};
8754
+
8755
+
// Horizontal stretchy braces
8756
+
defineFunction({
8757
+
type: "horizBrace",
8758
+
names: ["\\overbrace", "\\underbrace"],
8759
+
props: {
8760
+
numArgs: 1
8761
+
},
8762
+
handler({ parser, funcName }, args) {
8763
+
return {
8764
+
type: "horizBrace",
8765
+
mode: parser.mode,
8766
+
label: funcName,
8767
+
isOver: /^\\over/.test(funcName),
8768
+
base: args[0]
8769
+
};
8770
+
},
8771
+
mathmlBuilder: mathmlBuilder$4
8772
+
});
8773
+
8774
+
defineFunction({
8775
+
type: "href",
8776
+
names: ["\\href"],
8777
+
props: {
8778
+
numArgs: 2,
8779
+
argTypes: ["url", "original"],
8780
+
allowedInText: true
8781
+
},
8782
+
handler: ({ parser, token }, args) => {
8783
+
const body = args[1];
8784
+
const href = assertNodeType(args[0], "url").url;
8785
+
8786
+
if (
8787
+
!parser.settings.isTrusted({
8788
+
command: "\\href",
8789
+
url: href
8790
+
})
8791
+
) {
8792
+
throw new ParseError(`Function "\\href" is not trusted`, token)
8793
+
}
8794
+
8795
+
return {
8796
+
type: "href",
8797
+
mode: parser.mode,
8798
+
href,
8799
+
body: ordargument(body)
8800
+
};
8801
+
},
8802
+
mathmlBuilder: (group, style) => {
8803
+
const math = new MathNode("math", [buildExpressionRow(group.body, style)]);
8804
+
const anchorNode = new AnchorNode(group.href, [], [math]);
8805
+
return anchorNode
8806
+
}
8807
+
});
8808
+
8809
+
defineFunction({
8810
+
type: "href",
8811
+
names: ["\\url"],
8812
+
props: {
8813
+
numArgs: 1,
8814
+
argTypes: ["url"],
8815
+
allowedInText: true
8816
+
},
8817
+
handler: ({ parser, token }, args) => {
8818
+
const href = assertNodeType(args[0], "url").url;
8819
+
8820
+
if (
8821
+
!parser.settings.isTrusted({
8822
+
command: "\\url",
8823
+
url: href
8824
+
})
8825
+
) {
8826
+
throw new ParseError(`Function "\\url" is not trusted`, token)
8827
+
}
8828
+
8829
+
const chars = [];
8830
+
for (let i = 0; i < href.length; i++) {
8831
+
let c = href[i];
8832
+
if (c === "~") {
8833
+
c = "\\textasciitilde";
8834
+
}
8835
+
chars.push({
8836
+
type: "textord",
8837
+
mode: "text",
8838
+
text: c
8839
+
});
8840
+
}
8841
+
const body = {
8842
+
type: "text",
8843
+
mode: parser.mode,
8844
+
font: "\\texttt",
8845
+
body: chars
8846
+
};
8847
+
return {
8848
+
type: "href",
8849
+
mode: parser.mode,
8850
+
href,
8851
+
body: ordargument(body)
8852
+
};
8853
+
}
8854
+
});
8855
+
8856
+
defineFunction({
8857
+
type: "html",
8858
+
names: ["\\class", "\\id", "\\style", "\\data"],
8859
+
props: {
8860
+
numArgs: 2,
8861
+
argTypes: ["raw", "original"],
8862
+
allowedInText: true
8863
+
},
8864
+
handler: ({ parser, funcName, token }, args) => {
8865
+
const value = assertNodeType(args[0], "raw").string;
8866
+
const body = args[1];
8867
+
8868
+
if (parser.settings.strict) {
8869
+
throw new ParseError(`Function "${funcName}" is disabled in strict mode`, token)
8870
+
}
8871
+
8872
+
let trustContext;
8873
+
const attributes = {};
8874
+
8875
+
switch (funcName) {
8876
+
case "\\class":
8877
+
attributes.class = value;
8878
+
trustContext = {
8879
+
command: "\\class",
8880
+
class: value
8881
+
};
8882
+
break;
8883
+
case "\\id":
8884
+
attributes.id = value;
8885
+
trustContext = {
8886
+
command: "\\id",
8887
+
id: value
8888
+
};
8889
+
break;
8890
+
case "\\style":
8891
+
attributes.style = value;
8892
+
trustContext = {
8893
+
command: "\\style",
8894
+
style: value
8895
+
};
8896
+
break;
8897
+
case "\\data": {
8898
+
const data = value.split(",");
8899
+
for (let i = 0; i < data.length; i++) {
8900
+
const keyVal = data[i].split("=");
8901
+
if (keyVal.length !== 2) {
8902
+
throw new ParseError("Error parsing key-value for \\data");
8903
+
}
8904
+
attributes["data-" + keyVal[0].trim()] = keyVal[1].trim();
8905
+
}
8906
+
8907
+
trustContext = {
8908
+
command: "\\data",
8909
+
attributes
8910
+
};
8911
+
break;
8912
+
}
8913
+
default:
8914
+
throw new Error("Unrecognized html command");
8915
+
}
8916
+
8917
+
if (!parser.settings.isTrusted(trustContext)) {
8918
+
throw new ParseError(`Function "${funcName}" is not trusted`, token)
8919
+
}
8920
+
return {
8921
+
type: "html",
8922
+
mode: parser.mode,
8923
+
attributes,
8924
+
body: ordargument(body)
8925
+
};
8926
+
},
8927
+
mathmlBuilder: (group, style) => {
8928
+
const element = buildExpressionRow(group.body, style);
8929
+
8930
+
const classes = [];
8931
+
if (group.attributes.class) {
8932
+
classes.push(...group.attributes.class.trim().split(/\s+/));
8933
+
}
8934
+
element.classes = classes;
8935
+
8936
+
for (const attr in group.attributes) {
8937
+
if (attr !== "class" && Object.prototype.hasOwnProperty.call(group.attributes, attr)) {
8938
+
element.setAttribute(attr, group.attributes[attr]);
8939
+
}
8940
+
}
8941
+
8942
+
return element;
8943
+
}
8944
+
});
8945
+
8946
+
const sizeData = function(str) {
8947
+
if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) {
8948
+
// str is a number with no unit specified.
8949
+
// default unit is bp, per graphix package.
8950
+
return { number: +str, unit: "bp" }
8951
+
} else {
8952
+
const match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(str);
8953
+
if (!match) {
8954
+
throw new ParseError("Invalid size: '" + str + "' in \\includegraphics");
8955
+
}
8956
+
const data = {
8957
+
number: +(match[1] + match[2]), // sign + magnitude, cast to number
8958
+
unit: match[3]
8959
+
};
8960
+
if (!validUnit(data)) {
8961
+
throw new ParseError("Invalid unit: '" + data.unit + "' in \\includegraphics.");
8962
+
}
8963
+
return data
8964
+
}
8965
+
};
8966
+
8967
+
defineFunction({
8968
+
type: "includegraphics",
8969
+
names: ["\\includegraphics"],
8970
+
props: {
8971
+
numArgs: 1,
8972
+
numOptionalArgs: 1,
8973
+
argTypes: ["raw", "url"],
8974
+
allowedInText: false
8975
+
},
8976
+
handler: ({ parser, token }, args, optArgs) => {
8977
+
let width = { number: 0, unit: "em" };
8978
+
let height = { number: 0.9, unit: "em" }; // sorta character sized.
8979
+
let totalheight = { number: 0, unit: "em" };
8980
+
let alt = "";
8981
+
8982
+
if (optArgs[0]) {
8983
+
const attributeStr = assertNodeType(optArgs[0], "raw").string;
8984
+
8985
+
// Parser.js does not parse key/value pairs. We get a string.
8986
+
const attributes = attributeStr.split(",");
8987
+
for (let i = 0; i < attributes.length; i++) {
8988
+
const keyVal = attributes[i].split("=");
8989
+
if (keyVal.length === 2) {
8990
+
const str = keyVal[1].trim();
8991
+
switch (keyVal[0].trim()) {
8992
+
case "alt":
8993
+
alt = str;
8994
+
break
8995
+
case "width":
8996
+
width = sizeData(str);
8997
+
break
8998
+
case "height":
8999
+
height = sizeData(str);
9000
+
break
9001
+
case "totalheight":
9002
+
totalheight = sizeData(str);
9003
+
break
9004
+
default:
9005
+
throw new ParseError("Invalid key: '" + keyVal[0] + "' in \\includegraphics.")
9006
+
}
9007
+
}
9008
+
}
9009
+
}
9010
+
9011
+
const src = assertNodeType(args[0], "url").url;
9012
+
9013
+
if (alt === "") {
9014
+
// No alt given. Use the file name. Strip away the path.
9015
+
alt = src;
9016
+
alt = alt.replace(/^.*[\\/]/, "");
9017
+
alt = alt.substring(0, alt.lastIndexOf("."));
9018
+
}
9019
+
9020
+
if (
9021
+
!parser.settings.isTrusted({
9022
+
command: "\\includegraphics",
9023
+
url: src
9024
+
})
9025
+
) {
9026
+
throw new ParseError(`Function "\\includegraphics" is not trusted`, token)
9027
+
}
9028
+
9029
+
return {
9030
+
type: "includegraphics",
9031
+
mode: parser.mode,
9032
+
alt: alt,
9033
+
width: width,
9034
+
height: height,
9035
+
totalheight: totalheight,
9036
+
src: src
9037
+
}
9038
+
},
9039
+
mathmlBuilder: (group, style) => {
9040
+
const height = calculateSize(group.height, style);
9041
+
const depth = { number: 0, unit: "em" };
9042
+
9043
+
if (group.totalheight.number > 0) {
9044
+
if (group.totalheight.unit === height.unit &&
9045
+
group.totalheight.number > height.number) {
9046
+
depth.number = group.totalheight.number - height.number;
9047
+
depth.unit = height.unit;
9048
+
}
9049
+
}
9050
+
9051
+
let width = 0;
9052
+
if (group.width.number > 0) {
9053
+
width = calculateSize(group.width, style);
9054
+
}
9055
+
9056
+
const graphicStyle = { height: height.number + depth.number + "em" };
9057
+
if (width.number > 0) {
9058
+
graphicStyle.width = width.number + width.unit;
9059
+
}
9060
+
if (depth.number > 0) {
9061
+
graphicStyle.verticalAlign = -depth.number + depth.unit;
9062
+
}
9063
+
9064
+
const node = new Img(group.src, group.alt, graphicStyle);
9065
+
node.height = height;
9066
+
node.depth = depth;
9067
+
return new mathMLTree.MathNode("mtext", [node])
9068
+
}
9069
+
});
9070
+
9071
+
// Horizontal spacing commands
9072
+
9073
+
9074
+
// TODO: \hskip and \mskip should support plus and minus in lengths
9075
+
9076
+
defineFunction({
9077
+
type: "kern",
9078
+
names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"],
9079
+
props: {
9080
+
numArgs: 1,
9081
+
argTypes: ["size"],
9082
+
primitive: true,
9083
+
allowedInText: true
9084
+
},
9085
+
handler({ parser, funcName, token }, args) {
9086
+
const size = assertNodeType(args[0], "size");
9087
+
if (parser.settings.strict) {
9088
+
const mathFunction = funcName[1] === "m"; // \mkern, \mskip
9089
+
const muUnit = size.value.unit === "mu";
9090
+
if (mathFunction) {
9091
+
if (!muUnit) {
9092
+
throw new ParseError(`LaTeX's ${funcName} supports only mu units, ` +
9093
+
`not ${size.value.unit} units`, token)
9094
+
}
9095
+
if (parser.mode !== "math") {
9096
+
throw new ParseError(`LaTeX's ${funcName} works only in math mode`, token)
9097
+
}
9098
+
} else {
9099
+
// !mathFunction
9100
+
if (muUnit) {
9101
+
throw new ParseError(`LaTeX's ${funcName} doesn't support mu units`, token)
9102
+
}
9103
+
}
9104
+
}
9105
+
return {
9106
+
type: "kern",
9107
+
mode: parser.mode,
9108
+
dimension: size.value
9109
+
};
9110
+
},
9111
+
mathmlBuilder(group, style) {
9112
+
const dimension = calculateSize(group.dimension, style);
9113
+
const ch = dimension.unit === "em" ? spaceCharacter(dimension.number) : "";
9114
+
if (group.mode === "text" && ch.length > 0) {
9115
+
const character = new mathMLTree.TextNode(ch);
9116
+
return new mathMLTree.MathNode("mtext", [character]);
9117
+
} else {
9118
+
const node = new mathMLTree.MathNode("mspace");
9119
+
node.setAttribute("width", dimension.number + dimension.unit);
9120
+
if (dimension.number < 0) {
9121
+
node.style.marginLeft = dimension.number + dimension.unit;
9122
+
}
9123
+
return node;
9124
+
}
9125
+
}
9126
+
});
9127
+
9128
+
const spaceCharacter = function(width) {
9129
+
if (width >= 0.05555 && width <= 0.05556) {
9130
+
return "\u200a"; //  
9131
+
} else if (width >= 0.1666 && width <= 0.1667) {
9132
+
return "\u2009"; //  
9133
+
} else if (width >= 0.2222 && width <= 0.2223) {
9134
+
return "\u2005"; //  
9135
+
} else if (width >= 0.2777 && width <= 0.2778) {
9136
+
return "\u2005\u200a"; //   
9137
+
} else {
9138
+
return "";
9139
+
}
9140
+
};
9141
+
9142
+
// Limit valid characters to a small set, for safety.
9143
+
const invalidIdRegEx = /[^A-Za-z_0-9-]/g;
9144
+
9145
+
defineFunction({
9146
+
type: "label",
9147
+
names: ["\\label"],
9148
+
props: {
9149
+
numArgs: 1,
9150
+
argTypes: ["raw"]
9151
+
},
9152
+
handler({ parser }, args) {
9153
+
return {
9154
+
type: "label",
9155
+
mode: parser.mode,
9156
+
string: args[0].string.replace(invalidIdRegEx, "")
9157
+
};
9158
+
},
9159
+
mathmlBuilder(group, style) {
9160
+
// Return a no-width, no-ink element with an HTML id.
9161
+
const node = new mathMLTree.MathNode("mrow", [], ["tml-label"]);
9162
+
if (group.string.length > 0) {
9163
+
node.setLabel(group.string);
9164
+
}
9165
+
return node
9166
+
}
9167
+
});
9168
+
9169
+
// Horizontal overlap functions
9170
+
9171
+
const textModeLap = ["\\clap", "\\llap", "\\rlap"];
9172
+
9173
+
defineFunction({
9174
+
type: "lap",
9175
+
names: ["\\mathllap", "\\mathrlap", "\\mathclap", "\\clap", "\\llap", "\\rlap"],
9176
+
props: {
9177
+
numArgs: 1,
9178
+
allowedInText: true
9179
+
},
9180
+
handler: ({ parser, funcName, token }, args) => {
9181
+
if (textModeLap.includes(funcName)) {
9182
+
if (parser.settings.strict && parser.mode !== "text") {
9183
+
throw new ParseError(`{${funcName}} can be used only in text mode.
9184
+
Try \\math${funcName.slice(1)}`, token)
9185
+
}
9186
+
funcName = funcName.slice(1);
9187
+
} else {
9188
+
funcName = funcName.slice(5);
9189
+
}
9190
+
const body = args[0];
9191
+
return {
9192
+
type: "lap",
9193
+
mode: parser.mode,
9194
+
alignment: funcName,
9195
+
body
9196
+
}
9197
+
},
9198
+
mathmlBuilder: (group, style) => {
9199
+
// mathllap, mathrlap, mathclap
9200
+
let strut;
9201
+
if (group.alignment === "llap") {
9202
+
// We need an invisible strut with the same depth as the group.
9203
+
// We can't just read the depth, so we use \vphantom methods.
9204
+
const phantomInner = buildExpression(ordargument(group.body), style);
9205
+
const phantom = new mathMLTree.MathNode("mphantom", phantomInner);
9206
+
strut = new mathMLTree.MathNode("mpadded", [phantom]);
9207
+
strut.setAttribute("width", "0px");
9208
+
}
9209
+
9210
+
const inner = buildGroup$1(group.body, style);
9211
+
let node;
9212
+
if (group.alignment === "llap") {
9213
+
inner.style.position = "absolute";
9214
+
inner.style.right = "0";
9215
+
inner.style.bottom = `0`; // If we could have read the ink depth, it would go here.
9216
+
node = new mathMLTree.MathNode("mpadded", [strut, inner]);
9217
+
} else {
9218
+
node = new mathMLTree.MathNode("mpadded", [inner]);
9219
+
}
9220
+
9221
+
if (group.alignment === "rlap") {
9222
+
if (group.body.body.length > 0 && group.body.body[0].type === "genfrac") {
9223
+
// In Firefox, a <mpadded> squashes the 3/18em padding of a child \frac. Put it back.
9224
+
node.setAttribute("lspace", "0.16667em");
9225
+
}
9226
+
} else {
9227
+
const offset = group.alignment === "llap" ? "-1" : "-0.5";
9228
+
node.setAttribute("lspace", offset + "width");
9229
+
if (group.alignment === "llap") {
9230
+
node.style.position = "relative";
9231
+
} else {
9232
+
node.style.display = "flex";
9233
+
node.style.justifyContent = "center";
9234
+
}
9235
+
}
9236
+
node.setAttribute("width", "0px");
9237
+
return node
9238
+
}
9239
+
});
9240
+
9241
+
// Switching from text mode back to math mode
9242
+
defineFunction({
9243
+
type: "ordgroup",
9244
+
names: ["\\(", "$"],
9245
+
props: {
9246
+
numArgs: 0,
9247
+
allowedInText: true,
9248
+
allowedInMath: false
9249
+
},
9250
+
handler({ funcName, parser }, args) {
9251
+
const outerMode = parser.mode;
9252
+
parser.switchMode("math");
9253
+
const close = funcName === "\\(" ? "\\)" : "$";
9254
+
const body = parser.parseExpression(false, close);
9255
+
parser.expect(close);
9256
+
parser.switchMode(outerMode);
9257
+
return {
9258
+
type: "ordgroup",
9259
+
mode: parser.mode,
9260
+
body
9261
+
};
9262
+
}
9263
+
});
9264
+
9265
+
// Check for extra closing math delimiters
9266
+
defineFunction({
9267
+
type: "text", // Doesn't matter what this is.
9268
+
names: ["\\)", "\\]"],
9269
+
props: {
9270
+
numArgs: 0,
9271
+
allowedInText: true,
9272
+
allowedInMath: false
9273
+
},
9274
+
handler(context, token) {
9275
+
throw new ParseError(`Mismatched ${context.funcName}`, token);
9276
+
}
9277
+
});
9278
+
9279
+
const chooseStyle = (group, style) => {
9280
+
switch (style.level) {
9281
+
case StyleLevel.DISPLAY: // 0
9282
+
return group.display;
9283
+
case StyleLevel.TEXT: // 1
9284
+
return group.text;
9285
+
case StyleLevel.SCRIPT: // 2
9286
+
return group.script;
9287
+
case StyleLevel.SCRIPTSCRIPT: // 3
9288
+
return group.scriptscript;
9289
+
default:
9290
+
return group.text;
9291
+
}
9292
+
};
9293
+
9294
+
defineFunction({
9295
+
type: "mathchoice",
9296
+
names: ["\\mathchoice"],
9297
+
props: {
9298
+
numArgs: 4,
9299
+
primitive: true
9300
+
},
9301
+
handler: ({ parser }, args) => {
9302
+
return {
9303
+
type: "mathchoice",
9304
+
mode: parser.mode,
9305
+
display: ordargument(args[0]),
9306
+
text: ordargument(args[1]),
9307
+
script: ordargument(args[2]),
9308
+
scriptscript: ordargument(args[3])
9309
+
};
9310
+
},
9311
+
mathmlBuilder: (group, style) => {
9312
+
const body = chooseStyle(group, style);
9313
+
return buildExpressionRow(body, style);
9314
+
}
9315
+
});
9316
+
9317
+
const textAtomTypes = ["text", "textord", "mathord", "atom"];
9318
+
9319
+
const padding = width => {
9320
+
const node = new mathMLTree.MathNode("mspace");
9321
+
node.setAttribute("width", width + "em");
9322
+
return node
9323
+
};
9324
+
9325
+
function mathmlBuilder$3(group, style) {
9326
+
let node;
9327
+
const inner = buildExpression(group.body, style);
9328
+
9329
+
if (group.mclass === "minner") {
9330
+
node = new mathMLTree.MathNode("mpadded", inner);
9331
+
} else if (group.mclass === "mord") {
9332
+
if (group.isCharacterBox || inner[0].type === "mathord") {
9333
+
node = inner[0];
9334
+
node.type = "mi";
9335
+
if (node.children.length === 1 && node.children[0].text && node.children[0].text === "∇") {
9336
+
node.setAttribute("mathvariant", "normal");
9337
+
}
9338
+
} else {
9339
+
node = new mathMLTree.MathNode("mi", inner);
9340
+
}
9341
+
} else {
9342
+
node = new mathMLTree.MathNode("mrow", inner);
9343
+
if (group.mustPromote) {
9344
+
node = inner[0];
9345
+
node.type = "mo";
9346
+
if (group.isCharacterBox && group.body[0].text && /[A-Za-z]/.test(group.body[0].text)) {
9347
+
node.setAttribute("mathvariant", "italic");
9348
+
}
9349
+
} else {
9350
+
node = new mathMLTree.MathNode("mrow", inner);
9351
+
}
9352
+
9353
+
// Set spacing based on what is the most likely adjacent atom type.
9354
+
// See TeXbook p170.
9355
+
const doSpacing = style.level < 2; // Operator spacing is zero inside a (sub|super)script.
9356
+
if (node.type === "mrow") {
9357
+
if (doSpacing ) {
9358
+
if (group.mclass === "mbin") {
9359
+
// medium space
9360
+
node.children.unshift(padding(0.2222));
9361
+
node.children.push(padding(0.2222));
9362
+
} else if (group.mclass === "mrel") {
9363
+
// thickspace
9364
+
node.children.unshift(padding(0.2778));
9365
+
node.children.push(padding(0.2778));
9366
+
} else if (group.mclass === "mpunct") {
9367
+
node.children.push(padding(0.1667));
9368
+
} else if (group.mclass === "minner") {
9369
+
node.children.unshift(padding(0.0556)); // 1 mu is the most likely option
9370
+
node.children.push(padding(0.0556));
9371
+
}
9372
+
}
9373
+
} else {
9374
+
if (group.mclass === "mbin") {
9375
+
// medium space
9376
+
node.attributes.lspace = (doSpacing ? "0.2222em" : "0");
9377
+
node.attributes.rspace = (doSpacing ? "0.2222em" : "0");
9378
+
} else if (group.mclass === "mrel") {
9379
+
// thickspace
9380
+
node.attributes.lspace = (doSpacing ? "0.2778em" : "0");
9381
+
node.attributes.rspace = (doSpacing ? "0.2778em" : "0");
9382
+
} else if (group.mclass === "mpunct") {
9383
+
node.attributes.lspace = "0em";
9384
+
node.attributes.rspace = (doSpacing ? "0.1667em" : "0");
9385
+
} else if (group.mclass === "mopen" || group.mclass === "mclose") {
9386
+
node.attributes.lspace = "0em";
9387
+
node.attributes.rspace = "0em";
9388
+
} else if (group.mclass === "minner" && doSpacing) {
9389
+
node.attributes.lspace = "0.0556em"; // 1 mu is the most likely option
9390
+
node.attributes.width = "+0.1111em";
9391
+
}
9392
+
}
9393
+
9394
+
if (!(group.mclass === "mopen" || group.mclass === "mclose")) {
9395
+
delete node.attributes.stretchy;
9396
+
delete node.attributes.form;
9397
+
}
9398
+
}
9399
+
return node;
9400
+
}
9401
+
9402
+
// Math class commands except \mathop
9403
+
defineFunction({
9404
+
type: "mclass",
9405
+
names: [
9406
+
"\\mathord",
9407
+
"\\mathbin",
9408
+
"\\mathrel",
9409
+
"\\mathopen",
9410
+
"\\mathclose",
9411
+
"\\mathpunct",
9412
+
"\\mathinner"
9413
+
],
9414
+
props: {
9415
+
numArgs: 1,
9416
+
primitive: true
9417
+
},
9418
+
handler({ parser, funcName }, args) {
9419
+
const body = args[0];
9420
+
const isCharacterBox = utils.isCharacterBox(body);
9421
+
// We should not wrap a <mo> around a <mi> or <mord>. That would be invalid MathML.
9422
+
// In that case, we instead promote the text contents of the body to the parent.
9423
+
let mustPromote = true;
9424
+
const mord = { type: "mathord", text: "", mode: parser.mode };
9425
+
const arr = (body.body) ? body.body : [body];
9426
+
for (const arg of arr) {
9427
+
if (textAtomTypes.includes(arg.type)) {
9428
+
if (symbols[parser.mode][arg.text]) {
9429
+
mord.text += symbols[parser.mode][arg.text].replace;
9430
+
} else if (arg.text) {
9431
+
mord.text += arg.text;
9432
+
} else if (arg.body) {
9433
+
arg.body.map(e => { mord.text += e.text; });
9434
+
}
9435
+
} else {
9436
+
mustPromote = false;
9437
+
break
9438
+
}
9439
+
}
9440
+
return {
9441
+
type: "mclass",
9442
+
mode: parser.mode,
9443
+
mclass: "m" + funcName.slice(5),
9444
+
body: ordargument(mustPromote ? mord : body),
9445
+
isCharacterBox,
9446
+
mustPromote
9447
+
};
9448
+
},
9449
+
mathmlBuilder: mathmlBuilder$3
9450
+
});
9451
+
9452
+
const binrelClass = (arg) => {
9453
+
// \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument.
9454
+
// (by rendering separately and with {}s before and after, and measuring
9455
+
// the change in spacing). We'll do roughly the same by detecting the
9456
+
// atom type directly.
9457
+
const atom = arg.type === "ordgroup" && arg.body.length ? arg.body[0] : arg;
9458
+
if (atom.type === "atom" && (atom.family === "bin" || atom.family === "rel")) {
9459
+
return "m" + atom.family;
9460
+
} else {
9461
+
return "mord";
9462
+
}
9463
+
};
9464
+
9465
+
// \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord.
9466
+
// This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX.
9467
+
defineFunction({
9468
+
type: "mclass",
9469
+
names: ["\\@binrel"],
9470
+
props: {
9471
+
numArgs: 2
9472
+
},
9473
+
handler({ parser }, args) {
9474
+
return {
9475
+
type: "mclass",
9476
+
mode: parser.mode,
9477
+
mclass: binrelClass(args[0]),
9478
+
body: ordargument(args[1]),
9479
+
isCharacterBox: utils.isCharacterBox(args[1])
9480
+
};
9481
+
}
9482
+
});
9483
+
9484
+
// Build a relation or stacked op by placing one symbol on top of another
9485
+
defineFunction({
9486
+
type: "mclass",
9487
+
names: ["\\stackrel", "\\overset", "\\underset"],
9488
+
props: {
9489
+
numArgs: 2
9490
+
},
9491
+
handler({ parser, funcName }, args) {
9492
+
const baseArg = args[1];
9493
+
const shiftedArg = args[0];
9494
+
9495
+
const baseOp = {
9496
+
type: "op",
9497
+
mode: baseArg.mode,
9498
+
limits: true,
9499
+
alwaysHandleSupSub: true,
9500
+
parentIsSupSub: false,
9501
+
symbol: false,
9502
+
stack: true,
9503
+
suppressBaseShift: funcName !== "\\stackrel",
9504
+
body: ordargument(baseArg)
9505
+
};
9506
+
9507
+
return {
9508
+
type: "supsub",
9509
+
mode: shiftedArg.mode,
9510
+
base: baseOp,
9511
+
sup: funcName === "\\underset" ? null : shiftedArg,
9512
+
sub: funcName === "\\underset" ? shiftedArg : null
9513
+
};
9514
+
},
9515
+
mathmlBuilder: mathmlBuilder$3
9516
+
});
9517
+
9518
+
// Helper function
9519
+
const buildGroup = (el, style, noneNode) => {
9520
+
if (!el) { return noneNode }
9521
+
const node = buildGroup$1(el, style);
9522
+
if (node.type === "mrow" && node.children.length === 0) { return noneNode }
9523
+
return node
9524
+
};
9525
+
9526
+
defineFunction({
9527
+
type: "multiscript",
9528
+
names: ["\\sideset", "\\pres@cript"], // See macros.js for \prescript
9529
+
props: {
9530
+
numArgs: 3
9531
+
},
9532
+
handler({ parser, funcName, token }, args) {
9533
+
if (args[2].body.length === 0) {
9534
+
throw new ParseError(funcName + `cannot parse an empty base.`)
9535
+
}
9536
+
const base = args[2].body[0];
9537
+
if (parser.settings.strict && funcName === "\\sideset" && !base.symbol) {
9538
+
throw new ParseError(`The base of \\sideset must be a big operator. Try \\prescript.`)
9539
+
}
9540
+
9541
+
if ((args[0].body.length > 0 && args[0].body[0].type !== "supsub") ||
9542
+
(args[1].body.length > 0 && args[1].body[0].type !== "supsub")) {
9543
+
throw new ParseError("\\sideset can parse only subscripts and " +
9544
+
"superscripts in its first two arguments", token)
9545
+
}
9546
+
9547
+
// The prescripts and postscripts come wrapped in a supsub.
9548
+
const prescripts = args[0].body.length > 0 ? args[0].body[0] : null;
9549
+
const postscripts = args[1].body.length > 0 ? args[1].body[0] : null;
9550
+
9551
+
if (!prescripts && !postscripts) {
9552
+
return base
9553
+
} else if (!prescripts) {
9554
+
// It's not a multi-script. Get a \textstyle supsub.
9555
+
return {
9556
+
type: "styling",
9557
+
mode: parser.mode,
9558
+
scriptLevel: "text",
9559
+
body: [{
9560
+
type: "supsub",
9561
+
mode: parser.mode,
9562
+
base,
9563
+
sup: postscripts.sup,
9564
+
sub: postscripts.sub
9565
+
}]
9566
+
}
9567
+
} else {
9568
+
return {
9569
+
type: "multiscript",
9570
+
mode: parser.mode,
9571
+
isSideset: funcName === "\\sideset",
9572
+
prescripts,
9573
+
postscripts,
9574
+
base
9575
+
}
9576
+
}
9577
+
},
9578
+
mathmlBuilder(group, style) {
9579
+
const base = buildGroup$1(group.base, style);
9580
+
9581
+
const prescriptsNode = new mathMLTree.MathNode("mprescripts");
9582
+
const noneNode = new mathMLTree.MathNode("none");
9583
+
let children = [];
9584
+
9585
+
const preSub = buildGroup(group.prescripts.sub, style, noneNode);
9586
+
const preSup = buildGroup(group.prescripts.sup, style, noneNode);
9587
+
if (group.isSideset) {
9588
+
// This seems silly, but LaTeX does this. Firefox ignores it, which does not make me sad.
9589
+
preSub.setAttribute("style", "text-align: left;");
9590
+
preSup.setAttribute("style", "text-align: left;");
9591
+
}
9592
+
9593
+
if (group.postscripts) {
9594
+
const postSub = buildGroup(group.postscripts.sub, style, noneNode);
9595
+
const postSup = buildGroup(group.postscripts.sup, style, noneNode);
9596
+
children = [base, postSub, postSup, prescriptsNode, preSub, preSup];
9597
+
} else {
9598
+
children = [base, prescriptsNode, preSub, preSup];
9599
+
}
9600
+
9601
+
return new mathMLTree.MathNode("mmultiscripts", children);
9602
+
}
9603
+
});
9604
+
9605
+
defineFunction({
9606
+
type: "not",
9607
+
names: ["\\not"],
9608
+
props: {
9609
+
numArgs: 1,
9610
+
primitive: true,
9611
+
allowedInText: false
9612
+
},
9613
+
handler({ parser }, args) {
9614
+
const isCharacterBox = utils.isCharacterBox(args[0]);
9615
+
let body;
9616
+
if (isCharacterBox) {
9617
+
body = ordargument(args[0]);
9618
+
if (body[0].text.charAt(0) === "\\") {
9619
+
body[0].text = symbols.math[body[0].text].replace;
9620
+
}
9621
+
// \u0338 is the Unicode Combining Long Solidus Overlay
9622
+
body[0].text = body[0].text.slice(0, 1) + "\u0338" + body[0].text.slice(1);
9623
+
} else {
9624
+
// When the argument is not a character box, TeX does an awkward, poorly placed overlay.
9625
+
// We'll do the same.
9626
+
const notNode = { type: "textord", mode: "math", text: "\u0338" };
9627
+
const kernNode = { type: "kern", mode: "math", dimension: { number: -0.6, unit: "em" } };
9628
+
body = [notNode, kernNode, args[0]];
9629
+
}
9630
+
return {
9631
+
type: "not",
9632
+
mode: parser.mode,
9633
+
body,
9634
+
isCharacterBox
9635
+
};
9636
+
},
9637
+
mathmlBuilder(group, style) {
9638
+
if (group.isCharacterBox) {
9639
+
const inner = buildExpression(group.body, style, true);
9640
+
return inner[0]
9641
+
} else {
9642
+
return buildExpressionRow(group.body, style)
9643
+
}
9644
+
}
9645
+
});
9646
+
9647
+
// Limits, symbols
9648
+
9649
+
// Some helpers
9650
+
9651
+
const ordAtomTypes = ["textord", "mathord", "atom"];
9652
+
9653
+
// Most operators have a large successor symbol, but these don't.
9654
+
const noSuccessor = ["\\smallint"];
9655
+
9656
+
// Math operators (e.g. \sin) need a space between these types and themselves:
9657
+
const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright", "font"];
9658
+
9659
+
// NOTE: Unlike most `builders`s, this one handles not only "op", but also
9660
+
// "supsub" since some of them (like \int) can affect super/subscripting.
9661
+
9662
+
const setSpacing = node => {
9663
+
// The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
9664
+
// The most likely spacing for an OP is a thin space per TeXbook p170.
9665
+
node.attributes.lspace = "0.1667em";
9666
+
node.attributes.rspace = "0.1667em";
9667
+
};
9668
+
9669
+
const mathmlBuilder$2 = (group, style) => {
9670
+
let node;
9671
+
9672
+
if (group.symbol) {
9673
+
// This is a symbol. Just add the symbol.
9674
+
node = new MathNode("mo", [makeText(group.name, group.mode)]);
9675
+
if (noSuccessor.includes(group.name)) {
9676
+
node.setAttribute("largeop", "false");
9677
+
} else {
9678
+
node.setAttribute("movablelimits", "false");
9679
+
}
9680
+
if (group.fromMathOp) { setSpacing(node); }
9681
+
} else if (group.body) {
9682
+
// This is an operator with children. Add them.
9683
+
node = new MathNode("mo", buildExpression(group.body, style));
9684
+
if (group.fromMathOp) { setSpacing(node); }
9685
+
} else {
9686
+
// This is a text operator. Add all of the characters from the operator's name.
9687
+
node = new MathNode("mi", [new TextNode(group.name.slice(1))]);
9688
+
9689
+
if (!group.parentIsSupSub) {
9690
+
// Append an invisible <mo>⁡</mo>.
9691
+
// ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
9692
+
const operator = new MathNode("mo", [makeText("\u2061", "text")]);
9693
+
const row = [node, operator];
9694
+
// Set spacing
9695
+
if (group.needsLeadingSpace) {
9696
+
const lead = new MathNode("mspace");
9697
+
lead.setAttribute("width", "0.1667em"); // thin space.
9698
+
row.unshift(lead);
9699
+
}
9700
+
if (!group.isFollowedByDelimiter) {
9701
+
const trail = new MathNode("mspace");
9702
+
trail.setAttribute("width", "0.1667em"); // thin space.
9703
+
row.push(trail);
9704
+
}
9705
+
node = new MathNode("mrow", row);
9706
+
}
9707
+
}
9708
+
9709
+
return node;
9710
+
};
9711
+
9712
+
const singleCharBigOps = {
9713
+
"\u220F": "\\prod",
9714
+
"\u2210": "\\coprod",
9715
+
"\u2211": "\\sum",
9716
+
"\u22c0": "\\bigwedge",
9717
+
"\u22c1": "\\bigvee",
9718
+
"\u22c2": "\\bigcap",
9719
+
"\u22c3": "\\bigcup",
9720
+
"\u2a00": "\\bigodot",
9721
+
"\u2a01": "\\bigoplus",
9722
+
"\u2a02": "\\bigotimes",
9723
+
"\u2a04": "\\biguplus",
9724
+
"\u2a05": "\\bigsqcap",
9725
+
"\u2a06": "\\bigsqcup",
9726
+
"\u2a03": "\\bigcupdot",
9727
+
"\u2a07": "\\bigdoublevee",
9728
+
"\u2a08": "\\bigdoublewedge",
9729
+
"\u2a09": "\\bigtimes"
9730
+
};
9731
+
9732
+
defineFunction({
9733
+
type: "op",
9734
+
names: [
9735
+
"\\coprod",
9736
+
"\\bigvee",
9737
+
"\\bigwedge",
9738
+
"\\biguplus",
9739
+
"\\bigcupplus",
9740
+
"\\bigcupdot",
9741
+
"\\bigcap",
9742
+
"\\bigcup",
9743
+
"\\bigdoublevee",
9744
+
"\\bigdoublewedge",
9745
+
"\\intop",
9746
+
"\\prod",
9747
+
"\\sum",
9748
+
"\\bigotimes",
9749
+
"\\bigoplus",
9750
+
"\\bigodot",
9751
+
"\\bigsqcap",
9752
+
"\\bigsqcup",
9753
+
"\\bigtimes",
9754
+
"\\smallint",
9755
+
"\u220F",
9756
+
"\u2210",
9757
+
"\u2211",
9758
+
"\u22c0",
9759
+
"\u22c1",
9760
+
"\u22c2",
9761
+
"\u22c3",
9762
+
"\u2a00",
9763
+
"\u2a01",
9764
+
"\u2a02",
9765
+
"\u2a04",
9766
+
"\u2a06"
9767
+
],
9768
+
props: {
9769
+
numArgs: 0
9770
+
},
9771
+
handler: ({ parser, funcName }, args) => {
9772
+
let fName = funcName;
9773
+
if (fName.length === 1) {
9774
+
fName = singleCharBigOps[fName];
9775
+
}
9776
+
return {
9777
+
type: "op",
9778
+
mode: parser.mode,
9779
+
limits: true,
9780
+
parentIsSupSub: false,
9781
+
symbol: true,
9782
+
stack: false, // This is true for \stackrel{}, not here.
9783
+
name: fName
9784
+
};
9785
+
},
9786
+
mathmlBuilder: mathmlBuilder$2
9787
+
});
9788
+
9789
+
// Note: calling defineFunction with a type that's already been defined only
9790
+
// works because the same mathmlBuilder is being used.
9791
+
defineFunction({
9792
+
type: "op",
9793
+
names: ["\\mathop"],
9794
+
props: {
9795
+
numArgs: 1,
9796
+
primitive: true
9797
+
},
9798
+
handler: ({ parser }, args) => {
9799
+
const body = args[0];
9800
+
// It would be convienient to just wrap a <mo> around the argument.
9801
+
// But if the argument is a <mi> or <mord>, that would be invalid MathML.
9802
+
// In that case, we instead promote the text contents of the body to the parent.
9803
+
const arr = (body.body) ? body.body : [body];
9804
+
const isSymbol = arr.length === 1 && ordAtomTypes.includes(arr[0].type);
9805
+
return {
9806
+
type: "op",
9807
+
mode: parser.mode,
9808
+
limits: true,
9809
+
parentIsSupSub: false,
9810
+
symbol: isSymbol,
9811
+
fromMathOp: true,
9812
+
stack: false,
9813
+
name: isSymbol ? arr[0].text : null,
9814
+
body: isSymbol ? null : ordargument(body)
9815
+
};
9816
+
},
9817
+
mathmlBuilder: mathmlBuilder$2
9818
+
});
9819
+
9820
+
// There are 2 flags for operators; whether they produce limits in
9821
+
// displaystyle, and whether they are symbols and should grow in
9822
+
// displaystyle. These four groups cover the four possible choices.
9823
+
9824
+
const singleCharIntegrals = {
9825
+
"\u222b": "\\int",
9826
+
"\u222c": "\\iint",
9827
+
"\u222d": "\\iiint",
9828
+
"\u222e": "\\oint",
9829
+
"\u222f": "\\oiint",
9830
+
"\u2230": "\\oiiint",
9831
+
"\u2231": "\\intclockwise",
9832
+
"\u2232": "\\varointclockwise",
9833
+
"\u2a0c": "\\iiiint",
9834
+
"\u2a0d": "\\intbar",
9835
+
"\u2a0e": "\\intBar",
9836
+
"\u2a0f": "\\fint",
9837
+
"\u2a12": "\\rppolint",
9838
+
"\u2a13": "\\scpolint",
9839
+
"\u2a15": "\\pointint",
9840
+
"\u2a16": "\\sqint",
9841
+
"\u2a17": "\\intlarhk",
9842
+
"\u2a18": "\\intx",
9843
+
"\u2a19": "\\intcap",
9844
+
"\u2a1a": "\\intcup"
9845
+
};
9846
+
9847
+
// No limits, not symbols
9848
+
defineFunction({
9849
+
type: "op",
9850
+
names: [
9851
+
"\\arcsin",
9852
+
"\\arccos",
9853
+
"\\arctan",
9854
+
"\\arctg",
9855
+
"\\arcctg",
9856
+
"\\arg",
9857
+
"\\ch",
9858
+
"\\cos",
9859
+
"\\cosec",
9860
+
"\\cosh",
9861
+
"\\cot",
9862
+
"\\cotg",
9863
+
"\\coth",
9864
+
"\\csc",
9865
+
"\\ctg",
9866
+
"\\cth",
9867
+
"\\deg",
9868
+
"\\dim",
9869
+
"\\exp",
9870
+
"\\hom",
9871
+
"\\ker",
9872
+
"\\lg",
9873
+
"\\ln",
9874
+
"\\log",
9875
+
"\\sec",
9876
+
"\\sin",
9877
+
"\\sinh",
9878
+
"\\sh",
9879
+
"\\sgn",
9880
+
"\\tan",
9881
+
"\\tanh",
9882
+
"\\tg",
9883
+
"\\th"
9884
+
],
9885
+
props: {
9886
+
numArgs: 0
9887
+
},
9888
+
handler({ parser, funcName }) {
9889
+
const prevAtomType = parser.prevAtomType;
9890
+
const next = parser.gullet.future().text;
9891
+
return {
9892
+
type: "op",
9893
+
mode: parser.mode,
9894
+
limits: false,
9895
+
parentIsSupSub: false,
9896
+
symbol: false,
9897
+
stack: false,
9898
+
isFollowedByDelimiter: isDelimiter(next),
9899
+
needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
9900
+
name: funcName
9901
+
};
9902
+
},
9903
+
mathmlBuilder: mathmlBuilder$2
9904
+
});
9905
+
9906
+
// Limits, not symbols
9907
+
defineFunction({
9908
+
type: "op",
9909
+
names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"],
9910
+
props: {
9911
+
numArgs: 0
9912
+
},
9913
+
handler({ parser, funcName }) {
9914
+
const prevAtomType = parser.prevAtomType;
9915
+
const next = parser.gullet.future().text;
9916
+
return {
9917
+
type: "op",
9918
+
mode: parser.mode,
9919
+
limits: true,
9920
+
parentIsSupSub: false,
9921
+
symbol: false,
9922
+
stack: false,
9923
+
isFollowedByDelimiter: isDelimiter(next),
9924
+
needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
9925
+
name: funcName
9926
+
};
9927
+
},
9928
+
mathmlBuilder: mathmlBuilder$2
9929
+
});
9930
+
9931
+
// No limits, symbols
9932
+
defineFunction({
9933
+
type: "op",
9934
+
names: [
9935
+
"\\int",
9936
+
"\\iint",
9937
+
"\\iiint",
9938
+
"\\iiiint",
9939
+
"\\oint",
9940
+
"\\oiint",
9941
+
"\\oiiint",
9942
+
"\\intclockwise",
9943
+
"\\varointclockwise",
9944
+
"\\intbar",
9945
+
"\\intBar",
9946
+
"\\fint",
9947
+
"\\rppolint",
9948
+
"\\scpolint",
9949
+
"\\pointint",
9950
+
"\\sqint",
9951
+
"\\intlarhk",
9952
+
"\\intx",
9953
+
"\\intcap",
9954
+
"\\intcup",
9955
+
"\u222b",
9956
+
"\u222c",
9957
+
"\u222d",
9958
+
"\u222e",
9959
+
"\u222f",
9960
+
"\u2230",
9961
+
"\u2231",
9962
+
"\u2232",
9963
+
"\u2a0c",
9964
+
"\u2a0d",
9965
+
"\u2a0e",
9966
+
"\u2a0f",
9967
+
"\u2a12",
9968
+
"\u2a13",
9969
+
"\u2a15",
9970
+
"\u2a16",
9971
+
"\u2a17",
9972
+
"\u2a18",
9973
+
"\u2a19",
9974
+
"\u2a1a"
9975
+
],
9976
+
props: {
9977
+
numArgs: 0
9978
+
},
9979
+
handler({ parser, funcName }) {
9980
+
let fName = funcName;
9981
+
if (fName.length === 1) {
9982
+
fName = singleCharIntegrals[fName];
9983
+
}
9984
+
return {
9985
+
type: "op",
9986
+
mode: parser.mode,
9987
+
limits: false,
9988
+
parentIsSupSub: false,
9989
+
symbol: true,
9990
+
stack: false,
9991
+
name: fName
9992
+
};
9993
+
},
9994
+
mathmlBuilder: mathmlBuilder$2
9995
+
});
9996
+
9997
+
// NOTE: Unlike most builders, this one handles not only
9998
+
// "operatorname", but also "supsub" since \operatorname* can
9999
+
// affect super/subscripting.
10000
+
10001
+
const mathmlBuilder$1 = (group, style) => {
10002
+
let expression = buildExpression(group.body, style.withFont("mathrm"));
10003
+
10004
+
// Is expression a string or has it something like a fraction?
10005
+
let isAllString = true; // default
10006
+
for (let i = 0; i < expression.length; i++) {
10007
+
let node = expression[i];
10008
+
if (node instanceof mathMLTree.MathNode) {
10009
+
if (node.type === "mrow" && node.children.length === 1 &&
10010
+
node.children[0] instanceof mathMLTree.MathNode) {
10011
+
node = node.children[0];
10012
+
}
10013
+
switch (node.type) {
10014
+
case "mi":
10015
+
case "mn":
10016
+
case "ms":
10017
+
case "mtext":
10018
+
break; // Do nothing yet.
10019
+
case "mspace":
10020
+
{
10021
+
if (node.attributes.width) {
10022
+
const width = node.attributes.width.replace("em", "");
10023
+
const ch = spaceCharacter(Number(width));
10024
+
if (ch === "") {
10025
+
isAllString = false;
10026
+
} else {
10027
+
expression[i] = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode(ch)]);
10028
+
}
10029
+
}
10030
+
}
10031
+
break
10032
+
case "mo": {
10033
+
const child = node.children[0];
10034
+
if (node.children.length === 1 && child instanceof mathMLTree.TextNode) {
10035
+
child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*");
10036
+
} else {
10037
+
isAllString = false;
10038
+
}
10039
+
break
10040
+
}
10041
+
default:
10042
+
isAllString = false;
10043
+
}
10044
+
} else {
10045
+
isAllString = false;
10046
+
}
10047
+
}
10048
+
10049
+
if (isAllString) {
10050
+
// Write a single TextNode instead of multiple nested tags.
10051
+
const word = expression.map((node) => node.toText()).join("");
10052
+
expression = [new mathMLTree.TextNode(word)];
10053
+
} else if (
10054
+
expression.length === 1
10055
+
&& ["mover", "munder"].includes(expression[0].type) &&
10056
+
(expression[0].children[0].type === "mi" || expression[0].children[0].type === "mtext")
10057
+
) {
10058
+
expression[0].children[0].type = "mi";
10059
+
if (group.parentIsSupSub) {
10060
+
return new mathMLTree.MathNode("mrow", expression)
10061
+
} else {
10062
+
const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]);
10063
+
return mathMLTree.newDocumentFragment([expression[0], operator])
10064
+
}
10065
+
}
10066
+
10067
+
let wrapper;
10068
+
if (isAllString) {
10069
+
wrapper = new mathMLTree.MathNode("mi", expression);
10070
+
if (expression[0].text.length === 1) {
10071
+
wrapper.setAttribute("mathvariant", "normal");
10072
+
}
10073
+
} else {
10074
+
wrapper = new mathMLTree.MathNode("mrow", expression);
10075
+
}
10076
+
10077
+
if (!group.parentIsSupSub) {
10078
+
// Append an <mo>⁡</mo>.
10079
+
// ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
10080
+
const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]);
10081
+
const fragment = [wrapper, operator];
10082
+
if (group.needsLeadingSpace) {
10083
+
// LaTeX gives operator spacing, but a <mi> gets ord spacing.
10084
+
// So add a leading space.
10085
+
const space = new mathMLTree.MathNode("mspace");
10086
+
space.setAttribute("width", "0.1667em"); // thin space.
10087
+
fragment.unshift(space);
10088
+
}
10089
+
if (!group.isFollowedByDelimiter) {
10090
+
const trail = new mathMLTree.MathNode("mspace");
10091
+
trail.setAttribute("width", "0.1667em"); // thin space.
10092
+
fragment.push(trail);
10093
+
}
10094
+
return mathMLTree.newDocumentFragment(fragment)
10095
+
}
10096
+
10097
+
return wrapper
10098
+
};
10099
+
10100
+
// \operatorname
10101
+
// amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@
10102
+
defineFunction({
10103
+
type: "operatorname",
10104
+
names: ["\\operatorname@", "\\operatornamewithlimits"],
10105
+
props: {
10106
+
numArgs: 1,
10107
+
allowedInArgument: true
10108
+
},
10109
+
handler: ({ parser, funcName }, args) => {
10110
+
const body = args[0];
10111
+
const prevAtomType = parser.prevAtomType;
10112
+
const next = parser.gullet.future().text;
10113
+
return {
10114
+
type: "operatorname",
10115
+
mode: parser.mode,
10116
+
body: ordargument(body),
10117
+
alwaysHandleSupSub: (funcName === "\\operatornamewithlimits"),
10118
+
limits: false,
10119
+
parentIsSupSub: false,
10120
+
isFollowedByDelimiter: isDelimiter(next),
10121
+
needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType)
10122
+
};
10123
+
},
10124
+
mathmlBuilder: mathmlBuilder$1
10125
+
});
10126
+
10127
+
defineMacro("\\operatorname",
10128
+
"\\@ifstar\\operatornamewithlimits\\operatorname@");
10129
+
10130
+
defineFunctionBuilders({
10131
+
type: "ordgroup",
10132
+
mathmlBuilder(group, style) {
10133
+
return buildExpressionRow(group.body, style, group.semisimple);
10134
+
}
10135
+
});
10136
+
10137
+
defineFunction({
10138
+
type: "phantom",
10139
+
names: ["\\phantom"],
10140
+
props: {
10141
+
numArgs: 1,
10142
+
allowedInText: true
10143
+
},
10144
+
handler: ({ parser }, args) => {
10145
+
const body = args[0];
10146
+
return {
10147
+
type: "phantom",
10148
+
mode: parser.mode,
10149
+
body: ordargument(body)
10150
+
};
10151
+
},
10152
+
mathmlBuilder: (group, style) => {
10153
+
const inner = buildExpression(group.body, style);
10154
+
return new mathMLTree.MathNode("mphantom", inner);
10155
+
}
10156
+
});
10157
+
10158
+
defineFunction({
10159
+
type: "hphantom",
10160
+
names: ["\\hphantom"],
10161
+
props: {
10162
+
numArgs: 1,
10163
+
allowedInText: true
10164
+
},
10165
+
handler: ({ parser }, args) => {
10166
+
const body = args[0];
10167
+
return {
10168
+
type: "hphantom",
10169
+
mode: parser.mode,
10170
+
body
10171
+
};
10172
+
},
10173
+
mathmlBuilder: (group, style) => {
10174
+
const inner = buildExpression(ordargument(group.body), style);
10175
+
const phantom = new mathMLTree.MathNode("mphantom", inner);
10176
+
const node = new mathMLTree.MathNode("mpadded", [phantom]);
10177
+
node.setAttribute("height", "0px");
10178
+
node.setAttribute("depth", "0px");
10179
+
return node;
10180
+
}
10181
+
});
10182
+
10183
+
defineFunction({
10184
+
type: "vphantom",
10185
+
names: ["\\vphantom"],
10186
+
props: {
10187
+
numArgs: 1,
10188
+
allowedInText: true
10189
+
},
10190
+
handler: ({ parser }, args) => {
10191
+
const body = args[0];
10192
+
return {
10193
+
type: "vphantom",
10194
+
mode: parser.mode,
10195
+
body
10196
+
};
10197
+
},
10198
+
mathmlBuilder: (group, style) => {
10199
+
const inner = buildExpression(ordargument(group.body), style);
10200
+
const phantom = new mathMLTree.MathNode("mphantom", inner);
10201
+
const node = new mathMLTree.MathNode("mpadded", [phantom]);
10202
+
node.setAttribute("width", "0px");
10203
+
return node;
10204
+
}
10205
+
});
10206
+
10207
+
// In LaTeX, \pmb is a simulation of bold font.
10208
+
// The version of \pmb in ambsy.sty works by typesetting three copies of the argument
10209
+
// with small offsets. We use CSS font-weight:bold.
10210
+
10211
+
defineFunction({
10212
+
type: "pmb",
10213
+
names: ["\\pmb"],
10214
+
props: {
10215
+
numArgs: 1,
10216
+
allowedInText: true
10217
+
},
10218
+
handler({ parser }, args) {
10219
+
return {
10220
+
type: "pmb",
10221
+
mode: parser.mode,
10222
+
body: ordargument(args[0])
10223
+
}
10224
+
},
10225
+
mathmlBuilder(group, style) {
10226
+
const inner = buildExpression(group.body, style);
10227
+
// Wrap with an <mstyle> element.
10228
+
const node = wrapWithMstyle(inner);
10229
+
node.setAttribute("style", "font-weight:bold");
10230
+
return node
10231
+
}
10232
+
});
10233
+
10234
+
// \raise, \lower, and \raisebox
10235
+
10236
+
const mathmlBuilder = (group, style) => {
10237
+
const newStyle = style.withLevel(StyleLevel.TEXT);
10238
+
const node = new mathMLTree.MathNode("mpadded", [buildGroup$1(group.body, newStyle)]);
10239
+
const dy = calculateSize(group.dy, style);
10240
+
node.setAttribute("voffset", dy.number + dy.unit);
10241
+
// Add padding, which acts to increase height in Chromium.
10242
+
// TODO: Figure out some way to change height in Firefox w/o breaking Chromium.
10243
+
if (dy.number > 0) {
10244
+
node.style.padding = dy.number + dy.unit + " 0 0 0";
10245
+
} else {
10246
+
node.style.padding = "0 0 " + Math.abs(dy.number) + dy.unit + " 0";
10247
+
}
10248
+
return node
10249
+
};
10250
+
10251
+
defineFunction({
10252
+
type: "raise",
10253
+
names: ["\\raise", "\\lower"],
10254
+
props: {
10255
+
numArgs: 2,
10256
+
argTypes: ["size", "primitive"],
10257
+
primitive: true
10258
+
},
10259
+
handler({ parser, funcName }, args) {
10260
+
const amount = assertNodeType(args[0], "size").value;
10261
+
if (funcName === "\\lower") { amount.number *= -1; }
10262
+
const body = args[1];
10263
+
return {
10264
+
type: "raise",
10265
+
mode: parser.mode,
10266
+
dy: amount,
10267
+
body
10268
+
};
10269
+
},
10270
+
mathmlBuilder
10271
+
});
10272
+
10273
+
10274
+
defineFunction({
10275
+
type: "raise",
10276
+
names: ["\\raisebox"],
10277
+
props: {
10278
+
numArgs: 2,
10279
+
argTypes: ["size", "hbox"],
10280
+
allowedInText: true
10281
+
},
10282
+
handler({ parser, funcName }, args) {
10283
+
const amount = assertNodeType(args[0], "size").value;
10284
+
const body = args[1];
10285
+
return {
10286
+
type: "raise",
10287
+
mode: parser.mode,
10288
+
dy: amount,
10289
+
body
10290
+
};
10291
+
},
10292
+
mathmlBuilder
10293
+
});
10294
+
10295
+
defineFunction({
10296
+
type: "ref",
10297
+
names: ["\\ref", "\\eqref"],
10298
+
props: {
10299
+
numArgs: 1,
10300
+
argTypes: ["raw"]
10301
+
},
10302
+
handler({ parser, funcName }, args) {
10303
+
return {
10304
+
type: "ref",
10305
+
mode: parser.mode,
10306
+
funcName,
10307
+
string: args[0].string.replace(invalidIdRegEx, "")
10308
+
};
10309
+
},
10310
+
mathmlBuilder(group, style) {
10311
+
// Create an empty <a> node. Set a class and an href attribute.
10312
+
// The post-processor will populate with the target's tag or equation number.
10313
+
const classes = group.funcName === "\\ref" ? ["tml-ref"] : ["tml-ref", "tml-eqref"];
10314
+
return new AnchorNode("#" + group.string, classes, null)
10315
+
}
10316
+
});
10317
+
10318
+
defineFunction({
10319
+
type: "reflect",
10320
+
names: ["\\reflectbox"],
10321
+
props: {
10322
+
numArgs: 1,
10323
+
argTypes: ["hbox"],
10324
+
allowedInText: true
10325
+
},
10326
+
handler({ parser }, args) {
10327
+
return {
10328
+
type: "reflect",
10329
+
mode: parser.mode,
10330
+
body: args[0]
10331
+
};
10332
+
},
10333
+
mathmlBuilder(group, style) {
10334
+
const node = buildGroup$1(group.body, style);
10335
+
node.style.transform = "scaleX(-1)";
10336
+
return node
10337
+
}
10338
+
});
10339
+
10340
+
defineFunction({
10341
+
type: "internal",
10342
+
names: ["\\relax"],
10343
+
props: {
10344
+
numArgs: 0,
10345
+
allowedInText: true
10346
+
},
10347
+
handler({ parser }) {
10348
+
return {
10349
+
type: "internal",
10350
+
mode: parser.mode
10351
+
};
10352
+
}
10353
+
});
10354
+
10355
+
defineFunction({
10356
+
type: "rule",
10357
+
names: ["\\rule"],
10358
+
props: {
10359
+
numArgs: 2,
10360
+
numOptionalArgs: 1,
10361
+
allowedInText: true,
10362
+
allowedInMath: true,
10363
+
argTypes: ["size", "size", "size"]
10364
+
},
10365
+
handler({ parser }, args, optArgs) {
10366
+
const shift = optArgs[0];
10367
+
const width = assertNodeType(args[0], "size");
10368
+
const height = assertNodeType(args[1], "size");
10369
+
return {
10370
+
type: "rule",
10371
+
mode: parser.mode,
10372
+
shift: shift && assertNodeType(shift, "size").value,
10373
+
width: width.value,
10374
+
height: height.value
10375
+
};
10376
+
},
10377
+
mathmlBuilder(group, style) {
10378
+
const width = calculateSize(group.width, style);
10379
+
const height = calculateSize(group.height, style);
10380
+
const shift = group.shift
10381
+
? calculateSize(group.shift, style)
10382
+
: { number: 0, unit: "em" };
10383
+
const color = (style.color && style.getColor()) || "black";
10384
+
10385
+
const rule = new mathMLTree.MathNode("mspace");
10386
+
if (width.number > 0 && height.number > 0) {
10387
+
rule.setAttribute("mathbackground", color);
10388
+
}
10389
+
rule.setAttribute("width", width.number + width.unit);
10390
+
rule.setAttribute("height", height.number + height.unit);
10391
+
if (shift.number === 0) { return rule }
10392
+
10393
+
const wrapper = new mathMLTree.MathNode("mpadded", [rule]);
10394
+
if (shift.number >= 0) {
10395
+
wrapper.setAttribute("height", "+" + shift.number + shift.unit);
10396
+
} else {
10397
+
wrapper.setAttribute("height", shift.number + shift.unit);
10398
+
wrapper.setAttribute("depth", "+" + -shift.number + shift.unit);
10399
+
}
10400
+
wrapper.setAttribute("voffset", shift.number + shift.unit);
10401
+
return wrapper;
10402
+
}
10403
+
});
10404
+
10405
+
// The size mappings are taken from TeX with \normalsize=10pt.
10406
+
// We don't have to track script level. MathML does that.
10407
+
const sizeMap = {
10408
+
"\\tiny": 0.5,
10409
+
"\\sixptsize": 0.6,
10410
+
"\\Tiny": 0.6,
10411
+
"\\scriptsize": 0.7,
10412
+
"\\footnotesize": 0.8,
10413
+
"\\small": 0.9,
10414
+
"\\normalsize": 1.0,
10415
+
"\\large": 1.2,
10416
+
"\\Large": 1.44,
10417
+
"\\LARGE": 1.728,
10418
+
"\\huge": 2.074,
10419
+
"\\Huge": 2.488
10420
+
};
10421
+
10422
+
defineFunction({
10423
+
type: "sizing",
10424
+
names: [
10425
+
"\\tiny",
10426
+
"\\sixptsize",
10427
+
"\\Tiny",
10428
+
"\\scriptsize",
10429
+
"\\footnotesize",
10430
+
"\\small",
10431
+
"\\normalsize",
10432
+
"\\large",
10433
+
"\\Large",
10434
+
"\\LARGE",
10435
+
"\\huge",
10436
+
"\\Huge"
10437
+
],
10438
+
props: {
10439
+
numArgs: 0,
10440
+
allowedInText: true
10441
+
},
10442
+
handler: ({ breakOnTokenText, funcName, parser }, args) => {
10443
+
if (parser.settings.strict && parser.mode === "math") {
10444
+
// eslint-disable-next-line no-console
10445
+
console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`);
10446
+
}
10447
+
const body = parser.parseExpression(false, breakOnTokenText, true);
10448
+
return {
10449
+
type: "sizing",
10450
+
mode: parser.mode,
10451
+
funcName,
10452
+
body
10453
+
};
10454
+
},
10455
+
mathmlBuilder: (group, style) => {
10456
+
const newStyle = style.withFontSize(sizeMap[group.funcName]);
10457
+
const inner = buildExpression(group.body, newStyle);
10458
+
// Wrap with an <mstyle> element.
10459
+
const node = wrapWithMstyle(inner);
10460
+
const factor = (sizeMap[group.funcName] / style.fontSize).toFixed(4);
10461
+
node.setAttribute("mathsize", factor + "em");
10462
+
return node;
10463
+
}
10464
+
});
10465
+
10466
+
// smash, with optional [tb], as in AMS
10467
+
10468
+
defineFunction({
10469
+
type: "smash",
10470
+
names: ["\\smash"],
10471
+
props: {
10472
+
numArgs: 1,
10473
+
numOptionalArgs: 1,
10474
+
allowedInText: true
10475
+
},
10476
+
handler: ({ parser }, args, optArgs) => {
10477
+
let smashHeight = false;
10478
+
let smashDepth = false;
10479
+
const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup");
10480
+
if (tbArg) {
10481
+
// Optional [tb] argument is engaged.
10482
+
// ref: amsmath: \renewcommand{\smash}[1][tb]{%
10483
+
// def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}%
10484
+
let letter = "";
10485
+
for (let i = 0; i < tbArg.body.length; ++i) {
10486
+
const node = tbArg.body[i];
10487
+
// TODO: Write an AssertSymbolNode
10488
+
letter = node.text;
10489
+
if (letter === "t") {
10490
+
smashHeight = true;
10491
+
} else if (letter === "b") {
10492
+
smashDepth = true;
10493
+
} else {
10494
+
smashHeight = false;
10495
+
smashDepth = false;
10496
+
break;
10497
+
}
10498
+
}
10499
+
} else {
10500
+
smashHeight = true;
10501
+
smashDepth = true;
10502
+
}
10503
+
10504
+
const body = args[0];
10505
+
return {
10506
+
type: "smash",
10507
+
mode: parser.mode,
10508
+
body,
10509
+
smashHeight,
10510
+
smashDepth
10511
+
};
10512
+
},
10513
+
mathmlBuilder: (group, style) => {
10514
+
const node = new mathMLTree.MathNode("mpadded", [buildGroup$1(group.body, style)]);
10515
+
10516
+
if (group.smashHeight) {
10517
+
node.setAttribute("height", "0px");
10518
+
}
10519
+
10520
+
if (group.smashDepth) {
10521
+
node.setAttribute("depth", "0px");
10522
+
}
10523
+
10524
+
return node;
10525
+
}
10526
+
});
10527
+
10528
+
defineFunction({
10529
+
type: "sqrt",
10530
+
names: ["\\sqrt"],
10531
+
props: {
10532
+
numArgs: 1,
10533
+
numOptionalArgs: 1
10534
+
},
10535
+
handler({ parser }, args, optArgs) {
10536
+
const index = optArgs[0];
10537
+
const body = args[0];
10538
+
return {
10539
+
type: "sqrt",
10540
+
mode: parser.mode,
10541
+
body,
10542
+
index
10543
+
};
10544
+
},
10545
+
mathmlBuilder(group, style) {
10546
+
const { body, index } = group;
10547
+
return index
10548
+
? new mathMLTree.MathNode("mroot", [
10549
+
buildGroup$1(body, style),
10550
+
buildGroup$1(index, style.incrementLevel())
10551
+
])
10552
+
: new mathMLTree.MathNode("msqrt", [buildGroup$1(body, style)]);
10553
+
}
10554
+
});
10555
+
10556
+
const styleMap = {
10557
+
display: 0,
10558
+
text: 1,
10559
+
script: 2,
10560
+
scriptscript: 3
10561
+
};
10562
+
10563
+
const styleAttributes = {
10564
+
display: ["0", "true"],
10565
+
text: ["0", "false"],
10566
+
script: ["1", "false"],
10567
+
scriptscript: ["2", "false"]
10568
+
};
10569
+
10570
+
defineFunction({
10571
+
type: "styling",
10572
+
names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"],
10573
+
props: {
10574
+
numArgs: 0,
10575
+
allowedInText: true,
10576
+
primitive: true
10577
+
},
10578
+
handler({ breakOnTokenText, funcName, parser }, args) {
10579
+
// parse out the implicit body
10580
+
const body = parser.parseExpression(true, breakOnTokenText, true);
10581
+
10582
+
const scriptLevel = funcName.slice(1, funcName.length - 5);
10583
+
return {
10584
+
type: "styling",
10585
+
mode: parser.mode,
10586
+
// Figure out what scriptLevel to use by pulling out the scriptLevel from
10587
+
// the function name
10588
+
scriptLevel,
10589
+
body
10590
+
};
10591
+
},
10592
+
mathmlBuilder(group, style) {
10593
+
// Figure out what scriptLevel we're changing to.
10594
+
const newStyle = style.withLevel(styleMap[group.scriptLevel]);
10595
+
// The style argument in the next line does NOT directly set a MathML script level.
10596
+
// It just tracks the style level, in case we need to know it for supsub or mathchoice.
10597
+
const inner = buildExpression(group.body, newStyle);
10598
+
// Wrap with an <mstyle> element.
10599
+
const node = wrapWithMstyle(inner);
10600
+
10601
+
const attr = styleAttributes[group.scriptLevel];
10602
+
10603
+
// Here is where we set the MathML script level.
10604
+
node.setAttribute("scriptlevel", attr[0]);
10605
+
node.setAttribute("displaystyle", attr[1]);
10606
+
10607
+
return node;
10608
+
}
10609
+
});
10610
+
10611
+
/**
10612
+
* Sometimes, groups perform special rules when they have superscripts or
10613
+
* subscripts attached to them. This function lets the `supsub` group know that
10614
+
* Sometimes, groups perform special rules when they have superscripts or
10615
+
* its inner element should handle the superscripts and subscripts instead of
10616
+
* handling them itself.
10617
+
*/
10618
+
10619
+
// Helpers
10620
+
const symbolRegEx = /^m(over|under|underover)$/;
10621
+
10622
+
// Super scripts and subscripts, whose precise placement can depend on other
10623
+
// functions that precede them.
10624
+
defineFunctionBuilders({
10625
+
type: "supsub",
10626
+
mathmlBuilder(group, style) {
10627
+
// Is the inner group a relevant horizonal brace?
10628
+
let isBrace = false;
10629
+
let isOver;
10630
+
let isSup;
10631
+
let appendApplyFunction = false;
10632
+
let appendSpace = false;
10633
+
let needsLeadingSpace = false;
10634
+
10635
+
if (group.base && group.base.type === "horizBrace") {
10636
+
isSup = !!group.sup;
10637
+
if (isSup === group.base.isOver) {
10638
+
isBrace = true;
10639
+
isOver = group.base.isOver;
10640
+
}
10641
+
}
10642
+
10643
+
if (group.base && !group.base.stack &&
10644
+
(group.base.type === "op" || group.base.type === "operatorname")) {
10645
+
group.base.parentIsSupSub = true;
10646
+
appendApplyFunction = !group.base.symbol;
10647
+
appendSpace = appendApplyFunction && !group.isFollowedByDelimiter;
10648
+
needsLeadingSpace = group.base.needsLeadingSpace;
10649
+
}
10650
+
10651
+
const children = group.base && group.base.stack
10652
+
? [buildGroup$1(group.base.body[0], style)]
10653
+
: [buildGroup$1(group.base, style)];
10654
+
10655
+
// Note regarding scriptstyle level.
10656
+
// (Sub|super)scripts should not shrink beyond MathML scriptlevel 2 aka \scriptscriptstyle
10657
+
// Ref: https://w3c.github.io/mathml-core/#the-displaystyle-and-scriptlevel-attributes
10658
+
// (BTW, MathML scriptlevel 2 is equal to Temml level 3.)
10659
+
// But Chromium continues to shrink the (sub|super)scripts. So we explicitly set scriptlevel 2.
10660
+
10661
+
const childStyle = style.inSubOrSup();
10662
+
if (group.sub) {
10663
+
const sub = buildGroup$1(group.sub, childStyle);
10664
+
if (style.level === 3) { sub.setAttribute("scriptlevel", "2"); }
10665
+
children.push(sub);
10666
+
}
10667
+
10668
+
if (group.sup) {
10669
+
const sup = buildGroup$1(group.sup, childStyle);
10670
+
if (style.level === 3) { sup.setAttribute("scriptlevel", "2"); }
10671
+
const testNode = sup.type === "mrow" ? sup.children[0] : sup;
10672
+
if ((testNode && testNode.type === "mo" && testNode.classes.includes("tml-prime"))
10673
+
&& group.base && group.base.text && "fF".indexOf(group.base.text) > -1) {
10674
+
// Chromium does not address italic correction on prime. Prevent f′ from overlapping.
10675
+
testNode.classes.push("prime-pad");
10676
+
}
10677
+
children.push(sup);
10678
+
}
10679
+
10680
+
let nodeType;
10681
+
if (isBrace) {
10682
+
nodeType = isOver ? "mover" : "munder";
10683
+
} else if (!group.sub) {
10684
+
const base = group.base;
10685
+
if (
10686
+
base &&
10687
+
base.type === "op" &&
10688
+
base.limits &&
10689
+
(style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
10690
+
) {
10691
+
nodeType = "mover";
10692
+
} else if (
10693
+
base &&
10694
+
base.type === "operatorname" &&
10695
+
base.alwaysHandleSupSub &&
10696
+
(base.limits || style.level === StyleLevel.DISPLAY)
10697
+
) {
10698
+
nodeType = "mover";
10699
+
} else {
10700
+
nodeType = "msup";
10701
+
}
10702
+
} else if (!group.sup) {
10703
+
const base = group.base;
10704
+
if (
10705
+
base &&
10706
+
base.type === "op" &&
10707
+
base.limits &&
10708
+
(style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
10709
+
) {
10710
+
nodeType = "munder";
10711
+
} else if (
10712
+
base &&
10713
+
base.type === "operatorname" &&
10714
+
base.alwaysHandleSupSub &&
10715
+
(base.limits || style.level === StyleLevel.DISPLAY)
10716
+
) {
10717
+
nodeType = "munder";
10718
+
} else {
10719
+
nodeType = "msub";
10720
+
}
10721
+
} else {
10722
+
const base = group.base;
10723
+
if (base && ((base.type === "op" && base.limits) || base.type === "multiscript") &&
10724
+
(style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub)
10725
+
) {
10726
+
nodeType = "munderover";
10727
+
} else if (
10728
+
base &&
10729
+
base.type === "operatorname" &&
10730
+
base.alwaysHandleSupSub &&
10731
+
(style.level === StyleLevel.DISPLAY || base.limits)
10732
+
) {
10733
+
nodeType = "munderover";
10734
+
} else {
10735
+
nodeType = "msubsup";
10736
+
}
10737
+
}
10738
+
10739
+
let node = new mathMLTree.MathNode(nodeType, children);
10740
+
if (appendApplyFunction) {
10741
+
// Append an <mo>⁡</mo>.
10742
+
// ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
10743
+
const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]);
10744
+
if (needsLeadingSpace) {
10745
+
const space = new mathMLTree.MathNode("mspace");
10746
+
space.setAttribute("width", "0.1667em"); // thin space.
10747
+
node = mathMLTree.newDocumentFragment([space, node, operator]);
10748
+
} else {
10749
+
node = mathMLTree.newDocumentFragment([node, operator]);
10750
+
}
10751
+
if (appendSpace) {
10752
+
const space = new mathMLTree.MathNode("mspace");
10753
+
space.setAttribute("width", "0.1667em"); // thin space.
10754
+
node.children.push(space);
10755
+
}
10756
+
} else if (symbolRegEx.test(nodeType)) {
10757
+
// Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
10758
+
node = new mathMLTree.MathNode("mrow", [node]);
10759
+
}
10760
+
10761
+
return node
10762
+
}
10763
+
});
10764
+
10765
+
// Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js.
10766
+
10767
+
const temml_short = ["\\shortmid", "\\nshortmid", "\\shortparallel",
10768
+
"\\nshortparallel", "\\smallsetminus"];
10769
+
10770
+
const arrows = ["\\Rsh", "\\Lsh", "\\restriction"];
10771
+
10772
+
const isArrow = str => {
10773
+
if (str.length === 1) {
10774
+
const codePoint = str.codePointAt(0);
10775
+
return (0x218f < codePoint && codePoint < 0x2200)
10776
+
}
10777
+
return str.indexOf("arrow") > -1 || str.indexOf("harpoon") > -1 || arrows.includes(str)
10778
+
};
10779
+
10780
+
defineFunctionBuilders({
10781
+
type: "atom",
10782
+
mathmlBuilder(group, style) {
10783
+
const node = new mathMLTree.MathNode("mo", [makeText(group.text, group.mode)]);
10784
+
if (group.family === "punct") {
10785
+
node.setAttribute("separator", "true");
10786
+
} else if (group.family === "open" || group.family === "close") {
10787
+
// Delims built here should not stretch vertically.
10788
+
// See delimsizing.js for stretchy delims.
10789
+
if (group.family === "open") {
10790
+
node.setAttribute("form", "prefix");
10791
+
// Set an explicit attribute for stretch. Otherwise Firefox may do it wrong.
10792
+
node.setAttribute("stretchy", "false");
10793
+
} else if (group.family === "close") {
10794
+
node.setAttribute("form", "postfix");
10795
+
node.setAttribute("stretchy", "false");
10796
+
}
10797
+
} else if (group.text === "\\mid") {
10798
+
// Firefox messes up this spacing if at the end of an <mrow>. See it explicitly.
10799
+
node.setAttribute("lspace", "0.22em"); // medium space
10800
+
node.setAttribute("rspace", "0.22em");
10801
+
node.setAttribute("stretchy", "false");
10802
+
} else if (group.family === "rel" && isArrow(group.text)) {
10803
+
node.setAttribute("stretchy", "false");
10804
+
} else if (temml_short.includes(group.text)) {
10805
+
node.setAttribute("mathsize", "70%");
10806
+
} else if (group.text === ":") {
10807
+
// ":" is not in the MathML operator dictionary. Give it BIN spacing.
10808
+
node.attributes.lspace = "0.2222em";
10809
+
node.attributes.rspace = "0.2222em";
10810
+
}
10811
+
return node;
10812
+
}
10813
+
});
10814
+
10815
+
/**
10816
+
* Maps TeX font commands to "mathvariant" attribute in buildMathML.js
10817
+
*/
10818
+
const fontMap = {
10819
+
// styles
10820
+
mathbf: "bold",
10821
+
mathrm: "normal",
10822
+
textit: "italic",
10823
+
mathit: "italic",
10824
+
mathnormal: "italic",
10825
+
10826
+
// families
10827
+
mathbb: "double-struck",
10828
+
mathcal: "script",
10829
+
mathfrak: "fraktur",
10830
+
mathscr: "script",
10831
+
mathsf: "sans-serif",
10832
+
mathtt: "monospace"
10833
+
};
10834
+
10835
+
/**
10836
+
* Returns the math variant as a string or null if none is required.
10837
+
*/
10838
+
const getVariant = function(group, style) {
10839
+
// Handle font specifiers as best we can.
10840
+
// Chromium does not support the MathML mathvariant attribute.
10841
+
// So we'll use Unicode replacement characters instead.
10842
+
// But first, determine the math variant.
10843
+
10844
+
// Deal with the \textit, \textbf, etc., functions.
10845
+
if (style.fontFamily === "texttt") {
10846
+
return "monospace"
10847
+
} else if (style.fontFamily === "textsc") {
10848
+
return "normal"; // handled via character substitution in symbolsOrd.js.
10849
+
} else if (style.fontFamily === "textsf") {
10850
+
if (style.fontShape === "textit" && style.fontWeight === "textbf") {
10851
+
return "sans-serif-bold-italic"
10852
+
} else if (style.fontShape === "textit") {
10853
+
return "sans-serif-italic"
10854
+
} else if (style.fontWeight === "textbf") {
10855
+
return "sans-serif-bold"
10856
+
} else {
10857
+
return "sans-serif"
10858
+
}
10859
+
} else if (style.fontShape === "textit" && style.fontWeight === "textbf") {
10860
+
return "bold-italic"
10861
+
} else if (style.fontShape === "textit") {
10862
+
return "italic"
10863
+
} else if (style.fontWeight === "textbf") {
10864
+
return "bold"
10865
+
}
10866
+
10867
+
// Deal with the \mathit, mathbf, etc, functions.
10868
+
const font = style.font;
10869
+
if (!font || font === "mathnormal") {
10870
+
return null
10871
+
}
10872
+
10873
+
const mode = group.mode;
10874
+
switch (font) {
10875
+
case "mathit":
10876
+
return "italic"
10877
+
case "mathrm": {
10878
+
const codePoint = group.text.codePointAt(0);
10879
+
// LaTeX \mathrm returns italic for Greek characters.
10880
+
return (0x03ab < codePoint && codePoint < 0x03cf) ? "italic" : "normal"
10881
+
}
10882
+
case "greekItalic":
10883
+
return "italic"
10884
+
case "up@greek":
10885
+
return "normal"
10886
+
case "boldsymbol":
10887
+
case "mathboldsymbol":
10888
+
return "bold-italic"
10889
+
case "mathbf":
10890
+
return "bold"
10891
+
case "mathbb":
10892
+
return "double-struck"
10893
+
case "mathfrak":
10894
+
return "fraktur"
10895
+
case "mathscr":
10896
+
case "mathcal":
10897
+
return "script"
10898
+
case "mathsf":
10899
+
return "sans-serif"
10900
+
case "mathsfit":
10901
+
return "sans-serif-italic"
10902
+
case "mathtt":
10903
+
return "monospace"
10904
+
}
10905
+
10906
+
let text = group.text;
10907
+
if (symbols[mode][text] && symbols[mode][text].replace) {
10908
+
text = symbols[mode][text].replace;
10909
+
}
10910
+
10911
+
return Object.prototype.hasOwnProperty.call(fontMap, font) ? fontMap[font] : null
10912
+
};
10913
+
10914
+
// Chromium does not support the MathML `mathvariant` attribute.
10915
+
// Instead, we replace ASCII characters with Unicode characters that
10916
+
// are defined in the font as bold, italic, double-struck, etc.
10917
+
// This module identifies those Unicode code points.
10918
+
10919
+
// First, a few helpers.
10920
+
const script = Object.freeze({
10921
+
B: 0x20EA, // Offset from ASCII B to Unicode script B
10922
+
E: 0x20EB,
10923
+
F: 0x20EB,
10924
+
H: 0x20C3,
10925
+
I: 0x20C7,
10926
+
L: 0x20C6,
10927
+
M: 0x20E6,
10928
+
R: 0x20C9,
10929
+
e: 0x20CA,
10930
+
g: 0x20A3,
10931
+
o: 0x20C5
10932
+
});
10933
+
10934
+
const frak = Object.freeze({
10935
+
C: 0x20EA,
10936
+
H: 0x20C4,
10937
+
I: 0x20C8,
10938
+
R: 0x20CA,
10939
+
Z: 0x20CE
10940
+
});
10941
+
10942
+
const bbb = Object.freeze({
10943
+
C: 0x20BF, // blackboard bold
10944
+
H: 0x20C5,
10945
+
N: 0x20C7,
10946
+
P: 0x20C9,
10947
+
Q: 0x20C9,
10948
+
R: 0x20CB,
10949
+
Z: 0x20CA
10950
+
});
10951
+
10952
+
const bold = Object.freeze({
10953
+
"\u03f5": 0x1D2E7, // lunate epsilon
10954
+
"\u03d1": 0x1D30C, // vartheta
10955
+
"\u03f0": 0x1D2EE, // varkappa
10956
+
"\u03c6": 0x1D319, // varphi
10957
+
"\u03f1": 0x1D2EF, // varrho
10958
+
"\u03d6": 0x1D30B // varpi
10959
+
});
10960
+
10961
+
const boldItalic = Object.freeze({
10962
+
"\u03f5": 0x1D35B, // lunate epsilon
10963
+
"\u03d1": 0x1D380, // vartheta
10964
+
"\u03f0": 0x1D362, // varkappa
10965
+
"\u03c6": 0x1D38D, // varphi
10966
+
"\u03f1": 0x1D363, // varrho
10967
+
"\u03d6": 0x1D37F // varpi
10968
+
});
10969
+
10970
+
const boldsf = Object.freeze({
10971
+
"\u03f5": 0x1D395, // lunate epsilon
10972
+
"\u03d1": 0x1D3BA, // vartheta
10973
+
"\u03f0": 0x1D39C, // varkappa
10974
+
"\u03c6": 0x1D3C7, // varphi
10975
+
"\u03f1": 0x1D39D, // varrho
10976
+
"\u03d6": 0x1D3B9 // varpi
10977
+
});
10978
+
10979
+
const bisf = Object.freeze({
10980
+
"\u03f5": 0x1D3CF, // lunate epsilon
10981
+
"\u03d1": 0x1D3F4, // vartheta
10982
+
"\u03f0": 0x1D3D6, // varkappa
10983
+
"\u03c6": 0x1D401, // varphi
10984
+
"\u03f1": 0x1D3D7, // varrho
10985
+
"\u03d6": 0x1D3F3 // varpi
10986
+
});
10987
+
10988
+
// Code point offsets below are derived from https://www.unicode.org/charts/PDF/U1D400.pdf
10989
+
const offset = Object.freeze({
10990
+
upperCaseLatin: { // A-Z
10991
+
"normal": ch => { return 0 },
10992
+
"bold": ch => { return 0x1D3BF },
10993
+
"italic": ch => { return 0x1D3F3 },
10994
+
"bold-italic": ch => { return 0x1D427 },
10995
+
"script": ch => { return script[ch] || 0x1D45B },
10996
+
"script-bold": ch => { return 0x1D48F },
10997
+
"fraktur": ch => { return frak[ch] || 0x1D4C3 },
10998
+
"fraktur-bold": ch => { return 0x1D52B },
10999
+
"double-struck": ch => { return bbb[ch] || 0x1D4F7 },
11000
+
"sans-serif": ch => { return 0x1D55F },
11001
+
"sans-serif-bold": ch => { return 0x1D593 },
11002
+
"sans-serif-italic": ch => { return 0x1D5C7 },
11003
+
"sans-serif-bold-italic": ch => { return 0x1D63C },
11004
+
"monospace": ch => { return 0x1D62F }
11005
+
},
11006
+
lowerCaseLatin: { // a-z
11007
+
"normal": ch => { return 0 },
11008
+
"bold": ch => { return 0x1D3B9 },
11009
+
"italic": ch => { return ch === "h" ? 0x20A6 : 0x1D3ED },
11010
+
"bold-italic": ch => { return 0x1D421 },
11011
+
"script": ch => { return script[ch] || 0x1D455 },
11012
+
"script-bold": ch => { return 0x1D489 },
11013
+
"fraktur": ch => { return 0x1D4BD },
11014
+
"fraktur-bold": ch => { return 0x1D525 },
11015
+
"double-struck": ch => { return 0x1D4F1 },
11016
+
"sans-serif": ch => { return 0x1D559 },
11017
+
"sans-serif-bold": ch => { return 0x1D58D },
11018
+
"sans-serif-italic": ch => { return 0x1D5C1 },
11019
+
"sans-serif-bold-italic": ch => { return 0x1D5F5 },
11020
+
"monospace": ch => { return 0x1D629 }
11021
+
},
11022
+
upperCaseGreek: { // A-Ω
11023
+
"normal": ch => { return 0 },
11024
+
"bold": ch => { return 0x1D317 },
11025
+
"italic": ch => { return 0x1D351 },
11026
+
// \boldsymbol actually returns upright bold for upperCaseGreek
11027
+
"bold-italic": ch => { return 0x1D317 },
11028
+
"script": ch => { return 0 },
11029
+
"script-bold": ch => { return 0 },
11030
+
"fraktur": ch => { return 0 },
11031
+
"fraktur-bold": ch => { return 0 },
11032
+
"double-struck": ch => { return 0 },
11033
+
// Unicode has no code points for regular-weight san-serif Greek. Use bold.
11034
+
"sans-serif": ch => { return 0x1D3C5 },
11035
+
"sans-serif-bold": ch => { return 0x1D3C5 },
11036
+
"sans-serif-italic": ch => { return 0 },
11037
+
"sans-serif-bold-italic": ch => { return 0x1D3FF },
11038
+
"monospace": ch => { return 0 }
11039
+
},
11040
+
lowerCaseGreek: { // α-ω
11041
+
"normal": ch => { return 0 },
11042
+
"bold": ch => { return 0x1D311 },
11043
+
"italic": ch => { return 0x1D34B },
11044
+
"bold-italic": ch => { return ch === "\u03d5" ? 0x1D37E : 0x1D385 },
11045
+
"script": ch => { return 0 },
11046
+
"script-bold": ch => { return 0 },
11047
+
"fraktur": ch => { return 0 },
11048
+
"fraktur-bold": ch => { return 0 },
11049
+
"double-struck": ch => { return 0 },
11050
+
// Unicode has no code points for regular-weight san-serif Greek. Use bold.
11051
+
"sans-serif": ch => { return 0x1D3BF },
11052
+
"sans-serif-bold": ch => { return 0x1D3BF },
11053
+
"sans-serif-italic": ch => { return 0 },
11054
+
"sans-serif-bold-italic": ch => { return 0x1D3F9 },
11055
+
"monospace": ch => { return 0 }
11056
+
},
11057
+
varGreek: { // \varGamma, etc
11058
+
"normal": ch => { return 0 },
11059
+
"bold": ch => { return bold[ch] || -51 },
11060
+
"italic": ch => { return 0 },
11061
+
"bold-italic": ch => { return boldItalic[ch] || 0x3A },
11062
+
"script": ch => { return 0 },
11063
+
"script-bold": ch => { return 0 },
11064
+
"fraktur": ch => { return 0 },
11065
+
"fraktur-bold": ch => { return 0 },
11066
+
"double-struck": ch => { return 0 },
11067
+
"sans-serif": ch => { return boldsf[ch] || 0x74 },
11068
+
"sans-serif-bold": ch => { return boldsf[ch] || 0x74 },
11069
+
"sans-serif-italic": ch => { return 0 },
11070
+
"sans-serif-bold-italic": ch => { return bisf[ch] || 0xAE },
11071
+
"monospace": ch => { return 0 }
11072
+
},
11073
+
numeral: { // 0-9
11074
+
"normal": ch => { return 0 },
11075
+
"bold": ch => { return 0x1D79E },
11076
+
"italic": ch => { return 0 },
11077
+
"bold-italic": ch => { return 0 },
11078
+
"script": ch => { return 0 },
11079
+
"script-bold": ch => { return 0 },
11080
+
"fraktur": ch => { return 0 },
11081
+
"fraktur-bold": ch => { return 0 },
11082
+
"double-struck": ch => { return 0x1D7A8 },
11083
+
"sans-serif": ch => { return 0x1D7B2 },
11084
+
"sans-serif-bold": ch => { return 0x1D7BC },
11085
+
"sans-serif-italic": ch => { return 0 },
11086
+
"sans-serif-bold-italic": ch => { return 0 },
11087
+
"monospace": ch => { return 0x1D7C6 }
11088
+
}
11089
+
});
11090
+
11091
+
const variantChar = (ch, variant) => {
11092
+
const codePoint = ch.codePointAt(0);
11093
+
const block = 0x40 < codePoint && codePoint < 0x5b
11094
+
? "upperCaseLatin"
11095
+
: 0x60 < codePoint && codePoint < 0x7b
11096
+
? "lowerCaseLatin"
11097
+
: (0x390 < codePoint && codePoint < 0x3AA)
11098
+
? "upperCaseGreek"
11099
+
: 0x3B0 < codePoint && codePoint < 0x3CA || ch === "\u03d5"
11100
+
? "lowerCaseGreek"
11101
+
: 0x1D6E1 < codePoint && codePoint < 0x1D6FC || bold[ch]
11102
+
? "varGreek"
11103
+
: (0x2F < codePoint && codePoint < 0x3A)
11104
+
? "numeral"
11105
+
: "other";
11106
+
return block === "other"
11107
+
? ch
11108
+
: String.fromCodePoint(codePoint + offset[block][variant](ch))
11109
+
};
11110
+
11111
+
const smallCaps = Object.freeze({
11112
+
a: "ᴀ",
11113
+
b: "ʙ",
11114
+
c: "ᴄ",
11115
+
d: "ᴅ",
11116
+
e: "ᴇ",
11117
+
f: "ꜰ",
11118
+
g: "ɢ",
11119
+
h: "ʜ",
11120
+
i: "ɪ",
11121
+
j: "ᴊ",
11122
+
k: "ᴋ",
11123
+
l: "ʟ",
11124
+
m: "ᴍ",
11125
+
n: "ɴ",
11126
+
o: "ᴏ",
11127
+
p: "ᴘ",
11128
+
q: "ǫ",
11129
+
r: "ʀ",
11130
+
s: "s",
11131
+
t: "ᴛ",
11132
+
u: "ᴜ",
11133
+
v: "ᴠ",
11134
+
w: "ᴡ",
11135
+
x: "x",
11136
+
y: "ʏ",
11137
+
z: "ᴢ"
11138
+
});
11139
+
11140
+
// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
11141
+
// src/symbols.js.
11142
+
11143
+
const numberRegEx = /^\d(?:[\d,.]*\d)?$/;
11144
+
const latinRegEx = /[A-Ba-z]/;
11145
+
const primes = new Set(["\\prime", "\\dprime", "\\trprime", "\\qprime",
11146
+
"\\backprime", "\\backdprime", "\\backtrprime"]);
11147
+
11148
+
const italicNumber = (text, variant, tag) => {
11149
+
const mn = new mathMLTree.MathNode(tag, [text]);
11150
+
const wrapper = new mathMLTree.MathNode("mstyle", [mn]);
11151
+
wrapper.style["font-style"] = "italic";
11152
+
wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif";
11153
+
if (variant === "bold-italic") { wrapper.style["font-weight"] = "bold"; }
11154
+
return wrapper
11155
+
};
11156
+
11157
+
defineFunctionBuilders({
11158
+
type: "mathord",
11159
+
mathmlBuilder(group, style) {
11160
+
const text = makeText(group.text, group.mode, style);
11161
+
const codePoint = text.text.codePointAt(0);
11162
+
// Test for upper-case Greek
11163
+
const defaultVariant = (0x0390 < codePoint && codePoint < 0x03aa) ? "normal" : "italic";
11164
+
const variant = getVariant(group, style) || defaultVariant;
11165
+
if (variant === "script") {
11166
+
text.text = variantChar(text.text, variant);
11167
+
return new mathMLTree.MathNode("mi", [text], [style.font])
11168
+
} else if (variant !== "italic") {
11169
+
text.text = variantChar(text.text, variant);
11170
+
}
11171
+
let node = new mathMLTree.MathNode("mi", [text]);
11172
+
// TODO: Handle U+1D49C - U+1D4CF per https://www.unicode.org/charts/PDF/U1D400.pdf
11173
+
if (variant === "normal") {
11174
+
node.setAttribute("mathvariant", "normal");
11175
+
if (text.text.length === 1) {
11176
+
// A Firefox bug will apply spacing here, but there should be none. Fix it.
11177
+
node = new mathMLTree.MathNode("mrow", [node]);
11178
+
}
11179
+
}
11180
+
return node
11181
+
}
11182
+
});
11183
+
11184
+
defineFunctionBuilders({
11185
+
type: "textord",
11186
+
mathmlBuilder(group, style) {
11187
+
let ch = group.text;
11188
+
const codePoint = ch.codePointAt(0);
11189
+
if (style.fontFamily === "textsc") {
11190
+
// Convert small latin letters to small caps.
11191
+
if (96 < codePoint && codePoint < 123) {
11192
+
ch = smallCaps[ch];
11193
+
}
11194
+
}
11195
+
const text = makeText(ch, group.mode, style);
11196
+
const variant = getVariant(group, style) || "normal";
11197
+
11198
+
let node;
11199
+
if (numberRegEx.test(group.text)) {
11200
+
const tag = group.mode === "text" ? "mtext" : "mn";
11201
+
if (variant === "italic" || variant === "bold-italic") {
11202
+
return italicNumber(text, variant, tag)
11203
+
} else {
11204
+
if (variant !== "normal") {
11205
+
text.text = text.text.split("").map(c => variantChar(c, variant)).join("");
11206
+
}
11207
+
node = new mathMLTree.MathNode(tag, [text]);
11208
+
}
11209
+
} else if (group.mode === "text") {
11210
+
if (variant !== "normal") {
11211
+
text.text = variantChar(text.text, variant);
11212
+
}
11213
+
node = new mathMLTree.MathNode("mtext", [text]);
11214
+
} else if (primes.has(group.text)) {
11215
+
node = new mathMLTree.MathNode("mo", [text]);
11216
+
// TODO: If/when Chromium uses ssty variant for prime, remove the next line.
11217
+
node.classes.push("tml-prime");
11218
+
} else {
11219
+
const origText = text.text;
11220
+
if (variant !== "italic") {
11221
+
text.text = variantChar(text.text, variant);
11222
+
}
11223
+
node = new mathMLTree.MathNode("mi", [text]);
11224
+
if (text.text === origText && latinRegEx.test(origText)) {
11225
+
node.setAttribute("mathvariant", "italic");
11226
+
}
11227
+
}
11228
+
return node
11229
+
}
11230
+
});
11231
+
11232
+
// A map of CSS-based spacing functions to their CSS class.
11233
+
const cssSpace = {
11234
+
"\\nobreak": "nobreak",
11235
+
"\\allowbreak": "allowbreak"
11236
+
};
11237
+
11238
+
// A lookup table to determine whether a spacing function/symbol should be
11239
+
// treated like a regular space character. If a symbol or command is a key
11240
+
// in this table, then it should be a regular space character. Furthermore,
11241
+
// the associated value may have a `className` specifying an extra CSS class
11242
+
// to add to the created `span`.
11243
+
const regularSpace = {
11244
+
" ": {},
11245
+
"\\ ": {},
11246
+
"~": {
11247
+
className: "nobreak"
11248
+
},
11249
+
"\\space": {},
11250
+
"\\nobreakspace": {
11251
+
className: "nobreak"
11252
+
}
11253
+
};
11254
+
11255
+
// ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in
11256
+
// src/symbols.js.
11257
+
defineFunctionBuilders({
11258
+
type: "spacing",
11259
+
mathmlBuilder(group, style) {
11260
+
let node;
11261
+
11262
+
if (Object.prototype.hasOwnProperty.call(regularSpace, group.text)) {
11263
+
// Firefox does not render a space in a <mtext> </mtext>. So write a no-break space.
11264
+
// TODO: If Firefox fixes that bug, uncomment the next line and write ch into the node.
11265
+
//const ch = (regularSpace[group.text].className === "nobreak") ? "\u00a0" : " "
11266
+
node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("\u00a0")]);
11267
+
} else if (Object.prototype.hasOwnProperty.call(cssSpace, group.text)) {
11268
+
// MathML 3.0 calls for nobreak to occur in an <mo>, not an <mtext>
11269
+
// Ref: https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs
11270
+
node = new mathMLTree.MathNode("mo");
11271
+
if (group.text === "\\nobreak") {
11272
+
node.setAttribute("linebreak", "nobreak");
11273
+
}
11274
+
} else {
11275
+
throw new ParseError(`Unknown type of space "${group.text}"`)
11276
+
}
11277
+
11278
+
return node
11279
+
}
11280
+
});
11281
+
11282
+
defineFunctionBuilders({
11283
+
type: "tag"
11284
+
});
11285
+
11286
+
// For a \tag, the work usually done in a mathmlBuilder is instead done in buildMathML.js.
11287
+
// That way, a \tag can be pulled out of the parse tree and wrapped around the outer node.
11288
+
11289
+
// Non-mathy text, possibly in a font
11290
+
const textFontFamilies = {
11291
+
"\\text": undefined,
11292
+
"\\textrm": "textrm",
11293
+
"\\textsf": "textsf",
11294
+
"\\texttt": "texttt",
11295
+
"\\textnormal": "textrm",
11296
+
"\\textsc": "textsc" // small caps
11297
+
};
11298
+
11299
+
const textFontWeights = {
11300
+
"\\textbf": "textbf",
11301
+
"\\textmd": "textmd"
11302
+
};
11303
+
11304
+
const textFontShapes = {
11305
+
"\\textit": "textit",
11306
+
"\\textup": "textup"
11307
+
};
11308
+
11309
+
const styleWithFont = (group, style) => {
11310
+
const font = group.font;
11311
+
// Checks if the argument is a font family or a font style.
11312
+
if (!font) {
11313
+
return style;
11314
+
} else if (textFontFamilies[font]) {
11315
+
return style.withTextFontFamily(textFontFamilies[font]);
11316
+
} else if (textFontWeights[font]) {
11317
+
return style.withTextFontWeight(textFontWeights[font]);
11318
+
} else if (font === "\\emph") {
11319
+
return style.fontShape === "textit"
11320
+
? style.withTextFontShape("textup")
11321
+
: style.withTextFontShape("textit")
11322
+
}
11323
+
return style.withTextFontShape(textFontShapes[font])
11324
+
};
11325
+
11326
+
defineFunction({
11327
+
type: "text",
11328
+
names: [
11329
+
// Font families
11330
+
"\\text",
11331
+
"\\textrm",
11332
+
"\\textsf",
11333
+
"\\texttt",
11334
+
"\\textnormal",
11335
+
"\\textsc",
11336
+
// Font weights
11337
+
"\\textbf",
11338
+
"\\textmd",
11339
+
// Font Shapes
11340
+
"\\textit",
11341
+
"\\textup",
11342
+
"\\emph"
11343
+
],
11344
+
props: {
11345
+
numArgs: 1,
11346
+
argTypes: ["text"],
11347
+
allowedInArgument: true,
11348
+
allowedInText: true
11349
+
},
11350
+
handler({ parser, funcName }, args) {
11351
+
const body = args[0];
11352
+
return {
11353
+
type: "text",
11354
+
mode: parser.mode,
11355
+
body: ordargument(body),
11356
+
font: funcName
11357
+
};
11358
+
},
11359
+
mathmlBuilder(group, style) {
11360
+
const newStyle = styleWithFont(group, style);
11361
+
const mrow = buildExpressionRow(group.body, newStyle);
11362
+
return consolidateText(mrow)
11363
+
}
11364
+
});
11365
+
11366
+
// \vcenter: Vertically center the argument group on the math axis.
11367
+
11368
+
defineFunction({
11369
+
type: "vcenter",
11370
+
names: ["\\vcenter"],
11371
+
props: {
11372
+
numArgs: 1,
11373
+
argTypes: ["original"],
11374
+
allowedInText: false
11375
+
},
11376
+
handler({ parser }, args) {
11377
+
return {
11378
+
type: "vcenter",
11379
+
mode: parser.mode,
11380
+
body: args[0]
11381
+
};
11382
+
},
11383
+
mathmlBuilder(group, style) {
11384
+
// Use a math table to create vertically centered content.
11385
+
const mtd = new mathMLTree.MathNode("mtd", [buildGroup$1(group.body, style)]);
11386
+
mtd.style.padding = "0";
11387
+
const mtr = new mathMLTree.MathNode("mtr", [mtd]);
11388
+
return new mathMLTree.MathNode("mtable", [mtr])
11389
+
}
11390
+
});
11391
+
11392
+
defineFunction({
11393
+
type: "verb",
11394
+
names: ["\\verb"],
11395
+
props: {
11396
+
numArgs: 0,
11397
+
allowedInText: true
11398
+
},
11399
+
handler(context, args, optArgs) {
11400
+
// \verb and \verb* are dealt with directly in Parser.js.
11401
+
// If we end up here, it's because of a failure to match the two delimiters
11402
+
// in the regex in Lexer.js. LaTeX raises the following error when \verb is
11403
+
// terminated by end of line (or file).
11404
+
throw new ParseError("\\verb ended by end of line instead of matching delimiter");
11405
+
},
11406
+
mathmlBuilder(group, style) {
11407
+
const text = new mathMLTree.TextNode(makeVerb(group));
11408
+
const node = new mathMLTree.MathNode("mtext", [text]);
11409
+
node.setAttribute("mathvariant", "monospace");
11410
+
return node;
11411
+
}
11412
+
});
11413
+
11414
+
/**
11415
+
* Converts verb group into body string.
11416
+
*
11417
+
* \verb* replaces each space with an open box \u2423
11418
+
* \verb replaces each space with a no-break space \xA0
11419
+
*/
11420
+
const makeVerb = (group) => group.body.replace(/ /g, group.star ? "\u2423" : "\xA0");
11421
+
11422
+
/** Include this to ensure that all functions are defined. */
11423
+
11424
+
const functions = _functions;
11425
+
11426
+
/**
11427
+
* The Lexer class handles tokenizing the input in various ways. Since our
11428
+
* parser expects us to be able to backtrack, the lexer allows lexing from any
11429
+
* given starting point.
11430
+
*
11431
+
* Its main exposed function is the `lex` function, which takes a position to
11432
+
* lex from and a type of token to lex. It defers to the appropriate `_innerLex`
11433
+
* function.
11434
+
*
11435
+
* The various `_innerLex` functions perform the actual lexing of different
11436
+
* kinds.
11437
+
*/
11438
+
11439
+
11440
+
/* The following tokenRegex
11441
+
* - matches typical whitespace (but not NBSP etc.) using its first two groups
11442
+
* - does not match any control character \x00-\x1f except whitespace
11443
+
* - does not match a bare backslash
11444
+
* - matches any ASCII character except those just mentioned
11445
+
* - does not match the BMP private use area \uE000-\uF8FF
11446
+
* - does not match bare surrogate code units
11447
+
* - matches any BMP character except for those just described
11448
+
* - matches any valid Unicode surrogate pair
11449
+
* - mathches numerals
11450
+
* - matches a backslash followed by one or more whitespace characters
11451
+
* - matches a backslash followed by one or more letters then whitespace
11452
+
* - matches a backslash followed by any BMP character
11453
+
* Capturing groups:
11454
+
* [1] regular whitespace
11455
+
* [2] backslash followed by whitespace
11456
+
* [3] anything else, which may include:
11457
+
* [4] left character of \verb*
11458
+
* [5] left character of \verb
11459
+
* [6] backslash followed by word, excluding any trailing whitespace
11460
+
* Just because the Lexer matches something doesn't mean it's valid input:
11461
+
* If there is no matching function or symbol definition, the Parser will
11462
+
* still reject the input.
11463
+
*/
11464
+
const spaceRegexString = "[ \r\n\t]";
11465
+
const controlWordRegexString = "\\\\[a-zA-Z@]+";
11466
+
const controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]";
11467
+
const controlWordWhitespaceRegexString = `(${controlWordRegexString})${spaceRegexString}*`;
11468
+
const controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*";
11469
+
const combiningDiacriticalMarkString = "[\u0300-\u036f]";
11470
+
const combiningDiacriticalMarksEndRegex = new RegExp(`${combiningDiacriticalMarkString}+$`);
11471
+
const tokenRegexString =
11472
+
`(${spaceRegexString}+)|` + // whitespace
11473
+
`${controlSpaceRegexString}|` + // whitespace
11474
+
"([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
11475
+
`${combiningDiacriticalMarkString}*` + // ...plus accents
11476
+
"|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair
11477
+
`${combiningDiacriticalMarkString}*` + // ...plus accents
11478
+
"|\\\\verb\\*([^]).*?\\4" + // \verb*
11479
+
"|\\\\verb([^*a-zA-Z]).*?\\5" + // \verb unstarred
11480
+
`|${controlWordWhitespaceRegexString}` + // \macroName + spaces
11481
+
`|${controlSymbolRegexString})`; // \\, \', etc.
11482
+
11483
+
/** Main Lexer class */
11484
+
class Lexer {
11485
+
constructor(input, settings) {
11486
+
// Separate accents from characters
11487
+
this.input = input;
11488
+
this.settings = settings;
11489
+
this.tokenRegex = new RegExp(tokenRegexString, 'g');
11490
+
// Category codes. The lexer only supports comment characters (14) for now.
11491
+
// MacroExpander additionally distinguishes active (13).
11492
+
this.catcodes = {
11493
+
"%": 14, // comment character
11494
+
"~": 13 // active character
11495
+
};
11496
+
}
11497
+
11498
+
setCatcode(char, code) {
11499
+
this.catcodes[char] = code;
11500
+
}
11501
+
11502
+
/**
11503
+
* This function lexes a single token.
11504
+
*/
11505
+
lex() {
11506
+
const input = this.input;
11507
+
const pos = this.tokenRegex.lastIndex;
11508
+
if (pos === input.length) {
11509
+
return new Token("EOF", new SourceLocation(this, pos, pos));
11510
+
}
11511
+
const match = this.tokenRegex.exec(input);
11512
+
if (match === null || match.index !== pos) {
11513
+
throw new ParseError(
11514
+
`Unexpected character: '${input[pos]}'`,
11515
+
new Token(input[pos], new SourceLocation(this, pos, pos + 1))
11516
+
);
11517
+
}
11518
+
const text = match[6] || match[3] || (match[2] ? "\\ " : " ");
11519
+
11520
+
if (this.catcodes[text] === 14) {
11521
+
// comment character
11522
+
const nlIndex = input.indexOf("\n", this.tokenRegex.lastIndex);
11523
+
if (nlIndex === -1) {
11524
+
this.tokenRegex.lastIndex = input.length; // EOF
11525
+
if (this.settings.strict) {
11526
+
throw new ParseError("% comment has no terminating newline; LaTeX would " +
11527
+
"fail because of commenting the end of math mode")
11528
+
}
11529
+
} else {
11530
+
this.tokenRegex.lastIndex = nlIndex + 1;
11531
+
}
11532
+
return this.lex();
11533
+
}
11534
+
11535
+
return new Token(text, new SourceLocation(this, pos, this.tokenRegex.lastIndex));
11536
+
}
11537
+
}
11538
+
11539
+
/**
11540
+
* A `Namespace` refers to a space of nameable things like macros or lengths,
11541
+
* which can be `set` either globally or local to a nested group, using an
11542
+
* undo stack similar to how TeX implements this functionality.
11543
+
* Performance-wise, `get` and local `set` take constant time, while global
11544
+
* `set` takes time proportional to the depth of group nesting.
11545
+
*/
11546
+
11547
+
11548
+
class Namespace {
11549
+
/**
11550
+
* Both arguments are optional. The first argument is an object of
11551
+
* built-in mappings which never change. The second argument is an object
11552
+
* of initial (global-level) mappings, which will constantly change
11553
+
* according to any global/top-level `set`s done.
11554
+
*/
11555
+
constructor(builtins = {}, globalMacros = {}) {
11556
+
this.current = globalMacros;
11557
+
this.builtins = builtins;
11558
+
this.undefStack = [];
11559
+
}
11560
+
11561
+
/**
11562
+
* Start a new nested group, affecting future local `set`s.
11563
+
*/
11564
+
beginGroup() {
11565
+
this.undefStack.push({});
11566
+
}
11567
+
11568
+
/**
11569
+
* End current nested group, restoring values before the group began.
11570
+
*/
11571
+
endGroup() {
11572
+
if (this.undefStack.length === 0) {
11573
+
throw new ParseError(
11574
+
"Unbalanced namespace destruction: attempt " +
11575
+
"to pop global namespace; please report this as a bug"
11576
+
);
11577
+
}
11578
+
const undefs = this.undefStack.pop();
11579
+
for (const undef in undefs) {
11580
+
if (Object.prototype.hasOwnProperty.call(undefs, undef )) {
11581
+
if (undefs[undef] === undefined) {
11582
+
delete this.current[undef];
11583
+
} else {
11584
+
this.current[undef] = undefs[undef];
11585
+
}
11586
+
}
11587
+
}
11588
+
}
11589
+
11590
+
/**
11591
+
* Detect whether `name` has a definition. Equivalent to
11592
+
* `get(name) != null`.
11593
+
*/
11594
+
has(name) {
11595
+
return Object.prototype.hasOwnProperty.call(this.current, name ) ||
11596
+
Object.prototype.hasOwnProperty.call(this.builtins, name );
11597
+
}
11598
+
11599
+
/**
11600
+
* Get the current value of a name, or `undefined` if there is no value.
11601
+
*
11602
+
* Note: Do not use `if (namespace.get(...))` to detect whether a macro
11603
+
* is defined, as the definition may be the empty string which evaluates
11604
+
* to `false` in JavaScript. Use `if (namespace.get(...) != null)` or
11605
+
* `if (namespace.has(...))`.
11606
+
*/
11607
+
get(name) {
11608
+
if (Object.prototype.hasOwnProperty.call(this.current, name )) {
11609
+
return this.current[name];
11610
+
} else {
11611
+
return this.builtins[name];
11612
+
}
11613
+
}
11614
+
11615
+
/**
11616
+
* Set the current value of a name, and optionally set it globally too.
11617
+
* Local set() sets the current value and (when appropriate) adds an undo
11618
+
* operation to the undo stack. Global set() may change the undo
11619
+
* operation at every level, so takes time linear in their number.
11620
+
*/
11621
+
set(name, value, global = false) {
11622
+
if (global) {
11623
+
// Global set is equivalent to setting in all groups. Simulate this
11624
+
// by destroying any undos currently scheduled for this name,
11625
+
// and adding an undo with the *new* value (in case it later gets
11626
+
// locally reset within this environment).
11627
+
for (let i = 0; i < this.undefStack.length; i++) {
11628
+
delete this.undefStack[i][name];
11629
+
}
11630
+
if (this.undefStack.length > 0) {
11631
+
this.undefStack[this.undefStack.length - 1][name] = value;
11632
+
}
11633
+
} else {
11634
+
// Undo this set at end of this group (possibly to `undefined`),
11635
+
// unless an undo is already in place, in which case that older
11636
+
// value is the correct one.
11637
+
const top = this.undefStack[this.undefStack.length - 1];
11638
+
if (top && !Object.prototype.hasOwnProperty.call(top, name )) {
11639
+
top[name] = this.current[name];
11640
+
}
11641
+
}
11642
+
this.current[name] = value;
11643
+
}
11644
+
}
11645
+
11646
+
/**
11647
+
* This file contains the “gullet” where macros are expanded
11648
+
* until only non-macro tokens remain.
11649
+
*/
11650
+
11651
+
11652
+
// List of commands that act like macros but aren't defined as a macro,
11653
+
// function, or symbol. Used in `isDefined`.
11654
+
const implicitCommands = {
11655
+
"^": true, // Parser.js
11656
+
_: true, // Parser.js
11657
+
"\\limits": true, // Parser.js
11658
+
"\\nolimits": true // Parser.js
11659
+
};
11660
+
11661
+
class MacroExpander {
11662
+
constructor(input, settings, mode) {
11663
+
this.settings = settings;
11664
+
this.expansionCount = 0;
11665
+
this.feed(input);
11666
+
// Make new global namespace
11667
+
this.macros = new Namespace(macros, settings.macros);
11668
+
this.mode = mode;
11669
+
this.stack = []; // contains tokens in REVERSE order
11670
+
}
11671
+
11672
+
/**
11673
+
* Feed a new input string to the same MacroExpander
11674
+
* (with existing macros etc.).
11675
+
*/
11676
+
feed(input) {
11677
+
this.lexer = new Lexer(input, this.settings);
11678
+
}
11679
+
11680
+
/**
11681
+
* Switches between "text" and "math" modes.
11682
+
*/
11683
+
switchMode(newMode) {
11684
+
this.mode = newMode;
11685
+
}
11686
+
11687
+
/**
11688
+
* Start a new group nesting within all namespaces.
11689
+
*/
11690
+
beginGroup() {
11691
+
this.macros.beginGroup();
11692
+
}
11693
+
11694
+
/**
11695
+
* End current group nesting within all namespaces.
11696
+
*/
11697
+
endGroup() {
11698
+
this.macros.endGroup();
11699
+
}
11700
+
11701
+
/**
11702
+
* Returns the topmost token on the stack, without expanding it.
11703
+
* Similar in behavior to TeX's `\futurelet`.
11704
+
*/
11705
+
future() {
11706
+
if (this.stack.length === 0) {
11707
+
this.pushToken(this.lexer.lex());
11708
+
}
11709
+
return this.stack[this.stack.length - 1]
11710
+
}
11711
+
11712
+
/**
11713
+
* Remove and return the next unexpanded token.
11714
+
*/
11715
+
popToken() {
11716
+
this.future(); // ensure non-empty stack
11717
+
return this.stack.pop();
11718
+
}
11719
+
11720
+
/**
11721
+
* Add a given token to the token stack. In particular, this get be used
11722
+
* to put back a token returned from one of the other methods.
11723
+
*/
11724
+
pushToken(token) {
11725
+
this.stack.push(token);
11726
+
}
11727
+
11728
+
/**
11729
+
* Append an array of tokens to the token stack.
11730
+
*/
11731
+
pushTokens(tokens) {
11732
+
this.stack.push(...tokens);
11733
+
}
11734
+
11735
+
/**
11736
+
* Find an macro argument without expanding tokens and append the array of
11737
+
* tokens to the token stack. Uses Token as a container for the result.
11738
+
*/
11739
+
scanArgument(isOptional) {
11740
+
let start;
11741
+
let end;
11742
+
let tokens;
11743
+
if (isOptional) {
11744
+
this.consumeSpaces(); // \@ifnextchar gobbles any space following it
11745
+
if (this.future().text !== "[") {
11746
+
return null;
11747
+
}
11748
+
start = this.popToken(); // don't include [ in tokens
11749
+
({ tokens, end } = this.consumeArg(["]"]));
11750
+
} else {
11751
+
({ tokens, start, end } = this.consumeArg());
11752
+
}
11753
+
11754
+
// indicate the end of an argument
11755
+
this.pushToken(new Token("EOF", end.loc));
11756
+
11757
+
this.pushTokens(tokens);
11758
+
return start.range(end, "");
11759
+
}
11760
+
11761
+
/**
11762
+
* Consume all following space tokens, without expansion.
11763
+
*/
11764
+
consumeSpaces() {
11765
+
for (;;) {
11766
+
const token = this.future();
11767
+
if (token.text === " ") {
11768
+
this.stack.pop();
11769
+
} else {
11770
+
break;
11771
+
}
11772
+
}
11773
+
}
11774
+
11775
+
/**
11776
+
* Consume an argument from the token stream, and return the resulting array
11777
+
* of tokens and start/end token.
11778
+
*/
11779
+
consumeArg(delims) {
11780
+
// The argument for a delimited parameter is the shortest (possibly
11781
+
// empty) sequence of tokens with properly nested {...} groups that is
11782
+
// followed ... by this particular list of non-parameter tokens.
11783
+
// The argument for an undelimited parameter is the next nonblank
11784
+
// token, unless that token is ‘{’, when the argument will be the
11785
+
// entire {...} group that follows.
11786
+
const tokens = [];
11787
+
const isDelimited = delims && delims.length > 0;
11788
+
if (!isDelimited) {
11789
+
// Ignore spaces between arguments. As the TeXbook says:
11790
+
// "After you have said ‘\def\row#1#2{...}’, you are allowed to
11791
+
// put spaces between the arguments (e.g., ‘\row x n’), because
11792
+
// TeX doesn’t use single spaces as undelimited arguments."
11793
+
this.consumeSpaces();
11794
+
}
11795
+
const start = this.future();
11796
+
let tok;
11797
+
let depth = 0;
11798
+
let match = 0;
11799
+
do {
11800
+
tok = this.popToken();
11801
+
tokens.push(tok);
11802
+
if (tok.text === "{") {
11803
+
++depth;
11804
+
} else if (tok.text === "}") {
11805
+
--depth;
11806
+
if (depth === -1) {
11807
+
throw new ParseError("Extra }", tok);
11808
+
}
11809
+
} else if (tok.text === "EOF") {
11810
+
throw new ParseError(
11811
+
"Unexpected end of input in a macro argument" +
11812
+
", expected '" +
11813
+
(delims && isDelimited ? delims[match] : "}") +
11814
+
"'",
11815
+
tok
11816
+
);
11817
+
}
11818
+
if (delims && isDelimited) {
11819
+
if ((depth === 0 || (depth === 1 && delims[match] === "{")) && tok.text === delims[match]) {
11820
+
++match;
11821
+
if (match === delims.length) {
11822
+
// don't include delims in tokens
11823
+
tokens.splice(-match, match);
11824
+
break;
11825
+
}
11826
+
} else {
11827
+
match = 0;
11828
+
}
11829
+
}
11830
+
} while (depth !== 0 || isDelimited);
11831
+
// If the argument found ... has the form ‘{<nested tokens>}’,
11832
+
// ... the outermost braces enclosing the argument are removed
11833
+
if (start.text === "{" && tokens[tokens.length - 1].text === "}") {
11834
+
tokens.pop();
11835
+
tokens.shift();
11836
+
}
11837
+
tokens.reverse(); // to fit in with stack order
11838
+
return { tokens, start, end: tok };
11839
+
}
11840
+
11841
+
/**
11842
+
* Consume the specified number of (delimited) arguments from the token
11843
+
* stream and return the resulting array of arguments.
11844
+
*/
11845
+
consumeArgs(numArgs, delimiters) {
11846
+
if (delimiters) {
11847
+
if (delimiters.length !== numArgs + 1) {
11848
+
throw new ParseError("The length of delimiters doesn't match the number of args!");
11849
+
}
11850
+
const delims = delimiters[0];
11851
+
for (let i = 0; i < delims.length; i++) {
11852
+
const tok = this.popToken();
11853
+
if (delims[i] !== tok.text) {
11854
+
throw new ParseError("Use of the macro doesn't match its definition", tok);
11855
+
}
11856
+
}
11857
+
}
11858
+
11859
+
const args = [];
11860
+
for (let i = 0; i < numArgs; i++) {
11861
+
args.push(this.consumeArg(delimiters && delimiters[i + 1]).tokens);
11862
+
}
11863
+
return args;
11864
+
}
11865
+
11866
+
/**
11867
+
* Expand the next token only once if possible.
11868
+
*
11869
+
* If the token is expanded, the resulting tokens will be pushed onto
11870
+
* the stack in reverse order, and the number of such tokens will be
11871
+
* returned. This number might be zero or positive.
11872
+
*
11873
+
* If not, the return value is `false`, and the next token remains at the
11874
+
* top of the stack.
11875
+
*
11876
+
* In either case, the next token will be on the top of the stack,
11877
+
* or the stack will be empty (in case of empty expansion
11878
+
* and no other tokens).
11879
+
*
11880
+
* Used to implement `expandAfterFuture` and `expandNextToken`.
11881
+
*
11882
+
* If expandableOnly, only expandable tokens are expanded and
11883
+
* an undefined control sequence results in an error.
11884
+
*/
11885
+
expandOnce(expandableOnly) {
11886
+
const topToken = this.popToken();
11887
+
const name = topToken.text;
11888
+
const expansion = !topToken.noexpand ? this._getExpansion(name) : null;
11889
+
if (expansion == null || (expandableOnly && expansion.unexpandable)) {
11890
+
if (expandableOnly && expansion == null && name[0] === "\\" && !this.isDefined(name)) {
11891
+
throw new ParseError("Undefined control sequence: " + name);
11892
+
}
11893
+
this.pushToken(topToken);
11894
+
return false;
11895
+
}
11896
+
this.expansionCount++;
11897
+
if (this.expansionCount > this.settings.maxExpand) {
11898
+
throw new ParseError(
11899
+
"Too many expansions: infinite loop or " + "need to increase maxExpand setting"
11900
+
);
11901
+
}
11902
+
let tokens = expansion.tokens;
11903
+
const args = this.consumeArgs(expansion.numArgs, expansion.delimiters);
11904
+
if (expansion.numArgs) {
11905
+
// paste arguments in place of the placeholders
11906
+
tokens = tokens.slice(); // make a shallow copy
11907
+
for (let i = tokens.length - 1; i >= 0; --i) {
11908
+
let tok = tokens[i];
11909
+
if (tok.text === "#") {
11910
+
if (i === 0) {
11911
+
throw new ParseError("Incomplete placeholder at end of macro body", tok);
11912
+
}
11913
+
tok = tokens[--i]; // next token on stack
11914
+
if (tok.text === "#") {
11915
+
// ## → #
11916
+
tokens.splice(i + 1, 1); // drop first #
11917
+
} else if (/^[1-9]$/.test(tok.text)) {
11918
+
// replace the placeholder with the indicated argument
11919
+
tokens.splice(i, 2, ...args[+tok.text - 1]);
11920
+
} else {
11921
+
throw new ParseError("Not a valid argument number", tok);
11922
+
}
11923
+
}
11924
+
}
11925
+
}
11926
+
// Concatenate expansion onto top of stack.
11927
+
this.pushTokens(tokens);
11928
+
return tokens.length;
11929
+
}
11930
+
11931
+
/**
11932
+
* Expand the next token only once (if possible), and return the resulting
11933
+
* top token on the stack (without removing anything from the stack).
11934
+
* Similar in behavior to TeX's `\expandafter\futurelet`.
11935
+
* Equivalent to expandOnce() followed by future().
11936
+
*/
11937
+
expandAfterFuture() {
11938
+
this.expandOnce();
11939
+
return this.future();
11940
+
}
11941
+
11942
+
/**
11943
+
* Recursively expand first token, then return first non-expandable token.
11944
+
*/
11945
+
expandNextToken() {
11946
+
for (;;) {
11947
+
if (this.expandOnce() === false) { // fully expanded
11948
+
const token = this.stack.pop();
11949
+
// The token after \noexpand is interpreted as if its meaning were ‘\relax’
11950
+
if (token.treatAsRelax) {
11951
+
token.text = "\\relax";
11952
+
}
11953
+
return token
11954
+
}
11955
+
}
11956
+
11957
+
// This pathway is impossible.
11958
+
throw new Error(); // eslint-disable-line no-unreachable
11959
+
}
11960
+
11961
+
/**
11962
+
* Fully expand the given macro name and return the resulting list of
11963
+
* tokens, or return `undefined` if no such macro is defined.
11964
+
*/
11965
+
expandMacro(name) {
11966
+
return this.macros.has(name) ? this.expandTokens([new Token(name)]) : undefined;
11967
+
}
11968
+
11969
+
/**
11970
+
* Fully expand the given token stream and return the resulting list of
11971
+
* tokens. Note that the input tokens are in reverse order, but the
11972
+
* output tokens are in forward order.
11973
+
*/
11974
+
expandTokens(tokens) {
11975
+
const output = [];
11976
+
const oldStackLength = this.stack.length;
11977
+
this.pushTokens(tokens);
11978
+
while (this.stack.length > oldStackLength) {
11979
+
// Expand only expandable tokens
11980
+
if (this.expandOnce(true) === false) { // fully expanded
11981
+
const token = this.stack.pop();
11982
+
if (token.treatAsRelax) {
11983
+
// the expansion of \noexpand is the token itself
11984
+
token.noexpand = false;
11985
+
token.treatAsRelax = false;
11986
+
}
11987
+
output.push(token);
11988
+
}
11989
+
}
11990
+
return output;
11991
+
}
11992
+
11993
+
/**
11994
+
* Fully expand the given macro name and return the result as a string,
11995
+
* or return `undefined` if no such macro is defined.
11996
+
*/
11997
+
expandMacroAsText(name) {
11998
+
const tokens = this.expandMacro(name);
11999
+
if (tokens) {
12000
+
return tokens.map((token) => token.text).join("");
12001
+
} else {
12002
+
return tokens;
12003
+
}
12004
+
}
12005
+
12006
+
/**
12007
+
* Returns the expanded macro as a reversed array of tokens and a macro
12008
+
* argument count. Or returns `null` if no such macro.
12009
+
*/
12010
+
_getExpansion(name) {
12011
+
const definition = this.macros.get(name);
12012
+
if (definition == null) {
12013
+
// mainly checking for undefined here
12014
+
return definition;
12015
+
}
12016
+
// If a single character has an associated catcode other than 13
12017
+
// (active character), then don't expand it.
12018
+
if (name.length === 1) {
12019
+
const catcode = this.lexer.catcodes[name];
12020
+
if (catcode != null && catcode !== 13) {
12021
+
return
12022
+
}
12023
+
}
12024
+
const expansion = typeof definition === "function" ? definition(this) : definition;
12025
+
if (typeof expansion === "string") {
12026
+
let numArgs = 0;
12027
+
if (expansion.indexOf("#") !== -1) {
12028
+
const stripped = expansion.replace(/##/g, "");
12029
+
while (stripped.indexOf("#" + (numArgs + 1)) !== -1) {
12030
+
++numArgs;
12031
+
}
12032
+
}
12033
+
const bodyLexer = new Lexer(expansion, this.settings);
12034
+
const tokens = [];
12035
+
let tok = bodyLexer.lex();
12036
+
while (tok.text !== "EOF") {
12037
+
tokens.push(tok);
12038
+
tok = bodyLexer.lex();
12039
+
}
12040
+
tokens.reverse(); // to fit in with stack using push and pop
12041
+
const expanded = { tokens, numArgs };
12042
+
return expanded;
12043
+
}
12044
+
12045
+
return expansion;
12046
+
}
12047
+
12048
+
/**
12049
+
* Determine whether a command is currently "defined" (has some
12050
+
* functionality), meaning that it's a macro (in the current group),
12051
+
* a function, a symbol, or one of the special commands listed in
12052
+
* `implicitCommands`.
12053
+
*/
12054
+
isDefined(name) {
12055
+
return (
12056
+
this.macros.has(name) ||
12057
+
Object.prototype.hasOwnProperty.call(functions, name ) ||
12058
+
Object.prototype.hasOwnProperty.call(symbols.math, name ) ||
12059
+
Object.prototype.hasOwnProperty.call(symbols.text, name ) ||
12060
+
Object.prototype.hasOwnProperty.call(implicitCommands, name )
12061
+
);
12062
+
}
12063
+
12064
+
/**
12065
+
* Determine whether a command is expandable.
12066
+
*/
12067
+
isExpandable(name) {
12068
+
const macro = this.macros.get(name);
12069
+
return macro != null
12070
+
? typeof macro === "string" || typeof macro === "function" || !macro.unexpandable
12071
+
: Object.prototype.hasOwnProperty.call(functions, name ) && !functions[name].primitive;
12072
+
}
12073
+
}
12074
+
12075
+
// Helpers for Parser.js handling of Unicode (sub|super)script characters.
12076
+
12077
+
const unicodeSubRegEx = /^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/;
12078
+
12079
+
const uSubsAndSups = Object.freeze({
12080
+
'₊': '+',
12081
+
'₋': '-',
12082
+
'₌': '=',
12083
+
'₍': '(',
12084
+
'₎': ')',
12085
+
'₀': '0',
12086
+
'₁': '1',
12087
+
'₂': '2',
12088
+
'₃': '3',
12089
+
'₄': '4',
12090
+
'₅': '5',
12091
+
'₆': '6',
12092
+
'₇': '7',
12093
+
'₈': '8',
12094
+
'₉': '9',
12095
+
'\u2090': 'a',
12096
+
'\u2091': 'e',
12097
+
'\u2095': 'h',
12098
+
'\u1D62': 'i',
12099
+
'\u2C7C': 'j',
12100
+
'\u2096': 'k',
12101
+
'\u2097': 'l',
12102
+
'\u2098': 'm',
12103
+
'\u2099': 'n',
12104
+
'\u2092': 'o',
12105
+
'\u209A': 'p',
12106
+
'\u1D63': 'r',
12107
+
'\u209B': 's',
12108
+
'\u209C': 't',
12109
+
'\u1D64': 'u',
12110
+
'\u1D65': 'v',
12111
+
'\u2093': 'x',
12112
+
'\u1D66': 'β',
12113
+
'\u1D67': 'γ',
12114
+
'\u1D68': 'ρ',
12115
+
'\u1D69': '\u03d5',
12116
+
'\u1D6A': 'χ',
12117
+
'⁺': '+',
12118
+
'⁻': '-',
12119
+
'⁼': '=',
12120
+
'⁽': '(',
12121
+
'⁾': ')',
12122
+
'⁰': '0',
12123
+
'¹': '1',
12124
+
'²': '2',
12125
+
'³': '3',
12126
+
'⁴': '4',
12127
+
'⁵': '5',
12128
+
'⁶': '6',
12129
+
'⁷': '7',
12130
+
'⁸': '8',
12131
+
'⁹': '9',
12132
+
'\u1D2C': 'A',
12133
+
'\u1D2E': 'B',
12134
+
'\u1D30': 'D',
12135
+
'\u1D31': 'E',
12136
+
'\u1D33': 'G',
12137
+
'\u1D34': 'H',
12138
+
'\u1D35': 'I',
12139
+
'\u1D36': 'J',
12140
+
'\u1D37': 'K',
12141
+
'\u1D38': 'L',
12142
+
'\u1D39': 'M',
12143
+
'\u1D3A': 'N',
12144
+
'\u1D3C': 'O',
12145
+
'\u1D3E': 'P',
12146
+
'\u1D3F': 'R',
12147
+
'\u1D40': 'T',
12148
+
'\u1D41': 'U',
12149
+
'\u2C7D': 'V',
12150
+
'\u1D42': 'W',
12151
+
'\u1D43': 'a',
12152
+
'\u1D47': 'b',
12153
+
'\u1D9C': 'c',
12154
+
'\u1D48': 'd',
12155
+
'\u1D49': 'e',
12156
+
'\u1DA0': 'f',
12157
+
'\u1D4D': 'g',
12158
+
'\u02B0': 'h',
12159
+
'\u2071': 'i',
12160
+
'\u02B2': 'j',
12161
+
'\u1D4F': 'k',
12162
+
'\u02E1': 'l',
12163
+
'\u1D50': 'm',
12164
+
'\u207F': 'n',
12165
+
'\u1D52': 'o',
12166
+
'\u1D56': 'p',
12167
+
'\u02B3': 'r',
12168
+
'\u02E2': 's',
12169
+
'\u1D57': 't',
12170
+
'\u1D58': 'u',
12171
+
'\u1D5B': 'v',
12172
+
'\u02B7': 'w',
12173
+
'\u02E3': 'x',
12174
+
'\u02B8': 'y',
12175
+
'\u1DBB': 'z',
12176
+
'\u1D5D': 'β',
12177
+
'\u1D5E': 'γ',
12178
+
'\u1D5F': 'δ',
12179
+
'\u1D60': '\u03d5',
12180
+
'\u1D61': 'χ',
12181
+
'\u1DBF': 'θ'
12182
+
});
12183
+
12184
+
// Used for Unicode input of calligraphic and script letters
12185
+
const asciiFromScript = Object.freeze({
12186
+
"\ud835\udc9c": "A",
12187
+
"\u212c": "B",
12188
+
"\ud835\udc9e": "C",
12189
+
"\ud835\udc9f": "D",
12190
+
"\u2130": "E",
12191
+
"\u2131": "F",
12192
+
"\ud835\udca2": "G",
12193
+
"\u210B": "H",
12194
+
"\u2110": "I",
12195
+
"\ud835\udca5": "J",
12196
+
"\ud835\udca6": "K",
12197
+
"\u2112": "L",
12198
+
"\u2133": "M",
12199
+
"\ud835\udca9": "N",
12200
+
"\ud835\udcaa": "O",
12201
+
"\ud835\udcab": "P",
12202
+
"\ud835\udcac": "Q",
12203
+
"\u211B": "R",
12204
+
"\ud835\udcae": "S",
12205
+
"\ud835\udcaf": "T",
12206
+
"\ud835\udcb0": "U",
12207
+
"\ud835\udcb1": "V",
12208
+
"\ud835\udcb2": "W",
12209
+
"\ud835\udcb3": "X",
12210
+
"\ud835\udcb4": "Y",
12211
+
"\ud835\udcb5": "Z"
12212
+
});
12213
+
12214
+
// Mapping of Unicode accent characters to their LaTeX equivalent in text and
12215
+
// math mode (when they exist).
12216
+
var unicodeAccents = {
12217
+
"\u0301": { text: "\\'", math: "\\acute" },
12218
+
"\u0300": { text: "\\`", math: "\\grave" },
12219
+
"\u0308": { text: '\\"', math: "\\ddot" },
12220
+
"\u0303": { text: "\\~", math: "\\tilde" },
12221
+
"\u0304": { text: "\\=", math: "\\bar" },
12222
+
"\u0306": { text: "\\u", math: "\\breve" },
12223
+
"\u030c": { text: "\\v", math: "\\check" },
12224
+
"\u0302": { text: "\\^", math: "\\hat" },
12225
+
"\u0307": { text: "\\.", math: "\\dot" },
12226
+
"\u030a": { text: "\\r", math: "\\mathring" },
12227
+
"\u030b": { text: "\\H" },
12228
+
'\u0327': { text: '\\c' }
12229
+
};
12230
+
12231
+
var unicodeSymbols = {
12232
+
"á": "á",
12233
+
"à": "à",
12234
+
"ä": "ä",
12235
+
"ǟ": "ǟ",
12236
+
"ã": "ã",
12237
+
"ā": "ā",
12238
+
"ă": "ă",
12239
+
"ắ": "ắ",
12240
+
"ằ": "ằ",
12241
+
"ẵ": "ẵ",
12242
+
"ǎ": "ǎ",
12243
+
"â": "â",
12244
+
"ấ": "ấ",
12245
+
"ầ": "ầ",
12246
+
"ẫ": "ẫ",
12247
+
"ȧ": "ȧ",
12248
+
"ǡ": "ǡ",
12249
+
"å": "å",
12250
+
"ǻ": "ǻ",
12251
+
"ḃ": "ḃ",
12252
+
"ć": "ć",
12253
+
"č": "č",
12254
+
"ĉ": "ĉ",
12255
+
"ċ": "ċ",
12256
+
"ď": "ď",
12257
+
"ḋ": "ḋ",
12258
+
"é": "é",
12259
+
"è": "è",
12260
+
"ë": "ë",
12261
+
"ẽ": "ẽ",
12262
+
"ē": "ē",
12263
+
"ḗ": "ḗ",
12264
+
"ḕ": "ḕ",
12265
+
"ĕ": "ĕ",
12266
+
"ě": "ě",
12267
+
"ê": "ê",
12268
+
"ế": "ế",
12269
+
"ề": "ề",
12270
+
"ễ": "ễ",
12271
+
"ė": "ė",
12272
+
"ḟ": "ḟ",
12273
+
"ǵ": "ǵ",
12274
+
"ḡ": "ḡ",
12275
+
"ğ": "ğ",
12276
+
"ǧ": "ǧ",
12277
+
"ĝ": "ĝ",
12278
+
"ġ": "ġ",
12279
+
"ḧ": "ḧ",
12280
+
"ȟ": "ȟ",
12281
+
"ĥ": "ĥ",
12282
+
"ḣ": "ḣ",
12283
+
"í": "í",
12284
+
"ì": "ì",
12285
+
"ï": "ï",
12286
+
"ḯ": "ḯ",
12287
+
"ĩ": "ĩ",
12288
+
"ī": "ī",
12289
+
"ĭ": "ĭ",
12290
+
"ǐ": "ǐ",
12291
+
"î": "î",
12292
+
"ǰ": "ǰ",
12293
+
"ĵ": "ĵ",
12294
+
"ḱ": "ḱ",
12295
+
"ǩ": "ǩ",
12296
+
"ĺ": "ĺ",
12297
+
"ľ": "ľ",
12298
+
"ḿ": "ḿ",
12299
+
"ṁ": "ṁ",
12300
+
"ń": "ń",
12301
+
"ǹ": "ǹ",
12302
+
"ñ": "ñ",
12303
+
"ň": "ň",
12304
+
"ṅ": "ṅ",
12305
+
"ó": "ó",
12306
+
"ò": "ò",
12307
+
"ö": "ö",
12308
+
"ȫ": "ȫ",
12309
+
"õ": "õ",
12310
+
"ṍ": "ṍ",
12311
+
"ṏ": "ṏ",
12312
+
"ȭ": "ȭ",
12313
+
"ō": "ō",
12314
+
"ṓ": "ṓ",
12315
+
"ṑ": "ṑ",
12316
+
"ŏ": "ŏ",
12317
+
"ǒ": "ǒ",
12318
+
"ô": "ô",
12319
+
"ố": "ố",
12320
+
"ồ": "ồ",
12321
+
"ỗ": "ỗ",
12322
+
"ȯ": "ȯ",
12323
+
"ȱ": "ȱ",
12324
+
"ő": "ő",
12325
+
"ṕ": "ṕ",
12326
+
"ṗ": "ṗ",
12327
+
"ŕ": "ŕ",
12328
+
"ř": "ř",
12329
+
"ṙ": "ṙ",
12330
+
"ś": "ś",
12331
+
"ṥ": "ṥ",
12332
+
"š": "š",
12333
+
"ṧ": "ṧ",
12334
+
"ŝ": "ŝ",
12335
+
"ṡ": "ṡ",
12336
+
"ẗ": "ẗ",
12337
+
"ť": "ť",
12338
+
"ṫ": "ṫ",
12339
+
"ú": "ú",
12340
+
"ù": "ù",
12341
+
"ü": "ü",
12342
+
"ǘ": "ǘ",
12343
+
"ǜ": "ǜ",
12344
+
"ǖ": "ǖ",
12345
+
"ǚ": "ǚ",
12346
+
"ũ": "ũ",
12347
+
"ṹ": "ṹ",
12348
+
"ū": "ū",
12349
+
"ṻ": "ṻ",
12350
+
"ŭ": "ŭ",
12351
+
"ǔ": "ǔ",
12352
+
"û": "û",
12353
+
"ů": "ů",
12354
+
"ű": "ű",
12355
+
"ṽ": "ṽ",
12356
+
"ẃ": "ẃ",
12357
+
"ẁ": "ẁ",
12358
+
"ẅ": "ẅ",
12359
+
"ŵ": "ŵ",
12360
+
"ẇ": "ẇ",
12361
+
"ẘ": "ẘ",
12362
+
"ẍ": "ẍ",
12363
+
"ẋ": "ẋ",
12364
+
"ý": "ý",
12365
+
"ỳ": "ỳ",
12366
+
"ÿ": "ÿ",
12367
+
"ỹ": "ỹ",
12368
+
"ȳ": "ȳ",
12369
+
"ŷ": "ŷ",
12370
+
"ẏ": "ẏ",
12371
+
"ẙ": "ẙ",
12372
+
"ź": "ź",
12373
+
"ž": "ž",
12374
+
"ẑ": "ẑ",
12375
+
"ż": "ż",
12376
+
"Á": "Á",
12377
+
"À": "À",
12378
+
"Ä": "Ä",
12379
+
"Ǟ": "Ǟ",
12380
+
"Ã": "Ã",
12381
+
"Ā": "Ā",
12382
+
"Ă": "Ă",
12383
+
"Ắ": "Ắ",
12384
+
"Ằ": "Ằ",
12385
+
"Ẵ": "Ẵ",
12386
+
"Ǎ": "Ǎ",
12387
+
"Â": "Â",
12388
+
"Ấ": "Ấ",
12389
+
"Ầ": "Ầ",
12390
+
"Ẫ": "Ẫ",
12391
+
"Ȧ": "Ȧ",
12392
+
"Ǡ": "Ǡ",
12393
+
"Å": "Å",
12394
+
"Ǻ": "Ǻ",
12395
+
"Ḃ": "Ḃ",
12396
+
"Ć": "Ć",
12397
+
"Č": "Č",
12398
+
"Ĉ": "Ĉ",
12399
+
"Ċ": "Ċ",
12400
+
"Ď": "Ď",
12401
+
"Ḋ": "Ḋ",
12402
+
"É": "É",
12403
+
"È": "È",
12404
+
"Ë": "Ë",
12405
+
"Ẽ": "Ẽ",
12406
+
"Ē": "Ē",
12407
+
"Ḗ": "Ḗ",
12408
+
"Ḕ": "Ḕ",
12409
+
"Ĕ": "Ĕ",
12410
+
"Ě": "Ě",
12411
+
"Ê": "Ê",
12412
+
"Ế": "Ế",
12413
+
"Ề": "Ề",
12414
+
"Ễ": "Ễ",
12415
+
"Ė": "Ė",
12416
+
"Ḟ": "Ḟ",
12417
+
"Ǵ": "Ǵ",
12418
+
"Ḡ": "Ḡ",
12419
+
"Ğ": "Ğ",
12420
+
"Ǧ": "Ǧ",
12421
+
"Ĝ": "Ĝ",
12422
+
"Ġ": "Ġ",
12423
+
"Ḧ": "Ḧ",
12424
+
"Ȟ": "Ȟ",
12425
+
"Ĥ": "Ĥ",
12426
+
"Ḣ": "Ḣ",
12427
+
"Í": "Í",
12428
+
"Ì": "Ì",
12429
+
"Ï": "Ï",
12430
+
"Ḯ": "Ḯ",
12431
+
"Ĩ": "Ĩ",
12432
+
"Ī": "Ī",
12433
+
"Ĭ": "Ĭ",
12434
+
"Ǐ": "Ǐ",
12435
+
"Î": "Î",
12436
+
"İ": "İ",
12437
+
"Ĵ": "Ĵ",
12438
+
"Ḱ": "Ḱ",
12439
+
"Ǩ": "Ǩ",
12440
+
"Ĺ": "Ĺ",
12441
+
"Ľ": "Ľ",
12442
+
"Ḿ": "Ḿ",
12443
+
"Ṁ": "Ṁ",
12444
+
"Ń": "Ń",
12445
+
"Ǹ": "Ǹ",
12446
+
"Ñ": "Ñ",
12447
+
"Ň": "Ň",
12448
+
"Ṅ": "Ṅ",
12449
+
"Ó": "Ó",
12450
+
"Ò": "Ò",
12451
+
"Ö": "Ö",
12452
+
"Ȫ": "Ȫ",
12453
+
"Õ": "Õ",
12454
+
"Ṍ": "Ṍ",
12455
+
"Ṏ": "Ṏ",
12456
+
"Ȭ": "Ȭ",
12457
+
"Ō": "Ō",
12458
+
"Ṓ": "Ṓ",
12459
+
"Ṑ": "Ṑ",
12460
+
"Ŏ": "Ŏ",
12461
+
"Ǒ": "Ǒ",
12462
+
"Ô": "Ô",
12463
+
"Ố": "Ố",
12464
+
"Ồ": "Ồ",
12465
+
"Ỗ": "Ỗ",
12466
+
"Ȯ": "Ȯ",
12467
+
"Ȱ": "Ȱ",
12468
+
"Ő": "Ő",
12469
+
"Ṕ": "Ṕ",
12470
+
"Ṗ": "Ṗ",
12471
+
"Ŕ": "Ŕ",
12472
+
"Ř": "Ř",
12473
+
"Ṙ": "Ṙ",
12474
+
"Ś": "Ś",
12475
+
"Ṥ": "Ṥ",
12476
+
"Š": "Š",
12477
+
"Ṧ": "Ṧ",
12478
+
"Ŝ": "Ŝ",
12479
+
"Ṡ": "Ṡ",
12480
+
"Ť": "Ť",
12481
+
"Ṫ": "Ṫ",
12482
+
"Ú": "Ú",
12483
+
"Ù": "Ù",
12484
+
"Ü": "Ü",
12485
+
"Ǘ": "Ǘ",
12486
+
"Ǜ": "Ǜ",
12487
+
"Ǖ": "Ǖ",
12488
+
"Ǚ": "Ǚ",
12489
+
"Ũ": "Ũ",
12490
+
"Ṹ": "Ṹ",
12491
+
"Ū": "Ū",
12492
+
"Ṻ": "Ṻ",
12493
+
"Ŭ": "Ŭ",
12494
+
"Ǔ": "Ǔ",
12495
+
"Û": "Û",
12496
+
"Ů": "Ů",
12497
+
"Ű": "Ű",
12498
+
"Ṽ": "Ṽ",
12499
+
"Ẃ": "Ẃ",
12500
+
"Ẁ": "Ẁ",
12501
+
"Ẅ": "Ẅ",
12502
+
"Ŵ": "Ŵ",
12503
+
"Ẇ": "Ẇ",
12504
+
"Ẍ": "Ẍ",
12505
+
"Ẋ": "Ẋ",
12506
+
"Ý": "Ý",
12507
+
"Ỳ": "Ỳ",
12508
+
"Ÿ": "Ÿ",
12509
+
"Ỹ": "Ỹ",
12510
+
"Ȳ": "Ȳ",
12511
+
"Ŷ": "Ŷ",
12512
+
"Ẏ": "Ẏ",
12513
+
"Ź": "Ź",
12514
+
"Ž": "Ž",
12515
+
"Ẑ": "Ẑ",
12516
+
"Ż": "Ż",
12517
+
"ά": "ά",
12518
+
"ὰ": "ὰ",
12519
+
"ᾱ": "ᾱ",
12520
+
"ᾰ": "ᾰ",
12521
+
"έ": "έ",
12522
+
"ὲ": "ὲ",
12523
+
"ή": "ή",
12524
+
"ὴ": "ὴ",
12525
+
"ί": "ί",
12526
+
"ὶ": "ὶ",
12527
+
"ϊ": "ϊ",
12528
+
"ΐ": "ΐ",
12529
+
"ῒ": "ῒ",
12530
+
"ῑ": "ῑ",
12531
+
"ῐ": "ῐ",
12532
+
"ό": "ό",
12533
+
"ὸ": "ὸ",
12534
+
"ύ": "ύ",
12535
+
"ὺ": "ὺ",
12536
+
"ϋ": "ϋ",
12537
+
"ΰ": "ΰ",
12538
+
"ῢ": "ῢ",
12539
+
"ῡ": "ῡ",
12540
+
"ῠ": "ῠ",
12541
+
"ώ": "ώ",
12542
+
"ὼ": "ὼ",
12543
+
"Ύ": "Ύ",
12544
+
"Ὺ": "Ὺ",
12545
+
"Ϋ": "Ϋ",
12546
+
"Ῡ": "Ῡ",
12547
+
"Ῠ": "Ῠ",
12548
+
"Ώ": "Ώ",
12549
+
"Ὼ": "Ὼ"
12550
+
};
12551
+
12552
+
/* eslint no-constant-condition:0 */
12553
+
12554
+
const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
12555
+
const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/;
12556
+
12557
+
/**
12558
+
* This file contains the parser used to parse out a TeX expression from the
12559
+
* input. Since TeX isn't context-free, standard parsers don't work particularly
12560
+
* well.
12561
+
*
12562
+
* The strategy of this parser is as such:
12563
+
*
12564
+
* The main functions (the `.parse...` ones) take a position in the current
12565
+
* parse string to parse tokens from. The lexer (found in Lexer.js, stored at
12566
+
* this.gullet.lexer) also supports pulling out tokens at arbitrary places. When
12567
+
* individual tokens are needed at a position, the lexer is called to pull out a
12568
+
* token, which is then used.
12569
+
*
12570
+
* The parser has a property called "mode" indicating the mode that
12571
+
* the parser is currently in. Currently it has to be one of "math" or
12572
+
* "text", which denotes whether the current environment is a math-y
12573
+
* one or a text-y one (e.g. inside \text). Currently, this serves to
12574
+
* limit the functions which can be used in text mode.
12575
+
*
12576
+
* The main functions then return an object which contains the useful data that
12577
+
* was parsed at its given point, and a new position at the end of the parsed
12578
+
* data. The main functions can call each other and continue the parsing by
12579
+
* using the returned position as a new starting point.
12580
+
*
12581
+
* There are also extra `.handle...` functions, which pull out some reused
12582
+
* functionality into self-contained functions.
12583
+
*
12584
+
* The functions return ParseNodes.
12585
+
*/
12586
+
12587
+
class Parser {
12588
+
constructor(input, settings, isPreamble = false) {
12589
+
// Start in math mode
12590
+
this.mode = "math";
12591
+
// Create a new macro expander (gullet) and (indirectly via that) also a
12592
+
// new lexer (mouth) for this parser (stomach, in the language of TeX)
12593
+
this.gullet = new MacroExpander(input, settings, this.mode);
12594
+
// Store the settings for use in parsing
12595
+
this.settings = settings;
12596
+
// Are we defining a preamble?
12597
+
this.isPreamble = isPreamble;
12598
+
// Count leftright depth (for \middle errors)
12599
+
this.leftrightDepth = 0;
12600
+
this.prevAtomType = "";
12601
+
}
12602
+
12603
+
/**
12604
+
* Checks a result to make sure it has the right type, and throws an
12605
+
* appropriate error otherwise.
12606
+
*/
12607
+
expect(text, consume = true) {
12608
+
if (this.fetch().text !== text) {
12609
+
throw new ParseError(`Expected '${text}', got '${this.fetch().text}'`, this.fetch());
12610
+
}
12611
+
if (consume) {
12612
+
this.consume();
12613
+
}
12614
+
}
12615
+
12616
+
/**
12617
+
* Discards the current lookahead token, considering it consumed.
12618
+
*/
12619
+
consume() {
12620
+
this.nextToken = null;
12621
+
}
12622
+
12623
+
/**
12624
+
* Return the current lookahead token, or if there isn't one (at the
12625
+
* beginning, or if the previous lookahead token was consume()d),
12626
+
* fetch the next token as the new lookahead token and return it.
12627
+
*/
12628
+
fetch() {
12629
+
if (this.nextToken == null) {
12630
+
this.nextToken = this.gullet.expandNextToken();
12631
+
}
12632
+
return this.nextToken;
12633
+
}
12634
+
12635
+
/**
12636
+
* Switches between "text" and "math" modes.
12637
+
*/
12638
+
switchMode(newMode) {
12639
+
this.mode = newMode;
12640
+
this.gullet.switchMode(newMode);
12641
+
}
12642
+
12643
+
/**
12644
+
* Main parsing function, which parses an entire input.
12645
+
*/
12646
+
parse() {
12647
+
// Create a group namespace for every $...$, $$...$$, \[...\].)
12648
+
// A \def is then valid only within that pair of delimiters.
12649
+
this.gullet.beginGroup();
12650
+
12651
+
if (this.settings.colorIsTextColor) {
12652
+
// Use old \color behavior (same as LaTeX's \textcolor) if requested.
12653
+
// We do this within the group for the math expression, so it doesn't
12654
+
// pollute settings.macros.
12655
+
this.gullet.macros.set("\\color", "\\textcolor");
12656
+
}
12657
+
12658
+
// Try to parse the input
12659
+
const parse = this.parseExpression(false);
12660
+
12661
+
// If we succeeded, make sure there's an EOF at the end
12662
+
this.expect("EOF");
12663
+
12664
+
if (this.isPreamble) {
12665
+
const macros = Object.create(null);
12666
+
Object.entries(this.gullet.macros.current).forEach(([key, value]) => {
12667
+
macros[key] = value;
12668
+
});
12669
+
this.gullet.endGroup();
12670
+
return macros
12671
+
}
12672
+
12673
+
// The only local macro that we want to save is from \tag.
12674
+
const tag = this.gullet.macros.get("\\df@tag");
12675
+
12676
+
// End the group namespace for the expression
12677
+
this.gullet.endGroup();
12678
+
12679
+
if (tag) { this.gullet.macros.current["\\df@tag"] = tag; }
12680
+
12681
+
return parse;
12682
+
}
12683
+
12684
+
static get endOfExpression() {
12685
+
return ["}", "\\endgroup", "\\end", "\\right", "\\endtoggle", "&"];
12686
+
}
12687
+
12688
+
/**
12689
+
* Fully parse a separate sequence of tokens as a separate job.
12690
+
* Tokens should be specified in reverse order, as in a MacroDefinition.
12691
+
*/
12692
+
subparse(tokens) {
12693
+
// Save the next token from the current job.
12694
+
const oldToken = this.nextToken;
12695
+
this.consume();
12696
+
12697
+
// Run the new job, terminating it with an excess '}'
12698
+
this.gullet.pushToken(new Token("}"));
12699
+
this.gullet.pushTokens(tokens);
12700
+
const parse = this.parseExpression(false);
12701
+
this.expect("}");
12702
+
12703
+
// Restore the next token from the current job.
12704
+
this.nextToken = oldToken;
12705
+
12706
+
return parse;
12707
+
}
12708
+
12709
+
/**
12710
+
* Parses an "expression", which is a list of atoms.
12711
+
*
12712
+
* `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
12713
+
* happens when functions have higher precedence han infix
12714
+
* nodes in implicit parses.
12715
+
*
12716
+
* `breakOnTokenText`: The text of the token that the expression should end
12717
+
* with, or `null` if something else should end the
12718
+
* expression.
12719
+
*
12720
+
* `breakOnMiddle`: \color, \over, and old styling functions work on an implicit group.
12721
+
* These groups end just before the usual tokens, but they also
12722
+
* end just before `\middle`.
12723
+
*/
12724
+
parseExpression(breakOnInfix, breakOnTokenText, breakOnMiddle) {
12725
+
const body = [];
12726
+
this.prevAtomType = "";
12727
+
// Keep adding atoms to the body until we can't parse any more atoms (either
12728
+
// we reached the end, a }, or a \right)
12729
+
while (true) {
12730
+
// Ignore spaces in math mode
12731
+
if (this.mode === "math") {
12732
+
this.consumeSpaces();
12733
+
}
12734
+
const lex = this.fetch();
12735
+
if (Parser.endOfExpression.indexOf(lex.text) !== -1) {
12736
+
break;
12737
+
}
12738
+
if (breakOnTokenText && lex.text === breakOnTokenText) {
12739
+
break;
12740
+
}
12741
+
if (breakOnMiddle && lex.text === "\\middle") {
12742
+
break
12743
+
}
12744
+
if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) {
12745
+
break;
12746
+
}
12747
+
const atom = this.parseAtom(breakOnTokenText);
12748
+
if (!atom) {
12749
+
break;
12750
+
} else if (atom.type === "internal") {
12751
+
continue;
12752
+
}
12753
+
body.push(atom);
12754
+
// Keep a record of the atom type, so that op.js can set correct spacing.
12755
+
this.prevAtomType = atom.type === "atom" ? atom.family : atom.type;
12756
+
}
12757
+
if (this.mode === "text") {
12758
+
this.formLigatures(body);
12759
+
}
12760
+
return this.handleInfixNodes(body);
12761
+
}
12762
+
12763
+
/**
12764
+
* Rewrites infix operators such as \over with corresponding commands such
12765
+
* as \frac.
12766
+
*
12767
+
* There can only be one infix operator per group. If there's more than one
12768
+
* then the expression is ambiguous. This can be resolved by adding {}.
12769
+
*/
12770
+
handleInfixNodes(body) {
12771
+
let overIndex = -1;
12772
+
let funcName;
12773
+
12774
+
for (let i = 0; i < body.length; i++) {
12775
+
if (body[i].type === "infix") {
12776
+
if (overIndex !== -1) {
12777
+
throw new ParseError("only one infix operator per group", body[i].token);
12778
+
}
12779
+
overIndex = i;
12780
+
funcName = body[i].replaceWith;
12781
+
}
12782
+
}
12783
+
12784
+
if (overIndex !== -1 && funcName) {
12785
+
let numerNode;
12786
+
let denomNode;
12787
+
12788
+
const numerBody = body.slice(0, overIndex);
12789
+
const denomBody = body.slice(overIndex + 1);
12790
+
12791
+
if (numerBody.length === 1 && numerBody[0].type === "ordgroup") {
12792
+
numerNode = numerBody[0];
12793
+
} else {
12794
+
numerNode = { type: "ordgroup", mode: this.mode, body: numerBody };
12795
+
}
12796
+
12797
+
if (denomBody.length === 1 && denomBody[0].type === "ordgroup") {
12798
+
denomNode = denomBody[0];
12799
+
} else {
12800
+
denomNode = { type: "ordgroup", mode: this.mode, body: denomBody };
12801
+
}
12802
+
12803
+
let node;
12804
+
if (funcName === "\\\\abovefrac") {
12805
+
node = this.callFunction(funcName, [numerNode, body[overIndex], denomNode], []);
12806
+
} else {
12807
+
node = this.callFunction(funcName, [numerNode, denomNode], []);
12808
+
}
12809
+
return [node];
12810
+
} else {
12811
+
return body;
12812
+
}
12813
+
}
12814
+
12815
+
/**
12816
+
* Handle a subscript or superscript with nice errors.
12817
+
*/
12818
+
handleSupSubscript(
12819
+
name // For error reporting.
12820
+
) {
12821
+
const symbolToken = this.fetch();
12822
+
const symbol = symbolToken.text;
12823
+
this.consume();
12824
+
this.consumeSpaces(); // ignore spaces before sup/subscript argument
12825
+
const group = this.parseGroup(name);
12826
+
12827
+
if (!group) {
12828
+
throw new ParseError("Expected group after '" + symbol + "'", symbolToken);
12829
+
}
12830
+
12831
+
return group;
12832
+
}
12833
+
12834
+
/**
12835
+
* Converts the textual input of an unsupported command into a text node
12836
+
* contained within a color node whose color is determined by errorColor
12837
+
*/
12838
+
formatUnsupportedCmd(text) {
12839
+
const textordArray = [];
12840
+
12841
+
for (let i = 0; i < text.length; i++) {
12842
+
textordArray.push({ type: "textord", mode: "text", text: text[i] });
12843
+
}
12844
+
12845
+
const textNode = {
12846
+
type: "text",
12847
+
mode: this.mode,
12848
+
body: textordArray
12849
+
};
12850
+
12851
+
const colorNode = {
12852
+
type: "color",
12853
+
mode: this.mode,
12854
+
color: this.settings.errorColor,
12855
+
body: [textNode]
12856
+
};
12857
+
12858
+
return colorNode;
12859
+
}
12860
+
12861
+
/**
12862
+
* Parses a group with optional super/subscripts.
12863
+
*/
12864
+
parseAtom(breakOnTokenText) {
12865
+
// The body of an atom is an implicit group, so that things like
12866
+
// \left(x\right)^2 work correctly.
12867
+
const base = this.parseGroup("atom", breakOnTokenText);
12868
+
12869
+
// In text mode, we don't have superscripts or subscripts
12870
+
if (this.mode === "text") {
12871
+
return base;
12872
+
}
12873
+
12874
+
// Note that base may be empty (i.e. null) at this point.
12875
+
12876
+
let superscript;
12877
+
let subscript;
12878
+
while (true) {
12879
+
// Guaranteed in math mode, so eat any spaces first.
12880
+
this.consumeSpaces();
12881
+
12882
+
// Lex the first token
12883
+
const lex = this.fetch();
12884
+
12885
+
if (lex.text === "\\limits" || lex.text === "\\nolimits") {
12886
+
// We got a limit control
12887
+
if (base && base.type === "op") {
12888
+
const limits = lex.text === "\\limits";
12889
+
base.limits = limits;
12890
+
base.alwaysHandleSupSub = true;
12891
+
} else if (base && base.type === "operatorname") {
12892
+
if (base.alwaysHandleSupSub) {
12893
+
base.limits = lex.text === "\\limits";
12894
+
}
12895
+
} else {
12896
+
throw new ParseError("Limit controls must follow a math operator", lex);
12897
+
}
12898
+
this.consume();
12899
+
} else if (lex.text === "^") {
12900
+
// We got a superscript start
12901
+
if (superscript) {
12902
+
throw new ParseError("Double superscript", lex);
12903
+
}
12904
+
superscript = this.handleSupSubscript("superscript");
12905
+
} else if (lex.text === "_") {
12906
+
// We got a subscript start
12907
+
if (subscript) {
12908
+
throw new ParseError("Double subscript", lex);
12909
+
}
12910
+
subscript = this.handleSupSubscript("subscript");
12911
+
} else if (lex.text === "'") {
12912
+
// We got a prime
12913
+
if (superscript) {
12914
+
throw new ParseError("Double superscript", lex);
12915
+
}
12916
+
const prime = { type: "textord", mode: this.mode, text: "\\prime" };
12917
+
12918
+
// Many primes can be grouped together, so we handle this here
12919
+
const primes = [prime];
12920
+
this.consume();
12921
+
// Keep lexing tokens until we get something that's not a prime
12922
+
while (this.fetch().text === "'") {
12923
+
// For each one, add another prime to the list
12924
+
primes.push(prime);
12925
+
this.consume();
12926
+
}
12927
+
// If there's a superscript following the primes, combine that
12928
+
// superscript in with the primes.
12929
+
if (this.fetch().text === "^") {
12930
+
primes.push(this.handleSupSubscript("superscript"));
12931
+
}
12932
+
// Put everything into an ordgroup as the superscript
12933
+
superscript = { type: "ordgroup", mode: this.mode, body: primes };
12934
+
} else if (uSubsAndSups[lex.text]) {
12935
+
// A Unicode subscript or superscript character.
12936
+
// We treat these similarly to the unicode-math package.
12937
+
// So we render a string of Unicode (sub|super)scripts the
12938
+
// same as a (sub|super)script of regular characters.
12939
+
const isSub = unicodeSubRegEx.test(lex.text);
12940
+
const subsupTokens = [];
12941
+
subsupTokens.push(new Token(uSubsAndSups[lex.text]));
12942
+
this.consume();
12943
+
// Continue fetching tokens to fill out the group.
12944
+
while (true) {
12945
+
const token = this.fetch().text;
12946
+
if (!(uSubsAndSups[token])) { break }
12947
+
if (unicodeSubRegEx.test(token) !== isSub) { break }
12948
+
subsupTokens.unshift(new Token(uSubsAndSups[token]));
12949
+
this.consume();
12950
+
}
12951
+
// Now create a (sub|super)script.
12952
+
const body = this.subparse(subsupTokens);
12953
+
if (isSub) {
12954
+
subscript = { type: "ordgroup", mode: "math", body };
12955
+
} else {
12956
+
superscript = { type: "ordgroup", mode: "math", body };
12957
+
}
12958
+
} else {
12959
+
// If it wasn't ^, _, a Unicode (sub|super)script, or ', stop parsing super/subscripts
12960
+
break;
12961
+
}
12962
+
}
12963
+
12964
+
if (superscript || subscript) {
12965
+
if (base && base.type === "multiscript" && !base.postscripts) {
12966
+
// base is the result of a \prescript function.
12967
+
// Write the sub- & superscripts into the multiscript element.
12968
+
base.postscripts = { sup: superscript, sub: subscript };
12969
+
return base
12970
+
} else {
12971
+
// We got either a superscript or subscript, create a supsub
12972
+
const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
12973
+
? undefined
12974
+
: isDelimiter(this.nextToken.text);
12975
+
return {
12976
+
type: "supsub",
12977
+
mode: this.mode,
12978
+
base: base,
12979
+
sup: superscript,
12980
+
sub: subscript,
12981
+
isFollowedByDelimiter
12982
+
}
12983
+
}
12984
+
} else {
12985
+
// Otherwise return the original body
12986
+
return base;
12987
+
}
12988
+
}
12989
+
12990
+
/**
12991
+
* Parses an entire function, including its base and all of its arguments.
12992
+
*/
12993
+
parseFunction(
12994
+
breakOnTokenText,
12995
+
name // For determining its context
12996
+
) {
12997
+
const token = this.fetch();
12998
+
const func = token.text;
12999
+
const funcData = functions[func];
13000
+
if (!funcData) {
13001
+
return null;
13002
+
}
13003
+
this.consume(); // consume command token
13004
+
13005
+
if (name && name !== "atom" && !funcData.allowedInArgument) {
13006
+
throw new ParseError(
13007
+
"Got function '" + func + "' with no arguments" + (name ? " as " + name : ""),
13008
+
token
13009
+
);
13010
+
} else if (this.mode === "text" && !funcData.allowedInText) {
13011
+
throw new ParseError("Can't use function '" + func + "' in text mode", token);
13012
+
} else if (this.mode === "math" && funcData.allowedInMath === false) {
13013
+
throw new ParseError("Can't use function '" + func + "' in math mode", token);
13014
+
}
13015
+
13016
+
const prevAtomType = this.prevAtomType;
13017
+
const { args, optArgs } = this.parseArguments(func, funcData);
13018
+
this.prevAtomType = prevAtomType;
13019
+
return this.callFunction(func, args, optArgs, token, breakOnTokenText);
13020
+
}
13021
+
13022
+
/**
13023
+
* Call a function handler with a suitable context and arguments.
13024
+
*/
13025
+
callFunction(name, args, optArgs, token, breakOnTokenText) {
13026
+
const context = {
13027
+
funcName: name,
13028
+
parser: this,
13029
+
token,
13030
+
breakOnTokenText
13031
+
};
13032
+
const func = functions[name];
13033
+
if (func && func.handler) {
13034
+
return func.handler(context, args, optArgs);
13035
+
} else {
13036
+
throw new ParseError(`No function handler for ${name}`);
13037
+
}
13038
+
}
13039
+
13040
+
/**
13041
+
* Parses the arguments of a function or environment
13042
+
*/
13043
+
parseArguments(
13044
+
func, // Should look like "\name" or "\begin{name}".
13045
+
funcData
13046
+
) {
13047
+
const totalArgs = funcData.numArgs + funcData.numOptionalArgs;
13048
+
if (totalArgs === 0) {
13049
+
return { args: [], optArgs: [] };
13050
+
}
13051
+
13052
+
const args = [];
13053
+
const optArgs = [];
13054
+
13055
+
for (let i = 0; i < totalArgs; i++) {
13056
+
let argType = funcData.argTypes && funcData.argTypes[i];
13057
+
const isOptional = i < funcData.numOptionalArgs;
13058
+
13059
+
if (
13060
+
(funcData.primitive && argType == null) ||
13061
+
// \sqrt expands into primitive if optional argument doesn't exist
13062
+
(funcData.type === "sqrt" && i === 1 && optArgs[0] == null)
13063
+
) {
13064
+
argType = "primitive";
13065
+
}
13066
+
13067
+
const arg = this.parseGroupOfType(`argument to '${func}'`, argType, isOptional);
13068
+
if (isOptional) {
13069
+
optArgs.push(arg);
13070
+
} else if (arg != null) {
13071
+
args.push(arg);
13072
+
} else {
13073
+
// should be unreachable
13074
+
throw new ParseError("Null argument, please report this as a bug");
13075
+
}
13076
+
}
13077
+
13078
+
return { args, optArgs };
13079
+
}
13080
+
13081
+
/**
13082
+
* Parses a group when the mode is changing.
13083
+
*/
13084
+
parseGroupOfType(name, type, optional) {
13085
+
switch (type) {
13086
+
case "size":
13087
+
return this.parseSizeGroup(optional);
13088
+
case "url":
13089
+
return this.parseUrlGroup(optional);
13090
+
case "math":
13091
+
case "text":
13092
+
return this.parseArgumentGroup(optional, type);
13093
+
case "hbox": {
13094
+
// hbox argument type wraps the argument in the equivalent of
13095
+
// \hbox, which is like \text but switching to \textstyle size.
13096
+
const group = this.parseArgumentGroup(optional, "text");
13097
+
return group != null
13098
+
? {
13099
+
type: "styling",
13100
+
mode: group.mode,
13101
+
body: [group],
13102
+
scriptLevel: "text" // simulate \textstyle
13103
+
}
13104
+
: null;
13105
+
}
13106
+
case "raw": {
13107
+
const token = this.parseStringGroup("raw", optional);
13108
+
return token != null
13109
+
? {
13110
+
type: "raw",
13111
+
mode: "text",
13112
+
string: token.text
13113
+
}
13114
+
: null;
13115
+
}
13116
+
case "primitive": {
13117
+
if (optional) {
13118
+
throw new ParseError("A primitive argument cannot be optional");
13119
+
}
13120
+
const group = this.parseGroup(name);
13121
+
if (group == null) {
13122
+
throw new ParseError("Expected group as " + name, this.fetch());
13123
+
}
13124
+
return group;
13125
+
}
13126
+
case "original":
13127
+
case null:
13128
+
case undefined:
13129
+
return this.parseArgumentGroup(optional);
13130
+
default:
13131
+
throw new ParseError("Unknown group type as " + name, this.fetch());
13132
+
}
13133
+
}
13134
+
13135
+
/**
13136
+
* Discard any space tokens, fetching the next non-space token.
13137
+
*/
13138
+
consumeSpaces() {
13139
+
while (true) {
13140
+
const ch = this.fetch().text;
13141
+
// \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
13142
+
if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
13143
+
this.consume();
13144
+
} else {
13145
+
break
13146
+
}
13147
+
}
13148
+
}
13149
+
13150
+
/**
13151
+
* Parses a group, essentially returning the string formed by the
13152
+
* brace-enclosed tokens plus some position information.
13153
+
*/
13154
+
parseStringGroup(
13155
+
modeName, // Used to describe the mode in error messages.
13156
+
optional
13157
+
) {
13158
+
const argToken = this.gullet.scanArgument(optional);
13159
+
if (argToken == null) {
13160
+
return null;
13161
+
}
13162
+
let str = "";
13163
+
let nextToken;
13164
+
while ((nextToken = this.fetch()).text !== "EOF") {
13165
+
str += nextToken.text;
13166
+
this.consume();
13167
+
}
13168
+
this.consume(); // consume the end of the argument
13169
+
argToken.text = str;
13170
+
return argToken;
13171
+
}
13172
+
13173
+
/**
13174
+
* Parses a regex-delimited group: the largest sequence of tokens
13175
+
* whose concatenated strings match `regex`. Returns the string
13176
+
* formed by the tokens plus some position information.
13177
+
*/
13178
+
parseRegexGroup(
13179
+
regex,
13180
+
modeName // Used to describe the mode in error messages.
13181
+
) {
13182
+
const firstToken = this.fetch();
13183
+
let lastToken = firstToken;
13184
+
let str = "";
13185
+
let nextToken;
13186
+
while ((nextToken = this.fetch()).text !== "EOF" && regex.test(str + nextToken.text)) {
13187
+
lastToken = nextToken;
13188
+
str += lastToken.text;
13189
+
this.consume();
13190
+
}
13191
+
if (str === "") {
13192
+
throw new ParseError("Invalid " + modeName + ": '" + firstToken.text + "'", firstToken);
13193
+
}
13194
+
return firstToken.range(lastToken, str);
13195
+
}
13196
+
13197
+
/**
13198
+
* Parses a size specification, consisting of magnitude and unit.
13199
+
*/
13200
+
parseSizeGroup(optional) {
13201
+
let res;
13202
+
let isBlank = false;
13203
+
// don't expand before parseStringGroup
13204
+
this.gullet.consumeSpaces();
13205
+
if (!optional && this.gullet.future().text !== "{") {
13206
+
res = this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size");
13207
+
} else {
13208
+
res = this.parseStringGroup("size", optional);
13209
+
}
13210
+
if (!res) {
13211
+
return null;
13212
+
}
13213
+
if (!optional && res.text.length === 0) {
13214
+
// Because we've tested for what is !optional, this block won't
13215
+
// affect \kern, \hspace, etc. It will capture the mandatory arguments
13216
+
// to \genfrac and \above.
13217
+
res.text = "0pt"; // Enable \above{}
13218
+
isBlank = true; // This is here specifically for \genfrac
13219
+
}
13220
+
const match = sizeRegEx.exec(res.text);
13221
+
if (!match) {
13222
+
throw new ParseError("Invalid size: '" + res.text + "'", res);
13223
+
}
13224
+
const data = {
13225
+
number: +(match[1] + match[2]), // sign + magnitude, cast to number
13226
+
unit: match[3]
13227
+
};
13228
+
if (!validUnit(data)) {
13229
+
throw new ParseError("Invalid unit: '" + data.unit + "'", res);
13230
+
}
13231
+
return {
13232
+
type: "size",
13233
+
mode: this.mode,
13234
+
value: data,
13235
+
isBlank
13236
+
};
13237
+
}
13238
+
13239
+
/**
13240
+
* Parses an URL, checking escaped letters and allowed protocols,
13241
+
* and setting the catcode of % as an active character (as in \hyperref).
13242
+
*/
13243
+
parseUrlGroup(optional) {
13244
+
this.gullet.lexer.setCatcode("%", 13); // active character
13245
+
this.gullet.lexer.setCatcode("~", 12); // other character
13246
+
const res = this.parseStringGroup("url", optional);
13247
+
this.gullet.lexer.setCatcode("%", 14); // comment character
13248
+
this.gullet.lexer.setCatcode("~", 13); // active character
13249
+
if (res == null) {
13250
+
return null;
13251
+
}
13252
+
// hyperref package allows backslashes alone in href, but doesn't
13253
+
// generate valid links in such cases; we interpret this as
13254
+
// "undefined" behaviour, and keep them as-is. Some browser will
13255
+
// replace backslashes with forward slashes.
13256
+
let url = res.text.replace(/\\([#$%&~_^{}])/g, "$1");
13257
+
url = res.text.replace(/{\u2044}/g, "/");
13258
+
return {
13259
+
type: "url",
13260
+
mode: this.mode,
13261
+
url
13262
+
};
13263
+
}
13264
+
13265
+
/**
13266
+
* Parses an argument with the mode specified.
13267
+
*/
13268
+
parseArgumentGroup(optional, mode) {
13269
+
const argToken = this.gullet.scanArgument(optional);
13270
+
if (argToken == null) {
13271
+
return null;
13272
+
}
13273
+
const outerMode = this.mode;
13274
+
if (mode) {
13275
+
// Switch to specified mode
13276
+
this.switchMode(mode);
13277
+
}
13278
+
13279
+
this.gullet.beginGroup();
13280
+
const expression = this.parseExpression(false, "EOF");
13281
+
// TODO: find an alternative way to denote the end
13282
+
this.expect("EOF"); // expect the end of the argument
13283
+
this.gullet.endGroup();
13284
+
const result = {
13285
+
type: "ordgroup",
13286
+
mode: this.mode,
13287
+
loc: argToken.loc,
13288
+
body: expression
13289
+
};
13290
+
13291
+
if (mode) {
13292
+
// Switch mode back
13293
+
this.switchMode(outerMode);
13294
+
}
13295
+
return result;
13296
+
}
13297
+
13298
+
/**
13299
+
* Parses an ordinary group, which is either a single nucleus (like "x")
13300
+
* or an expression in braces (like "{x+y}") or an implicit group, a group
13301
+
* that starts at the current position, and ends right before a higher explicit
13302
+
* group ends, or at EOF.
13303
+
*/
13304
+
parseGroup(
13305
+
name, // For error reporting.
13306
+
breakOnTokenText
13307
+
) {
13308
+
const firstToken = this.fetch();
13309
+
const text = firstToken.text;
13310
+
13311
+
let result;
13312
+
// Try to parse an open brace or \begingroup
13313
+
if (text === "{" || text === "\\begingroup" || text === "\\toggle") {
13314
+
this.consume();
13315
+
const groupEnd = text === "{"
13316
+
? "}"
13317
+
: text === "\\begingroup"
13318
+
? "\\endgroup"
13319
+
: "\\endtoggle";
13320
+
13321
+
this.gullet.beginGroup();
13322
+
// If we get a brace, parse an expression
13323
+
const expression = this.parseExpression(false, groupEnd);
13324
+
const lastToken = this.fetch();
13325
+
this.expect(groupEnd); // Check that we got a matching closing brace
13326
+
this.gullet.endGroup();
13327
+
result = {
13328
+
type: (lastToken.text === "\\endtoggle" ? "toggle" : "ordgroup"),
13329
+
mode: this.mode,
13330
+
loc: SourceLocation.range(firstToken, lastToken),
13331
+
body: expression,
13332
+
// A group formed by \begingroup...\endgroup is a semi-simple group
13333
+
// which doesn't affect spacing in math mode, i.e., is transparent.
13334
+
// https://tex.stackexchange.com/questions/1930/
13335
+
semisimple: text === "\\begingroup" || undefined
13336
+
};
13337
+
} else {
13338
+
// If there exists a function with this name, parse the function.
13339
+
// Otherwise, just return a nucleus
13340
+
result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol();
13341
+
if (result == null && text[0] === "\\" &&
13342
+
!Object.prototype.hasOwnProperty.call(implicitCommands, text )) {
13343
+
result = this.formatUnsupportedCmd(text);
13344
+
this.consume();
13345
+
}
13346
+
}
13347
+
return result;
13348
+
}
13349
+
13350
+
/**
13351
+
* Form ligature-like combinations of characters for text mode.
13352
+
* This includes inputs like "--", "---", "``" and "''".
13353
+
* The result will simply replace multiple textord nodes with a single
13354
+
* character in each value by a single textord node having multiple
13355
+
* characters in its value. The representation is still ASCII source.
13356
+
* The group will be modified in place.
13357
+
*/
13358
+
formLigatures(group) {
13359
+
let n = group.length - 1;
13360
+
for (let i = 0; i < n; ++i) {
13361
+
const a = group[i];
13362
+
const v = a.text;
13363
+
if (v === "-" && group[i + 1].text === "-") {
13364
+
if (i + 1 < n && group[i + 2].text === "-") {
13365
+
group.splice(i, 3, {
13366
+
type: "textord",
13367
+
mode: "text",
13368
+
loc: SourceLocation.range(a, group[i + 2]),
13369
+
text: "---"
13370
+
});
13371
+
n -= 2;
13372
+
} else {
13373
+
group.splice(i, 2, {
13374
+
type: "textord",
13375
+
mode: "text",
13376
+
loc: SourceLocation.range(a, group[i + 1]),
13377
+
text: "--"
13378
+
});
13379
+
n -= 1;
13380
+
}
13381
+
}
13382
+
if ((v === "'" || v === "`") && group[i + 1].text === v) {
13383
+
group.splice(i, 2, {
13384
+
type: "textord",
13385
+
mode: "text",
13386
+
loc: SourceLocation.range(a, group[i + 1]),
13387
+
text: v + v
13388
+
});
13389
+
n -= 1;
13390
+
}
13391
+
}
13392
+
}
13393
+
13394
+
/**
13395
+
* Parse a single symbol out of the string. Here, we handle single character
13396
+
* symbols and special functions like \verb.
13397
+
*/
13398
+
parseSymbol() {
13399
+
const nucleus = this.fetch();
13400
+
let text = nucleus.text;
13401
+
13402
+
if (/^\\verb[^a-zA-Z]/.test(text)) {
13403
+
this.consume();
13404
+
let arg = text.slice(5);
13405
+
const star = arg.charAt(0) === "*";
13406
+
if (star) {
13407
+
arg = arg.slice(1);
13408
+
}
13409
+
// Lexer's tokenRegex is constructed to always have matching
13410
+
// first/last characters.
13411
+
if (arg.length < 2 || arg.charAt(0) !== arg.slice(-1)) {
13412
+
throw new ParseError(`\\verb assertion failed --
13413
+
please report what input caused this bug`);
13414
+
}
13415
+
arg = arg.slice(1, -1); // remove first and last char
13416
+
return {
13417
+
type: "verb",
13418
+
mode: "text",
13419
+
body: arg,
13420
+
star
13421
+
};
13422
+
}
13423
+
// At this point, we should have a symbol, possibly with accents.
13424
+
// First expand any accented base symbol according to unicodeSymbols.
13425
+
if (Object.prototype.hasOwnProperty.call(unicodeSymbols, text[0]) &&
13426
+
this.mode === "math" && !symbols[this.mode][text[0]]) {
13427
+
// This behavior is not strict (XeTeX-compatible) in math mode.
13428
+
if (this.settings.strict && this.mode === "math") {
13429
+
throw new ParseError(`Accented Unicode text character "${text[0]}" used in ` + `math mode`,
13430
+
nucleus
13431
+
);
13432
+
}
13433
+
text = unicodeSymbols[text[0]] + text.slice(1);
13434
+
}
13435
+
// Strip off any combining characters
13436
+
const match = this.mode === "math"
13437
+
? combiningDiacriticalMarksEndRegex.exec(text)
13438
+
: null;
13439
+
if (match) {
13440
+
text = text.substring(0, match.index);
13441
+
if (text === "i") {
13442
+
text = "\u0131"; // dotless i, in math and text mode
13443
+
} else if (text === "j") {
13444
+
text = "\u0237"; // dotless j, in math and text mode
13445
+
}
13446
+
}
13447
+
// Recognize base symbol
13448
+
let symbol;
13449
+
if (symbols[this.mode][text]) {
13450
+
let group = symbols[this.mode][text].group;
13451
+
if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
13452
+
// Change from a binary operator to a unary (prefix) operator
13453
+
group = "open";
13454
+
}
13455
+
const loc = SourceLocation.range(nucleus);
13456
+
let s;
13457
+
if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
13458
+
const family = group;
13459
+
s = {
13460
+
type: "atom",
13461
+
mode: this.mode,
13462
+
family,
13463
+
loc,
13464
+
text
13465
+
};
13466
+
} else {
13467
+
if (asciiFromScript[text]) {
13468
+
// Unicode 14 disambiguates chancery from roundhand.
13469
+
// See https://www.unicode.org/charts/PDF/U1D400.pdf
13470
+
this.consume();
13471
+
const nextCode = this.fetch().text.charCodeAt(0);
13472
+
// mathcal is Temml default. Use mathscript if called for.
13473
+
const font = nextCode === 0xfe01 ? "mathscr" : "mathcal";
13474
+
if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume(); }
13475
+
return {
13476
+
type: "font",
13477
+
mode: "math",
13478
+
font,
13479
+
body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] }
13480
+
}
13481
+
}
13482
+
// Default ord character. No disambiguation necessary.
13483
+
s = {
13484
+
type: group,
13485
+
mode: this.mode,
13486
+
loc,
13487
+
text
13488
+
};
13489
+
}
13490
+
symbol = s;
13491
+
} else if (text.charCodeAt(0) >= 0x80 || combiningDiacriticalMarksEndRegex.exec(text)) {
13492
+
// no symbol for e.g. ^
13493
+
if (this.settings.strict && this.mode === "math") {
13494
+
throw new ParseError(`Unicode text character "${text[0]}" used in math mode`, nucleus)
13495
+
}
13496
+
// All nonmathematical Unicode characters are rendered as if they
13497
+
// are in text mode (wrapped in \text) because that's what it
13498
+
// takes to render them in LaTeX.
13499
+
symbol = {
13500
+
type: "textord",
13501
+
mode: "text",
13502
+
loc: SourceLocation.range(nucleus),
13503
+
text
13504
+
};
13505
+
} else {
13506
+
return null; // EOF, ^, _, {, }, etc.
13507
+
}
13508
+
this.consume();
13509
+
// Transform combining characters into accents
13510
+
if (match) {
13511
+
for (let i = 0; i < match[0].length; i++) {
13512
+
const accent = match[0][i];
13513
+
if (!unicodeAccents[accent]) {
13514
+
throw new ParseError(`Unknown accent ' ${accent}'`, nucleus);
13515
+
}
13516
+
const command = unicodeAccents[accent][this.mode] ||
13517
+
unicodeAccents[accent].text;
13518
+
if (!command) {
13519
+
throw new ParseError(`Accent ${accent} unsupported in ${this.mode} mode`, nucleus);
13520
+
}
13521
+
symbol = {
13522
+
type: "accent",
13523
+
mode: this.mode,
13524
+
loc: SourceLocation.range(nucleus),
13525
+
label: command,
13526
+
isStretchy: false,
13527
+
base: symbol
13528
+
};
13529
+
}
13530
+
}
13531
+
return symbol;
13532
+
}
13533
+
}
13534
+
13535
+
/**
13536
+
* Parses an expression using a Parser, then returns the parsed result.
13537
+
*/
13538
+
const parseTree = function(toParse, settings) {
13539
+
if (!(typeof toParse === "string" || toParse instanceof String)) {
13540
+
throw new TypeError("Temml can only parse string typed expression")
13541
+
}
13542
+
const parser = new Parser(toParse, settings);
13543
+
// Blank out any \df@tag to avoid spurious "Duplicate \tag" errors
13544
+
delete parser.gullet.macros.current["\\df@tag"];
13545
+
13546
+
let tree = parser.parse();
13547
+
13548
+
// LaTeX ignores a \tag placed outside an AMS environment.
13549
+
if (!(tree.length > 0 && tree[0].type && tree[0].type === "array" && tree[0].addEqnNum)) {
13550
+
// If the input used \tag, it will set the \df@tag macro to the tag.
13551
+
// In this case, we separately parse the tag and wrap the tree.
13552
+
if (parser.gullet.macros.get("\\df@tag")) {
13553
+
if (!settings.displayMode) {
13554
+
throw new ParseError("\\tag works only in display mode")
13555
+
}
13556
+
parser.gullet.feed("\\df@tag");
13557
+
tree = [
13558
+
{
13559
+
type: "tag",
13560
+
mode: "text",
13561
+
body: tree,
13562
+
tag: parser.parse()
13563
+
}
13564
+
];
13565
+
}
13566
+
}
13567
+
13568
+
return tree
13569
+
};
13570
+
13571
+
/**
13572
+
* This file contains information about the style that the mathmlBuilder carries
13573
+
* around with it. Data is held in an `Style` object, and when
13574
+
* recursing, a new `Style` object can be created with the `.with*` functions.
13575
+
*/
13576
+
13577
+
const subOrSupLevel = [2, 2, 3, 3];
13578
+
13579
+
/**
13580
+
* This is the main Style class. It contains the current style.level, color, and font.
13581
+
*
13582
+
* Style objects should not be modified. To create a new Style with
13583
+
* different properties, call a `.with*` method.
13584
+
*/
13585
+
class Style {
13586
+
constructor(data) {
13587
+
// Style.level can be 0 | 1 | 2 | 3, which correspond to
13588
+
// displaystyle, textstyle, scriptstyle, and scriptscriptstyle.
13589
+
// style.level usually does not directly set MathML's script level. MathML does that itself.
13590
+
// However, Chromium does not stop shrinking after scriptscriptstyle, so we do explicitly
13591
+
// set a scriptlevel attribute in those conditions.
13592
+
// We also use style.level to track math style so that we can get the correct
13593
+
// scriptlevel when needed in supsub.js, mathchoice.js, or for dimensions in em.
13594
+
this.level = data.level;
13595
+
this.color = data.color; // string | void
13596
+
// A font family applies to a group of fonts (i.e. SansSerif), while a font
13597
+
// represents a specific font (i.e. SansSerif Bold).
13598
+
// See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm
13599
+
this.font = data.font || ""; // string
13600
+
this.fontFamily = data.fontFamily || ""; // string
13601
+
this.fontSize = data.fontSize || 1.0; // number
13602
+
this.fontWeight = data.fontWeight || "";
13603
+
this.fontShape = data.fontShape || "";
13604
+
this.maxSize = data.maxSize; // [number, number]
13605
+
}
13606
+
13607
+
/**
13608
+
* Returns a new style object with the same properties as "this". Properties
13609
+
* from "extension" will be copied to the new style object.
13610
+
*/
13611
+
extend(extension) {
13612
+
const data = {
13613
+
level: this.level,
13614
+
color: this.color,
13615
+
font: this.font,
13616
+
fontFamily: this.fontFamily,
13617
+
fontSize: this.fontSize,
13618
+
fontWeight: this.fontWeight,
13619
+
fontShape: this.fontShape,
13620
+
maxSize: this.maxSize
13621
+
};
13622
+
13623
+
for (const key in extension) {
13624
+
if (Object.prototype.hasOwnProperty.call(extension, key)) {
13625
+
data[key] = extension[key];
13626
+
}
13627
+
}
13628
+
13629
+
return new Style(data);
13630
+
}
13631
+
13632
+
withLevel(n) {
13633
+
return this.extend({
13634
+
level: n
13635
+
});
13636
+
}
13637
+
13638
+
incrementLevel() {
13639
+
return this.extend({
13640
+
level: Math.min(this.level + 1, 3)
13641
+
});
13642
+
}
13643
+
13644
+
inSubOrSup() {
13645
+
return this.extend({
13646
+
level: subOrSupLevel[this.level]
13647
+
})
13648
+
}
13649
+
13650
+
/**
13651
+
* Create a new style object with the given color.
13652
+
*/
13653
+
withColor(color) {
13654
+
return this.extend({
13655
+
color: color
13656
+
});
13657
+
}
13658
+
13659
+
/**
13660
+
* Creates a new style object with the given math font or old text font.
13661
+
* @type {[type]}
13662
+
*/
13663
+
withFont(font) {
13664
+
return this.extend({
13665
+
font
13666
+
});
13667
+
}
13668
+
13669
+
/**
13670
+
* Create a new style objects with the given fontFamily.
13671
+
*/
13672
+
withTextFontFamily(fontFamily) {
13673
+
return this.extend({
13674
+
fontFamily,
13675
+
font: ""
13676
+
});
13677
+
}
13678
+
13679
+
/**
13680
+
* Creates a new style object with the given font size
13681
+
*/
13682
+
withFontSize(num) {
13683
+
return this.extend({
13684
+
fontSize: num
13685
+
});
13686
+
}
13687
+
13688
+
/**
13689
+
* Creates a new style object with the given font weight
13690
+
*/
13691
+
withTextFontWeight(fontWeight) {
13692
+
return this.extend({
13693
+
fontWeight,
13694
+
font: ""
13695
+
});
13696
+
}
13697
+
13698
+
/**
13699
+
* Creates a new style object with the given font weight
13700
+
*/
13701
+
withTextFontShape(fontShape) {
13702
+
return this.extend({
13703
+
fontShape,
13704
+
font: ""
13705
+
});
13706
+
}
13707
+
13708
+
/**
13709
+
* Gets the CSS color of the current style object
13710
+
*/
13711
+
getColor() {
13712
+
return this.color;
13713
+
}
13714
+
}
13715
+
13716
+
/* Temml Post Process
13717
+
* Populate the text contents of each \ref & \eqref
13718
+
*
13719
+
* As with other Temml code, this file is released under terms of the MIT license.
13720
+
* https://mit-license.org/
13721
+
*/
13722
+
13723
+
const version = "0.10.34";
13724
+
13725
+
function postProcess(block) {
13726
+
const labelMap = {};
13727
+
let i = 0;
13728
+
13729
+
// Get a collection of the parents of each \tag & auto-numbered equation
13730
+
const amsEqns = document.getElementsByClassName('tml-eqn');
13731
+
for (let parent of amsEqns) {
13732
+
// AMS automatically numbered equation.
13733
+
// Assign an id.
13734
+
i += 1;
13735
+
parent.setAttribute("id", "tml-eqn-" + String(i));
13736
+
// No need to write a number into the text content of the element.
13737
+
// A CSS counter has done that even if this postProcess() function is not used.
13738
+
13739
+
// Find any \label that refers to an AMS automatic eqn number.
13740
+
while (true) {
13741
+
if (parent.tagName === "mtable") { break }
13742
+
const labels = parent.getElementsByClassName("tml-label");
13743
+
if (labels.length > 0) {
13744
+
const id = parent.attributes.id.value;
13745
+
labelMap[id] = String(i);
13746
+
break
13747
+
} else {
13748
+
parent = parent.parentElement;
13749
+
}
13750
+
}
13751
+
}
13752
+
13753
+
// Find \labels associated with \tag
13754
+
const taggedEqns = document.getElementsByClassName('tml-tageqn');
13755
+
for (const parent of taggedEqns) {
13756
+
const labels = parent.getElementsByClassName("tml-label");
13757
+
if (labels.length > 0) {
13758
+
const tags = parent.getElementsByClassName("tml-tag");
13759
+
if (tags.length > 0) {
13760
+
const id = parent.attributes.id.value;
13761
+
labelMap[id] = tags[0].textContent;
13762
+
}
13763
+
}
13764
+
}
13765
+
13766
+
// Populate \ref & \eqref text content
13767
+
const refs = block.getElementsByClassName("tml-ref");
13768
+
[...refs].forEach(ref => {
13769
+
const attr = ref.getAttribute("href");
13770
+
let str = labelMap[attr.slice(1)];
13771
+
if (ref.className.indexOf("tml-eqref") === -1) {
13772
+
// \ref. Omit parens.
13773
+
str = str.replace(/^\(/, "");
13774
+
str = str.replace(/\)$/, "");
13775
+
} else {
13776
+
// \eqref. Include parens
13777
+
if (str.charAt(0) !== "(") { str = "(" + str; }
13778
+
if (str.slice(-1) !== ")") { str = str + ")"; }
13779
+
}
13780
+
const mtext = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mtext");
13781
+
mtext.appendChild(document.createTextNode(str));
13782
+
const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math");
13783
+
math.appendChild(mtext);
13784
+
ref.appendChild(math);
13785
+
});
13786
+
}
13787
+
13788
+
/* eslint no-console:0 */
13789
+
/**
13790
+
* This is the main entry point for Temml. Here, we expose functions for
13791
+
* rendering expressions either to DOM nodes or to markup strings.
13792
+
*
13793
+
* We also expose the ParseError class to check if errors thrown from Temml are
13794
+
* errors in the expression, or errors in javascript handling.
13795
+
*/
13796
+
13797
+
13798
+
/**
13799
+
* @type {import('./temml').render}
13800
+
* Parse and build an expression, and place that expression in the DOM node
13801
+
* given.
13802
+
*/
13803
+
let render = function(expression, baseNode, options = {}) {
13804
+
baseNode.textContent = "";
13805
+
const alreadyInMathElement = baseNode.tagName.toLowerCase() === "math";
13806
+
if (alreadyInMathElement) { options.wrap = "none"; }
13807
+
const math = renderToMathMLTree(expression, options);
13808
+
if (alreadyInMathElement) {
13809
+
// The <math> element already exists. Populate it.
13810
+
baseNode.textContent = "";
13811
+
math.children.forEach(e => { baseNode.appendChild(e.toNode()); });
13812
+
} else if (math.children.length > 1) {
13813
+
baseNode.textContent = "";
13814
+
math.children.forEach(e => { baseNode.appendChild(e.toNode()); });
13815
+
} else {
13816
+
baseNode.appendChild(math.toNode());
13817
+
}
13818
+
};
13819
+
13820
+
// Temml's styles don't work properly in quirks mode. Print out an error, and
13821
+
// disable rendering.
13822
+
if (typeof document !== "undefined") {
13823
+
if (document.compatMode !== "CSS1Compat") {
13824
+
typeof console !== "undefined" &&
13825
+
console.warn(
13826
+
"Warning: Temml doesn't work in quirks mode. Make sure your " +
13827
+
"website has a suitable doctype."
13828
+
);
13829
+
13830
+
render = function() {
13831
+
throw new ParseError("Temml doesn't work in quirks mode.");
13832
+
};
13833
+
}
13834
+
}
13835
+
13836
+
/**
13837
+
* @type {import('./temml').renderToString}
13838
+
* Parse and build an expression, and return the markup for that.
13839
+
*/
13840
+
const renderToString = function(expression, options) {
13841
+
const markup = renderToMathMLTree(expression, options).toMarkup();
13842
+
return markup;
13843
+
};
13844
+
13845
+
/**
13846
+
* @type {import('./temml').generateParseTree}
13847
+
* Parse an expression and return the parse tree.
13848
+
*/
13849
+
const generateParseTree = function(expression, options) {
13850
+
const settings = new Settings(options);
13851
+
return parseTree(expression, settings);
13852
+
};
13853
+
13854
+
/**
13855
+
* @type {import('./temml').definePreamble}
13856
+
* Take an expression which contains a preamble.
13857
+
* Parse it and return the macros.
13858
+
*/
13859
+
const definePreamble = function(expression, options) {
13860
+
const settings = new Settings(options);
13861
+
settings.macros = {};
13862
+
if (!(typeof expression === "string" || expression instanceof String)) {
13863
+
throw new TypeError("Temml can only parse string typed expression")
13864
+
}
13865
+
const parser = new Parser(expression, settings, true);
13866
+
// Blank out any \df@tag to avoid spurious "Duplicate \tag" errors
13867
+
delete parser.gullet.macros.current["\\df@tag"];
13868
+
const macros = parser.parse();
13869
+
return macros
13870
+
};
13871
+
13872
+
/**
13873
+
* If the given error is a Temml ParseError,
13874
+
* renders the invalid LaTeX as a span with hover title giving the Temml
13875
+
* error message. Otherwise, simply throws the error.
13876
+
*/
13877
+
const renderError = function(error, expression, options) {
13878
+
if (options.throwOnError || !(error instanceof ParseError)) {
13879
+
throw error;
13880
+
}
13881
+
const node = new Span(["temml-error"], [new TextNode$1(expression + "\n" + error.toString())]);
13882
+
node.style.color = options.errorColor;
13883
+
node.style.whiteSpace = "pre-line";
13884
+
return node;
13885
+
};
13886
+
13887
+
/**
13888
+
* @type {import('./temml').renderToMathMLTree}
13889
+
* Generates and returns the Temml build tree. This is used for advanced
13890
+
* use cases (like rendering to custom output).
13891
+
*/
13892
+
const renderToMathMLTree = function(expression, options) {
13893
+
const settings = new Settings(options);
13894
+
try {
13895
+
const tree = parseTree(expression, settings);
13896
+
const style = new Style({
13897
+
level: settings.displayMode ? StyleLevel.DISPLAY : StyleLevel.TEXT,
13898
+
maxSize: settings.maxSize
13899
+
});
13900
+
return buildMathML(tree, expression, style, settings);
13901
+
} catch (error) {
13902
+
return renderError(error, expression, settings);
13903
+
}
13904
+
};
13905
+
13906
+
/** @type {import('./temml').default} */
13907
+
var temml = {
13908
+
/**
13909
+
* Current Temml version
13910
+
*/
13911
+
version: version,
13912
+
/**
13913
+
* Renders the given LaTeX into MathML, and adds
13914
+
* it as a child to the specified DOM node.
13915
+
*/
13916
+
render,
13917
+
/**
13918
+
* Renders the given LaTeX into MathML string,
13919
+
* for sending to the client.
13920
+
*/
13921
+
renderToString,
13922
+
/**
13923
+
* Post-process an entire HTML block.
13924
+
* Writes AMS auto-numbers and implements \ref{}.
13925
+
* Typcally called once, after a loop has rendered many individual spans.
13926
+
*/
13927
+
postProcess,
13928
+
/**
13929
+
* Temml error, usually during parsing.
13930
+
*/
13931
+
ParseError,
13932
+
/**
13933
+
* Creates a set of macros with document-wide scope.
13934
+
*/
13935
+
definePreamble,
13936
+
/**
13937
+
* Parses the given LaTeX into Temml's internal parse tree structure,
13938
+
* without rendering to HTML or MathML.
13939
+
*
13940
+
* NOTE: This method is not currently recommended for public use.
13941
+
* The internal tree representation is unstable and is very likely
13942
+
* to change. Use at your own risk.
13943
+
*/
13944
+
__parse: generateParseTree,
13945
+
/**
13946
+
* Renders the given LaTeX into a MathML internal DOM tree
13947
+
* representation, without flattening that representation to a string.
13948
+
*
13949
+
* NOTE: This method is not currently recommended for public use.
13950
+
* The internal tree representation is unstable and is very likely
13951
+
* to change. Use at your own risk.
13952
+
*/
13953
+
__renderToMathMLTree: renderToMathMLTree,
13954
+
/**
13955
+
* adds a new symbol to builtin symbols table
13956
+
*/
13957
+
__defineSymbol: defineSymbol,
13958
+
/**
13959
+
* adds a new macro to builtin macro list
13960
+
*/
13961
+
__defineMacro: defineMacro
13962
+
};
13963
+
13964
+
13965
+
13966
+
;// ./node_modules/@wordpress/latex-to-mathml/build-module/index.js
13967
+
13968
+
function latexToMathML(latex, { displayMode = true } = {}) {
13969
+
const mathML = temml.renderToString(latex, {
13970
+
displayMode,
13971
+
annotate: true,
13972
+
throwOnError: true
13973
+
});
13974
+
const doc = document.implementation.createHTMLDocument("");
13975
+
doc.body.innerHTML = mathML;
13976
+
return doc.body.querySelector("math")?.innerHTML ?? "";
13977
+
}
13978
+
13979
+
13980
+
(window.wp = window.wp || {}).latexToMathml = __webpack_exports__;
13981
+
/******/ })()
13982
+
;