[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 /******/ (() => { // webpackBootstrap 2 /******/ "use strict"; 3 /******/ // The require scope 4 /******/ var __webpack_require__ = {}; 5 /******/ 6 /************************************************************************/ 7 /******/ /* webpack/runtime/define property getters */ 8 /******/ (() => { 9 /******/ // define getter functions for harmony exports 10 /******/ __webpack_require__.d = (exports, definition) => { 11 /******/ for(var key in definition) { 12 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 13 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 14 /******/ } 15 /******/ } 16 /******/ }; 17 /******/ })(); 18 /******/ 19 /******/ /* webpack/runtime/hasOwnProperty shorthand */ 20 /******/ (() => { 21 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 22 /******/ })(); 23 /******/ 24 /******/ /* webpack/runtime/make namespace object */ 25 /******/ (() => { 26 /******/ // define __esModule on exports 27 /******/ __webpack_require__.r = (exports) => { 28 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 29 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 30 /******/ } 31 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 32 /******/ }; 33 /******/ })(); 34 /******/ 35 /************************************************************************/ 36 var __webpack_exports__ = {}; 37 // ESM COMPAT FLAG 38 __webpack_require__.r(__webpack_exports__); 39 40 // EXPORTS 41 __webpack_require__.d(__webpack_exports__, { 42 "default": () => (/* binding */ latexToMathML) 43 }); 44 45 ;// ./node_modules/temml/dist/temml.mjs 46 /** 47 * This is the ParseError class, which is the main error thrown by Temml 48 * functions when something has gone wrong. This is used to distinguish internal 49 * errors from errors in the expression that the user provided. 50 * 51 * If possible, a caller should provide a Token or ParseNode with information 52 * about where in the source string the problem occurred. 53 */ 54 class ParseError { 55 constructor( 56 message, // The error message 57 token // An object providing position information 58 ) { 59 let error = " " + message; 60 let start; 61 62 const loc = token && token.loc; 63 if (loc && loc.start <= loc.end) { 64 // If we have the input and a position, make the error a bit fancier 65 66 // Get the input 67 const input = loc.lexer.input; 68 69 // Prepend some information 70 start = loc.start; 71 const end = loc.end; 72 if (start === input.length) { 73 error += " at end of input: "; 74 } else { 75 error += " at position " + (start + 1) + ": "; 76 } 77 78 // Underline token in question using combining underscores 79 const underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332"); 80 81 // Extract some context from the input and add it to the error 82 let left; 83 if (start > 15) { 84 left = "…" + input.slice(start - 15, start); 85 } else { 86 left = input.slice(0, start); 87 } 88 let right; 89 if (end + 15 < input.length) { 90 right = input.slice(end, end + 15) + "…"; 91 } else { 92 right = input.slice(end); 93 } 94 error += left + underlined + right; 95 } 96 97 // Some hackery to make ParseError a prototype of Error 98 // See http://stackoverflow.com/a/8460753 99 const self = new Error(error); 100 self.name = "ParseError"; 101 self.__proto__ = ParseError.prototype; 102 self.position = start; 103 return self; 104 } 105 } 106 107 ParseError.prototype.__proto__ = Error.prototype; 108 109 // 110 /** 111 * This file contains a list of utility functions which are useful in other 112 * files. 113 */ 114 115 /** 116 * Provide a default value if a setting is undefined 117 */ 118 const deflt = function(setting, defaultIfUndefined) { 119 return setting === undefined ? defaultIfUndefined : setting; 120 }; 121 122 // hyphenate and escape adapted from Facebook's React under Apache 2 license 123 124 const uppercase = /([A-Z])/g; 125 const hyphenate = function(str) { 126 return str.replace(uppercase, "-$1").toLowerCase(); 127 }; 128 129 const ESCAPE_LOOKUP = { 130 "&": "&", 131 ">": ">", 132 "<": "<", 133 '"': """, 134 "'": "'" 135 }; 136 137 const ESCAPE_REGEX = /[&><"']/g; 138 139 /** 140 * Escapes text to prevent scripting attacks. 141 */ 142 function temml_escape(text) { 143 return String(text).replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]); 144 } 145 146 /** 147 * Sometimes we want to pull out the innermost element of a group. In most 148 * cases, this will just be the group itself, but when ordgroups and colors have 149 * a single element, we want to pull that out. 150 */ 151 const getBaseElem = function(group) { 152 if (group.type === "ordgroup") { 153 if (group.body.length === 1) { 154 return getBaseElem(group.body[0]); 155 } else { 156 return group; 157 } 158 } else if (group.type === "color") { 159 if (group.body.length === 1) { 160 return getBaseElem(group.body[0]); 161 } else { 162 return group; 163 } 164 } else if (group.type === "font") { 165 return getBaseElem(group.body); 166 } else { 167 return group; 168 } 169 }; 170 171 /** 172 * TeXbook algorithms often reference "character boxes", which are simply groups 173 * with a single character in them. To decide if something is a character box, 174 * we find its innermost group, and see if it is a single character. 175 */ 176 const isCharacterBox = function(group) { 177 const baseElem = getBaseElem(group); 178 179 // These are all the types of groups which hold single characters 180 return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom" 181 }; 182 183 const assert = function(value) { 184 if (!value) { 185 throw new Error("Expected non-null, but got " + String(value)); 186 } 187 return value; 188 }; 189 190 /** 191 * Return the protocol of a URL, or "_relative" if the URL does not specify a 192 * protocol (and thus is relative), or `null` if URL has invalid protocol 193 * (so should be outright rejected). 194 */ 195 const protocolFromUrl = function(url) { 196 // Check for possible leading protocol. 197 // https://url.spec.whatwg.org/#url-parsing strips leading whitespace 198 // (\x00) or C0 control (\x00-\x1F) characters. 199 // eslint-disable-next-line no-control-regex 200 const protocol = /^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(url); 201 if (!protocol) { 202 return "_relative"; 203 } 204 // Reject weird colons 205 if (protocol[2] !== ":") { 206 return null; 207 } 208 // Reject invalid characters in scheme according to 209 // https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 210 if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(protocol[1])) { 211 return null; 212 } 213 // Lowercase the protocol 214 return protocol[1].toLowerCase(); 215 }; 216 217 /** 218 * Round `n` to 4 decimal places, or to the nearest 1/10,000th em. The TeXbook 219 * gives an acceptable rounding error of 100sp (which would be the nearest 220 * 1/6551.6em with our ptPerEm = 10): 221 * http://www.ctex.org/documents/shredder/src/texbook.pdf#page=69 222 */ 223 const round = function(n) { 224 return +n.toFixed(4); 225 }; 226 227 var utils = { 228 deflt, 229 escape: temml_escape, 230 hyphenate, 231 getBaseElem, 232 isCharacterBox, 233 protocolFromUrl, 234 round 235 }; 236 237 /** 238 * This is a module for storing settings passed into Temml. It correctly handles 239 * default settings. 240 */ 241 242 243 /** 244 * The main Settings object 245 */ 246 class Settings { 247 constructor(options) { 248 // allow null options 249 options = options || {}; 250 this.displayMode = utils.deflt(options.displayMode, false); // boolean 251 this.annotate = utils.deflt(options.annotate, false); // boolean 252 this.leqno = utils.deflt(options.leqno, false); // boolean 253 this.throwOnError = utils.deflt(options.throwOnError, false); // boolean 254 this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string 255 this.macros = options.macros || {}; 256 this.wrap = utils.deflt(options.wrap, "tex"); // "tex" | "=" 257 this.xml = utils.deflt(options.xml, false); // boolean 258 this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean 259 this.strict = utils.deflt(options.strict, false); // boolean 260 this.trust = utils.deflt(options.trust, false); // trust context. See html.js. 261 this.maxSize = (options.maxSize === undefined 262 ? [Infinity, Infinity] 263 : Array.isArray(options.maxSize) 264 ? options.maxSize 265 : [Infinity, Infinity] 266 ); 267 this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000)); // number 268 } 269 270 /** 271 * Check whether to test potentially dangerous input, and return 272 * `true` (trusted) or `false` (untrusted). The sole argument `context` 273 * should be an object with `command` field specifying the relevant LaTeX 274 * command (as a string starting with `\`), and any other arguments, etc. 275 * If `context` has a `url` field, a `protocol` field will automatically 276 * get added by this function (changing the specified object). 277 */ 278 isTrusted(context) { 279 if (context.url && !context.protocol) { 280 const protocol = utils.protocolFromUrl(context.url); 281 if (protocol == null) { 282 return false 283 } 284 context.protocol = protocol; 285 } 286 const trust = typeof this.trust === "function" ? this.trust(context) : this.trust; 287 return Boolean(trust); 288 } 289 } 290 291 /** 292 * All registered functions. 293 * `functions.js` just exports this same dictionary again and makes it public. 294 * `Parser.js` requires this dictionary. 295 */ 296 const _functions = {}; 297 298 /** 299 * All MathML builders. Should be only used in the `define*` and the `build*ML` 300 * functions. 301 */ 302 const _mathmlGroupBuilders = {}; 303 304 function defineFunction({ 305 type, 306 names, 307 props, 308 handler, 309 mathmlBuilder 310 }) { 311 // Set default values of functions 312 const data = { 313 type, 314 numArgs: props.numArgs, 315 argTypes: props.argTypes, 316 allowedInArgument: !!props.allowedInArgument, 317 allowedInText: !!props.allowedInText, 318 allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath, 319 numOptionalArgs: props.numOptionalArgs || 0, 320 infix: !!props.infix, 321 primitive: !!props.primitive, 322 handler: handler 323 }; 324 for (let i = 0; i < names.length; ++i) { 325 _functions[names[i]] = data; 326 } 327 if (type) { 328 if (mathmlBuilder) { 329 _mathmlGroupBuilders[type] = mathmlBuilder; 330 } 331 } 332 } 333 334 /** 335 * Use this to register only the MathML builder for a function(e.g. 336 * if the function's ParseNode is generated in Parser.js rather than via a 337 * stand-alone handler provided to `defineFunction`). 338 */ 339 function defineFunctionBuilders({ type, mathmlBuilder }) { 340 defineFunction({ 341 type, 342 names: [], 343 props: { numArgs: 0 }, 344 handler() { 345 throw new Error("Should never be called.") 346 }, 347 mathmlBuilder 348 }); 349 } 350 351 const normalizeArgument = function(arg) { 352 return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg 353 }; 354 355 // Since the corresponding buildMathML function expects a 356 // list of elements, we normalize for different kinds of arguments 357 const ordargument = function(arg) { 358 return arg.type === "ordgroup" ? arg.body : [arg] 359 }; 360 361 /** 362 * This node represents a document fragment, which contains elements, but when 363 * placed into the DOM doesn't have any representation itself. It only contains 364 * children and doesn't have any DOM node properties. 365 */ 366 class DocumentFragment { 367 constructor(children) { 368 this.children = children; 369 this.classes = []; 370 this.style = {}; 371 } 372 373 hasClass(className) { 374 return this.classes.includes(className); 375 } 376 377 /** Convert the fragment into a node. */ 378 toNode() { 379 const frag = document.createDocumentFragment(); 380 381 for (let i = 0; i < this.children.length; i++) { 382 frag.appendChild(this.children[i].toNode()); 383 } 384 385 return frag; 386 } 387 388 /** Convert the fragment into HTML markup. */ 389 toMarkup() { 390 let markup = ""; 391 392 // Simply concatenate the markup for the children together. 393 for (let i = 0; i < this.children.length; i++) { 394 markup += this.children[i].toMarkup(); 395 } 396 397 return markup; 398 } 399 400 /** 401 * Converts the math node into a string, similar to innerText. Applies to 402 * MathDomNode's only. 403 */ 404 toText() { 405 // To avoid this, we would subclass documentFragment separately for 406 // MathML, but polyfills for subclassing is expensive per PR 1469. 407 const toText = (child) => child.toText(); 408 return this.children.map(toText).join(""); 409 } 410 } 411 412 /** 413 * These objects store the data about the DOM nodes we create, as well as some 414 * extra data. They can then be transformed into real DOM nodes with the 415 * `toNode` function or HTML markup using `toMarkup`. They are useful for both 416 * storing extra properties on the nodes, as well as providing a way to easily 417 * work with the DOM. 418 * 419 * Similar functions for working with MathML nodes exist in mathMLTree.js. 420 * 421 */ 422 423 /** 424 * Create an HTML className based on a list of classes. In addition to joining 425 * with spaces, we also remove empty classes. 426 */ 427 const createClass = function(classes) { 428 return classes.filter((cls) => cls).join(" "); 429 }; 430 431 const initNode = function(classes, style) { 432 this.classes = classes || []; 433 this.attributes = {}; 434 this.style = style || {}; 435 }; 436 437 /** 438 * Convert into an HTML node 439 */ 440 const toNode = function(tagName) { 441 const node = document.createElement(tagName); 442 443 // Apply the class 444 node.className = createClass(this.classes); 445 446 // Apply inline styles 447 for (const style in this.style) { 448 if (Object.prototype.hasOwnProperty.call(this.style, style )) { 449 node.style[style] = this.style[style]; 450 } 451 } 452 453 // Apply attributes 454 for (const attr in this.attributes) { 455 if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { 456 node.setAttribute(attr, this.attributes[attr]); 457 } 458 } 459 460 // Append the children, also as HTML nodes 461 for (let i = 0; i < this.children.length; i++) { 462 node.appendChild(this.children[i].toNode()); 463 } 464 465 return node; 466 }; 467 468 /** 469 * Convert into an HTML markup string 470 */ 471 const toMarkup = function(tagName) { 472 let markup = `<$tagName}`; 473 474 // Add the class 475 if (this.classes.length) { 476 markup += ` class="$utils.escape(createClass(this.classes))}"`; 477 } 478 479 let styles = ""; 480 481 // Add the styles, after hyphenation 482 for (const style in this.style) { 483 if (Object.prototype.hasOwnProperty.call(this.style, style )) { 484 styles += `$utils.hyphenate(style)}:$this.style[style]};`; 485 } 486 } 487 488 if (styles) { 489 markup += ` style="$styles}"`; 490 } 491 492 // Add the attributes 493 for (const attr in this.attributes) { 494 if (Object.prototype.hasOwnProperty.call(this.attributes, attr )) { 495 markup += ` $attr}="$utils.escape(this.attributes[attr])}"`; 496 } 497 } 498 499 markup += ">"; 500 501 // Add the markup of the children, also as markup 502 for (let i = 0; i < this.children.length; i++) { 503 markup += this.children[i].toMarkup(); 504 } 505 506 markup += `</$tagName}>`; 507 508 return markup; 509 }; 510 511 /** 512 * This node represents a span node, with a className, a list of children, and 513 * an inline style. 514 * 515 */ 516 class Span { 517 constructor(classes, children, style) { 518 initNode.call(this, classes, style); 519 this.children = children || []; 520 } 521 522 setAttribute(attribute, value) { 523 this.attributes[attribute] = value; 524 } 525 526 toNode() { 527 return toNode.call(this, "span"); 528 } 529 530 toMarkup() { 531 return toMarkup.call(this, "span"); 532 } 533 } 534 535 let TextNode$1 = class TextNode { 536 constructor(text) { 537 this.text = text; 538 } 539 toNode() { 540 return document.createTextNode(this.text); 541 } 542 toMarkup() { 543 return utils.escape(this.text); 544 } 545 }; 546 547 // Create an <a href="…"> node. 548 class AnchorNode { 549 constructor(href, classes, children) { 550 this.href = href; 551 this.classes = classes; 552 this.children = children || []; 553 } 554 555 toNode() { 556 const node = document.createElement("a"); 557 node.setAttribute("href", this.href); 558 if (this.classes.length > 0) { 559 node.className = createClass(this.classes); 560 } 561 for (let i = 0; i < this.children.length; i++) { 562 node.appendChild(this.children[i].toNode()); 563 } 564 return node 565 } 566 567 toMarkup() { 568 let markup = `<a href='$utils.escape(this.href)}'`; 569 if (this.classes.length > 0) { 570 markup += ` class="$utils.escape(createClass(this.classes))}"`; 571 } 572 markup += ">"; 573 for (let i = 0; i < this.children.length; i++) { 574 markup += this.children[i].toMarkup(); 575 } 576 markup += "</a>"; 577 return markup 578 } 579 } 580 581 /* 582 * This node represents an image embed (<img>) element. 583 */ 584 class Img { 585 constructor(src, alt, style) { 586 this.alt = alt; 587 this.src = src; 588 this.classes = ["mord"]; 589 this.style = style; 590 } 591 592 hasClass(className) { 593 return this.classes.includes(className); 594 } 595 596 toNode() { 597 const node = document.createElement("img"); 598 node.src = this.src; 599 node.alt = this.alt; 600 node.className = "mord"; 601 602 // Apply inline styles 603 for (const style in this.style) { 604 if (Object.prototype.hasOwnProperty.call(this.style, style )) { 605 node.style[style] = this.style[style]; 606 } 607 } 608 609 return node; 610 } 611 612 toMarkup() { 613 let markup = `<img src='$this.src}' alt='$this.alt}'`; 614 615 // Add the styles, after hyphenation 616 let styles = ""; 617 for (const style in this.style) { 618 if (Object.prototype.hasOwnProperty.call(this.style, style )) { 619 styles += `$utils.hyphenate(style)}:$this.style[style]};`; 620 } 621 } 622 if (styles) { 623 markup += ` style="$utils.escape(styles)}"`; 624 } 625 626 markup += ">"; 627 return markup; 628 } 629 } 630 631 // 632 /** 633 * These objects store data about MathML nodes. 634 * The `toNode` and `toMarkup` functions create namespaced DOM nodes and 635 * HTML text markup respectively. 636 */ 637 638 639 function newDocumentFragment(children) { 640 return new DocumentFragment(children); 641 } 642 643 /** 644 * This node represents a general purpose MathML node of any type, 645 * for example, `"mo"` or `"mspace"`, corresponding to `<mo>` and 646 * `<mspace>` tags). 647 */ 648 class MathNode { 649 constructor(type, children, classes, style) { 650 this.type = type; 651 this.attributes = {}; 652 this.children = children || []; 653 this.classes = classes || []; 654 this.style = style || {}; // Used for <mstyle> elements 655 this.label = ""; 656 } 657 658 /** 659 * Sets an attribute on a MathML node. MathML depends on attributes to convey a 660 * semantic content, so this is used heavily. 661 */ 662 setAttribute(name, value) { 663 this.attributes[name] = value; 664 } 665 666 /** 667 * Gets an attribute on a MathML node. 668 */ 669 getAttribute(name) { 670 return this.attributes[name]; 671 } 672 673 setLabel(value) { 674 this.label = value; 675 } 676 677 /** 678 * Converts the math node into a MathML-namespaced DOM element. 679 */ 680 toNode() { 681 const node = document.createElementNS("http://www.w3.org/1998/Math/MathML", this.type); 682 683 for (const attr in this.attributes) { 684 if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { 685 node.setAttribute(attr, this.attributes[attr]); 686 } 687 } 688 689 if (this.classes.length > 0) { 690 node.className = createClass(this.classes); 691 } 692 693 // Apply inline styles 694 for (const style in this.style) { 695 if (Object.prototype.hasOwnProperty.call(this.style, style )) { 696 node.style[style] = this.style[style]; 697 } 698 } 699 700 for (let i = 0; i < this.children.length; i++) { 701 node.appendChild(this.children[i].toNode()); 702 } 703 704 return node; 705 } 706 707 /** 708 * Converts the math node into an HTML markup string. 709 */ 710 toMarkup() { 711 let markup = "<" + this.type; 712 713 // Add the attributes 714 for (const attr in this.attributes) { 715 if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { 716 markup += " " + attr + '="'; 717 markup += utils.escape(this.attributes[attr]); 718 markup += '"'; 719 } 720 } 721 722 if (this.classes.length > 0) { 723 markup += ` class="$utils.escape(createClass(this.classes))}"`; 724 } 725 726 let styles = ""; 727 728 // Add the styles, after hyphenation 729 for (const style in this.style) { 730 if (Object.prototype.hasOwnProperty.call(this.style, style )) { 731 styles += `$utils.hyphenate(style)}:$this.style[style]};`; 732 } 733 } 734 735 if (styles) { 736 markup += ` style="$styles}"`; 737 } 738 739 markup += ">"; 740 741 for (let i = 0; i < this.children.length; i++) { 742 markup += this.children[i].toMarkup(); 743 } 744 745 markup += "</" + this.type + ">"; 746 747 return markup; 748 } 749 750 /** 751 * Converts the math node into a string, similar to innerText, but escaped. 752 */ 753 toText() { 754 return this.children.map((child) => child.toText()).join(""); 755 } 756 } 757 758 /** 759 * This node represents a piece of text. 760 */ 761 class TextNode { 762 constructor(text) { 763 this.text = text; 764 } 765 766 /** 767 * Converts the text node into a DOM text node. 768 */ 769 toNode() { 770 return document.createTextNode(this.text); 771 } 772 773 /** 774 * Converts the text node into escaped HTML markup 775 * (representing the text itself). 776 */ 777 toMarkup() { 778 return utils.escape(this.toText()); 779 } 780 781 /** 782 * Converts the text node into a string 783 * (representing the text itself). 784 */ 785 toText() { 786 return this.text; 787 } 788 } 789 790 // Do not make an <mrow> the only child of a <mstyle>. 791 // An <mstyle> acts as its own implicit <mrow>. 792 const wrapWithMstyle = expression => { 793 let node; 794 if (expression.length === 1 && expression[0].type === "mrow") { 795 node = expression.pop(); 796 node.type = "mstyle"; 797 } else { 798 node = new MathNode("mstyle", expression); 799 } 800 return node 801 }; 802 803 var mathMLTree = { 804 MathNode, 805 TextNode, 806 newDocumentFragment 807 }; 808 809 /** 810 * This file provides support for building horizontal stretchy elements. 811 */ 812 813 814 // TODO: Remove when Chromium stretches \widetilde & \widehat 815 const estimatedWidth = node => { 816 let width = 0; 817 if (node.body) { 818 for (const item of node.body) { 819 width += estimatedWidth(item); 820 } 821 } else if (node.type === "supsub") { 822 width += estimatedWidth(node.base); 823 if (node.sub) { width += 0.7 * estimatedWidth(node.sub); } 824 if (node.sup) { width += 0.7 * estimatedWidth(node.sup); } 825 } else if (node.type === "mathord" || node.type === "textord") { 826 for (const ch of node.text.split('')) { 827 const codePoint = ch.codePointAt(0); 828 if ((0x60 < codePoint && codePoint < 0x7B) || (0x03B0 < codePoint && codePoint < 0x3CA)) { 829 width += 0.56; // lower case latin or greek. Use advance width of letter n 830 } else if (0x2F < codePoint && codePoint < 0x3A) { 831 width += 0.50; // numerals. 832 } else { 833 width += 0.92; // advance width of letter M 834 } 835 } 836 } else { 837 width += 1.0; 838 } 839 return width 840 }; 841 842 const stretchyCodePoint = { 843 widehat: "^", 844 widecheck: "ˇ", 845 widetilde: "~", 846 wideparen: "⏜", // \u23dc 847 utilde: "~", 848 overleftarrow: "\u2190", 849 underleftarrow: "\u2190", 850 xleftarrow: "\u2190", 851 overrightarrow: "\u2192", 852 underrightarrow: "\u2192", 853 xrightarrow: "\u2192", 854 underbrace: "\u23df", 855 overbrace: "\u23de", 856 overgroup: "\u23e0", 857 overparen: "⏜", 858 undergroup: "\u23e1", 859 underparen: "\u23dd", 860 overleftrightarrow: "\u2194", 861 underleftrightarrow: "\u2194", 862 xleftrightarrow: "\u2194", 863 Overrightarrow: "\u21d2", 864 xRightarrow: "\u21d2", 865 overleftharpoon: "\u21bc", 866 xleftharpoonup: "\u21bc", 867 overrightharpoon: "\u21c0", 868 xrightharpoonup: "\u21c0", 869 xLeftarrow: "\u21d0", 870 xLeftrightarrow: "\u21d4", 871 xhookleftarrow: "\u21a9", 872 xhookrightarrow: "\u21aa", 873 xmapsto: "\u21a6", 874 xrightharpoondown: "\u21c1", 875 xleftharpoondown: "\u21bd", 876 xtwoheadleftarrow: "\u219e", 877 xtwoheadrightarrow: "\u21a0", 878 xlongequal: "=", 879 xrightleftarrows: "\u21c4", 880 yields: "\u2192", 881 yieldsLeft: "\u2190", 882 mesomerism: "\u2194", 883 longrightharpoonup: "\u21c0", 884 longleftharpoondown: "\u21bd", 885 eqrightharpoonup: "\u21c0", 886 eqleftharpoondown: "\u21bd", 887 "\\cdrightarrow": "\u2192", 888 "\\cdleftarrow": "\u2190", 889 "\\cdlongequal": "=" 890 }; 891 892 const mathMLnode = function(label) { 893 const child = new mathMLTree.TextNode(stretchyCodePoint[label.slice(1)]); 894 const node = new mathMLTree.MathNode("mo", [child]); 895 node.setAttribute("stretchy", "true"); 896 return node 897 }; 898 899 const crookedWides = ["\\widetilde", "\\widehat", "\\widecheck", "\\utilde"]; 900 901 // TODO: Remove when Chromium stretches \widetilde & \widehat 902 const accentNode = (group) => { 903 const mo = mathMLnode(group.label); 904 if (crookedWides.includes(group.label)) { 905 const width = estimatedWidth(group.base); 906 if (1 < width && width < 1.6) { 907 mo.classes.push("tml-crooked-2"); 908 } else if (1.6 <= width && width < 2.5) { 909 mo.classes.push("tml-crooked-3"); 910 } else if (2.5 <= width) { 911 mo.classes.push("tml-crooked-4"); 912 } 913 } 914 return mo 915 }; 916 917 var stretchy = { 918 mathMLnode, 919 accentNode 920 }; 921 922 /** 923 * This file holds a list of all no-argument functions and single-character 924 * symbols (like 'a' or ';'). 925 * 926 * For each of the symbols, there are two properties they can have: 927 * - group (required): the ParseNode group type the symbol should have (i.e. 928 "textord", "mathord", etc). 929 * - replace: the character that this symbol or function should be 930 * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi 931 * character in the main font). 932 * 933 * The outermost map in the table indicates what mode the symbols should be 934 * accepted in (e.g. "math" or "text"). 935 */ 936 937 // Some of these have a "-token" suffix since these are also used as `ParseNode` 938 // types for raw text tokens, and we want to avoid conflicts with higher-level 939 // `ParseNode` types. These `ParseNode`s are constructed within `Parser` by 940 // looking up the `symbols` map. 941 const ATOMS = { 942 bin: 1, 943 close: 1, 944 inner: 1, 945 open: 1, 946 punct: 1, 947 rel: 1 948 }; 949 const NON_ATOMS = { 950 "accent-token": 1, 951 mathord: 1, 952 "op-token": 1, 953 spacing: 1, 954 textord: 1 955 }; 956 957 const symbols = { 958 math: {}, 959 text: {} 960 }; 961 962 /** `acceptUnicodeChar = true` is only applicable if `replace` is set. */ 963 function defineSymbol(mode, group, replace, name, acceptUnicodeChar) { 964 symbols[mode][name] = { group, replace }; 965 966 if (acceptUnicodeChar && replace) { 967 symbols[mode][replace] = symbols[mode][name]; 968 } 969 } 970 971 // Some abbreviations for commonly used strings. 972 // This helps minify the code, and also spotting typos using jshint. 973 974 // modes: 975 const math = "math"; 976 const temml_text = "text"; 977 978 // groups: 979 const accent = "accent-token"; 980 const bin = "bin"; 981 const temml_close = "close"; 982 const inner = "inner"; 983 const mathord = "mathord"; 984 const op = "op-token"; 985 const temml_open = "open"; 986 const punct = "punct"; 987 const rel = "rel"; 988 const spacing = "spacing"; 989 const textord = "textord"; 990 991 // Now comes the symbol table 992 993 // Relation Symbols 994 defineSymbol(math, rel, "\u2261", "\\equiv", true); 995 defineSymbol(math, rel, "\u227a", "\\prec", true); 996 defineSymbol(math, rel, "\u227b", "\\succ", true); 997 defineSymbol(math, rel, "\u223c", "\\sim", true); 998 defineSymbol(math, rel, "\u27c2", "\\perp", true); 999 defineSymbol(math, rel, "\u2aaf", "\\preceq", true); 1000 defineSymbol(math, rel, "\u2ab0", "\\succeq", true); 1001 defineSymbol(math, rel, "\u2243", "\\simeq", true); 1002 defineSymbol(math, rel, "\u224c", "\\backcong", true); 1003 defineSymbol(math, rel, "|", "\\mid", true); 1004 defineSymbol(math, rel, "\u226a", "\\ll", true); 1005 defineSymbol(math, rel, "\u226b", "\\gg", true); 1006 defineSymbol(math, rel, "\u224d", "\\asymp", true); 1007 defineSymbol(math, rel, "\u2225", "\\parallel"); 1008 defineSymbol(math, rel, "\u2323", "\\smile", true); 1009 defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true); 1010 defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true); 1011 defineSymbol(math, rel, "\u2250", "\\doteq", true); 1012 defineSymbol(math, rel, "\u2322", "\\frown", true); 1013 defineSymbol(math, rel, "\u220b", "\\ni", true); 1014 defineSymbol(math, rel, "\u220c", "\\notni", true); 1015 defineSymbol(math, rel, "\u221d", "\\propto", true); 1016 defineSymbol(math, rel, "\u22a2", "\\vdash", true); 1017 defineSymbol(math, rel, "\u22a3", "\\dashv", true); 1018 defineSymbol(math, rel, "\u220b", "\\owns"); 1019 defineSymbol(math, rel, "\u2258", "\\arceq", true); 1020 defineSymbol(math, rel, "\u2259", "\\wedgeq", true); 1021 defineSymbol(math, rel, "\u225a", "\\veeeq", true); 1022 defineSymbol(math, rel, "\u225b", "\\stareq", true); 1023 defineSymbol(math, rel, "\u225d", "\\eqdef", true); 1024 defineSymbol(math, rel, "\u225e", "\\measeq", true); 1025 defineSymbol(math, rel, "\u225f", "\\questeq", true); 1026 defineSymbol(math, rel, "\u2260", "\\ne", true); 1027 defineSymbol(math, rel, "\u2260", "\\neq"); 1028 // unicodemath 1029 defineSymbol(math, rel, "\u2a75", "\\eqeq", true); 1030 defineSymbol(math, rel, "\u2a76", "\\eqeqeq", true); 1031 // mathtools.sty 1032 defineSymbol(math, rel, "\u2237", "\\dblcolon", true); 1033 defineSymbol(math, rel, "\u2254", "\\coloneqq", true); 1034 defineSymbol(math, rel, "\u2255", "\\eqqcolon", true); 1035 defineSymbol(math, rel, "\u2239", "\\eqcolon", true); 1036 defineSymbol(math, rel, "\u2A74", "\\Coloneqq", true); 1037 1038 // Punctuation 1039 defineSymbol(math, punct, "\u002e", "\\ldotp"); 1040 defineSymbol(math, punct, "\u00b7", "\\cdotp"); 1041 1042 // Misc Symbols 1043 defineSymbol(math, textord, "\u0023", "\\#"); 1044 defineSymbol(temml_text, textord, "\u0023", "\\#"); 1045 defineSymbol(math, textord, "\u0026", "\\&"); 1046 defineSymbol(temml_text, textord, "\u0026", "\\&"); 1047 defineSymbol(math, textord, "\u2135", "\\aleph", true); 1048 defineSymbol(math, textord, "\u2200", "\\forall", true); 1049 defineSymbol(math, textord, "\u210f", "\\hbar", true); 1050 defineSymbol(math, textord, "\u2203", "\\exists", true); 1051 // ∇ is actually a unary operator, not binary. But this works. 1052 defineSymbol(math, bin, "\u2207", "\\nabla", true); 1053 defineSymbol(math, textord, "\u266d", "\\flat", true); 1054 defineSymbol(math, textord, "\u2113", "\\ell", true); 1055 defineSymbol(math, textord, "\u266e", "\\natural", true); 1056 defineSymbol(math, textord, "Å", "\\Angstrom", true); 1057 defineSymbol(temml_text, textord, "Å", "\\Angstrom", true); 1058 defineSymbol(math, textord, "\u2663", "\\clubsuit", true); 1059 defineSymbol(math, textord, "\u2667", "\\varclubsuit", true); 1060 defineSymbol(math, textord, "\u2118", "\\wp", true); 1061 defineSymbol(math, textord, "\u266f", "\\sharp", true); 1062 defineSymbol(math, textord, "\u2662", "\\diamondsuit", true); 1063 defineSymbol(math, textord, "\u2666", "\\vardiamondsuit", true); 1064 defineSymbol(math, textord, "\u211c", "\\Re", true); 1065 defineSymbol(math, textord, "\u2661", "\\heartsuit", true); 1066 defineSymbol(math, textord, "\u2665", "\\varheartsuit", true); 1067 defineSymbol(math, textord, "\u2111", "\\Im", true); 1068 defineSymbol(math, textord, "\u2660", "\\spadesuit", true); 1069 defineSymbol(math, textord, "\u2664", "\\varspadesuit", true); 1070 defineSymbol(math, textord, "\u2640", "\\female", true); 1071 defineSymbol(math, textord, "\u2642", "\\male", true); 1072 defineSymbol(math, textord, "\u00a7", "\\S", true); 1073 defineSymbol(temml_text, textord, "\u00a7", "\\S"); 1074 defineSymbol(math, textord, "\u00b6", "\\P", true); 1075 defineSymbol(temml_text, textord, "\u00b6", "\\P"); 1076 defineSymbol(temml_text, textord, "\u263a", "\\smiley", true); 1077 defineSymbol(math, textord, "\u263a", "\\smiley", true); 1078 1079 // Math and Text 1080 defineSymbol(math, textord, "\u2020", "\\dag"); 1081 defineSymbol(temml_text, textord, "\u2020", "\\dag"); 1082 defineSymbol(temml_text, textord, "\u2020", "\\textdagger"); 1083 defineSymbol(math, textord, "\u2021", "\\ddag"); 1084 defineSymbol(temml_text, textord, "\u2021", "\\ddag"); 1085 defineSymbol(temml_text, textord, "\u2021", "\\textdaggerdbl"); 1086 1087 // Large Delimiters 1088 defineSymbol(math, temml_close, "\u23b1", "\\rmoustache", true); 1089 defineSymbol(math, temml_open, "\u23b0", "\\lmoustache", true); 1090 defineSymbol(math, temml_close, "\u27ef", "\\rgroup", true); 1091 defineSymbol(math, temml_open, "\u27ee", "\\lgroup", true); 1092 1093 // Binary Operators 1094 defineSymbol(math, bin, "\u2213", "\\mp", true); 1095 defineSymbol(math, bin, "\u2296", "\\ominus", true); 1096 defineSymbol(math, bin, "\u228e", "\\uplus", true); 1097 defineSymbol(math, bin, "\u2293", "\\sqcap", true); 1098 defineSymbol(math, bin, "\u2217", "\\ast"); 1099 defineSymbol(math, bin, "\u2294", "\\sqcup", true); 1100 defineSymbol(math, bin, "\u25ef", "\\bigcirc", true); 1101 defineSymbol(math, bin, "\u2219", "\\bullet", true); 1102 defineSymbol(math, bin, "\u2021", "\\ddagger"); 1103 defineSymbol(math, bin, "\u2240", "\\wr", true); 1104 defineSymbol(math, bin, "\u2a3f", "\\amalg"); 1105 defineSymbol(math, bin, "\u0026", "\\And"); // from amsmath 1106 defineSymbol(math, bin, "\u2AFD", "\\sslash", true); // from stmaryrd 1107 1108 // Arrow Symbols 1109 defineSymbol(math, rel, "\u27f5", "\\longleftarrow", true); 1110 defineSymbol(math, rel, "\u21d0", "\\Leftarrow", true); 1111 defineSymbol(math, rel, "\u27f8", "\\Longleftarrow", true); 1112 defineSymbol(math, rel, "\u27f6", "\\longrightarrow", true); 1113 defineSymbol(math, rel, "\u21d2", "\\Rightarrow", true); 1114 defineSymbol(math, rel, "\u27f9", "\\Longrightarrow", true); 1115 defineSymbol(math, rel, "\u2194", "\\leftrightarrow", true); 1116 defineSymbol(math, rel, "\u27f7", "\\longleftrightarrow", true); 1117 defineSymbol(math, rel, "\u21d4", "\\Leftrightarrow", true); 1118 defineSymbol(math, rel, "\u27fa", "\\Longleftrightarrow", true); 1119 defineSymbol(math, rel, "\u21a4", "\\mapsfrom", true); 1120 defineSymbol(math, rel, "\u21a6", "\\mapsto", true); 1121 defineSymbol(math, rel, "\u27fc", "\\longmapsto", true); 1122 defineSymbol(math, rel, "\u2197", "\\nearrow", true); 1123 defineSymbol(math, rel, "\u21a9", "\\hookleftarrow", true); 1124 defineSymbol(math, rel, "\u21aa", "\\hookrightarrow", true); 1125 defineSymbol(math, rel, "\u2198", "\\searrow", true); 1126 defineSymbol(math, rel, "\u21bc", "\\leftharpoonup", true); 1127 defineSymbol(math, rel, "\u21c0", "\\rightharpoonup", true); 1128 defineSymbol(math, rel, "\u2199", "\\swarrow", true); 1129 defineSymbol(math, rel, "\u21bd", "\\leftharpoondown", true); 1130 defineSymbol(math, rel, "\u21c1", "\\rightharpoondown", true); 1131 defineSymbol(math, rel, "\u2196", "\\nwarrow", true); 1132 defineSymbol(math, rel, "\u21cc", "\\rightleftharpoons", true); 1133 defineSymbol(math, mathord, "\u21af", "\\lightning", true); 1134 defineSymbol(math, mathord, "\u220E", "\\QED", true); 1135 defineSymbol(math, mathord, "\u2030", "\\permil", true); 1136 defineSymbol(temml_text, textord, "\u2030", "\\permil"); 1137 defineSymbol(math, mathord, "\u2609", "\\astrosun", true); 1138 defineSymbol(math, mathord, "\u263c", "\\sun", true); 1139 defineSymbol(math, mathord, "\u263e", "\\leftmoon", true); 1140 defineSymbol(math, mathord, "\u263d", "\\rightmoon", true); 1141 defineSymbol(math, mathord, "\u2295", "\\Earth"); 1142 1143 // AMS Negated Binary Relations 1144 defineSymbol(math, rel, "\u226e", "\\nless", true); 1145 // Symbol names preceeded by "@" each have a corresponding macro. 1146 defineSymbol(math, rel, "\u2a87", "\\lneq", true); 1147 defineSymbol(math, rel, "\u2268", "\\lneqq", true); 1148 defineSymbol(math, rel, "\u2268\ufe00", "\\lvertneqq"); 1149 defineSymbol(math, rel, "\u22e6", "\\lnsim", true); 1150 defineSymbol(math, rel, "\u2a89", "\\lnapprox", true); 1151 defineSymbol(math, rel, "\u2280", "\\nprec", true); 1152 // unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym. 1153 defineSymbol(math, rel, "\u22e0", "\\npreceq", true); 1154 defineSymbol(math, rel, "\u22e8", "\\precnsim", true); 1155 defineSymbol(math, rel, "\u2ab9", "\\precnapprox", true); 1156 defineSymbol(math, rel, "\u2241", "\\nsim", true); 1157 defineSymbol(math, rel, "\u2224", "\\nmid", true); 1158 defineSymbol(math, rel, "\u2224", "\\nshortmid"); 1159 defineSymbol(math, rel, "\u22ac", "\\nvdash", true); 1160 defineSymbol(math, rel, "\u22ad", "\\nvDash", true); 1161 defineSymbol(math, rel, "\u22ea", "\\ntriangleleft"); 1162 defineSymbol(math, rel, "\u22ec", "\\ntrianglelefteq", true); 1163 defineSymbol(math, rel, "\u2284", "\\nsubset", true); 1164 defineSymbol(math, rel, "\u2285", "\\nsupset", true); 1165 defineSymbol(math, rel, "\u228a", "\\subsetneq", true); 1166 defineSymbol(math, rel, "\u228a\ufe00", "\\varsubsetneq"); 1167 defineSymbol(math, rel, "\u2acb", "\\subsetneqq", true); 1168 defineSymbol(math, rel, "\u2acb\ufe00", "\\varsubsetneqq"); 1169 defineSymbol(math, rel, "\u226f", "\\ngtr", true); 1170 defineSymbol(math, rel, "\u2a88", "\\gneq", true); 1171 defineSymbol(math, rel, "\u2269", "\\gneqq", true); 1172 defineSymbol(math, rel, "\u2269\ufe00", "\\gvertneqq"); 1173 defineSymbol(math, rel, "\u22e7", "\\gnsim", true); 1174 defineSymbol(math, rel, "\u2a8a", "\\gnapprox", true); 1175 defineSymbol(math, rel, "\u2281", "\\nsucc", true); 1176 // unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym. 1177 defineSymbol(math, rel, "\u22e1", "\\nsucceq", true); 1178 defineSymbol(math, rel, "\u22e9", "\\succnsim", true); 1179 defineSymbol(math, rel, "\u2aba", "\\succnapprox", true); 1180 // unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym. 1181 defineSymbol(math, rel, "\u2246", "\\ncong", true); 1182 defineSymbol(math, rel, "\u2226", "\\nparallel", true); 1183 defineSymbol(math, rel, "\u2226", "\\nshortparallel"); 1184 defineSymbol(math, rel, "\u22af", "\\nVDash", true); 1185 defineSymbol(math, rel, "\u22eb", "\\ntriangleright"); 1186 defineSymbol(math, rel, "\u22ed", "\\ntrianglerighteq", true); 1187 defineSymbol(math, rel, "\u228b", "\\supsetneq", true); 1188 defineSymbol(math, rel, "\u228b", "\\varsupsetneq"); 1189 defineSymbol(math, rel, "\u2acc", "\\supsetneqq", true); 1190 defineSymbol(math, rel, "\u2acc\ufe00", "\\varsupsetneqq"); 1191 defineSymbol(math, rel, "\u22ae", "\\nVdash", true); 1192 defineSymbol(math, rel, "\u2ab5", "\\precneqq", true); 1193 defineSymbol(math, rel, "\u2ab6", "\\succneqq", true); 1194 defineSymbol(math, bin, "\u22b4", "\\unlhd"); 1195 defineSymbol(math, bin, "\u22b5", "\\unrhd"); 1196 1197 // AMS Negated Arrows 1198 defineSymbol(math, rel, "\u219a", "\\nleftarrow", true); 1199 defineSymbol(math, rel, "\u219b", "\\nrightarrow", true); 1200 defineSymbol(math, rel, "\u21cd", "\\nLeftarrow", true); 1201 defineSymbol(math, rel, "\u21cf", "\\nRightarrow", true); 1202 defineSymbol(math, rel, "\u21ae", "\\nleftrightarrow", true); 1203 defineSymbol(math, rel, "\u21ce", "\\nLeftrightarrow", true); 1204 1205 // AMS Misc 1206 defineSymbol(math, rel, "\u25b3", "\\vartriangle"); 1207 defineSymbol(math, textord, "\u210f", "\\hslash"); 1208 defineSymbol(math, textord, "\u25bd", "\\triangledown"); 1209 defineSymbol(math, textord, "\u25ca", "\\lozenge"); 1210 defineSymbol(math, textord, "\u24c8", "\\circledS"); 1211 defineSymbol(math, textord, "\u00ae", "\\circledR", true); 1212 defineSymbol(temml_text, textord, "\u00ae", "\\circledR"); 1213 defineSymbol(temml_text, textord, "\u00ae", "\\textregistered"); 1214 defineSymbol(math, textord, "\u2221", "\\measuredangle", true); 1215 defineSymbol(math, textord, "\u2204", "\\nexists"); 1216 defineSymbol(math, textord, "\u2127", "\\mho"); 1217 defineSymbol(math, textord, "\u2132", "\\Finv", true); 1218 defineSymbol(math, textord, "\u2141", "\\Game", true); 1219 defineSymbol(math, textord, "\u2035", "\\backprime"); 1220 defineSymbol(math, textord, "\u2036", "\\backdprime"); 1221 defineSymbol(math, textord, "\u2037", "\\backtrprime"); 1222 defineSymbol(math, textord, "\u25b2", "\\blacktriangle"); 1223 defineSymbol(math, textord, "\u25bc", "\\blacktriangledown"); 1224 defineSymbol(math, textord, "\u25a0", "\\blacksquare"); 1225 defineSymbol(math, textord, "\u29eb", "\\blacklozenge"); 1226 defineSymbol(math, textord, "\u2605", "\\bigstar"); 1227 defineSymbol(math, textord, "\u2222", "\\sphericalangle", true); 1228 defineSymbol(math, textord, "\u2201", "\\complement", true); 1229 // unicode-math maps U+F0 to \matheth. We map to AMS function \eth 1230 defineSymbol(math, textord, "\u00f0", "\\eth", true); 1231 defineSymbol(temml_text, textord, "\u00f0", "\u00f0"); 1232 defineSymbol(math, textord, "\u2571", "\\diagup"); 1233 defineSymbol(math, textord, "\u2572", "\\diagdown"); 1234 defineSymbol(math, textord, "\u25a1", "\\square"); 1235 defineSymbol(math, textord, "\u25a1", "\\Box"); 1236 defineSymbol(math, textord, "\u25ca", "\\Diamond"); 1237 // unicode-math maps U+A5 to \mathyen. We map to AMS function \yen 1238 defineSymbol(math, textord, "\u00a5", "\\yen", true); 1239 defineSymbol(temml_text, textord, "\u00a5", "\\yen", true); 1240 defineSymbol(math, textord, "\u2713", "\\checkmark", true); 1241 defineSymbol(temml_text, textord, "\u2713", "\\checkmark"); 1242 defineSymbol(math, textord, "\u2717", "\\ballotx", true); 1243 defineSymbol(temml_text, textord, "\u2717", "\\ballotx"); 1244 defineSymbol(temml_text, textord, "\u2022", "\\textbullet"); 1245 1246 // AMS Hebrew 1247 defineSymbol(math, textord, "\u2136", "\\beth", true); 1248 defineSymbol(math, textord, "\u2138", "\\daleth", true); 1249 defineSymbol(math, textord, "\u2137", "\\gimel", true); 1250 1251 // AMS Greek 1252 defineSymbol(math, textord, "\u03dd", "\\digamma", true); 1253 defineSymbol(math, textord, "\u03f0", "\\varkappa"); 1254 1255 // AMS Delimiters 1256 defineSymbol(math, temml_open, "\u231C", "\\ulcorner", true); 1257 defineSymbol(math, temml_close, "\u231D", "\\urcorner", true); 1258 defineSymbol(math, temml_open, "\u231E", "\\llcorner", true); 1259 defineSymbol(math, temml_close, "\u231F", "\\lrcorner", true); 1260 1261 // AMS Binary Relations 1262 defineSymbol(math, rel, "\u2266", "\\leqq", true); 1263 defineSymbol(math, rel, "\u2a7d", "\\leqslant", true); 1264 defineSymbol(math, rel, "\u2a95", "\\eqslantless", true); 1265 defineSymbol(math, rel, "\u2272", "\\lesssim", true); 1266 defineSymbol(math, rel, "\u2a85", "\\lessapprox", true); 1267 defineSymbol(math, rel, "\u224a", "\\approxeq", true); 1268 defineSymbol(math, bin, "\u22d6", "\\lessdot"); 1269 defineSymbol(math, rel, "\u22d8", "\\lll", true); 1270 defineSymbol(math, rel, "\u2276", "\\lessgtr", true); 1271 defineSymbol(math, rel, "\u22da", "\\lesseqgtr", true); 1272 defineSymbol(math, rel, "\u2a8b", "\\lesseqqgtr", true); 1273 defineSymbol(math, rel, "\u2251", "\\doteqdot"); 1274 defineSymbol(math, rel, "\u2253", "\\risingdotseq", true); 1275 defineSymbol(math, rel, "\u2252", "\\fallingdotseq", true); 1276 defineSymbol(math, rel, "\u223d", "\\backsim", true); 1277 defineSymbol(math, rel, "\u22cd", "\\backsimeq", true); 1278 defineSymbol(math, rel, "\u2ac5", "\\subseteqq", true); 1279 defineSymbol(math, rel, "\u22d0", "\\Subset", true); 1280 defineSymbol(math, rel, "\u228f", "\\sqsubset", true); 1281 defineSymbol(math, rel, "\u227c", "\\preccurlyeq", true); 1282 defineSymbol(math, rel, "\u22de", "\\curlyeqprec", true); 1283 defineSymbol(math, rel, "\u227e", "\\precsim", true); 1284 defineSymbol(math, rel, "\u2ab7", "\\precapprox", true); 1285 defineSymbol(math, rel, "\u22b2", "\\vartriangleleft"); 1286 defineSymbol(math, rel, "\u22b4", "\\trianglelefteq"); 1287 defineSymbol(math, rel, "\u22a8", "\\vDash", true); 1288 defineSymbol(math, rel, "\u22ab", "\\VDash", true); 1289 defineSymbol(math, rel, "\u22aa", "\\Vvdash", true); 1290 defineSymbol(math, rel, "\u2323", "\\smallsmile"); 1291 defineSymbol(math, rel, "\u2322", "\\smallfrown"); 1292 defineSymbol(math, rel, "\u224f", "\\bumpeq", true); 1293 defineSymbol(math, rel, "\u224e", "\\Bumpeq", true); 1294 defineSymbol(math, rel, "\u2267", "\\geqq", true); 1295 defineSymbol(math, rel, "\u2a7e", "\\geqslant", true); 1296 defineSymbol(math, rel, "\u2a96", "\\eqslantgtr", true); 1297 defineSymbol(math, rel, "\u2273", "\\gtrsim", true); 1298 defineSymbol(math, rel, "\u2a86", "\\gtrapprox", true); 1299 defineSymbol(math, bin, "\u22d7", "\\gtrdot"); 1300 defineSymbol(math, rel, "\u22d9", "\\ggg", true); 1301 defineSymbol(math, rel, "\u2277", "\\gtrless", true); 1302 defineSymbol(math, rel, "\u22db", "\\gtreqless", true); 1303 defineSymbol(math, rel, "\u2a8c", "\\gtreqqless", true); 1304 defineSymbol(math, rel, "\u2256", "\\eqcirc", true); 1305 defineSymbol(math, rel, "\u2257", "\\circeq", true); 1306 defineSymbol(math, rel, "\u225c", "\\triangleq", true); 1307 defineSymbol(math, rel, "\u223c", "\\thicksim"); 1308 defineSymbol(math, rel, "\u2248", "\\thickapprox"); 1309 defineSymbol(math, rel, "\u2ac6", "\\supseteqq", true); 1310 defineSymbol(math, rel, "\u22d1", "\\Supset", true); 1311 defineSymbol(math, rel, "\u2290", "\\sqsupset", true); 1312 defineSymbol(math, rel, "\u227d", "\\succcurlyeq", true); 1313 defineSymbol(math, rel, "\u22df", "\\curlyeqsucc", true); 1314 defineSymbol(math, rel, "\u227f", "\\succsim", true); 1315 defineSymbol(math, rel, "\u2ab8", "\\succapprox", true); 1316 defineSymbol(math, rel, "\u22b3", "\\vartriangleright"); 1317 defineSymbol(math, rel, "\u22b5", "\\trianglerighteq"); 1318 defineSymbol(math, rel, "\u22a9", "\\Vdash", true); 1319 defineSymbol(math, rel, "\u2223", "\\shortmid"); 1320 defineSymbol(math, rel, "\u2225", "\\shortparallel"); 1321 defineSymbol(math, rel, "\u226c", "\\between", true); 1322 defineSymbol(math, rel, "\u22d4", "\\pitchfork", true); 1323 defineSymbol(math, rel, "\u221d", "\\varpropto"); 1324 defineSymbol(math, rel, "\u25c0", "\\blacktriangleleft"); 1325 // unicode-math says that \therefore is a mathord atom. 1326 // We kept the amssymb atom type, which is rel. 1327 defineSymbol(math, rel, "\u2234", "\\therefore", true); 1328 defineSymbol(math, rel, "\u220d", "\\backepsilon"); 1329 defineSymbol(math, rel, "\u25b6", "\\blacktriangleright"); 1330 // unicode-math says that \because is a mathord atom. 1331 // We kept the amssymb atom type, which is rel. 1332 defineSymbol(math, rel, "\u2235", "\\because", true); 1333 defineSymbol(math, rel, "\u22d8", "\\llless"); 1334 defineSymbol(math, rel, "\u22d9", "\\gggtr"); 1335 defineSymbol(math, bin, "\u22b2", "\\lhd"); 1336 defineSymbol(math, bin, "\u22b3", "\\rhd"); 1337 defineSymbol(math, rel, "\u2242", "\\eqsim", true); 1338 defineSymbol(math, rel, "\u2251", "\\Doteq", true); 1339 defineSymbol(math, rel, "\u297d", "\\strictif", true); 1340 defineSymbol(math, rel, "\u297c", "\\strictfi", true); 1341 1342 // AMS Binary Operators 1343 defineSymbol(math, bin, "\u2214", "\\dotplus", true); 1344 defineSymbol(math, bin, "\u2216", "\\smallsetminus"); 1345 defineSymbol(math, bin, "\u22d2", "\\Cap", true); 1346 defineSymbol(math, bin, "\u22d3", "\\Cup", true); 1347 defineSymbol(math, bin, "\u2a5e", "\\doublebarwedge", true); 1348 defineSymbol(math, bin, "\u229f", "\\boxminus", true); 1349 defineSymbol(math, bin, "\u229e", "\\boxplus", true); 1350 defineSymbol(math, bin, "\u29C4", "\\boxslash", true); 1351 defineSymbol(math, bin, "\u22c7", "\\divideontimes", true); 1352 defineSymbol(math, bin, "\u22c9", "\\ltimes", true); 1353 defineSymbol(math, bin, "\u22ca", "\\rtimes", true); 1354 defineSymbol(math, bin, "\u22cb", "\\leftthreetimes", true); 1355 defineSymbol(math, bin, "\u22cc", "\\rightthreetimes", true); 1356 defineSymbol(math, bin, "\u22cf", "\\curlywedge", true); 1357 defineSymbol(math, bin, "\u22ce", "\\curlyvee", true); 1358 defineSymbol(math, bin, "\u229d", "\\circleddash", true); 1359 defineSymbol(math, bin, "\u229b", "\\circledast", true); 1360 defineSymbol(math, bin, "\u22ba", "\\intercal", true); 1361 defineSymbol(math, bin, "\u22d2", "\\doublecap"); 1362 defineSymbol(math, bin, "\u22d3", "\\doublecup"); 1363 defineSymbol(math, bin, "\u22a0", "\\boxtimes", true); 1364 defineSymbol(math, bin, "\u22c8", "\\bowtie", true); 1365 defineSymbol(math, bin, "\u22c8", "\\Join"); 1366 defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true); 1367 defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true); 1368 defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true); 1369 1370 // stix Binary Operators 1371 defineSymbol(math, bin, "\u2238", "\\dotminus", true); 1372 defineSymbol(math, bin, "\u27D1", "\\wedgedot", true); 1373 defineSymbol(math, bin, "\u27C7", "\\veedot", true); 1374 defineSymbol(math, bin, "\u2A62", "\\doublebarvee", true); 1375 defineSymbol(math, bin, "\u2A63", "\\veedoublebar", true); 1376 defineSymbol(math, bin, "\u2A5F", "\\wedgebar", true); 1377 defineSymbol(math, bin, "\u2A60", "\\wedgedoublebar", true); 1378 defineSymbol(math, bin, "\u2A54", "\\Vee", true); 1379 defineSymbol(math, bin, "\u2A53", "\\Wedge", true); 1380 defineSymbol(math, bin, "\u2A43", "\\barcap", true); 1381 defineSymbol(math, bin, "\u2A42", "\\barcup", true); 1382 defineSymbol(math, bin, "\u2A48", "\\capbarcup", true); 1383 defineSymbol(math, bin, "\u2A40", "\\capdot", true); 1384 defineSymbol(math, bin, "\u2A47", "\\capovercup", true); 1385 defineSymbol(math, bin, "\u2A46", "\\cupovercap", true); 1386 defineSymbol(math, bin, "\u2A4D", "\\closedvarcap", true); 1387 defineSymbol(math, bin, "\u2A4C", "\\closedvarcup", true); 1388 defineSymbol(math, bin, "\u2A2A", "\\minusdot", true); 1389 defineSymbol(math, bin, "\u2A2B", "\\minusfdots", true); 1390 defineSymbol(math, bin, "\u2A2C", "\\minusrdots", true); 1391 defineSymbol(math, bin, "\u22BB", "\\Xor", true); 1392 defineSymbol(math, bin, "\u22BC", "\\Nand", true); 1393 defineSymbol(math, bin, "\u22BD", "\\Nor", true); 1394 defineSymbol(math, bin, "\u22BD", "\\barvee"); 1395 defineSymbol(math, bin, "\u2AF4", "\\interleave", true); 1396 defineSymbol(math, bin, "\u29E2", "\\shuffle", true); 1397 defineSymbol(math, bin, "\u2AF6", "\\threedotcolon", true); 1398 defineSymbol(math, bin, "\u2982", "\\typecolon", true); 1399 defineSymbol(math, bin, "\u223E", "\\invlazys", true); 1400 defineSymbol(math, bin, "\u2A4B", "\\twocaps", true); 1401 defineSymbol(math, bin, "\u2A4A", "\\twocups", true); 1402 defineSymbol(math, bin, "\u2A4E", "\\Sqcap", true); 1403 defineSymbol(math, bin, "\u2A4F", "\\Sqcup", true); 1404 defineSymbol(math, bin, "\u2A56", "\\veeonvee", true); 1405 defineSymbol(math, bin, "\u2A55", "\\wedgeonwedge", true); 1406 defineSymbol(math, bin, "\u29D7", "\\blackhourglass", true); 1407 defineSymbol(math, bin, "\u29C6", "\\boxast", true); 1408 defineSymbol(math, bin, "\u29C8", "\\boxbox", true); 1409 defineSymbol(math, bin, "\u29C7", "\\boxcircle", true); 1410 defineSymbol(math, bin, "\u229C", "\\circledequal", true); 1411 defineSymbol(math, bin, "\u29B7", "\\circledparallel", true); 1412 defineSymbol(math, bin, "\u29B6", "\\circledvert", true); 1413 defineSymbol(math, bin, "\u29B5", "\\circlehbar", true); 1414 defineSymbol(math, bin, "\u27E1", "\\concavediamond", true); 1415 defineSymbol(math, bin, "\u27E2", "\\concavediamondtickleft", true); 1416 defineSymbol(math, bin, "\u27E3", "\\concavediamondtickright", true); 1417 defineSymbol(math, bin, "\u22C4", "\\diamond", true); 1418 defineSymbol(math, bin, "\u29D6", "\\hourglass", true); 1419 defineSymbol(math, bin, "\u27E0", "\\lozengeminus", true); 1420 defineSymbol(math, bin, "\u233D", "\\obar", true); 1421 defineSymbol(math, bin, "\u29B8", "\\obslash", true); 1422 defineSymbol(math, bin, "\u2A38", "\\odiv", true); 1423 defineSymbol(math, bin, "\u29C1", "\\ogreaterthan", true); 1424 defineSymbol(math, bin, "\u29C0", "\\olessthan", true); 1425 defineSymbol(math, bin, "\u29B9", "\\operp", true); 1426 defineSymbol(math, bin, "\u2A37", "\\Otimes", true); 1427 defineSymbol(math, bin, "\u2A36", "\\otimeshat", true); 1428 defineSymbol(math, bin, "\u22C6", "\\star", true); 1429 defineSymbol(math, bin, "\u25B3", "\\triangle", true); 1430 defineSymbol(math, bin, "\u2A3A", "\\triangleminus", true); 1431 defineSymbol(math, bin, "\u2A39", "\\triangleplus", true); 1432 defineSymbol(math, bin, "\u2A3B", "\\triangletimes", true); 1433 defineSymbol(math, bin, "\u27E4", "\\whitesquaretickleft", true); 1434 defineSymbol(math, bin, "\u27E5", "\\whitesquaretickright", true); 1435 defineSymbol(math, bin, "\u2A33", "\\smashtimes", true); 1436 1437 // AMS Arrows 1438 // Note: unicode-math maps \u21e2 to their own function \rightdasharrow. 1439 // We'll map it to AMS function \dashrightarrow. It produces the same atom. 1440 defineSymbol(math, rel, "\u21e2", "\\dashrightarrow", true); 1441 // unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym. 1442 defineSymbol(math, rel, "\u21e0", "\\dashleftarrow", true); 1443 defineSymbol(math, rel, "\u21c7", "\\leftleftarrows", true); 1444 defineSymbol(math, rel, "\u21c6", "\\leftrightarrows", true); 1445 defineSymbol(math, rel, "\u21da", "\\Lleftarrow", true); 1446 defineSymbol(math, rel, "\u219e", "\\twoheadleftarrow", true); 1447 defineSymbol(math, rel, "\u21a2", "\\leftarrowtail", true); 1448 defineSymbol(math, rel, "\u21ab", "\\looparrowleft", true); 1449 defineSymbol(math, rel, "\u21cb", "\\leftrightharpoons", true); 1450 defineSymbol(math, rel, "\u21b6", "\\curvearrowleft", true); 1451 // unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym. 1452 defineSymbol(math, rel, "\u21ba", "\\circlearrowleft", true); 1453 defineSymbol(math, rel, "\u21b0", "\\Lsh", true); 1454 defineSymbol(math, rel, "\u21c8", "\\upuparrows", true); 1455 defineSymbol(math, rel, "\u21bf", "\\upharpoonleft", true); 1456 defineSymbol(math, rel, "\u21c3", "\\downharpoonleft", true); 1457 defineSymbol(math, rel, "\u22b6", "\\origof", true); 1458 defineSymbol(math, rel, "\u22b7", "\\imageof", true); 1459 defineSymbol(math, rel, "\u22b8", "\\multimap", true); 1460 defineSymbol(math, rel, "\u21ad", "\\leftrightsquigarrow", true); 1461 defineSymbol(math, rel, "\u21c9", "\\rightrightarrows", true); 1462 defineSymbol(math, rel, "\u21c4", "\\rightleftarrows", true); 1463 defineSymbol(math, rel, "\u21a0", "\\twoheadrightarrow", true); 1464 defineSymbol(math, rel, "\u21a3", "\\rightarrowtail", true); 1465 defineSymbol(math, rel, "\u21ac", "\\looparrowright", true); 1466 defineSymbol(math, rel, "\u21b7", "\\curvearrowright", true); 1467 // unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym. 1468 defineSymbol(math, rel, "\u21bb", "\\circlearrowright", true); 1469 defineSymbol(math, rel, "\u21b1", "\\Rsh", true); 1470 defineSymbol(math, rel, "\u21ca", "\\downdownarrows", true); 1471 defineSymbol(math, rel, "\u21be", "\\upharpoonright", true); 1472 defineSymbol(math, rel, "\u21c2", "\\downharpoonright", true); 1473 defineSymbol(math, rel, "\u21dd", "\\rightsquigarrow", true); 1474 defineSymbol(math, rel, "\u21dd", "\\leadsto"); 1475 defineSymbol(math, rel, "\u21db", "\\Rrightarrow", true); 1476 defineSymbol(math, rel, "\u21be", "\\restriction"); 1477 1478 defineSymbol(math, textord, "\u2018", "`"); 1479 defineSymbol(math, textord, "$", "\\$"); 1480 defineSymbol(temml_text, textord, "$", "\\$"); 1481 defineSymbol(temml_text, textord, "$", "\\textdollar"); 1482 defineSymbol(math, textord, "¢", "\\cent"); 1483 defineSymbol(temml_text, textord, "¢", "\\cent"); 1484 defineSymbol(math, textord, "%", "\\%"); 1485 defineSymbol(temml_text, textord, "%", "\\%"); 1486 defineSymbol(math, textord, "_", "\\_"); 1487 defineSymbol(temml_text, textord, "_", "\\_"); 1488 defineSymbol(temml_text, textord, "_", "\\textunderscore"); 1489 defineSymbol(temml_text, textord, "\u2423", "\\textvisiblespace", true); 1490 defineSymbol(math, textord, "\u2220", "\\angle", true); 1491 defineSymbol(math, textord, "\u221e", "\\infty", true); 1492 defineSymbol(math, textord, "\u2032", "\\prime"); 1493 defineSymbol(math, textord, "\u2033", "\\dprime"); 1494 defineSymbol(math, textord, "\u2034", "\\trprime"); 1495 defineSymbol(math, textord, "\u2057", "\\qprime"); 1496 defineSymbol(math, textord, "\u25b3", "\\triangle"); 1497 defineSymbol(temml_text, textord, "\u0391", "\\Alpha", true); 1498 defineSymbol(temml_text, textord, "\u0392", "\\Beta", true); 1499 defineSymbol(temml_text, textord, "\u0393", "\\Gamma", true); 1500 defineSymbol(temml_text, textord, "\u0394", "\\Delta", true); 1501 defineSymbol(temml_text, textord, "\u0395", "\\Epsilon", true); 1502 defineSymbol(temml_text, textord, "\u0396", "\\Zeta", true); 1503 defineSymbol(temml_text, textord, "\u0397", "\\Eta", true); 1504 defineSymbol(temml_text, textord, "\u0398", "\\Theta", true); 1505 defineSymbol(temml_text, textord, "\u0399", "\\Iota", true); 1506 defineSymbol(temml_text, textord, "\u039a", "\\Kappa", true); 1507 defineSymbol(temml_text, textord, "\u039b", "\\Lambda", true); 1508 defineSymbol(temml_text, textord, "\u039c", "\\Mu", true); 1509 defineSymbol(temml_text, textord, "\u039d", "\\Nu", true); 1510 defineSymbol(temml_text, textord, "\u039e", "\\Xi", true); 1511 defineSymbol(temml_text, textord, "\u039f", "\\Omicron", true); 1512 defineSymbol(temml_text, textord, "\u03a0", "\\Pi", true); 1513 defineSymbol(temml_text, textord, "\u03a1", "\\Rho", true); 1514 defineSymbol(temml_text, textord, "\u03a3", "\\Sigma", true); 1515 defineSymbol(temml_text, textord, "\u03a4", "\\Tau", true); 1516 defineSymbol(temml_text, textord, "\u03a5", "\\Upsilon", true); 1517 defineSymbol(temml_text, textord, "\u03a6", "\\Phi", true); 1518 defineSymbol(temml_text, textord, "\u03a7", "\\Chi", true); 1519 defineSymbol(temml_text, textord, "\u03a8", "\\Psi", true); 1520 defineSymbol(temml_text, textord, "\u03a9", "\\Omega", true); 1521 defineSymbol(math, mathord, "\u0391", "\\Alpha", true); 1522 defineSymbol(math, mathord, "\u0392", "\\Beta", true); 1523 defineSymbol(math, mathord, "\u0393", "\\Gamma", true); 1524 defineSymbol(math, mathord, "\u0394", "\\Delta", true); 1525 defineSymbol(math, mathord, "\u0395", "\\Epsilon", true); 1526 defineSymbol(math, mathord, "\u0396", "\\Zeta", true); 1527 defineSymbol(math, mathord, "\u0397", "\\Eta", true); 1528 defineSymbol(math, mathord, "\u0398", "\\Theta", true); 1529 defineSymbol(math, mathord, "\u0399", "\\Iota", true); 1530 defineSymbol(math, mathord, "\u039a", "\\Kappa", true); 1531 defineSymbol(math, mathord, "\u039b", "\\Lambda", true); 1532 defineSymbol(math, mathord, "\u039c", "\\Mu", true); 1533 defineSymbol(math, mathord, "\u039d", "\\Nu", true); 1534 defineSymbol(math, mathord, "\u039e", "\\Xi", true); 1535 defineSymbol(math, mathord, "\u039f", "\\Omicron", true); 1536 defineSymbol(math, mathord, "\u03a0", "\\Pi", true); 1537 defineSymbol(math, mathord, "\u03a1", "\\Rho", true); 1538 defineSymbol(math, mathord, "\u03a3", "\\Sigma", true); 1539 defineSymbol(math, mathord, "\u03a4", "\\Tau", true); 1540 defineSymbol(math, mathord, "\u03a5", "\\Upsilon", true); 1541 defineSymbol(math, mathord, "\u03a6", "\\Phi", true); 1542 defineSymbol(math, mathord, "\u03a7", "\\Chi", true); 1543 defineSymbol(math, mathord, "\u03a8", "\\Psi", true); 1544 defineSymbol(math, mathord, "\u03a9", "\\Omega", true); 1545 defineSymbol(math, temml_open, "\u00ac", "\\neg", true); 1546 defineSymbol(math, temml_open, "\u00ac", "\\lnot"); 1547 defineSymbol(math, textord, "\u22a4", "\\top"); 1548 defineSymbol(math, textord, "\u22a5", "\\bot"); 1549 defineSymbol(math, textord, "\u2205", "\\emptyset"); 1550 defineSymbol(math, textord, "\u2300", "\\varnothing"); 1551 defineSymbol(math, mathord, "\u03b1", "\\alpha", true); 1552 defineSymbol(math, mathord, "\u03b2", "\\beta", true); 1553 defineSymbol(math, mathord, "\u03b3", "\\gamma", true); 1554 defineSymbol(math, mathord, "\u03b4", "\\delta", true); 1555 defineSymbol(math, mathord, "\u03f5", "\\epsilon", true); 1556 defineSymbol(math, mathord, "\u03b6", "\\zeta", true); 1557 defineSymbol(math, mathord, "\u03b7", "\\eta", true); 1558 defineSymbol(math, mathord, "\u03b8", "\\theta", true); 1559 defineSymbol(math, mathord, "\u03b9", "\\iota", true); 1560 defineSymbol(math, mathord, "\u03ba", "\\kappa", true); 1561 defineSymbol(math, mathord, "\u03bb", "\\lambda", true); 1562 defineSymbol(math, mathord, "\u03bc", "\\mu", true); 1563 defineSymbol(math, mathord, "\u03bd", "\\nu", true); 1564 defineSymbol(math, mathord, "\u03be", "\\xi", true); 1565 defineSymbol(math, mathord, "\u03bf", "\\omicron", true); 1566 defineSymbol(math, mathord, "\u03c0", "\\pi", true); 1567 defineSymbol(math, mathord, "\u03c1", "\\rho", true); 1568 defineSymbol(math, mathord, "\u03c3", "\\sigma", true); 1569 defineSymbol(math, mathord, "\u03c4", "\\tau", true); 1570 defineSymbol(math, mathord, "\u03c5", "\\upsilon", true); 1571 defineSymbol(math, mathord, "\u03d5", "\\phi", true); 1572 defineSymbol(math, mathord, "\u03c7", "\\chi", true); 1573 defineSymbol(math, mathord, "\u03c8", "\\psi", true); 1574 defineSymbol(math, mathord, "\u03c9", "\\omega", true); 1575 defineSymbol(math, mathord, "\u03b5", "\\varepsilon", true); 1576 defineSymbol(math, mathord, "\u03d1", "\\vartheta", true); 1577 defineSymbol(math, mathord, "\u03d6", "\\varpi", true); 1578 defineSymbol(math, mathord, "\u03f1", "\\varrho", true); 1579 defineSymbol(math, mathord, "\u03c2", "\\varsigma", true); 1580 defineSymbol(math, mathord, "\u03c6", "\\varphi", true); 1581 defineSymbol(math, mathord, "\u03d8", "\\Coppa", true); 1582 defineSymbol(math, mathord, "\u03d9", "\\coppa", true); 1583 defineSymbol(math, mathord, "\u03d9", "\\varcoppa", true); 1584 defineSymbol(math, mathord, "\u03de", "\\Koppa", true); 1585 defineSymbol(math, mathord, "\u03df", "\\koppa", true); 1586 defineSymbol(math, mathord, "\u03e0", "\\Sampi", true); 1587 defineSymbol(math, mathord, "\u03e1", "\\sampi", true); 1588 defineSymbol(math, mathord, "\u03da", "\\Stigma", true); 1589 defineSymbol(math, mathord, "\u03db", "\\stigma", true); 1590 defineSymbol(math, mathord, "\u2aeb", "\\Bot"); 1591 defineSymbol(math, bin, "\u2217", "\u2217", true); 1592 defineSymbol(math, bin, "+", "+"); 1593 defineSymbol(math, bin, "\u2217", "*"); 1594 defineSymbol(math, bin, "\u2044", "/", true); 1595 defineSymbol(math, bin, "\u2044", "\u2044"); 1596 defineSymbol(math, bin, "\u2212", "-", true); 1597 defineSymbol(math, bin, "\u22c5", "\\cdot", true); 1598 defineSymbol(math, bin, "\u2218", "\\circ", true); 1599 defineSymbol(math, bin, "\u00f7", "\\div", true); 1600 defineSymbol(math, bin, "\u00b1", "\\pm", true); 1601 defineSymbol(math, bin, "\u00d7", "\\times", true); 1602 defineSymbol(math, bin, "\u2229", "\\cap", true); 1603 defineSymbol(math, bin, "\u222a", "\\cup", true); 1604 defineSymbol(math, bin, "\u2216", "\\setminus", true); 1605 defineSymbol(math, bin, "\u2227", "\\land"); 1606 defineSymbol(math, bin, "\u2228", "\\lor"); 1607 defineSymbol(math, bin, "\u2227", "\\wedge", true); 1608 defineSymbol(math, bin, "\u2228", "\\vee", true); 1609 defineSymbol(math, temml_open, "\u27e6", "\\llbracket", true); // stmaryrd/semantic packages 1610 defineSymbol(math, temml_close, "\u27e7", "\\rrbracket", true); 1611 defineSymbol(math, temml_open, "\u27e8", "\\langle", true); 1612 defineSymbol(math, temml_open, "\u27ea", "\\lAngle", true); 1613 defineSymbol(math, temml_open, "\u2989", "\\llangle", true); 1614 defineSymbol(math, temml_open, "|", "\\lvert"); 1615 defineSymbol(math, temml_open, "\u2016", "\\lVert", true); 1616 defineSymbol(math, textord, "!", "\\oc"); // cmll package 1617 defineSymbol(math, textord, "?", "\\wn"); 1618 defineSymbol(math, textord, "\u2193", "\\shpos"); 1619 defineSymbol(math, textord, "\u2195", "\\shift"); 1620 defineSymbol(math, textord, "\u2191", "\\shneg"); 1621 defineSymbol(math, temml_close, "?", "?"); 1622 defineSymbol(math, temml_close, "!", "!"); 1623 defineSymbol(math, temml_close, "‼", "‼"); 1624 defineSymbol(math, temml_close, "\u27e9", "\\rangle", true); 1625 defineSymbol(math, temml_close, "\u27eb", "\\rAngle", true); 1626 defineSymbol(math, temml_close, "\u298a", "\\rrangle", true); 1627 defineSymbol(math, temml_close, "|", "\\rvert"); 1628 defineSymbol(math, temml_close, "\u2016", "\\rVert"); 1629 defineSymbol(math, temml_open, "\u2983", "\\lBrace", true); // stmaryrd/semantic packages 1630 defineSymbol(math, temml_close, "\u2984", "\\rBrace", true); 1631 defineSymbol(math, rel, "=", "\\equal", true); 1632 defineSymbol(math, rel, ":", ":"); 1633 defineSymbol(math, rel, "\u2248", "\\approx", true); 1634 defineSymbol(math, rel, "\u2245", "\\cong", true); 1635 defineSymbol(math, rel, "\u2265", "\\ge"); 1636 defineSymbol(math, rel, "\u2265", "\\geq", true); 1637 defineSymbol(math, rel, "\u2190", "\\gets"); 1638 defineSymbol(math, rel, ">", "\\gt", true); 1639 defineSymbol(math, rel, "\u2208", "\\in", true); 1640 defineSymbol(math, rel, "\u2209", "\\notin", true); 1641 defineSymbol(math, rel, "\ue020", "\\@not"); 1642 defineSymbol(math, rel, "\u2282", "\\subset", true); 1643 defineSymbol(math, rel, "\u2283", "\\supset", true); 1644 defineSymbol(math, rel, "\u2286", "\\subseteq", true); 1645 defineSymbol(math, rel, "\u2287", "\\supseteq", true); 1646 defineSymbol(math, rel, "\u2288", "\\nsubseteq", true); 1647 defineSymbol(math, rel, "\u2288", "\\nsubseteqq"); 1648 defineSymbol(math, rel, "\u2289", "\\nsupseteq", true); 1649 defineSymbol(math, rel, "\u2289", "\\nsupseteqq"); 1650 defineSymbol(math, rel, "\u22a8", "\\models"); 1651 defineSymbol(math, rel, "\u2190", "\\leftarrow", true); 1652 defineSymbol(math, rel, "\u2264", "\\le"); 1653 defineSymbol(math, rel, "\u2264", "\\leq", true); 1654 defineSymbol(math, rel, "<", "\\lt", true); 1655 defineSymbol(math, rel, "\u2192", "\\rightarrow", true); 1656 defineSymbol(math, rel, "\u2192", "\\to"); 1657 defineSymbol(math, rel, "\u2271", "\\ngeq", true); 1658 defineSymbol(math, rel, "\u2271", "\\ngeqq"); 1659 defineSymbol(math, rel, "\u2271", "\\ngeqslant"); 1660 defineSymbol(math, rel, "\u2270", "\\nleq", true); 1661 defineSymbol(math, rel, "\u2270", "\\nleqq"); 1662 defineSymbol(math, rel, "\u2270", "\\nleqslant"); 1663 defineSymbol(math, rel, "\u2aeb", "\\Perp", true); //cmll package 1664 defineSymbol(math, spacing, "\u00a0", "\\ "); 1665 defineSymbol(math, spacing, "\u00a0", "\\space"); 1666 // Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{% 1667 defineSymbol(math, spacing, "\u00a0", "\\nobreakspace"); 1668 defineSymbol(temml_text, spacing, "\u00a0", "\\ "); 1669 defineSymbol(temml_text, spacing, "\u00a0", " "); 1670 defineSymbol(temml_text, spacing, "\u00a0", "\\space"); 1671 defineSymbol(temml_text, spacing, "\u00a0", "\\nobreakspace"); 1672 defineSymbol(math, spacing, null, "\\nobreak"); 1673 defineSymbol(math, spacing, null, "\\allowbreak"); 1674 defineSymbol(math, punct, ",", ","); 1675 defineSymbol(temml_text, punct, ":", ":"); 1676 defineSymbol(math, punct, ";", ";"); 1677 defineSymbol(math, bin, "\u22bc", "\\barwedge"); 1678 defineSymbol(math, bin, "\u22bb", "\\veebar"); 1679 defineSymbol(math, bin, "\u2299", "\\odot", true); 1680 // Firefox turns ⊕ into an emoji. So append \uFE0E. Define Unicode character in macros, not here. 1681 defineSymbol(math, bin, "\u2295\uFE0E", "\\oplus"); 1682 defineSymbol(math, bin, "\u2297", "\\otimes", true); 1683 defineSymbol(math, textord, "\u2202", "\\partial", true); 1684 defineSymbol(math, bin, "\u2298", "\\oslash", true); 1685 defineSymbol(math, bin, "\u229a", "\\circledcirc", true); 1686 defineSymbol(math, bin, "\u22a1", "\\boxdot", true); 1687 defineSymbol(math, bin, "\u25b3", "\\bigtriangleup"); 1688 defineSymbol(math, bin, "\u25bd", "\\bigtriangledown"); 1689 defineSymbol(math, bin, "\u2020", "\\dagger"); 1690 defineSymbol(math, bin, "\u22c4", "\\diamond"); 1691 defineSymbol(math, bin, "\u25c3", "\\triangleleft"); 1692 defineSymbol(math, bin, "\u25b9", "\\triangleright"); 1693 defineSymbol(math, temml_open, "{", "\\{"); 1694 defineSymbol(temml_text, textord, "{", "\\{"); 1695 defineSymbol(temml_text, textord, "{", "\\textbraceleft"); 1696 defineSymbol(math, temml_close, "}", "\\}"); 1697 defineSymbol(temml_text, textord, "}", "\\}"); 1698 defineSymbol(temml_text, textord, "}", "\\textbraceright"); 1699 defineSymbol(math, temml_open, "{", "\\lbrace"); 1700 defineSymbol(math, temml_close, "}", "\\rbrace"); 1701 defineSymbol(math, temml_open, "[", "\\lbrack", true); 1702 defineSymbol(temml_text, textord, "[", "\\lbrack", true); 1703 defineSymbol(math, temml_close, "]", "\\rbrack", true); 1704 defineSymbol(temml_text, textord, "]", "\\rbrack", true); 1705 defineSymbol(math, temml_open, "(", "\\lparen", true); 1706 defineSymbol(math, temml_close, ")", "\\rparen", true); 1707 defineSymbol(math, temml_open, "⦇", "\\llparenthesis", true); 1708 defineSymbol(math, temml_close, "⦈", "\\rrparenthesis", true); 1709 defineSymbol(temml_text, textord, "<", "\\textless", true); // in T1 fontenc 1710 defineSymbol(temml_text, textord, ">", "\\textgreater", true); // in T1 fontenc 1711 defineSymbol(math, temml_open, "\u230a", "\\lfloor", true); 1712 defineSymbol(math, temml_close, "\u230b", "\\rfloor", true); 1713 defineSymbol(math, temml_open, "\u2308", "\\lceil", true); 1714 defineSymbol(math, temml_close, "\u2309", "\\rceil", true); 1715 defineSymbol(math, textord, "\\", "\\backslash"); 1716 defineSymbol(math, textord, "|", "|"); 1717 defineSymbol(math, textord, "|", "\\vert"); 1718 defineSymbol(temml_text, textord, "|", "\\textbar", true); // in T1 fontenc 1719 defineSymbol(math, textord, "\u2016", "\\|"); 1720 defineSymbol(math, textord, "\u2016", "\\Vert"); 1721 defineSymbol(temml_text, textord, "\u2016", "\\textbardbl"); 1722 defineSymbol(temml_text, textord, "~", "\\textasciitilde"); 1723 defineSymbol(temml_text, textord, "\\", "\\textbackslash"); 1724 defineSymbol(temml_text, textord, "^", "\\textasciicircum"); 1725 defineSymbol(math, rel, "\u2191", "\\uparrow", true); 1726 defineSymbol(math, rel, "\u21d1", "\\Uparrow", true); 1727 defineSymbol(math, rel, "\u2193", "\\downarrow", true); 1728 defineSymbol(math, rel, "\u21d3", "\\Downarrow", true); 1729 defineSymbol(math, rel, "\u2195", "\\updownarrow", true); 1730 defineSymbol(math, rel, "\u21d5", "\\Updownarrow", true); 1731 defineSymbol(math, op, "\u2210", "\\coprod"); 1732 defineSymbol(math, op, "\u22c1", "\\bigvee"); 1733 defineSymbol(math, op, "\u22c0", "\\bigwedge"); 1734 defineSymbol(math, op, "\u2a04", "\\biguplus"); 1735 defineSymbol(math, op, "\u2a04", "\\bigcupplus"); 1736 defineSymbol(math, op, "\u2a03", "\\bigcupdot"); 1737 defineSymbol(math, op, "\u2a07", "\\bigdoublevee"); 1738 defineSymbol(math, op, "\u2a08", "\\bigdoublewedge"); 1739 defineSymbol(math, op, "\u22c2", "\\bigcap"); 1740 defineSymbol(math, op, "\u22c3", "\\bigcup"); 1741 defineSymbol(math, op, "\u222b", "\\int"); 1742 defineSymbol(math, op, "\u222b", "\\intop"); 1743 defineSymbol(math, op, "\u222c", "\\iint"); 1744 defineSymbol(math, op, "\u222d", "\\iiint"); 1745 defineSymbol(math, op, "\u220f", "\\prod"); 1746 defineSymbol(math, op, "\u2211", "\\sum"); 1747 defineSymbol(math, op, "\u2a02", "\\bigotimes"); 1748 defineSymbol(math, op, "\u2a01", "\\bigoplus"); 1749 defineSymbol(math, op, "\u2a00", "\\bigodot"); 1750 defineSymbol(math, op, "\u2a09", "\\bigtimes"); 1751 defineSymbol(math, op, "\u222e", "\\oint"); 1752 defineSymbol(math, op, "\u222f", "\\oiint"); 1753 defineSymbol(math, op, "\u2230", "\\oiiint"); 1754 defineSymbol(math, op, "\u2231", "\\intclockwise"); 1755 defineSymbol(math, op, "\u2232", "\\varointclockwise"); 1756 defineSymbol(math, op, "\u2a0c", "\\iiiint"); 1757 defineSymbol(math, op, "\u2a0d", "\\intbar"); 1758 defineSymbol(math, op, "\u2a0e", "\\intBar"); 1759 defineSymbol(math, op, "\u2a0f", "\\fint"); 1760 defineSymbol(math, op, "\u2a12", "\\rppolint"); 1761 defineSymbol(math, op, "\u2a13", "\\scpolint"); 1762 defineSymbol(math, op, "\u2a15", "\\pointint"); 1763 defineSymbol(math, op, "\u2a16", "\\sqint"); 1764 defineSymbol(math, op, "\u2a17", "\\intlarhk"); 1765 defineSymbol(math, op, "\u2a18", "\\intx"); 1766 defineSymbol(math, op, "\u2a19", "\\intcap"); 1767 defineSymbol(math, op, "\u2a1a", "\\intcup"); 1768 defineSymbol(math, op, "\u2a05", "\\bigsqcap"); 1769 defineSymbol(math, op, "\u2a06", "\\bigsqcup"); 1770 defineSymbol(math, op, "\u222b", "\\smallint"); 1771 defineSymbol(temml_text, inner, "\u2026", "\\textellipsis"); 1772 defineSymbol(math, inner, "\u2026", "\\mathellipsis"); 1773 defineSymbol(temml_text, inner, "\u2026", "\\ldots", true); 1774 defineSymbol(math, inner, "\u2026", "\\ldots", true); 1775 defineSymbol(math, inner, "\u22f0", "\\iddots", true); 1776 defineSymbol(math, inner, "\u22ef", "\\@cdots", true); 1777 defineSymbol(math, inner, "\u22f1", "\\ddots", true); 1778 defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro 1779 defineSymbol(temml_text, textord, "\u22ee", "\\varvdots"); 1780 defineSymbol(math, accent, "\u02ca", "\\acute"); 1781 defineSymbol(math, accent, "\u0060", "\\grave"); 1782 defineSymbol(math, accent, "\u00a8", "\\ddot"); 1783 defineSymbol(math, accent, "\u2026", "\\dddot"); 1784 defineSymbol(math, accent, "\u2026\u002e", "\\ddddot"); 1785 defineSymbol(math, accent, "\u007e", "\\tilde"); 1786 defineSymbol(math, accent, "\u203e", "\\bar"); 1787 defineSymbol(math, accent, "\u02d8", "\\breve"); 1788 defineSymbol(math, accent, "\u02c7", "\\check"); 1789 defineSymbol(math, accent, "\u005e", "\\hat"); 1790 defineSymbol(math, accent, "\u2192", "\\vec"); 1791 defineSymbol(math, accent, "\u02d9", "\\dot"); 1792 defineSymbol(math, accent, "\u02da", "\\mathring"); 1793 defineSymbol(math, mathord, "\u0131", "\\imath", true); 1794 defineSymbol(math, mathord, "\u0237", "\\jmath", true); 1795 defineSymbol(math, textord, "\u0131", "\u0131"); 1796 defineSymbol(math, textord, "\u0237", "\u0237"); 1797 defineSymbol(temml_text, textord, "\u0131", "\\i", true); 1798 defineSymbol(temml_text, textord, "\u0237", "\\j", true); 1799 defineSymbol(temml_text, textord, "\u00df", "\\ss", true); 1800 defineSymbol(temml_text, textord, "\u00e6", "\\ae", true); 1801 defineSymbol(temml_text, textord, "\u0153", "\\oe", true); 1802 defineSymbol(temml_text, textord, "\u00f8", "\\o", true); 1803 defineSymbol(math, mathord, "\u00f8", "\\o", true); 1804 defineSymbol(temml_text, textord, "\u00c6", "\\AE", true); 1805 defineSymbol(temml_text, textord, "\u0152", "\\OE", true); 1806 defineSymbol(temml_text, textord, "\u00d8", "\\O", true); 1807 defineSymbol(math, mathord, "\u00d8", "\\O", true); 1808 defineSymbol(temml_text, accent, "\u02ca", "\\'"); // acute 1809 defineSymbol(temml_text, accent, "\u02cb", "\\`"); // grave 1810 defineSymbol(temml_text, accent, "\u02c6", "\\^"); // circumflex 1811 defineSymbol(temml_text, accent, "\u02dc", "\\~"); // tilde 1812 defineSymbol(temml_text, accent, "\u02c9", "\\="); // macron 1813 defineSymbol(temml_text, accent, "\u02d8", "\\u"); // breve 1814 defineSymbol(temml_text, accent, "\u02d9", "\\."); // dot above 1815 defineSymbol(temml_text, accent, "\u00b8", "\\c"); // cedilla 1816 defineSymbol(temml_text, accent, "\u02da", "\\r"); // ring above 1817 defineSymbol(temml_text, accent, "\u02c7", "\\v"); // caron 1818 defineSymbol(temml_text, accent, "\u00a8", '\\"'); // diaresis 1819 defineSymbol(temml_text, accent, "\u02dd", "\\H"); // double acute 1820 defineSymbol(math, accent, "\u02ca", "\\'"); // acute 1821 defineSymbol(math, accent, "\u02cb", "\\`"); // grave 1822 defineSymbol(math, accent, "\u02c6", "\\^"); // circumflex 1823 defineSymbol(math, accent, "\u02dc", "\\~"); // tilde 1824 defineSymbol(math, accent, "\u02c9", "\\="); // macron 1825 defineSymbol(math, accent, "\u02d8", "\\u"); // breve 1826 defineSymbol(math, accent, "\u02d9", "\\."); // dot above 1827 defineSymbol(math, accent, "\u00b8", "\\c"); // cedilla 1828 defineSymbol(math, accent, "\u02da", "\\r"); // ring above 1829 defineSymbol(math, accent, "\u02c7", "\\v"); // caron 1830 defineSymbol(math, accent, "\u00a8", '\\"'); // diaresis 1831 defineSymbol(math, accent, "\u02dd", "\\H"); // double acute 1832 1833 // These ligatures are detected and created in Parser.js's `formLigatures`. 1834 const ligatures = { 1835 "--": true, 1836 "---": true, 1837 "``": true, 1838 "''": true 1839 }; 1840 1841 defineSymbol(temml_text, textord, "\u2013", "--", true); 1842 defineSymbol(temml_text, textord, "\u2013", "\\textendash"); 1843 defineSymbol(temml_text, textord, "\u2014", "---", true); 1844 defineSymbol(temml_text, textord, "\u2014", "\\textemdash"); 1845 defineSymbol(temml_text, textord, "\u2018", "`", true); 1846 defineSymbol(temml_text, textord, "\u2018", "\\textquoteleft"); 1847 defineSymbol(temml_text, textord, "\u2019", "'", true); 1848 defineSymbol(temml_text, textord, "\u2019", "\\textquoteright"); 1849 defineSymbol(temml_text, textord, "\u201c", "``", true); 1850 defineSymbol(temml_text, textord, "\u201c", "\\textquotedblleft"); 1851 defineSymbol(temml_text, textord, "\u201d", "''", true); 1852 defineSymbol(temml_text, textord, "\u201d", "\\textquotedblright"); 1853 // \degree from gensymb package 1854 defineSymbol(math, textord, "\u00b0", "\\degree", true); 1855 defineSymbol(temml_text, textord, "\u00b0", "\\degree"); 1856 // \textdegree from inputenc package 1857 defineSymbol(temml_text, textord, "\u00b0", "\\textdegree", true); 1858 // TODO: In LaTeX, \pounds can generate a different character in text and math 1859 // mode, but among our fonts, only Main-Regular defines this character "163". 1860 defineSymbol(math, textord, "\u00a3", "\\pounds"); 1861 defineSymbol(math, textord, "\u00a3", "\\mathsterling", true); 1862 defineSymbol(temml_text, textord, "\u00a3", "\\pounds"); 1863 defineSymbol(temml_text, textord, "\u00a3", "\\textsterling", true); 1864 defineSymbol(math, textord, "\u2720", "\\maltese"); 1865 defineSymbol(temml_text, textord, "\u2720", "\\maltese"); 1866 defineSymbol(math, textord, "\u20ac", "\\euro", true); 1867 defineSymbol(temml_text, textord, "\u20ac", "\\euro", true); 1868 defineSymbol(temml_text, textord, "\u20ac", "\\texteuro"); 1869 defineSymbol(math, textord, "\u00a9", "\\copyright", true); 1870 defineSymbol(temml_text, textord, "\u00a9", "\\textcopyright"); 1871 defineSymbol(math, textord, "\u2300", "\\diameter", true); 1872 defineSymbol(temml_text, textord, "\u2300", "\\diameter"); 1873 1874 // Italic Greek 1875 defineSymbol(math, textord, "𝛤", "\\varGamma"); 1876 defineSymbol(math, textord, "𝛥", "\\varDelta"); 1877 defineSymbol(math, textord, "𝛩", "\\varTheta"); 1878 defineSymbol(math, textord, "𝛬", "\\varLambda"); 1879 defineSymbol(math, textord, "𝛯", "\\varXi"); 1880 defineSymbol(math, textord, "𝛱", "\\varPi"); 1881 defineSymbol(math, textord, "𝛴", "\\varSigma"); 1882 defineSymbol(math, textord, "𝛶", "\\varUpsilon"); 1883 defineSymbol(math, textord, "𝛷", "\\varPhi"); 1884 defineSymbol(math, textord, "𝛹", "\\varPsi"); 1885 defineSymbol(math, textord, "𝛺", "\\varOmega"); 1886 defineSymbol(temml_text, textord, "𝛤", "\\varGamma"); 1887 defineSymbol(temml_text, textord, "𝛥", "\\varDelta"); 1888 defineSymbol(temml_text, textord, "𝛩", "\\varTheta"); 1889 defineSymbol(temml_text, textord, "𝛬", "\\varLambda"); 1890 defineSymbol(temml_text, textord, "𝛯", "\\varXi"); 1891 defineSymbol(temml_text, textord, "𝛱", "\\varPi"); 1892 defineSymbol(temml_text, textord, "𝛴", "\\varSigma"); 1893 defineSymbol(temml_text, textord, "𝛶", "\\varUpsilon"); 1894 defineSymbol(temml_text, textord, "𝛷", "\\varPhi"); 1895 defineSymbol(temml_text, textord, "𝛹", "\\varPsi"); 1896 defineSymbol(temml_text, textord, "𝛺", "\\varOmega"); 1897 1898 1899 // There are lots of symbols which are the same, so we add them in afterwards. 1900 // All of these are textords in math mode 1901 const mathTextSymbols = '0123456789/@."'; 1902 for (let i = 0; i < mathTextSymbols.length; i++) { 1903 const ch = mathTextSymbols.charAt(i); 1904 defineSymbol(math, textord, ch, ch); 1905 } 1906 1907 // All of these are textords in text mode 1908 const textSymbols = '0123456789!@*()-=+";:?/.,'; 1909 for (let i = 0; i < textSymbols.length; i++) { 1910 const ch = textSymbols.charAt(i); 1911 defineSymbol(temml_text, textord, ch, ch); 1912 } 1913 1914 // All of these are textords in text mode, and mathords in math mode 1915 const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 1916 for (let i = 0; i < letters.length; i++) { 1917 const ch = letters.charAt(i); 1918 defineSymbol(math, mathord, ch, ch); 1919 defineSymbol(temml_text, textord, ch, ch); 1920 } 1921 1922 // Some more letters in Unicode Basic Multilingual Plane. 1923 const narrow = "ÇÐÞçþℂℍℕℙℚℝℤℎℏℊℋℌℐℑℒℓ℘ℛℜℬℰℱℳℭℨ"; 1924 for (let i = 0; i < narrow.length; i++) { 1925 const ch = narrow.charAt(i); 1926 defineSymbol(math, mathord, ch, ch); 1927 defineSymbol(temml_text, textord, ch, ch); 1928 } 1929 1930 // The next loop loads wide (surrogate pair) characters. 1931 // We support some letters in the Unicode range U+1D400 to U+1D7FF, 1932 // Mathematical Alphanumeric Symbols. 1933 let wideChar = ""; 1934 for (let i = 0; i < letters.length; i++) { 1935 // The hex numbers in the next line are a surrogate pair. 1936 // 0xD835 is the high surrogate for all letters in the range we support. 1937 // 0xDC00 is the low surrogate for bold A. 1938 wideChar = String.fromCharCode(0xd835, 0xdc00 + i); // A-Z a-z bold 1939 defineSymbol(math, mathord, wideChar, wideChar); 1940 defineSymbol(temml_text, textord, wideChar, wideChar); 1941 1942 wideChar = String.fromCharCode(0xd835, 0xdc34 + i); // A-Z a-z italic 1943 defineSymbol(math, mathord, wideChar, wideChar); 1944 defineSymbol(temml_text, textord, wideChar, wideChar); 1945 1946 wideChar = String.fromCharCode(0xd835, 0xdc68 + i); // A-Z a-z bold italic 1947 defineSymbol(math, mathord, wideChar, wideChar); 1948 defineSymbol(temml_text, textord, wideChar, wideChar); 1949 1950 wideChar = String.fromCharCode(0xd835, 0xdd04 + i); // A-Z a-z Fractur 1951 defineSymbol(math, mathord, wideChar, wideChar); 1952 defineSymbol(temml_text, textord, wideChar, wideChar); 1953 1954 wideChar = String.fromCharCode(0xd835, 0xdda0 + i); // A-Z a-z sans-serif 1955 defineSymbol(math, mathord, wideChar, wideChar); 1956 defineSymbol(temml_text, textord, wideChar, wideChar); 1957 1958 wideChar = String.fromCharCode(0xd835, 0xddd4 + i); // A-Z a-z sans bold 1959 defineSymbol(math, mathord, wideChar, wideChar); 1960 defineSymbol(temml_text, textord, wideChar, wideChar); 1961 1962 wideChar = String.fromCharCode(0xd835, 0xde08 + i); // A-Z a-z sans italic 1963 defineSymbol(math, mathord, wideChar, wideChar); 1964 defineSymbol(temml_text, textord, wideChar, wideChar); 1965 1966 wideChar = String.fromCharCode(0xd835, 0xde70 + i); // A-Z a-z monospace 1967 defineSymbol(math, mathord, wideChar, wideChar); 1968 defineSymbol(temml_text, textord, wideChar, wideChar); 1969 1970 wideChar = String.fromCharCode(0xd835, 0xdd38 + i); // A-Z a-z double struck 1971 defineSymbol(math, mathord, wideChar, wideChar); 1972 defineSymbol(temml_text, textord, wideChar, wideChar); 1973 1974 const ch = letters.charAt(i); 1975 wideChar = String.fromCharCode(0xd835, 0xdc9c + i); // A-Z a-z calligraphic 1976 defineSymbol(math, mathord, ch, wideChar); 1977 defineSymbol(temml_text, textord, ch, wideChar); 1978 } 1979 1980 // Next, some wide character numerals 1981 for (let i = 0; i < 10; i++) { 1982 wideChar = String.fromCharCode(0xd835, 0xdfce + i); // 0-9 bold 1983 defineSymbol(math, mathord, wideChar, wideChar); 1984 defineSymbol(temml_text, textord, wideChar, wideChar); 1985 1986 wideChar = String.fromCharCode(0xd835, 0xdfe2 + i); // 0-9 sans serif 1987 defineSymbol(math, mathord, wideChar, wideChar); 1988 defineSymbol(temml_text, textord, wideChar, wideChar); 1989 1990 wideChar = String.fromCharCode(0xd835, 0xdfec + i); // 0-9 bold sans 1991 defineSymbol(math, mathord, wideChar, wideChar); 1992 defineSymbol(temml_text, textord, wideChar, wideChar); 1993 1994 wideChar = String.fromCharCode(0xd835, 0xdff6 + i); // 0-9 monospace 1995 defineSymbol(math, mathord, wideChar, wideChar); 1996 defineSymbol(temml_text, textord, wideChar, wideChar); 1997 } 1998 1999 /* 2000 * Neither Firefox nor Chrome support hard line breaks or soft line breaks. 2001 * (Despite https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs) 2002 * So Temml has work-arounds for both hard and soft breaks. 2003 * The work-arounds sadly do not work simultaneously. Any top-level hard 2004 * break makes soft line breaks impossible. 2005 * 2006 * Hard breaks are simulated by creating a <mtable> and putting each line in its own <mtr>. 2007 * 2008 * To create soft line breaks, Temml avoids using the <semantics> and <annotation> tags. 2009 * Then the top level of a <math> element can be occupied by <mrow> elements, and the browser 2010 * will break after a <mrow> if the expression extends beyond the container limit. 2011 * 2012 * The default is for soft line breaks after each top-level binary or 2013 * relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that 2014 * each <mrow> ends in a binary or relational operator. 2015 * 2016 * An option is for soft line breaks before an "=" sign. That changes the <mrow>s. 2017 * 2018 * Soft line breaks will not work in Chromium and Safari, only Firefox. 2019 * 2020 * Hopefully browsers will someday do their own linebreaking and we will be able to delete 2021 * much of this module. 2022 */ 2023 2024 const openDelims = "([{⌊⌈⟨⟮⎰⟦⦃"; 2025 const closeDelims = ")]}⌋⌉⟩⟯⎱⟦⦄"; 2026 2027 function setLineBreaks(expression, wrapMode, isDisplayMode) { 2028 const mtrs = []; 2029 let mrows = []; 2030 let block = []; 2031 let numTopLevelEquals = 0; 2032 let i = 0; 2033 let level = 0; 2034 while (i < expression.length) { 2035 while (expression[i] instanceof DocumentFragment) { 2036 expression.splice(i, 1, ...expression[i].children); // Expand the fragment. 2037 } 2038 const node = expression[i]; 2039 if (node.attributes && node.attributes.linebreak && 2040 node.attributes.linebreak === "newline") { 2041 // A hard line break. Create a <mtr> for the current block. 2042 if (block.length > 0) { 2043 mrows.push(new mathMLTree.MathNode("mrow", block)); 2044 } 2045 mrows.push(node); 2046 block = []; 2047 const mtd = new mathMLTree.MathNode("mtd", mrows); 2048 mtd.style.textAlign = "left"; 2049 mtrs.push(new mathMLTree.MathNode("mtr", [mtd])); 2050 mrows = []; 2051 i += 1; 2052 continue 2053 } 2054 block.push(node); 2055 if (node.type && node.type === "mo" && node.children.length === 1 && 2056 !Object.hasOwn(node.attributes, "movablelimits")) { 2057 const ch = node.children[0].text; 2058 if (openDelims.indexOf(ch) > -1) { 2059 level += 1; 2060 } else if (closeDelims.indexOf(ch) > -1) { 2061 level -= 1; 2062 } else if (level === 0 && wrapMode === "=" && ch === "=") { 2063 numTopLevelEquals += 1; 2064 if (numTopLevelEquals > 1) { 2065 block.pop(); 2066 // Start a new block. (Insert a soft linebreak.) 2067 const element = new mathMLTree.MathNode("mrow", block); 2068 mrows.push(element); 2069 block = [node]; 2070 } 2071 } else if (level === 0 && wrapMode === "tex" && ch !== "∇") { 2072 // Check if the following node is a \nobreak text node, e.g. "~"" 2073 const next = i < expression.length - 1 ? expression[i + 1] : null; 2074 let glueIsFreeOfNobreak = true; 2075 if ( 2076 !( 2077 next && 2078 next.type === "mtext" && 2079 next.attributes.linebreak && 2080 next.attributes.linebreak === "nobreak" 2081 ) 2082 ) { 2083 // We may need to start a new block. 2084 // First, put any post-operator glue on same line as operator. 2085 for (let j = i + 1; j < expression.length; j++) { 2086 const nd = expression[j]; 2087 if ( 2088 nd.type && 2089 nd.type === "mspace" && 2090 !(nd.attributes.linebreak && nd.attributes.linebreak === "newline") 2091 ) { 2092 block.push(nd); 2093 i += 1; 2094 if ( 2095 nd.attributes && 2096 nd.attributes.linebreak && 2097 nd.attributes.linebreak === "nobreak" 2098 ) { 2099 glueIsFreeOfNobreak = false; 2100 } 2101 } else { 2102 break; 2103 } 2104 } 2105 } 2106 if (glueIsFreeOfNobreak) { 2107 // Start a new block. (Insert a soft linebreak.) 2108 const element = new mathMLTree.MathNode("mrow", block); 2109 mrows.push(element); 2110 block = []; 2111 } 2112 } 2113 } 2114 i += 1; 2115 } 2116 if (block.length > 0) { 2117 const element = new mathMLTree.MathNode("mrow", block); 2118 mrows.push(element); 2119 } 2120 if (mtrs.length > 0) { 2121 const mtd = new mathMLTree.MathNode("mtd", mrows); 2122 mtd.style.textAlign = "left"; 2123 const mtr = new mathMLTree.MathNode("mtr", [mtd]); 2124 mtrs.push(mtr); 2125 const mtable = new mathMLTree.MathNode("mtable", mtrs); 2126 if (!isDisplayMode) { 2127 mtable.setAttribute("columnalign", "left"); 2128 mtable.setAttribute("rowspacing", "0em"); 2129 } 2130 return mtable 2131 } 2132 return mathMLTree.newDocumentFragment(mrows); 2133 } 2134 2135 /** 2136 * This file converts a parse tree into a corresponding MathML tree. The main 2137 * entry point is the `buildMathML` function, which takes a parse tree from the 2138 * parser. 2139 */ 2140 2141 2142 /** 2143 * Takes a symbol and converts it into a MathML text node after performing 2144 * optional replacement from symbols.js. 2145 */ 2146 const makeText = function(text, mode, style) { 2147 if ( 2148 symbols[mode][text] && 2149 symbols[mode][text].replace && 2150 text.charCodeAt(0) !== 0xd835 && 2151 !( 2152 Object.prototype.hasOwnProperty.call(ligatures, text) && 2153 style && 2154 ((style.fontFamily && style.fontFamily.slice(4, 6) === "tt") || 2155 (style.font && style.font.slice(4, 6) === "tt")) 2156 ) 2157 ) { 2158 text = symbols[mode][text].replace; 2159 } 2160 2161 return new mathMLTree.TextNode(text); 2162 }; 2163 2164 const copyChar = (newRow, child) => { 2165 if (newRow.children.length === 0 || 2166 newRow.children[newRow.children.length - 1].type !== "mtext") { 2167 const mtext = new mathMLTree.MathNode( 2168 "mtext", 2169 [new mathMLTree.TextNode(child.children[0].text)] 2170 ); 2171 newRow.children.push(mtext); 2172 } else { 2173 newRow.children[newRow.children.length - 1].children[0].text += child.children[0].text; 2174 } 2175 }; 2176 2177 const consolidateText = mrow => { 2178 // If possible, consolidate adjacent <mtext> elements into a single element. 2179 if (mrow.type !== "mrow" && mrow.type !== "mstyle") { return mrow } 2180 if (mrow.children.length === 0) { return mrow } // empty group, e.g., \text{} 2181 const newRow = new mathMLTree.MathNode("mrow"); 2182 for (let i = 0; i < mrow.children.length; i++) { 2183 const child = mrow.children[i]; 2184 if (child.type === "mtext" && Object.keys(child.attributes).length === 0) { 2185 copyChar(newRow, child); 2186 } else if (child.type === "mrow") { 2187 // We'll also check the children of an mrow. One level only. No recursion. 2188 let canConsolidate = true; 2189 for (let j = 0; j < child.children.length; j++) { 2190 const grandChild = child.children[j]; 2191 if (grandChild.type !== "mtext" || Object.keys(child.attributes).length !== 0) { 2192 canConsolidate = false; 2193 break 2194 } 2195 } 2196 if (canConsolidate) { 2197 for (let j = 0; j < child.children.length; j++) { 2198 const grandChild = child.children[j]; 2199 copyChar(newRow, grandChild); 2200 } 2201 } else { 2202 newRow.children.push(child); 2203 } 2204 } else { 2205 newRow.children.push(child); 2206 } 2207 } 2208 for (let i = 0; i < newRow.children.length; i++) { 2209 if (newRow.children[i].type === "mtext") { 2210 const mtext = newRow.children[i]; 2211 // Firefox does not render a space at either end of an <mtext> string. 2212 // To get proper rendering, we replace leading or trailing spaces with no-break spaces. 2213 if (mtext.children[0].text.charAt(0) === " ") { 2214 mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1); 2215 } 2216 const L = mtext.children[0].text.length; 2217 if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") { 2218 mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0"; 2219 } 2220 for (const [key, value] of Object.entries(mrow.attributes)) { 2221 mtext.attributes[key] = value; 2222 } 2223 } 2224 } 2225 if (newRow.children.length === 1 && newRow.children[0].type === "mtext") { 2226 return newRow.children[0]; // A consolidated <mtext> 2227 } else { 2228 return newRow 2229 } 2230 }; 2231 2232 /** 2233 * Wrap the given array of nodes in an <mrow> node if needed, i.e., 2234 * unless the array has length 1. Always returns a single node. 2235 */ 2236 const makeRow = function(body, semisimple = false) { 2237 if (body.length === 1 && !(body[0] instanceof DocumentFragment)) { 2238 return body[0]; 2239 } else if (!semisimple) { 2240 // Suppress spacing on <mo> nodes at both ends of the row. 2241 if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) { 2242 body[0].attributes.lspace = "0em"; 2243 body[0].attributes.rspace = "0em"; 2244 } 2245 const end = body.length - 1; 2246 if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) { 2247 body[end].attributes.lspace = "0em"; 2248 body[end].attributes.rspace = "0em"; 2249 } 2250 } 2251 return new mathMLTree.MathNode("mrow", body); 2252 }; 2253 2254 /** 2255 * Check for <mi>.</mi> which is how a dot renders in MathML, 2256 * or <mo separator="true" lspace="0em" rspace="0em">,</mo> 2257 * which is how a braced comma {,} renders in MathML 2258 */ 2259 function isNumberPunctuation(group) { 2260 if (!group) { 2261 return false 2262 } 2263 if (group.type === 'mi' && group.children.length === 1) { 2264 const child = group.children[0]; 2265 return child instanceof TextNode && child.text === '.' 2266 } else if (group.type === "mtext" && group.children.length === 1) { 2267 const child = group.children[0]; 2268 return child instanceof TextNode && child.text === '\u2008' // punctuation space 2269 } else if (group.type === 'mo' && group.children.length === 1 && 2270 group.getAttribute('separator') === 'true' && 2271 group.getAttribute('lspace') === '0em' && 2272 group.getAttribute('rspace') === '0em') { 2273 const child = group.children[0]; 2274 return child instanceof TextNode && child.text === ',' 2275 } else { 2276 return false 2277 } 2278 } 2279 const isComma = (expression, i) => { 2280 const node = expression[i]; 2281 const followingNode = expression[i + 1]; 2282 return (node.type === "atom" && node.text === ",") && 2283 // Don't consolidate if there is a space after the comma. 2284 node.loc && followingNode.loc && node.loc.end === followingNode.loc.start 2285 }; 2286 2287 const isRel = item => { 2288 return (item.type === "atom" && item.family === "rel") || 2289 (item.type === "mclass" && item.mclass === "mrel") 2290 }; 2291 2292 /** 2293 * Takes a list of nodes, builds them, and returns a list of the generated 2294 * MathML nodes. Also do a couple chores along the way: 2295 * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}. 2296 * (2) Suppress spacing between two adjacent relations. 2297 */ 2298 const buildExpression = function(expression, style, semisimple = false) { 2299 if (!semisimple && expression.length === 1) { 2300 const group = buildGroup$1(expression[0], style); 2301 if (group instanceof MathNode && group.type === "mo") { 2302 // When TeX writers want to suppress spacing on an operator, 2303 // they often put the operator by itself inside braces. 2304 group.setAttribute("lspace", "0em"); 2305 group.setAttribute("rspace", "0em"); 2306 } 2307 return [group]; 2308 } 2309 2310 const groups = []; 2311 const groupArray = []; 2312 let lastGroup; 2313 for (let i = 0; i < expression.length; i++) { 2314 groupArray.push(buildGroup$1(expression[i], style)); 2315 } 2316 2317 for (let i = 0; i < groupArray.length; i++) { 2318 const group = groupArray[i]; 2319 2320 // Suppress spacing between adjacent relations 2321 if (i < expression.length - 1 && isRel(expression[i]) && isRel(expression[i + 1])) { 2322 group.setAttribute("rspace", "0em"); 2323 } 2324 if (i > 0 && isRel(expression[i]) && isRel(expression[i - 1])) { 2325 group.setAttribute("lspace", "0em"); 2326 } 2327 2328 // Concatenate numbers 2329 if (group.type === 'mn' && lastGroup && lastGroup.type === 'mn') { 2330 // Concatenate <mn>...</mn> followed by <mi>.</mi> 2331 lastGroup.children.push(...group.children); 2332 continue 2333 } else if (isNumberPunctuation(group) && lastGroup && lastGroup.type === 'mn') { 2334 // Concatenate <mn>...</mn> followed by <mi>.</mi> 2335 lastGroup.children.push(...group.children); 2336 continue 2337 } else if (lastGroup && lastGroup.type === "mn" && i < groupArray.length - 1 && 2338 groupArray[i + 1].type === "mn" && isComma(expression, i)) { 2339 lastGroup.children.push(...group.children); 2340 continue 2341 } else if (group.type === 'mn' && isNumberPunctuation(lastGroup)) { 2342 // Concatenate <mi>.</mi> followed by <mn>...</mn> 2343 group.children = [...lastGroup.children, ...group.children]; 2344 groups.pop(); 2345 } else if ((group.type === 'msup' || group.type === 'msub') && 2346 group.children.length >= 1 && lastGroup && 2347 (lastGroup.type === 'mn' || isNumberPunctuation(lastGroup))) { 2348 // Put preceding <mn>...</mn> or <mi>.</mi> inside base of 2349 // <msup><mn>...base...</mn>...exponent...</msup> (or <msub>) 2350 const base = group.children[0]; 2351 if (base instanceof MathNode && base.type === 'mn' && lastGroup) { 2352 base.children = [...lastGroup.children, ...base.children]; 2353 groups.pop(); 2354 } 2355 } 2356 groups.push(group); 2357 lastGroup = group; 2358 } 2359 return groups 2360 }; 2361 2362 /** 2363 * Equivalent to buildExpression, but wraps the elements in an <mrow> 2364 * if there's more than one. Returns a single node instead of an array. 2365 */ 2366 const buildExpressionRow = function(expression, style, semisimple = false) { 2367 return makeRow(buildExpression(expression, style, semisimple), semisimple); 2368 }; 2369 2370 /** 2371 * Takes a group from the parser and calls the appropriate groupBuilders function 2372 * on it to produce a MathML node. 2373 */ 2374 const buildGroup$1 = function(group, style) { 2375 if (!group) { 2376 return new mathMLTree.MathNode("mrow"); 2377 } 2378 2379 if (_mathmlGroupBuilders[group.type]) { 2380 // Call the groupBuilders function 2381 const result = _mathmlGroupBuilders[group.type](group, style); 2382 return result; 2383 } else { 2384 throw new ParseError("Got group of unknown type: '" + group.type + "'"); 2385 } 2386 }; 2387 2388 const glue$1 = _ => { 2389 return new mathMLTree.MathNode("mtd", [], [], { padding: "0", width: "50%" }) 2390 }; 2391 2392 const labelContainers = ["mrow", "mtd", "mtable", "mtr"]; 2393 const getLabel = parent => { 2394 for (const node of parent.children) { 2395 if (node.type && labelContainers.includes(node.type)) { 2396 if (node.classes && node.classes[0] === "tml-label") { 2397 const label = node.label; 2398 return label 2399 } else { 2400 const label = getLabel(node); 2401 if (label) { return label } 2402 } 2403 } else if (!node.type) { 2404 const label = getLabel(node); 2405 if (label) { return label } 2406 } 2407 } 2408 }; 2409 2410 const taggedExpression = (expression, tag, style, leqno) => { 2411 tag = buildExpressionRow(tag[0].body, style); 2412 tag = consolidateText(tag); 2413 tag.classes.push("tml-tag"); 2414 2415 const label = getLabel(expression); // from a \label{} function. 2416 expression = new mathMLTree.MathNode("mtd", [expression]); 2417 const rowArray = [glue$1(), expression, glue$1()]; 2418 rowArray[leqno ? 0 : 2].classes.push(leqno ? "tml-left" : "tml-right"); 2419 rowArray[leqno ? 0 : 2].children.push(tag); 2420 const mtr = new mathMLTree.MathNode("mtr", rowArray, ["tml-tageqn"]); 2421 if (label) { mtr.setAttribute("id", label); } 2422 const table = new mathMLTree.MathNode("mtable", [mtr]); 2423 table.style.width = "100%"; 2424 table.setAttribute("displaystyle", "true"); 2425 return table 2426 }; 2427 2428 /** 2429 * Takes a full parse tree and settings and builds a MathML representation of 2430 * it. 2431 */ 2432 function buildMathML(tree, texExpression, style, settings) { 2433 // Strip off outer tag wrapper for processing below. 2434 let tag = null; 2435 if (tree.length === 1 && tree[0].type === "tag") { 2436 tag = tree[0].tag; 2437 tree = tree[0].body; 2438 } 2439 2440 const expression = buildExpression(tree, style); 2441 2442 if (expression.length === 1 && expression[0] instanceof AnchorNode) { 2443 return expression[0] 2444 } 2445 2446 const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap; 2447 2448 const n1 = expression.length === 0 ? null : expression[0]; 2449 let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode) 2450 ? expression[0] 2451 : setLineBreaks(expression, wrap, settings.displayMode); 2452 2453 if (tag) { 2454 wrapper = taggedExpression(wrapper, tag, style, settings.leqno); 2455 } 2456 2457 if (settings.annotate) { 2458 // Build a TeX annotation of the source 2459 const annotation = new mathMLTree.MathNode( 2460 "annotation", [new mathMLTree.TextNode(texExpression)]); 2461 annotation.setAttribute("encoding", "application/x-tex"); 2462 wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]); 2463 } 2464 2465 const math = new mathMLTree.MathNode("math", [wrapper]); 2466 2467 if (settings.xml) { 2468 math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML"); 2469 } 2470 if (wrapper.style.width) { 2471 math.style.width = "100%"; 2472 } 2473 if (settings.displayMode) { 2474 math.setAttribute("display", "block"); 2475 math.style.display = "block math"; // necessary in Chromium. 2476 // Firefox and Safari do not recognize display: "block math". 2477 // Set a class so that the CSS file can set display: block. 2478 math.classes = ["tml-display"]; 2479 } 2480 return math; 2481 } 2482 2483 const smalls = "acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚𝐜𝐞𝐠𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐮𝐯𝐰𝐱𝐲𝐳"; 2484 const talls = "ABCDEFGHIJKLMNOPQRSTUVWXYZbdfhkltΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩβδλζφθψ" 2485 + "𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐛𝐝𝐟𝐡𝐤𝐥𝐭"; 2486 const longSmalls = new Set(["\\alpha", "\\gamma", "\\delta", "\\epsilon", "\\eta", "\\iota", 2487 "\\kappa", "\\mu", "\\nu", "\\pi", "\\rho", "\\sigma", "\\tau", "\\upsilon", "\\chi", "\\psi", 2488 "\\omega", "\\imath", "\\jmath"]); 2489 const longTalls = new Set(["\\Gamma", "\\Delta", "\\Sigma", "\\Omega", "\\beta", "\\delta", 2490 "\\lambda", "\\theta", "\\psi"]); 2491 2492 const mathmlBuilder$a = (group, style) => { 2493 const accentNode = group.isStretchy 2494 ? stretchy.accentNode(group) 2495 : new mathMLTree.MathNode("mo", [makeText(group.label, group.mode)]); 2496 2497 if (group.label === "\\vec") { 2498 accentNode.style.transform = "scale(0.75) translate(10%, 30%)"; 2499 } else { 2500 accentNode.style.mathStyle = "normal"; 2501 accentNode.style.mathDepth = "0"; 2502 if (needWebkitShift.has(group.label) && utils.isCharacterBox(group.base)) { 2503 let shift = ""; 2504 const ch = group.base.text; 2505 if (smalls.indexOf(ch) > -1 || longSmalls.has(ch)) { shift = "tml-xshift"; } 2506 if (talls.indexOf(ch) > -1 || longTalls.has(ch)) { shift = "tml-capshift"; } 2507 if (shift) { accentNode.classes.push(shift); } 2508 } 2509 } 2510 if (!group.isStretchy) { 2511 accentNode.setAttribute("stretchy", "false"); 2512 } 2513 2514 const node = new mathMLTree.MathNode((group.label === "\\c" ? "munder" : "mover"), 2515 [buildGroup$1(group.base, style), accentNode] 2516 ); 2517 2518 return node; 2519 }; 2520 2521 const nonStretchyAccents = new Set([ 2522 "\\acute", 2523 "\\grave", 2524 "\\ddot", 2525 "\\dddot", 2526 "\\ddddot", 2527 "\\tilde", 2528 "\\bar", 2529 "\\breve", 2530 "\\check", 2531 "\\hat", 2532 "\\vec", 2533 "\\dot", 2534 "\\mathring" 2535 ]); 2536 2537 const needWebkitShift = new Set([ 2538 "\\acute", 2539 "\\bar", 2540 "\\breve", 2541 "\\check", 2542 "\\dot", 2543 "\\ddot", 2544 "\\grave", 2545 "\\hat", 2546 "\\mathring", 2547 "\\'", "\\^", "\\~", "\\=", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v" 2548 ]); 2549 2550 const combiningChar = { 2551 "\\`": "\u0300", 2552 "\\'": "\u0301", 2553 "\\^": "\u0302", 2554 "\\~": "\u0303", 2555 "\\=": "\u0304", 2556 "\\u": "\u0306", 2557 "\\.": "\u0307", 2558 '\\"': "\u0308", 2559 "\\r": "\u030A", 2560 "\\H": "\u030B", 2561 "\\v": "\u030C" 2562 }; 2563 2564 // Accents 2565 defineFunction({ 2566 type: "accent", 2567 names: [ 2568 "\\acute", 2569 "\\grave", 2570 "\\ddot", 2571 "\\dddot", 2572 "\\ddddot", 2573 "\\tilde", 2574 "\\bar", 2575 "\\breve", 2576 "\\check", 2577 "\\hat", 2578 "\\vec", 2579 "\\dot", 2580 "\\mathring", 2581 "\\overparen", 2582 "\\widecheck", 2583 "\\widehat", 2584 "\\wideparen", 2585 "\\widetilde", 2586 "\\overrightarrow", 2587 "\\overleftarrow", 2588 "\\Overrightarrow", 2589 "\\overleftrightarrow", 2590 "\\overgroup", 2591 "\\overleftharpoon", 2592 "\\overrightharpoon" 2593 ], 2594 props: { 2595 numArgs: 1 2596 }, 2597 handler: (context, args) => { 2598 const base = normalizeArgument(args[0]); 2599 2600 const isStretchy = !nonStretchyAccents.has(context.funcName); 2601 2602 return { 2603 type: "accent", 2604 mode: context.parser.mode, 2605 label: context.funcName, 2606 isStretchy: isStretchy, 2607 base: base 2608 }; 2609 }, 2610 mathmlBuilder: mathmlBuilder$a 2611 }); 2612 2613 // Text-mode accents 2614 defineFunction({ 2615 type: "accent", 2616 names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\c", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"], 2617 props: { 2618 numArgs: 1, 2619 allowedInText: true, 2620 allowedInMath: true, 2621 argTypes: ["primitive"] 2622 }, 2623 handler: (context, args) => { 2624 const base = normalizeArgument(args[0]); 2625 const mode = context.parser.mode; 2626 2627 if (mode === "math" && context.parser.settings.strict) { 2628 // LaTeX only writes a warning. It doesn't stop. We'll issue the same warning. 2629 // eslint-disable-next-line no-console 2630 console.log(`Temml parse error: Command $context.funcName} is invalid in math mode.`); 2631 } 2632 2633 if (mode === "text" && base.text && base.text.length === 1 2634 && context.funcName in combiningChar && smalls.indexOf(base.text) > -1) { 2635 // Return a combining accent character 2636 return { 2637 type: "textord", 2638 mode: "text", 2639 text: base.text + combiningChar[context.funcName] 2640 } 2641 } else { 2642 // Build up the accent 2643 return { 2644 type: "accent", 2645 mode: mode, 2646 label: context.funcName, 2647 isStretchy: false, 2648 base: base 2649 } 2650 } 2651 }, 2652 mathmlBuilder: mathmlBuilder$a 2653 }); 2654 2655 defineFunction({ 2656 type: "accentUnder", 2657 names: [ 2658 "\\underleftarrow", 2659 "\\underrightarrow", 2660 "\\underleftrightarrow", 2661 "\\undergroup", 2662 "\\underparen", 2663 "\\utilde" 2664 ], 2665 props: { 2666 numArgs: 1 2667 }, 2668 handler: ({ parser, funcName }, args) => { 2669 const base = args[0]; 2670 return { 2671 type: "accentUnder", 2672 mode: parser.mode, 2673 label: funcName, 2674 base: base 2675 }; 2676 }, 2677 mathmlBuilder: (group, style) => { 2678 const accentNode = stretchy.accentNode(group); 2679 accentNode.style["math-depth"] = 0; 2680 const node = new mathMLTree.MathNode("munder", [ 2681 buildGroup$1(group.base, style), 2682 accentNode 2683 ]); 2684 return node; 2685 } 2686 }); 2687 2688 /** 2689 * This file does conversion between units. In particular, it provides 2690 * calculateSize to convert other units into CSS units. 2691 */ 2692 2693 2694 const ptPerUnit = { 2695 // Convert to CSS (Postscipt) points, not TeX points 2696 // https://en.wikibooks.org/wiki/LaTeX/Lengths and 2697 // https://tex.stackexchange.com/a/8263 2698 pt: 800 / 803, // convert TeX point to CSS (Postscript) point 2699 pc: (12 * 800) / 803, // pica 2700 dd: ((1238 / 1157) * 800) / 803, // didot 2701 cc: ((14856 / 1157) * 800) / 803, // cicero (12 didot) 2702 nd: ((685 / 642) * 800) / 803, // new didot 2703 nc: ((1370 / 107) * 800) / 803, // new cicero (12 new didot) 2704 sp: ((1 / 65536) * 800) / 803, // scaled point (TeX's internal smallest unit) 2705 mm: (25.4 / 72), 2706 cm: (2.54 / 72), 2707 in: (1 / 72), 2708 px: (96 / 72) 2709 }; 2710 2711 /** 2712 * Determine whether the specified unit (either a string defining the unit 2713 * or a "size" parse node containing a unit field) is valid. 2714 */ 2715 const validUnits = [ 2716 "em", 2717 "ex", 2718 "mu", 2719 "pt", 2720 "mm", 2721 "cm", 2722 "in", 2723 "px", 2724 "bp", 2725 "pc", 2726 "dd", 2727 "cc", 2728 "nd", 2729 "nc", 2730 "sp" 2731 ]; 2732 2733 const validUnit = function(unit) { 2734 if (typeof unit !== "string") { 2735 unit = unit.unit; 2736 } 2737 return validUnits.indexOf(unit) > -1 2738 }; 2739 2740 const emScale = styleLevel => { 2741 const scriptLevel = Math.max(styleLevel - 1, 0); 2742 return [1, 0.7, 0.5][scriptLevel] 2743 }; 2744 2745 /* 2746 * Convert a "size" parse node (with numeric "number" and string "unit" fields, 2747 * as parsed by functions.js argType "size") into a CSS value. 2748 */ 2749 const calculateSize = function(sizeValue, style) { 2750 let number = sizeValue.number; 2751 if (style.maxSize[0] < 0 && number > 0) { 2752 return { number: 0, unit: "em" } 2753 } 2754 const unit = sizeValue.unit; 2755 switch (unit) { 2756 case "mm": 2757 case "cm": 2758 case "in": 2759 case "px": { 2760 const numInCssPts = number * ptPerUnit[unit]; 2761 if (numInCssPts > style.maxSize[1]) { 2762 return { number: style.maxSize[1], unit: "pt" } 2763 } 2764 return { number, unit }; // absolute CSS units. 2765 } 2766 case "em": 2767 case "ex": { 2768 // In TeX, em and ex do not change size in \scriptstyle. 2769 if (unit === "ex") { number *= 0.431; } 2770 number = Math.min(number / emScale(style.level), style.maxSize[0]); 2771 return { number: utils.round(number), unit: "em" }; 2772 } 2773 case "bp": { 2774 if (number > style.maxSize[1]) { number = style.maxSize[1]; } 2775 return { number, unit: "pt" }; // TeX bp is a CSS pt. (1/72 inch). 2776 } 2777 case "pt": 2778 case "pc": 2779 case "dd": 2780 case "cc": 2781 case "nd": 2782 case "nc": 2783 case "sp": { 2784 number = Math.min(number * ptPerUnit[unit], style.maxSize[1]); 2785 return { number: utils.round(number), unit: "pt" } 2786 } 2787 case "mu": { 2788 number = Math.min(number / 18, style.maxSize[0]); 2789 return { number: utils.round(number), unit: "em" } 2790 } 2791 default: 2792 throw new ParseError("Invalid unit: '" + unit + "'") 2793 } 2794 }; 2795 2796 // Helper functions 2797 2798 const padding$2 = width => { 2799 const node = new mathMLTree.MathNode("mspace"); 2800 node.setAttribute("width", width + "em"); 2801 return node 2802 }; 2803 2804 const paddedNode = (group, lspace = 0.3, rspace = 0, mustSmash = false) => { 2805 if (group == null && rspace === 0) { return padding$2(lspace) } 2806 const row = group ? [group] : []; 2807 if (lspace !== 0) { row.unshift(padding$2(lspace)); } 2808 if (rspace > 0) { row.push(padding$2(rspace)); } 2809 if (mustSmash) { 2810 // Used for the bottom arrow in a {CD} environment 2811 const mpadded = new mathMLTree.MathNode("mpadded", row); 2812 mpadded.setAttribute("height", "0"); 2813 return mpadded 2814 } else { 2815 return new mathMLTree.MathNode("mrow", row) 2816 } 2817 }; 2818 2819 const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel); 2820 2821 const munderoverNode = (fName, body, below, style) => { 2822 const arrowNode = stretchy.mathMLnode(fName); 2823 // Is this the short part of a mhchem equilibrium arrow? 2824 const isEq = fName.slice(1, 3) === "eq"; 2825 const minWidth = fName.charAt(1) === "x" 2826 ? "1.75" // mathtools extensible arrows are ≥ 1.75em long 2827 : fName.slice(2, 4) === "cd" 2828 ? "3.0" // cd package arrows 2829 : isEq 2830 ? "1.0" // The shorter harpoon of a mhchem equilibrium arrow 2831 : "2.0"; // other mhchem arrows 2832 // TODO: When Firefox supports minsize, use the next line. 2833 //arrowNode.setAttribute("minsize", String(minWidth) + "em") 2834 arrowNode.setAttribute("lspace", "0"); 2835 arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0")); 2836 2837 // <munderover> upper and lower labels are set to scriptlevel by MathML 2838 // So we have to adjust our label dimensions accordingly. 2839 const labelStyle = style.withLevel(style.level < 2 ? 2 : 3); 2840 const minArrowWidth = labelSize(minWidth, labelStyle.level); 2841 // The dummyNode will be inside a <mover> inside a <mover> 2842 // So it will be at scriptlevel 3 2843 const dummyWidth = labelSize(minWidth, 3); 2844 const emptyLabel = paddedNode(null, minArrowWidth.toFixed(4), 0); 2845 const dummyNode = paddedNode(null, dummyWidth.toFixed(4), 0); 2846 // The arrow is a little longer than the label. Set a spacer length. 2847 const space = labelSize((isEq ? 0 : 0.3), labelStyle.level).toFixed(4); 2848 let upperNode; 2849 let lowerNode; 2850 2851 const gotUpper = (body && body.body && 2852 // \hphantom visible content 2853 (body.body.body || body.body.length > 0)); 2854 if (gotUpper) { 2855 let label = buildGroup$1(body, labelStyle); 2856 const mustSmash = (fName === "\\\\cdrightarrow" || fName === "\\\\cdleftarrow"); 2857 label = paddedNode(label, space, space, mustSmash); 2858 // Since Firefox does not support minsize, stack a invisible node 2859 // on top of the label. Its width will serve as a min-width. 2860 // TODO: Refactor this after Firefox supports minsize. 2861 upperNode = new mathMLTree.MathNode("mover", [label, dummyNode]); 2862 } 2863 const gotLower = (below && below.body && 2864 (below.body.body || below.body.length > 0)); 2865 if (gotLower) { 2866 let label = buildGroup$1(below, labelStyle); 2867 label = paddedNode(label, space, space); 2868 lowerNode = new mathMLTree.MathNode("munder", [label, dummyNode]); 2869 } 2870 2871 let node; 2872 if (!gotUpper && !gotLower) { 2873 node = new mathMLTree.MathNode("mover", [arrowNode, emptyLabel]); 2874 } else if (gotUpper && gotLower) { 2875 node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]); 2876 } else if (gotUpper) { 2877 node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]); 2878 } else { 2879 node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]); 2880 } 2881 if (minWidth === "3.0") { node.style.height = "1em"; } // CD environment 2882 node.setAttribute("accent", "false"); // Necessary for MS Word 2883 return node 2884 }; 2885 2886 // Stretchy arrows with an optional argument 2887 defineFunction({ 2888 type: "xArrow", 2889 names: [ 2890 "\\xleftarrow", 2891 "\\xrightarrow", 2892 "\\xLeftarrow", 2893 "\\xRightarrow", 2894 "\\xleftrightarrow", 2895 "\\xLeftrightarrow", 2896 "\\xhookleftarrow", 2897 "\\xhookrightarrow", 2898 "\\xmapsto", 2899 "\\xrightharpoondown", 2900 "\\xrightharpoonup", 2901 "\\xleftharpoondown", 2902 "\\xleftharpoonup", 2903 "\\xlongequal", 2904 "\\xtwoheadrightarrow", 2905 "\\xtwoheadleftarrow", 2906 // The next 5 functions are here only to support mhchem 2907 "\\yields", 2908 "\\yieldsLeft", 2909 "\\mesomerism", 2910 "\\longrightharpoonup", 2911 "\\longleftharpoondown", 2912 // The next 3 functions are here only to support the {CD} environment. 2913 "\\\\cdrightarrow", 2914 "\\\\cdleftarrow", 2915 "\\\\cdlongequal" 2916 ], 2917 props: { 2918 numArgs: 1, 2919 numOptionalArgs: 1 2920 }, 2921 handler({ parser, funcName }, args, optArgs) { 2922 return { 2923 type: "xArrow", 2924 mode: parser.mode, 2925 name: funcName, 2926 body: args[0], 2927 below: optArgs[0] 2928 }; 2929 }, 2930 mathmlBuilder(group, style) { 2931 // Build the arrow and its labels. 2932 const node = munderoverNode(group.name, group.body, group.below, style); 2933 // Create operator spacing for a relation. 2934 const row = [node]; 2935 row.unshift(padding$2(0.2778)); 2936 row.push(padding$2(0.2778)); 2937 return new mathMLTree.MathNode("mrow", row) 2938 } 2939 }); 2940 2941 const arrowComponent = { 2942 "\\xtofrom": ["\\xrightarrow", "\\xleftarrow"], 2943 "\\xleftrightharpoons": ["\\xleftharpoonup", "\\xrightharpoondown"], 2944 "\\xrightleftharpoons": ["\\xrightharpoonup", "\\xleftharpoondown"], 2945 "\\yieldsLeftRight": ["\\yields", "\\yieldsLeft"], 2946 // The next three all get the same harpoon glyphs. Only the lengths and paddings differ. 2947 "\\equilibrium": ["\\longrightharpoonup", "\\longleftharpoondown"], 2948 "\\equilibriumRight": ["\\longrightharpoonup", "\\eqleftharpoondown"], 2949 "\\equilibriumLeft": ["\\eqrightharpoonup", "\\longleftharpoondown"] 2950 }; 2951 2952 // Browsers are not good at stretching a glyph that contains a pair of stacked arrows such as ⇄. 2953 // So we stack a pair of single arrows. 2954 defineFunction({ 2955 type: "stackedArrow", 2956 names: [ 2957 "\\xtofrom", // expfeil 2958 "\\xleftrightharpoons", // mathtools 2959 "\\xrightleftharpoons", // mathtools 2960 "\\yieldsLeftRight", // mhchem 2961 "\\equilibrium", // mhchem 2962 "\\equilibriumRight", 2963 "\\equilibriumLeft" 2964 ], 2965 props: { 2966 numArgs: 1, 2967 numOptionalArgs: 1 2968 }, 2969 handler({ parser, funcName }, args, optArgs) { 2970 const lowerArrowBody = args[0] 2971 ? { 2972 type: "hphantom", 2973 mode: parser.mode, 2974 body: args[0] 2975 } 2976 : null; 2977 const upperArrowBelow = optArgs[0] 2978 ? { 2979 type: "hphantom", 2980 mode: parser.mode, 2981 body: optArgs[0] 2982 } 2983 : null; 2984 return { 2985 type: "stackedArrow", 2986 mode: parser.mode, 2987 name: funcName, 2988 body: args[0], 2989 upperArrowBelow, 2990 lowerArrowBody, 2991 below: optArgs[0] 2992 }; 2993 }, 2994 mathmlBuilder(group, style) { 2995 const topLabel = arrowComponent[group.name][0]; 2996 const botLabel = arrowComponent[group.name][1]; 2997 const topArrow = munderoverNode(topLabel, group.body, group.upperArrowBelow, style); 2998 const botArrow = munderoverNode(botLabel, group.lowerArrowBody, group.below, style); 2999 let wrapper; 3000 3001 const raiseNode = new mathMLTree.MathNode("mpadded", [topArrow]); 3002 raiseNode.setAttribute("voffset", "0.3em"); 3003 raiseNode.setAttribute("height", "+0.3em"); 3004 raiseNode.setAttribute("depth", "-0.3em"); 3005 // One of the arrows is given ~zero width. so the other has the same horzontal alignment. 3006 if (group.name === "\\equilibriumLeft") { 3007 const botNode = new mathMLTree.MathNode("mpadded", [botArrow]); 3008 botNode.setAttribute("width", "0.5em"); 3009 wrapper = new mathMLTree.MathNode( 3010 "mpadded", 3011 [padding$2(0.2778), botNode, raiseNode, padding$2(0.2778)] 3012 ); 3013 } else { 3014 raiseNode.setAttribute("width", (group.name === "\\equilibriumRight" ? "0.5em" : "0")); 3015 wrapper = new mathMLTree.MathNode( 3016 "mpadded", 3017 [padding$2(0.2778), raiseNode, botArrow, padding$2(0.2778)] 3018 ); 3019 } 3020 3021 wrapper.setAttribute("voffset", "-0.18em"); 3022 wrapper.setAttribute("height", "-0.18em"); 3023 wrapper.setAttribute("depth", "+0.18em"); 3024 return wrapper 3025 } 3026 }); 3027 3028 /** 3029 * Asserts that the node is of the given type and returns it with stricter 3030 * typing. Throws if the node's type does not match. 3031 */ 3032 function assertNodeType(node, type) { 3033 if (!node || node.type !== type) { 3034 throw new Error( 3035 `Expected node of type $type}, but got ` + 3036 (node ? `node of type $node.type}` : String(node)) 3037 ); 3038 } 3039 return node; 3040 } 3041 3042 /** 3043 * Returns the node more strictly typed iff it is of the given type. Otherwise, 3044 * returns null. 3045 */ 3046 function assertSymbolNodeType(node) { 3047 const typedNode = checkSymbolNodeType(node); 3048 if (!typedNode) { 3049 throw new Error( 3050 `Expected node of symbol group type, but got ` + 3051 (node ? `node of type $node.type}` : String(node)) 3052 ); 3053 } 3054 return typedNode; 3055 } 3056 3057 /** 3058 * Returns the node more strictly typed iff it is of the given type. Otherwise, 3059 * returns null. 3060 */ 3061 function checkSymbolNodeType(node) { 3062 if (node && (node.type === "atom" || 3063 Object.prototype.hasOwnProperty.call(NON_ATOMS, node.type))) { 3064 return node; 3065 } 3066 return null; 3067 } 3068 3069 const cdArrowFunctionName = { 3070 ">": "\\\\cdrightarrow", 3071 "<": "\\\\cdleftarrow", 3072 "=": "\\\\cdlongequal", 3073 A: "\\uparrow", 3074 V: "\\downarrow", 3075 "|": "\\Vert", 3076 ".": "no arrow" 3077 }; 3078 3079 const newCell = () => { 3080 // Create an empty cell, to be filled below with parse nodes. 3081 return { type: "styling", body: [], mode: "math", scriptLevel: "display" }; 3082 }; 3083 3084 const isStartOfArrow = (node) => { 3085 return node.type === "textord" && node.text === "@"; 3086 }; 3087 3088 const isLabelEnd = (node, endChar) => { 3089 return (node.type === "mathord" || node.type === "atom") && node.text === endChar; 3090 }; 3091 3092 function cdArrow(arrowChar, labels, parser) { 3093 // Return a parse tree of an arrow and its labels. 3094 // This acts in a way similar to a macro expansion. 3095 const funcName = cdArrowFunctionName[arrowChar]; 3096 switch (funcName) { 3097 case "\\\\cdrightarrow": 3098 case "\\\\cdleftarrow": 3099 return parser.callFunction(funcName, [labels[0]], [labels[1]]); 3100 case "\\uparrow": 3101 case "\\downarrow": { 3102 const leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []); 3103 const bareArrow = { 3104 type: "atom", 3105 text: funcName, 3106 mode: "math", 3107 family: "rel" 3108 }; 3109 const sizedArrow = parser.callFunction("\\Big", [bareArrow], []); 3110 const rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []); 3111 const arrowGroup = { 3112 type: "ordgroup", 3113 mode: "math", 3114 body: [leftLabel, sizedArrow, rightLabel], 3115 semisimple: true 3116 }; 3117 return parser.callFunction("\\\\cdparent", [arrowGroup], []); 3118 } 3119 case "\\\\cdlongequal": 3120 return parser.callFunction("\\\\cdlongequal", [], []); 3121 case "\\Vert": { 3122 const arrow = { type: "textord", text: "\\Vert", mode: "math" }; 3123 return parser.callFunction("\\Big", [arrow], []); 3124 } 3125 default: 3126 return { type: "textord", text: " ", mode: "math" }; 3127 } 3128 } 3129 3130 function parseCD(parser) { 3131 // Get the array's parse nodes with \\ temporarily mapped to \cr. 3132 const parsedRows = []; 3133 parser.gullet.beginGroup(); 3134 parser.gullet.macros.set("\\cr", "\\\\\\relax"); 3135 parser.gullet.beginGroup(); 3136 while (true) { 3137 // Get the parse nodes for the next row. 3138 parsedRows.push(parser.parseExpression(false, "\\\\")); 3139 parser.gullet.endGroup(); 3140 parser.gullet.beginGroup(); 3141 const next = parser.fetch().text; 3142 if (next === "&" || next === "\\\\") { 3143 parser.consume(); 3144 } else if (next === "\\end") { 3145 if (parsedRows[parsedRows.length - 1].length === 0) { 3146 parsedRows.pop(); // final row ended in \\ 3147 } 3148 break; 3149 } else { 3150 throw new ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken); 3151 } 3152 } 3153 3154 let row = []; 3155 const body = [row]; 3156 3157 // Loop thru the parse nodes. Collect them into cells and arrows. 3158 for (let i = 0; i < parsedRows.length; i++) { 3159 // Start a new row. 3160 const rowNodes = parsedRows[i]; 3161 // Create the first cell. 3162 let cell = newCell(); 3163 3164 for (let j = 0; j < rowNodes.length; j++) { 3165 if (!isStartOfArrow(rowNodes[j])) { 3166 // If a parseNode is not an arrow, it goes into a cell. 3167 cell.body.push(rowNodes[j]); 3168 } else { 3169 // Parse node j is an "@", the start of an arrow. 3170 // Before starting on the arrow, push the cell into `row`. 3171 row.push(cell); 3172 3173 // Now collect parseNodes into an arrow. 3174 // The character after "@" defines the arrow type. 3175 j += 1; 3176 const arrowChar = assertSymbolNodeType(rowNodes[j]).text; 3177 3178 // Create two empty label nodes. We may or may not use them. 3179 const labels = new Array(2); 3180 labels[0] = { type: "ordgroup", mode: "math", body: [] }; 3181 labels[1] = { type: "ordgroup", mode: "math", body: [] }; 3182 3183 // Process the arrow. 3184 if ("=|.".indexOf(arrowChar) > -1) ; else if ("<>AV".indexOf(arrowChar) > -1) { 3185 // Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take 3186 // two optional labels. E.g. the right-point arrow syntax is 3187 // really: @>{optional label}>{optional label}> 3188 // Collect parseNodes into labels. 3189 for (let labelNum = 0; labelNum < 2; labelNum++) { 3190 let inLabel = true; 3191 for (let k = j + 1; k < rowNodes.length; k++) { 3192 if (isLabelEnd(rowNodes[k], arrowChar)) { 3193 inLabel = false; 3194 j = k; 3195 break; 3196 } 3197 if (isStartOfArrow(rowNodes[k])) { 3198 throw new ParseError( 3199 "Missing a " + arrowChar + " character to complete a CD arrow.", 3200 rowNodes[k] 3201 ); 3202 } 3203 3204 labels[labelNum].body.push(rowNodes[k]); 3205 } 3206 if (inLabel) { 3207 // isLabelEnd never returned a true. 3208 throw new ParseError( 3209 "Missing a " + arrowChar + " character to complete a CD arrow.", 3210 rowNodes[j] 3211 ); 3212 } 3213 } 3214 } else { 3215 throw new ParseError(`Expected one of "<>AV=|." after @.`); 3216 } 3217 3218 // Now join the arrow to its labels. 3219 const arrow = cdArrow(arrowChar, labels, parser); 3220 3221 // Wrap the arrow in a styling node 3222 row.push(arrow); 3223 // In CD's syntax, cells are implicit. That is, everything that 3224 // is not an arrow gets collected into a cell. So create an empty 3225 // cell now. It will collect upcoming parseNodes. 3226 cell = newCell(); 3227 } 3228 } 3229 if (i % 2 === 0) { 3230 // Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell 3231 // The last cell is not yet pushed into `row`, so: 3232 row.push(cell); 3233 } else { 3234 // Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow 3235 // Remove the empty cell that was placed at the beginning of `row`. 3236 row.shift(); 3237 } 3238 row = []; 3239 body.push(row); 3240 } 3241 body.pop(); 3242 3243 // End row group 3244 parser.gullet.endGroup(); 3245 // End array group defining \\ 3246 parser.gullet.endGroup(); 3247 3248 return { 3249 type: "array", 3250 mode: "math", 3251 body, 3252 tags: null, 3253 labels: new Array(body.length + 1).fill(""), 3254 envClasses: ["jot", "cd"], 3255 cols: [], 3256 hLinesBeforeRow: new Array(body.length + 1).fill([]) 3257 }; 3258 } 3259 3260 // The functions below are not available for general use. 3261 // They are here only for internal use by the {CD} environment in placing labels 3262 // next to vertical arrows. 3263 3264 // We don't need any such functions for horizontal arrows because we can reuse 3265 // the functionality that already exists for extensible arrows. 3266 3267 defineFunction({ 3268 type: "cdlabel", 3269 names: ["\\\\cdleft", "\\\\cdright"], 3270 props: { 3271 numArgs: 1 3272 }, 3273 handler({ parser, funcName }, args) { 3274 return { 3275 type: "cdlabel", 3276 mode: parser.mode, 3277 side: funcName.slice(4), 3278 label: args[0] 3279 }; 3280 }, 3281 mathmlBuilder(group, style) { 3282 if (group.label.body.length === 0) { 3283 return new mathMLTree.MathNode("mrow", style) // empty label 3284 } 3285 // Abuse an <mtable> to create vertically centered content. 3286 const mtd = new mathMLTree.MathNode("mtd", [buildGroup$1(group.label, style)]); 3287 mtd.style.padding = "0"; 3288 const mtr = new mathMLTree.MathNode("mtr", [mtd]); 3289 const mtable = new mathMLTree.MathNode("mtable", [mtr]); 3290 const label = new mathMLTree.MathNode("mpadded", [mtable]); 3291 // Set the label width to zero so that the arrow will be centered under the corner cell. 3292 label.setAttribute("width", "0"); 3293 label.setAttribute("displaystyle", "false"); 3294 label.setAttribute("scriptlevel", "1"); 3295 if (group.side === "left") { 3296 label.style.display = "flex"; 3297 label.style.justifyContent = "flex-end"; 3298 } 3299 return label; 3300 } 3301 }); 3302 3303 defineFunction({ 3304 type: "cdlabelparent", 3305 names: ["\\\\cdparent"], 3306 props: { 3307 numArgs: 1 3308 }, 3309 handler({ parser }, args) { 3310 return { 3311 type: "cdlabelparent", 3312 mode: parser.mode, 3313 fragment: args[0] 3314 }; 3315 }, 3316 mathmlBuilder(group, style) { 3317 return new mathMLTree.MathNode("mrow", [buildGroup$1(group.fragment, style)]); 3318 } 3319 }); 3320 3321 // \@char is an internal function that takes a grouped decimal argument like 3322 // {123} and converts into symbol with code 123. It is used by the *macro* 3323 // \char defined in macros.js. 3324 defineFunction({ 3325 type: "textord", 3326 names: ["\\@char"], 3327 props: { 3328 numArgs: 1, 3329 allowedInText: true 3330 }, 3331 handler({ parser, token }, args) { 3332 const arg = assertNodeType(args[0], "ordgroup"); 3333 const group = arg.body; 3334 let number = ""; 3335 for (let i = 0; i < group.length; i++) { 3336 const node = assertNodeType(group[i], "textord"); 3337 number += node.text; 3338 } 3339 const code = parseInt(number); 3340 if (isNaN(code)) { 3341 throw new ParseError(`\\@char has non-numeric argument $number}`, token) 3342 } 3343 return { 3344 type: "textord", 3345 mode: parser.mode, 3346 text: String.fromCodePoint(code) 3347 } 3348 } 3349 }); 3350 3351 // Helpers 3352 const htmlRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6})$/i; 3353 const htmlOrNameRegEx = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i; 3354 const RGBregEx = /^ *\d{1,3} *(?:, *\d{1,3} *){2}$/; 3355 const rgbRegEx = /^ *[10](?:\.\d*)? *(?:, *[10](?:\.\d*)? *){2}$/; 3356 const xcolorHtmlRegEx = /^[a-f0-9]{6}$/i; 3357 const toHex = num => { 3358 let str = num.toString(16); 3359 if (str.length === 1) { str = "0" + str; } 3360 return str 3361 }; 3362 3363 // Colors from Tables 4.1 and 4.2 of the xcolor package. 3364 // Table 4.1 (lower case) RGB values are taken from chroma and xcolor.dtx. 3365 // Table 4.2 (Capitalizzed) values were sampled, because Chroma contains a unreliable 3366 // conversion from cmyk to RGB. See https://tex.stackexchange.com/a/537274. 3367 const xcolors = JSON.parse(`{ 3368 "Apricot": "#ffb484", 3369 "Aquamarine": "#08b4bc", 3370 "Bittersweet": "#c84c14", 3371 "blue": "#0000FF", 3372 "Blue": "#303494", 3373 "BlueGreen": "#08b4bc", 3374 "BlueViolet": "#503c94", 3375 "BrickRed": "#b8341c", 3376 "brown": "#BF8040", 3377 "Brown": "#802404", 3378 "BurntOrange": "#f8941c", 3379 "CadetBlue": "#78749c", 3380 "CarnationPink": "#f884b4", 3381 "Cerulean": "#08a4e4", 3382 "CornflowerBlue": "#40ace4", 3383 "cyan": "#00FFFF", 3384 "Cyan": "#08acec", 3385 "Dandelion": "#ffbc44", 3386 "darkgray": "#404040", 3387 "DarkOrchid": "#a8548c", 3388 "Emerald": "#08ac9c", 3389 "ForestGreen": "#089c54", 3390 "Fuchsia": "#90348c", 3391 "Goldenrod": "#ffdc44", 3392 "gray": "#808080", 3393 "Gray": "#98949c", 3394 "green": "#00FF00", 3395 "Green": "#08a44c", 3396 "GreenYellow": "#e0e474", 3397 "JungleGreen": "#08ac9c", 3398 "Lavender": "#f89cc4", 3399 "lightgray": "#c0c0c0", 3400 "lime": "#BFFF00", 3401 "LimeGreen": "#90c43c", 3402 "magenta": "#FF00FF", 3403 "Magenta": "#f0048c", 3404 "Mahogany": "#b0341c", 3405 "Maroon": "#b03434", 3406 "Melon": "#f89c7c", 3407 "MidnightBlue": "#086494", 3408 "Mulberry": "#b03c94", 3409 "NavyBlue": "#086cbc", 3410 "olive": "#7F7F00", 3411 "OliveGreen": "#407c34", 3412 "orange": "#FF8000", 3413 "Orange": "#f8843c", 3414 "OrangeRed": "#f0145c", 3415 "Orchid": "#b074ac", 3416 "Peach": "#f8945c", 3417 "Periwinkle": "#8074bc", 3418 "PineGreen": "#088c74", 3419 "pink": "#ff7f7f", 3420 "Plum": "#98248c", 3421 "ProcessBlue": "#08b4ec", 3422 "purple": "#BF0040", 3423 "Purple": "#a0449c", 3424 "RawSienna": "#983c04", 3425 "red": "#ff0000", 3426 "Red": "#f01c24", 3427 "RedOrange": "#f86434", 3428 "RedViolet": "#a0246c", 3429 "Rhodamine": "#f0549c", 3430 "Royallue": "#0874bc", 3431 "RoyalPurple": "#683c9c", 3432 "RubineRed": "#f0047c", 3433 "Salmon": "#f8948c", 3434 "SeaGreen": "#30bc9c", 3435 "Sepia": "#701404", 3436 "SkyBlue": "#48c4dc", 3437 "SpringGreen": "#c8dc64", 3438 "Tan": "#e09c74", 3439 "teal": "#007F7F", 3440 "TealBlue": "#08acb4", 3441 "Thistle": "#d884b4", 3442 "Turquoise": "#08b4cc", 3443 "violet": "#800080", 3444 "Violet": "#60449c", 3445 "VioletRed": "#f054a4", 3446 "WildStrawberry": "#f0246c", 3447 "yellow": "#FFFF00", 3448 "Yellow": "#fff404", 3449 "YellowGreen": "#98cc6c", 3450 "YellowOrange": "#ffa41c" 3451 }`); 3452 3453 const colorFromSpec = (model, spec) => { 3454 let color = ""; 3455 if (model === "HTML") { 3456 if (!htmlRegEx.test(spec)) { 3457 throw new ParseError("Invalid HTML input.") 3458 } 3459 color = spec; 3460 } else if (model === "RGB") { 3461 if (!RGBregEx.test(spec)) { 3462 throw new ParseError("Invalid RGB input.") 3463 } 3464 spec.split(",").map(e => { color += toHex(Number(e.trim())); }); 3465 } else { 3466 if (!rgbRegEx.test(spec)) { 3467 throw new ParseError("Invalid rbg input.") 3468 } 3469 spec.split(",").map(e => { 3470 const num = Number(e.trim()); 3471 if (num > 1) { throw new ParseError("Color rgb input must be < 1.") } 3472 color += toHex(Number((num * 255).toFixed(0))); 3473 }); 3474 } 3475 if (color.charAt(0) !== "#") { color = "#" + color; } 3476 return color 3477 }; 3478 3479 const validateColor = (color, macros, token) => { 3480 const macroName = `\\\\color@$color}`; // from \defineColor. 3481 const match = htmlOrNameRegEx.exec(color); 3482 if (!match) { throw new ParseError("Invalid color: '" + color + "'", token) } 3483 // We allow a 6-digit HTML color spec without a leading "#". 3484 // This follows the xcolor package's HTML color model. 3485 // Predefined color names are all missed by this RegEx pattern. 3486 if (xcolorHtmlRegEx.test(color)) { 3487 return "#" + color 3488 } else if (color.charAt(0) === "#") { 3489 return color 3490 } else if (macros.has(macroName)) { 3491 color = macros.get(macroName).tokens[0].text; 3492 } else if (xcolors[color]) { 3493 color = xcolors[color]; 3494 } 3495 return color 3496 }; 3497 3498 const mathmlBuilder$9 = (group, style) => { 3499 // In LaTeX, color is not supposed to change the spacing of any node. 3500 // So instead of wrapping the group in an <mstyle>, we apply 3501 // the color individually to each node and return a document fragment. 3502 let expr = buildExpression(group.body, style.withColor(group.color)); 3503 expr = expr.map(e => { 3504 e.style.color = group.color; 3505 return e 3506 }); 3507 return mathMLTree.newDocumentFragment(expr) 3508 }; 3509 3510 defineFunction({ 3511 type: "color", 3512 names: ["\\textcolor"], 3513 props: { 3514 numArgs: 2, 3515 numOptionalArgs: 1, 3516 allowedInText: true, 3517 argTypes: ["raw", "raw", "original"] 3518 }, 3519 handler({ parser, token }, args, optArgs) { 3520 const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; 3521 let color = ""; 3522 if (model) { 3523 const spec = assertNodeType(args[0], "raw").string; 3524 color = colorFromSpec(model, spec); 3525 } else { 3526 color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); 3527 } 3528 const body = args[1]; 3529 return { 3530 type: "color", 3531 mode: parser.mode, 3532 color, 3533 isTextColor: true, 3534 body: ordargument(body) 3535 } 3536 }, 3537 mathmlBuilder: mathmlBuilder$9 3538 }); 3539 3540 defineFunction({ 3541 type: "color", 3542 names: ["\\color"], 3543 props: { 3544 numArgs: 1, 3545 numOptionalArgs: 1, 3546 allowedInText: true, 3547 argTypes: ["raw", "raw"] 3548 }, 3549 handler({ parser, breakOnTokenText, token }, args, optArgs) { 3550 const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; 3551 let color = ""; 3552 if (model) { 3553 const spec = assertNodeType(args[0], "raw").string; 3554 color = colorFromSpec(model, spec); 3555 } else { 3556 color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token); 3557 } 3558 3559 // Parse out the implicit body that should be colored. 3560 const body = parser.parseExpression(true, breakOnTokenText, true); 3561 3562 return { 3563 type: "color", 3564 mode: parser.mode, 3565 color, 3566 isTextColor: false, 3567 body 3568 } 3569 }, 3570 mathmlBuilder: mathmlBuilder$9 3571 }); 3572 3573 defineFunction({ 3574 type: "color", 3575 names: ["\\definecolor"], 3576 props: { 3577 numArgs: 3, 3578 allowedInText: true, 3579 argTypes: ["raw", "raw", "raw"] 3580 }, 3581 handler({ parser, funcName, token }, args) { 3582 const name = assertNodeType(args[0], "raw").string; 3583 if (!/^[A-Za-z]+$/.test(name)) { 3584 throw new ParseError("Color name must be latin letters.", token) 3585 } 3586 const model = assertNodeType(args[1], "raw").string; 3587 if (!["HTML", "RGB", "rgb"].includes(model)) { 3588 throw new ParseError("Color model must be HTML, RGB, or rgb.", token) 3589 } 3590 const spec = assertNodeType(args[2], "raw").string; 3591 const color = colorFromSpec(model, spec); 3592 parser.gullet.macros.set(`\\\\color@$name}`, { tokens: [{ text: color }], numArgs: 0 }); 3593 return { type: "internal", mode: parser.mode } 3594 } 3595 // No mathmlBuilder. The point of \definecolor is to set a macro. 3596 }); 3597 3598 // Row breaks within tabular environments, and line breaks at top level 3599 3600 3601 // \DeclareRobustCommand\\{...\@xnewline} 3602 defineFunction({ 3603 type: "cr", 3604 names: ["\\\\"], 3605 props: { 3606 numArgs: 0, 3607 numOptionalArgs: 0, 3608 allowedInText: true 3609 }, 3610 3611 handler({ parser }, args, optArgs) { 3612 const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null; 3613 const newLine = !parser.settings.displayMode; 3614 return { 3615 type: "cr", 3616 mode: parser.mode, 3617 newLine, 3618 size: size && assertNodeType(size, "size").value 3619 } 3620 }, 3621 3622 // The following builder is called only at the top level, 3623 // not within tabular/array environments. 3624 3625 mathmlBuilder(group, style) { 3626 // MathML 3.0 calls for newline to occur in an <mo> or an <mspace>. 3627 // Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.linebreaking 3628 const node = new mathMLTree.MathNode("mo"); 3629 if (group.newLine) { 3630 node.setAttribute("linebreak", "newline"); 3631 if (group.size) { 3632 const size = calculateSize(group.size, style); 3633 node.setAttribute("height", size.number + size.unit); 3634 } 3635 } 3636 return node 3637 } 3638 }); 3639 3640 const globalMap = { 3641 "\\global": "\\global", 3642 "\\long": "\\\\globallong", 3643 "\\\\globallong": "\\\\globallong", 3644 "\\def": "\\gdef", 3645 "\\gdef": "\\gdef", 3646 "\\edef": "\\xdef", 3647 "\\xdef": "\\xdef", 3648 "\\let": "\\\\globallet", 3649 "\\futurelet": "\\\\globalfuture" 3650 }; 3651 3652 const checkControlSequence = (tok) => { 3653 const name = tok.text; 3654 if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { 3655 throw new ParseError("Expected a control sequence", tok); 3656 } 3657 return name; 3658 }; 3659 3660 const getRHS = (parser) => { 3661 let tok = parser.gullet.popToken(); 3662 if (tok.text === "=") { 3663 // consume optional equals 3664 tok = parser.gullet.popToken(); 3665 if (tok.text === " ") { 3666 // consume one optional space 3667 tok = parser.gullet.popToken(); 3668 } 3669 } 3670 return tok; 3671 }; 3672 3673 const letCommand = (parser, name, tok, global) => { 3674 let macro = parser.gullet.macros.get(tok.text); 3675 if (macro == null) { 3676 // don't expand it later even if a macro with the same name is defined 3677 // e.g., \let\foo=\frac \def\frac{\relax} \frac12 3678 tok.noexpand = true; 3679 macro = { 3680 tokens: [tok], 3681 numArgs: 0, 3682 // reproduce the same behavior in expansion 3683 unexpandable: !parser.gullet.isExpandable(tok.text) 3684 }; 3685 } 3686 parser.gullet.macros.set(name, macro, global); 3687 }; 3688 3689 // <assignment> -> <non-macro assignment>|<macro assignment> 3690 // <non-macro assignment> -> <simple assignment>|\global<non-macro assignment> 3691 // <macro assignment> -> <definition>|<prefix><macro assignment> 3692 // <prefix> -> \global|\long|\outer 3693 defineFunction({ 3694 type: "internal", 3695 names: [ 3696 "\\global", 3697 "\\long", 3698 "\\\\globallong" // can’t be entered directly 3699 ], 3700 props: { 3701 numArgs: 0, 3702 allowedInText: true 3703 }, 3704 handler({ parser, funcName }) { 3705 parser.consumeSpaces(); 3706 const token = parser.fetch(); 3707 if (globalMap[token.text]) { 3708 // Temml doesn't have \par, so ignore \long 3709 if (funcName === "\\global" || funcName === "\\\\globallong") { 3710 token.text = globalMap[token.text]; 3711 } 3712 return assertNodeType(parser.parseFunction(), "internal"); 3713 } 3714 throw new ParseError(`Invalid token after macro prefix`, token); 3715 } 3716 }); 3717 3718 // Basic support for macro definitions: \def, \gdef, \edef, \xdef 3719 // <definition> -> <def><control sequence><definition text> 3720 // <def> -> \def|\gdef|\edef|\xdef 3721 // <definition text> -> <parameter text><left brace><balanced text><right brace> 3722 defineFunction({ 3723 type: "internal", 3724 names: ["\\def", "\\gdef", "\\edef", "\\xdef"], 3725 props: { 3726 numArgs: 0, 3727 allowedInText: true, 3728 primitive: true 3729 }, 3730 handler({ parser, funcName }) { 3731 let tok = parser.gullet.popToken(); 3732 const name = tok.text; 3733 if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { 3734 throw new ParseError("Expected a control sequence", tok); 3735 } 3736 3737 let numArgs = 0; 3738 let insert; 3739 const delimiters = [[]]; 3740 // <parameter text> contains no braces 3741 while (parser.gullet.future().text !== "{") { 3742 tok = parser.gullet.popToken(); 3743 if (tok.text === "#") { 3744 // If the very last character of the <parameter text> is #, so that 3745 // this # is immediately followed by {, TeX will behave as if the { 3746 // had been inserted at the right end of both the parameter text 3747 // and the replacement text. 3748 if (parser.gullet.future().text === "{") { 3749 insert = parser.gullet.future(); 3750 delimiters[numArgs].push("{"); 3751 break; 3752 } 3753 3754 // A parameter, the first appearance of # must be followed by 1, 3755 // the next by 2, and so on; up to nine #’s are allowed 3756 tok = parser.gullet.popToken(); 3757 if (!/^[1-9]$/.test(tok.text)) { 3758 throw new ParseError(`Invalid argument number "$tok.text}"`); 3759 } 3760 if (parseInt(tok.text) !== numArgs + 1) { 3761 throw new ParseError(`Argument number "$tok.text}" out of order`); 3762 } 3763 numArgs++; 3764 delimiters.push([]); 3765 } else if (tok.text === "EOF") { 3766 throw new ParseError("Expected a macro definition"); 3767 } else { 3768 delimiters[numArgs].push(tok.text); 3769 } 3770 } 3771 // replacement text, enclosed in '{' and '}' and properly nested 3772 let { tokens } = parser.gullet.consumeArg(); 3773 if (insert) { 3774 tokens.unshift(insert); 3775 } 3776 3777 if (funcName === "\\edef" || funcName === "\\xdef") { 3778 tokens = parser.gullet.expandTokens(tokens); 3779 if (tokens.length > parser.gullet.settings.maxExpand) { 3780 throw new ParseError("Too many expansions in an " + funcName); 3781 } 3782 tokens.reverse(); // to fit in with stack order 3783 } 3784 // Final arg is the expansion of the macro 3785 parser.gullet.macros.set( 3786 name, 3787 { tokens, numArgs, delimiters }, 3788 funcName === globalMap[funcName] 3789 ); 3790 return { type: "internal", mode: parser.mode }; 3791 } 3792 }); 3793 3794 // <simple assignment> -> <let assignment> 3795 // <let assignment> -> \futurelet<control sequence><token><token> 3796 // | \let<control sequence><equals><one optional space><token> 3797 // <equals> -> <optional spaces>|<optional spaces>= 3798 defineFunction({ 3799 type: "internal", 3800 names: [ 3801 "\\let", 3802 "\\\\globallet" // can’t be entered directly 3803 ], 3804 props: { 3805 numArgs: 0, 3806 allowedInText: true, 3807 primitive: true 3808 }, 3809 handler({ parser, funcName }) { 3810 const name = checkControlSequence(parser.gullet.popToken()); 3811 parser.gullet.consumeSpaces(); 3812 const tok = getRHS(parser); 3813 letCommand(parser, name, tok, funcName === "\\\\globallet"); 3814 return { type: "internal", mode: parser.mode }; 3815 } 3816 }); 3817 3818 // ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf 3819 defineFunction({ 3820 type: "internal", 3821 names: [ 3822 "\\futurelet", 3823 "\\\\globalfuture" // can’t be entered directly 3824 ], 3825 props: { 3826 numArgs: 0, 3827 allowedInText: true, 3828 primitive: true 3829 }, 3830 handler({ parser, funcName }) { 3831 const name = checkControlSequence(parser.gullet.popToken()); 3832 const middle = parser.gullet.popToken(); 3833 const tok = parser.gullet.popToken(); 3834 letCommand(parser, name, tok, funcName === "\\\\globalfuture"); 3835 parser.gullet.pushToken(tok); 3836 parser.gullet.pushToken(middle); 3837 return { type: "internal", mode: parser.mode }; 3838 } 3839 }); 3840 3841 defineFunction({ 3842 type: "internal", 3843 names: ["\\newcommand", "\\renewcommand", "\\providecommand"], 3844 props: { 3845 numArgs: 0, 3846 allowedInText: true, 3847 primitive: true 3848 }, 3849 handler({ parser, funcName }) { 3850 let name = ""; 3851 const tok = parser.gullet.popToken(); 3852 if (tok.text === "{") { 3853 name = checkControlSequence(parser.gullet.popToken()); 3854 parser.gullet.popToken(); 3855 } else { 3856 name = checkControlSequence(tok); 3857 } 3858 3859 const exists = parser.gullet.isDefined(name); 3860 if (exists && funcName === "\\newcommand") { 3861 throw new ParseError( 3862 `\\newcommand{$name}} attempting to redefine $name}; use \\renewcommand` 3863 ); 3864 } 3865 if (!exists && funcName === "\\renewcommand") { 3866 throw new ParseError( 3867 `\\renewcommand{$name}} when command $name} does not yet exist; use \\newcommand` 3868 ); 3869 } 3870 3871 let numArgs = 0; 3872 if (parser.gullet.future().text === "[") { 3873 let tok = parser.gullet.popToken(); 3874 tok = parser.gullet.popToken(); 3875 if (!/^[0-9]$/.test(tok.text)) { 3876 throw new ParseError(`Invalid number of arguments: "$tok.text}"`); 3877 } 3878 numArgs = parseInt(tok.text); 3879 tok = parser.gullet.popToken(); 3880 if (tok.text !== "]") { 3881 throw new ParseError(`Invalid argument "$tok.text}"`); 3882 } 3883 } 3884 3885 // replacement text, enclosed in '{' and '}' and properly nested 3886 const { tokens } = parser.gullet.consumeArg(); 3887 3888 if (!(funcName === "\\providecommand" && parser.gullet.macros.has(name))) { 3889 // Ignore \providecommand 3890 parser.gullet.macros.set( 3891 name, 3892 { tokens, numArgs } 3893 ); 3894 } 3895 3896 return { type: "internal", mode: parser.mode }; 3897 3898 } 3899 }); 3900 3901 // Extra data needed for the delimiter handler down below 3902 const delimiterSizes = { 3903 "\\bigl": { mclass: "mopen", size: 1 }, 3904 "\\Bigl": { mclass: "mopen", size: 2 }, 3905 "\\biggl": { mclass: "mopen", size: 3 }, 3906 "\\Biggl": { mclass: "mopen", size: 4 }, 3907 "\\bigr": { mclass: "mclose", size: 1 }, 3908 "\\Bigr": { mclass: "mclose", size: 2 }, 3909 "\\biggr": { mclass: "mclose", size: 3 }, 3910 "\\Biggr": { mclass: "mclose", size: 4 }, 3911 "\\bigm": { mclass: "mrel", size: 1 }, 3912 "\\Bigm": { mclass: "mrel", size: 2 }, 3913 "\\biggm": { mclass: "mrel", size: 3 }, 3914 "\\Biggm": { mclass: "mrel", size: 4 }, 3915 "\\big": { mclass: "mord", size: 1 }, 3916 "\\Big": { mclass: "mord", size: 2 }, 3917 "\\bigg": { mclass: "mord", size: 3 }, 3918 "\\Bigg": { mclass: "mord", size: 4 } 3919 }; 3920 3921 const delimiters = [ 3922 "(", 3923 "\\lparen", 3924 ")", 3925 "\\rparen", 3926 "[", 3927 "\\lbrack", 3928 "]", 3929 "\\rbrack", 3930 "\\{", 3931 "\\lbrace", 3932 "\\}", 3933 "\\rbrace", 3934 "⦇", 3935 "\\llparenthesis", 3936 "⦈", 3937 "\\rrparenthesis", 3938 "\\lfloor", 3939 "\\rfloor", 3940 "\u230a", 3941 "\u230b", 3942 "\\lceil", 3943 "\\rceil", 3944 "\u2308", 3945 "\u2309", 3946 "<", 3947 ">", 3948 "\\langle", 3949 "\u27e8", 3950 "\\rangle", 3951 "\u27e9", 3952 "\\lAngle", 3953 "\u27ea", 3954 "\\rAngle", 3955 "\u27eb", 3956 "\\llangle", 3957 "⦉", 3958 "\\rrangle", 3959 "⦊", 3960 "\\lt", 3961 "\\gt", 3962 "\\lvert", 3963 "\\rvert", 3964 "\\lVert", 3965 "\\rVert", 3966 "\\lgroup", 3967 "\\rgroup", 3968 "\u27ee", 3969 "\u27ef", 3970 "\\lmoustache", 3971 "\\rmoustache", 3972 "\u23b0", 3973 "\u23b1", 3974 "\\llbracket", 3975 "\\rrbracket", 3976 "\u27e6", 3977 "\u27e6", 3978 "\\lBrace", 3979 "\\rBrace", 3980 "\u2983", 3981 "\u2984", 3982 "/", 3983 "\\backslash", 3984 "|", 3985 "\\vert", 3986 "\\|", 3987 "\\Vert", 3988 "\u2016", 3989 "\\uparrow", 3990 "\\Uparrow", 3991 "\\downarrow", 3992 "\\Downarrow", 3993 "\\updownarrow", 3994 "\\Updownarrow", 3995 "." 3996 ]; 3997 3998 // Export isDelimiter for benefit of parser. 3999 const dels = ["}", "\\left", "\\middle", "\\right"]; 4000 const isDelimiter = str => str.length > 0 && 4001 (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str)); 4002 4003 // Metrics of the different sizes. Found by looking at TeX's output of 4004 // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$ 4005 // Used to create stacked delimiters of appropriate sizes in makeSizedDelim. 4006 const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; 4007 4008 // Delimiter functions 4009 function checkDelimiter(delim, context) { 4010 const symDelim = checkSymbolNodeType(delim); 4011 if (symDelim && delimiters.includes(symDelim.text)) { 4012 // If a character is not in the MathML operator dictionary, it will not stretch. 4013 // Replace such characters w/characters that will stretch. 4014 if (["<", "\\lt"].includes(symDelim.text)) { symDelim.text = "⟨"; } 4015 if ([">", "\\gt"].includes(symDelim.text)) { symDelim.text = "⟩"; } 4016 return symDelim; 4017 } else if (symDelim) { 4018 throw new ParseError(`Invalid delimiter '$symDelim.text}' after '$context.funcName}'`, delim); 4019 } else { 4020 throw new ParseError(`Invalid delimiter type '$delim.type}'`, delim); 4021 } 4022 } 4023 4024 // / \ 4025 const needExplicitStretch = ["\u002F", "\u005C", "\\backslash", "\\vert", "|"]; 4026 4027 defineFunction({ 4028 type: "delimsizing", 4029 names: [ 4030 "\\bigl", 4031 "\\Bigl", 4032 "\\biggl", 4033 "\\Biggl", 4034 "\\bigr", 4035 "\\Bigr", 4036 "\\biggr", 4037 "\\Biggr", 4038 "\\bigm", 4039 "\\Bigm", 4040 "\\biggm", 4041 "\\Biggm", 4042 "\\big", 4043 "\\Big", 4044 "\\bigg", 4045 "\\Bigg" 4046 ], 4047 props: { 4048 numArgs: 1, 4049 argTypes: ["primitive"] 4050 }, 4051 handler: (context, args) => { 4052 const delim = checkDelimiter(args[0], context); 4053 4054 return { 4055 type: "delimsizing", 4056 mode: context.parser.mode, 4057 size: delimiterSizes[context.funcName].size, 4058 mclass: delimiterSizes[context.funcName].mclass, 4059 delim: delim.text 4060 }; 4061 }, 4062 mathmlBuilder: (group) => { 4063 const children = []; 4064 4065 if (group.delim === ".") { group.delim = ""; } 4066 children.push(makeText(group.delim, group.mode)); 4067 4068 const node = new mathMLTree.MathNode("mo", children); 4069 4070 if (group.mclass === "mopen" || group.mclass === "mclose") { 4071 // Only some of the delimsizing functions act as fences, and they 4072 // return "mopen" or "mclose" mclass. 4073 node.setAttribute("fence", "true"); 4074 } else { 4075 // Explicitly disable fencing if it's not a fence, to override the 4076 // defaults. 4077 node.setAttribute("fence", "false"); 4078 } 4079 if (needExplicitStretch.includes(group.delim) || group.delim.indexOf("arrow") > -1) { 4080 // We have to explicitly set stretchy to true. 4081 node.setAttribute("stretchy", "true"); 4082 } 4083 node.setAttribute("symmetric", "true"); // Needed for tall arrows in Firefox. 4084 node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em"); 4085 node.setAttribute("maxsize", sizeToMaxHeight[group.size] + "em"); 4086 return node; 4087 } 4088 }); 4089 4090 function assertParsed(group) { 4091 if (!group.body) { 4092 throw new Error("Bug: The leftright ParseNode wasn't fully parsed."); 4093 } 4094 } 4095 4096 defineFunction({ 4097 type: "leftright-right", 4098 names: ["\\right"], 4099 props: { 4100 numArgs: 1, 4101 argTypes: ["primitive"] 4102 }, 4103 handler: (context, args) => { 4104 return { 4105 type: "leftright-right", 4106 mode: context.parser.mode, 4107 delim: checkDelimiter(args[0], context).text 4108 }; 4109 } 4110 }); 4111 4112 defineFunction({ 4113 type: "leftright", 4114 names: ["\\left"], 4115 props: { 4116 numArgs: 1, 4117 argTypes: ["primitive"] 4118 }, 4119 handler: (context, args) => { 4120 const delim = checkDelimiter(args[0], context); 4121 4122 const parser = context.parser; 4123 // Parse out the implicit body 4124 ++parser.leftrightDepth; 4125 // parseExpression stops before '\\right' or `\\middle` 4126 let body = parser.parseExpression(false, null, true); 4127 let nextToken = parser.fetch(); 4128 while (nextToken.text === "\\middle") { 4129 // `\middle`, from the ε-TeX package, ends one group and starts another group. 4130 // We had to parse this expression with `breakOnMiddle` enabled in order 4131 // to get TeX-compliant parsing of \over. 4132 // But we do not want, at this point, to end on \middle, so continue 4133 // to parse until we fetch a `\right`. 4134 parser.consume(); 4135 const middle = parser.fetch().text; 4136 if (!symbols.math[middle]) { 4137 throw new ParseError(`Invalid delimiter '$middle}' after '\\middle'`); 4138 } 4139 checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" }); 4140 body.push({ type: "middle", mode: "math", delim: middle }); 4141 parser.consume(); 4142 body = body.concat(parser.parseExpression(false, null, true)); 4143 nextToken = parser.fetch(); 4144 } 4145 --parser.leftrightDepth; 4146 // Check the next token 4147 parser.expect("\\right", false); 4148 const right = assertNodeType(parser.parseFunction(), "leftright-right"); 4149 return { 4150 type: "leftright", 4151 mode: parser.mode, 4152 body, 4153 left: delim.text, 4154 right: right.delim 4155 }; 4156 }, 4157 mathmlBuilder: (group, style) => { 4158 assertParsed(group); 4159 const inner = buildExpression(group.body, style); 4160 4161 if (group.left === ".") { group.left = ""; } 4162 const leftNode = new mathMLTree.MathNode("mo", [makeText(group.left, group.mode)]); 4163 leftNode.setAttribute("fence", "true"); 4164 leftNode.setAttribute("form", "prefix"); 4165 if (group.left === "/" || group.left === "\u005C" || group.left.indexOf("arrow") > -1) { 4166 leftNode.setAttribute("stretchy", "true"); 4167 } 4168 inner.unshift(leftNode); 4169 4170 if (group.right === ".") { group.right = ""; } 4171 const rightNode = new mathMLTree.MathNode("mo", [makeText(group.right, group.mode)]); 4172 rightNode.setAttribute("fence", "true"); 4173 rightNode.setAttribute("form", "postfix"); 4174 if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) { 4175 rightNode.setAttribute("stretchy", "true"); 4176 } 4177 if (group.body.length > 0) { 4178 const lastElement = group.body[group.body.length - 1]; 4179 if (lastElement.type === "color" && !lastElement.isTextColor) { 4180 // \color is a switch. If the last element is of type "color" then 4181 // the user set the \color switch and left it on. 4182 // A \right delimiter turns the switch off, but the delimiter itself gets the color. 4183 rightNode.setAttribute("mathcolor", lastElement.color); 4184 } 4185 } 4186 inner.push(rightNode); 4187 4188 return makeRow(inner); 4189 } 4190 }); 4191 4192 defineFunction({ 4193 type: "middle", 4194 names: ["\\middle"], 4195 props: { 4196 numArgs: 1, 4197 argTypes: ["primitive"] 4198 }, 4199 handler: (context, args) => { 4200 const delim = checkDelimiter(args[0], context); 4201 if (!context.parser.leftrightDepth) { 4202 throw new ParseError("\\middle without preceding \\left", delim); 4203 } 4204 4205 return { 4206 type: "middle", 4207 mode: context.parser.mode, 4208 delim: delim.text 4209 }; 4210 }, 4211 mathmlBuilder: (group, style) => { 4212 const textNode = makeText(group.delim, group.mode); 4213 const middleNode = new mathMLTree.MathNode("mo", [textNode]); 4214 middleNode.setAttribute("fence", "true"); 4215 if (group.delim.indexOf("arrow") > -1) { 4216 middleNode.setAttribute("stretchy", "true"); 4217 } 4218 // The next line is not semantically correct, but 4219 // Chromium fails to stretch if it is not there. 4220 middleNode.setAttribute("form", "prefix"); 4221 // MathML gives 5/18em spacing to each <mo> element. 4222 // \middle should get delimiter spacing instead. 4223 middleNode.setAttribute("lspace", "0.05em"); 4224 middleNode.setAttribute("rspace", "0.05em"); 4225 return middleNode; 4226 } 4227 }); 4228 4229 const padding$1 = _ => { 4230 const node = new mathMLTree.MathNode("mspace"); 4231 node.setAttribute("width", "3pt"); 4232 return node 4233 }; 4234 4235 const mathmlBuilder$8 = (group, style) => { 4236 let node; 4237 if (group.label.indexOf("colorbox") > -1 || group.label === "\\boxed") { 4238 // MathML core does not support +width attribute in <mpadded>. 4239 // Firefox does not reliably add side padding. 4240 // Insert <mspace> 4241 node = new mathMLTree.MathNode("mrow", [ 4242 padding$1(), 4243 buildGroup$1(group.body, style), 4244 padding$1() 4245 ]); 4246 } else { 4247 node = new mathMLTree.MathNode("menclose", [buildGroup$1(group.body, style)]); 4248 } 4249 switch (group.label) { 4250 case "\\overline": 4251 node.setAttribute("notation", "top"); // for Firefox & WebKit 4252 node.classes.push("tml-overline"); // for Chromium 4253 break 4254 case "\\underline": 4255 node.setAttribute("notation", "bottom"); 4256 node.classes.push("tml-underline"); 4257 break 4258 case "\\cancel": 4259 node.setAttribute("notation", "updiagonalstrike"); 4260 node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "upstrike"])); 4261 break 4262 case "\\bcancel": 4263 node.setAttribute("notation", "downdiagonalstrike"); 4264 node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "downstrike"])); 4265 break 4266 case "\\sout": 4267 node.setAttribute("notation", "horizontalstrike"); 4268 node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "sout"])); 4269 break 4270 case "\\xcancel": 4271 node.setAttribute("notation", "updiagonalstrike downdiagonalstrike"); 4272 node.classes.push("tml-xcancel"); 4273 break 4274 case "\\longdiv": 4275 node.setAttribute("notation", "longdiv"); 4276 node.classes.push("longdiv-top"); 4277 node.children.push(new mathMLTree.MathNode("mrow", [], ["longdiv-arc"])); 4278 break 4279 case "\\phase": 4280 node.setAttribute("notation", "phasorangle"); 4281 node.classes.push("phasor-bottom"); 4282 node.children.push(new mathMLTree.MathNode("mrow", [], ["phasor-angle"])); 4283 break 4284 case "\\textcircled": 4285 node.setAttribute("notation", "circle"); 4286 node.classes.push("circle-pad"); 4287 node.children.push(new mathMLTree.MathNode("mrow", [], ["textcircle"])); 4288 break 4289 case "\\angl": 4290 node.setAttribute("notation", "actuarial"); 4291 node.classes.push("actuarial"); 4292 break 4293 case "\\boxed": 4294 // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty 4295 node.setAttribute("notation", "box"); 4296 node.classes.push("tml-box"); 4297 node.setAttribute("scriptlevel", "0"); 4298 node.setAttribute("displaystyle", "true"); 4299 break 4300 case "\\fbox": 4301 node.setAttribute("notation", "box"); 4302 node.classes.push("tml-fbox"); 4303 break 4304 case "\\fcolorbox": 4305 case "\\colorbox": { 4306 // <menclose> doesn't have a good notation option for \colorbox. 4307 // So use <mpadded> instead. Set some attributes that come 4308 // included with <menclose>. 4309 //const fboxsep = 3; // 3 pt from LaTeX source2e 4310 //node.setAttribute("height", `+${2 * fboxsep}pt`) 4311 //node.setAttribute("voffset", `${fboxsep}pt`) 4312 const style = { padding: "3pt 0 3pt 0" }; 4313 4314 if (group.label === "\\fcolorbox") { 4315 style.border = "0.0667em solid " + String(group.borderColor); 4316 } 4317 node.style = style; 4318 break 4319 } 4320 } 4321 if (group.backgroundColor) { 4322 node.setAttribute("mathbackground", group.backgroundColor); 4323 } 4324 return node; 4325 }; 4326 4327 defineFunction({ 4328 type: "enclose", 4329 names: ["\\colorbox"], 4330 props: { 4331 numArgs: 2, 4332 numOptionalArgs: 1, 4333 allowedInText: true, 4334 argTypes: ["raw", "raw", "text"] 4335 }, 4336 handler({ parser, funcName }, args, optArgs) { 4337 const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; 4338 let color = ""; 4339 if (model) { 4340 const spec = assertNodeType(args[0], "raw").string; 4341 color = colorFromSpec(model, spec); 4342 } else { 4343 color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); 4344 } 4345 const body = args[1]; 4346 return { 4347 type: "enclose", 4348 mode: parser.mode, 4349 label: funcName, 4350 backgroundColor: color, 4351 body 4352 }; 4353 }, 4354 mathmlBuilder: mathmlBuilder$8 4355 }); 4356 4357 defineFunction({ 4358 type: "enclose", 4359 names: ["\\fcolorbox"], 4360 props: { 4361 numArgs: 3, 4362 numOptionalArgs: 1, 4363 allowedInText: true, 4364 argTypes: ["raw", "raw", "raw", "text"] 4365 }, 4366 handler({ parser, funcName }, args, optArgs) { 4367 const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string; 4368 let borderColor = ""; 4369 let backgroundColor; 4370 if (model) { 4371 const borderSpec = assertNodeType(args[0], "raw").string; 4372 const backgroundSpec = assertNodeType(args[0], "raw").string; 4373 borderColor = colorFromSpec(model, borderSpec); 4374 backgroundColor = colorFromSpec(model, backgroundSpec); 4375 } else { 4376 borderColor = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros); 4377 backgroundColor = validateColor(assertNodeType(args[1], "raw").string, parser.gullet.macros); 4378 } 4379 const body = args[2]; 4380 return { 4381 type: "enclose", 4382 mode: parser.mode, 4383 label: funcName, 4384 backgroundColor, 4385 borderColor, 4386 body 4387 }; 4388 }, 4389 mathmlBuilder: mathmlBuilder$8 4390 }); 4391 4392 defineFunction({ 4393 type: "enclose", 4394 names: ["\\fbox"], 4395 props: { 4396 numArgs: 1, 4397 argTypes: ["hbox"], 4398 allowedInText: true 4399 }, 4400 handler({ parser }, args) { 4401 return { 4402 type: "enclose", 4403 mode: parser.mode, 4404 label: "\\fbox", 4405 body: args[0] 4406 }; 4407 } 4408 }); 4409 4410 defineFunction({ 4411 type: "enclose", 4412 names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", 4413 "\\boxed", "\\longdiv", "\\phase"], 4414 props: { 4415 numArgs: 1 4416 }, 4417 handler({ parser, funcName }, args) { 4418 const body = args[0]; 4419 return { 4420 type: "enclose", 4421 mode: parser.mode, 4422 label: funcName, 4423 body 4424 }; 4425 }, 4426 mathmlBuilder: mathmlBuilder$8 4427 }); 4428 4429 defineFunction({ 4430 type: "enclose", 4431 names: ["\\underline"], 4432 props: { 4433 numArgs: 1, 4434 allowedInText: true 4435 }, 4436 handler({ parser, funcName }, args) { 4437 const body = args[0]; 4438 return { 4439 type: "enclose", 4440 mode: parser.mode, 4441 label: funcName, 4442 body 4443 }; 4444 }, 4445 mathmlBuilder: mathmlBuilder$8 4446 }); 4447 4448 4449 defineFunction({ 4450 type: "enclose", 4451 names: ["\\textcircled"], 4452 props: { 4453 numArgs: 1, 4454 argTypes: ["text"], 4455 allowedInArgument: true, 4456 allowedInText: true 4457 }, 4458 handler({ parser, funcName }, args) { 4459 const body = args[0]; 4460 return { 4461 type: "enclose", 4462 mode: parser.mode, 4463 label: funcName, 4464 body 4465 }; 4466 }, 4467 mathmlBuilder: mathmlBuilder$8 4468 }); 4469 4470 /** 4471 * All registered environments. 4472 * `environments.js` exports this same dictionary again and makes it public. 4473 * `Parser.js` requires this dictionary via `environments.js`. 4474 */ 4475 const _environments = {}; 4476 4477 function defineEnvironment({ type, names, props, handler, mathmlBuilder }) { 4478 // Set default values of environments. 4479 const data = { 4480 type, 4481 numArgs: props.numArgs || 0, 4482 allowedInText: false, 4483 numOptionalArgs: 0, 4484 handler 4485 }; 4486 for (let i = 0; i < names.length; ++i) { 4487 _environments[names[i]] = data; 4488 } 4489 if (mathmlBuilder) { 4490 _mathmlGroupBuilders[type] = mathmlBuilder; 4491 } 4492 } 4493 4494 /** 4495 * Lexing or parsing positional information for error reporting. 4496 * This object is immutable. 4497 */ 4498 class SourceLocation { 4499 constructor(lexer, start, end) { 4500 this.lexer = lexer; // Lexer holding the input string. 4501 this.start = start; // Start offset, zero-based inclusive. 4502 this.end = end; // End offset, zero-based exclusive. 4503 } 4504 4505 /** 4506 * Merges two `SourceLocation`s from location providers, given they are 4507 * provided in order of appearance. 4508 * - Returns the first one's location if only the first is provided. 4509 * - Returns a merged range of the first and the last if both are provided 4510 * and their lexers match. 4511 * - Otherwise, returns null. 4512 */ 4513 static range(first, second) { 4514 if (!second) { 4515 return first && first.loc; 4516 } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) { 4517 return null; 4518 } else { 4519 return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end); 4520 } 4521 } 4522 } 4523 4524 /** 4525 * Interface required to break circular dependency between Token, Lexer, and 4526 * ParseError. 4527 */ 4528 4529 /** 4530 * The resulting token returned from `lex`. 4531 * 4532 * It consists of the token text plus some position information. 4533 * The position information is essentially a range in an input string, 4534 * but instead of referencing the bare input string, we refer to the lexer. 4535 * That way it is possible to attach extra metadata to the input string, 4536 * like for example a file name or similar. 4537 * 4538 * The position information is optional, so it is OK to construct synthetic 4539 * tokens if appropriate. Not providing available position information may 4540 * lead to degraded error reporting, though. 4541 */ 4542 class Token { 4543 constructor( 4544 text, // the text of this token 4545 loc 4546 ) { 4547 this.text = text; 4548 this.loc = loc; 4549 } 4550 4551 /** 4552 * Given a pair of tokens (this and endToken), compute a `Token` encompassing 4553 * the whole input range enclosed by these two. 4554 */ 4555 range( 4556 endToken, // last token of the range, inclusive 4557 text // the text of the newly constructed token 4558 ) { 4559 return new Token(text, SourceLocation.range(this, endToken)); 4560 } 4561 } 4562 4563 // In TeX, there are actually three sets of dimensions, one for each of 4564 // textstyle, scriptstyle, and scriptscriptstyle. These are 4565 // provided in the the arrays below, in that order. 4566 // 4567 4568 // Math style is not quite the same thing as script level. 4569 const StyleLevel = { 4570 DISPLAY: 0, 4571 TEXT: 1, 4572 SCRIPT: 2, 4573 SCRIPTSCRIPT: 3 4574 }; 4575 4576 /** 4577 * All registered global/built-in macros. 4578 * `macros.js` exports this same dictionary again and makes it public. 4579 * `Parser.js` requires this dictionary via `macros.js`. 4580 */ 4581 const _macros = {}; 4582 4583 // This function might one day accept an additional argument and do more things. 4584 function defineMacro(name, body) { 4585 _macros[name] = body; 4586 } 4587 4588 /** 4589 * Predefined macros for Temml. 4590 * This can be used to define some commands in terms of others. 4591 */ 4592 4593 const macros = _macros; 4594 4595 ////////////////////////////////////////////////////////////////////// 4596 // macro tools 4597 4598 defineMacro("\\noexpand", function(context) { 4599 // The expansion is the token itself; but that token is interpreted 4600 // as if its meaning were ‘\relax’ if it is a control sequence that 4601 // would ordinarily be expanded by TeX’s expansion rules. 4602 const t = context.popToken(); 4603 if (context.isExpandable(t.text)) { 4604 t.noexpand = true; 4605 t.treatAsRelax = true; 4606 } 4607 return { tokens: [t], numArgs: 0 }; 4608 }); 4609 4610 defineMacro("\\expandafter", function(context) { 4611 // TeX first reads the token that comes immediately after \expandafter, 4612 // without expanding it; let’s call this token t. Then TeX reads the 4613 // token that comes after t (and possibly more tokens, if that token 4614 // has an argument), replacing it by its expansion. Finally TeX puts 4615 // t back in front of that expansion. 4616 const t = context.popToken(); 4617 context.expandOnce(true); // expand only an expandable token 4618 return { tokens: [t], numArgs: 0 }; 4619 }); 4620 4621 // LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2 4622 // TeX source: \long\def\@firstoftwo#1#2{#1} 4623 defineMacro("\\@firstoftwo", function(context) { 4624 const args = context.consumeArgs(2); 4625 return { tokens: args[0], numArgs: 0 }; 4626 }); 4627 4628 // LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1 4629 // TeX source: \long\def\@secondoftwo#1#2{#2} 4630 defineMacro("\\@secondoftwo", function(context) { 4631 const args = context.consumeArgs(2); 4632 return { tokens: args[1], numArgs: 0 }; 4633 }); 4634 4635 // LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded) 4636 // symbol that isn't a space, consuming any spaces but not consuming the 4637 // first nonspace character. If that nonspace character matches #1, then 4638 // the macro expands to #2; otherwise, it expands to #3. 4639 defineMacro("\\@ifnextchar", function(context) { 4640 const args = context.consumeArgs(3); // symbol, if, else 4641 context.consumeSpaces(); 4642 const nextToken = context.future(); 4643 if (args[0].length === 1 && args[0][0].text === nextToken.text) { 4644 return { tokens: args[1], numArgs: 0 }; 4645 } else { 4646 return { tokens: args[2], numArgs: 0 }; 4647 } 4648 }); 4649 4650 // LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol. 4651 // If it is `*`, then it consumes the symbol, and the macro expands to #1; 4652 // otherwise, the macro expands to #2 (without consuming the symbol). 4653 // TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}} 4654 defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}"); 4655 4656 // LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode 4657 defineMacro("\\TextOrMath", function(context) { 4658 const args = context.consumeArgs(2); 4659 if (context.mode === "text") { 4660 return { tokens: args[0], numArgs: 0 }; 4661 } else { 4662 return { tokens: args[1], numArgs: 0 }; 4663 } 4664 }); 4665 4666 const stringFromArg = arg => { 4667 // Reverse the order of the arg and return a string. 4668 let str = ""; 4669 for (let i = arg.length - 1; i > -1; i--) { 4670 str += arg[i].text; 4671 } 4672 return str 4673 }; 4674 4675 // Lookup table for parsing numbers in base 8 through 16 4676 const digitToNumber = { 4677 0: 0, 4678 1: 1, 4679 2: 2, 4680 3: 3, 4681 4: 4, 4682 5: 5, 4683 6: 6, 4684 7: 7, 4685 8: 8, 4686 9: 9, 4687 a: 10, 4688 A: 10, 4689 b: 11, 4690 B: 11, 4691 c: 12, 4692 C: 12, 4693 d: 13, 4694 D: 13, 4695 e: 14, 4696 E: 14, 4697 f: 15, 4698 F: 15 4699 }; 4700 4701 const nextCharNumber = context => { 4702 const numStr = context.future().text; 4703 if (numStr === "EOF") { return [null, ""] } 4704 return [digitToNumber[numStr.charAt(0)], numStr] 4705 }; 4706 4707 const appendCharNumbers = (number, numStr, base) => { 4708 for (let i = 1; i < numStr.length; i++) { 4709 const digit = digitToNumber[numStr.charAt(i)]; 4710 number *= base; 4711 number += digit; 4712 } 4713 return number 4714 }; 4715 4716 // TeX \char makes a literal character (catcode 12) using the following forms: 4717 // (see The TeXBook, p. 43) 4718 // \char123 -- decimal 4719 // \char'123 -- octal 4720 // \char"123 -- hex 4721 // \char`x -- character that can be written (i.e. isn't active) 4722 // \char`\x -- character that cannot be written (e.g. %) 4723 // These all refer to characters from the font, so we turn them into special 4724 // calls to a function \@char dealt with in the Parser. 4725 defineMacro("\\char", function(context) { 4726 let token = context.popToken(); 4727 let base; 4728 let number = ""; 4729 if (token.text === "'") { 4730 base = 8; 4731 token = context.popToken(); 4732 } else if (token.text === '"') { 4733 base = 16; 4734 token = context.popToken(); 4735 } else if (token.text === "`") { 4736 token = context.popToken(); 4737 if (token.text[0] === "\\") { 4738 number = token.text.charCodeAt(1); 4739 } else if (token.text === "EOF") { 4740 throw new ParseError("\\char` missing argument"); 4741 } else { 4742 number = token.text.charCodeAt(0); 4743 } 4744 } else { 4745 base = 10; 4746 } 4747 if (base) { 4748 // Parse a number in the given base, starting with first `token`. 4749 let numStr = token.text; 4750 number = digitToNumber[numStr.charAt(0)]; 4751 if (number == null || number >= base) { 4752 throw new ParseError(`Invalid base-$base} digit $token.text}`); 4753 } 4754 number = appendCharNumbers(number, numStr, base); 4755 let digit; 4756 [digit, numStr] = nextCharNumber(context); 4757 while (digit != null && digit < base) { 4758 number *= base; 4759 number += digit; 4760 number = appendCharNumbers(number, numStr, base); 4761 context.popToken(); 4762 [digit, numStr] = nextCharNumber(context); 4763 } 4764 } 4765 return `\\@char{$number}}`; 4766 }); 4767 4768 function recreateArgStr(context) { 4769 // Recreate the macro's original argument string from the array of parse tokens. 4770 const tokens = context.consumeArgs(1)[0]; 4771 let str = ""; 4772 let expectedLoc = tokens[tokens.length - 1].loc.start; 4773 for (let i = tokens.length - 1; i >= 0; i--) { 4774 const actualLoc = tokens[i].loc.start; 4775 if (actualLoc > expectedLoc) { 4776 // context.consumeArgs has eaten a space. 4777 str += " "; 4778 expectedLoc = actualLoc; 4779 } 4780 str += tokens[i].text; 4781 expectedLoc += tokens[i].text.length; 4782 } 4783 return str 4784 } 4785 4786 // The Latin Modern font renders <mi>√</mi> at the wrong vertical alignment. 4787 // This macro provides a better rendering. 4788 defineMacro("\\surd", '\\sqrt{\\vphantom{|}}'); 4789 4790 // See comment for \oplus in symbols.js. 4791 defineMacro("\u2295", "\\oplus"); 4792 4793 // Since Temml has no \par, ignore \long. 4794 defineMacro("\\long", ""); 4795 4796 ////////////////////////////////////////////////////////////////////// 4797 // Grouping 4798 // \let\bgroup={ \let\egroup=} 4799 defineMacro("\\bgroup", "{"); 4800 defineMacro("\\egroup", "}"); 4801 4802 // Symbols from latex.ltx: 4803 // \def~{\nobreakspace{}} 4804 // \def\lq{`} 4805 // \def\rq{'} 4806 // \def \aa {\r a} 4807 defineMacro("~", "\\nobreakspace"); 4808 defineMacro("\\lq", "`"); 4809 defineMacro("\\rq", "'"); 4810 defineMacro("\\aa", "\\r a"); 4811 4812 defineMacro("\\Bbbk", "\\Bbb{k}"); 4813 4814 // \mathstrut from the TeXbook, p 360 4815 defineMacro("\\mathstrut", "\\vphantom{(}"); 4816 4817 // \underbar from TeXbook p 353 4818 defineMacro("\\underbar", "\\underline{\\text{#1}}"); 4819 4820 ////////////////////////////////////////////////////////////////////// 4821 // LaTeX_2ε 4822 4823 // \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@ 4824 // \kern6\p@\hbox{.}\hbox{.}\hbox{.}}} 4825 // We'll call \varvdots, which gets a glyph from symbols.js. 4826 // The zero-width rule gets us an equivalent to the vertical 6pt kern. 4827 defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}"); 4828 defineMacro("\u22ee", "\\vdots"); 4829 4830 // {array} environment gaps 4831 defineMacro("\\arraystretch", "1"); // line spacing factor times 12pt 4832 defineMacro("\\arraycolsep", "6pt"); // half the width separating columns 4833 4834 ////////////////////////////////////////////////////////////////////// 4835 // amsmath.sty 4836 // http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf 4837 4838 //\newcommand{\substack}[1]{\subarray{c}#1\endsubarray} 4839 defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"); 4840 4841 // \def\iff{\DOTSB\;\Longleftrightarrow\;} 4842 // \def\implies{\DOTSB\;\Longrightarrow\;} 4843 // \def\impliedby{\DOTSB\;\Longleftarrow\;} 4844 defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"); 4845 defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"); 4846 defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"); 4847 4848 // AMSMath's automatic \dots, based on \mdots@@ macro. 4849 const dotsByToken = { 4850 ",": "\\dotsc", 4851 "\\not": "\\dotsb", 4852 // \keybin@ checks for the following: 4853 "+": "\\dotsb", 4854 "=": "\\dotsb", 4855 "<": "\\dotsb", 4856 ">": "\\dotsb", 4857 "-": "\\dotsb", 4858 "*": "\\dotsb", 4859 ":": "\\dotsb", 4860 // Symbols whose definition starts with \DOTSB: 4861 "\\DOTSB": "\\dotsb", 4862 "\\coprod": "\\dotsb", 4863 "\\bigvee": "\\dotsb", 4864 "\\bigwedge": "\\dotsb", 4865 "\\biguplus": "\\dotsb", 4866 "\\bigcap": "\\dotsb", 4867 "\\bigcup": "\\dotsb", 4868 "\\prod": "\\dotsb", 4869 "\\sum": "\\dotsb", 4870 "\\bigotimes": "\\dotsb", 4871 "\\bigoplus": "\\dotsb", 4872 "\\bigodot": "\\dotsb", 4873 "\\bigsqcap": "\\dotsb", 4874 "\\bigsqcup": "\\dotsb", 4875 "\\bigtimes": "\\dotsb", 4876 "\\And": "\\dotsb", 4877 "\\longrightarrow": "\\dotsb", 4878 "\\Longrightarrow": "\\dotsb", 4879 "\\longleftarrow": "\\dotsb", 4880 "\\Longleftarrow": "\\dotsb", 4881 "\\longleftrightarrow": "\\dotsb", 4882 "\\Longleftrightarrow": "\\dotsb", 4883 "\\mapsto": "\\dotsb", 4884 "\\longmapsto": "\\dotsb", 4885 "\\hookrightarrow": "\\dotsb", 4886 "\\doteq": "\\dotsb", 4887 // Symbols whose definition starts with \mathbin: 4888 "\\mathbin": "\\dotsb", 4889 // Symbols whose definition starts with \mathrel: 4890 "\\mathrel": "\\dotsb", 4891 "\\relbar": "\\dotsb", 4892 "\\Relbar": "\\dotsb", 4893 "\\xrightarrow": "\\dotsb", 4894 "\\xleftarrow": "\\dotsb", 4895 // Symbols whose definition starts with \DOTSI: 4896 "\\DOTSI": "\\dotsi", 4897 "\\int": "\\dotsi", 4898 "\\oint": "\\dotsi", 4899 "\\iint": "\\dotsi", 4900 "\\iiint": "\\dotsi", 4901 "\\iiiint": "\\dotsi", 4902 "\\idotsint": "\\dotsi", 4903 // Symbols whose definition starts with \DOTSX: 4904 "\\DOTSX": "\\dotsx" 4905 }; 4906 4907 defineMacro("\\dots", function(context) { 4908 // TODO: If used in text mode, should expand to \textellipsis. 4909 // However, in Temml, \textellipsis and \ldots behave the same 4910 // (in text mode), and it's unlikely we'd see any of the math commands 4911 // that affect the behavior of \dots when in text mode. So fine for now 4912 // (until we support \ifmmode ... \else ... \fi). 4913 let thedots = "\\dotso"; 4914 const next = context.expandAfterFuture().text; 4915 if (next in dotsByToken) { 4916 thedots = dotsByToken[next]; 4917 } else if (next.slice(0, 4) === "\\not") { 4918 thedots = "\\dotsb"; 4919 } else if (next in symbols.math) { 4920 if (["bin", "rel"].includes(symbols.math[next].group)) { 4921 thedots = "\\dotsb"; 4922 } 4923 } 4924 return thedots; 4925 }); 4926 4927 const spaceAfterDots = { 4928 // \rightdelim@ checks for the following: 4929 ")": true, 4930 "]": true, 4931 "\\rbrack": true, 4932 "\\}": true, 4933 "\\rbrace": true, 4934 "\\rangle": true, 4935 "\\rceil": true, 4936 "\\rfloor": true, 4937 "\\rgroup": true, 4938 "\\rmoustache": true, 4939 "\\right": true, 4940 "\\bigr": true, 4941 "\\biggr": true, 4942 "\\Bigr": true, 4943 "\\Biggr": true, 4944 // \extra@ also tests for the following: 4945 $: true, 4946 // \extrap@ checks for the following: 4947 ";": true, 4948 ".": true, 4949 ",": true 4950 }; 4951 4952 defineMacro("\\dotso", function(context) { 4953 const next = context.future().text; 4954 if (next in spaceAfterDots) { 4955 return "\\ldots\\,"; 4956 } else { 4957 return "\\ldots"; 4958 } 4959 }); 4960 4961 defineMacro("\\dotsc", function(context) { 4962 const next = context.future().text; 4963 // \dotsc uses \extra@ but not \extrap@, instead specially checking for 4964 // ';' and '.', but doesn't check for ','. 4965 if (next in spaceAfterDots && next !== ",") { 4966 return "\\ldots\\,"; 4967 } else { 4968 return "\\ldots"; 4969 } 4970 }); 4971 4972 defineMacro("\\cdots", function(context) { 4973 const next = context.future().text; 4974 if (next in spaceAfterDots) { 4975 return "\\@cdots\\,"; 4976 } else { 4977 return "\\@cdots"; 4978 } 4979 }); 4980 4981 defineMacro("\\dotsb", "\\cdots"); 4982 defineMacro("\\dotsm", "\\cdots"); 4983 defineMacro("\\dotsi", "\\!\\cdots"); 4984 defineMacro("\\idotsint", "\\dotsi"); 4985 // amsmath doesn't actually define \dotsx, but \dots followed by a macro 4986 // starting with \DOTSX implies \dotso, and then \extra@ detects this case 4987 // and forces the added `\,`. 4988 defineMacro("\\dotsx", "\\ldots\\,"); 4989 4990 // \let\DOTSI\relax 4991 // \let\DOTSB\relax 4992 // \let\DOTSX\relax 4993 defineMacro("\\DOTSI", "\\relax"); 4994 defineMacro("\\DOTSB", "\\relax"); 4995 defineMacro("\\DOTSX", "\\relax"); 4996 4997 // Spacing, based on amsmath.sty's override of LaTeX defaults 4998 // \DeclareRobustCommand{\tmspace}[3]{% 4999 // \ifmmode\mskip#1#2\else\kern#1#3\fi\relax} 5000 defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"); 5001 // \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}} 5002 // TODO: math mode should use \thinmuskip 5003 defineMacro("\\,", "{\\tmspace+{3mu}{.1667em}}"); 5004 // \let\thinspace\, 5005 defineMacro("\\thinspace", "\\,"); 5006 // \def\>{\mskip\medmuskip} 5007 // \renewcommand{\:}{\tmspace+\medmuskip{.2222em}} 5008 // TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu 5009 defineMacro("\\>", "\\mskip{4mu}"); 5010 defineMacro("\\:", "{\\tmspace+{4mu}{.2222em}}"); 5011 // \let\medspace\: 5012 defineMacro("\\medspace", "\\:"); 5013 // \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}} 5014 // TODO: math mode should use \thickmuskip = 5mu plus 5mu 5015 defineMacro("\\;", "{\\tmspace+{5mu}{.2777em}}"); 5016 // \let\thickspace\; 5017 defineMacro("\\thickspace", "\\;"); 5018 // \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}} 5019 // TODO: math mode should use \thinmuskip 5020 defineMacro("\\!", "{\\tmspace-{3mu}{.1667em}}"); 5021 // \let\negthinspace\! 5022 defineMacro("\\negthinspace", "\\!"); 5023 // \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}} 5024 // TODO: math mode should use \medmuskip 5025 defineMacro("\\negmedspace", "{\\tmspace-{4mu}{.2222em}}"); 5026 // \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}} 5027 // TODO: math mode should use \thickmuskip 5028 defineMacro("\\negthickspace", "{\\tmspace-{5mu}{.277em}}"); 5029 // \def\enspace{\kern.5em } 5030 defineMacro("\\enspace", "\\kern.5em "); 5031 // \def\enskip{\hskip.5em\relax} 5032 defineMacro("\\enskip", "\\hskip.5em\\relax"); 5033 // \def\quad{\hskip1em\relax} 5034 defineMacro("\\quad", "\\hskip1em\\relax"); 5035 // \def\qquad{\hskip2em\relax} 5036 defineMacro("\\qquad", "\\hskip2em\\relax"); 5037 5038 defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax"); 5039 5040 // \tag@in@display form of \tag 5041 defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren"); 5042 defineMacro("\\tag@paren", "\\tag@literal{({#1})}"); 5043 defineMacro("\\tag@literal", (context) => { 5044 if (context.macros.get("\\df@tag")) { 5045 throw new ParseError("Multiple \\tag"); 5046 } 5047 return "\\gdef\\df@tag{\\text{#1}}"; 5048 }); 5049 defineMacro("\\notag", "\\nonumber"); 5050 defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}"); 5051 5052 // \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin 5053 // {\operator@font mod}\penalty900 5054 // \mkern5mu\nonscript\mskip-\medmuskip} 5055 // \newcommand{\pod}[1]{\allowbreak 5056 // \if@display\mkern18mu\else\mkern8mu\fi(#1)} 5057 // \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}} 5058 // \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu 5059 // \else\mkern12mu\fi{\operator@font mod}\,\,#1} 5060 // TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu 5061 defineMacro("\\bmod", "\\mathbin{\\text{mod}}"); 5062 defineMacro( 5063 "\\pod", 5064 "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)" 5065 ); 5066 defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"); 5067 defineMacro( 5068 "\\mod", 5069 "\\allowbreak" + 5070 "\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" + 5071 "{\\rm mod}\\,\\,#1" 5072 ); 5073 5074 ////////////////////////////////////////////////////////////////////// 5075 // LaTeX source2e 5076 5077 // \expandafter\let\expandafter\@normalcr 5078 // \csname\expandafter\@gobble\string\\ \endcsname 5079 // \DeclareRobustCommand\newline{\@normalcr\relax} 5080 defineMacro("\\newline", "\\\\\\relax"); 5081 5082 // \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@} 5083 // TODO: Doesn't normally work in math mode because \@ fails. 5084 defineMacro("\\TeX", "\\textrm{T}\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125em\\textrm{X}"); 5085 5086 defineMacro( 5087 "\\LaTeX", 5088 "\\textrm{L}\\kern-.35em\\raisebox{0.2em}{\\scriptstyle A}\\kern-.15em\\TeX" 5089 ); 5090 5091 defineMacro( 5092 "\\Temml", 5093 // eslint-disable-next-line max-len 5094 "\\textrm{T}\\kern-0.2em\\lower{0.2em}{\\textrm{E}}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}" 5095 ); 5096 5097 // \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace} 5098 // \def\@hspace#1{\hskip #1\relax} 5099 // \def\@hspacer#1{\vrule \@width\z@\nobreak 5100 // \hskip #1\hskip \z@skip} 5101 defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace"); 5102 defineMacro("\\@hspace", "\\hskip #1\\relax"); 5103 defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"); 5104 5105 defineMacro("\\colon", `\\mathpunct{\\char"3a}`); 5106 5107 ////////////////////////////////////////////////////////////////////// 5108 // mathtools.sty 5109 5110 defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}"); 5111 5112 //\providecommand\ordinarycolon{:} 5113 defineMacro("\\ordinarycolon", `\\char"3a`); 5114 // Raise to center on the math axis, as closely as possible. 5115 defineMacro("\\vcentcolon", "\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}}"); 5116 // \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}} 5117 defineMacro("\\coloneq", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2212}'); 5118 // \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}} 5119 defineMacro("\\Coloneq", '\\mathrel{\\char"2237\\char"2212}'); 5120 // \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon} 5121 defineMacro("\\Eqqcolon", '\\mathrel{\\char"3d\\char"2237}'); 5122 // \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon} 5123 defineMacro("\\Eqcolon", '\\mathrel{\\char"2212\\char"2237}'); 5124 // \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx} 5125 defineMacro("\\colonapprox", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"2248}'); 5126 // \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx} 5127 defineMacro("\\Colonapprox", '\\mathrel{\\char"2237\\char"2248}'); 5128 // \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim} 5129 defineMacro("\\colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); 5130 // \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim} 5131 defineMacro("\\Colonsim", '\\mathrel{\\raisebox{0.035em}{\\ordinarycolon}\\char"223c}'); 5132 5133 ////////////////////////////////////////////////////////////////////// 5134 // colonequals.sty 5135 5136 // Alternate names for mathtools's macros: 5137 defineMacro("\\ratio", "\\vcentcolon"); 5138 defineMacro("\\coloncolon", "\\dblcolon"); 5139 defineMacro("\\colonequals", "\\coloneqq"); 5140 defineMacro("\\coloncolonequals", "\\Coloneqq"); 5141 defineMacro("\\equalscolon", "\\eqqcolon"); 5142 defineMacro("\\equalscoloncolon", "\\Eqqcolon"); 5143 defineMacro("\\colonminus", "\\coloneq"); 5144 defineMacro("\\coloncolonminus", "\\Coloneq"); 5145 defineMacro("\\minuscolon", "\\eqcolon"); 5146 defineMacro("\\minuscoloncolon", "\\Eqcolon"); 5147 // \colonapprox name is same in mathtools and colonequals. 5148 defineMacro("\\coloncolonapprox", "\\Colonapprox"); 5149 // \colonsim name is same in mathtools and colonequals. 5150 defineMacro("\\coloncolonsim", "\\Colonsim"); 5151 5152 // Present in newtxmath, pxfonts and txfonts 5153 defineMacro("\\notni", "\\mathrel{\\char`\u220C}"); 5154 defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"); 5155 defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"); 5156 5157 ////////////////////////////////////////////////////////////////////// 5158 // From amsopn.sty 5159 defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"); 5160 defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"); 5161 defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\text{lim}}}"); 5162 defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\text{lim}}}"); 5163 defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\text{lim}}}"); 5164 defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\text{lim}}}"); 5165 5166 defineMacro("\\centerdot", "{\\medspace\\rule{0.167em}{0.189em}\\medspace}"); 5167 5168 ////////////////////////////////////////////////////////////////////// 5169 // statmath.sty 5170 // https://ctan.math.illinois.edu/macros/latex/contrib/statmath/statmath.pdf 5171 5172 defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"); 5173 defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"); 5174 defineMacro("\\plim", "\\DOTSB\\operatorname*{plim}"); 5175 5176 ////////////////////////////////////////////////////////////////////// 5177 // MnSymbol.sty 5178 5179 defineMacro("\\leftmodels", "\\mathop{\\reflectbox{$\\models$}}"); 5180 5181 ////////////////////////////////////////////////////////////////////// 5182 // braket.sty 5183 // http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf 5184 5185 defineMacro("\\bra", "\\mathinner{\\langle{#1}|}"); 5186 defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}"); 5187 defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}"); 5188 defineMacro("\\Bra", "\\left\\langle#1\\right|"); 5189 defineMacro("\\Ket", "\\left|#1\\right\\rangle"); 5190 // A helper for \Braket and \Set 5191 const replaceVert = (argStr, match) => { 5192 const ch = match[0] === "|" ? "\\vert" : "\\Vert"; 5193 const replaceStr = `}\\,\\middle$ch}\\,{`; 5194 return argStr.slice(0, match.index) + replaceStr + argStr.slice(match.index + match[0].length) 5195 }; 5196 defineMacro("\\Braket", function(context) { 5197 let argStr = recreateArgStr(context); 5198 const regEx = /\|\||\||\\\|/g; 5199 let match; 5200 while ((match = regEx.exec(argStr)) !== null) { 5201 argStr = replaceVert(argStr, match); 5202 } 5203 return "\\left\\langle{" + argStr + "}\\right\\rangle" 5204 }); 5205 defineMacro("\\Set", function(context) { 5206 let argStr = recreateArgStr(context); 5207 const match = /\|\||\||\\\|/.exec(argStr); 5208 if (match) { 5209 argStr = replaceVert(argStr, match); 5210 } 5211 return "\\left\\{\\:{" + argStr + "}\\:\\right\\}" 5212 }); 5213 defineMacro("\\set", function(context) { 5214 const argStr = recreateArgStr(context); 5215 return "\\{{" + argStr.replace(/\|/, "}\\mid{") + "}\\}" 5216 }); 5217 5218 ////////////////////////////////////////////////////////////////////// 5219 // actuarialangle.dtx 5220 defineMacro("\\angln", "{\\angl n}"); 5221 5222 ////////////////////////////////////////////////////////////////////// 5223 // derivative.sty 5224 defineMacro("\\odv", "\\@ifstar\\odv@next\\odv@numerator"); 5225 defineMacro("\\odv@numerator", "\\frac{\\mathrm{d}#1}{\\mathrm{d}#2}"); 5226 defineMacro("\\odv@next", "\\frac{\\mathrm{d}}{\\mathrm{d}#2}#1"); 5227 defineMacro("\\pdv", "\\@ifstar\\pdv@next\\pdv@numerator"); 5228 5229 const pdvHelper = args => { 5230 const numerator = args[0][0].text; 5231 const denoms = stringFromArg(args[1]).split(","); 5232 const power = String(denoms.length); 5233 const numOp = power === "1" ? "\\partial" : `\\partial^$power}`; 5234 let denominator = ""; 5235 denoms.map(e => { denominator += "\\partial " + e.trim() + "\\,";}); 5236 return [numerator, numOp, denominator.replace(/\\,$/, "")] 5237 }; 5238 defineMacro("\\pdv@numerator", function(context) { 5239 const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); 5240 return `\\frac{$numOp} $numerator}}{$denominator}}` 5241 }); 5242 defineMacro("\\pdv@next", function(context) { 5243 const [numerator, numOp, denominator] = pdvHelper(context.consumeArgs(2)); 5244 return `\\frac{$numOp}}{$denominator}} $numerator}` 5245 }); 5246 5247 ////////////////////////////////////////////////////////////////////// 5248 // upgreek.dtx 5249 defineMacro("\\upalpha", "\\up@greek{\\alpha}"); 5250 defineMacro("\\upbeta", "\\up@greek{\\beta}"); 5251 defineMacro("\\upgamma", "\\up@greek{\\gamma}"); 5252 defineMacro("\\updelta", "\\up@greek{\\delta}"); 5253 defineMacro("\\upepsilon", "\\up@greek{\\epsilon}"); 5254 defineMacro("\\upzeta", "\\up@greek{\\zeta}"); 5255 defineMacro("\\upeta", "\\up@greek{\\eta}"); 5256 defineMacro("\\uptheta", "\\up@greek{\\theta}"); 5257 defineMacro("\\upiota", "\\up@greek{\\iota}"); 5258 defineMacro("\\upkappa", "\\up@greek{\\kappa}"); 5259 defineMacro("\\uplambda", "\\up@greek{\\lambda}"); 5260 defineMacro("\\upmu", "\\up@greek{\\mu}"); 5261 defineMacro("\\upnu", "\\up@greek{\\nu}"); 5262 defineMacro("\\upxi", "\\up@greek{\\xi}"); 5263 defineMacro("\\upomicron", "\\up@greek{\\omicron}"); 5264 defineMacro("\\uppi", "\\up@greek{\\pi}"); 5265 defineMacro("\\upalpha", "\\up@greek{\\alpha}"); 5266 defineMacro("\\uprho", "\\up@greek{\\rho}"); 5267 defineMacro("\\upsigma", "\\up@greek{\\sigma}"); 5268 defineMacro("\\uptau", "\\up@greek{\\tau}"); 5269 defineMacro("\\upupsilon", "\\up@greek{\\upsilon}"); 5270 defineMacro("\\upphi", "\\up@greek{\\phi}"); 5271 defineMacro("\\upchi", "\\up@greek{\\chi}"); 5272 defineMacro("\\uppsi", "\\up@greek{\\psi}"); 5273 defineMacro("\\upomega", "\\up@greek{\\omega}"); 5274 5275 ////////////////////////////////////////////////////////////////////// 5276 // cmll package 5277 defineMacro("\\invamp", '\\mathbin{\\char"214b}'); 5278 defineMacro("\\parr", '\\mathbin{\\char"214b}'); 5279 defineMacro("\\with", '\\mathbin{\\char"26}'); 5280 defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}'); 5281 defineMacro("\\multimapboth", '\\mathrel{\\char"29df}'); 5282 defineMacro("\\scoh", '{\\mkern5mu\\char"2322\\mkern5mu}'); 5283 defineMacro("\\sincoh", '{\\mkern5mu\\char"2323\\mkern5mu}'); 5284 defineMacro("\\coh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2322}}} 5285 {\\smash{\\lower4mu{\\char"2323}}}\\mkern5mu}`); 5286 defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{\\char"2323}}} 5287 {\\smash{\\lower4mu{\\char"2322}}}\\mkern5mu}`); 5288 5289 5290 ////////////////////////////////////////////////////////////////////// 5291 // chemstyle package 5292 defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}"); 5293 5294 /* eslint-disable */ 5295 /* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ 5296 /* vim: set ts=2 et sw=2 tw=80: */ 5297 5298 /************************************************************* 5299 * 5300 * Temml mhchem.js 5301 * 5302 * This file implements a Temml version of mhchem version 3.3.0. 5303 * It is adapted from MathJax/extensions/TeX/mhchem.js 5304 * It differs from the MathJax version as follows: 5305 * 1. The interface is changed so that it can be called from Temml, not MathJax. 5306 * 2. \rlap and \llap are replaced with \mathrlap and \mathllap. 5307 * 3. The reaction arrow code is simplified. All reaction arrows are rendered 5308 * using Temml extensible arrows instead of building non-extensible arrows. 5309 * 4. The ~bond forms are composed entirely of \rule elements. 5310 * 5. Two dashes in _getBond are wrapped in braces to suppress spacing. i.e., {-} 5311 * 6. The electron dot uses \textbullet instead of \bullet. 5312 * 7. \smash[T] has been removed. (WebKit hides anything inside \smash{…}) 5313 * 5314 * This code, as other Temml code, is released under the MIT license. 5315 * 5316 * /************************************************************* 5317 * 5318 * MathJax/extensions/TeX/mhchem.js 5319 * 5320 * Implements the \ce command for handling chemical formulas 5321 * from the mhchem LaTeX package. 5322 * 5323 * --------------------------------------------------------------------- 5324 * 5325 * Copyright (c) 2011-2015 The MathJax Consortium 5326 * Copyright (c) 2015-2018 Martin Hensel 5327 * 5328 * Licensed under the Apache License, Version 2.0 (the "License"); 5329 * you may not use this file except in compliance with the License. 5330 * You may obtain a copy of the License at 5331 * 5332 * http://www.apache.org/licenses/LICENSE-2.0 5333 * 5334 * Unless required by applicable law or agreed to in writing, software 5335 * distributed under the License is distributed on an "AS IS" BASIS, 5336 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5337 * See the License for the specific language governing permissions and 5338 * limitations under the License. 5339 */ 5340 5341 // 5342 // Coding Style 5343 // - use '' for identifiers that can by minified/uglified 5344 // - use "" for strings that need to stay untouched 5345 5346 // version: "3.3.0" for MathJax and Temml 5347 5348 5349 // Add \ce, \pu, and \tripleDash to the Temml macros. 5350 5351 defineMacro("\\ce", function(context) { 5352 return chemParse(context.consumeArgs(1)[0], "ce") 5353 }); 5354 5355 defineMacro("\\pu", function(context) { 5356 return chemParse(context.consumeArgs(1)[0], "pu"); 5357 }); 5358 5359 // Math fonts do not include glyphs for the ~ form of bonds. So we'll send path geometry 5360 // So we'll compose characters built from \rule elements. 5361 defineMacro("\\uniDash", `{\\rule{0.672em}{0.06em}}`) 5362 defineMacro("\\triDash", `{\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}\\kern2mu\\rule{0.15em}{0.06em}}`) 5363 defineMacro("\\tripleDash", `\\kern0.075em\\raise0.25em{\\triDash}\\kern0.075em`) 5364 defineMacro("\\tripleDashOverLine", `\\kern0.075em\\mathrlap{\\raise0.125em{\\uniDash}}\\raise0.34em{\\triDash}\\kern0.075em`) 5365 defineMacro("\\tripleDashOverDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\triDash}}\\raise0.27em{\\uniDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) 5366 defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap{\\raise0.48em{\\uniDash}}\\raise0.27em{\\triDash}}{\\raise0.05em{\\uniDash}}\\kern0.075em`) 5367 5368 // 5369 // This is the main function for handing the \ce and \pu commands. 5370 // It takes the argument to \ce or \pu and returns the corresponding TeX string. 5371 // 5372 5373 var chemParse = function (tokens, stateMachine) { 5374 // Recreate the argument string from Temml's array of tokens. 5375 var str = ""; 5376 var expectedLoc = tokens.length && tokens[tokens.length - 1].loc.start 5377 for (var i = tokens.length - 1; i >= 0; i--) { 5378 if(tokens[i].loc.start > expectedLoc) { 5379 // context.consumeArgs has eaten a space. 5380 str += " "; 5381 expectedLoc = tokens[i].loc.start; 5382 } 5383 str += tokens[i].text; 5384 expectedLoc += tokens[i].text.length; 5385 } 5386 // Call the mhchem core parser. 5387 var tex = texify.go(mhchemParser.go(str, stateMachine)); 5388 return tex; 5389 }; 5390 5391 // 5392 // Core parser for mhchem syntax (recursive) 5393 // 5394 /** @type {MhchemParser} */ 5395 var mhchemParser = { 5396 // 5397 // Parses mchem \ce syntax 5398 // 5399 // Call like 5400 // go("H2O"); 5401 // 5402 go: function (input, stateMachine) { 5403 if (!input) { return []; } 5404 if (stateMachine === undefined) { stateMachine = 'ce'; } 5405 var state = '0'; 5406 5407 // 5408 // String buffers for parsing: 5409 // 5410 // buffer.a == amount 5411 // buffer.o == element 5412 // buffer.b == left-side superscript 5413 // buffer.p == left-side subscript 5414 // buffer.q == right-side subscript 5415 // buffer.d == right-side superscript 5416 // 5417 // buffer.r == arrow 5418 // buffer.rdt == arrow, script above, type 5419 // buffer.rd == arrow, script above, content 5420 // buffer.rqt == arrow, script below, type 5421 // buffer.rq == arrow, script below, content 5422 // 5423 // buffer.text_ 5424 // buffer.rm 5425 // etc. 5426 // 5427 // buffer.parenthesisLevel == int, starting at 0 5428 // buffer.sb == bool, space before 5429 // buffer.beginsWithBond == bool 5430 // 5431 // These letters are also used as state names. 5432 // 5433 // Other states: 5434 // 0 == begin of main part (arrow/operator unlikely) 5435 // 1 == next entity 5436 // 2 == next entity (arrow/operator unlikely) 5437 // 3 == next atom 5438 // c == macro 5439 // 5440 /** @type {Buffer} */ 5441 var buffer = {}; 5442 buffer['parenthesisLevel'] = 0; 5443 5444 input = input.replace(/\n/g, " "); 5445 input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-"); 5446 input = input.replace(/[\u2026]/g, "..."); 5447 5448 // 5449 // Looks through mhchemParser.transitions, to execute a matching action 5450 // (recursive) 5451 // 5452 var lastInput; 5453 var watchdog = 10; 5454 /** @type {ParserOutput[]} */ 5455 var output = []; 5456 while (true) { 5457 if (lastInput !== input) { 5458 watchdog = 10; 5459 lastInput = input; 5460 } else { 5461 watchdog--; 5462 } 5463 // 5464 // Find actions in transition table 5465 // 5466 var machine = mhchemParser.stateMachines[stateMachine]; 5467 var t = machine.transitions[state] || machine.transitions['*']; 5468 iterateTransitions: 5469 for (var i=0; i<t.length; i++) { 5470 var matches = mhchemParser.patterns.match_(t[i].pattern, input); 5471 if (matches) { 5472 // 5473 // Execute actions 5474 // 5475 var task = t[i].task; 5476 for (var iA=0; iA<task.action_.length; iA++) { 5477 var o; 5478 // 5479 // Find and execute action 5480 // 5481 if (machine.actions[task.action_[iA].type_]) { 5482 o = machine.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option); 5483 } else if (mhchemParser.actions[task.action_[iA].type_]) { 5484 o = mhchemParser.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option); 5485 } else { 5486 throw ["MhchemBugA", "mhchem bug A. Please report. (" + task.action_[iA].type_ + ")"]; // Trying to use non-existing action 5487 } 5488 // 5489 // Add output 5490 // 5491 mhchemParser.concatArray(output, o); 5492 } 5493 // 5494 // Set next state, 5495 // Shorten input, 5496 // Continue with next character 5497 // (= apply only one transition per position) 5498 // 5499 state = task.nextState || state; 5500 if (input.length > 0) { 5501 if (!task.revisit) { 5502 input = matches.remainder; 5503 } 5504 if (!task.toContinue) { 5505 break iterateTransitions; 5506 } 5507 } else { 5508 return output; 5509 } 5510 } 5511 } 5512 // 5513 // Prevent infinite loop 5514 // 5515 if (watchdog <= 0) { 5516 throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character 5517 } 5518 } 5519 }, 5520 concatArray: function (a, b) { 5521 if (b) { 5522 if (Array.isArray(b)) { 5523 for (var iB=0; iB<b.length; iB++) { 5524 a.push(b[iB]); 5525 } 5526 } else { 5527 a.push(b); 5528 } 5529 } 5530 }, 5531 5532 patterns: { 5533 // 5534 // Matching patterns 5535 // either regexps or function that return null or {match_:"a", remainder:"bc"} 5536 // 5537 patterns: { 5538 // property names must not look like integers ("2") for correct property traversal order, later on 5539 'empty': /^$/, 5540 'else': /^./, 5541 'else2': /^./, 5542 'space': /^\s/, 5543 'space A': /^\s(?=[A-Z\\$])/, 5544 'space$': /^\s$/, 5545 'a-z': /^[a-z]/, 5546 'x': /^x/, 5547 'x$': /^x$/, 5548 'i$': /^i$/, 5549 'letters': /^(?:[a-zA-Z\u03B1-\u03C9\u0391-\u03A9?@]|(?:\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/, 5550 '\\greek': /^\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))/, 5551 'one lowercase latin letter $': /^(?:([a-z])(?:$|[^a-zA-Z]))$/, 5552 '$one lowercase latin letter$ $': /^\$(?:([a-z])(?:$|[^a-zA-Z]))\$$/, 5553 'one lowercase greek letter $': /^(?:\$?[\u03B1-\u03C9]\$?|\$?\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\s*\$?)(?:\s+|\{\}|(?![a-zA-Z]))$/, 5554 'digits': /^[0-9]+/, 5555 '-9.,9': /^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/, 5556 '-9.,9 no missing 0': /^[+\-]?[0-9]+(?:[.,][0-9]+)?/, 5557 '(-)(9.,9)(e)(99)': function (input) { 5558 var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))?(\((?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))\))?(?:([eE]|\s*(\*|x|\\times|\u00D7)\s*10\^)([+\-]?[0-9]+|\{[+\-]?[0-9]+\}))?/); 5559 if (m && m[0]) { 5560 return { match_: m.splice(1), remainder: input.substr(m[0].length) }; 5561 } 5562 return null; 5563 }, 5564 '(-)(9)^(-9)': function (input) { 5565 var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/); 5566 if (m && m[0]) { 5567 return { match_: m.splice(1), remainder: input.substr(m[0].length) }; 5568 } 5569 return null; 5570 }, 5571 'state of aggregation $': function (input) { // ... or crystal system 5572 var a = mhchemParser.patterns.findObserveGroups(input, "", /^\([a-z]{1,3}(?=[\),])/, ")", ""); // (aq), (aq,$\infty$), (aq, sat) 5573 if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) { return a; } // AND end of 'phrase' 5574 var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/); // OR crystal system ($o$) (\ca$c$) 5575 if (m) { 5576 return { match_: m[0], remainder: input.substr(m[0].length) }; 5577 } 5578 return null; 5579 }, 5580 '_{(state of aggregation)}$': /^_\{(\([a-z]{1,3}\))\}/, 5581 '{[(': /^(?:\\\{|\[|\()/, 5582 ')]}': /^(?:\)|\]|\\\})/, 5583 ', ': /^[,;]\s*/, 5584 ',': /^[,;]/, 5585 '.': /^[.]/, 5586 '. ': /^([.\u22C5\u00B7\u2022])\s*/, 5587 '...': /^\.\.\.(?=$|[^.])/, 5588 '* ': /^([*])\s*/, 5589 '^{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^{", "", "", "}"); }, 5590 '^($...$)': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^", "$", "$", ""); }, 5591 '^a': /^\^([0-9]+|[^\\_])/, 5592 '^\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); }, 5593 '^\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "^", /^\\[a-zA-Z]+\{/, "}", ""); }, 5594 '^\\x': /^\^(\\[a-zA-Z]+)\s*/, 5595 '^(-1)': /^\^(-?\d+)/, 5596 '\'': /^'/, 5597 '_{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_{", "", "", "}"); }, 5598 '_($...$)': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_", "$", "$", ""); }, 5599 '_9': /^_([+\-]?[0-9]+|[^\\])/, 5600 '_\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); }, 5601 '_\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "_", /^\\[a-zA-Z]+\{/, "}", ""); }, 5602 '_\\x': /^_(\\[a-zA-Z]+)\s*/, 5603 '^_': /^(?:\^(?=_)|\_(?=\^)|[\^_]$)/, 5604 '{}': /^\{\}/, 5605 '{...}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", "{", "}", ""); }, 5606 '{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "{", "", "", "}"); }, 5607 '$...$': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", "$", "$", ""); }, 5608 '${(...)}$': function (input) { return mhchemParser.patterns.findObserveGroups(input, "${", "", "", "}$"); }, 5609 '$(...)$': function (input) { return mhchemParser.patterns.findObserveGroups(input, "$", "", "", "$"); }, 5610 '=<>': /^[=<>]/, 5611 '#': /^[#\u2261]/, 5612 '+': /^\+/, 5613 '-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, // -space -, -; -] -/ -$ -state-of-aggregation 5614 '-9': /^-(?=[0-9])/, 5615 '- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/, 5616 '-': /^-/, 5617 'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/, 5618 'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/, 5619 'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/, 5620 '\\bond{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\bond{", "", "", "}"); }, 5621 '->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/, 5622 'CMT': /^[CMT](?=\[)/, 5623 '[(...)]': function (input) { return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]"); }, 5624 '1st-level escape': /^(&|\\\\|\\hline)\s*/, 5625 '\\,': /^(?:\\[,\ ;:])/, // \\x - but output no space before 5626 '\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); }, 5627 '\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", ""); }, 5628 '\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/, 5629 '\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/, 5630 'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, // only those with numbers in front, because the others will be formatted correctly anyway 5631 'others': /^[\/~|]/, 5632 '\\frac{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\frac{", "", "", "}", "{", "", "", "}"); }, 5633 '\\overset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\overset{", "", "", "}", "{", "", "", "}"); }, 5634 '\\underset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underset{", "", "", "}", "{", "", "", "}"); }, 5635 '\\underbrace{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underbrace{", "", "", "}_", "{", "", "", "}"); }, 5636 '\\color{(...)}0': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}"); }, 5637 '\\color{(...)}{(...)}1': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}", "{", "", "", "}"); }, 5638 '\\color(...){(...)}2': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}"); }, 5639 '\\ce{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\ce{", "", "", "}"); }, 5640 'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, 5641 'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, // 0 could be oxidation or charge 5642 'roman numeral': /^[IVX]+/, 5643 '1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/, 5644 'amount': function (input) { 5645 var match; 5646 // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing 5647 match = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/); 5648 if (match) { 5649 return { match_: match[0], remainder: input.substr(match[0].length) }; 5650 } 5651 var a = mhchemParser.patterns.findObserveGroups(input, "", "$", "$", ""); 5652 if (a) { // e.g. $2n-1$, $-$ 5653 match = a.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/); 5654 if (match) { 5655 return { match_: match[0], remainder: input.substr(match[0].length) }; 5656 } 5657 } 5658 return null; 5659 }, 5660 'amount2': function (input) { return this['amount'](input); }, 5661 '(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/, 5662 'formula$': function (input) { 5663 if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula 5664 var match = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/); 5665 if (match) { 5666 return { match_: match[0], remainder: input.substr(match[0].length) }; 5667 } 5668 return null; 5669 }, 5670 'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/, 5671 '/': /^\s*(\/)\s*/, 5672 '//': /^\s*(\/\/)\s*/, 5673 '*': /^\s*[*.]\s*/ 5674 }, 5675 findObserveGroups: function (input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) { 5676 /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */ 5677 var _match = function (input, pattern) { 5678 if (typeof pattern === "string") { 5679 if (input.indexOf(pattern) !== 0) { return null; } 5680 return pattern; 5681 } else { 5682 var match = input.match(pattern); 5683 if (!match) { return null; } 5684 return match[0]; 5685 } 5686 }; 5687 /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */ 5688 var _findObserveGroups = function (input, i, endChars) { 5689 var braces = 0; 5690 while (i < input.length) { 5691 var a = input.charAt(i); 5692 var match = _match(input.substr(i), endChars); 5693 if (match !== null && braces === 0) { 5694 return { endMatchBegin: i, endMatchEnd: i + match.length }; 5695 } else if (a === "{") { 5696 braces++; 5697 } else if (a === "}") { 5698 if (braces === 0) { 5699 throw ["ExtraCloseMissingOpen", "Extra close brace or missing open brace"]; 5700 } else { 5701 braces--; 5702 } 5703 } 5704 i++; 5705 } 5706 if (braces > 0) { 5707 return null; 5708 } 5709 return null; 5710 }; 5711 var match = _match(input, begExcl); 5712 if (match === null) { return null; } 5713 input = input.substr(match.length); 5714 match = _match(input, begIncl); 5715 if (match === null) { return null; } 5716 var e = _findObserveGroups(input, match.length, endIncl || endExcl); 5717 if (e === null) { return null; } 5718 var match1 = input.substring(0, (endIncl ? e.endMatchEnd : e.endMatchBegin)); 5719 if (!(beg2Excl || beg2Incl)) { 5720 return { 5721 match_: match1, 5722 remainder: input.substr(e.endMatchEnd) 5723 }; 5724 } else { 5725 var group2 = this.findObserveGroups(input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl); 5726 if (group2 === null) { return null; } 5727 /** @type {string[]} */ 5728 var matchRet = [match1, group2.match_]; 5729 return { 5730 match_: (combine ? matchRet.join("") : matchRet), 5731 remainder: group2.remainder 5732 }; 5733 } 5734 }, 5735 5736 // 5737 // Matching function 5738 // e.g. match("a", input) will look for the regexp called "a" and see if it matches 5739 // returns null or {match_:"a", remainder:"bc"} 5740 // 5741 match_: function (m, input) { 5742 var pattern = mhchemParser.patterns.patterns[m]; 5743 if (pattern === undefined) { 5744 throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern 5745 } else if (typeof pattern === "function") { 5746 return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser 5747 } else { // RegExp 5748 var match = input.match(pattern); 5749 if (match) { 5750 var mm; 5751 if (match[2]) { 5752 mm = [ match[1], match[2] ]; 5753 } else if (match[1]) { 5754 mm = match[1]; 5755 } else { 5756 mm = match[0]; 5757 } 5758 return { match_: mm, remainder: input.substr(match[0].length) }; 5759 } 5760 return null; 5761 } 5762 } 5763 }, 5764 5765 // 5766 // Generic state machine actions 5767 // 5768 actions: { 5769 'a=': function (buffer, m) { buffer.a = (buffer.a || "") + m; }, 5770 'b=': function (buffer, m) { buffer.b = (buffer.b || "") + m; }, 5771 'p=': function (buffer, m) { buffer.p = (buffer.p || "") + m; }, 5772 'o=': function (buffer, m) { buffer.o = (buffer.o || "") + m; }, 5773 'q=': function (buffer, m) { buffer.q = (buffer.q || "") + m; }, 5774 'd=': function (buffer, m) { buffer.d = (buffer.d || "") + m; }, 5775 'rm=': function (buffer, m) { buffer.rm = (buffer.rm || "") + m; }, 5776 'text=': function (buffer, m) { buffer.text_ = (buffer.text_ || "") + m; }, 5777 'insert': function (buffer, m, a) { return { type_: a }; }, 5778 'insert+p1': function (buffer, m, a) { return { type_: a, p1: m }; }, 5779 'insert+p1+p2': function (buffer, m, a) { return { type_: a, p1: m[0], p2: m[1] }; }, 5780 'copy': function (buffer, m) { return m; }, 5781 'rm': function (buffer, m) { return { type_: 'rm', p1: m || ""}; }, 5782 'text': function (buffer, m) { return mhchemParser.go(m, 'text'); }, 5783 '{text}': function (buffer, m) { 5784 var ret = [ "{" ]; 5785 mhchemParser.concatArray(ret, mhchemParser.go(m, 'text')); 5786 ret.push("}"); 5787 return ret; 5788 }, 5789 'tex-math': function (buffer, m) { return mhchemParser.go(m, 'tex-math'); }, 5790 'tex-math tight': function (buffer, m) { return mhchemParser.go(m, 'tex-math tight'); }, 5791 'bond': function (buffer, m, k) { return { type_: 'bond', kind_: k || m }; }, 5792 'color0-output': function (buffer, m) { return { type_: 'color0', color: m[0] }; }, 5793 'ce': function (buffer, m) { return mhchemParser.go(m); }, 5794 '1/2': function (buffer, m) { 5795 /** @type {ParserOutput[]} */ 5796 var ret = []; 5797 if (m.match(/^[+\-]/)) { 5798 ret.push(m.substr(0, 1)); 5799 m = m.substr(1); 5800 } 5801 var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/); 5802 n[1] = n[1].replace(/\$/g, ""); 5803 ret.push({ type_: 'frac', p1: n[1], p2: n[2] }); 5804 if (n[3]) { 5805 n[3] = n[3].replace(/\$/g, ""); 5806 ret.push({ type_: 'tex-math', p1: n[3] }); 5807 } 5808 return ret; 5809 }, 5810 '9,9': function (buffer, m) { return mhchemParser.go(m, '9,9'); } 5811 }, 5812 // 5813 // createTransitions 5814 // convert { 'letter': { 'state': { action_: 'output' } } } to { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] } 5815 // with expansion of 'a|b' to 'a' and 'b' (at 2 places) 5816 // 5817 createTransitions: function (o) { 5818 var pattern, state; 5819 /** @type {string[]} */ 5820 var stateArray; 5821 var i; 5822 // 5823 // 1. Collect all states 5824 // 5825 /** @type {Transitions} */ 5826 var transitions = {}; 5827 for (pattern in o) { 5828 for (state in o[pattern]) { 5829 stateArray = state.split("|"); 5830 o[pattern][state].stateArray = stateArray; 5831 for (i=0; i<stateArray.length; i++) { 5832 transitions[stateArray[i]] = []; 5833 } 5834 } 5835 } 5836 // 5837 // 2. Fill states 5838 // 5839 for (pattern in o) { 5840 for (state in o[pattern]) { 5841 stateArray = o[pattern][state].stateArray || []; 5842 for (i=0; i<stateArray.length; i++) { 5843 // 5844 // 2a. Normalize actions into array: 'text=' ==> [{type_:'text='}] 5845 // (Note to myself: Resolving the function here would be problematic. It would need .bind (for *this*) and currying (for *option*).) 5846 // 5847 /** @type {any} */ 5848 var p = o[pattern][state]; 5849 if (p.action_) { 5850 p.action_ = [].concat(p.action_); 5851 for (var k=0; k<p.action_.length; k++) { 5852 if (typeof p.action_[k] === "string") { 5853 p.action_[k] = { type_: p.action_[k] }; 5854 } 5855 } 5856 } else { 5857 p.action_ = []; 5858 } 5859 // 5860 // 2.b Multi-insert 5861 // 5862 var patternArray = pattern.split("|"); 5863 for (var j=0; j<patternArray.length; j++) { 5864 if (stateArray[i] === '*') { // insert into all 5865 for (var t in transitions) { 5866 transitions[t].push({ pattern: patternArray[j], task: p }); 5867 } 5868 } else { 5869 transitions[stateArray[i]].push({ pattern: patternArray[j], task: p }); 5870 } 5871 } 5872 } 5873 } 5874 } 5875 return transitions; 5876 }, 5877 stateMachines: {} 5878 }; 5879 5880 // 5881 // Definition of state machines 5882 // 5883 mhchemParser.stateMachines = { 5884 // 5885 // \ce state machines 5886 // 5887 //#region ce 5888 'ce': { // main parser 5889 transitions: mhchemParser.createTransitions({ 5890 'empty': { 5891 '*': { action_: 'output' } }, 5892 'else': { 5893 '0|1|2': { action_: 'beginsWithBond=false', revisit: true, toContinue: true } }, 5894 'oxidation$': { 5895 '0': { action_: 'oxidation-output' } }, 5896 'CMT': { 5897 'r': { action_: 'rdt=', nextState: 'rt' }, 5898 'rd': { action_: 'rqt=', nextState: 'rdt' } }, 5899 'arrowUpDown': { 5900 '0|1|2|as': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '1' } }, 5901 'uprightEntities': { 5902 '0|1|2': { action_: [ 'o=', 'output' ], nextState: '1' } }, 5903 'orbital': { 5904 '0|1|2|3': { action_: 'o=', nextState: 'o' } }, 5905 '->': { 5906 '0|1|2|3': { action_: 'r=', nextState: 'r' }, 5907 'a|as': { action_: [ 'output', 'r=' ], nextState: 'r' }, 5908 '*': { action_: [ 'output', 'r=' ], nextState: 'r' } }, 5909 '+': { 5910 'o': { action_: 'd= kv', nextState: 'd' }, 5911 'd|D': { action_: 'd=', nextState: 'd' }, 5912 'q': { action_: 'd=', nextState: 'qd' }, 5913 'qd|qD': { action_: 'd=', nextState: 'qd' }, 5914 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' }, 5915 '3': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } }, 5916 'amount': { 5917 '0|2': { action_: 'a=', nextState: 'a' } }, 5918 'pm-operator': { 5919 '0|1|2|a|as': { action_: [ 'sb=false', 'output', { type_: 'operator', option: '\\pm' } ], nextState: '0' } }, 5920 'operator': { 5921 '0|1|2|a|as': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } }, 5922 '-$': { 5923 'o|q': { action_: [ 'charge or bond', 'output' ], nextState: 'qd' }, 5924 'd': { action_: 'd=', nextState: 'd' }, 5925 'D': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' }, 5926 'q': { action_: 'd=', nextState: 'qd' }, 5927 'qd': { action_: 'd=', nextState: 'qd' }, 5928 'qD|dq': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } }, 5929 '-9': { 5930 '3|o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '3' } }, 5931 '- orbital overlap': { 5932 'o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' }, 5933 'd': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' } }, 5934 '-': { 5935 '0|1|2': { action_: [ { type_: 'output', option: 1 }, 'beginsWithBond=true', { type_: 'bond', option: "-" } ], nextState: '3' }, 5936 '3': { action_: { type_: 'bond', option: "-" } }, 5937 'a': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' }, 5938 'as': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "-" } ], nextState: '3' }, 5939 'b': { action_: 'b=' }, 5940 'o': { action_: { type_: '- after o/d', option: false }, nextState: '2' }, 5941 'q': { action_: { type_: '- after o/d', option: false }, nextState: '2' }, 5942 'd|qd|dq': { action_: { type_: '- after o/d', option: true }, nextState: '2' }, 5943 'D|qD|p': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } }, 5944 'amount2': { 5945 '1|3': { action_: 'a=', nextState: 'a' } }, 5946 'letters': { 5947 '0|1|2|3|a|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, 5948 'q|dq': { action_: ['output', 'o='], nextState: 'o' }, 5949 'd|D|qd|qD': { action_: 'o after d', nextState: 'o' } }, 5950 'digits': { 5951 'o': { action_: 'q=', nextState: 'q' }, 5952 'd|D': { action_: 'q=', nextState: 'dq' }, 5953 'q': { action_: [ 'output', 'o=' ], nextState: 'o' }, 5954 'a': { action_: 'o=', nextState: 'o' } }, 5955 'space A': { 5956 'b|p|bp': {} }, 5957 'space': { 5958 'a': { nextState: 'as' }, 5959 '0': { action_: 'sb=false' }, 5960 '1|2': { action_: 'sb=true' }, 5961 'r|rt|rd|rdt|rdq': { action_: 'output', nextState: '0' }, 5962 '*': { action_: [ 'output', 'sb=true' ], nextState: '1'} }, 5963 '1st-level escape': { 5964 '1|2': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ] }, 5965 '*': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ], nextState: '0' } }, 5966 '[(...)]': { 5967 'r|rt': { action_: 'rd=', nextState: 'rd' }, 5968 'rd|rdt': { action_: 'rq=', nextState: 'rdq' } }, 5969 '...': { 5970 'o|d|D|dq|qd|qD': { action_: [ 'output', { type_: 'bond', option: "..." } ], nextState: '3' }, 5971 '*': { action_: [ { type_: 'output', option: 1 }, { type_: 'insert', option: 'ellipsis' } ], nextState: '1' } }, 5972 '. |* ': { 5973 '*': { action_: [ 'output', { type_: 'insert', option: 'addition compound' } ], nextState: '1' } }, 5974 'state of aggregation $': { 5975 '*': { action_: [ 'output', 'state of aggregation' ], nextState: '1' } }, 5976 '{[(': { 5977 'a|as|o': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' }, 5978 '0|1|2|3': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' }, 5979 '*': { action_: [ 'output', 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' } }, 5980 ')]}': { 5981 '0|1|2|3|b|p|bp|o': { action_: [ 'o=', 'parenthesisLevel--' ], nextState: 'o' }, 5982 'a|as|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=', 'parenthesisLevel--' ], nextState: 'o' } }, 5983 ', ': { 5984 '*': { action_: [ 'output', 'comma' ], nextState: '0' } }, 5985 '^_': { // ^ and _ without a sensible argument 5986 '*': { } }, 5987 '^{(...)}|^($...$)': { 5988 '0|1|2|as': { action_: 'b=', nextState: 'b' }, 5989 'p': { action_: 'b=', nextState: 'bp' }, 5990 '3|o': { action_: 'd= kv', nextState: 'D' }, 5991 'q': { action_: 'd=', nextState: 'qD' }, 5992 'd|D|qd|qD|dq': { action_: [ 'output', 'd=' ], nextState: 'D' } }, 5993 '^a|^\\x{}{}|^\\x{}|^\\x|\'': { 5994 '0|1|2|as': { action_: 'b=', nextState: 'b' }, 5995 'p': { action_: 'b=', nextState: 'bp' }, 5996 '3|o': { action_: 'd= kv', nextState: 'd' }, 5997 'q': { action_: 'd=', nextState: 'qd' }, 5998 'd|qd|D|qD': { action_: 'd=' }, 5999 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' } }, 6000 '_{(state of aggregation)}$': { 6001 'd|D|q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } }, 6002 '_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': { 6003 '0|1|2|as': { action_: 'p=', nextState: 'p' }, 6004 'b': { action_: 'p=', nextState: 'bp' }, 6005 '3|o': { action_: 'q=', nextState: 'q' }, 6006 'd|D': { action_: 'q=', nextState: 'dq' }, 6007 'q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } }, 6008 '=<>': { 6009 '0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: '3' } }, 6010 '#': { 6011 '0|1|2|3|a|as|o': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "#" } ], nextState: '3' } }, 6012 '{}': { 6013 '*': { action_: { type_: 'output', option: 1 }, nextState: '1' } }, 6014 '{...}': { 6015 '0|1|2|3|a|as|b|p|bp': { action_: 'o=', nextState: 'o' }, 6016 'o|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } }, 6017 '$...$': { 6018 'a': { action_: 'a=' }, // 2$n$ 6019 '0|1|2|3|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, // not 'amount' 6020 'as|o': { action_: 'o=' }, 6021 'q|d|D|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } }, 6022 '\\bond{(...)}': { 6023 '*': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: "3" } }, 6024 '\\frac{(...)}': { 6025 '*': { action_: [ { type_: 'output', option: 1 }, 'frac-output' ], nextState: '3' } }, 6026 '\\overset{(...)}': { 6027 '*': { action_: [ { type_: 'output', option: 2 }, 'overset-output' ], nextState: '3' } }, 6028 '\\underset{(...)}': { 6029 '*': { action_: [ { type_: 'output', option: 2 }, 'underset-output' ], nextState: '3' } }, 6030 '\\underbrace{(...)}': { 6031 '*': { action_: [ { type_: 'output', option: 2 }, 'underbrace-output' ], nextState: '3' } }, 6032 '\\color{(...)}{(...)}1|\\color(...){(...)}2': { 6033 '*': { action_: [ { type_: 'output', option: 2 }, 'color-output' ], nextState: '3' } }, 6034 '\\color{(...)}0': { 6035 '*': { action_: [ { type_: 'output', option: 2 }, 'color0-output' ] } }, 6036 '\\ce{(...)}': { 6037 '*': { action_: [ { type_: 'output', option: 2 }, 'ce' ], nextState: '3' } }, 6038 '\\,': { 6039 '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '1' } }, 6040 '\\x{}{}|\\x{}|\\x': { 6041 '0|1|2|3|a|as|b|p|bp|o|c0': { action_: [ 'o=', 'output' ], nextState: '3' }, 6042 '*': { action_: ['output', 'o=', 'output' ], nextState: '3' } }, 6043 'others': { 6044 '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '3' } }, 6045 'else2': { 6046 'a': { action_: 'a to o', nextState: 'o', revisit: true }, 6047 'as': { action_: [ 'output', 'sb=true' ], nextState: '1', revisit: true }, 6048 'r|rt|rd|rdt|rdq': { action_: [ 'output' ], nextState: '0', revisit: true }, 6049 '*': { action_: [ 'output', 'copy' ], nextState: '3' } } 6050 }), 6051 actions: { 6052 'o after d': function (buffer, m) { 6053 var ret; 6054 if ((buffer.d || "").match(/^[0-9]+$/)) { 6055 var tmp = buffer.d; 6056 buffer.d = undefined; 6057 ret = this['output'](buffer); 6058 buffer.b = tmp; 6059 } else { 6060 ret = this['output'](buffer); 6061 } 6062 mhchemParser.actions['o='](buffer, m); 6063 return ret; 6064 }, 6065 'd= kv': function (buffer, m) { 6066 buffer.d = m; 6067 buffer.dType = 'kv'; 6068 }, 6069 'charge or bond': function (buffer, m) { 6070 if (buffer['beginsWithBond']) { 6071 /** @type {ParserOutput[]} */ 6072 var ret = []; 6073 mhchemParser.concatArray(ret, this['output'](buffer)); 6074 mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); 6075 return ret; 6076 } else { 6077 buffer.d = m; 6078 } 6079 }, 6080 '- after o/d': function (buffer, m, isAfterD) { 6081 var c1 = mhchemParser.patterns.match_('orbital', buffer.o || ""); 6082 var c2 = mhchemParser.patterns.match_('one lowercase greek letter $', buffer.o || ""); 6083 var c3 = mhchemParser.patterns.match_('one lowercase latin letter $', buffer.o || ""); 6084 var c4 = mhchemParser.patterns.match_('$one lowercase latin letter$ $', buffer.o || ""); 6085 var hyphenFollows = m==="-" && ( c1 && c1.remainder==="" || c2 || c3 || c4 ); 6086 if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) { 6087 buffer.o = '$' + buffer.o + '$'; 6088 } 6089 /** @type {ParserOutput[]} */ 6090 var ret = []; 6091 if (hyphenFollows) { 6092 mhchemParser.concatArray(ret, this['output'](buffer)); 6093 ret.push({ type_: 'hyphen' }); 6094 } else { 6095 c1 = mhchemParser.patterns.match_('digits', buffer.d || ""); 6096 if (isAfterD && c1 && c1.remainder==='') { 6097 mhchemParser.concatArray(ret, mhchemParser.actions['d='](buffer, m)); 6098 mhchemParser.concatArray(ret, this['output'](buffer)); 6099 } else { 6100 mhchemParser.concatArray(ret, this['output'](buffer)); 6101 mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); 6102 } 6103 } 6104 return ret; 6105 }, 6106 'a to o': function (buffer) { 6107 buffer.o = buffer.a; 6108 buffer.a = undefined; 6109 }, 6110 'sb=true': function (buffer) { buffer.sb = true; }, 6111 'sb=false': function (buffer) { buffer.sb = false; }, 6112 'beginsWithBond=true': function (buffer) { buffer['beginsWithBond'] = true; }, 6113 'beginsWithBond=false': function (buffer) { buffer['beginsWithBond'] = false; }, 6114 'parenthesisLevel++': function (buffer) { buffer['parenthesisLevel']++; }, 6115 'parenthesisLevel--': function (buffer) { buffer['parenthesisLevel']--; }, 6116 'state of aggregation': function (buffer, m) { 6117 return { type_: 'state of aggregation', p1: mhchemParser.go(m, 'o') }; 6118 }, 6119 'comma': function (buffer, m) { 6120 var a = m.replace(/\s*$/, ''); 6121 var withSpace = (a !== m); 6122 if (withSpace && buffer['parenthesisLevel'] === 0) { 6123 return { type_: 'comma enumeration L', p1: a }; 6124 } else { 6125 return { type_: 'comma enumeration M', p1: a }; 6126 } 6127 }, 6128 'output': function (buffer, m, entityFollows) { 6129 // entityFollows: 6130 // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb) 6131 // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1) 6132 // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as) 6133 /** @type {ParserOutput | ParserOutput[]} */ 6134 var ret; 6135 if (!buffer.r) { 6136 ret = []; 6137 if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) { 6138 //ret = []; 6139 } else { 6140 if (buffer.sb) { 6141 ret.push({ type_: 'entitySkip' }); 6142 } 6143 if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows!==2) { 6144 buffer.o = buffer.a; 6145 buffer.a = undefined; 6146 } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) { 6147 buffer.o = buffer.a; 6148 buffer.d = buffer.b; 6149 buffer.q = buffer.p; 6150 buffer.a = buffer.b = buffer.p = undefined; 6151 } else { 6152 if (buffer.o && buffer.dType==='kv' && mhchemParser.patterns.match_('d-oxidation$', buffer.d || "")) { 6153 buffer.dType = 'oxidation'; 6154 } else if (buffer.o && buffer.dType==='kv' && !buffer.q) { 6155 buffer.dType = undefined; 6156 } 6157 } 6158 ret.push({ 6159 type_: 'chemfive', 6160 a: mhchemParser.go(buffer.a, 'a'), 6161 b: mhchemParser.go(buffer.b, 'bd'), 6162 p: mhchemParser.go(buffer.p, 'pq'), 6163 o: mhchemParser.go(buffer.o, 'o'), 6164 q: mhchemParser.go(buffer.q, 'pq'), 6165 d: mhchemParser.go(buffer.d, (buffer.dType === 'oxidation' ? 'oxidation' : 'bd')), 6166 dType: buffer.dType 6167 }); 6168 } 6169 } else { // r 6170 /** @type {ParserOutput[]} */ 6171 var rd; 6172 if (buffer.rdt === 'M') { 6173 rd = mhchemParser.go(buffer.rd, 'tex-math'); 6174 } else if (buffer.rdt === 'T') { 6175 rd = [ { type_: 'text', p1: buffer.rd || "" } ]; 6176 } else { 6177 rd = mhchemParser.go(buffer.rd); 6178 } 6179 /** @type {ParserOutput[]} */ 6180 var rq; 6181 if (buffer.rqt === 'M') { 6182 rq = mhchemParser.go(buffer.rq, 'tex-math'); 6183 } else if (buffer.rqt === 'T') { 6184 rq = [ { type_: 'text', p1: buffer.rq || ""} ]; 6185 } else { 6186 rq = mhchemParser.go(buffer.rq); 6187 } 6188 ret = { 6189 type_: 'arrow', 6190 r: buffer.r, 6191 rd: rd, 6192 rq: rq 6193 }; 6194 } 6195 for (var p in buffer) { 6196 if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') { 6197 delete buffer[p]; 6198 } 6199 } 6200 return ret; 6201 }, 6202 'oxidation-output': function (buffer, m) { 6203 var ret = [ "{" ]; 6204 mhchemParser.concatArray(ret, mhchemParser.go(m, 'oxidation')); 6205 ret.push("}"); 6206 return ret; 6207 }, 6208 'frac-output': function (buffer, m) { 6209 return { type_: 'frac-ce', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; 6210 }, 6211 'overset-output': function (buffer, m) { 6212 return { type_: 'overset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; 6213 }, 6214 'underset-output': function (buffer, m) { 6215 return { type_: 'underset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; 6216 }, 6217 'underbrace-output': function (buffer, m) { 6218 return { type_: 'underbrace', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) }; 6219 }, 6220 'color-output': function (buffer, m) { 6221 return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1]) }; 6222 }, 6223 'r=': function (buffer, m) { buffer.r = m; }, 6224 'rdt=': function (buffer, m) { buffer.rdt = m; }, 6225 'rd=': function (buffer, m) { buffer.rd = m; }, 6226 'rqt=': function (buffer, m) { buffer.rqt = m; }, 6227 'rq=': function (buffer, m) { buffer.rq = m; }, 6228 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; } 6229 } 6230 }, 6231 'a': { 6232 transitions: mhchemParser.createTransitions({ 6233 'empty': { 6234 '*': {} }, 6235 '1/2$': { 6236 '0': { action_: '1/2' } }, 6237 'else': { 6238 '0': { nextState: '1', revisit: true } }, 6239 '$(...)$': { 6240 '*': { action_: 'tex-math tight', nextState: '1' } }, 6241 ',': { 6242 '*': { action_: { type_: 'insert', option: 'commaDecimal' } } }, 6243 'else2': { 6244 '*': { action_: 'copy' } } 6245 }), 6246 actions: {} 6247 }, 6248 'o': { 6249 transitions: mhchemParser.createTransitions({ 6250 'empty': { 6251 '*': {} }, 6252 '1/2$': { 6253 '0': { action_: '1/2' } }, 6254 'else': { 6255 '0': { nextState: '1', revisit: true } }, 6256 'letters': { 6257 '*': { action_: 'rm' } }, 6258 '\\ca': { 6259 '*': { action_: { type_: 'insert', option: 'circa' } } }, 6260 '\\x{}{}|\\x{}|\\x': { 6261 '*': { action_: 'copy' } }, 6262 '${(...)}$|$(...)$': { 6263 '*': { action_: 'tex-math' } }, 6264 '{(...)}': { 6265 '*': { action_: '{text}' } }, 6266 'else2': { 6267 '*': { action_: 'copy' } } 6268 }), 6269 actions: {} 6270 }, 6271 'text': { 6272 transitions: mhchemParser.createTransitions({ 6273 'empty': { 6274 '*': { action_: 'output' } }, 6275 '{...}': { 6276 '*': { action_: 'text=' } }, 6277 '${(...)}$|$(...)$': { 6278 '*': { action_: 'tex-math' } }, 6279 '\\greek': { 6280 '*': { action_: [ 'output', 'rm' ] } }, 6281 '\\,|\\x{}{}|\\x{}|\\x': { 6282 '*': { action_: [ 'output', 'copy' ] } }, 6283 'else': { 6284 '*': { action_: 'text=' } } 6285 }), 6286 actions: { 6287 'output': function (buffer) { 6288 if (buffer.text_) { 6289 /** @type {ParserOutput} */ 6290 var ret = { type_: 'text', p1: buffer.text_ }; 6291 for (var p in buffer) { delete buffer[p]; } 6292 return ret; 6293 } 6294 } 6295 } 6296 }, 6297 'pq': { 6298 transitions: mhchemParser.createTransitions({ 6299 'empty': { 6300 '*': {} }, 6301 'state of aggregation $': { 6302 '*': { action_: 'state of aggregation' } }, 6303 'i$': { 6304 '0': { nextState: '!f', revisit: true } }, 6305 '(KV letters),': { 6306 '0': { action_: 'rm', nextState: '0' } }, 6307 'formula$': { 6308 '0': { nextState: 'f', revisit: true } }, 6309 '1/2$': { 6310 '0': { action_: '1/2' } }, 6311 'else': { 6312 '0': { nextState: '!f', revisit: true } }, 6313 '${(...)}$|$(...)$': { 6314 '*': { action_: 'tex-math' } }, 6315 '{(...)}': { 6316 '*': { action_: 'text' } }, 6317 'a-z': { 6318 'f': { action_: 'tex-math' } }, 6319 'letters': { 6320 '*': { action_: 'rm' } }, 6321 '-9.,9': { 6322 '*': { action_: '9,9' } }, 6323 ',': { 6324 '*': { action_: { type_: 'insert+p1', option: 'comma enumeration S' } } }, 6325 '\\color{(...)}{(...)}1|\\color(...){(...)}2': { 6326 '*': { action_: 'color-output' } }, 6327 '\\color{(...)}0': { 6328 '*': { action_: 'color0-output' } }, 6329 '\\ce{(...)}': { 6330 '*': { action_: 'ce' } }, 6331 '\\,|\\x{}{}|\\x{}|\\x': { 6332 '*': { action_: 'copy' } }, 6333 'else2': { 6334 '*': { action_: 'copy' } } 6335 }), 6336 actions: { 6337 'state of aggregation': function (buffer, m) { 6338 return { type_: 'state of aggregation subscript', p1: mhchemParser.go(m, 'o') }; 6339 }, 6340 'color-output': function (buffer, m) { 6341 return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'pq') }; 6342 } 6343 } 6344 }, 6345 'bd': { 6346 transitions: mhchemParser.createTransitions({ 6347 'empty': { 6348 '*': {} }, 6349 'x$': { 6350 '0': { nextState: '!f', revisit: true } }, 6351 'formula$': { 6352 '0': { nextState: 'f', revisit: true } }, 6353 'else': { 6354 '0': { nextState: '!f', revisit: true } }, 6355 '-9.,9 no missing 0': { 6356 '*': { action_: '9,9' } }, 6357 '.': { 6358 '*': { action_: { type_: 'insert', option: 'electron dot' } } }, 6359 'a-z': { 6360 'f': { action_: 'tex-math' } }, 6361 'x': { 6362 '*': { action_: { type_: 'insert', option: 'KV x' } } }, 6363 'letters': { 6364 '*': { action_: 'rm' } }, 6365 '\'': { 6366 '*': { action_: { type_: 'insert', option: 'prime' } } }, 6367 '${(...)}$|$(...)$': { 6368 '*': { action_: 'tex-math' } }, 6369 '{(...)}': { 6370 '*': { action_: 'text' } }, 6371 '\\color{(...)}{(...)}1|\\color(...){(...)}2': { 6372 '*': { action_: 'color-output' } }, 6373 '\\color{(...)}0': { 6374 '*': { action_: 'color0-output' } }, 6375 '\\ce{(...)}': { 6376 '*': { action_: 'ce' } }, 6377 '\\,|\\x{}{}|\\x{}|\\x': { 6378 '*': { action_: 'copy' } }, 6379 'else2': { 6380 '*': { action_: 'copy' } } 6381 }), 6382 actions: { 6383 'color-output': function (buffer, m) { 6384 return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'bd') }; 6385 } 6386 } 6387 }, 6388 'oxidation': { 6389 transitions: mhchemParser.createTransitions({ 6390 'empty': { 6391 '*': {} }, 6392 'roman numeral': { 6393 '*': { action_: 'roman-numeral' } }, 6394 '${(...)}$|$(...)$': { 6395 '*': { action_: 'tex-math' } }, 6396 'else': { 6397 '*': { action_: 'copy' } } 6398 }), 6399 actions: { 6400 'roman-numeral': function (buffer, m) { return { type_: 'roman numeral', p1: m || "" }; } 6401 } 6402 }, 6403 'tex-math': { 6404 transitions: mhchemParser.createTransitions({ 6405 'empty': { 6406 '*': { action_: 'output' } }, 6407 '\\ce{(...)}': { 6408 '*': { action_: [ 'output', 'ce' ] } }, 6409 '{...}|\\,|\\x{}{}|\\x{}|\\x': { 6410 '*': { action_: 'o=' } }, 6411 'else': { 6412 '*': { action_: 'o=' } } 6413 }), 6414 actions: { 6415 'output': function (buffer) { 6416 if (buffer.o) { 6417 /** @type {ParserOutput} */ 6418 var ret = { type_: 'tex-math', p1: buffer.o }; 6419 for (var p in buffer) { delete buffer[p]; } 6420 return ret; 6421 } 6422 } 6423 } 6424 }, 6425 'tex-math tight': { 6426 transitions: mhchemParser.createTransitions({ 6427 'empty': { 6428 '*': { action_: 'output' } }, 6429 '\\ce{(...)}': { 6430 '*': { action_: [ 'output', 'ce' ] } }, 6431 '{...}|\\,|\\x{}{}|\\x{}|\\x': { 6432 '*': { action_: 'o=' } }, 6433 '-|+': { 6434 '*': { action_: 'tight operator' } }, 6435 'else': { 6436 '*': { action_: 'o=' } } 6437 }), 6438 actions: { 6439 'tight operator': function (buffer, m) { buffer.o = (buffer.o || "") + "{"+m+"}"; }, 6440 'output': function (buffer) { 6441 if (buffer.o) { 6442 /** @type {ParserOutput} */ 6443 var ret = { type_: 'tex-math', p1: buffer.o }; 6444 for (var p in buffer) { delete buffer[p]; } 6445 return ret; 6446 } 6447 } 6448 } 6449 }, 6450 '9,9': { 6451 transitions: mhchemParser.createTransitions({ 6452 'empty': { 6453 '*': {} }, 6454 ',': { 6455 '*': { action_: 'comma' } }, 6456 'else': { 6457 '*': { action_: 'copy' } } 6458 }), 6459 actions: { 6460 'comma': function () { return { type_: 'commaDecimal' }; } 6461 } 6462 }, 6463 //#endregion 6464 // 6465 // \pu state machines 6466 // 6467 //#region pu 6468 'pu': { 6469 transitions: mhchemParser.createTransitions({ 6470 'empty': { 6471 '*': { action_: 'output' } }, 6472 'space$': { 6473 '*': { action_: [ 'output', 'space' ] } }, 6474 '{[(|)]}': { 6475 '0|a': { action_: 'copy' } }, 6476 '(-)(9)^(-9)': { 6477 '0': { action_: 'number^', nextState: 'a' } }, 6478 '(-)(9.,9)(e)(99)': { 6479 '0': { action_: 'enumber', nextState: 'a' } }, 6480 'space': { 6481 '0|a': {} }, 6482 'pm-operator': { 6483 '0|a': { action_: { type_: 'operator', option: '\\pm' }, nextState: '0' } }, 6484 'operator': { 6485 '0|a': { action_: 'copy', nextState: '0' } }, 6486 '//': { 6487 'd': { action_: 'o=', nextState: '/' } }, 6488 '/': { 6489 'd': { action_: 'o=', nextState: '/' } }, 6490 '{...}|else': { 6491 '0|d': { action_: 'd=', nextState: 'd' }, 6492 'a': { action_: [ 'space', 'd=' ], nextState: 'd' }, 6493 '/|q': { action_: 'q=', nextState: 'q' } } 6494 }), 6495 actions: { 6496 'enumber': function (buffer, m) { 6497 /** @type {ParserOutput[]} */ 6498 var ret = []; 6499 if (m[0] === "+-" || m[0] === "+/-") { 6500 ret.push("\\pm "); 6501 } else if (m[0]) { 6502 ret.push(m[0]); 6503 } 6504 if (m[1]) { 6505 mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); 6506 if (m[2]) { 6507 if (m[2].match(/[,.]/)) { 6508 mhchemParser.concatArray(ret, mhchemParser.go(m[2], 'pu-9,9')); 6509 } else { 6510 ret.push(m[2]); 6511 } 6512 } 6513 m[3] = m[4] || m[3]; 6514 if (m[3]) { 6515 m[3] = m[3].trim(); 6516 if (m[3] === "e" || m[3].substr(0, 1) === "*") { 6517 ret.push({ type_: 'cdot' }); 6518 } else { 6519 ret.push({ type_: 'times' }); 6520 } 6521 } 6522 } 6523 if (m[3]) { 6524 ret.push("10^{"+m[5]+"}"); 6525 } 6526 return ret; 6527 }, 6528 'number^': function (buffer, m) { 6529 /** @type {ParserOutput[]} */ 6530 var ret = []; 6531 if (m[0] === "+-" || m[0] === "+/-") { 6532 ret.push("\\pm "); 6533 } else if (m[0]) { 6534 ret.push(m[0]); 6535 } 6536 mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); 6537 ret.push("^{"+m[2]+"}"); 6538 return ret; 6539 }, 6540 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; }, 6541 'space': function () { return { type_: 'pu-space-1' }; }, 6542 'output': function (buffer) { 6543 /** @type {ParserOutput | ParserOutput[]} */ 6544 var ret; 6545 var md = mhchemParser.patterns.match_('{(...)}', buffer.d || ""); 6546 if (md && md.remainder === '') { buffer.d = md.match_; } 6547 var mq = mhchemParser.patterns.match_('{(...)}', buffer.q || ""); 6548 if (mq && mq.remainder === '') { buffer.q = mq.match_; } 6549 if (buffer.d) { 6550 buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); 6551 buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); 6552 } 6553 if (buffer.q) { // fraction 6554 buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); 6555 buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); 6556 var b5 = { 6557 d: mhchemParser.go(buffer.d, 'pu'), 6558 q: mhchemParser.go(buffer.q, 'pu') 6559 }; 6560 if (buffer.o === '//') { 6561 ret = { type_: 'pu-frac', p1: b5.d, p2: b5.q }; 6562 } else { 6563 ret = b5.d; 6564 if (b5.d.length > 1 || b5.q.length > 1) { 6565 ret.push({ type_: ' / ' }); 6566 } else { 6567 ret.push({ type_: '/' }); 6568 } 6569 mhchemParser.concatArray(ret, b5.q); 6570 } 6571 } else { // no fraction 6572 ret = mhchemParser.go(buffer.d, 'pu-2'); 6573 } 6574 for (var p in buffer) { delete buffer[p]; } 6575 return ret; 6576 } 6577 } 6578 }, 6579 'pu-2': { 6580 transitions: mhchemParser.createTransitions({ 6581 'empty': { 6582 '*': { action_: 'output' } }, 6583 '*': { 6584 '*': { action_: [ 'output', 'cdot' ], nextState: '0' } }, 6585 '\\x': { 6586 '*': { action_: 'rm=' } }, 6587 'space': { 6588 '*': { action_: [ 'output', 'space' ], nextState: '0' } }, 6589 '^{(...)}|^(-1)': { 6590 '1': { action_: '^(-1)' } }, 6591 '-9.,9': { 6592 '0': { action_: 'rm=', nextState: '0' }, 6593 '1': { action_: '^(-1)', nextState: '0' } }, 6594 '{...}|else': { 6595 '*': { action_: 'rm=', nextState: '1' } } 6596 }), 6597 actions: { 6598 'cdot': function () { return { type_: 'tight cdot' }; }, 6599 '^(-1)': function (buffer, m) { buffer.rm += "^{"+m+"}"; }, 6600 'space': function () { return { type_: 'pu-space-2' }; }, 6601 'output': function (buffer) { 6602 /** @type {ParserOutput | ParserOutput[]} */ 6603 var ret = []; 6604 if (buffer.rm) { 6605 var mrm = mhchemParser.patterns.match_('{(...)}', buffer.rm || ""); 6606 if (mrm && mrm.remainder === '') { 6607 ret = mhchemParser.go(mrm.match_, 'pu'); 6608 } else { 6609 ret = { type_: 'rm', p1: buffer.rm }; 6610 } 6611 } 6612 for (var p in buffer) { delete buffer[p]; } 6613 return ret; 6614 } 6615 } 6616 }, 6617 'pu-9,9': { 6618 transitions: mhchemParser.createTransitions({ 6619 'empty': { 6620 '0': { action_: 'output-0' }, 6621 'o': { action_: 'output-o' } }, 6622 ',': { 6623 '0': { action_: [ 'output-0', 'comma' ], nextState: 'o' } }, 6624 '.': { 6625 '0': { action_: [ 'output-0', 'copy' ], nextState: 'o' } }, 6626 'else': { 6627 '*': { action_: 'text=' } } 6628 }), 6629 actions: { 6630 'comma': function () { return { type_: 'commaDecimal' }; }, 6631 'output-0': function (buffer) { 6632 /** @type {ParserOutput[]} */ 6633 var ret = []; 6634 buffer.text_ = buffer.text_ || ""; 6635 if (buffer.text_.length > 4) { 6636 var a = buffer.text_.length % 3; 6637 if (a === 0) { a = 3; } 6638 for (var i=buffer.text_.length-3; i>0; i-=3) { 6639 ret.push(buffer.text_.substr(i, 3)); 6640 ret.push({ type_: '1000 separator' }); 6641 } 6642 ret.push(buffer.text_.substr(0, a)); 6643 ret.reverse(); 6644 } else { 6645 ret.push(buffer.text_); 6646 } 6647 for (var p in buffer) { delete buffer[p]; } 6648 return ret; 6649 }, 6650 'output-o': function (buffer) { 6651 /** @type {ParserOutput[]} */ 6652 var ret = []; 6653 buffer.text_ = buffer.text_ || ""; 6654 if (buffer.text_.length > 4) { 6655 var a = buffer.text_.length - 3; 6656 for (var i=0; i<a; i+=3) { 6657 ret.push(buffer.text_.substr(i, 3)); 6658 ret.push({ type_: '1000 separator' }); 6659 } 6660 ret.push(buffer.text_.substr(i)); 6661 } else { 6662 ret.push(buffer.text_); 6663 } 6664 for (var p in buffer) { delete buffer[p]; } 6665 return ret; 6666 } 6667 } 6668 } 6669 //#endregion 6670 }; 6671 6672 // 6673 // texify: Take MhchemParser output and convert it to TeX 6674 // 6675 /** @type {Texify} */ 6676 var texify = { 6677 go: function (input, isInner) { // (recursive, max 4 levels) 6678 if (!input) { return ""; } 6679 var res = ""; 6680 var cee = false; 6681 for (var i=0; i < input.length; i++) { 6682 var inputi = input[i]; 6683 if (typeof inputi === "string") { 6684 res += inputi; 6685 } else { 6686 res += texify._go2(inputi); 6687 if (inputi.type_ === '1st-level escape') { cee = true; } 6688 } 6689 } 6690 if (!isInner && !cee && res) { 6691 res = "{" + res + "}"; 6692 } 6693 return res; 6694 }, 6695 _goInner: function (input) { 6696 if (!input) { return input; } 6697 return texify.go(input, true); 6698 }, 6699 _go2: function (buf) { 6700 /** @type {undefined | string} */ 6701 var res; 6702 switch (buf.type_) { 6703 case 'chemfive': 6704 res = ""; 6705 var b5 = { 6706 a: texify._goInner(buf.a), 6707 b: texify._goInner(buf.b), 6708 p: texify._goInner(buf.p), 6709 o: texify._goInner(buf.o), 6710 q: texify._goInner(buf.q), 6711 d: texify._goInner(buf.d) 6712 }; 6713 // 6714 // a 6715 // 6716 if (b5.a) { 6717 if (b5.a.match(/^[+\-]/)) { b5.a = "{"+b5.a+"}"; } 6718 res += b5.a + "\\,"; 6719 } 6720 // 6721 // b and p 6722 // 6723 if (b5.b || b5.p) { 6724 res += "{\\vphantom{X}}"; 6725 res += "^{\\hphantom{"+(b5.b||"")+"}}_{\\hphantom{"+(b5.p||"")+"}}"; 6726 res += "{\\vphantom{X}}"; 6727 // In the next two lines, I've removed \smash[t] (ron) 6728 // TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0" 6729 //res += "^{\\smash[t]{\\vphantom{2}}\\mathllap{"+(b5.b||"")+"}}"; 6730 res += "^{\\vphantom{2}\\mathllap{"+(b5.b||"")+"}}"; 6731 //res += "_{\\vphantom{2}\\mathllap{\\smash[t]{"+(b5.p||"")+"}}}"; 6732 res += "_{\\vphantom{2}\\mathllap{"+(b5.p||"")+"}}"; 6733 } 6734 // 6735 // o 6736 // 6737 if (b5.o) { 6738 if (b5.o.match(/^[+\-]/)) { b5.o = "{"+b5.o+"}"; } 6739 res += b5.o; 6740 } 6741 // 6742 // q and d 6743 // 6744 if (buf.dType === 'kv') { 6745 if (b5.d || b5.q) { 6746 res += "{\\vphantom{X}}"; 6747 } 6748 if (b5.d) { 6749 res += "^{"+b5.d+"}"; 6750 } 6751 if (b5.q) { 6752 // In the next line, I've removed \smash[t] (ron) 6753 // TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0" 6754 //res += "_{\\smash[t]{"+b5.q+"}}"; 6755 res += "_{"+b5.q+"}"; 6756 } 6757 } else if (buf.dType === 'oxidation') { 6758 if (b5.d) { 6759 res += "{\\vphantom{X}}"; 6760 res += "^{"+b5.d+"}"; 6761 } 6762 if (b5.q) { 6763 // A Firefox bug adds a bogus depth to <mphantom>, so we change \vphantom{X} to {} 6764 // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. 6765 // res += "{\\vphantom{X}}"; 6766 res += "{{}}"; 6767 // In the next line, I've removed \smash[t] (ron) 6768 // TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0" 6769 //res += "_{\\smash[t]{"+b5.q+"}}"; 6770 res += "_{"+b5.q+"}"; 6771 } 6772 } else { 6773 if (b5.q) { 6774 // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. 6775 // res += "{\\vphantom{X}}"; 6776 res += "{{}}"; 6777 // In the next line, I've removed \smash[t] (ron) 6778 // TODO: Revert \smash[t] when WebKit properly renders <mpadded> w/height="0" 6779 //res += "_{\\smash[t]{"+b5.q+"}}"; 6780 res += "_{"+b5.q+"}"; 6781 } 6782 if (b5.d) { 6783 // TODO: Reinstate \vphantom{X} when the Firefox bug is fixed. 6784 // res += "{\\vphantom{X}}"; 6785 res += "{{}}"; 6786 res += "^{"+b5.d+"}"; 6787 } 6788 } 6789 break; 6790 case 'rm': 6791 res = "\\mathrm{"+buf.p1+"}"; 6792 break; 6793 case 'text': 6794 if (buf.p1.match(/[\^_]/)) { 6795 buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}"); 6796 res = "\\mathrm{"+buf.p1+"}"; 6797 } else { 6798 res = "\\text{"+buf.p1+"}"; 6799 } 6800 break; 6801 case 'roman numeral': 6802 res = "\\mathrm{"+buf.p1+"}"; 6803 break; 6804 case 'state of aggregation': 6805 res = "\\mskip2mu "+texify._goInner(buf.p1); 6806 break; 6807 case 'state of aggregation subscript': 6808 res = "\\mskip1mu "+texify._goInner(buf.p1); 6809 break; 6810 case 'bond': 6811 res = texify._getBond(buf.kind_); 6812 if (!res) { 6813 throw ["MhchemErrorBond", "mhchem Error. Unknown bond type (" + buf.kind_ + ")"]; 6814 } 6815 break; 6816 case 'frac': 6817 var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}"; 6818 res = "\\mathchoice{\\textstyle"+c+"}{"+c+"}{"+c+"}{"+c+"}"; 6819 break; 6820 case 'pu-frac': 6821 var d = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; 6822 res = "\\mathchoice{\\textstyle"+d+"}{"+d+"}{"+d+"}{"+d+"}"; 6823 break; 6824 case 'tex-math': 6825 res = buf.p1 + " "; 6826 break; 6827 case 'frac-ce': 6828 res = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; 6829 break; 6830 case 'overset': 6831 res = "\\overset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; 6832 break; 6833 case 'underset': 6834 res = "\\underset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; 6835 break; 6836 case 'underbrace': 6837 res = "\\underbrace{" + texify._goInner(buf.p1) + "}_{" + texify._goInner(buf.p2) + "}"; 6838 break; 6839 case 'color': 6840 res = "{\\color{" + buf.color1 + "}{" + texify._goInner(buf.color2) + "}}"; 6841 break; 6842 case 'color0': 6843 res = "\\color{" + buf.color + "}"; 6844 break; 6845 case 'arrow': 6846 var b6 = { 6847 rd: texify._goInner(buf.rd), 6848 rq: texify._goInner(buf.rq) 6849 }; 6850 var arrow = texify._getArrow(buf.r); 6851 if (b6.rq) { arrow += "[{\\rm " + b6.rq + "}]"; } 6852 if (b6.rd) { 6853 arrow += "{\\rm " + b6.rd + "}"; 6854 } else { 6855 arrow += "{}"; 6856 } 6857 res = arrow; 6858 break; 6859 case 'operator': 6860 res = texify._getOperator(buf.kind_); 6861 break; 6862 case '1st-level escape': 6863 res = buf.p1+" "; // &, \\\\, \\hlin 6864 break; 6865 case 'space': 6866 res = " "; 6867 break; 6868 case 'entitySkip': 6869 res = "~"; 6870 break; 6871 case 'pu-space-1': 6872 res = "~"; 6873 break; 6874 case 'pu-space-2': 6875 res = "\\mkern3mu "; 6876 break; 6877 case '1000 separator': 6878 res = "\\mkern2mu "; 6879 break; 6880 case 'commaDecimal': 6881 res = "{,}"; 6882 break; 6883 case 'comma enumeration L': 6884 res = "{"+buf.p1+"}\\mkern6mu "; 6885 break; 6886 case 'comma enumeration M': 6887 res = "{"+buf.p1+"}\\mkern3mu "; 6888 break; 6889 case 'comma enumeration S': 6890 res = "{"+buf.p1+"}\\mkern1mu "; 6891 break; 6892 case 'hyphen': 6893 res = "\\text{-}"; 6894 break; 6895 case 'addition compound': 6896 res = "\\,{\\cdot}\\,"; 6897 break; 6898 case 'electron dot': 6899 res = "\\mkern1mu \\text{\\textbullet}\\mkern1mu "; 6900 break; 6901 case 'KV x': 6902 res = "{\\times}"; 6903 break; 6904 case 'prime': 6905 res = "\\prime "; 6906 break; 6907 case 'cdot': 6908 res = "\\cdot "; 6909 break; 6910 case 'tight cdot': 6911 res = "\\mkern1mu{\\cdot}\\mkern1mu "; 6912 break; 6913 case 'times': 6914 res = "\\times "; 6915 break; 6916 case 'circa': 6917 res = "{\\sim}"; 6918 break; 6919 case '^': 6920 res = "uparrow"; 6921 break; 6922 case 'v': 6923 res = "downarrow"; 6924 break; 6925 case 'ellipsis': 6926 res = "\\ldots "; 6927 break; 6928 case '/': 6929 res = "/"; 6930 break; 6931 case ' / ': 6932 res = "\\,/\\,"; 6933 break; 6934 default: 6935 assertNever(buf); 6936 throw ["MhchemBugT", "mhchem bug T. Please report."]; // Missing texify rule or unknown MhchemParser output 6937 } 6938 assertString(res); 6939 return res; 6940 }, 6941 _getArrow: function (a) { 6942 switch (a) { 6943 case "->": return "\\yields"; 6944 case "\u2192": return "\\yields"; 6945 case "\u27F6": return "\\yields"; 6946 case "<-": return "\\yieldsLeft"; 6947 case "<->": return "\\mesomerism"; 6948 case "<-->": return "\\yieldsLeftRight"; 6949 case "<=>": return "\\equilibrium"; 6950 case "\u21CC": return "\\equilibrium"; 6951 case "<=>>": return "\\equilibriumRight"; 6952 case "<<=>": return "\\equilibriumLeft"; 6953 default: 6954 assertNever(a); 6955 throw ["MhchemBugT", "mhchem bug T. Please report."]; 6956 } 6957 }, 6958 _getBond: function (a) { 6959 switch (a) { 6960 case "-": return "{-}"; 6961 case "1": return "{-}"; 6962 case "=": return "{=}"; 6963 case "2": return "{=}"; 6964 case "#": return "{\\equiv}"; 6965 case "3": return "{\\equiv}"; 6966 case "~": return "{\\tripleDash}"; 6967 case "~-": return "{\\tripleDashOverLine}"; 6968 case "~=": return "{\\tripleDashOverDoubleLine}"; 6969 case "~--": return "{\\tripleDashOverDoubleLine}"; 6970 case "-~-": return "{\\tripleDashBetweenDoubleLine}"; 6971 case "...": return "{{\\cdot}{\\cdot}{\\cdot}}"; 6972 case "....": return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}"; 6973 case "->": return "{\\rightarrow}"; 6974 case "<-": return "{\\leftarrow}"; 6975 case "<": return "{<}"; 6976 case ">": return "{>}"; 6977 default: 6978 assertNever(a); 6979 throw ["MhchemBugT", "mhchem bug T. Please report."]; 6980 } 6981 }, 6982 _getOperator: function (a) { 6983 switch (a) { 6984 case "+": return " {}+{} "; 6985 case "-": return " {}-{} "; 6986 case "=": return " {}={} "; 6987 case "<": return " {}<{} "; 6988 case ">": return " {}>{} "; 6989 case "<<": return " {}\\ll{} "; 6990 case ">>": return " {}\\gg{} "; 6991 case "\\pm": return " {}\\pm{} "; 6992 case "\\approx": return " {}\\approx{} "; 6993 case "$\\approx$": return " {}\\approx{} "; 6994 case "v": return " \\downarrow{} "; 6995 case "(v)": return " \\downarrow{} "; 6996 case "^": return " \\uparrow{} "; 6997 case "(^)": return " \\uparrow{} "; 6998 default: 6999 assertNever(a); 7000 throw ["MhchemBugT", "mhchem bug T. Please report."]; 7001 } 7002 } 7003 }; 7004 7005 // 7006 // Helpers for code analysis 7007 // Will show type error at calling position 7008 // 7009 /** @param {number} a */ 7010 function assertNever(a) {} 7011 /** @param {string} a */ 7012 function assertString(a) {} 7013 7014 /* eslint-disable no-undef */ 7015 7016 ////////////////////////////////////////////////////////////////////// 7017 // texvc.sty 7018 7019 // The texvc package contains macros available in mediawiki pages. 7020 // We omit the functions deprecated at 7021 // https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax 7022 7023 // We also omit texvc's \O, which conflicts with \text{\O} 7024 7025 defineMacro("\\darr", "\\downarrow"); 7026 defineMacro("\\dArr", "\\Downarrow"); 7027 defineMacro("\\Darr", "\\Downarrow"); 7028 defineMacro("\\lang", "\\langle"); 7029 defineMacro("\\rang", "\\rangle"); 7030 defineMacro("\\uarr", "\\uparrow"); 7031 defineMacro("\\uArr", "\\Uparrow"); 7032 defineMacro("\\Uarr", "\\Uparrow"); 7033 defineMacro("\\N", "\\mathbb{N}"); 7034 defineMacro("\\R", "\\mathbb{R}"); 7035 defineMacro("\\Z", "\\mathbb{Z}"); 7036 defineMacro("\\alef", "\\aleph"); 7037 defineMacro("\\alefsym", "\\aleph"); 7038 defineMacro("\\bull", "\\bullet"); 7039 defineMacro("\\clubs", "\\clubsuit"); 7040 defineMacro("\\cnums", "\\mathbb{C}"); 7041 defineMacro("\\Complex", "\\mathbb{C}"); 7042 defineMacro("\\Dagger", "\\ddagger"); 7043 defineMacro("\\diamonds", "\\diamondsuit"); 7044 defineMacro("\\empty", "\\emptyset"); 7045 defineMacro("\\exist", "\\exists"); 7046 defineMacro("\\harr", "\\leftrightarrow"); 7047 defineMacro("\\hArr", "\\Leftrightarrow"); 7048 defineMacro("\\Harr", "\\Leftrightarrow"); 7049 defineMacro("\\hearts", "\\heartsuit"); 7050 defineMacro("\\image", "\\Im"); 7051 defineMacro("\\infin", "\\infty"); 7052 defineMacro("\\isin", "\\in"); 7053 defineMacro("\\larr", "\\leftarrow"); 7054 defineMacro("\\lArr", "\\Leftarrow"); 7055 defineMacro("\\Larr", "\\Leftarrow"); 7056 defineMacro("\\lrarr", "\\leftrightarrow"); 7057 defineMacro("\\lrArr", "\\Leftrightarrow"); 7058 defineMacro("\\Lrarr", "\\Leftrightarrow"); 7059 defineMacro("\\natnums", "\\mathbb{N}"); 7060 defineMacro("\\plusmn", "\\pm"); 7061 defineMacro("\\rarr", "\\rightarrow"); 7062 defineMacro("\\rArr", "\\Rightarrow"); 7063 defineMacro("\\Rarr", "\\Rightarrow"); 7064 defineMacro("\\real", "\\Re"); 7065 defineMacro("\\reals", "\\mathbb{R}"); 7066 defineMacro("\\Reals", "\\mathbb{R}"); 7067 defineMacro("\\sdot", "\\cdot"); 7068 defineMacro("\\sect", "\\S"); 7069 defineMacro("\\spades", "\\spadesuit"); 7070 defineMacro("\\sub", "\\subset"); 7071 defineMacro("\\sube", "\\subseteq"); 7072 defineMacro("\\supe", "\\supseteq"); 7073 defineMacro("\\thetasym", "\\vartheta"); 7074 defineMacro("\\weierp", "\\wp"); 7075 7076 /* eslint-disable no-undef */ 7077 7078 /**************************************************** 7079 * 7080 * physics.js 7081 * 7082 * Implements the Physics Package for LaTeX input. 7083 * 7084 * --------------------------------------------------------------------- 7085 * 7086 * The original version of this file is licensed as follows: 7087 * Copyright (c) 2015-2016 Kolen Cheung <https://github.com/ickc/MathJax-third-party-extensions>. 7088 * 7089 * Licensed under the Apache License, Version 2.0 (the "License"); 7090 * you may not use this file except in compliance with the License. 7091 * You may obtain a copy of the License at 7092 * 7093 * http://www.apache.org/licenses/LICENSE-2.0 7094 * 7095 * Unless required by applicable law or agreed to in writing, software 7096 * distributed under the License is distributed on an "AS IS" BASIS, 7097 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7098 * See the License for the specific language governing permissions and 7099 * limitations under the License. 7100 * 7101 * --------------------------------------------------------------------- 7102 * 7103 * This file has been revised from the original in the following ways: 7104 * 1. The interface is changed so that it can be called from Temml, not MathJax. 7105 * 2. \Re and \Im are not used, to avoid conflict with existing LaTeX letters. 7106 * 7107 * This revision of the file is released under the MIT license. 7108 * https://mit-license.org/ 7109 */ 7110 defineMacro("\\quantity", "{\\left\\{ #1 \\right\\}}"); 7111 defineMacro("\\qty", "{\\left\\{ #1 \\right\\}}"); 7112 defineMacro("\\pqty", "{\\left( #1 \\right)}"); 7113 defineMacro("\\bqty", "{\\left[ #1 \\right]}"); 7114 defineMacro("\\vqty", "{\\left\\vert #1 \\right\\vert}"); 7115 defineMacro("\\Bqty", "{\\left\\{ #1 \\right\\}}"); 7116 defineMacro("\\absolutevalue", "{\\left\\vert #1 \\right\\vert}"); 7117 defineMacro("\\abs", "{\\left\\vert #1 \\right\\vert}"); 7118 defineMacro("\\norm", "{\\left\\Vert #1 \\right\\Vert}"); 7119 defineMacro("\\evaluated", "{\\left.#1 \\right\\vert}"); 7120 defineMacro("\\eval", "{\\left.#1 \\right\\vert}"); 7121 defineMacro("\\order", "{\\mathcal{O} \\left( #1 \\right)}"); 7122 defineMacro("\\commutator", "{\\left[ #1 , #2 \\right]}"); 7123 defineMacro("\\comm", "{\\left[ #1 , #2 \\right]}"); 7124 defineMacro("\\anticommutator", "{\\left\\{ #1 , #2 \\right\\}}"); 7125 defineMacro("\\acomm", "{\\left\\{ #1 , #2 \\right\\}}"); 7126 defineMacro("\\poissonbracket", "{\\left\\{ #1 , #2 \\right\\}}"); 7127 defineMacro("\\pb", "{\\left\\{ #1 , #2 \\right\\}}"); 7128 defineMacro("\\vectorbold", "{\\boldsymbol{ #1 }}"); 7129 defineMacro("\\vb", "{\\boldsymbol{ #1 }}"); 7130 defineMacro("\\vectorarrow", "{\\vec{\\boldsymbol{ #1 }}}"); 7131 defineMacro("\\va", "{\\vec{\\boldsymbol{ #1 }}}"); 7132 defineMacro("\\vectorunit", "{{\\boldsymbol{\\hat{ #1 }}}}"); 7133 defineMacro("\\vu", "{{\\boldsymbol{\\hat{ #1 }}}}"); 7134 defineMacro("\\dotproduct", "\\mathbin{\\boldsymbol\\cdot}"); 7135 defineMacro("\\vdot", "{\\boldsymbol\\cdot}"); 7136 defineMacro("\\crossproduct", "\\mathbin{\\boldsymbol\\times}"); 7137 defineMacro("\\cross", "\\mathbin{\\boldsymbol\\times}"); 7138 defineMacro("\\cp", "\\mathbin{\\boldsymbol\\times}"); 7139 defineMacro("\\gradient", "{\\boldsymbol\\nabla}"); 7140 defineMacro("\\grad", "{\\boldsymbol\\nabla}"); 7141 defineMacro("\\divergence", "{\\grad\\vdot}"); 7142 //defineMacro("\\div", "{\\grad\\vdot}"); Not included in Temml. Conflicts w/LaTeX \div 7143 defineMacro("\\curl", "{\\grad\\cross}"); 7144 defineMacro("\\laplacian", "\\nabla^2"); 7145 defineMacro("\\tr", "{\\operatorname{tr}}"); 7146 defineMacro("\\Tr", "{\\operatorname{Tr}}"); 7147 defineMacro("\\rank", "{\\operatorname{rank}}"); 7148 defineMacro("\\erf", "{\\operatorname{erf}}"); 7149 defineMacro("\\Res", "{\\operatorname{Res}}"); 7150 defineMacro("\\principalvalue", "{\\mathcal{P}}"); 7151 defineMacro("\\pv", "{\\mathcal{P}}"); 7152 defineMacro("\\PV", "{\\operatorname{P.V.}}"); 7153 // Temml does not use the next two lines. They conflict with LaTeX letters. 7154 //defineMacro("\\Re", "{\\operatorname{Re} \\left\\{ #1 \\right\\}}"); 7155 //defineMacro("\\Im", "{\\operatorname{Im} \\left\\{ #1 \\right\\}}"); 7156 defineMacro("\\qqtext", "{\\quad\\text{ #1 }\\quad}"); 7157 defineMacro("\\qq", "{\\quad\\text{ #1 }\\quad}"); 7158 defineMacro("\\qcomma", "{\\text{,}\\quad}"); 7159 defineMacro("\\qc", "{\\text{,}\\quad}"); 7160 defineMacro("\\qcc", "{\\quad\\text{c.c.}\\quad}"); 7161 defineMacro("\\qif", "{\\quad\\text{if}\\quad}"); 7162 defineMacro("\\qthen", "{\\quad\\text{then}\\quad}"); 7163 defineMacro("\\qelse", "{\\quad\\text{else}\\quad}"); 7164 defineMacro("\\qotherwise", "{\\quad\\text{otherwise}\\quad}"); 7165 defineMacro("\\qunless", "{\\quad\\text{unless}\\quad}"); 7166 defineMacro("\\qgiven", "{\\quad\\text{given}\\quad}"); 7167 defineMacro("\\qusing", "{\\quad\\text{using}\\quad}"); 7168 defineMacro("\\qassume", "{\\quad\\text{assume}\\quad}"); 7169 defineMacro("\\qsince", "{\\quad\\text{since}\\quad}"); 7170 defineMacro("\\qlet", "{\\quad\\text{let}\\quad}"); 7171 defineMacro("\\qfor", "{\\quad\\text{for}\\quad}"); 7172 defineMacro("\\qall", "{\\quad\\text{all}\\quad}"); 7173 defineMacro("\\qeven", "{\\quad\\text{even}\\quad}"); 7174 defineMacro("\\qodd", "{\\quad\\text{odd}\\quad}"); 7175 defineMacro("\\qinteger", "{\\quad\\text{integer}\\quad}"); 7176 defineMacro("\\qand", "{\\quad\\text{and}\\quad}"); 7177 defineMacro("\\qor", "{\\quad\\text{or}\\quad}"); 7178 defineMacro("\\qas", "{\\quad\\text{as}\\quad}"); 7179 defineMacro("\\qin", "{\\quad\\text{in}\\quad}"); 7180 defineMacro("\\differential", "{\\text{d}}"); 7181 defineMacro("\\dd", "{\\text{d}}"); 7182 defineMacro("\\derivative", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}"); 7183 defineMacro("\\dv", "{\\frac{\\text{d}{ #1 }}{\\text{d}{ #2 }}}"); 7184 defineMacro("\\partialderivative", "{\\frac{\\partial{ #1 }}{\\partial{ #2 }}}"); 7185 defineMacro("\\variation", "{\\delta}"); 7186 defineMacro("\\var", "{\\delta}"); 7187 defineMacro("\\functionalderivative", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}"); 7188 defineMacro("\\fdv", "{\\frac{\\delta{ #1 }}{\\delta{ #2 }}}"); 7189 defineMacro("\\innerproduct", "{\\left\\langle {#1} \\mid { #2} \\right\\rangle}"); 7190 defineMacro("\\outerproduct", 7191 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); 7192 defineMacro("\\dyad", 7193 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); 7194 defineMacro("\\ketbra", 7195 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); 7196 defineMacro("\\op", 7197 "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}"); 7198 defineMacro("\\expectationvalue", "{\\left\\langle {#1 } \\right\\rangle}"); 7199 defineMacro("\\expval", "{\\left\\langle {#1 } \\right\\rangle}"); 7200 defineMacro("\\ev", "{\\left\\langle {#1 } \\right\\rangle}"); 7201 defineMacro("\\matrixelement", 7202 "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); 7203 defineMacro("\\matrixel", 7204 "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); 7205 defineMacro("\\mel", 7206 "{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}"); 7207 7208 // Helper functions 7209 function getHLines(parser) { 7210 // Return an array. The array length = number of hlines. 7211 // Each element in the array tells if the line is dashed. 7212 const hlineInfo = []; 7213 parser.consumeSpaces(); 7214 let nxt = parser.fetch().text; 7215 if (nxt === "\\relax") { 7216 parser.consume(); 7217 parser.consumeSpaces(); 7218 nxt = parser.fetch().text; 7219 } 7220 while (nxt === "\\hline" || nxt === "\\hdashline") { 7221 parser.consume(); 7222 hlineInfo.push(nxt === "\\hdashline"); 7223 parser.consumeSpaces(); 7224 nxt = parser.fetch().text; 7225 } 7226 return hlineInfo; 7227 } 7228 7229 const validateAmsEnvironmentContext = context => { 7230 const settings = context.parser.settings; 7231 if (!settings.displayMode) { 7232 throw new ParseError(`{$context.envName}} can be used only in display mode.`); 7233 } 7234 }; 7235 7236 const sizeRegEx$1 = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; 7237 const arrayGaps = macros => { 7238 let arraystretch = macros.get("\\arraystretch"); 7239 if (typeof arraystretch !== "string") { 7240 arraystretch = stringFromArg(arraystretch.tokens); 7241 } 7242 arraystretch = isNaN(arraystretch) ? null : Number(arraystretch); 7243 let arraycolsepStr = macros.get("\\arraycolsep"); 7244 if (typeof arraycolsepStr !== "string") { 7245 arraycolsepStr = stringFromArg(arraycolsepStr.tokens); 7246 } 7247 const match = sizeRegEx$1.exec(arraycolsepStr); 7248 const arraycolsep = match 7249 ? { number: +(match[1] + match[2]), unit: match[3] } 7250 : null; 7251 return [arraystretch, arraycolsep] 7252 }; 7253 7254 const checkCellForLabels = cell => { 7255 // Check if the author wrote a \tag{} inside this cell. 7256 let rowLabel = ""; 7257 for (let i = 0; i < cell.length; i++) { 7258 if (cell[i].type === "label") { 7259 if (rowLabel) { throw new ParseError(("Multiple \\labels in one row")) } 7260 rowLabel = cell[i].string; 7261 } 7262 } 7263 return rowLabel 7264 }; 7265 7266 // autoTag (an argument to parseArray) can be one of three values: 7267 // * undefined: Regular (not-top-level) array; no tags on each row 7268 // * true: Automatic equation numbering, overridable by \tag 7269 // * false: Tags allowed on each row, but no automatic numbering 7270 // This function *doesn't* work with the "split" environment name. 7271 function getAutoTag(name) { 7272 if (name.indexOf("ed") === -1) { 7273 return name.indexOf("*") === -1; 7274 } 7275 // return undefined; 7276 } 7277 7278 /** 7279 * Parse the body of the environment, with rows delimited by \\ and 7280 * columns delimited by &, and create a nested list in row-major order 7281 * with one group per cell. If given an optional argument scriptLevel 7282 * ("text", "display", etc.), then each cell is cast into that scriptLevel. 7283 */ 7284 function parseArray( 7285 parser, 7286 { 7287 cols, // [{ type: string , align: l|c|r|null }] 7288 envClasses, // align(ed|at|edat) | array | cases | cd | small | multline 7289 autoTag, // boolean 7290 singleRow, // boolean 7291 emptySingleRow, // boolean 7292 maxNumCols, // number 7293 leqno, // boolean 7294 arraystretch, // number | null 7295 arraycolsep // size value | null 7296 }, 7297 scriptLevel 7298 ) { 7299 parser.gullet.beginGroup(); 7300 if (!singleRow) { 7301 // \cr is equivalent to \\ without the optional size argument (see below) 7302 // TODO: provide helpful error when \cr is used outside array environment 7303 parser.gullet.macros.set("\\cr", "\\\\\\relax"); 7304 } 7305 7306 // Start group for first cell 7307 parser.gullet.beginGroup(); 7308 7309 let row = []; 7310 const body = [row]; 7311 const rowGaps = []; 7312 const labels = []; 7313 7314 const hLinesBeforeRow = []; 7315 7316 const tags = (autoTag != null ? [] : undefined); 7317 7318 // amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent 7319 // whether this row should have an equation number. Simulate this with 7320 // a \@eqnsw macro set to 1 or 0. 7321 function beginRow() { 7322 if (autoTag) { 7323 parser.gullet.macros.set("\\@eqnsw", "1", true); 7324 } 7325 } 7326 function endRow() { 7327 if (tags) { 7328 if (parser.gullet.macros.get("\\df@tag")) { 7329 tags.push(parser.subparse([new Token("\\df@tag")])); 7330 parser.gullet.macros.set("\\df@tag", undefined, true); 7331 } else { 7332 tags.push(Boolean(autoTag) && 7333 parser.gullet.macros.get("\\@eqnsw") === "1"); 7334 } 7335 } 7336 } 7337 beginRow(); 7338 7339 // Test for \hline at the top of the array. 7340 hLinesBeforeRow.push(getHLines(parser)); 7341 7342 while (true) { 7343 // Parse each cell in its own group (namespace) 7344 let cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\"); 7345 parser.gullet.endGroup(); 7346 parser.gullet.beginGroup(); 7347 7348 cell = { 7349 type: "ordgroup", 7350 mode: parser.mode, 7351 body: cell, 7352 semisimple: true 7353 }; 7354 row.push(cell); 7355 const next = parser.fetch().text; 7356 if (next === "&") { 7357 if (maxNumCols && row.length === maxNumCols) { 7358 if (envClasses.includes("array")) { 7359 if (parser.settings.strict) { 7360 throw new ParseError("Too few columns " + "specified in the {array} column argument.", 7361 parser.nextToken) 7362 } 7363 } else if (maxNumCols === 2) { 7364 throw new ParseError("The split environment accepts no more than two columns", 7365 parser.nextToken); 7366 } else { 7367 throw new ParseError("The equation environment accepts only one column", 7368 parser.nextToken) 7369 } 7370 } 7371 parser.consume(); 7372 } else if (next === "\\end") { 7373 endRow(); 7374 // Arrays terminate newlines with `\crcr` which consumes a `\cr` if 7375 // the last line is empty. However, AMS environments keep the 7376 // empty row if it's the only one. 7377 // NOTE: Currently, `cell` is the last item added into `row`. 7378 if (row.length === 1 && cell.body.length === 0 && (body.length > 1 || !emptySingleRow)) { 7379 body.pop(); 7380 } 7381 labels.push(checkCellForLabels(cell.body)); 7382 if (hLinesBeforeRow.length < body.length + 1) { 7383 hLinesBeforeRow.push([]); 7384 } 7385 break; 7386 } else if (next === "\\\\") { 7387 parser.consume(); 7388 let size; 7389 // \def\Let@{\let\\\math@cr} 7390 // \def\math@cr{...\math@cr@} 7391 // \def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}} 7392 // \def\math@cr@@[#1]{...\math@cr@@@...} 7393 // \def\math@cr@@@{\cr} 7394 if (parser.gullet.future().text !== " ") { 7395 size = parser.parseSizeGroup(true); 7396 } 7397 rowGaps.push(size ? size.value : null); 7398 endRow(); 7399 7400 labels.push(checkCellForLabels(cell.body)); 7401 7402 // check for \hline(s) following the row separator 7403 hLinesBeforeRow.push(getHLines(parser)); 7404 7405 row = []; 7406 body.push(row); 7407 beginRow(); 7408 } else { 7409 throw new ParseError("Expected & or \\\\ or \\cr or \\end", parser.nextToken); 7410 } 7411 } 7412 7413 // End cell group 7414 parser.gullet.endGroup(); 7415 // End array group defining \cr 7416 parser.gullet.endGroup(); 7417 7418 return { 7419 type: "array", 7420 mode: parser.mode, 7421 body, 7422 cols, 7423 rowGaps, 7424 hLinesBeforeRow, 7425 envClasses, 7426 autoTag, 7427 scriptLevel, 7428 tags, 7429 labels, 7430 leqno, 7431 arraystretch, 7432 arraycolsep 7433 }; 7434 } 7435 7436 // Decides on a scriptLevel for cells in an array according to whether the given 7437 // environment name starts with the letter 'd'. 7438 function dCellStyle(envName) { 7439 return envName.slice(0, 1) === "d" ? "display" : "text" 7440 } 7441 7442 const alignMap = { 7443 c: "center ", 7444 l: "left ", 7445 r: "right " 7446 }; 7447 7448 const glue = group => { 7449 const glueNode = new mathMLTree.MathNode("mtd", []); 7450 glueNode.style = { padding: "0", width: "50%" }; 7451 if (group.envClasses.includes("multline")) { 7452 glueNode.style.width = "7.5%"; 7453 } 7454 return glueNode 7455 }; 7456 7457 const mathmlBuilder$7 = function(group, style) { 7458 const tbl = []; 7459 const numRows = group.body.length; 7460 const hlines = group.hLinesBeforeRow; 7461 7462 for (let i = 0; i < numRows; i++) { 7463 const rw = group.body[i]; 7464 const row = []; 7465 const cellLevel = group.scriptLevel === "text" 7466 ? StyleLevel.TEXT 7467 : group.scriptLevel === "script" 7468 ? StyleLevel.SCRIPT 7469 : StyleLevel.DISPLAY; 7470 7471 for (let j = 0; j < rw.length; j++) { 7472 const mtd = new mathMLTree.MathNode( 7473 "mtd", 7474 [buildGroup$1(rw[j], style.withLevel(cellLevel))] 7475 ); 7476 7477 if (group.envClasses.includes("multline")) { 7478 const align = i === 0 ? "left" : i === numRows - 1 ? "right" : "center"; 7479 mtd.setAttribute("columnalign", align); 7480 if (align !== "center") { 7481 mtd.classes.push("tml-" + align); 7482 } 7483 } 7484 row.push(mtd); 7485 } 7486 const numColumns = group.body[0].length; 7487 // Fill out a short row with empty <mtd> elements. 7488 for (let k = 0; k < numColumns - rw.length; k++) { 7489 row.push(new mathMLTree.MathNode("mtd", [], style)); 7490 } 7491 if (group.autoTag) { 7492 const tag = group.tags[i]; 7493 let tagElement; 7494 if (tag === true) { // automatic numbering 7495 tagElement = new mathMLTree.MathNode("mtext", [new Span(["tml-eqn"])]); 7496 } else if (tag === false) { 7497 // \nonumber/\notag or starred environment 7498 tagElement = new mathMLTree.MathNode("mtext", [], []); 7499 } else { // manual \tag 7500 tagElement = buildExpressionRow(tag[0].body, style.withLevel(cellLevel), true); 7501 tagElement = consolidateText(tagElement); 7502 tagElement.classes = ["tml-tag"]; 7503 } 7504 if (tagElement) { 7505 row.unshift(glue(group)); 7506 row.push(glue(group)); 7507 if (group.leqno) { 7508 row[0].children.push(tagElement); 7509 row[0].classes.push("tml-left"); 7510 } else { 7511 row[row.length - 1].children.push(tagElement); 7512 row[row.length - 1].classes.push("tml-right"); 7513 } 7514 } 7515 } 7516 const mtr = new mathMLTree.MathNode("mtr", row, []); 7517 const label = group.labels.shift(); 7518 if (label && group.tags && group.tags[i]) { 7519 mtr.setAttribute("id", label); 7520 if (Array.isArray(group.tags[i])) { mtr.classes.push("tml-tageqn"); } 7521 } 7522 7523 // Write horizontal rules 7524 if (i === 0 && hlines[0].length > 0) { 7525 if (hlines[0].length === 2) { 7526 mtr.children.forEach(cell => { cell.style.borderTop = "0.15em double"; }); 7527 } else { 7528 mtr.children.forEach(cell => { 7529 cell.style.borderTop = hlines[0][0] ? "0.06em dashed" : "0.06em solid"; 7530 }); 7531 } 7532 } 7533 if (hlines[i + 1].length > 0) { 7534 if (hlines[i + 1].length === 2) { 7535 mtr.children.forEach(cell => { cell.style.borderBottom = "0.15em double"; }); 7536 } else { 7537 mtr.children.forEach(cell => { 7538 cell.style.borderBottom = hlines[i + 1][0] ? "0.06em dashed" : "0.06em solid"; 7539 }); 7540 } 7541 } 7542 tbl.push(mtr); 7543 } 7544 7545 if (group.envClasses.length > 0) { 7546 if (group.arraystretch && group.arraystretch !== 1) { 7547 // In LaTeX, \arraystretch is a factor applied to a 12pt strut height. 7548 // It defines a baseline to baseline distance. 7549 // Here, we do an approximation of that approach. 7550 const pad = String(1.4 * group.arraystretch - 0.8) + "ex"; 7551 for (let i = 0; i < tbl.length; i++) { 7552 for (let j = 0; j < tbl[i].children.length; j++) { 7553 tbl[i].children[j].style.paddingTop = pad; 7554 tbl[i].children[j].style.paddingBottom = pad; 7555 } 7556 } 7557 } 7558 let sidePadding = group.envClasses.includes("abut") 7559 ? "0" 7560 : group.envClasses.includes("cases") 7561 ? "0" 7562 : group.envClasses.includes("small") 7563 ? "0.1389" 7564 : group.envClasses.includes("cd") 7565 ? "0.25" 7566 : "0.4"; // default side padding 7567 let sidePadUnit = "em"; 7568 if (group.arraycolsep) { 7569 const arraySidePad = calculateSize(group.arraycolsep, style); 7570 sidePadding = arraySidePad.number.toFixed(4); 7571 sidePadUnit = arraySidePad.unit; 7572 } 7573 7574 const numCols = tbl.length === 0 ? 0 : tbl[0].children.length; 7575 7576 const sidePad = (j, hand) => { 7577 if (j === 0 && hand === 0) { return "0" } 7578 if (j === numCols - 1 && hand === 1) { return "0" } 7579 if (group.envClasses[0] !== "align") { return sidePadding } 7580 if (hand === 1) { return "0" } 7581 if (group.autoTag) { 7582 return (j % 2) ? "1" : "0" 7583 } else { 7584 return (j % 2) ? "0" : "1" 7585 } 7586 }; 7587 7588 // Side padding 7589 for (let i = 0; i < tbl.length; i++) { 7590 for (let j = 0; j < tbl[i].children.length; j++) { 7591 tbl[i].children[j].style.paddingLeft = `$sidePad(j, 0)}$sidePadUnit}`; 7592 tbl[i].children[j].style.paddingRight = `$sidePad(j, 1)}$sidePadUnit}`; 7593 } 7594 } 7595 7596 // Justification 7597 const align = group.envClasses.includes("align") || group.envClasses.includes("alignat"); 7598 for (let i = 0; i < tbl.length; i++) { 7599 const row = tbl[i]; 7600 if (align) { 7601 for (let j = 0; j < row.children.length; j++) { 7602 // Chromium does not recognize text-align: left. Use -webkit- 7603 // TODO: Remove -webkit- when Chromium no longer needs it. 7604 row.children[j].classes = ["tml-" + (j % 2 ? "left" : "right")]; 7605 } 7606 if (group.autoTag) { 7607 const k = group.leqno ? 0 : row.children.length - 1; 7608 row.children[k].classes = ["tml-" + (group.leqno ? "left" : "right")]; 7609 } 7610 } 7611 if (row.children.length > 1 && group.envClasses.includes("cases")) { 7612 row.children[1].style.paddingLeft = "1em"; 7613 } 7614 7615 if (group.envClasses.includes("cases") || group.envClasses.includes("subarray")) { 7616 for (const cell of row.children) { 7617 cell.classes.push("tml-left"); 7618 } 7619 } 7620 } 7621 } else { 7622 // Set zero padding on side of the matrix 7623 for (let i = 0; i < tbl.length; i++) { 7624 tbl[i].children[0].style.paddingLeft = "0em"; 7625 if (tbl[i].children.length === tbl[0].children.length) { 7626 tbl[i].children[tbl[i].children.length - 1].style.paddingRight = "0em"; 7627 } 7628 } 7629 } 7630 7631 let table = new mathMLTree.MathNode("mtable", tbl); 7632 if (group.envClasses.length > 0) { 7633 // Top & bottom padding 7634 if (group.envClasses.includes("jot")) { 7635 table.classes.push("tml-jot"); 7636 } else if (group.envClasses.includes("small")) { 7637 table.classes.push("tml-small"); 7638 } 7639 } 7640 if (group.scriptLevel === "display") { table.setAttribute("displaystyle", "true"); } 7641 7642 if (group.autoTag || group.envClasses.includes("multline")) { 7643 table.style.width = "100%"; 7644 } 7645 7646 // Column separator lines and column alignment 7647 let align = ""; 7648 7649 if (group.cols && group.cols.length > 0) { 7650 const cols = group.cols; 7651 let prevTypeWasAlign = false; 7652 let iStart = 0; 7653 let iEnd = cols.length; 7654 7655 while (cols[iStart].type === "separator") { 7656 iStart += 1; 7657 } 7658 while (cols[iEnd - 1].type === "separator") { 7659 iEnd -= 1; 7660 } 7661 7662 if (cols[0].type === "separator") { 7663 const sep = cols[1].type === "separator" 7664 ? "0.15em double" 7665 : cols[0].separator === "|" 7666 ? "0.06em solid " 7667 : "0.06em dashed "; 7668 for (const row of table.children) { 7669 row.children[0].style.borderLeft = sep; 7670 } 7671 } 7672 let iCol = group.autoTag ? 0 : -1; 7673 for (let i = iStart; i < iEnd; i++) { 7674 if (cols[i].type === "align") { 7675 const colAlign = alignMap[cols[i].align]; 7676 align += colAlign; 7677 iCol += 1; 7678 for (const row of table.children) { 7679 if (colAlign.trim() !== "center" && iCol < row.children.length) { 7680 row.children[iCol].classes = ["tml-" + colAlign.trim()]; 7681 } 7682 } 7683 prevTypeWasAlign = true; 7684 } else if (cols[i].type === "separator") { 7685 // MathML accepts only single lines between cells. 7686 // So we read only the first of consecutive separators. 7687 if (prevTypeWasAlign) { 7688 const sep = cols[i + 1].type === "separator" 7689 ? "0.15em double" 7690 : cols[i].separator === "|" 7691 ? "0.06em solid" 7692 : "0.06em dashed"; 7693 for (const row of table.children) { 7694 if (iCol < row.children.length) { 7695 row.children[iCol].style.borderRight = sep; 7696 } 7697 } 7698 } 7699 prevTypeWasAlign = false; 7700 } 7701 } 7702 if (cols[cols.length - 1].type === "separator") { 7703 const sep = cols[cols.length - 2].type === "separator" 7704 ? "0.15em double" 7705 : cols[cols.length - 1].separator === "|" 7706 ? "0.06em solid" 7707 : "0.06em dashed"; 7708 for (const row of table.children) { 7709 row.children[row.children.length - 1].style.borderRight = sep; 7710 row.children[row.children.length - 1].style.paddingRight = "0.4em"; 7711 } 7712 } 7713 } 7714 if (group.autoTag) { 7715 // allow for glue cells on each side 7716 align = "left " + (align.length > 0 ? align : "center ") + "right "; 7717 } 7718 if (align) { 7719 // Firefox reads this attribute, not the -webkit-left|right written above. 7720 // TODO: When Chrome no longer needs "-webkit-", use CSS and delete the next line. 7721 table.setAttribute("columnalign", align.trim()); 7722 } 7723 7724 if (group.envClasses.includes("small")) { 7725 // A small array. Wrap in scriptstyle. 7726 table = new mathMLTree.MathNode("mstyle", [table]); 7727 table.setAttribute("scriptlevel", "1"); 7728 } 7729 7730 return table 7731 }; 7732 7733 // Convenience function for align, align*, aligned, alignat, alignat*, alignedat, split. 7734 const alignedHandler = function(context, args) { 7735 if (context.envName.indexOf("ed") === -1) { 7736 validateAmsEnvironmentContext(context); 7737 } 7738 const isSplit = context.envName === "split"; 7739 const cols = []; 7740 const res = parseArray( 7741 context.parser, 7742 { 7743 cols, 7744 emptySingleRow: true, 7745 autoTag: isSplit ? undefined : getAutoTag(context.envName), 7746 envClasses: ["abut", "jot"], // set row spacing & provisional column spacing 7747 maxNumCols: context.envName === "split" ? 2 : undefined, 7748 leqno: context.parser.settings.leqno 7749 }, 7750 "display" 7751 ); 7752 7753 // Determining number of columns. 7754 // 1. If the first argument is given, we use it as a number of columns, 7755 // and makes sure that each row doesn't exceed that number. 7756 // 2. Otherwise, just count number of columns = maximum number 7757 // of cells in each row ("aligned" mode -- isAligned will be true). 7758 // 7759 // At the same time, prepend empty group {} at beginning of every second 7760 // cell in each row (starting with second cell) so that operators become 7761 // binary. This behavior is implemented in amsmath's \start@aligned. 7762 let numMaths; 7763 let numCols = 0; 7764 const isAlignedAt = context.envName.indexOf("at") > -1; 7765 if (args[0] && isAlignedAt) { 7766 // alignat environment takes an argument w/ number of columns 7767 let arg0 = ""; 7768 for (let i = 0; i < args[0].body.length; i++) { 7769 const textord = assertNodeType(args[0].body[i], "textord"); 7770 arg0 += textord.text; 7771 } 7772 if (isNaN(arg0)) { 7773 throw new ParseError("The alignat enviroment requires a numeric first argument.") 7774 } 7775 numMaths = Number(arg0); 7776 numCols = numMaths * 2; 7777 } 7778 res.body.forEach(function(row) { 7779 if (isAlignedAt) { 7780 // Case 1 7781 const curMaths = row.length / 2; 7782 if (numMaths < curMaths) { 7783 throw new ParseError( 7784 "Too many math in a row: " + `expected $numMaths}, but got $curMaths}`, 7785 row[0] 7786 ); 7787 } 7788 } else if (numCols < row.length) { 7789 // Case 2 7790 numCols = row.length; 7791 } 7792 }); 7793 7794 // Adjusting alignment. 7795 // In aligned mode, we add one \qquad between columns; 7796 // otherwise we add nothing. 7797 for (let i = 0; i < numCols; ++i) { 7798 let align = "r"; 7799 if (i % 2 === 1) { 7800 align = "l"; 7801 } 7802 cols[i] = { 7803 type: "align", 7804 align: align 7805 }; 7806 } 7807 if (context.envName === "split") ; else if (isAlignedAt) { 7808 res.envClasses.push("alignat"); // Sets justification 7809 } else { 7810 res.envClasses[0] = "align"; // Sets column spacing & justification 7811 } 7812 return res; 7813 }; 7814 7815 // Arrays are part of LaTeX, defined in lttab.dtx so its documentation 7816 // is part of the source2e.pdf file of LaTeX2e source documentation. 7817 // {darray} is an {array} environment where cells are set in \displaystyle, 7818 // as defined in nccmath.sty. 7819 defineEnvironment({ 7820 type: "array", 7821 names: ["array", "darray"], 7822 props: { 7823 numArgs: 1 7824 }, 7825 handler(context, args) { 7826 // Since no types are specified above, the two possibilities are 7827 // - The argument is wrapped in {} or [], in which case Parser's 7828 // parseGroup() returns an "ordgroup" wrapping some symbol node. 7829 // - The argument is a bare symbol node. 7830 const symNode = checkSymbolNodeType(args[0]); 7831 const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; 7832 const cols = colalign.map(function(nde) { 7833 const node = assertSymbolNodeType(nde); 7834 const ca = node.text; 7835 if ("lcr".indexOf(ca) !== -1) { 7836 return { 7837 type: "align", 7838 align: ca 7839 }; 7840 } else if (ca === "|") { 7841 return { 7842 type: "separator", 7843 separator: "|" 7844 }; 7845 } else if (ca === ":") { 7846 return { 7847 type: "separator", 7848 separator: ":" 7849 }; 7850 } 7851 throw new ParseError("Unknown column alignment: " + ca, nde); 7852 }); 7853 const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); 7854 const res = { 7855 cols, 7856 envClasses: ["array"], 7857 maxNumCols: cols.length, 7858 arraystretch, 7859 arraycolsep 7860 }; 7861 return parseArray(context.parser, res, dCellStyle(context.envName)); 7862 }, 7863 mathmlBuilder: mathmlBuilder$7 7864 }); 7865 7866 // The matrix environments of amsmath builds on the array environment 7867 // of LaTeX, which is discussed above. 7868 // The mathtools package adds starred versions of the same environments. 7869 // These have an optional argument to choose left|center|right justification. 7870 defineEnvironment({ 7871 type: "array", 7872 names: [ 7873 "matrix", 7874 "pmatrix", 7875 "bmatrix", 7876 "Bmatrix", 7877 "vmatrix", 7878 "Vmatrix", 7879 "matrix*", 7880 "pmatrix*", 7881 "bmatrix*", 7882 "Bmatrix*", 7883 "vmatrix*", 7884 "Vmatrix*" 7885 ], 7886 props: { 7887 numArgs: 0 7888 }, 7889 handler(context) { 7890 const delimiters = { 7891 matrix: null, 7892 pmatrix: ["(", ")"], 7893 bmatrix: ["[", "]"], 7894 Bmatrix: ["\\{", "\\}"], 7895 vmatrix: ["|", "|"], 7896 Vmatrix: ["\\Vert", "\\Vert"] 7897 }[context.envName.replace("*", "")]; 7898 // \hskip -\arraycolsep in amsmath 7899 let colAlign = "c"; 7900 const payload = { 7901 envClasses: [], 7902 cols: [] 7903 }; 7904 if (context.envName.charAt(context.envName.length - 1) === "*") { 7905 // It's one of the mathtools starred functions. 7906 // Parse the optional alignment argument. 7907 const parser = context.parser; 7908 parser.consumeSpaces(); 7909 if (parser.fetch().text === "[") { 7910 parser.consume(); 7911 parser.consumeSpaces(); 7912 colAlign = parser.fetch().text; 7913 if ("lcr".indexOf(colAlign) === -1) { 7914 throw new ParseError("Expected l or c or r", parser.nextToken); 7915 } 7916 parser.consume(); 7917 parser.consumeSpaces(); 7918 parser.expect("]"); 7919 parser.consume(); 7920 payload.cols = []; 7921 } 7922 } 7923 const res = parseArray(context.parser, payload, "text"); 7924 res.cols = new Array(res.body[0].length).fill({ type: "align", align: colAlign }); 7925 const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros); 7926 return delimiters 7927 ? { 7928 type: "leftright", 7929 mode: context.mode, 7930 body: [res], 7931 left: delimiters[0], 7932 right: delimiters[1], 7933 rightColor: undefined, // \right uninfluenced by \color in array 7934 arraystretch, 7935 arraycolsep 7936 } 7937 : res; 7938 }, 7939 mathmlBuilder: mathmlBuilder$7 7940 }); 7941 7942 defineEnvironment({ 7943 type: "array", 7944 names: ["smallmatrix"], 7945 props: { 7946 numArgs: 0 7947 }, 7948 handler(context) { 7949 const payload = { type: "small" }; 7950 const res = parseArray(context.parser, payload, "script"); 7951 res.envClasses = ["small"]; 7952 return res; 7953 }, 7954 mathmlBuilder: mathmlBuilder$7 7955 }); 7956 7957 defineEnvironment({ 7958 type: "array", 7959 names: ["subarray"], 7960 props: { 7961 numArgs: 1 7962 }, 7963 handler(context, args) { 7964 // Parsing of {subarray} is similar to {array} 7965 const symNode = checkSymbolNodeType(args[0]); 7966 const colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; 7967 const cols = colalign.map(function(nde) { 7968 const node = assertSymbolNodeType(nde); 7969 const ca = node.text; 7970 // {subarray} only recognizes "l" & "c" 7971 if ("lc".indexOf(ca) !== -1) { 7972 return { 7973 type: "align", 7974 align: ca 7975 }; 7976 } 7977 throw new ParseError("Unknown column alignment: " + ca, nde); 7978 }); 7979 if (cols.length > 1) { 7980 throw new ParseError("{subarray} can contain only one column"); 7981 } 7982 let res = { 7983 cols, 7984 envClasses: ["small"] 7985 }; 7986 res = parseArray(context.parser, res, "script"); 7987 if (res.body.length > 0 && res.body[0].length > 1) { 7988 throw new ParseError("{subarray} can contain only one column"); 7989 } 7990 return res; 7991 }, 7992 mathmlBuilder: mathmlBuilder$7 7993 }); 7994 7995 // A cases environment (in amsmath.sty) is almost equivalent to 7996 // \def 7997 // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. 7998 // {dcases} is a {cases} environment where cells are set in \displaystyle, 7999 // as defined in mathtools.sty. 8000 // {rcases} is another mathtools environment. It's brace is on the right side. 8001 defineEnvironment({ 8002 type: "array", 8003 names: ["cases", "dcases", "rcases", "drcases"], 8004 props: { 8005 numArgs: 0 8006 }, 8007 handler(context) { 8008 const payload = { 8009 cols: [], 8010 envClasses: ["cases"] 8011 }; 8012 const res = parseArray(context.parser, payload, dCellStyle(context.envName)); 8013 return { 8014 type: "leftright", 8015 mode: context.mode, 8016 body: [res], 8017 left: context.envName.indexOf("r") > -1 ? "." : "\\{", 8018 right: context.envName.indexOf("r") > -1 ? "\\}" : ".", 8019 rightColor: undefined 8020 }; 8021 }, 8022 mathmlBuilder: mathmlBuilder$7 8023 }); 8024 8025 // In the align environment, one uses ampersands, &, to specify number of 8026 // columns in each row, and to locate spacing between each column. 8027 // align gets automatic numbering. align* and aligned do not. 8028 // The alignedat environment can be used in math mode. 8029 defineEnvironment({ 8030 type: "array", 8031 names: ["align", "align*", "aligned", "split"], 8032 props: { 8033 numArgs: 0 8034 }, 8035 handler: alignedHandler, 8036 mathmlBuilder: mathmlBuilder$7 8037 }); 8038 8039 // alignat environment is like an align environment, but one must explicitly 8040 // specify maximum number of columns in each row, and can adjust where spacing occurs. 8041 defineEnvironment({ 8042 type: "array", 8043 names: ["alignat", "alignat*", "alignedat"], 8044 props: { 8045 numArgs: 1 8046 }, 8047 handler: alignedHandler, 8048 mathmlBuilder: mathmlBuilder$7 8049 }); 8050 8051 // A gathered environment is like an array environment with one centered 8052 // column, but where rows are considered lines so get \jot line spacing 8053 // and contents are set in \displaystyle. 8054 defineEnvironment({ 8055 type: "array", 8056 names: ["gathered", "gather", "gather*"], 8057 props: { 8058 numArgs: 0 8059 }, 8060 handler(context) { 8061 if (context.envName !== "gathered") { 8062 validateAmsEnvironmentContext(context); 8063 } 8064 const res = { 8065 cols: [], 8066 envClasses: ["abut", "jot"], 8067 autoTag: getAutoTag(context.envName), 8068 emptySingleRow: true, 8069 leqno: context.parser.settings.leqno 8070 }; 8071 return parseArray(context.parser, res, "display"); 8072 }, 8073 mathmlBuilder: mathmlBuilder$7 8074 }); 8075 8076 defineEnvironment({ 8077 type: "array", 8078 names: ["equation", "equation*"], 8079 props: { 8080 numArgs: 0 8081 }, 8082 handler(context) { 8083 validateAmsEnvironmentContext(context); 8084 const res = { 8085 autoTag: getAutoTag(context.envName), 8086 emptySingleRow: true, 8087 singleRow: true, 8088 maxNumCols: 1, 8089 envClasses: ["align"], 8090 leqno: context.parser.settings.leqno 8091 }; 8092 return parseArray(context.parser, res, "display"); 8093 }, 8094 mathmlBuilder: mathmlBuilder$7 8095 }); 8096 8097 defineEnvironment({ 8098 type: "array", 8099 names: ["multline", "multline*"], 8100 props: { 8101 numArgs: 0 8102 }, 8103 handler(context) { 8104 validateAmsEnvironmentContext(context); 8105 const res = { 8106 autoTag: context.envName === "multline", 8107 maxNumCols: 1, 8108 envClasses: ["jot", "multline"], 8109 leqno: context.parser.settings.leqno 8110 }; 8111 return parseArray(context.parser, res, "display"); 8112 }, 8113 mathmlBuilder: mathmlBuilder$7 8114 }); 8115 8116 defineEnvironment({ 8117 type: "array", 8118 names: ["CD"], 8119 props: { 8120 numArgs: 0 8121 }, 8122 handler(context) { 8123 validateAmsEnvironmentContext(context); 8124 return parseCD(context.parser); 8125 }, 8126 mathmlBuilder: mathmlBuilder$7 8127 }); 8128 8129 // Catch \hline outside array environment 8130 defineFunction({ 8131 type: "text", // Doesn't matter what this is. 8132 names: ["\\hline", "\\hdashline"], 8133 props: { 8134 numArgs: 0, 8135 allowedInText: true, 8136 allowedInMath: true 8137 }, 8138 handler(context, args) { 8139 throw new ParseError(`$context.funcName} valid only within array environment`); 8140 } 8141 }); 8142 8143 const environments = _environments; 8144 8145 // Environment delimiters. HTML/MathML rendering is defined in the corresponding 8146 // defineEnvironment definitions. 8147 defineFunction({ 8148 type: "environment", 8149 names: ["\\begin", "\\end"], 8150 props: { 8151 numArgs: 1, 8152 argTypes: ["text"] 8153 }, 8154 handler({ parser, funcName }, args) { 8155 const nameGroup = args[0]; 8156 if (nameGroup.type !== "ordgroup") { 8157 throw new ParseError("Invalid environment name", nameGroup); 8158 } 8159 let envName = ""; 8160 for (let i = 0; i < nameGroup.body.length; ++i) { 8161 envName += assertNodeType(nameGroup.body[i], "textord").text; 8162 } 8163 8164 if (funcName === "\\begin") { 8165 // begin...end is similar to left...right 8166 if (!Object.prototype.hasOwnProperty.call(environments, envName )) { 8167 throw new ParseError("No such environment: " + envName, nameGroup); 8168 } 8169 // Build the environment object. Arguments and other information will 8170 // be made available to the begin and end methods using properties. 8171 const env = environments[envName]; 8172 const { args, optArgs } = parser.parseArguments("\\begin{" + envName + "}", env); 8173 const context = { 8174 mode: parser.mode, 8175 envName, 8176 parser 8177 }; 8178 const result = env.handler(context, args, optArgs); 8179 parser.expect("\\end", false); 8180 const endNameToken = parser.nextToken; 8181 const end = assertNodeType(parser.parseFunction(), "environment"); 8182 if (end.name !== envName) { 8183 throw new ParseError( 8184 `Mismatch: \\begin{$envName}} matched by \\end{$end.name}}`, 8185 endNameToken 8186 ); 8187 } 8188 return result; 8189 } 8190 8191 return { 8192 type: "environment", 8193 mode: parser.mode, 8194 name: envName, 8195 nameGroup 8196 }; 8197 } 8198 }); 8199 8200 defineFunction({ 8201 type: "envTag", 8202 names: ["\\env@tag"], 8203 props: { 8204 numArgs: 1, 8205 argTypes: ["math"] 8206 }, 8207 handler({ parser }, args) { 8208 return { 8209 type: "envTag", 8210 mode: parser.mode, 8211 body: args[0] 8212 }; 8213 }, 8214 mathmlBuilder(group, style) { 8215 return new mathMLTree.MathNode("mrow"); 8216 } 8217 }); 8218 8219 defineFunction({ 8220 type: "noTag", 8221 names: ["\\env@notag"], 8222 props: { 8223 numArgs: 0 8224 }, 8225 handler({ parser }) { 8226 return { 8227 type: "noTag", 8228 mode: parser.mode 8229 }; 8230 }, 8231 mathmlBuilder(group, style) { 8232 return new mathMLTree.MathNode("mrow"); 8233 } 8234 }); 8235 8236 const isLongVariableName = (group, font) => { 8237 if (font !== "mathrm" || group.body.type !== "ordgroup" || group.body.body.length === 1) { 8238 return false 8239 } 8240 if (group.body.body[0].type !== "mathord") { return false } 8241 for (let i = 1; i < group.body.body.length; i++) { 8242 const parseNodeType = group.body.body[i].type; 8243 if (!(parseNodeType === "mathord" || 8244 (parseNodeType === "textord" && !isNaN(group.body.body[i].text)))) { 8245 return false 8246 } 8247 } 8248 return true 8249 }; 8250 8251 const mathmlBuilder$6 = (group, style) => { 8252 const font = group.font; 8253 const newStyle = style.withFont(font); 8254 const mathGroup = buildGroup$1(group.body, newStyle); 8255 8256 if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{} 8257 if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) { 8258 mathGroup.style.fontWeight = "bold"; 8259 return mathGroup 8260 } 8261 // Check if it is possible to consolidate elements into a single <mi> element. 8262 if (isLongVariableName(group, font)) { 8263 // This is a \mathrm{…} group. It gets special treatment because symbolsOrd.js 8264 // wraps <mi> elements with <mrow>s to work around a Firefox bug. 8265 const mi = mathGroup.children[0].children[0]; 8266 delete mi.attributes.mathvariant; 8267 for (let i = 1; i < mathGroup.children.length; i++) { 8268 mi.children[0].text += mathGroup.children[i].type === "mn" 8269 ? mathGroup.children[i].children[0].text 8270 : mathGroup.children[i].children[0].children[0].text; 8271 } 8272 // Wrap in a <mrow> to prevent the same Firefox bug. 8273 const bogus = new mathMLTree.MathNode("mtext", new mathMLTree.TextNode("\u200b")); 8274 return new mathMLTree.MathNode("mrow", [bogus, mi]) 8275 } 8276 let canConsolidate = mathGroup.children[0].type === "mo"; 8277 for (let i = 1; i < mathGroup.children.length; i++) { 8278 if (mathGroup.children[i].type === "mo" && font === "boldsymbol") { 8279 mathGroup.children[i].style.fontWeight = "bold"; 8280 } 8281 if (mathGroup.children[i].type !== "mi") { canConsolidate = false; } 8282 const localVariant = mathGroup.children[i].attributes && 8283 mathGroup.children[i].attributes.mathvariant || ""; 8284 if (localVariant !== "normal") { canConsolidate = false; } 8285 } 8286 if (!canConsolidate) { return mathGroup } 8287 // Consolidate the <mi> elements. 8288 const mi = mathGroup.children[0]; 8289 for (let i = 1; i < mathGroup.children.length; i++) { 8290 mi.children.push(mathGroup.children[i].children[0]); 8291 } 8292 if (mi.attributes.mathvariant && mi.attributes.mathvariant === "normal") { 8293 // Workaround for a Firefox bug that renders spurious space around 8294 // a <mi mathvariant="normal"> 8295 // Ref: https://bugs.webkit.org/show_bug.cgi?id=129097 8296 // We insert a text node that contains a zero-width space and wrap in an mrow. 8297 // TODO: Get rid of this <mi> workaround when the Firefox bug is fixed. 8298 const bogus = new mathMLTree.MathNode("mtext", new mathMLTree.TextNode("\u200b")); 8299 return new mathMLTree.MathNode("mrow", [bogus, mi]) 8300 } 8301 return mi 8302 }; 8303 8304 const fontAliases = { 8305 "\\Bbb": "\\mathbb", 8306 "\\bold": "\\mathbf", 8307 "\\frak": "\\mathfrak", 8308 "\\bm": "\\boldsymbol" 8309 }; 8310 8311 defineFunction({ 8312 type: "font", 8313 names: [ 8314 // styles 8315 "\\mathrm", 8316 "\\mathit", 8317 "\\mathbf", 8318 "\\mathnormal", 8319 "\\up@greek", 8320 "\\boldsymbol", 8321 8322 // families 8323 "\\mathbb", 8324 "\\mathcal", 8325 "\\mathfrak", 8326 "\\mathscr", 8327 "\\mathsf", 8328 "\\mathsfit", 8329 "\\mathtt", 8330 8331 // aliases 8332 "\\Bbb", 8333 "\\bm", 8334 "\\bold", 8335 "\\frak" 8336 ], 8337 props: { 8338 numArgs: 1, 8339 allowedInArgument: true 8340 }, 8341 handler: ({ parser, funcName }, args) => { 8342 const body = normalizeArgument(args[0]); 8343 let func = funcName; 8344 if (func in fontAliases) { 8345 func = fontAliases[func]; 8346 } 8347 return { 8348 type: "font", 8349 mode: parser.mode, 8350 font: func.slice(1), 8351 body 8352 }; 8353 }, 8354 mathmlBuilder: mathmlBuilder$6 8355 }); 8356 8357 // Old font changing functions 8358 defineFunction({ 8359 type: "font", 8360 names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"], 8361 props: { 8362 numArgs: 0, 8363 allowedInText: true 8364 }, 8365 handler: ({ parser, funcName, breakOnTokenText }, args) => { 8366 const { mode } = parser; 8367 const body = parser.parseExpression(true, breakOnTokenText, true); 8368 const fontStyle = `math$funcName.slice(1)}`; 8369 8370 return { 8371 type: "font", 8372 mode: mode, 8373 font: fontStyle, 8374 body: { 8375 type: "ordgroup", 8376 mode: parser.mode, 8377 body 8378 } 8379 }; 8380 }, 8381 mathmlBuilder: mathmlBuilder$6 8382 }); 8383 8384 const stylArray = ["display", "text", "script", "scriptscript"]; 8385 const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 }; 8386 8387 const mathmlBuilder$5 = (group, style) => { 8388 // Track the scriptLevel of the numerator and denominator. 8389 // We may need that info for \mathchoice or for adjusting em dimensions. 8390 const childOptions = group.scriptLevel === "auto" 8391 ? style.incrementLevel() 8392 : group.scriptLevel === "display" 8393 ? style.withLevel(StyleLevel.TEXT) 8394 : group.scriptLevel === "text" 8395 ? style.withLevel(StyleLevel.SCRIPT) 8396 : style.withLevel(StyleLevel.SCRIPTSCRIPT); 8397 8398 // Chromium (wrongly) continues to shrink fractions beyond scriptscriptlevel. 8399 // So we check for levels that Chromium shrinks too small. 8400 // If necessary, set an explicit fraction depth. 8401 const numer = buildGroup$1(group.numer, childOptions); 8402 const denom = buildGroup$1(group.denom, childOptions); 8403 if (style.level === 3) { 8404 numer.style.mathDepth = "2"; 8405 numer.setAttribute("scriptlevel", "2"); 8406 denom.style.mathDepth = "2"; 8407 denom.setAttribute("scriptlevel", "2"); 8408 } 8409 8410 let node = new mathMLTree.MathNode("mfrac", [numer, denom]); 8411 8412 if (!group.hasBarLine) { 8413 node.setAttribute("linethickness", "0px"); 8414 } else if (group.barSize) { 8415 const ruleWidth = calculateSize(group.barSize, style); 8416 node.setAttribute("linethickness", ruleWidth.number + ruleWidth.unit); 8417 } 8418 8419 if (group.leftDelim != null || group.rightDelim != null) { 8420 const withDelims = []; 8421 8422 if (group.leftDelim != null) { 8423 const leftOp = new mathMLTree.MathNode("mo", [ 8424 new mathMLTree.TextNode(group.leftDelim.replace("\\", "")) 8425 ]); 8426 leftOp.setAttribute("fence", "true"); 8427 withDelims.push(leftOp); 8428 } 8429 8430 withDelims.push(node); 8431 8432 if (group.rightDelim != null) { 8433 const rightOp = new mathMLTree.MathNode("mo", [ 8434 new mathMLTree.TextNode(group.rightDelim.replace("\\", "")) 8435 ]); 8436 rightOp.setAttribute("fence", "true"); 8437 withDelims.push(rightOp); 8438 } 8439 8440 node = makeRow(withDelims); 8441 } 8442 8443 if (group.scriptLevel !== "auto") { 8444 node = new mathMLTree.MathNode("mstyle", [node]); 8445 node.setAttribute("displaystyle", String(group.scriptLevel === "display")); 8446 node.setAttribute("scriptlevel", scriptLevel[group.scriptLevel]); 8447 } 8448 8449 return node; 8450 }; 8451 8452 defineFunction({ 8453 type: "genfrac", 8454 names: [ 8455 "\\dfrac", 8456 "\\frac", 8457 "\\tfrac", 8458 "\\dbinom", 8459 "\\binom", 8460 "\\tbinom", 8461 "\\\\atopfrac", // can’t be entered directly 8462 "\\\\bracefrac", 8463 "\\\\brackfrac" // ditto 8464 ], 8465 props: { 8466 numArgs: 2, 8467 allowedInArgument: true 8468 }, 8469 handler: ({ parser, funcName }, args) => { 8470 const numer = args[0]; 8471 const denom = args[1]; 8472 let hasBarLine = false; 8473 let leftDelim = null; 8474 let rightDelim = null; 8475 let scriptLevel = "auto"; 8476 8477 switch (funcName) { 8478 case "\\dfrac": 8479 case "\\frac": 8480 case "\\tfrac": 8481 hasBarLine = true; 8482 break; 8483 case "\\\\atopfrac": 8484 hasBarLine = false; 8485 break; 8486 case "\\dbinom": 8487 case "\\binom": 8488 case "\\tbinom": 8489 leftDelim = "("; 8490 rightDelim = ")"; 8491 break; 8492 case "\\\\bracefrac": 8493 leftDelim = "\\{"; 8494 rightDelim = "\\}"; 8495 break; 8496 case "\\\\brackfrac": 8497 leftDelim = "["; 8498 rightDelim = "]"; 8499 break; 8500 default: 8501 throw new Error("Unrecognized genfrac command"); 8502 } 8503 8504 switch (funcName) { 8505 case "\\dfrac": 8506 case "\\dbinom": 8507 scriptLevel = "display"; 8508 break; 8509 case "\\tfrac": 8510 case "\\tbinom": 8511 scriptLevel = "text"; 8512 break; 8513 } 8514 8515 return { 8516 type: "genfrac", 8517 mode: parser.mode, 8518 continued: false, 8519 numer, 8520 denom, 8521 hasBarLine, 8522 leftDelim, 8523 rightDelim, 8524 scriptLevel, 8525 barSize: null 8526 }; 8527 }, 8528 mathmlBuilder: mathmlBuilder$5 8529 }); 8530 8531 defineFunction({ 8532 type: "genfrac", 8533 names: ["\\cfrac"], 8534 props: { 8535 numArgs: 2 8536 }, 8537 handler: ({ parser, funcName }, args) => { 8538 const numer = args[0]; 8539 const denom = args[1]; 8540 8541 return { 8542 type: "genfrac", 8543 mode: parser.mode, 8544 continued: true, 8545 numer, 8546 denom, 8547 hasBarLine: true, 8548 leftDelim: null, 8549 rightDelim: null, 8550 scriptLevel: "display", 8551 barSize: null 8552 }; 8553 } 8554 }); 8555 8556 // Infix generalized fractions -- these are not rendered directly, but replaced 8557 // immediately by one of the variants above. 8558 defineFunction({ 8559 type: "infix", 8560 names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"], 8561 props: { 8562 numArgs: 0, 8563 infix: true 8564 }, 8565 handler({ parser, funcName, token }) { 8566 let replaceWith; 8567 switch (funcName) { 8568 case "\\over": 8569 replaceWith = "\\frac"; 8570 break; 8571 case "\\choose": 8572 replaceWith = "\\binom"; 8573 break; 8574 case "\\atop": 8575 replaceWith = "\\\\atopfrac"; 8576 break; 8577 case "\\brace": 8578 replaceWith = "\\\\bracefrac"; 8579 break; 8580 case "\\brack": 8581 replaceWith = "\\\\brackfrac"; 8582 break; 8583 default: 8584 throw new Error("Unrecognized infix genfrac command"); 8585 } 8586 return { 8587 type: "infix", 8588 mode: parser.mode, 8589 replaceWith, 8590 token 8591 }; 8592 } 8593 }); 8594 8595 const delimFromValue = function(delimString) { 8596 let delim = null; 8597 if (delimString.length > 0) { 8598 delim = delimString; 8599 delim = delim === "." ? null : delim; 8600 } 8601 return delim; 8602 }; 8603 8604 defineFunction({ 8605 type: "genfrac", 8606 names: ["\\genfrac"], 8607 props: { 8608 numArgs: 6, 8609 allowedInArgument: true, 8610 argTypes: ["math", "math", "size", "text", "math", "math"] 8611 }, 8612 handler({ parser }, args) { 8613 const numer = args[4]; 8614 const denom = args[5]; 8615 8616 // Look into the parse nodes to get the desired delimiters. 8617 const leftNode = normalizeArgument(args[0]); 8618 const leftDelim = leftNode.type === "atom" && leftNode.family === "open" 8619 ? delimFromValue(leftNode.text) 8620 : null; 8621 const rightNode = normalizeArgument(args[1]); 8622 const rightDelim = 8623 rightNode.type === "atom" && rightNode.family === "close" 8624 ? delimFromValue(rightNode.text) 8625 : null; 8626 8627 const barNode = assertNodeType(args[2], "size"); 8628 let hasBarLine; 8629 let barSize = null; 8630 if (barNode.isBlank) { 8631 // \genfrac acts differently than \above. 8632 // \genfrac treats an empty size group as a signal to use a 8633 // standard bar size. \above would see size = 0 and omit the bar. 8634 hasBarLine = true; 8635 } else { 8636 barSize = barNode.value; 8637 hasBarLine = barSize.number > 0; 8638 } 8639 8640 // Find out if we want displaystyle, textstyle, etc. 8641 let scriptLevel = "auto"; 8642 let styl = args[3]; 8643 if (styl.type === "ordgroup") { 8644 if (styl.body.length > 0) { 8645 const textOrd = assertNodeType(styl.body[0], "textord"); 8646 scriptLevel = stylArray[Number(textOrd.text)]; 8647 } 8648 } else { 8649 styl = assertNodeType(styl, "textord"); 8650 scriptLevel = stylArray[Number(styl.text)]; 8651 } 8652 8653 return { 8654 type: "genfrac", 8655 mode: parser.mode, 8656 numer, 8657 denom, 8658 continued: false, 8659 hasBarLine, 8660 barSize, 8661 leftDelim, 8662 rightDelim, 8663 scriptLevel 8664 }; 8665 }, 8666 mathmlBuilder: mathmlBuilder$5 8667 }); 8668 8669 // \above is an infix fraction that also defines a fraction bar size. 8670 defineFunction({ 8671 type: "infix", 8672 names: ["\\above"], 8673 props: { 8674 numArgs: 1, 8675 argTypes: ["size"], 8676 infix: true 8677 }, 8678 handler({ parser, funcName, token }, args) { 8679 return { 8680 type: "infix", 8681 mode: parser.mode, 8682 replaceWith: "\\\\abovefrac", 8683 barSize: assertNodeType(args[0], "size").value, 8684 token 8685 }; 8686 } 8687 }); 8688 8689 defineFunction({ 8690 type: "genfrac", 8691 names: ["\\\\abovefrac"], 8692 props: { 8693 numArgs: 3, 8694 argTypes: ["math", "size", "math"] 8695 }, 8696 handler: ({ parser, funcName }, args) => { 8697 const numer = args[0]; 8698 const barSize = assert(assertNodeType(args[1], "infix").barSize); 8699 const denom = args[2]; 8700 8701 const hasBarLine = barSize.number > 0; 8702 return { 8703 type: "genfrac", 8704 mode: parser.mode, 8705 numer, 8706 denom, 8707 continued: false, 8708 hasBarLine, 8709 barSize, 8710 leftDelim: null, 8711 rightDelim: null, 8712 scriptLevel: "auto" 8713 }; 8714 }, 8715 8716 mathmlBuilder: mathmlBuilder$5 8717 }); 8718 8719 // \hbox is provided for compatibility with LaTeX functions that act on a box. 8720 // This function by itself doesn't do anything but set scriptlevel to \textstyle 8721 // and prevent a soft line break. 8722 8723 defineFunction({ 8724 type: "hbox", 8725 names: ["\\hbox"], 8726 props: { 8727 numArgs: 1, 8728 argTypes: ["hbox"], 8729 allowedInArgument: true, 8730 allowedInText: false 8731 }, 8732 handler({ parser }, args) { 8733 return { 8734 type: "hbox", 8735 mode: parser.mode, 8736 body: ordargument(args[0]) 8737 }; 8738 }, 8739 mathmlBuilder(group, style) { 8740 const newStyle = style.withLevel(StyleLevel.TEXT); 8741 const mrow = buildExpressionRow(group.body, newStyle); 8742 return consolidateText(mrow) 8743 } 8744 }); 8745 8746 const mathmlBuilder$4 = (group, style) => { 8747 const accentNode = stretchy.mathMLnode(group.label); 8748 accentNode.style["math-depth"] = 0; 8749 return new mathMLTree.MathNode(group.isOver ? "mover" : "munder", [ 8750 buildGroup$1(group.base, style), 8751 accentNode 8752 ]); 8753 }; 8754 8755 // Horizontal stretchy braces 8756 defineFunction({ 8757 type: "horizBrace", 8758 names: ["\\overbrace", "\\underbrace"], 8759 props: { 8760 numArgs: 1 8761 }, 8762 handler({ parser, funcName }, args) { 8763 return { 8764 type: "horizBrace", 8765 mode: parser.mode, 8766 label: funcName, 8767 isOver: /^\\over/.test(funcName), 8768 base: args[0] 8769 }; 8770 }, 8771 mathmlBuilder: mathmlBuilder$4 8772 }); 8773 8774 defineFunction({ 8775 type: "href", 8776 names: ["\\href"], 8777 props: { 8778 numArgs: 2, 8779 argTypes: ["url", "original"], 8780 allowedInText: true 8781 }, 8782 handler: ({ parser, token }, args) => { 8783 const body = args[1]; 8784 const href = assertNodeType(args[0], "url").url; 8785 8786 if ( 8787 !parser.settings.isTrusted({ 8788 command: "\\href", 8789 url: href 8790 }) 8791 ) { 8792 throw new ParseError(`Function "\\href" is not trusted`, token) 8793 } 8794 8795 return { 8796 type: "href", 8797 mode: parser.mode, 8798 href, 8799 body: ordargument(body) 8800 }; 8801 }, 8802 mathmlBuilder: (group, style) => { 8803 const math = new MathNode("math", [buildExpressionRow(group.body, style)]); 8804 const anchorNode = new AnchorNode(group.href, [], [math]); 8805 return anchorNode 8806 } 8807 }); 8808 8809 defineFunction({ 8810 type: "href", 8811 names: ["\\url"], 8812 props: { 8813 numArgs: 1, 8814 argTypes: ["url"], 8815 allowedInText: true 8816 }, 8817 handler: ({ parser, token }, args) => { 8818 const href = assertNodeType(args[0], "url").url; 8819 8820 if ( 8821 !parser.settings.isTrusted({ 8822 command: "\\url", 8823 url: href 8824 }) 8825 ) { 8826 throw new ParseError(`Function "\\url" is not trusted`, token) 8827 } 8828 8829 const chars = []; 8830 for (let i = 0; i < href.length; i++) { 8831 let c = href[i]; 8832 if (c === "~") { 8833 c = "\\textasciitilde"; 8834 } 8835 chars.push({ 8836 type: "textord", 8837 mode: "text", 8838 text: c 8839 }); 8840 } 8841 const body = { 8842 type: "text", 8843 mode: parser.mode, 8844 font: "\\texttt", 8845 body: chars 8846 }; 8847 return { 8848 type: "href", 8849 mode: parser.mode, 8850 href, 8851 body: ordargument(body) 8852 }; 8853 } 8854 }); 8855 8856 defineFunction({ 8857 type: "html", 8858 names: ["\\class", "\\id", "\\style", "\\data"], 8859 props: { 8860 numArgs: 2, 8861 argTypes: ["raw", "original"], 8862 allowedInText: true 8863 }, 8864 handler: ({ parser, funcName, token }, args) => { 8865 const value = assertNodeType(args[0], "raw").string; 8866 const body = args[1]; 8867 8868 if (parser.settings.strict) { 8869 throw new ParseError(`Function "$funcName}" is disabled in strict mode`, token) 8870 } 8871 8872 let trustContext; 8873 const attributes = {}; 8874 8875 switch (funcName) { 8876 case "\\class": 8877 attributes.class = value; 8878 trustContext = { 8879 command: "\\class", 8880 class: value 8881 }; 8882 break; 8883 case "\\id": 8884 attributes.id = value; 8885 trustContext = { 8886 command: "\\id", 8887 id: value 8888 }; 8889 break; 8890 case "\\style": 8891 attributes.style = value; 8892 trustContext = { 8893 command: "\\style", 8894 style: value 8895 }; 8896 break; 8897 case "\\data": { 8898 const data = value.split(","); 8899 for (let i = 0; i < data.length; i++) { 8900 const keyVal = data[i].split("="); 8901 if (keyVal.length !== 2) { 8902 throw new ParseError("Error parsing key-value for \\data"); 8903 } 8904 attributes["data-" + keyVal[0].trim()] = keyVal[1].trim(); 8905 } 8906 8907 trustContext = { 8908 command: "\\data", 8909 attributes 8910 }; 8911 break; 8912 } 8913 default: 8914 throw new Error("Unrecognized html command"); 8915 } 8916 8917 if (!parser.settings.isTrusted(trustContext)) { 8918 throw new ParseError(`Function "$funcName}" is not trusted`, token) 8919 } 8920 return { 8921 type: "html", 8922 mode: parser.mode, 8923 attributes, 8924 body: ordargument(body) 8925 }; 8926 }, 8927 mathmlBuilder: (group, style) => { 8928 const element = buildExpressionRow(group.body, style); 8929 8930 const classes = []; 8931 if (group.attributes.class) { 8932 classes.push(...group.attributes.class.trim().split(/\s+/)); 8933 } 8934 element.classes = classes; 8935 8936 for (const attr in group.attributes) { 8937 if (attr !== "class" && Object.prototype.hasOwnProperty.call(group.attributes, attr)) { 8938 element.setAttribute(attr, group.attributes[attr]); 8939 } 8940 } 8941 8942 return element; 8943 } 8944 }); 8945 8946 const sizeData = function(str) { 8947 if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) { 8948 // str is a number with no unit specified. 8949 // default unit is bp, per graphix package. 8950 return { number: +str, unit: "bp" } 8951 } else { 8952 const match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(str); 8953 if (!match) { 8954 throw new ParseError("Invalid size: '" + str + "' in \\includegraphics"); 8955 } 8956 const data = { 8957 number: +(match[1] + match[2]), // sign + magnitude, cast to number 8958 unit: match[3] 8959 }; 8960 if (!validUnit(data)) { 8961 throw new ParseError("Invalid unit: '" + data.unit + "' in \\includegraphics."); 8962 } 8963 return data 8964 } 8965 }; 8966 8967 defineFunction({ 8968 type: "includegraphics", 8969 names: ["\\includegraphics"], 8970 props: { 8971 numArgs: 1, 8972 numOptionalArgs: 1, 8973 argTypes: ["raw", "url"], 8974 allowedInText: false 8975 }, 8976 handler: ({ parser, token }, args, optArgs) => { 8977 let width = { number: 0, unit: "em" }; 8978 let height = { number: 0.9, unit: "em" }; // sorta character sized. 8979 let totalheight = { number: 0, unit: "em" }; 8980 let alt = ""; 8981 8982 if (optArgs[0]) { 8983 const attributeStr = assertNodeType(optArgs[0], "raw").string; 8984 8985 // Parser.js does not parse key/value pairs. We get a string. 8986 const attributes = attributeStr.split(","); 8987 for (let i = 0; i < attributes.length; i++) { 8988 const keyVal = attributes[i].split("="); 8989 if (keyVal.length === 2) { 8990 const str = keyVal[1].trim(); 8991 switch (keyVal[0].trim()) { 8992 case "alt": 8993 alt = str; 8994 break 8995 case "width": 8996 width = sizeData(str); 8997 break 8998 case "height": 8999 height = sizeData(str); 9000 break 9001 case "totalheight": 9002 totalheight = sizeData(str); 9003 break 9004 default: 9005 throw new ParseError("Invalid key: '" + keyVal[0] + "' in \\includegraphics.") 9006 } 9007 } 9008 } 9009 } 9010 9011 const src = assertNodeType(args[0], "url").url; 9012 9013 if (alt === "") { 9014 // No alt given. Use the file name. Strip away the path. 9015 alt = src; 9016 alt = alt.replace(/^.*[\\/]/, ""); 9017 alt = alt.substring(0, alt.lastIndexOf(".")); 9018 } 9019 9020 if ( 9021 !parser.settings.isTrusted({ 9022 command: "\\includegraphics", 9023 url: src 9024 }) 9025 ) { 9026 throw new ParseError(`Function "\\includegraphics" is not trusted`, token) 9027 } 9028 9029 return { 9030 type: "includegraphics", 9031 mode: parser.mode, 9032 alt: alt, 9033 width: width, 9034 height: height, 9035 totalheight: totalheight, 9036 src: src 9037 } 9038 }, 9039 mathmlBuilder: (group, style) => { 9040 const height = calculateSize(group.height, style); 9041 const depth = { number: 0, unit: "em" }; 9042 9043 if (group.totalheight.number > 0) { 9044 if (group.totalheight.unit === height.unit && 9045 group.totalheight.number > height.number) { 9046 depth.number = group.totalheight.number - height.number; 9047 depth.unit = height.unit; 9048 } 9049 } 9050 9051 let width = 0; 9052 if (group.width.number > 0) { 9053 width = calculateSize(group.width, style); 9054 } 9055 9056 const graphicStyle = { height: height.number + depth.number + "em" }; 9057 if (width.number > 0) { 9058 graphicStyle.width = width.number + width.unit; 9059 } 9060 if (depth.number > 0) { 9061 graphicStyle.verticalAlign = -depth.number + depth.unit; 9062 } 9063 9064 const node = new Img(group.src, group.alt, graphicStyle); 9065 node.height = height; 9066 node.depth = depth; 9067 return new mathMLTree.MathNode("mtext", [node]) 9068 } 9069 }); 9070 9071 // Horizontal spacing commands 9072 9073 9074 // TODO: \hskip and \mskip should support plus and minus in lengths 9075 9076 defineFunction({ 9077 type: "kern", 9078 names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"], 9079 props: { 9080 numArgs: 1, 9081 argTypes: ["size"], 9082 primitive: true, 9083 allowedInText: true 9084 }, 9085 handler({ parser, funcName, token }, args) { 9086 const size = assertNodeType(args[0], "size"); 9087 if (parser.settings.strict) { 9088 const mathFunction = funcName[1] === "m"; // \mkern, \mskip 9089 const muUnit = size.value.unit === "mu"; 9090 if (mathFunction) { 9091 if (!muUnit) { 9092 throw new ParseError(`LaTeX's $funcName} supports only mu units, ` + 9093 `not $size.value.unit} units`, token) 9094 } 9095 if (parser.mode !== "math") { 9096 throw new ParseError(`LaTeX's $funcName} works only in math mode`, token) 9097 } 9098 } else { 9099 // !mathFunction 9100 if (muUnit) { 9101 throw new ParseError(`LaTeX's $funcName} doesn't support mu units`, token) 9102 } 9103 } 9104 } 9105 return { 9106 type: "kern", 9107 mode: parser.mode, 9108 dimension: size.value 9109 }; 9110 }, 9111 mathmlBuilder(group, style) { 9112 const dimension = calculateSize(group.dimension, style); 9113 const ch = dimension.unit === "em" ? spaceCharacter(dimension.number) : ""; 9114 if (group.mode === "text" && ch.length > 0) { 9115 const character = new mathMLTree.TextNode(ch); 9116 return new mathMLTree.MathNode("mtext", [character]); 9117 } else { 9118 const node = new mathMLTree.MathNode("mspace"); 9119 node.setAttribute("width", dimension.number + dimension.unit); 9120 if (dimension.number < 0) { 9121 node.style.marginLeft = dimension.number + dimension.unit; 9122 } 9123 return node; 9124 } 9125 } 9126 }); 9127 9128 const spaceCharacter = function(width) { 9129 if (width >= 0.05555 && width <= 0.05556) { 9130 return "\u200a"; //   9131 } else if (width >= 0.1666 && width <= 0.1667) { 9132 return "\u2009"; //   9133 } else if (width >= 0.2222 && width <= 0.2223) { 9134 return "\u2005"; //   9135 } else if (width >= 0.2777 && width <= 0.2778) { 9136 return "\u2005\u200a"; //    9137 } else { 9138 return ""; 9139 } 9140 }; 9141 9142 // Limit valid characters to a small set, for safety. 9143 const invalidIdRegEx = /[^A-Za-z_0-9-]/g; 9144 9145 defineFunction({ 9146 type: "label", 9147 names: ["\\label"], 9148 props: { 9149 numArgs: 1, 9150 argTypes: ["raw"] 9151 }, 9152 handler({ parser }, args) { 9153 return { 9154 type: "label", 9155 mode: parser.mode, 9156 string: args[0].string.replace(invalidIdRegEx, "") 9157 }; 9158 }, 9159 mathmlBuilder(group, style) { 9160 // Return a no-width, no-ink element with an HTML id. 9161 const node = new mathMLTree.MathNode("mrow", [], ["tml-label"]); 9162 if (group.string.length > 0) { 9163 node.setLabel(group.string); 9164 } 9165 return node 9166 } 9167 }); 9168 9169 // Horizontal overlap functions 9170 9171 const textModeLap = ["\\clap", "\\llap", "\\rlap"]; 9172 9173 defineFunction({ 9174 type: "lap", 9175 names: ["\\mathllap", "\\mathrlap", "\\mathclap", "\\clap", "\\llap", "\\rlap"], 9176 props: { 9177 numArgs: 1, 9178 allowedInText: true 9179 }, 9180 handler: ({ parser, funcName, token }, args) => { 9181 if (textModeLap.includes(funcName)) { 9182 if (parser.settings.strict && parser.mode !== "text") { 9183 throw new ParseError(`{$funcName}} can be used only in text mode. 9184 Try \\math$funcName.slice(1)}`, token) 9185 } 9186 funcName = funcName.slice(1); 9187 } else { 9188 funcName = funcName.slice(5); 9189 } 9190 const body = args[0]; 9191 return { 9192 type: "lap", 9193 mode: parser.mode, 9194 alignment: funcName, 9195 body 9196 } 9197 }, 9198 mathmlBuilder: (group, style) => { 9199 // mathllap, mathrlap, mathclap 9200 let strut; 9201 if (group.alignment === "llap") { 9202 // We need an invisible strut with the same depth as the group. 9203 // We can't just read the depth, so we use \vphantom methods. 9204 const phantomInner = buildExpression(ordargument(group.body), style); 9205 const phantom = new mathMLTree.MathNode("mphantom", phantomInner); 9206 strut = new mathMLTree.MathNode("mpadded", [phantom]); 9207 strut.setAttribute("width", "0px"); 9208 } 9209 9210 const inner = buildGroup$1(group.body, style); 9211 let node; 9212 if (group.alignment === "llap") { 9213 inner.style.position = "absolute"; 9214 inner.style.right = "0"; 9215 inner.style.bottom = `0`; // If we could have read the ink depth, it would go here. 9216 node = new mathMLTree.MathNode("mpadded", [strut, inner]); 9217 } else { 9218 node = new mathMLTree.MathNode("mpadded", [inner]); 9219 } 9220 9221 if (group.alignment === "rlap") { 9222 if (group.body.body.length > 0 && group.body.body[0].type === "genfrac") { 9223 // In Firefox, a <mpadded> squashes the 3/18em padding of a child \frac. Put it back. 9224 node.setAttribute("lspace", "0.16667em"); 9225 } 9226 } else { 9227 const offset = group.alignment === "llap" ? "-1" : "-0.5"; 9228 node.setAttribute("lspace", offset + "width"); 9229 if (group.alignment === "llap") { 9230 node.style.position = "relative"; 9231 } else { 9232 node.style.display = "flex"; 9233 node.style.justifyContent = "center"; 9234 } 9235 } 9236 node.setAttribute("width", "0px"); 9237 return node 9238 } 9239 }); 9240 9241 // Switching from text mode back to math mode 9242 defineFunction({ 9243 type: "ordgroup", 9244 names: ["\\(", "$"], 9245 props: { 9246 numArgs: 0, 9247 allowedInText: true, 9248 allowedInMath: false 9249 }, 9250 handler({ funcName, parser }, args) { 9251 const outerMode = parser.mode; 9252 parser.switchMode("math"); 9253 const close = funcName === "\\(" ? "\\)" : "$"; 9254 const body = parser.parseExpression(false, close); 9255 parser.expect(close); 9256 parser.switchMode(outerMode); 9257 return { 9258 type: "ordgroup", 9259 mode: parser.mode, 9260 body 9261 }; 9262 } 9263 }); 9264 9265 // Check for extra closing math delimiters 9266 defineFunction({ 9267 type: "text", // Doesn't matter what this is. 9268 names: ["\\)", "\\]"], 9269 props: { 9270 numArgs: 0, 9271 allowedInText: true, 9272 allowedInMath: false 9273 }, 9274 handler(context, token) { 9275 throw new ParseError(`Mismatched $context.funcName}`, token); 9276 } 9277 }); 9278 9279 const chooseStyle = (group, style) => { 9280 switch (style.level) { 9281 case StyleLevel.DISPLAY: // 0 9282 return group.display; 9283 case StyleLevel.TEXT: // 1 9284 return group.text; 9285 case StyleLevel.SCRIPT: // 2 9286 return group.script; 9287 case StyleLevel.SCRIPTSCRIPT: // 3 9288 return group.scriptscript; 9289 default: 9290 return group.text; 9291 } 9292 }; 9293 9294 defineFunction({ 9295 type: "mathchoice", 9296 names: ["\\mathchoice"], 9297 props: { 9298 numArgs: 4, 9299 primitive: true 9300 }, 9301 handler: ({ parser }, args) => { 9302 return { 9303 type: "mathchoice", 9304 mode: parser.mode, 9305 display: ordargument(args[0]), 9306 text: ordargument(args[1]), 9307 script: ordargument(args[2]), 9308 scriptscript: ordargument(args[3]) 9309 }; 9310 }, 9311 mathmlBuilder: (group, style) => { 9312 const body = chooseStyle(group, style); 9313 return buildExpressionRow(body, style); 9314 } 9315 }); 9316 9317 const textAtomTypes = ["text", "textord", "mathord", "atom"]; 9318 9319 const padding = width => { 9320 const node = new mathMLTree.MathNode("mspace"); 9321 node.setAttribute("width", width + "em"); 9322 return node 9323 }; 9324 9325 function mathmlBuilder$3(group, style) { 9326 let node; 9327 const inner = buildExpression(group.body, style); 9328 9329 if (group.mclass === "minner") { 9330 node = new mathMLTree.MathNode("mpadded", inner); 9331 } else if (group.mclass === "mord") { 9332 if (group.isCharacterBox || inner[0].type === "mathord") { 9333 node = inner[0]; 9334 node.type = "mi"; 9335 if (node.children.length === 1 && node.children[0].text && node.children[0].text === "∇") { 9336 node.setAttribute("mathvariant", "normal"); 9337 } 9338 } else { 9339 node = new mathMLTree.MathNode("mi", inner); 9340 } 9341 } else { 9342 node = new mathMLTree.MathNode("mrow", inner); 9343 if (group.mustPromote) { 9344 node = inner[0]; 9345 node.type = "mo"; 9346 if (group.isCharacterBox && group.body[0].text && /[A-Za-z]/.test(group.body[0].text)) { 9347 node.setAttribute("mathvariant", "italic"); 9348 } 9349 } else { 9350 node = new mathMLTree.MathNode("mrow", inner); 9351 } 9352 9353 // Set spacing based on what is the most likely adjacent atom type. 9354 // See TeXbook p170. 9355 const doSpacing = style.level < 2; // Operator spacing is zero inside a (sub|super)script. 9356 if (node.type === "mrow") { 9357 if (doSpacing ) { 9358 if (group.mclass === "mbin") { 9359 // medium space 9360 node.children.unshift(padding(0.2222)); 9361 node.children.push(padding(0.2222)); 9362 } else if (group.mclass === "mrel") { 9363 // thickspace 9364 node.children.unshift(padding(0.2778)); 9365 node.children.push(padding(0.2778)); 9366 } else if (group.mclass === "mpunct") { 9367 node.children.push(padding(0.1667)); 9368 } else if (group.mclass === "minner") { 9369 node.children.unshift(padding(0.0556)); // 1 mu is the most likely option 9370 node.children.push(padding(0.0556)); 9371 } 9372 } 9373 } else { 9374 if (group.mclass === "mbin") { 9375 // medium space 9376 node.attributes.lspace = (doSpacing ? "0.2222em" : "0"); 9377 node.attributes.rspace = (doSpacing ? "0.2222em" : "0"); 9378 } else if (group.mclass === "mrel") { 9379 // thickspace 9380 node.attributes.lspace = (doSpacing ? "0.2778em" : "0"); 9381 node.attributes.rspace = (doSpacing ? "0.2778em" : "0"); 9382 } else if (group.mclass === "mpunct") { 9383 node.attributes.lspace = "0em"; 9384 node.attributes.rspace = (doSpacing ? "0.1667em" : "0"); 9385 } else if (group.mclass === "mopen" || group.mclass === "mclose") { 9386 node.attributes.lspace = "0em"; 9387 node.attributes.rspace = "0em"; 9388 } else if (group.mclass === "minner" && doSpacing) { 9389 node.attributes.lspace = "0.0556em"; // 1 mu is the most likely option 9390 node.attributes.width = "+0.1111em"; 9391 } 9392 } 9393 9394 if (!(group.mclass === "mopen" || group.mclass === "mclose")) { 9395 delete node.attributes.stretchy; 9396 delete node.attributes.form; 9397 } 9398 } 9399 return node; 9400 } 9401 9402 // Math class commands except \mathop 9403 defineFunction({ 9404 type: "mclass", 9405 names: [ 9406 "\\mathord", 9407 "\\mathbin", 9408 "\\mathrel", 9409 "\\mathopen", 9410 "\\mathclose", 9411 "\\mathpunct", 9412 "\\mathinner" 9413 ], 9414 props: { 9415 numArgs: 1, 9416 primitive: true 9417 }, 9418 handler({ parser, funcName }, args) { 9419 const body = args[0]; 9420 const isCharacterBox = utils.isCharacterBox(body); 9421 // We should not wrap a <mo> around a <mi> or <mord>. That would be invalid MathML. 9422 // In that case, we instead promote the text contents of the body to the parent. 9423 let mustPromote = true; 9424 const mord = { type: "mathord", text: "", mode: parser.mode }; 9425 const arr = (body.body) ? body.body : [body]; 9426 for (const arg of arr) { 9427 if (textAtomTypes.includes(arg.type)) { 9428 if (symbols[parser.mode][arg.text]) { 9429 mord.text += symbols[parser.mode][arg.text].replace; 9430 } else if (arg.text) { 9431 mord.text += arg.text; 9432 } else if (arg.body) { 9433 arg.body.map(e => { mord.text += e.text; }); 9434 } 9435 } else { 9436 mustPromote = false; 9437 break 9438 } 9439 } 9440 return { 9441 type: "mclass", 9442 mode: parser.mode, 9443 mclass: "m" + funcName.slice(5), 9444 body: ordargument(mustPromote ? mord : body), 9445 isCharacterBox, 9446 mustPromote 9447 }; 9448 }, 9449 mathmlBuilder: mathmlBuilder$3 9450 }); 9451 9452 const binrelClass = (arg) => { 9453 // \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument. 9454 // (by rendering separately and with {}s before and after, and measuring 9455 // the change in spacing). We'll do roughly the same by detecting the 9456 // atom type directly. 9457 const atom = arg.type === "ordgroup" && arg.body.length ? arg.body[0] : arg; 9458 if (atom.type === "atom" && (atom.family === "bin" || atom.family === "rel")) { 9459 return "m" + atom.family; 9460 } else { 9461 return "mord"; 9462 } 9463 }; 9464 9465 // \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord. 9466 // This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX. 9467 defineFunction({ 9468 type: "mclass", 9469 names: ["\\@binrel"], 9470 props: { 9471 numArgs: 2 9472 }, 9473 handler({ parser }, args) { 9474 return { 9475 type: "mclass", 9476 mode: parser.mode, 9477 mclass: binrelClass(args[0]), 9478 body: ordargument(args[1]), 9479 isCharacterBox: utils.isCharacterBox(args[1]) 9480 }; 9481 } 9482 }); 9483 9484 // Build a relation or stacked op by placing one symbol on top of another 9485 defineFunction({ 9486 type: "mclass", 9487 names: ["\\stackrel", "\\overset", "\\underset"], 9488 props: { 9489 numArgs: 2 9490 }, 9491 handler({ parser, funcName }, args) { 9492 const baseArg = args[1]; 9493 const shiftedArg = args[0]; 9494 9495 const baseOp = { 9496 type: "op", 9497 mode: baseArg.mode, 9498 limits: true, 9499 alwaysHandleSupSub: true, 9500 parentIsSupSub: false, 9501 symbol: false, 9502 stack: true, 9503 suppressBaseShift: funcName !== "\\stackrel", 9504 body: ordargument(baseArg) 9505 }; 9506 9507 return { 9508 type: "supsub", 9509 mode: shiftedArg.mode, 9510 base: baseOp, 9511 sup: funcName === "\\underset" ? null : shiftedArg, 9512 sub: funcName === "\\underset" ? shiftedArg : null 9513 }; 9514 }, 9515 mathmlBuilder: mathmlBuilder$3 9516 }); 9517 9518 // Helper function 9519 const buildGroup = (el, style, noneNode) => { 9520 if (!el) { return noneNode } 9521 const node = buildGroup$1(el, style); 9522 if (node.type === "mrow" && node.children.length === 0) { return noneNode } 9523 return node 9524 }; 9525 9526 defineFunction({ 9527 type: "multiscript", 9528 names: ["\\sideset", "\\pres@cript"], // See macros.js for \prescript 9529 props: { 9530 numArgs: 3 9531 }, 9532 handler({ parser, funcName, token }, args) { 9533 if (args[2].body.length === 0) { 9534 throw new ParseError(funcName + `cannot parse an empty base.`) 9535 } 9536 const base = args[2].body[0]; 9537 if (parser.settings.strict && funcName === "\\sideset" && !base.symbol) { 9538 throw new ParseError(`The base of \\sideset must be a big operator. Try \\prescript.`) 9539 } 9540 9541 if ((args[0].body.length > 0 && args[0].body[0].type !== "supsub") || 9542 (args[1].body.length > 0 && args[1].body[0].type !== "supsub")) { 9543 throw new ParseError("\\sideset can parse only subscripts and " + 9544 "superscripts in its first two arguments", token) 9545 } 9546 9547 // The prescripts and postscripts come wrapped in a supsub. 9548 const prescripts = args[0].body.length > 0 ? args[0].body[0] : null; 9549 const postscripts = args[1].body.length > 0 ? args[1].body[0] : null; 9550 9551 if (!prescripts && !postscripts) { 9552 return base 9553 } else if (!prescripts) { 9554 // It's not a multi-script. Get a \textstyle supsub. 9555 return { 9556 type: "styling", 9557 mode: parser.mode, 9558 scriptLevel: "text", 9559 body: [{ 9560 type: "supsub", 9561 mode: parser.mode, 9562 base, 9563 sup: postscripts.sup, 9564 sub: postscripts.sub 9565 }] 9566 } 9567 } else { 9568 return { 9569 type: "multiscript", 9570 mode: parser.mode, 9571 isSideset: funcName === "\\sideset", 9572 prescripts, 9573 postscripts, 9574 base 9575 } 9576 } 9577 }, 9578 mathmlBuilder(group, style) { 9579 const base = buildGroup$1(group.base, style); 9580 9581 const prescriptsNode = new mathMLTree.MathNode("mprescripts"); 9582 const noneNode = new mathMLTree.MathNode("none"); 9583 let children = []; 9584 9585 const preSub = buildGroup(group.prescripts.sub, style, noneNode); 9586 const preSup = buildGroup(group.prescripts.sup, style, noneNode); 9587 if (group.isSideset) { 9588 // This seems silly, but LaTeX does this. Firefox ignores it, which does not make me sad. 9589 preSub.setAttribute("style", "text-align: left;"); 9590 preSup.setAttribute("style", "text-align: left;"); 9591 } 9592 9593 if (group.postscripts) { 9594 const postSub = buildGroup(group.postscripts.sub, style, noneNode); 9595 const postSup = buildGroup(group.postscripts.sup, style, noneNode); 9596 children = [base, postSub, postSup, prescriptsNode, preSub, preSup]; 9597 } else { 9598 children = [base, prescriptsNode, preSub, preSup]; 9599 } 9600 9601 return new mathMLTree.MathNode("mmultiscripts", children); 9602 } 9603 }); 9604 9605 defineFunction({ 9606 type: "not", 9607 names: ["\\not"], 9608 props: { 9609 numArgs: 1, 9610 primitive: true, 9611 allowedInText: false 9612 }, 9613 handler({ parser }, args) { 9614 const isCharacterBox = utils.isCharacterBox(args[0]); 9615 let body; 9616 if (isCharacterBox) { 9617 body = ordargument(args[0]); 9618 if (body[0].text.charAt(0) === "\\") { 9619 body[0].text = symbols.math[body[0].text].replace; 9620 } 9621 // \u0338 is the Unicode Combining Long Solidus Overlay 9622 body[0].text = body[0].text.slice(0, 1) + "\u0338" + body[0].text.slice(1); 9623 } else { 9624 // When the argument is not a character box, TeX does an awkward, poorly placed overlay. 9625 // We'll do the same. 9626 const notNode = { type: "textord", mode: "math", text: "\u0338" }; 9627 const kernNode = { type: "kern", mode: "math", dimension: { number: -0.6, unit: "em" } }; 9628 body = [notNode, kernNode, args[0]]; 9629 } 9630 return { 9631 type: "not", 9632 mode: parser.mode, 9633 body, 9634 isCharacterBox 9635 }; 9636 }, 9637 mathmlBuilder(group, style) { 9638 if (group.isCharacterBox) { 9639 const inner = buildExpression(group.body, style, true); 9640 return inner[0] 9641 } else { 9642 return buildExpressionRow(group.body, style) 9643 } 9644 } 9645 }); 9646 9647 // Limits, symbols 9648 9649 // Some helpers 9650 9651 const ordAtomTypes = ["textord", "mathord", "atom"]; 9652 9653 // Most operators have a large successor symbol, but these don't. 9654 const noSuccessor = ["\\smallint"]; 9655 9656 // Math operators (e.g. \sin) need a space between these types and themselves: 9657 const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright", "font"]; 9658 9659 // NOTE: Unlike most `builders`s, this one handles not only "op", but also 9660 // "supsub" since some of them (like \int) can affect super/subscripting. 9661 9662 const setSpacing = node => { 9663 // The user wrote a \mathop{…} function. Change spacing from default to OP spacing. 9664 // The most likely spacing for an OP is a thin space per TeXbook p170. 9665 node.attributes.lspace = "0.1667em"; 9666 node.attributes.rspace = "0.1667em"; 9667 }; 9668 9669 const mathmlBuilder$2 = (group, style) => { 9670 let node; 9671 9672 if (group.symbol) { 9673 // This is a symbol. Just add the symbol. 9674 node = new MathNode("mo", [makeText(group.name, group.mode)]); 9675 if (noSuccessor.includes(group.name)) { 9676 node.setAttribute("largeop", "false"); 9677 } else { 9678 node.setAttribute("movablelimits", "false"); 9679 } 9680 if (group.fromMathOp) { setSpacing(node); } 9681 } else if (group.body) { 9682 // This is an operator with children. Add them. 9683 node = new MathNode("mo", buildExpression(group.body, style)); 9684 if (group.fromMathOp) { setSpacing(node); } 9685 } else { 9686 // This is a text operator. Add all of the characters from the operator's name. 9687 node = new MathNode("mi", [new TextNode(group.name.slice(1))]); 9688 9689 if (!group.parentIsSupSub) { 9690 // Append an invisible <mo>⁡</mo>. 9691 // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 9692 const operator = new MathNode("mo", [makeText("\u2061", "text")]); 9693 const row = [node, operator]; 9694 // Set spacing 9695 if (group.needsLeadingSpace) { 9696 const lead = new MathNode("mspace"); 9697 lead.setAttribute("width", "0.1667em"); // thin space. 9698 row.unshift(lead); 9699 } 9700 if (!group.isFollowedByDelimiter) { 9701 const trail = new MathNode("mspace"); 9702 trail.setAttribute("width", "0.1667em"); // thin space. 9703 row.push(trail); 9704 } 9705 node = new MathNode("mrow", row); 9706 } 9707 } 9708 9709 return node; 9710 }; 9711 9712 const singleCharBigOps = { 9713 "\u220F": "\\prod", 9714 "\u2210": "\\coprod", 9715 "\u2211": "\\sum", 9716 "\u22c0": "\\bigwedge", 9717 "\u22c1": "\\bigvee", 9718 "\u22c2": "\\bigcap", 9719 "\u22c3": "\\bigcup", 9720 "\u2a00": "\\bigodot", 9721 "\u2a01": "\\bigoplus", 9722 "\u2a02": "\\bigotimes", 9723 "\u2a04": "\\biguplus", 9724 "\u2a05": "\\bigsqcap", 9725 "\u2a06": "\\bigsqcup", 9726 "\u2a03": "\\bigcupdot", 9727 "\u2a07": "\\bigdoublevee", 9728 "\u2a08": "\\bigdoublewedge", 9729 "\u2a09": "\\bigtimes" 9730 }; 9731 9732 defineFunction({ 9733 type: "op", 9734 names: [ 9735 "\\coprod", 9736 "\\bigvee", 9737 "\\bigwedge", 9738 "\\biguplus", 9739 "\\bigcupplus", 9740 "\\bigcupdot", 9741 "\\bigcap", 9742 "\\bigcup", 9743 "\\bigdoublevee", 9744 "\\bigdoublewedge", 9745 "\\intop", 9746 "\\prod", 9747 "\\sum", 9748 "\\bigotimes", 9749 "\\bigoplus", 9750 "\\bigodot", 9751 "\\bigsqcap", 9752 "\\bigsqcup", 9753 "\\bigtimes", 9754 "\\smallint", 9755 "\u220F", 9756 "\u2210", 9757 "\u2211", 9758 "\u22c0", 9759 "\u22c1", 9760 "\u22c2", 9761 "\u22c3", 9762 "\u2a00", 9763 "\u2a01", 9764 "\u2a02", 9765 "\u2a04", 9766 "\u2a06" 9767 ], 9768 props: { 9769 numArgs: 0 9770 }, 9771 handler: ({ parser, funcName }, args) => { 9772 let fName = funcName; 9773 if (fName.length === 1) { 9774 fName = singleCharBigOps[fName]; 9775 } 9776 return { 9777 type: "op", 9778 mode: parser.mode, 9779 limits: true, 9780 parentIsSupSub: false, 9781 symbol: true, 9782 stack: false, // This is true for \stackrel{}, not here. 9783 name: fName 9784 }; 9785 }, 9786 mathmlBuilder: mathmlBuilder$2 9787 }); 9788 9789 // Note: calling defineFunction with a type that's already been defined only 9790 // works because the same mathmlBuilder is being used. 9791 defineFunction({ 9792 type: "op", 9793 names: ["\\mathop"], 9794 props: { 9795 numArgs: 1, 9796 primitive: true 9797 }, 9798 handler: ({ parser }, args) => { 9799 const body = args[0]; 9800 // It would be convienient to just wrap a <mo> around the argument. 9801 // But if the argument is a <mi> or <mord>, that would be invalid MathML. 9802 // In that case, we instead promote the text contents of the body to the parent. 9803 const arr = (body.body) ? body.body : [body]; 9804 const isSymbol = arr.length === 1 && ordAtomTypes.includes(arr[0].type); 9805 return { 9806 type: "op", 9807 mode: parser.mode, 9808 limits: true, 9809 parentIsSupSub: false, 9810 symbol: isSymbol, 9811 fromMathOp: true, 9812 stack: false, 9813 name: isSymbol ? arr[0].text : null, 9814 body: isSymbol ? null : ordargument(body) 9815 }; 9816 }, 9817 mathmlBuilder: mathmlBuilder$2 9818 }); 9819 9820 // There are 2 flags for operators; whether they produce limits in 9821 // displaystyle, and whether they are symbols and should grow in 9822 // displaystyle. These four groups cover the four possible choices. 9823 9824 const singleCharIntegrals = { 9825 "\u222b": "\\int", 9826 "\u222c": "\\iint", 9827 "\u222d": "\\iiint", 9828 "\u222e": "\\oint", 9829 "\u222f": "\\oiint", 9830 "\u2230": "\\oiiint", 9831 "\u2231": "\\intclockwise", 9832 "\u2232": "\\varointclockwise", 9833 "\u2a0c": "\\iiiint", 9834 "\u2a0d": "\\intbar", 9835 "\u2a0e": "\\intBar", 9836 "\u2a0f": "\\fint", 9837 "\u2a12": "\\rppolint", 9838 "\u2a13": "\\scpolint", 9839 "\u2a15": "\\pointint", 9840 "\u2a16": "\\sqint", 9841 "\u2a17": "\\intlarhk", 9842 "\u2a18": "\\intx", 9843 "\u2a19": "\\intcap", 9844 "\u2a1a": "\\intcup" 9845 }; 9846 9847 // No limits, not symbols 9848 defineFunction({ 9849 type: "op", 9850 names: [ 9851 "\\arcsin", 9852 "\\arccos", 9853 "\\arctan", 9854 "\\arctg", 9855 "\\arcctg", 9856 "\\arg", 9857 "\\ch", 9858 "\\cos", 9859 "\\cosec", 9860 "\\cosh", 9861 "\\cot", 9862 "\\cotg", 9863 "\\coth", 9864 "\\csc", 9865 "\\ctg", 9866 "\\cth", 9867 "\\deg", 9868 "\\dim", 9869 "\\exp", 9870 "\\hom", 9871 "\\ker", 9872 "\\lg", 9873 "\\ln", 9874 "\\log", 9875 "\\sec", 9876 "\\sin", 9877 "\\sinh", 9878 "\\sh", 9879 "\\sgn", 9880 "\\tan", 9881 "\\tanh", 9882 "\\tg", 9883 "\\th" 9884 ], 9885 props: { 9886 numArgs: 0 9887 }, 9888 handler({ parser, funcName }) { 9889 const prevAtomType = parser.prevAtomType; 9890 const next = parser.gullet.future().text; 9891 return { 9892 type: "op", 9893 mode: parser.mode, 9894 limits: false, 9895 parentIsSupSub: false, 9896 symbol: false, 9897 stack: false, 9898 isFollowedByDelimiter: isDelimiter(next), 9899 needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), 9900 name: funcName 9901 }; 9902 }, 9903 mathmlBuilder: mathmlBuilder$2 9904 }); 9905 9906 // Limits, not symbols 9907 defineFunction({ 9908 type: "op", 9909 names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"], 9910 props: { 9911 numArgs: 0 9912 }, 9913 handler({ parser, funcName }) { 9914 const prevAtomType = parser.prevAtomType; 9915 const next = parser.gullet.future().text; 9916 return { 9917 type: "op", 9918 mode: parser.mode, 9919 limits: true, 9920 parentIsSupSub: false, 9921 symbol: false, 9922 stack: false, 9923 isFollowedByDelimiter: isDelimiter(next), 9924 needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType), 9925 name: funcName 9926 }; 9927 }, 9928 mathmlBuilder: mathmlBuilder$2 9929 }); 9930 9931 // No limits, symbols 9932 defineFunction({ 9933 type: "op", 9934 names: [ 9935 "\\int", 9936 "\\iint", 9937 "\\iiint", 9938 "\\iiiint", 9939 "\\oint", 9940 "\\oiint", 9941 "\\oiiint", 9942 "\\intclockwise", 9943 "\\varointclockwise", 9944 "\\intbar", 9945 "\\intBar", 9946 "\\fint", 9947 "\\rppolint", 9948 "\\scpolint", 9949 "\\pointint", 9950 "\\sqint", 9951 "\\intlarhk", 9952 "\\intx", 9953 "\\intcap", 9954 "\\intcup", 9955 "\u222b", 9956 "\u222c", 9957 "\u222d", 9958 "\u222e", 9959 "\u222f", 9960 "\u2230", 9961 "\u2231", 9962 "\u2232", 9963 "\u2a0c", 9964 "\u2a0d", 9965 "\u2a0e", 9966 "\u2a0f", 9967 "\u2a12", 9968 "\u2a13", 9969 "\u2a15", 9970 "\u2a16", 9971 "\u2a17", 9972 "\u2a18", 9973 "\u2a19", 9974 "\u2a1a" 9975 ], 9976 props: { 9977 numArgs: 0 9978 }, 9979 handler({ parser, funcName }) { 9980 let fName = funcName; 9981 if (fName.length === 1) { 9982 fName = singleCharIntegrals[fName]; 9983 } 9984 return { 9985 type: "op", 9986 mode: parser.mode, 9987 limits: false, 9988 parentIsSupSub: false, 9989 symbol: true, 9990 stack: false, 9991 name: fName 9992 }; 9993 }, 9994 mathmlBuilder: mathmlBuilder$2 9995 }); 9996 9997 // NOTE: Unlike most builders, this one handles not only 9998 // "operatorname", but also "supsub" since \operatorname* can 9999 // affect super/subscripting. 10000 10001 const mathmlBuilder$1 = (group, style) => { 10002 let expression = buildExpression(group.body, style.withFont("mathrm")); 10003 10004 // Is expression a string or has it something like a fraction? 10005 let isAllString = true; // default 10006 for (let i = 0; i < expression.length; i++) { 10007 let node = expression[i]; 10008 if (node instanceof mathMLTree.MathNode) { 10009 if (node.type === "mrow" && node.children.length === 1 && 10010 node.children[0] instanceof mathMLTree.MathNode) { 10011 node = node.children[0]; 10012 } 10013 switch (node.type) { 10014 case "mi": 10015 case "mn": 10016 case "ms": 10017 case "mtext": 10018 break; // Do nothing yet. 10019 case "mspace": 10020 { 10021 if (node.attributes.width) { 10022 const width = node.attributes.width.replace("em", ""); 10023 const ch = spaceCharacter(Number(width)); 10024 if (ch === "") { 10025 isAllString = false; 10026 } else { 10027 expression[i] = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode(ch)]); 10028 } 10029 } 10030 } 10031 break 10032 case "mo": { 10033 const child = node.children[0]; 10034 if (node.children.length === 1 && child instanceof mathMLTree.TextNode) { 10035 child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); 10036 } else { 10037 isAllString = false; 10038 } 10039 break 10040 } 10041 default: 10042 isAllString = false; 10043 } 10044 } else { 10045 isAllString = false; 10046 } 10047 } 10048 10049 if (isAllString) { 10050 // Write a single TextNode instead of multiple nested tags. 10051 const word = expression.map((node) => node.toText()).join(""); 10052 expression = [new mathMLTree.TextNode(word)]; 10053 } else if ( 10054 expression.length === 1 10055 && ["mover", "munder"].includes(expression[0].type) && 10056 (expression[0].children[0].type === "mi" || expression[0].children[0].type === "mtext") 10057 ) { 10058 expression[0].children[0].type = "mi"; 10059 if (group.parentIsSupSub) { 10060 return new mathMLTree.MathNode("mrow", expression) 10061 } else { 10062 const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]); 10063 return mathMLTree.newDocumentFragment([expression[0], operator]) 10064 } 10065 } 10066 10067 let wrapper; 10068 if (isAllString) { 10069 wrapper = new mathMLTree.MathNode("mi", expression); 10070 if (expression[0].text.length === 1) { 10071 wrapper.setAttribute("mathvariant", "normal"); 10072 } 10073 } else { 10074 wrapper = new mathMLTree.MathNode("mrow", expression); 10075 } 10076 10077 if (!group.parentIsSupSub) { 10078 // Append an <mo>⁡</mo>. 10079 // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 10080 const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]); 10081 const fragment = [wrapper, operator]; 10082 if (group.needsLeadingSpace) { 10083 // LaTeX gives operator spacing, but a <mi> gets ord spacing. 10084 // So add a leading space. 10085 const space = new mathMLTree.MathNode("mspace"); 10086 space.setAttribute("width", "0.1667em"); // thin space. 10087 fragment.unshift(space); 10088 } 10089 if (!group.isFollowedByDelimiter) { 10090 const trail = new mathMLTree.MathNode("mspace"); 10091 trail.setAttribute("width", "0.1667em"); // thin space. 10092 fragment.push(trail); 10093 } 10094 return mathMLTree.newDocumentFragment(fragment) 10095 } 10096 10097 return wrapper 10098 }; 10099 10100 // \operatorname 10101 // amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@ 10102 defineFunction({ 10103 type: "operatorname", 10104 names: ["\\operatorname@", "\\operatornamewithlimits"], 10105 props: { 10106 numArgs: 1, 10107 allowedInArgument: true 10108 }, 10109 handler: ({ parser, funcName }, args) => { 10110 const body = args[0]; 10111 const prevAtomType = parser.prevAtomType; 10112 const next = parser.gullet.future().text; 10113 return { 10114 type: "operatorname", 10115 mode: parser.mode, 10116 body: ordargument(body), 10117 alwaysHandleSupSub: (funcName === "\\operatornamewithlimits"), 10118 limits: false, 10119 parentIsSupSub: false, 10120 isFollowedByDelimiter: isDelimiter(next), 10121 needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType) 10122 }; 10123 }, 10124 mathmlBuilder: mathmlBuilder$1 10125 }); 10126 10127 defineMacro("\\operatorname", 10128 "\\@ifstar\\operatornamewithlimits\\operatorname@"); 10129 10130 defineFunctionBuilders({ 10131 type: "ordgroup", 10132 mathmlBuilder(group, style) { 10133 return buildExpressionRow(group.body, style, group.semisimple); 10134 } 10135 }); 10136 10137 defineFunction({ 10138 type: "phantom", 10139 names: ["\\phantom"], 10140 props: { 10141 numArgs: 1, 10142 allowedInText: true 10143 }, 10144 handler: ({ parser }, args) => { 10145 const body = args[0]; 10146 return { 10147 type: "phantom", 10148 mode: parser.mode, 10149 body: ordargument(body) 10150 }; 10151 }, 10152 mathmlBuilder: (group, style) => { 10153 const inner = buildExpression(group.body, style); 10154 return new mathMLTree.MathNode("mphantom", inner); 10155 } 10156 }); 10157 10158 defineFunction({ 10159 type: "hphantom", 10160 names: ["\\hphantom"], 10161 props: { 10162 numArgs: 1, 10163 allowedInText: true 10164 }, 10165 handler: ({ parser }, args) => { 10166 const body = args[0]; 10167 return { 10168 type: "hphantom", 10169 mode: parser.mode, 10170 body 10171 }; 10172 }, 10173 mathmlBuilder: (group, style) => { 10174 const inner = buildExpression(ordargument(group.body), style); 10175 const phantom = new mathMLTree.MathNode("mphantom", inner); 10176 const node = new mathMLTree.MathNode("mpadded", [phantom]); 10177 node.setAttribute("height", "0px"); 10178 node.setAttribute("depth", "0px"); 10179 return node; 10180 } 10181 }); 10182 10183 defineFunction({ 10184 type: "vphantom", 10185 names: ["\\vphantom"], 10186 props: { 10187 numArgs: 1, 10188 allowedInText: true 10189 }, 10190 handler: ({ parser }, args) => { 10191 const body = args[0]; 10192 return { 10193 type: "vphantom", 10194 mode: parser.mode, 10195 body 10196 }; 10197 }, 10198 mathmlBuilder: (group, style) => { 10199 const inner = buildExpression(ordargument(group.body), style); 10200 const phantom = new mathMLTree.MathNode("mphantom", inner); 10201 const node = new mathMLTree.MathNode("mpadded", [phantom]); 10202 node.setAttribute("width", "0px"); 10203 return node; 10204 } 10205 }); 10206 10207 // In LaTeX, \pmb is a simulation of bold font. 10208 // The version of \pmb in ambsy.sty works by typesetting three copies of the argument 10209 // with small offsets. We use CSS font-weight:bold. 10210 10211 defineFunction({ 10212 type: "pmb", 10213 names: ["\\pmb"], 10214 props: { 10215 numArgs: 1, 10216 allowedInText: true 10217 }, 10218 handler({ parser }, args) { 10219 return { 10220 type: "pmb", 10221 mode: parser.mode, 10222 body: ordargument(args[0]) 10223 } 10224 }, 10225 mathmlBuilder(group, style) { 10226 const inner = buildExpression(group.body, style); 10227 // Wrap with an <mstyle> element. 10228 const node = wrapWithMstyle(inner); 10229 node.setAttribute("style", "font-weight:bold"); 10230 return node 10231 } 10232 }); 10233 10234 // \raise, \lower, and \raisebox 10235 10236 const mathmlBuilder = (group, style) => { 10237 const newStyle = style.withLevel(StyleLevel.TEXT); 10238 const node = new mathMLTree.MathNode("mpadded", [buildGroup$1(group.body, newStyle)]); 10239 const dy = calculateSize(group.dy, style); 10240 node.setAttribute("voffset", dy.number + dy.unit); 10241 // Add padding, which acts to increase height in Chromium. 10242 // TODO: Figure out some way to change height in Firefox w/o breaking Chromium. 10243 if (dy.number > 0) { 10244 node.style.padding = dy.number + dy.unit + " 0 0 0"; 10245 } else { 10246 node.style.padding = "0 0 " + Math.abs(dy.number) + dy.unit + " 0"; 10247 } 10248 return node 10249 }; 10250 10251 defineFunction({ 10252 type: "raise", 10253 names: ["\\raise", "\\lower"], 10254 props: { 10255 numArgs: 2, 10256 argTypes: ["size", "primitive"], 10257 primitive: true 10258 }, 10259 handler({ parser, funcName }, args) { 10260 const amount = assertNodeType(args[0], "size").value; 10261 if (funcName === "\\lower") { amount.number *= -1; } 10262 const body = args[1]; 10263 return { 10264 type: "raise", 10265 mode: parser.mode, 10266 dy: amount, 10267 body 10268 }; 10269 }, 10270 mathmlBuilder 10271 }); 10272 10273 10274 defineFunction({ 10275 type: "raise", 10276 names: ["\\raisebox"], 10277 props: { 10278 numArgs: 2, 10279 argTypes: ["size", "hbox"], 10280 allowedInText: true 10281 }, 10282 handler({ parser, funcName }, args) { 10283 const amount = assertNodeType(args[0], "size").value; 10284 const body = args[1]; 10285 return { 10286 type: "raise", 10287 mode: parser.mode, 10288 dy: amount, 10289 body 10290 }; 10291 }, 10292 mathmlBuilder 10293 }); 10294 10295 defineFunction({ 10296 type: "ref", 10297 names: ["\\ref", "\\eqref"], 10298 props: { 10299 numArgs: 1, 10300 argTypes: ["raw"] 10301 }, 10302 handler({ parser, funcName }, args) { 10303 return { 10304 type: "ref", 10305 mode: parser.mode, 10306 funcName, 10307 string: args[0].string.replace(invalidIdRegEx, "") 10308 }; 10309 }, 10310 mathmlBuilder(group, style) { 10311 // Create an empty <a> node. Set a class and an href attribute. 10312 // The post-processor will populate with the target's tag or equation number. 10313 const classes = group.funcName === "\\ref" ? ["tml-ref"] : ["tml-ref", "tml-eqref"]; 10314 return new AnchorNode("#" + group.string, classes, null) 10315 } 10316 }); 10317 10318 defineFunction({ 10319 type: "reflect", 10320 names: ["\\reflectbox"], 10321 props: { 10322 numArgs: 1, 10323 argTypes: ["hbox"], 10324 allowedInText: true 10325 }, 10326 handler({ parser }, args) { 10327 return { 10328 type: "reflect", 10329 mode: parser.mode, 10330 body: args[0] 10331 }; 10332 }, 10333 mathmlBuilder(group, style) { 10334 const node = buildGroup$1(group.body, style); 10335 node.style.transform = "scaleX(-1)"; 10336 return node 10337 } 10338 }); 10339 10340 defineFunction({ 10341 type: "internal", 10342 names: ["\\relax"], 10343 props: { 10344 numArgs: 0, 10345 allowedInText: true 10346 }, 10347 handler({ parser }) { 10348 return { 10349 type: "internal", 10350 mode: parser.mode 10351 }; 10352 } 10353 }); 10354 10355 defineFunction({ 10356 type: "rule", 10357 names: ["\\rule"], 10358 props: { 10359 numArgs: 2, 10360 numOptionalArgs: 1, 10361 allowedInText: true, 10362 allowedInMath: true, 10363 argTypes: ["size", "size", "size"] 10364 }, 10365 handler({ parser }, args, optArgs) { 10366 const shift = optArgs[0]; 10367 const width = assertNodeType(args[0], "size"); 10368 const height = assertNodeType(args[1], "size"); 10369 return { 10370 type: "rule", 10371 mode: parser.mode, 10372 shift: shift && assertNodeType(shift, "size").value, 10373 width: width.value, 10374 height: height.value 10375 }; 10376 }, 10377 mathmlBuilder(group, style) { 10378 const width = calculateSize(group.width, style); 10379 const height = calculateSize(group.height, style); 10380 const shift = group.shift 10381 ? calculateSize(group.shift, style) 10382 : { number: 0, unit: "em" }; 10383 const color = (style.color && style.getColor()) || "black"; 10384 10385 const rule = new mathMLTree.MathNode("mspace"); 10386 if (width.number > 0 && height.number > 0) { 10387 rule.setAttribute("mathbackground", color); 10388 } 10389 rule.setAttribute("width", width.number + width.unit); 10390 rule.setAttribute("height", height.number + height.unit); 10391 if (shift.number === 0) { return rule } 10392 10393 const wrapper = new mathMLTree.MathNode("mpadded", [rule]); 10394 if (shift.number >= 0) { 10395 wrapper.setAttribute("height", "+" + shift.number + shift.unit); 10396 } else { 10397 wrapper.setAttribute("height", shift.number + shift.unit); 10398 wrapper.setAttribute("depth", "+" + -shift.number + shift.unit); 10399 } 10400 wrapper.setAttribute("voffset", shift.number + shift.unit); 10401 return wrapper; 10402 } 10403 }); 10404 10405 // The size mappings are taken from TeX with \normalsize=10pt. 10406 // We don't have to track script level. MathML does that. 10407 const sizeMap = { 10408 "\\tiny": 0.5, 10409 "\\sixptsize": 0.6, 10410 "\\Tiny": 0.6, 10411 "\\scriptsize": 0.7, 10412 "\\footnotesize": 0.8, 10413 "\\small": 0.9, 10414 "\\normalsize": 1.0, 10415 "\\large": 1.2, 10416 "\\Large": 1.44, 10417 "\\LARGE": 1.728, 10418 "\\huge": 2.074, 10419 "\\Huge": 2.488 10420 }; 10421 10422 defineFunction({ 10423 type: "sizing", 10424 names: [ 10425 "\\tiny", 10426 "\\sixptsize", 10427 "\\Tiny", 10428 "\\scriptsize", 10429 "\\footnotesize", 10430 "\\small", 10431 "\\normalsize", 10432 "\\large", 10433 "\\Large", 10434 "\\LARGE", 10435 "\\huge", 10436 "\\Huge" 10437 ], 10438 props: { 10439 numArgs: 0, 10440 allowedInText: true 10441 }, 10442 handler: ({ breakOnTokenText, funcName, parser }, args) => { 10443 if (parser.settings.strict && parser.mode === "math") { 10444 // eslint-disable-next-line no-console 10445 console.log(`Temml strict-mode warning: Command $funcName} is invalid in math mode.`); 10446 } 10447 const body = parser.parseExpression(false, breakOnTokenText, true); 10448 return { 10449 type: "sizing", 10450 mode: parser.mode, 10451 funcName, 10452 body 10453 }; 10454 }, 10455 mathmlBuilder: (group, style) => { 10456 const newStyle = style.withFontSize(sizeMap[group.funcName]); 10457 const inner = buildExpression(group.body, newStyle); 10458 // Wrap with an <mstyle> element. 10459 const node = wrapWithMstyle(inner); 10460 const factor = (sizeMap[group.funcName] / style.fontSize).toFixed(4); 10461 node.setAttribute("mathsize", factor + "em"); 10462 return node; 10463 } 10464 }); 10465 10466 // smash, with optional [tb], as in AMS 10467 10468 defineFunction({ 10469 type: "smash", 10470 names: ["\\smash"], 10471 props: { 10472 numArgs: 1, 10473 numOptionalArgs: 1, 10474 allowedInText: true 10475 }, 10476 handler: ({ parser }, args, optArgs) => { 10477 let smashHeight = false; 10478 let smashDepth = false; 10479 const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup"); 10480 if (tbArg) { 10481 // Optional [tb] argument is engaged. 10482 // ref: amsmath: \renewcommand{\smash}[1][tb]{% 10483 // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}% 10484 let letter = ""; 10485 for (let i = 0; i < tbArg.body.length; ++i) { 10486 const node = tbArg.body[i]; 10487 // TODO: Write an AssertSymbolNode 10488 letter = node.text; 10489 if (letter === "t") { 10490 smashHeight = true; 10491 } else if (letter === "b") { 10492 smashDepth = true; 10493 } else { 10494 smashHeight = false; 10495 smashDepth = false; 10496 break; 10497 } 10498 } 10499 } else { 10500 smashHeight = true; 10501 smashDepth = true; 10502 } 10503 10504 const body = args[0]; 10505 return { 10506 type: "smash", 10507 mode: parser.mode, 10508 body, 10509 smashHeight, 10510 smashDepth 10511 }; 10512 }, 10513 mathmlBuilder: (group, style) => { 10514 const node = new mathMLTree.MathNode("mpadded", [buildGroup$1(group.body, style)]); 10515 10516 if (group.smashHeight) { 10517 node.setAttribute("height", "0px"); 10518 } 10519 10520 if (group.smashDepth) { 10521 node.setAttribute("depth", "0px"); 10522 } 10523 10524 return node; 10525 } 10526 }); 10527 10528 defineFunction({ 10529 type: "sqrt", 10530 names: ["\\sqrt"], 10531 props: { 10532 numArgs: 1, 10533 numOptionalArgs: 1 10534 }, 10535 handler({ parser }, args, optArgs) { 10536 const index = optArgs[0]; 10537 const body = args[0]; 10538 return { 10539 type: "sqrt", 10540 mode: parser.mode, 10541 body, 10542 index 10543 }; 10544 }, 10545 mathmlBuilder(group, style) { 10546 const { body, index } = group; 10547 return index 10548 ? new mathMLTree.MathNode("mroot", [ 10549 buildGroup$1(body, style), 10550 buildGroup$1(index, style.incrementLevel()) 10551 ]) 10552 : new mathMLTree.MathNode("msqrt", [buildGroup$1(body, style)]); 10553 } 10554 }); 10555 10556 const styleMap = { 10557 display: 0, 10558 text: 1, 10559 script: 2, 10560 scriptscript: 3 10561 }; 10562 10563 const styleAttributes = { 10564 display: ["0", "true"], 10565 text: ["0", "false"], 10566 script: ["1", "false"], 10567 scriptscript: ["2", "false"] 10568 }; 10569 10570 defineFunction({ 10571 type: "styling", 10572 names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"], 10573 props: { 10574 numArgs: 0, 10575 allowedInText: true, 10576 primitive: true 10577 }, 10578 handler({ breakOnTokenText, funcName, parser }, args) { 10579 // parse out the implicit body 10580 const body = parser.parseExpression(true, breakOnTokenText, true); 10581 10582 const scriptLevel = funcName.slice(1, funcName.length - 5); 10583 return { 10584 type: "styling", 10585 mode: parser.mode, 10586 // Figure out what scriptLevel to use by pulling out the scriptLevel from 10587 // the function name 10588 scriptLevel, 10589 body 10590 }; 10591 }, 10592 mathmlBuilder(group, style) { 10593 // Figure out what scriptLevel we're changing to. 10594 const newStyle = style.withLevel(styleMap[group.scriptLevel]); 10595 // The style argument in the next line does NOT directly set a MathML script level. 10596 // It just tracks the style level, in case we need to know it for supsub or mathchoice. 10597 const inner = buildExpression(group.body, newStyle); 10598 // Wrap with an <mstyle> element. 10599 const node = wrapWithMstyle(inner); 10600 10601 const attr = styleAttributes[group.scriptLevel]; 10602 10603 // Here is where we set the MathML script level. 10604 node.setAttribute("scriptlevel", attr[0]); 10605 node.setAttribute("displaystyle", attr[1]); 10606 10607 return node; 10608 } 10609 }); 10610 10611 /** 10612 * Sometimes, groups perform special rules when they have superscripts or 10613 * subscripts attached to them. This function lets the `supsub` group know that 10614 * Sometimes, groups perform special rules when they have superscripts or 10615 * its inner element should handle the superscripts and subscripts instead of 10616 * handling them itself. 10617 */ 10618 10619 // Helpers 10620 const symbolRegEx = /^m(over|under|underover)$/; 10621 10622 // Super scripts and subscripts, whose precise placement can depend on other 10623 // functions that precede them. 10624 defineFunctionBuilders({ 10625 type: "supsub", 10626 mathmlBuilder(group, style) { 10627 // Is the inner group a relevant horizonal brace? 10628 let isBrace = false; 10629 let isOver; 10630 let isSup; 10631 let appendApplyFunction = false; 10632 let appendSpace = false; 10633 let needsLeadingSpace = false; 10634 10635 if (group.base && group.base.type === "horizBrace") { 10636 isSup = !!group.sup; 10637 if (isSup === group.base.isOver) { 10638 isBrace = true; 10639 isOver = group.base.isOver; 10640 } 10641 } 10642 10643 if (group.base && !group.base.stack && 10644 (group.base.type === "op" || group.base.type === "operatorname")) { 10645 group.base.parentIsSupSub = true; 10646 appendApplyFunction = !group.base.symbol; 10647 appendSpace = appendApplyFunction && !group.isFollowedByDelimiter; 10648 needsLeadingSpace = group.base.needsLeadingSpace; 10649 } 10650 10651 const children = group.base && group.base.stack 10652 ? [buildGroup$1(group.base.body[0], style)] 10653 : [buildGroup$1(group.base, style)]; 10654 10655 // Note regarding scriptstyle level. 10656 // (Sub|super)scripts should not shrink beyond MathML scriptlevel 2 aka \scriptscriptstyle 10657 // Ref: https://w3c.github.io/mathml-core/#the-displaystyle-and-scriptlevel-attributes 10658 // (BTW, MathML scriptlevel 2 is equal to Temml level 3.) 10659 // But Chromium continues to shrink the (sub|super)scripts. So we explicitly set scriptlevel 2. 10660 10661 const childStyle = style.inSubOrSup(); 10662 if (group.sub) { 10663 const sub = buildGroup$1(group.sub, childStyle); 10664 if (style.level === 3) { sub.setAttribute("scriptlevel", "2"); } 10665 children.push(sub); 10666 } 10667 10668 if (group.sup) { 10669 const sup = buildGroup$1(group.sup, childStyle); 10670 if (style.level === 3) { sup.setAttribute("scriptlevel", "2"); } 10671 const testNode = sup.type === "mrow" ? sup.children[0] : sup; 10672 if ((testNode && testNode.type === "mo" && testNode.classes.includes("tml-prime")) 10673 && group.base && group.base.text && "fF".indexOf(group.base.text) > -1) { 10674 // Chromium does not address italic correction on prime. Prevent f′ from overlapping. 10675 testNode.classes.push("prime-pad"); 10676 } 10677 children.push(sup); 10678 } 10679 10680 let nodeType; 10681 if (isBrace) { 10682 nodeType = isOver ? "mover" : "munder"; 10683 } else if (!group.sub) { 10684 const base = group.base; 10685 if ( 10686 base && 10687 base.type === "op" && 10688 base.limits && 10689 (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) 10690 ) { 10691 nodeType = "mover"; 10692 } else if ( 10693 base && 10694 base.type === "operatorname" && 10695 base.alwaysHandleSupSub && 10696 (base.limits || style.level === StyleLevel.DISPLAY) 10697 ) { 10698 nodeType = "mover"; 10699 } else { 10700 nodeType = "msup"; 10701 } 10702 } else if (!group.sup) { 10703 const base = group.base; 10704 if ( 10705 base && 10706 base.type === "op" && 10707 base.limits && 10708 (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) 10709 ) { 10710 nodeType = "munder"; 10711 } else if ( 10712 base && 10713 base.type === "operatorname" && 10714 base.alwaysHandleSupSub && 10715 (base.limits || style.level === StyleLevel.DISPLAY) 10716 ) { 10717 nodeType = "munder"; 10718 } else { 10719 nodeType = "msub"; 10720 } 10721 } else { 10722 const base = group.base; 10723 if (base && ((base.type === "op" && base.limits) || base.type === "multiscript") && 10724 (style.level === StyleLevel.DISPLAY || base.alwaysHandleSupSub) 10725 ) { 10726 nodeType = "munderover"; 10727 } else if ( 10728 base && 10729 base.type === "operatorname" && 10730 base.alwaysHandleSupSub && 10731 (style.level === StyleLevel.DISPLAY || base.limits) 10732 ) { 10733 nodeType = "munderover"; 10734 } else { 10735 nodeType = "msubsup"; 10736 } 10737 } 10738 10739 let node = new mathMLTree.MathNode(nodeType, children); 10740 if (appendApplyFunction) { 10741 // Append an <mo>⁡</mo>. 10742 // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 10743 const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]); 10744 if (needsLeadingSpace) { 10745 const space = new mathMLTree.MathNode("mspace"); 10746 space.setAttribute("width", "0.1667em"); // thin space. 10747 node = mathMLTree.newDocumentFragment([space, node, operator]); 10748 } else { 10749 node = mathMLTree.newDocumentFragment([node, operator]); 10750 } 10751 if (appendSpace) { 10752 const space = new mathMLTree.MathNode("mspace"); 10753 space.setAttribute("width", "0.1667em"); // thin space. 10754 node.children.push(space); 10755 } 10756 } else if (symbolRegEx.test(nodeType)) { 10757 // Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits. 10758 node = new mathMLTree.MathNode("mrow", [node]); 10759 } 10760 10761 return node 10762 } 10763 }); 10764 10765 // Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js. 10766 10767 const temml_short = ["\\shortmid", "\\nshortmid", "\\shortparallel", 10768 "\\nshortparallel", "\\smallsetminus"]; 10769 10770 const arrows = ["\\Rsh", "\\Lsh", "\\restriction"]; 10771 10772 const isArrow = str => { 10773 if (str.length === 1) { 10774 const codePoint = str.codePointAt(0); 10775 return (0x218f < codePoint && codePoint < 0x2200) 10776 } 10777 return str.indexOf("arrow") > -1 || str.indexOf("harpoon") > -1 || arrows.includes(str) 10778 }; 10779 10780 defineFunctionBuilders({ 10781 type: "atom", 10782 mathmlBuilder(group, style) { 10783 const node = new mathMLTree.MathNode("mo", [makeText(group.text, group.mode)]); 10784 if (group.family === "punct") { 10785 node.setAttribute("separator", "true"); 10786 } else if (group.family === "open" || group.family === "close") { 10787 // Delims built here should not stretch vertically. 10788 // See delimsizing.js for stretchy delims. 10789 if (group.family === "open") { 10790 node.setAttribute("form", "prefix"); 10791 // Set an explicit attribute for stretch. Otherwise Firefox may do it wrong. 10792 node.setAttribute("stretchy", "false"); 10793 } else if (group.family === "close") { 10794 node.setAttribute("form", "postfix"); 10795 node.setAttribute("stretchy", "false"); 10796 } 10797 } else if (group.text === "\\mid") { 10798 // Firefox messes up this spacing if at the end of an <mrow>. See it explicitly. 10799 node.setAttribute("lspace", "0.22em"); // medium space 10800 node.setAttribute("rspace", "0.22em"); 10801 node.setAttribute("stretchy", "false"); 10802 } else if (group.family === "rel" && isArrow(group.text)) { 10803 node.setAttribute("stretchy", "false"); 10804 } else if (temml_short.includes(group.text)) { 10805 node.setAttribute("mathsize", "70%"); 10806 } else if (group.text === ":") { 10807 // ":" is not in the MathML operator dictionary. Give it BIN spacing. 10808 node.attributes.lspace = "0.2222em"; 10809 node.attributes.rspace = "0.2222em"; 10810 } 10811 return node; 10812 } 10813 }); 10814 10815 /** 10816 * Maps TeX font commands to "mathvariant" attribute in buildMathML.js 10817 */ 10818 const fontMap = { 10819 // styles 10820 mathbf: "bold", 10821 mathrm: "normal", 10822 textit: "italic", 10823 mathit: "italic", 10824 mathnormal: "italic", 10825 10826 // families 10827 mathbb: "double-struck", 10828 mathcal: "script", 10829 mathfrak: "fraktur", 10830 mathscr: "script", 10831 mathsf: "sans-serif", 10832 mathtt: "monospace" 10833 }; 10834 10835 /** 10836 * Returns the math variant as a string or null if none is required. 10837 */ 10838 const getVariant = function(group, style) { 10839 // Handle font specifiers as best we can. 10840 // Chromium does not support the MathML mathvariant attribute. 10841 // So we'll use Unicode replacement characters instead. 10842 // But first, determine the math variant. 10843 10844 // Deal with the \textit, \textbf, etc., functions. 10845 if (style.fontFamily === "texttt") { 10846 return "monospace" 10847 } else if (style.fontFamily === "textsc") { 10848 return "normal"; // handled via character substitution in symbolsOrd.js. 10849 } else if (style.fontFamily === "textsf") { 10850 if (style.fontShape === "textit" && style.fontWeight === "textbf") { 10851 return "sans-serif-bold-italic" 10852 } else if (style.fontShape === "textit") { 10853 return "sans-serif-italic" 10854 } else if (style.fontWeight === "textbf") { 10855 return "sans-serif-bold" 10856 } else { 10857 return "sans-serif" 10858 } 10859 } else if (style.fontShape === "textit" && style.fontWeight === "textbf") { 10860 return "bold-italic" 10861 } else if (style.fontShape === "textit") { 10862 return "italic" 10863 } else if (style.fontWeight === "textbf") { 10864 return "bold" 10865 } 10866 10867 // Deal with the \mathit, mathbf, etc, functions. 10868 const font = style.font; 10869 if (!font || font === "mathnormal") { 10870 return null 10871 } 10872 10873 const mode = group.mode; 10874 switch (font) { 10875 case "mathit": 10876 return "italic" 10877 case "mathrm": { 10878 const codePoint = group.text.codePointAt(0); 10879 // LaTeX \mathrm returns italic for Greek characters. 10880 return (0x03ab < codePoint && codePoint < 0x03cf) ? "italic" : "normal" 10881 } 10882 case "greekItalic": 10883 return "italic" 10884 case "up@greek": 10885 return "normal" 10886 case "boldsymbol": 10887 case "mathboldsymbol": 10888 return "bold-italic" 10889 case "mathbf": 10890 return "bold" 10891 case "mathbb": 10892 return "double-struck" 10893 case "mathfrak": 10894 return "fraktur" 10895 case "mathscr": 10896 case "mathcal": 10897 return "script" 10898 case "mathsf": 10899 return "sans-serif" 10900 case "mathsfit": 10901 return "sans-serif-italic" 10902 case "mathtt": 10903 return "monospace" 10904 } 10905 10906 let text = group.text; 10907 if (symbols[mode][text] && symbols[mode][text].replace) { 10908 text = symbols[mode][text].replace; 10909 } 10910 10911 return Object.prototype.hasOwnProperty.call(fontMap, font) ? fontMap[font] : null 10912 }; 10913 10914 // Chromium does not support the MathML `mathvariant` attribute. 10915 // Instead, we replace ASCII characters with Unicode characters that 10916 // are defined in the font as bold, italic, double-struck, etc. 10917 // This module identifies those Unicode code points. 10918 10919 // First, a few helpers. 10920 const script = Object.freeze({ 10921 B: 0x20EA, // Offset from ASCII B to Unicode script B 10922 E: 0x20EB, 10923 F: 0x20EB, 10924 H: 0x20C3, 10925 I: 0x20C7, 10926 L: 0x20C6, 10927 M: 0x20E6, 10928 R: 0x20C9, 10929 e: 0x20CA, 10930 g: 0x20A3, 10931 o: 0x20C5 10932 }); 10933 10934 const frak = Object.freeze({ 10935 C: 0x20EA, 10936 H: 0x20C4, 10937 I: 0x20C8, 10938 R: 0x20CA, 10939 Z: 0x20CE 10940 }); 10941 10942 const bbb = Object.freeze({ 10943 C: 0x20BF, // blackboard bold 10944 H: 0x20C5, 10945 N: 0x20C7, 10946 P: 0x20C9, 10947 Q: 0x20C9, 10948 R: 0x20CB, 10949 Z: 0x20CA 10950 }); 10951 10952 const bold = Object.freeze({ 10953 "\u03f5": 0x1D2E7, // lunate epsilon 10954 "\u03d1": 0x1D30C, // vartheta 10955 "\u03f0": 0x1D2EE, // varkappa 10956 "\u03c6": 0x1D319, // varphi 10957 "\u03f1": 0x1D2EF, // varrho 10958 "\u03d6": 0x1D30B // varpi 10959 }); 10960 10961 const boldItalic = Object.freeze({ 10962 "\u03f5": 0x1D35B, // lunate epsilon 10963 "\u03d1": 0x1D380, // vartheta 10964 "\u03f0": 0x1D362, // varkappa 10965 "\u03c6": 0x1D38D, // varphi 10966 "\u03f1": 0x1D363, // varrho 10967 "\u03d6": 0x1D37F // varpi 10968 }); 10969 10970 const boldsf = Object.freeze({ 10971 "\u03f5": 0x1D395, // lunate epsilon 10972 "\u03d1": 0x1D3BA, // vartheta 10973 "\u03f0": 0x1D39C, // varkappa 10974 "\u03c6": 0x1D3C7, // varphi 10975 "\u03f1": 0x1D39D, // varrho 10976 "\u03d6": 0x1D3B9 // varpi 10977 }); 10978 10979 const bisf = Object.freeze({ 10980 "\u03f5": 0x1D3CF, // lunate epsilon 10981 "\u03d1": 0x1D3F4, // vartheta 10982 "\u03f0": 0x1D3D6, // varkappa 10983 "\u03c6": 0x1D401, // varphi 10984 "\u03f1": 0x1D3D7, // varrho 10985 "\u03d6": 0x1D3F3 // varpi 10986 }); 10987 10988 // Code point offsets below are derived from https://www.unicode.org/charts/PDF/U1D400.pdf 10989 const offset = Object.freeze({ 10990 upperCaseLatin: { // A-Z 10991 "normal": ch => { return 0 }, 10992 "bold": ch => { return 0x1D3BF }, 10993 "italic": ch => { return 0x1D3F3 }, 10994 "bold-italic": ch => { return 0x1D427 }, 10995 "script": ch => { return script[ch] || 0x1D45B }, 10996 "script-bold": ch => { return 0x1D48F }, 10997 "fraktur": ch => { return frak[ch] || 0x1D4C3 }, 10998 "fraktur-bold": ch => { return 0x1D52B }, 10999 "double-struck": ch => { return bbb[ch] || 0x1D4F7 }, 11000 "sans-serif": ch => { return 0x1D55F }, 11001 "sans-serif-bold": ch => { return 0x1D593 }, 11002 "sans-serif-italic": ch => { return 0x1D5C7 }, 11003 "sans-serif-bold-italic": ch => { return 0x1D63C }, 11004 "monospace": ch => { return 0x1D62F } 11005 }, 11006 lowerCaseLatin: { // a-z 11007 "normal": ch => { return 0 }, 11008 "bold": ch => { return 0x1D3B9 }, 11009 "italic": ch => { return ch === "h" ? 0x20A6 : 0x1D3ED }, 11010 "bold-italic": ch => { return 0x1D421 }, 11011 "script": ch => { return script[ch] || 0x1D455 }, 11012 "script-bold": ch => { return 0x1D489 }, 11013 "fraktur": ch => { return 0x1D4BD }, 11014 "fraktur-bold": ch => { return 0x1D525 }, 11015 "double-struck": ch => { return 0x1D4F1 }, 11016 "sans-serif": ch => { return 0x1D559 }, 11017 "sans-serif-bold": ch => { return 0x1D58D }, 11018 "sans-serif-italic": ch => { return 0x1D5C1 }, 11019 "sans-serif-bold-italic": ch => { return 0x1D5F5 }, 11020 "monospace": ch => { return 0x1D629 } 11021 }, 11022 upperCaseGreek: { // A-Ω 11023 "normal": ch => { return 0 }, 11024 "bold": ch => { return 0x1D317 }, 11025 "italic": ch => { return 0x1D351 }, 11026 // \boldsymbol actually returns upright bold for upperCaseGreek 11027 "bold-italic": ch => { return 0x1D317 }, 11028 "script": ch => { return 0 }, 11029 "script-bold": ch => { return 0 }, 11030 "fraktur": ch => { return 0 }, 11031 "fraktur-bold": ch => { return 0 }, 11032 "double-struck": ch => { return 0 }, 11033 // Unicode has no code points for regular-weight san-serif Greek. Use bold. 11034 "sans-serif": ch => { return 0x1D3C5 }, 11035 "sans-serif-bold": ch => { return 0x1D3C5 }, 11036 "sans-serif-italic": ch => { return 0 }, 11037 "sans-serif-bold-italic": ch => { return 0x1D3FF }, 11038 "monospace": ch => { return 0 } 11039 }, 11040 lowerCaseGreek: { // α-ω 11041 "normal": ch => { return 0 }, 11042 "bold": ch => { return 0x1D311 }, 11043 "italic": ch => { return 0x1D34B }, 11044 "bold-italic": ch => { return ch === "\u03d5" ? 0x1D37E : 0x1D385 }, 11045 "script": ch => { return 0 }, 11046 "script-bold": ch => { return 0 }, 11047 "fraktur": ch => { return 0 }, 11048 "fraktur-bold": ch => { return 0 }, 11049 "double-struck": ch => { return 0 }, 11050 // Unicode has no code points for regular-weight san-serif Greek. Use bold. 11051 "sans-serif": ch => { return 0x1D3BF }, 11052 "sans-serif-bold": ch => { return 0x1D3BF }, 11053 "sans-serif-italic": ch => { return 0 }, 11054 "sans-serif-bold-italic": ch => { return 0x1D3F9 }, 11055 "monospace": ch => { return 0 } 11056 }, 11057 varGreek: { // \varGamma, etc 11058 "normal": ch => { return 0 }, 11059 "bold": ch => { return bold[ch] || -51 }, 11060 "italic": ch => { return 0 }, 11061 "bold-italic": ch => { return boldItalic[ch] || 0x3A }, 11062 "script": ch => { return 0 }, 11063 "script-bold": ch => { return 0 }, 11064 "fraktur": ch => { return 0 }, 11065 "fraktur-bold": ch => { return 0 }, 11066 "double-struck": ch => { return 0 }, 11067 "sans-serif": ch => { return boldsf[ch] || 0x74 }, 11068 "sans-serif-bold": ch => { return boldsf[ch] || 0x74 }, 11069 "sans-serif-italic": ch => { return 0 }, 11070 "sans-serif-bold-italic": ch => { return bisf[ch] || 0xAE }, 11071 "monospace": ch => { return 0 } 11072 }, 11073 numeral: { // 0-9 11074 "normal": ch => { return 0 }, 11075 "bold": ch => { return 0x1D79E }, 11076 "italic": ch => { return 0 }, 11077 "bold-italic": ch => { return 0 }, 11078 "script": ch => { return 0 }, 11079 "script-bold": ch => { return 0 }, 11080 "fraktur": ch => { return 0 }, 11081 "fraktur-bold": ch => { return 0 }, 11082 "double-struck": ch => { return 0x1D7A8 }, 11083 "sans-serif": ch => { return 0x1D7B2 }, 11084 "sans-serif-bold": ch => { return 0x1D7BC }, 11085 "sans-serif-italic": ch => { return 0 }, 11086 "sans-serif-bold-italic": ch => { return 0 }, 11087 "monospace": ch => { return 0x1D7C6 } 11088 } 11089 }); 11090 11091 const variantChar = (ch, variant) => { 11092 const codePoint = ch.codePointAt(0); 11093 const block = 0x40 < codePoint && codePoint < 0x5b 11094 ? "upperCaseLatin" 11095 : 0x60 < codePoint && codePoint < 0x7b 11096 ? "lowerCaseLatin" 11097 : (0x390 < codePoint && codePoint < 0x3AA) 11098 ? "upperCaseGreek" 11099 : 0x3B0 < codePoint && codePoint < 0x3CA || ch === "\u03d5" 11100 ? "lowerCaseGreek" 11101 : 0x1D6E1 < codePoint && codePoint < 0x1D6FC || bold[ch] 11102 ? "varGreek" 11103 : (0x2F < codePoint && codePoint < 0x3A) 11104 ? "numeral" 11105 : "other"; 11106 return block === "other" 11107 ? ch 11108 : String.fromCodePoint(codePoint + offset[block][variant](ch)) 11109 }; 11110 11111 const smallCaps = Object.freeze({ 11112 a: "ᴀ", 11113 b: "ʙ", 11114 c: "ᴄ", 11115 d: "ᴅ", 11116 e: "ᴇ", 11117 f: "ꜰ", 11118 g: "ɢ", 11119 h: "ʜ", 11120 i: "ɪ", 11121 j: "ᴊ", 11122 k: "ᴋ", 11123 l: "ʟ", 11124 m: "ᴍ", 11125 n: "ɴ", 11126 o: "ᴏ", 11127 p: "ᴘ", 11128 q: "ǫ", 11129 r: "ʀ", 11130 s: "s", 11131 t: "ᴛ", 11132 u: "ᴜ", 11133 v: "ᴠ", 11134 w: "ᴡ", 11135 x: "x", 11136 y: "ʏ", 11137 z: "ᴢ" 11138 }); 11139 11140 // "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in 11141 // src/symbols.js. 11142 11143 const numberRegEx = /^\d(?:[\d,.]*\d)?$/; 11144 const latinRegEx = /[A-Ba-z]/; 11145 const primes = new Set(["\\prime", "\\dprime", "\\trprime", "\\qprime", 11146 "\\backprime", "\\backdprime", "\\backtrprime"]); 11147 11148 const italicNumber = (text, variant, tag) => { 11149 const mn = new mathMLTree.MathNode(tag, [text]); 11150 const wrapper = new mathMLTree.MathNode("mstyle", [mn]); 11151 wrapper.style["font-style"] = "italic"; 11152 wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif"; 11153 if (variant === "bold-italic") { wrapper.style["font-weight"] = "bold"; } 11154 return wrapper 11155 }; 11156 11157 defineFunctionBuilders({ 11158 type: "mathord", 11159 mathmlBuilder(group, style) { 11160 const text = makeText(group.text, group.mode, style); 11161 const codePoint = text.text.codePointAt(0); 11162 // Test for upper-case Greek 11163 const defaultVariant = (0x0390 < codePoint && codePoint < 0x03aa) ? "normal" : "italic"; 11164 const variant = getVariant(group, style) || defaultVariant; 11165 if (variant === "script") { 11166 text.text = variantChar(text.text, variant); 11167 return new mathMLTree.MathNode("mi", [text], [style.font]) 11168 } else if (variant !== "italic") { 11169 text.text = variantChar(text.text, variant); 11170 } 11171 let node = new mathMLTree.MathNode("mi", [text]); 11172 // TODO: Handle U+1D49C - U+1D4CF per https://www.unicode.org/charts/PDF/U1D400.pdf 11173 if (variant === "normal") { 11174 node.setAttribute("mathvariant", "normal"); 11175 if (text.text.length === 1) { 11176 // A Firefox bug will apply spacing here, but there should be none. Fix it. 11177 node = new mathMLTree.MathNode("mrow", [node]); 11178 } 11179 } 11180 return node 11181 } 11182 }); 11183 11184 defineFunctionBuilders({ 11185 type: "textord", 11186 mathmlBuilder(group, style) { 11187 let ch = group.text; 11188 const codePoint = ch.codePointAt(0); 11189 if (style.fontFamily === "textsc") { 11190 // Convert small latin letters to small caps. 11191 if (96 < codePoint && codePoint < 123) { 11192 ch = smallCaps[ch]; 11193 } 11194 } 11195 const text = makeText(ch, group.mode, style); 11196 const variant = getVariant(group, style) || "normal"; 11197 11198 let node; 11199 if (numberRegEx.test(group.text)) { 11200 const tag = group.mode === "text" ? "mtext" : "mn"; 11201 if (variant === "italic" || variant === "bold-italic") { 11202 return italicNumber(text, variant, tag) 11203 } else { 11204 if (variant !== "normal") { 11205 text.text = text.text.split("").map(c => variantChar(c, variant)).join(""); 11206 } 11207 node = new mathMLTree.MathNode(tag, [text]); 11208 } 11209 } else if (group.mode === "text") { 11210 if (variant !== "normal") { 11211 text.text = variantChar(text.text, variant); 11212 } 11213 node = new mathMLTree.MathNode("mtext", [text]); 11214 } else if (primes.has(group.text)) { 11215 node = new mathMLTree.MathNode("mo", [text]); 11216 // TODO: If/when Chromium uses ssty variant for prime, remove the next line. 11217 node.classes.push("tml-prime"); 11218 } else { 11219 const origText = text.text; 11220 if (variant !== "italic") { 11221 text.text = variantChar(text.text, variant); 11222 } 11223 node = new mathMLTree.MathNode("mi", [text]); 11224 if (text.text === origText && latinRegEx.test(origText)) { 11225 node.setAttribute("mathvariant", "italic"); 11226 } 11227 } 11228 return node 11229 } 11230 }); 11231 11232 // A map of CSS-based spacing functions to their CSS class. 11233 const cssSpace = { 11234 "\\nobreak": "nobreak", 11235 "\\allowbreak": "allowbreak" 11236 }; 11237 11238 // A lookup table to determine whether a spacing function/symbol should be 11239 // treated like a regular space character. If a symbol or command is a key 11240 // in this table, then it should be a regular space character. Furthermore, 11241 // the associated value may have a `className` specifying an extra CSS class 11242 // to add to the created `span`. 11243 const regularSpace = { 11244 " ": {}, 11245 "\\ ": {}, 11246 "~": { 11247 className: "nobreak" 11248 }, 11249 "\\space": {}, 11250 "\\nobreakspace": { 11251 className: "nobreak" 11252 } 11253 }; 11254 11255 // ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in 11256 // src/symbols.js. 11257 defineFunctionBuilders({ 11258 type: "spacing", 11259 mathmlBuilder(group, style) { 11260 let node; 11261 11262 if (Object.prototype.hasOwnProperty.call(regularSpace, group.text)) { 11263 // Firefox does not render a space in a <mtext> </mtext>. So write a no-break space. 11264 // TODO: If Firefox fixes that bug, uncomment the next line and write ch into the node. 11265 //const ch = (regularSpace[group.text].className === "nobreak") ? "\u00a0" : " " 11266 node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("\u00a0")]); 11267 } else if (Object.prototype.hasOwnProperty.call(cssSpace, group.text)) { 11268 // MathML 3.0 calls for nobreak to occur in an <mo>, not an <mtext> 11269 // Ref: https://www.w3.org/Math/draft-spec/mathml.html#chapter3_presm.lbattrs 11270 node = new mathMLTree.MathNode("mo"); 11271 if (group.text === "\\nobreak") { 11272 node.setAttribute("linebreak", "nobreak"); 11273 } 11274 } else { 11275 throw new ParseError(`Unknown type of space "$group.text}"`) 11276 } 11277 11278 return node 11279 } 11280 }); 11281 11282 defineFunctionBuilders({ 11283 type: "tag" 11284 }); 11285 11286 // For a \tag, the work usually done in a mathmlBuilder is instead done in buildMathML.js. 11287 // That way, a \tag can be pulled out of the parse tree and wrapped around the outer node. 11288 11289 // Non-mathy text, possibly in a font 11290 const textFontFamilies = { 11291 "\\text": undefined, 11292 "\\textrm": "textrm", 11293 "\\textsf": "textsf", 11294 "\\texttt": "texttt", 11295 "\\textnormal": "textrm", 11296 "\\textsc": "textsc" // small caps 11297 }; 11298 11299 const textFontWeights = { 11300 "\\textbf": "textbf", 11301 "\\textmd": "textmd" 11302 }; 11303 11304 const textFontShapes = { 11305 "\\textit": "textit", 11306 "\\textup": "textup" 11307 }; 11308 11309 const styleWithFont = (group, style) => { 11310 const font = group.font; 11311 // Checks if the argument is a font family or a font style. 11312 if (!font) { 11313 return style; 11314 } else if (textFontFamilies[font]) { 11315 return style.withTextFontFamily(textFontFamilies[font]); 11316 } else if (textFontWeights[font]) { 11317 return style.withTextFontWeight(textFontWeights[font]); 11318 } else if (font === "\\emph") { 11319 return style.fontShape === "textit" 11320 ? style.withTextFontShape("textup") 11321 : style.withTextFontShape("textit") 11322 } 11323 return style.withTextFontShape(textFontShapes[font]) 11324 }; 11325 11326 defineFunction({ 11327 type: "text", 11328 names: [ 11329 // Font families 11330 "\\text", 11331 "\\textrm", 11332 "\\textsf", 11333 "\\texttt", 11334 "\\textnormal", 11335 "\\textsc", 11336 // Font weights 11337 "\\textbf", 11338 "\\textmd", 11339 // Font Shapes 11340 "\\textit", 11341 "\\textup", 11342 "\\emph" 11343 ], 11344 props: { 11345 numArgs: 1, 11346 argTypes: ["text"], 11347 allowedInArgument: true, 11348 allowedInText: true 11349 }, 11350 handler({ parser, funcName }, args) { 11351 const body = args[0]; 11352 return { 11353 type: "text", 11354 mode: parser.mode, 11355 body: ordargument(body), 11356 font: funcName 11357 }; 11358 }, 11359 mathmlBuilder(group, style) { 11360 const newStyle = styleWithFont(group, style); 11361 const mrow = buildExpressionRow(group.body, newStyle); 11362 return consolidateText(mrow) 11363 } 11364 }); 11365 11366 // \vcenter: Vertically center the argument group on the math axis. 11367 11368 defineFunction({ 11369 type: "vcenter", 11370 names: ["\\vcenter"], 11371 props: { 11372 numArgs: 1, 11373 argTypes: ["original"], 11374 allowedInText: false 11375 }, 11376 handler({ parser }, args) { 11377 return { 11378 type: "vcenter", 11379 mode: parser.mode, 11380 body: args[0] 11381 }; 11382 }, 11383 mathmlBuilder(group, style) { 11384 // Use a math table to create vertically centered content. 11385 const mtd = new mathMLTree.MathNode("mtd", [buildGroup$1(group.body, style)]); 11386 mtd.style.padding = "0"; 11387 const mtr = new mathMLTree.MathNode("mtr", [mtd]); 11388 return new mathMLTree.MathNode("mtable", [mtr]) 11389 } 11390 }); 11391 11392 defineFunction({ 11393 type: "verb", 11394 names: ["\\verb"], 11395 props: { 11396 numArgs: 0, 11397 allowedInText: true 11398 }, 11399 handler(context, args, optArgs) { 11400 // \verb and \verb* are dealt with directly in Parser.js. 11401 // If we end up here, it's because of a failure to match the two delimiters 11402 // in the regex in Lexer.js. LaTeX raises the following error when \verb is 11403 // terminated by end of line (or file). 11404 throw new ParseError("\\verb ended by end of line instead of matching delimiter"); 11405 }, 11406 mathmlBuilder(group, style) { 11407 const text = new mathMLTree.TextNode(makeVerb(group)); 11408 const node = new mathMLTree.MathNode("mtext", [text]); 11409 node.setAttribute("mathvariant", "monospace"); 11410 return node; 11411 } 11412 }); 11413 11414 /** 11415 * Converts verb group into body string. 11416 * 11417 * \verb* replaces each space with an open box \u2423 11418 * \verb replaces each space with a no-break space \xA0 11419 */ 11420 const makeVerb = (group) => group.body.replace(/ /g, group.star ? "\u2423" : "\xA0"); 11421 11422 /** Include this to ensure that all functions are defined. */ 11423 11424 const functions = _functions; 11425 11426 /** 11427 * The Lexer class handles tokenizing the input in various ways. Since our 11428 * parser expects us to be able to backtrack, the lexer allows lexing from any 11429 * given starting point. 11430 * 11431 * Its main exposed function is the `lex` function, which takes a position to 11432 * lex from and a type of token to lex. It defers to the appropriate `_innerLex` 11433 * function. 11434 * 11435 * The various `_innerLex` functions perform the actual lexing of different 11436 * kinds. 11437 */ 11438 11439 11440 /* The following tokenRegex 11441 * - matches typical whitespace (but not NBSP etc.) using its first two groups 11442 * - does not match any control character \x00-\x1f except whitespace 11443 * - does not match a bare backslash 11444 * - matches any ASCII character except those just mentioned 11445 * - does not match the BMP private use area \uE000-\uF8FF 11446 * - does not match bare surrogate code units 11447 * - matches any BMP character except for those just described 11448 * - matches any valid Unicode surrogate pair 11449 * - mathches numerals 11450 * - matches a backslash followed by one or more whitespace characters 11451 * - matches a backslash followed by one or more letters then whitespace 11452 * - matches a backslash followed by any BMP character 11453 * Capturing groups: 11454 * [1] regular whitespace 11455 * [2] backslash followed by whitespace 11456 * [3] anything else, which may include: 11457 * [4] left character of \verb* 11458 * [5] left character of \verb 11459 * [6] backslash followed by word, excluding any trailing whitespace 11460 * Just because the Lexer matches something doesn't mean it's valid input: 11461 * If there is no matching function or symbol definition, the Parser will 11462 * still reject the input. 11463 */ 11464 const spaceRegexString = "[ \r\n\t]"; 11465 const controlWordRegexString = "\\\\[a-zA-Z@]+"; 11466 const controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]"; 11467 const controlWordWhitespaceRegexString = `($controlWordRegexString})$spaceRegexString}*`; 11468 const controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*"; 11469 const combiningDiacriticalMarkString = "[\u0300-\u036f]"; 11470 const combiningDiacriticalMarksEndRegex = new RegExp(`$combiningDiacriticalMarkString}+$`); 11471 const tokenRegexString = 11472 `($spaceRegexString}+)|` + // whitespace 11473 `$controlSpaceRegexString}|` + // whitespace 11474 "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint 11475 `$combiningDiacriticalMarkString}*` + // ...plus accents 11476 "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair 11477 `$combiningDiacriticalMarkString}*` + // ...plus accents 11478 "|\\\\verb\\*([^]).*?\\4" + // \verb* 11479 "|\\\\verb([^*a-zA-Z]).*?\\5" + // \verb unstarred 11480 `|$controlWordWhitespaceRegexString}` + // \macroName + spaces 11481 `|$controlSymbolRegexString})`; // \\, \', etc. 11482 11483 /** Main Lexer class */ 11484 class Lexer { 11485 constructor(input, settings) { 11486 // Separate accents from characters 11487 this.input = input; 11488 this.settings = settings; 11489 this.tokenRegex = new RegExp(tokenRegexString, 'g'); 11490 // Category codes. The lexer only supports comment characters (14) for now. 11491 // MacroExpander additionally distinguishes active (13). 11492 this.catcodes = { 11493 "%": 14, // comment character 11494 "~": 13 // active character 11495 }; 11496 } 11497 11498 setCatcode(char, code) { 11499 this.catcodes[char] = code; 11500 } 11501 11502 /** 11503 * This function lexes a single token. 11504 */ 11505 lex() { 11506 const input = this.input; 11507 const pos = this.tokenRegex.lastIndex; 11508 if (pos === input.length) { 11509 return new Token("EOF", new SourceLocation(this, pos, pos)); 11510 } 11511 const match = this.tokenRegex.exec(input); 11512 if (match === null || match.index !== pos) { 11513 throw new ParseError( 11514 `Unexpected character: '$input[pos]}'`, 11515 new Token(input[pos], new SourceLocation(this, pos, pos + 1)) 11516 ); 11517 } 11518 const text = match[6] || match[3] || (match[2] ? "\\ " : " "); 11519 11520 if (this.catcodes[text] === 14) { 11521 // comment character 11522 const nlIndex = input.indexOf("\n", this.tokenRegex.lastIndex); 11523 if (nlIndex === -1) { 11524 this.tokenRegex.lastIndex = input.length; // EOF 11525 if (this.settings.strict) { 11526 throw new ParseError("% comment has no terminating newline; LaTeX would " + 11527 "fail because of commenting the end of math mode") 11528 } 11529 } else { 11530 this.tokenRegex.lastIndex = nlIndex + 1; 11531 } 11532 return this.lex(); 11533 } 11534 11535 return new Token(text, new SourceLocation(this, pos, this.tokenRegex.lastIndex)); 11536 } 11537 } 11538 11539 /** 11540 * A `Namespace` refers to a space of nameable things like macros or lengths, 11541 * which can be `set` either globally or local to a nested group, using an 11542 * undo stack similar to how TeX implements this functionality. 11543 * Performance-wise, `get` and local `set` take constant time, while global 11544 * `set` takes time proportional to the depth of group nesting. 11545 */ 11546 11547 11548 class Namespace { 11549 /** 11550 * Both arguments are optional. The first argument is an object of 11551 * built-in mappings which never change. The second argument is an object 11552 * of initial (global-level) mappings, which will constantly change 11553 * according to any global/top-level `set`s done. 11554 */ 11555 constructor(builtins = {}, globalMacros = {}) { 11556 this.current = globalMacros; 11557 this.builtins = builtins; 11558 this.undefStack = []; 11559 } 11560 11561 /** 11562 * Start a new nested group, affecting future local `set`s. 11563 */ 11564 beginGroup() { 11565 this.undefStack.push({}); 11566 } 11567 11568 /** 11569 * End current nested group, restoring values before the group began. 11570 */ 11571 endGroup() { 11572 if (this.undefStack.length === 0) { 11573 throw new ParseError( 11574 "Unbalanced namespace destruction: attempt " + 11575 "to pop global namespace; please report this as a bug" 11576 ); 11577 } 11578 const undefs = this.undefStack.pop(); 11579 for (const undef in undefs) { 11580 if (Object.prototype.hasOwnProperty.call(undefs, undef )) { 11581 if (undefs[undef] === undefined) { 11582 delete this.current[undef]; 11583 } else { 11584 this.current[undef] = undefs[undef]; 11585 } 11586 } 11587 } 11588 } 11589 11590 /** 11591 * Detect whether `name` has a definition. Equivalent to 11592 * `get(name) != null`. 11593 */ 11594 has(name) { 11595 return Object.prototype.hasOwnProperty.call(this.current, name ) || 11596 Object.prototype.hasOwnProperty.call(this.builtins, name ); 11597 } 11598 11599 /** 11600 * Get the current value of a name, or `undefined` if there is no value. 11601 * 11602 * Note: Do not use `if (namespace.get(...))` to detect whether a macro 11603 * is defined, as the definition may be the empty string which evaluates 11604 * to `false` in JavaScript. Use `if (namespace.get(...) != null)` or 11605 * `if (namespace.has(...))`. 11606 */ 11607 get(name) { 11608 if (Object.prototype.hasOwnProperty.call(this.current, name )) { 11609 return this.current[name]; 11610 } else { 11611 return this.builtins[name]; 11612 } 11613 } 11614 11615 /** 11616 * Set the current value of a name, and optionally set it globally too. 11617 * Local set() sets the current value and (when appropriate) adds an undo 11618 * operation to the undo stack. Global set() may change the undo 11619 * operation at every level, so takes time linear in their number. 11620 */ 11621 set(name, value, global = false) { 11622 if (global) { 11623 // Global set is equivalent to setting in all groups. Simulate this 11624 // by destroying any undos currently scheduled for this name, 11625 // and adding an undo with the *new* value (in case it later gets 11626 // locally reset within this environment). 11627 for (let i = 0; i < this.undefStack.length; i++) { 11628 delete this.undefStack[i][name]; 11629 } 11630 if (this.undefStack.length > 0) { 11631 this.undefStack[this.undefStack.length - 1][name] = value; 11632 } 11633 } else { 11634 // Undo this set at end of this group (possibly to `undefined`), 11635 // unless an undo is already in place, in which case that older 11636 // value is the correct one. 11637 const top = this.undefStack[this.undefStack.length - 1]; 11638 if (top && !Object.prototype.hasOwnProperty.call(top, name )) { 11639 top[name] = this.current[name]; 11640 } 11641 } 11642 this.current[name] = value; 11643 } 11644 } 11645 11646 /** 11647 * This file contains the “gullet” where macros are expanded 11648 * until only non-macro tokens remain. 11649 */ 11650 11651 11652 // List of commands that act like macros but aren't defined as a macro, 11653 // function, or symbol. Used in `isDefined`. 11654 const implicitCommands = { 11655 "^": true, // Parser.js 11656 _: true, // Parser.js 11657 "\\limits": true, // Parser.js 11658 "\\nolimits": true // Parser.js 11659 }; 11660 11661 class MacroExpander { 11662 constructor(input, settings, mode) { 11663 this.settings = settings; 11664 this.expansionCount = 0; 11665 this.feed(input); 11666 // Make new global namespace 11667 this.macros = new Namespace(macros, settings.macros); 11668 this.mode = mode; 11669 this.stack = []; // contains tokens in REVERSE order 11670 } 11671 11672 /** 11673 * Feed a new input string to the same MacroExpander 11674 * (with existing macros etc.). 11675 */ 11676 feed(input) { 11677 this.lexer = new Lexer(input, this.settings); 11678 } 11679 11680 /** 11681 * Switches between "text" and "math" modes. 11682 */ 11683 switchMode(newMode) { 11684 this.mode = newMode; 11685 } 11686 11687 /** 11688 * Start a new group nesting within all namespaces. 11689 */ 11690 beginGroup() { 11691 this.macros.beginGroup(); 11692 } 11693 11694 /** 11695 * End current group nesting within all namespaces. 11696 */ 11697 endGroup() { 11698 this.macros.endGroup(); 11699 } 11700 11701 /** 11702 * Returns the topmost token on the stack, without expanding it. 11703 * Similar in behavior to TeX's `\futurelet`. 11704 */ 11705 future() { 11706 if (this.stack.length === 0) { 11707 this.pushToken(this.lexer.lex()); 11708 } 11709 return this.stack[this.stack.length - 1] 11710 } 11711 11712 /** 11713 * Remove and return the next unexpanded token. 11714 */ 11715 popToken() { 11716 this.future(); // ensure non-empty stack 11717 return this.stack.pop(); 11718 } 11719 11720 /** 11721 * Add a given token to the token stack. In particular, this get be used 11722 * to put back a token returned from one of the other methods. 11723 */ 11724 pushToken(token) { 11725 this.stack.push(token); 11726 } 11727 11728 /** 11729 * Append an array of tokens to the token stack. 11730 */ 11731 pushTokens(tokens) { 11732 this.stack.push(...tokens); 11733 } 11734 11735 /** 11736 * Find an macro argument without expanding tokens and append the array of 11737 * tokens to the token stack. Uses Token as a container for the result. 11738 */ 11739 scanArgument(isOptional) { 11740 let start; 11741 let end; 11742 let tokens; 11743 if (isOptional) { 11744 this.consumeSpaces(); // \@ifnextchar gobbles any space following it 11745 if (this.future().text !== "[") { 11746 return null; 11747 } 11748 start = this.popToken(); // don't include [ in tokens 11749 ({ tokens, end } = this.consumeArg(["]"])); 11750 } else { 11751 ({ tokens, start, end } = this.consumeArg()); 11752 } 11753 11754 // indicate the end of an argument 11755 this.pushToken(new Token("EOF", end.loc)); 11756 11757 this.pushTokens(tokens); 11758 return start.range(end, ""); 11759 } 11760 11761 /** 11762 * Consume all following space tokens, without expansion. 11763 */ 11764 consumeSpaces() { 11765 for (;;) { 11766 const token = this.future(); 11767 if (token.text === " ") { 11768 this.stack.pop(); 11769 } else { 11770 break; 11771 } 11772 } 11773 } 11774 11775 /** 11776 * Consume an argument from the token stream, and return the resulting array 11777 * of tokens and start/end token. 11778 */ 11779 consumeArg(delims) { 11780 // The argument for a delimited parameter is the shortest (possibly 11781 // empty) sequence of tokens with properly nested {...} groups that is 11782 // followed ... by this particular list of non-parameter tokens. 11783 // The argument for an undelimited parameter is the next nonblank 11784 // token, unless that token is ‘{’, when the argument will be the 11785 // entire {...} group that follows. 11786 const tokens = []; 11787 const isDelimited = delims && delims.length > 0; 11788 if (!isDelimited) { 11789 // Ignore spaces between arguments. As the TeXbook says: 11790 // "After you have said ‘\def\row#1#2{...}’, you are allowed to 11791 // put spaces between the arguments (e.g., ‘\row x n’), because 11792 // TeX doesn’t use single spaces as undelimited arguments." 11793 this.consumeSpaces(); 11794 } 11795 const start = this.future(); 11796 let tok; 11797 let depth = 0; 11798 let match = 0; 11799 do { 11800 tok = this.popToken(); 11801 tokens.push(tok); 11802 if (tok.text === "{") { 11803 ++depth; 11804 } else if (tok.text === "}") { 11805 --depth; 11806 if (depth === -1) { 11807 throw new ParseError("Extra }", tok); 11808 } 11809 } else if (tok.text === "EOF") { 11810 throw new ParseError( 11811 "Unexpected end of input in a macro argument" + 11812 ", expected '" + 11813 (delims && isDelimited ? delims[match] : "}") + 11814 "'", 11815 tok 11816 ); 11817 } 11818 if (delims && isDelimited) { 11819 if ((depth === 0 || (depth === 1 && delims[match] === "{")) && tok.text === delims[match]) { 11820 ++match; 11821 if (match === delims.length) { 11822 // don't include delims in tokens 11823 tokens.splice(-match, match); 11824 break; 11825 } 11826 } else { 11827 match = 0; 11828 } 11829 } 11830 } while (depth !== 0 || isDelimited); 11831 // If the argument found ... has the form ‘{<nested tokens>}’, 11832 // ... the outermost braces enclosing the argument are removed 11833 if (start.text === "{" && tokens[tokens.length - 1].text === "}") { 11834 tokens.pop(); 11835 tokens.shift(); 11836 } 11837 tokens.reverse(); // to fit in with stack order 11838 return { tokens, start, end: tok }; 11839 } 11840 11841 /** 11842 * Consume the specified number of (delimited) arguments from the token 11843 * stream and return the resulting array of arguments. 11844 */ 11845 consumeArgs(numArgs, delimiters) { 11846 if (delimiters) { 11847 if (delimiters.length !== numArgs + 1) { 11848 throw new ParseError("The length of delimiters doesn't match the number of args!"); 11849 } 11850 const delims = delimiters[0]; 11851 for (let i = 0; i < delims.length; i++) { 11852 const tok = this.popToken(); 11853 if (delims[i] !== tok.text) { 11854 throw new ParseError("Use of the macro doesn't match its definition", tok); 11855 } 11856 } 11857 } 11858 11859 const args = []; 11860 for (let i = 0; i < numArgs; i++) { 11861 args.push(this.consumeArg(delimiters && delimiters[i + 1]).tokens); 11862 } 11863 return args; 11864 } 11865 11866 /** 11867 * Expand the next token only once if possible. 11868 * 11869 * If the token is expanded, the resulting tokens will be pushed onto 11870 * the stack in reverse order, and the number of such tokens will be 11871 * returned. This number might be zero or positive. 11872 * 11873 * If not, the return value is `false`, and the next token remains at the 11874 * top of the stack. 11875 * 11876 * In either case, the next token will be on the top of the stack, 11877 * or the stack will be empty (in case of empty expansion 11878 * and no other tokens). 11879 * 11880 * Used to implement `expandAfterFuture` and `expandNextToken`. 11881 * 11882 * If expandableOnly, only expandable tokens are expanded and 11883 * an undefined control sequence results in an error. 11884 */ 11885 expandOnce(expandableOnly) { 11886 const topToken = this.popToken(); 11887 const name = topToken.text; 11888 const expansion = !topToken.noexpand ? this._getExpansion(name) : null; 11889 if (expansion == null || (expandableOnly && expansion.unexpandable)) { 11890 if (expandableOnly && expansion == null && name[0] === "\\" && !this.isDefined(name)) { 11891 throw new ParseError("Undefined control sequence: " + name); 11892 } 11893 this.pushToken(topToken); 11894 return false; 11895 } 11896 this.expansionCount++; 11897 if (this.expansionCount > this.settings.maxExpand) { 11898 throw new ParseError( 11899 "Too many expansions: infinite loop or " + "need to increase maxExpand setting" 11900 ); 11901 } 11902 let tokens = expansion.tokens; 11903 const args = this.consumeArgs(expansion.numArgs, expansion.delimiters); 11904 if (expansion.numArgs) { 11905 // paste arguments in place of the placeholders 11906 tokens = tokens.slice(); // make a shallow copy 11907 for (let i = tokens.length - 1; i >= 0; --i) { 11908 let tok = tokens[i]; 11909 if (tok.text === "#") { 11910 if (i === 0) { 11911 throw new ParseError("Incomplete placeholder at end of macro body", tok); 11912 } 11913 tok = tokens[--i]; // next token on stack 11914 if (tok.text === "#") { 11915 // ## → # 11916 tokens.splice(i + 1, 1); // drop first # 11917 } else if (/^[1-9]$/.test(tok.text)) { 11918 // replace the placeholder with the indicated argument 11919 tokens.splice(i, 2, ...args[+tok.text - 1]); 11920 } else { 11921 throw new ParseError("Not a valid argument number", tok); 11922 } 11923 } 11924 } 11925 } 11926 // Concatenate expansion onto top of stack. 11927 this.pushTokens(tokens); 11928 return tokens.length; 11929 } 11930 11931 /** 11932 * Expand the next token only once (if possible), and return the resulting 11933 * top token on the stack (without removing anything from the stack). 11934 * Similar in behavior to TeX's `\expandafter\futurelet`. 11935 * Equivalent to expandOnce() followed by future(). 11936 */ 11937 expandAfterFuture() { 11938 this.expandOnce(); 11939 return this.future(); 11940 } 11941 11942 /** 11943 * Recursively expand first token, then return first non-expandable token. 11944 */ 11945 expandNextToken() { 11946 for (;;) { 11947 if (this.expandOnce() === false) { // fully expanded 11948 const token = this.stack.pop(); 11949 // The token after \noexpand is interpreted as if its meaning were ‘\relax’ 11950 if (token.treatAsRelax) { 11951 token.text = "\\relax"; 11952 } 11953 return token 11954 } 11955 } 11956 11957 // This pathway is impossible. 11958 throw new Error(); // eslint-disable-line no-unreachable 11959 } 11960 11961 /** 11962 * Fully expand the given macro name and return the resulting list of 11963 * tokens, or return `undefined` if no such macro is defined. 11964 */ 11965 expandMacro(name) { 11966 return this.macros.has(name) ? this.expandTokens([new Token(name)]) : undefined; 11967 } 11968 11969 /** 11970 * Fully expand the given token stream and return the resulting list of 11971 * tokens. Note that the input tokens are in reverse order, but the 11972 * output tokens are in forward order. 11973 */ 11974 expandTokens(tokens) { 11975 const output = []; 11976 const oldStackLength = this.stack.length; 11977 this.pushTokens(tokens); 11978 while (this.stack.length > oldStackLength) { 11979 // Expand only expandable tokens 11980 if (this.expandOnce(true) === false) { // fully expanded 11981 const token = this.stack.pop(); 11982 if (token.treatAsRelax) { 11983 // the expansion of \noexpand is the token itself 11984 token.noexpand = false; 11985 token.treatAsRelax = false; 11986 } 11987 output.push(token); 11988 } 11989 } 11990 return output; 11991 } 11992 11993 /** 11994 * Fully expand the given macro name and return the result as a string, 11995 * or return `undefined` if no such macro is defined. 11996 */ 11997 expandMacroAsText(name) { 11998 const tokens = this.expandMacro(name); 11999 if (tokens) { 12000 return tokens.map((token) => token.text).join(""); 12001 } else { 12002 return tokens; 12003 } 12004 } 12005 12006 /** 12007 * Returns the expanded macro as a reversed array of tokens and a macro 12008 * argument count. Or returns `null` if no such macro. 12009 */ 12010 _getExpansion(name) { 12011 const definition = this.macros.get(name); 12012 if (definition == null) { 12013 // mainly checking for undefined here 12014 return definition; 12015 } 12016 // If a single character has an associated catcode other than 13 12017 // (active character), then don't expand it. 12018 if (name.length === 1) { 12019 const catcode = this.lexer.catcodes[name]; 12020 if (catcode != null && catcode !== 13) { 12021 return 12022 } 12023 } 12024 const expansion = typeof definition === "function" ? definition(this) : definition; 12025 if (typeof expansion === "string") { 12026 let numArgs = 0; 12027 if (expansion.indexOf("#") !== -1) { 12028 const stripped = expansion.replace(/##/g, ""); 12029 while (stripped.indexOf("#" + (numArgs + 1)) !== -1) { 12030 ++numArgs; 12031 } 12032 } 12033 const bodyLexer = new Lexer(expansion, this.settings); 12034 const tokens = []; 12035 let tok = bodyLexer.lex(); 12036 while (tok.text !== "EOF") { 12037 tokens.push(tok); 12038 tok = bodyLexer.lex(); 12039 } 12040 tokens.reverse(); // to fit in with stack using push and pop 12041 const expanded = { tokens, numArgs }; 12042 return expanded; 12043 } 12044 12045 return expansion; 12046 } 12047 12048 /** 12049 * Determine whether a command is currently "defined" (has some 12050 * functionality), meaning that it's a macro (in the current group), 12051 * a function, a symbol, or one of the special commands listed in 12052 * `implicitCommands`. 12053 */ 12054 isDefined(name) { 12055 return ( 12056 this.macros.has(name) || 12057 Object.prototype.hasOwnProperty.call(functions, name ) || 12058 Object.prototype.hasOwnProperty.call(symbols.math, name ) || 12059 Object.prototype.hasOwnProperty.call(symbols.text, name ) || 12060 Object.prototype.hasOwnProperty.call(implicitCommands, name ) 12061 ); 12062 } 12063 12064 /** 12065 * Determine whether a command is expandable. 12066 */ 12067 isExpandable(name) { 12068 const macro = this.macros.get(name); 12069 return macro != null 12070 ? typeof macro === "string" || typeof macro === "function" || !macro.unexpandable 12071 : Object.prototype.hasOwnProperty.call(functions, name ) && !functions[name].primitive; 12072 } 12073 } 12074 12075 // Helpers for Parser.js handling of Unicode (sub|super)script characters. 12076 12077 const unicodeSubRegEx = /^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/; 12078 12079 const uSubsAndSups = Object.freeze({ 12080 '₊': '+', 12081 '₋': '-', 12082 '₌': '=', 12083 '₍': '(', 12084 '₎': ')', 12085 '₀': '0', 12086 '₁': '1', 12087 '₂': '2', 12088 '₃': '3', 12089 '₄': '4', 12090 '₅': '5', 12091 '₆': '6', 12092 '₇': '7', 12093 '₈': '8', 12094 '₉': '9', 12095 '\u2090': 'a', 12096 '\u2091': 'e', 12097 '\u2095': 'h', 12098 '\u1D62': 'i', 12099 '\u2C7C': 'j', 12100 '\u2096': 'k', 12101 '\u2097': 'l', 12102 '\u2098': 'm', 12103 '\u2099': 'n', 12104 '\u2092': 'o', 12105 '\u209A': 'p', 12106 '\u1D63': 'r', 12107 '\u209B': 's', 12108 '\u209C': 't', 12109 '\u1D64': 'u', 12110 '\u1D65': 'v', 12111 '\u2093': 'x', 12112 '\u1D66': 'β', 12113 '\u1D67': 'γ', 12114 '\u1D68': 'ρ', 12115 '\u1D69': '\u03d5', 12116 '\u1D6A': 'χ', 12117 '⁺': '+', 12118 '⁻': '-', 12119 '⁼': '=', 12120 '⁽': '(', 12121 '⁾': ')', 12122 '⁰': '0', 12123 '¹': '1', 12124 '²': '2', 12125 '³': '3', 12126 '⁴': '4', 12127 '⁵': '5', 12128 '⁶': '6', 12129 '⁷': '7', 12130 '⁸': '8', 12131 '⁹': '9', 12132 '\u1D2C': 'A', 12133 '\u1D2E': 'B', 12134 '\u1D30': 'D', 12135 '\u1D31': 'E', 12136 '\u1D33': 'G', 12137 '\u1D34': 'H', 12138 '\u1D35': 'I', 12139 '\u1D36': 'J', 12140 '\u1D37': 'K', 12141 '\u1D38': 'L', 12142 '\u1D39': 'M', 12143 '\u1D3A': 'N', 12144 '\u1D3C': 'O', 12145 '\u1D3E': 'P', 12146 '\u1D3F': 'R', 12147 '\u1D40': 'T', 12148 '\u1D41': 'U', 12149 '\u2C7D': 'V', 12150 '\u1D42': 'W', 12151 '\u1D43': 'a', 12152 '\u1D47': 'b', 12153 '\u1D9C': 'c', 12154 '\u1D48': 'd', 12155 '\u1D49': 'e', 12156 '\u1DA0': 'f', 12157 '\u1D4D': 'g', 12158 '\u02B0': 'h', 12159 '\u2071': 'i', 12160 '\u02B2': 'j', 12161 '\u1D4F': 'k', 12162 '\u02E1': 'l', 12163 '\u1D50': 'm', 12164 '\u207F': 'n', 12165 '\u1D52': 'o', 12166 '\u1D56': 'p', 12167 '\u02B3': 'r', 12168 '\u02E2': 's', 12169 '\u1D57': 't', 12170 '\u1D58': 'u', 12171 '\u1D5B': 'v', 12172 '\u02B7': 'w', 12173 '\u02E3': 'x', 12174 '\u02B8': 'y', 12175 '\u1DBB': 'z', 12176 '\u1D5D': 'β', 12177 '\u1D5E': 'γ', 12178 '\u1D5F': 'δ', 12179 '\u1D60': '\u03d5', 12180 '\u1D61': 'χ', 12181 '\u1DBF': 'θ' 12182 }); 12183 12184 // Used for Unicode input of calligraphic and script letters 12185 const asciiFromScript = Object.freeze({ 12186 "\ud835\udc9c": "A", 12187 "\u212c": "B", 12188 "\ud835\udc9e": "C", 12189 "\ud835\udc9f": "D", 12190 "\u2130": "E", 12191 "\u2131": "F", 12192 "\ud835\udca2": "G", 12193 "\u210B": "H", 12194 "\u2110": "I", 12195 "\ud835\udca5": "J", 12196 "\ud835\udca6": "K", 12197 "\u2112": "L", 12198 "\u2133": "M", 12199 "\ud835\udca9": "N", 12200 "\ud835\udcaa": "O", 12201 "\ud835\udcab": "P", 12202 "\ud835\udcac": "Q", 12203 "\u211B": "R", 12204 "\ud835\udcae": "S", 12205 "\ud835\udcaf": "T", 12206 "\ud835\udcb0": "U", 12207 "\ud835\udcb1": "V", 12208 "\ud835\udcb2": "W", 12209 "\ud835\udcb3": "X", 12210 "\ud835\udcb4": "Y", 12211 "\ud835\udcb5": "Z" 12212 }); 12213 12214 // Mapping of Unicode accent characters to their LaTeX equivalent in text and 12215 // math mode (when they exist). 12216 var unicodeAccents = { 12217 "\u0301": { text: "\\'", math: "\\acute" }, 12218 "\u0300": { text: "\\`", math: "\\grave" }, 12219 "\u0308": { text: '\\"', math: "\\ddot" }, 12220 "\u0303": { text: "\\~", math: "\\tilde" }, 12221 "\u0304": { text: "\\=", math: "\\bar" }, 12222 "\u0306": { text: "\\u", math: "\\breve" }, 12223 "\u030c": { text: "\\v", math: "\\check" }, 12224 "\u0302": { text: "\\^", math: "\\hat" }, 12225 "\u0307": { text: "\\.", math: "\\dot" }, 12226 "\u030a": { text: "\\r", math: "\\mathring" }, 12227 "\u030b": { text: "\\H" }, 12228 '\u0327': { text: '\\c' } 12229 }; 12230 12231 var unicodeSymbols = { 12232 "á": "á", 12233 "à": "à", 12234 "ä": "ä", 12235 "ǟ": "ǟ", 12236 "ã": "ã", 12237 "ā": "ā", 12238 "ă": "ă", 12239 "ắ": "ắ", 12240 "ằ": "ằ", 12241 "ẵ": "ẵ", 12242 "ǎ": "ǎ", 12243 "â": "â", 12244 "ấ": "ấ", 12245 "ầ": "ầ", 12246 "ẫ": "ẫ", 12247 "ȧ": "ȧ", 12248 "ǡ": "ǡ", 12249 "å": "å", 12250 "ǻ": "ǻ", 12251 "ḃ": "ḃ", 12252 "ć": "ć", 12253 "č": "č", 12254 "ĉ": "ĉ", 12255 "ċ": "ċ", 12256 "ď": "ď", 12257 "ḋ": "ḋ", 12258 "é": "é", 12259 "è": "è", 12260 "ë": "ë", 12261 "ẽ": "ẽ", 12262 "ē": "ē", 12263 "ḗ": "ḗ", 12264 "ḕ": "ḕ", 12265 "ĕ": "ĕ", 12266 "ě": "ě", 12267 "ê": "ê", 12268 "ế": "ế", 12269 "ề": "ề", 12270 "ễ": "ễ", 12271 "ė": "ė", 12272 "ḟ": "ḟ", 12273 "ǵ": "ǵ", 12274 "ḡ": "ḡ", 12275 "ğ": "ğ", 12276 "ǧ": "ǧ", 12277 "ĝ": "ĝ", 12278 "ġ": "ġ", 12279 "ḧ": "ḧ", 12280 "ȟ": "ȟ", 12281 "ĥ": "ĥ", 12282 "ḣ": "ḣ", 12283 "í": "í", 12284 "ì": "ì", 12285 "ï": "ï", 12286 "ḯ": "ḯ", 12287 "ĩ": "ĩ", 12288 "ī": "ī", 12289 "ĭ": "ĭ", 12290 "ǐ": "ǐ", 12291 "î": "î", 12292 "ǰ": "ǰ", 12293 "ĵ": "ĵ", 12294 "ḱ": "ḱ", 12295 "ǩ": "ǩ", 12296 "ĺ": "ĺ", 12297 "ľ": "ľ", 12298 "ḿ": "ḿ", 12299 "ṁ": "ṁ", 12300 "ń": "ń", 12301 "ǹ": "ǹ", 12302 "ñ": "ñ", 12303 "ň": "ň", 12304 "ṅ": "ṅ", 12305 "ó": "ó", 12306 "ò": "ò", 12307 "ö": "ö", 12308 "ȫ": "ȫ", 12309 "õ": "õ", 12310 "ṍ": "ṍ", 12311 "ṏ": "ṏ", 12312 "ȭ": "ȭ", 12313 "ō": "ō", 12314 "ṓ": "ṓ", 12315 "ṑ": "ṑ", 12316 "ŏ": "ŏ", 12317 "ǒ": "ǒ", 12318 "ô": "ô", 12319 "ố": "ố", 12320 "ồ": "ồ", 12321 "ỗ": "ỗ", 12322 "ȯ": "ȯ", 12323 "ȱ": "ȱ", 12324 "ő": "ő", 12325 "ṕ": "ṕ", 12326 "ṗ": "ṗ", 12327 "ŕ": "ŕ", 12328 "ř": "ř", 12329 "ṙ": "ṙ", 12330 "ś": "ś", 12331 "ṥ": "ṥ", 12332 "š": "š", 12333 "ṧ": "ṧ", 12334 "ŝ": "ŝ", 12335 "ṡ": "ṡ", 12336 "ẗ": "ẗ", 12337 "ť": "ť", 12338 "ṫ": "ṫ", 12339 "ú": "ú", 12340 "ù": "ù", 12341 "ü": "ü", 12342 "ǘ": "ǘ", 12343 "ǜ": "ǜ", 12344 "ǖ": "ǖ", 12345 "ǚ": "ǚ", 12346 "ũ": "ũ", 12347 "ṹ": "ṹ", 12348 "ū": "ū", 12349 "ṻ": "ṻ", 12350 "ŭ": "ŭ", 12351 "ǔ": "ǔ", 12352 "û": "û", 12353 "ů": "ů", 12354 "ű": "ű", 12355 "ṽ": "ṽ", 12356 "ẃ": "ẃ", 12357 "ẁ": "ẁ", 12358 "ẅ": "ẅ", 12359 "ŵ": "ŵ", 12360 "ẇ": "ẇ", 12361 "ẘ": "ẘ", 12362 "ẍ": "ẍ", 12363 "ẋ": "ẋ", 12364 "ý": "ý", 12365 "ỳ": "ỳ", 12366 "ÿ": "ÿ", 12367 "ỹ": "ỹ", 12368 "ȳ": "ȳ", 12369 "ŷ": "ŷ", 12370 "ẏ": "ẏ", 12371 "ẙ": "ẙ", 12372 "ź": "ź", 12373 "ž": "ž", 12374 "ẑ": "ẑ", 12375 "ż": "ż", 12376 "Á": "Á", 12377 "À": "À", 12378 "Ä": "Ä", 12379 "Ǟ": "Ǟ", 12380 "Ã": "Ã", 12381 "Ā": "Ā", 12382 "Ă": "Ă", 12383 "Ắ": "Ắ", 12384 "Ằ": "Ằ", 12385 "Ẵ": "Ẵ", 12386 "Ǎ": "Ǎ", 12387 "Â": "Â", 12388 "Ấ": "Ấ", 12389 "Ầ": "Ầ", 12390 "Ẫ": "Ẫ", 12391 "Ȧ": "Ȧ", 12392 "Ǡ": "Ǡ", 12393 "Å": "Å", 12394 "Ǻ": "Ǻ", 12395 "Ḃ": "Ḃ", 12396 "Ć": "Ć", 12397 "Č": "Č", 12398 "Ĉ": "Ĉ", 12399 "Ċ": "Ċ", 12400 "Ď": "Ď", 12401 "Ḋ": "Ḋ", 12402 "É": "É", 12403 "È": "È", 12404 "Ë": "Ë", 12405 "Ẽ": "Ẽ", 12406 "Ē": "Ē", 12407 "Ḗ": "Ḗ", 12408 "Ḕ": "Ḕ", 12409 "Ĕ": "Ĕ", 12410 "Ě": "Ě", 12411 "Ê": "Ê", 12412 "Ế": "Ế", 12413 "Ề": "Ề", 12414 "Ễ": "Ễ", 12415 "Ė": "Ė", 12416 "Ḟ": "Ḟ", 12417 "Ǵ": "Ǵ", 12418 "Ḡ": "Ḡ", 12419 "Ğ": "Ğ", 12420 "Ǧ": "Ǧ", 12421 "Ĝ": "Ĝ", 12422 "Ġ": "Ġ", 12423 "Ḧ": "Ḧ", 12424 "Ȟ": "Ȟ", 12425 "Ĥ": "Ĥ", 12426 "Ḣ": "Ḣ", 12427 "Í": "Í", 12428 "Ì": "Ì", 12429 "Ï": "Ï", 12430 "Ḯ": "Ḯ", 12431 "Ĩ": "Ĩ", 12432 "Ī": "Ī", 12433 "Ĭ": "Ĭ", 12434 "Ǐ": "Ǐ", 12435 "Î": "Î", 12436 "İ": "İ", 12437 "Ĵ": "Ĵ", 12438 "Ḱ": "Ḱ", 12439 "Ǩ": "Ǩ", 12440 "Ĺ": "Ĺ", 12441 "Ľ": "Ľ", 12442 "Ḿ": "Ḿ", 12443 "Ṁ": "Ṁ", 12444 "Ń": "Ń", 12445 "Ǹ": "Ǹ", 12446 "Ñ": "Ñ", 12447 "Ň": "Ň", 12448 "Ṅ": "Ṅ", 12449 "Ó": "Ó", 12450 "Ò": "Ò", 12451 "Ö": "Ö", 12452 "Ȫ": "Ȫ", 12453 "Õ": "Õ", 12454 "Ṍ": "Ṍ", 12455 "Ṏ": "Ṏ", 12456 "Ȭ": "Ȭ", 12457 "Ō": "Ō", 12458 "Ṓ": "Ṓ", 12459 "Ṑ": "Ṑ", 12460 "Ŏ": "Ŏ", 12461 "Ǒ": "Ǒ", 12462 "Ô": "Ô", 12463 "Ố": "Ố", 12464 "Ồ": "Ồ", 12465 "Ỗ": "Ỗ", 12466 "Ȯ": "Ȯ", 12467 "Ȱ": "Ȱ", 12468 "Ő": "Ő", 12469 "Ṕ": "Ṕ", 12470 "Ṗ": "Ṗ", 12471 "Ŕ": "Ŕ", 12472 "Ř": "Ř", 12473 "Ṙ": "Ṙ", 12474 "Ś": "Ś", 12475 "Ṥ": "Ṥ", 12476 "Š": "Š", 12477 "Ṧ": "Ṧ", 12478 "Ŝ": "Ŝ", 12479 "Ṡ": "Ṡ", 12480 "Ť": "Ť", 12481 "Ṫ": "Ṫ", 12482 "Ú": "Ú", 12483 "Ù": "Ù", 12484 "Ü": "Ü", 12485 "Ǘ": "Ǘ", 12486 "Ǜ": "Ǜ", 12487 "Ǖ": "Ǖ", 12488 "Ǚ": "Ǚ", 12489 "Ũ": "Ũ", 12490 "Ṹ": "Ṹ", 12491 "Ū": "Ū", 12492 "Ṻ": "Ṻ", 12493 "Ŭ": "Ŭ", 12494 "Ǔ": "Ǔ", 12495 "Û": "Û", 12496 "Ů": "Ů", 12497 "Ű": "Ű", 12498 "Ṽ": "Ṽ", 12499 "Ẃ": "Ẃ", 12500 "Ẁ": "Ẁ", 12501 "Ẅ": "Ẅ", 12502 "Ŵ": "Ŵ", 12503 "Ẇ": "Ẇ", 12504 "Ẍ": "Ẍ", 12505 "Ẋ": "Ẋ", 12506 "Ý": "Ý", 12507 "Ỳ": "Ỳ", 12508 "Ÿ": "Ÿ", 12509 "Ỹ": "Ỹ", 12510 "Ȳ": "Ȳ", 12511 "Ŷ": "Ŷ", 12512 "Ẏ": "Ẏ", 12513 "Ź": "Ź", 12514 "Ž": "Ž", 12515 "Ẑ": "Ẑ", 12516 "Ż": "Ż", 12517 "ά": "ά", 12518 "ὰ": "ὰ", 12519 "ᾱ": "ᾱ", 12520 "ᾰ": "ᾰ", 12521 "έ": "έ", 12522 "ὲ": "ὲ", 12523 "ή": "ή", 12524 "ὴ": "ὴ", 12525 "ί": "ί", 12526 "ὶ": "ὶ", 12527 "ϊ": "ϊ", 12528 "ΐ": "ΐ", 12529 "ῒ": "ῒ", 12530 "ῑ": "ῑ", 12531 "ῐ": "ῐ", 12532 "ό": "ό", 12533 "ὸ": "ὸ", 12534 "ύ": "ύ", 12535 "ὺ": "ὺ", 12536 "ϋ": "ϋ", 12537 "ΰ": "ΰ", 12538 "ῢ": "ῢ", 12539 "ῡ": "ῡ", 12540 "ῠ": "ῠ", 12541 "ώ": "ώ", 12542 "ὼ": "ὼ", 12543 "Ύ": "Ύ", 12544 "Ὺ": "Ὺ", 12545 "Ϋ": "Ϋ", 12546 "Ῡ": "Ῡ", 12547 "Ῠ": "Ῠ", 12548 "Ώ": "Ώ", 12549 "Ὼ": "Ὼ" 12550 }; 12551 12552 /* eslint no-constant-condition:0 */ 12553 12554 const binLeftCancellers = ["bin", "op", "open", "punct", "rel"]; 12555 const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/; 12556 12557 /** 12558 * This file contains the parser used to parse out a TeX expression from the 12559 * input. Since TeX isn't context-free, standard parsers don't work particularly 12560 * well. 12561 * 12562 * The strategy of this parser is as such: 12563 * 12564 * The main functions (the `.parse...` ones) take a position in the current 12565 * parse string to parse tokens from. The lexer (found in Lexer.js, stored at 12566 * this.gullet.lexer) also supports pulling out tokens at arbitrary places. When 12567 * individual tokens are needed at a position, the lexer is called to pull out a 12568 * token, which is then used. 12569 * 12570 * The parser has a property called "mode" indicating the mode that 12571 * the parser is currently in. Currently it has to be one of "math" or 12572 * "text", which denotes whether the current environment is a math-y 12573 * one or a text-y one (e.g. inside \text). Currently, this serves to 12574 * limit the functions which can be used in text mode. 12575 * 12576 * The main functions then return an object which contains the useful data that 12577 * was parsed at its given point, and a new position at the end of the parsed 12578 * data. The main functions can call each other and continue the parsing by 12579 * using the returned position as a new starting point. 12580 * 12581 * There are also extra `.handle...` functions, which pull out some reused 12582 * functionality into self-contained functions. 12583 * 12584 * The functions return ParseNodes. 12585 */ 12586 12587 class Parser { 12588 constructor(input, settings, isPreamble = false) { 12589 // Start in math mode 12590 this.mode = "math"; 12591 // Create a new macro expander (gullet) and (indirectly via that) also a 12592 // new lexer (mouth) for this parser (stomach, in the language of TeX) 12593 this.gullet = new MacroExpander(input, settings, this.mode); 12594 // Store the settings for use in parsing 12595 this.settings = settings; 12596 // Are we defining a preamble? 12597 this.isPreamble = isPreamble; 12598 // Count leftright depth (for \middle errors) 12599 this.leftrightDepth = 0; 12600 this.prevAtomType = ""; 12601 } 12602 12603 /** 12604 * Checks a result to make sure it has the right type, and throws an 12605 * appropriate error otherwise. 12606 */ 12607 expect(text, consume = true) { 12608 if (this.fetch().text !== text) { 12609 throw new ParseError(`Expected '$text}', got '$this.fetch().text}'`, this.fetch()); 12610 } 12611 if (consume) { 12612 this.consume(); 12613 } 12614 } 12615 12616 /** 12617 * Discards the current lookahead token, considering it consumed. 12618 */ 12619 consume() { 12620 this.nextToken = null; 12621 } 12622 12623 /** 12624 * Return the current lookahead token, or if there isn't one (at the 12625 * beginning, or if the previous lookahead token was consume()d), 12626 * fetch the next token as the new lookahead token and return it. 12627 */ 12628 fetch() { 12629 if (this.nextToken == null) { 12630 this.nextToken = this.gullet.expandNextToken(); 12631 } 12632 return this.nextToken; 12633 } 12634 12635 /** 12636 * Switches between "text" and "math" modes. 12637 */ 12638 switchMode(newMode) { 12639 this.mode = newMode; 12640 this.gullet.switchMode(newMode); 12641 } 12642 12643 /** 12644 * Main parsing function, which parses an entire input. 12645 */ 12646 parse() { 12647 // Create a group namespace for every $...$, $$...$$, \[...\].) 12648 // A \def is then valid only within that pair of delimiters. 12649 this.gullet.beginGroup(); 12650 12651 if (this.settings.colorIsTextColor) { 12652 // Use old \color behavior (same as LaTeX's \textcolor) if requested. 12653 // We do this within the group for the math expression, so it doesn't 12654 // pollute settings.macros. 12655 this.gullet.macros.set("\\color", "\\textcolor"); 12656 } 12657 12658 // Try to parse the input 12659 const parse = this.parseExpression(false); 12660 12661 // If we succeeded, make sure there's an EOF at the end 12662 this.expect("EOF"); 12663 12664 if (this.isPreamble) { 12665 const macros = Object.create(null); 12666 Object.entries(this.gullet.macros.current).forEach(([key, value]) => { 12667 macros[key] = value; 12668 }); 12669 this.gullet.endGroup(); 12670 return macros 12671 } 12672 12673 // The only local macro that we want to save is from \tag. 12674 const tag = this.gullet.macros.get("\\df@tag"); 12675 12676 // End the group namespace for the expression 12677 this.gullet.endGroup(); 12678 12679 if (tag) { this.gullet.macros.current["\\df@tag"] = tag; } 12680 12681 return parse; 12682 } 12683 12684 static get endOfExpression() { 12685 return ["}", "\\endgroup", "\\end", "\\right", "\\endtoggle", "&"]; 12686 } 12687 12688 /** 12689 * Fully parse a separate sequence of tokens as a separate job. 12690 * Tokens should be specified in reverse order, as in a MacroDefinition. 12691 */ 12692 subparse(tokens) { 12693 // Save the next token from the current job. 12694 const oldToken = this.nextToken; 12695 this.consume(); 12696 12697 // Run the new job, terminating it with an excess '}' 12698 this.gullet.pushToken(new Token("}")); 12699 this.gullet.pushTokens(tokens); 12700 const parse = this.parseExpression(false); 12701 this.expect("}"); 12702 12703 // Restore the next token from the current job. 12704 this.nextToken = oldToken; 12705 12706 return parse; 12707 } 12708 12709 /** 12710 * Parses an "expression", which is a list of atoms. 12711 * 12712 * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This 12713 * happens when functions have higher precedence han infix 12714 * nodes in implicit parses. 12715 * 12716 * `breakOnTokenText`: The text of the token that the expression should end 12717 * with, or `null` if something else should end the 12718 * expression. 12719 * 12720 * `breakOnMiddle`: \color, \over, and old styling functions work on an implicit group. 12721 * These groups end just before the usual tokens, but they also 12722 * end just before `\middle`. 12723 */ 12724 parseExpression(breakOnInfix, breakOnTokenText, breakOnMiddle) { 12725 const body = []; 12726 this.prevAtomType = ""; 12727 // Keep adding atoms to the body until we can't parse any more atoms (either 12728 // we reached the end, a }, or a \right) 12729 while (true) { 12730 // Ignore spaces in math mode 12731 if (this.mode === "math") { 12732 this.consumeSpaces(); 12733 } 12734 const lex = this.fetch(); 12735 if (Parser.endOfExpression.indexOf(lex.text) !== -1) { 12736 break; 12737 } 12738 if (breakOnTokenText && lex.text === breakOnTokenText) { 12739 break; 12740 } 12741 if (breakOnMiddle && lex.text === "\\middle") { 12742 break 12743 } 12744 if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) { 12745 break; 12746 } 12747 const atom = this.parseAtom(breakOnTokenText); 12748 if (!atom) { 12749 break; 12750 } else if (atom.type === "internal") { 12751 continue; 12752 } 12753 body.push(atom); 12754 // Keep a record of the atom type, so that op.js can set correct spacing. 12755 this.prevAtomType = atom.type === "atom" ? atom.family : atom.type; 12756 } 12757 if (this.mode === "text") { 12758 this.formLigatures(body); 12759 } 12760 return this.handleInfixNodes(body); 12761 } 12762 12763 /** 12764 * Rewrites infix operators such as \over with corresponding commands such 12765 * as \frac. 12766 * 12767 * There can only be one infix operator per group. If there's more than one 12768 * then the expression is ambiguous. This can be resolved by adding {}. 12769 */ 12770 handleInfixNodes(body) { 12771 let overIndex = -1; 12772 let funcName; 12773 12774 for (let i = 0; i < body.length; i++) { 12775 if (body[i].type === "infix") { 12776 if (overIndex !== -1) { 12777 throw new ParseError("only one infix operator per group", body[i].token); 12778 } 12779 overIndex = i; 12780 funcName = body[i].replaceWith; 12781 } 12782 } 12783 12784 if (overIndex !== -1 && funcName) { 12785 let numerNode; 12786 let denomNode; 12787 12788 const numerBody = body.slice(0, overIndex); 12789 const denomBody = body.slice(overIndex + 1); 12790 12791 if (numerBody.length === 1 && numerBody[0].type === "ordgroup") { 12792 numerNode = numerBody[0]; 12793 } else { 12794 numerNode = { type: "ordgroup", mode: this.mode, body: numerBody }; 12795 } 12796 12797 if (denomBody.length === 1 && denomBody[0].type === "ordgroup") { 12798 denomNode = denomBody[0]; 12799 } else { 12800 denomNode = { type: "ordgroup", mode: this.mode, body: denomBody }; 12801 } 12802 12803 let node; 12804 if (funcName === "\\\\abovefrac") { 12805 node = this.callFunction(funcName, [numerNode, body[overIndex], denomNode], []); 12806 } else { 12807 node = this.callFunction(funcName, [numerNode, denomNode], []); 12808 } 12809 return [node]; 12810 } else { 12811 return body; 12812 } 12813 } 12814 12815 /** 12816 * Handle a subscript or superscript with nice errors. 12817 */ 12818 handleSupSubscript( 12819 name // For error reporting. 12820 ) { 12821 const symbolToken = this.fetch(); 12822 const symbol = symbolToken.text; 12823 this.consume(); 12824 this.consumeSpaces(); // ignore spaces before sup/subscript argument 12825 const group = this.parseGroup(name); 12826 12827 if (!group) { 12828 throw new ParseError("Expected group after '" + symbol + "'", symbolToken); 12829 } 12830 12831 return group; 12832 } 12833 12834 /** 12835 * Converts the textual input of an unsupported command into a text node 12836 * contained within a color node whose color is determined by errorColor 12837 */ 12838 formatUnsupportedCmd(text) { 12839 const textordArray = []; 12840 12841 for (let i = 0; i < text.length; i++) { 12842 textordArray.push({ type: "textord", mode: "text", text: text[i] }); 12843 } 12844 12845 const textNode = { 12846 type: "text", 12847 mode: this.mode, 12848 body: textordArray 12849 }; 12850 12851 const colorNode = { 12852 type: "color", 12853 mode: this.mode, 12854 color: this.settings.errorColor, 12855 body: [textNode] 12856 }; 12857 12858 return colorNode; 12859 } 12860 12861 /** 12862 * Parses a group with optional super/subscripts. 12863 */ 12864 parseAtom(breakOnTokenText) { 12865 // The body of an atom is an implicit group, so that things like 12866 // \left(x\right)^2 work correctly. 12867 const base = this.parseGroup("atom", breakOnTokenText); 12868 12869 // In text mode, we don't have superscripts or subscripts 12870 if (this.mode === "text") { 12871 return base; 12872 } 12873 12874 // Note that base may be empty (i.e. null) at this point. 12875 12876 let superscript; 12877 let subscript; 12878 while (true) { 12879 // Guaranteed in math mode, so eat any spaces first. 12880 this.consumeSpaces(); 12881 12882 // Lex the first token 12883 const lex = this.fetch(); 12884 12885 if (lex.text === "\\limits" || lex.text === "\\nolimits") { 12886 // We got a limit control 12887 if (base && base.type === "op") { 12888 const limits = lex.text === "\\limits"; 12889 base.limits = limits; 12890 base.alwaysHandleSupSub = true; 12891 } else if (base && base.type === "operatorname") { 12892 if (base.alwaysHandleSupSub) { 12893 base.limits = lex.text === "\\limits"; 12894 } 12895 } else { 12896 throw new ParseError("Limit controls must follow a math operator", lex); 12897 } 12898 this.consume(); 12899 } else if (lex.text === "^") { 12900 // We got a superscript start 12901 if (superscript) { 12902 throw new ParseError("Double superscript", lex); 12903 } 12904 superscript = this.handleSupSubscript("superscript"); 12905 } else if (lex.text === "_") { 12906 // We got a subscript start 12907 if (subscript) { 12908 throw new ParseError("Double subscript", lex); 12909 } 12910 subscript = this.handleSupSubscript("subscript"); 12911 } else if (lex.text === "'") { 12912 // We got a prime 12913 if (superscript) { 12914 throw new ParseError("Double superscript", lex); 12915 } 12916 const prime = { type: "textord", mode: this.mode, text: "\\prime" }; 12917 12918 // Many primes can be grouped together, so we handle this here 12919 const primes = [prime]; 12920 this.consume(); 12921 // Keep lexing tokens until we get something that's not a prime 12922 while (this.fetch().text === "'") { 12923 // For each one, add another prime to the list 12924 primes.push(prime); 12925 this.consume(); 12926 } 12927 // If there's a superscript following the primes, combine that 12928 // superscript in with the primes. 12929 if (this.fetch().text === "^") { 12930 primes.push(this.handleSupSubscript("superscript")); 12931 } 12932 // Put everything into an ordgroup as the superscript 12933 superscript = { type: "ordgroup", mode: this.mode, body: primes }; 12934 } else if (uSubsAndSups[lex.text]) { 12935 // A Unicode subscript or superscript character. 12936 // We treat these similarly to the unicode-math package. 12937 // So we render a string of Unicode (sub|super)scripts the 12938 // same as a (sub|super)script of regular characters. 12939 const isSub = unicodeSubRegEx.test(lex.text); 12940 const subsupTokens = []; 12941 subsupTokens.push(new Token(uSubsAndSups[lex.text])); 12942 this.consume(); 12943 // Continue fetching tokens to fill out the group. 12944 while (true) { 12945 const token = this.fetch().text; 12946 if (!(uSubsAndSups[token])) { break } 12947 if (unicodeSubRegEx.test(token) !== isSub) { break } 12948 subsupTokens.unshift(new Token(uSubsAndSups[token])); 12949 this.consume(); 12950 } 12951 // Now create a (sub|super)script. 12952 const body = this.subparse(subsupTokens); 12953 if (isSub) { 12954 subscript = { type: "ordgroup", mode: "math", body }; 12955 } else { 12956 superscript = { type: "ordgroup", mode: "math", body }; 12957 } 12958 } else { 12959 // If it wasn't ^, _, a Unicode (sub|super)script, or ', stop parsing super/subscripts 12960 break; 12961 } 12962 } 12963 12964 if (superscript || subscript) { 12965 if (base && base.type === "multiscript" && !base.postscripts) { 12966 // base is the result of a \prescript function. 12967 // Write the sub- & superscripts into the multiscript element. 12968 base.postscripts = { sup: superscript, sub: subscript }; 12969 return base 12970 } else { 12971 // We got either a superscript or subscript, create a supsub 12972 const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname") 12973 ? undefined 12974 : isDelimiter(this.nextToken.text); 12975 return { 12976 type: "supsub", 12977 mode: this.mode, 12978 base: base, 12979 sup: superscript, 12980 sub: subscript, 12981 isFollowedByDelimiter 12982 } 12983 } 12984 } else { 12985 // Otherwise return the original body 12986 return base; 12987 } 12988 } 12989 12990 /** 12991 * Parses an entire function, including its base and all of its arguments. 12992 */ 12993 parseFunction( 12994 breakOnTokenText, 12995 name // For determining its context 12996 ) { 12997 const token = this.fetch(); 12998 const func = token.text; 12999 const funcData = functions[func]; 13000 if (!funcData) { 13001 return null; 13002 } 13003 this.consume(); // consume command token 13004 13005 if (name && name !== "atom" && !funcData.allowedInArgument) { 13006 throw new ParseError( 13007 "Got function '" + func + "' with no arguments" + (name ? " as " + name : ""), 13008 token 13009 ); 13010 } else if (this.mode === "text" && !funcData.allowedInText) { 13011 throw new ParseError("Can't use function '" + func + "' in text mode", token); 13012 } else if (this.mode === "math" && funcData.allowedInMath === false) { 13013 throw new ParseError("Can't use function '" + func + "' in math mode", token); 13014 } 13015 13016 const prevAtomType = this.prevAtomType; 13017 const { args, optArgs } = this.parseArguments(func, funcData); 13018 this.prevAtomType = prevAtomType; 13019 return this.callFunction(func, args, optArgs, token, breakOnTokenText); 13020 } 13021 13022 /** 13023 * Call a function handler with a suitable context and arguments. 13024 */ 13025 callFunction(name, args, optArgs, token, breakOnTokenText) { 13026 const context = { 13027 funcName: name, 13028 parser: this, 13029 token, 13030 breakOnTokenText 13031 }; 13032 const func = functions[name]; 13033 if (func && func.handler) { 13034 return func.handler(context, args, optArgs); 13035 } else { 13036 throw new ParseError(`No function handler for $name}`); 13037 } 13038 } 13039 13040 /** 13041 * Parses the arguments of a function or environment 13042 */ 13043 parseArguments( 13044 func, // Should look like "\name" or "\begin{name}". 13045 funcData 13046 ) { 13047 const totalArgs = funcData.numArgs + funcData.numOptionalArgs; 13048 if (totalArgs === 0) { 13049 return { args: [], optArgs: [] }; 13050 } 13051 13052 const args = []; 13053 const optArgs = []; 13054 13055 for (let i = 0; i < totalArgs; i++) { 13056 let argType = funcData.argTypes && funcData.argTypes[i]; 13057 const isOptional = i < funcData.numOptionalArgs; 13058 13059 if ( 13060 (funcData.primitive && argType == null) || 13061 // \sqrt expands into primitive if optional argument doesn't exist 13062 (funcData.type === "sqrt" && i === 1 && optArgs[0] == null) 13063 ) { 13064 argType = "primitive"; 13065 } 13066 13067 const arg = this.parseGroupOfType(`argument to '$func}'`, argType, isOptional); 13068 if (isOptional) { 13069 optArgs.push(arg); 13070 } else if (arg != null) { 13071 args.push(arg); 13072 } else { 13073 // should be unreachable 13074 throw new ParseError("Null argument, please report this as a bug"); 13075 } 13076 } 13077 13078 return { args, optArgs }; 13079 } 13080 13081 /** 13082 * Parses a group when the mode is changing. 13083 */ 13084 parseGroupOfType(name, type, optional) { 13085 switch (type) { 13086 case "size": 13087 return this.parseSizeGroup(optional); 13088 case "url": 13089 return this.parseUrlGroup(optional); 13090 case "math": 13091 case "text": 13092 return this.parseArgumentGroup(optional, type); 13093 case "hbox": { 13094 // hbox argument type wraps the argument in the equivalent of 13095 // \hbox, which is like \text but switching to \textstyle size. 13096 const group = this.parseArgumentGroup(optional, "text"); 13097 return group != null 13098 ? { 13099 type: "styling", 13100 mode: group.mode, 13101 body: [group], 13102 scriptLevel: "text" // simulate \textstyle 13103 } 13104 : null; 13105 } 13106 case "raw": { 13107 const token = this.parseStringGroup("raw", optional); 13108 return token != null 13109 ? { 13110 type: "raw", 13111 mode: "text", 13112 string: token.text 13113 } 13114 : null; 13115 } 13116 case "primitive": { 13117 if (optional) { 13118 throw new ParseError("A primitive argument cannot be optional"); 13119 } 13120 const group = this.parseGroup(name); 13121 if (group == null) { 13122 throw new ParseError("Expected group as " + name, this.fetch()); 13123 } 13124 return group; 13125 } 13126 case "original": 13127 case null: 13128 case undefined: 13129 return this.parseArgumentGroup(optional); 13130 default: 13131 throw new ParseError("Unknown group type as " + name, this.fetch()); 13132 } 13133 } 13134 13135 /** 13136 * Discard any space tokens, fetching the next non-space token. 13137 */ 13138 consumeSpaces() { 13139 while (true) { 13140 const ch = this.fetch().text; 13141 // \ufe0e is the Unicode variation selector to supress emoji. Ignore it. 13142 if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") { 13143 this.consume(); 13144 } else { 13145 break 13146 } 13147 } 13148 } 13149 13150 /** 13151 * Parses a group, essentially returning the string formed by the 13152 * brace-enclosed tokens plus some position information. 13153 */ 13154 parseStringGroup( 13155 modeName, // Used to describe the mode in error messages. 13156 optional 13157 ) { 13158 const argToken = this.gullet.scanArgument(optional); 13159 if (argToken == null) { 13160 return null; 13161 } 13162 let str = ""; 13163 let nextToken; 13164 while ((nextToken = this.fetch()).text !== "EOF") { 13165 str += nextToken.text; 13166 this.consume(); 13167 } 13168 this.consume(); // consume the end of the argument 13169 argToken.text = str; 13170 return argToken; 13171 } 13172 13173 /** 13174 * Parses a regex-delimited group: the largest sequence of tokens 13175 * whose concatenated strings match `regex`. Returns the string 13176 * formed by the tokens plus some position information. 13177 */ 13178 parseRegexGroup( 13179 regex, 13180 modeName // Used to describe the mode in error messages. 13181 ) { 13182 const firstToken = this.fetch(); 13183 let lastToken = firstToken; 13184 let str = ""; 13185 let nextToken; 13186 while ((nextToken = this.fetch()).text !== "EOF" && regex.test(str + nextToken.text)) { 13187 lastToken = nextToken; 13188 str += lastToken.text; 13189 this.consume(); 13190 } 13191 if (str === "") { 13192 throw new ParseError("Invalid " + modeName + ": '" + firstToken.text + "'", firstToken); 13193 } 13194 return firstToken.range(lastToken, str); 13195 } 13196 13197 /** 13198 * Parses a size specification, consisting of magnitude and unit. 13199 */ 13200 parseSizeGroup(optional) { 13201 let res; 13202 let isBlank = false; 13203 // don't expand before parseStringGroup 13204 this.gullet.consumeSpaces(); 13205 if (!optional && this.gullet.future().text !== "{") { 13206 res = this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size"); 13207 } else { 13208 res = this.parseStringGroup("size", optional); 13209 } 13210 if (!res) { 13211 return null; 13212 } 13213 if (!optional && res.text.length === 0) { 13214 // Because we've tested for what is !optional, this block won't 13215 // affect \kern, \hspace, etc. It will capture the mandatory arguments 13216 // to \genfrac and \above. 13217 res.text = "0pt"; // Enable \above{} 13218 isBlank = true; // This is here specifically for \genfrac 13219 } 13220 const match = sizeRegEx.exec(res.text); 13221 if (!match) { 13222 throw new ParseError("Invalid size: '" + res.text + "'", res); 13223 } 13224 const data = { 13225 number: +(match[1] + match[2]), // sign + magnitude, cast to number 13226 unit: match[3] 13227 }; 13228 if (!validUnit(data)) { 13229 throw new ParseError("Invalid unit: '" + data.unit + "'", res); 13230 } 13231 return { 13232 type: "size", 13233 mode: this.mode, 13234 value: data, 13235 isBlank 13236 }; 13237 } 13238 13239 /** 13240 * Parses an URL, checking escaped letters and allowed protocols, 13241 * and setting the catcode of % as an active character (as in \hyperref). 13242 */ 13243 parseUrlGroup(optional) { 13244 this.gullet.lexer.setCatcode("%", 13); // active character 13245 this.gullet.lexer.setCatcode("~", 12); // other character 13246 const res = this.parseStringGroup("url", optional); 13247 this.gullet.lexer.setCatcode("%", 14); // comment character 13248 this.gullet.lexer.setCatcode("~", 13); // active character 13249 if (res == null) { 13250 return null; 13251 } 13252 // hyperref package allows backslashes alone in href, but doesn't 13253 // generate valid links in such cases; we interpret this as 13254 // "undefined" behaviour, and keep them as-is. Some browser will 13255 // replace backslashes with forward slashes. 13256 let url = res.text.replace(/\\([#$%&~_^{}])/g, "$1"); 13257 url = res.text.replace(/{\u2044}/g, "/"); 13258 return { 13259 type: "url", 13260 mode: this.mode, 13261 url 13262 }; 13263 } 13264 13265 /** 13266 * Parses an argument with the mode specified. 13267 */ 13268 parseArgumentGroup(optional, mode) { 13269 const argToken = this.gullet.scanArgument(optional); 13270 if (argToken == null) { 13271 return null; 13272 } 13273 const outerMode = this.mode; 13274 if (mode) { 13275 // Switch to specified mode 13276 this.switchMode(mode); 13277 } 13278 13279 this.gullet.beginGroup(); 13280 const expression = this.parseExpression(false, "EOF"); 13281 // TODO: find an alternative way to denote the end 13282 this.expect("EOF"); // expect the end of the argument 13283 this.gullet.endGroup(); 13284 const result = { 13285 type: "ordgroup", 13286 mode: this.mode, 13287 loc: argToken.loc, 13288 body: expression 13289 }; 13290 13291 if (mode) { 13292 // Switch mode back 13293 this.switchMode(outerMode); 13294 } 13295 return result; 13296 } 13297 13298 /** 13299 * Parses an ordinary group, which is either a single nucleus (like "x") 13300 * or an expression in braces (like "{x+y}") or an implicit group, a group 13301 * that starts at the current position, and ends right before a higher explicit 13302 * group ends, or at EOF. 13303 */ 13304 parseGroup( 13305 name, // For error reporting. 13306 breakOnTokenText 13307 ) { 13308 const firstToken = this.fetch(); 13309 const text = firstToken.text; 13310 13311 let result; 13312 // Try to parse an open brace or \begingroup 13313 if (text === "{" || text === "\\begingroup" || text === "\\toggle") { 13314 this.consume(); 13315 const groupEnd = text === "{" 13316 ? "}" 13317 : text === "\\begingroup" 13318 ? "\\endgroup" 13319 : "\\endtoggle"; 13320 13321 this.gullet.beginGroup(); 13322 // If we get a brace, parse an expression 13323 const expression = this.parseExpression(false, groupEnd); 13324 const lastToken = this.fetch(); 13325 this.expect(groupEnd); // Check that we got a matching closing brace 13326 this.gullet.endGroup(); 13327 result = { 13328 type: (lastToken.text === "\\endtoggle" ? "toggle" : "ordgroup"), 13329 mode: this.mode, 13330 loc: SourceLocation.range(firstToken, lastToken), 13331 body: expression, 13332 // A group formed by \begingroup...\endgroup is a semi-simple group 13333 // which doesn't affect spacing in math mode, i.e., is transparent. 13334 // https://tex.stackexchange.com/questions/1930/ 13335 semisimple: text === "\\begingroup" || undefined 13336 }; 13337 } else { 13338 // If there exists a function with this name, parse the function. 13339 // Otherwise, just return a nucleus 13340 result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol(); 13341 if (result == null && text[0] === "\\" && 13342 !Object.prototype.hasOwnProperty.call(implicitCommands, text )) { 13343 result = this.formatUnsupportedCmd(text); 13344 this.consume(); 13345 } 13346 } 13347 return result; 13348 } 13349 13350 /** 13351 * Form ligature-like combinations of characters for text mode. 13352 * This includes inputs like "--", "---", "``" and "''". 13353 * The result will simply replace multiple textord nodes with a single 13354 * character in each value by a single textord node having multiple 13355 * characters in its value. The representation is still ASCII source. 13356 * The group will be modified in place. 13357 */ 13358 formLigatures(group) { 13359 let n = group.length - 1; 13360 for (let i = 0; i < n; ++i) { 13361 const a = group[i]; 13362 const v = a.text; 13363 if (v === "-" && group[i + 1].text === "-") { 13364 if (i + 1 < n && group[i + 2].text === "-") { 13365 group.splice(i, 3, { 13366 type: "textord", 13367 mode: "text", 13368 loc: SourceLocation.range(a, group[i + 2]), 13369 text: "---" 13370 }); 13371 n -= 2; 13372 } else { 13373 group.splice(i, 2, { 13374 type: "textord", 13375 mode: "text", 13376 loc: SourceLocation.range(a, group[i + 1]), 13377 text: "--" 13378 }); 13379 n -= 1; 13380 } 13381 } 13382 if ((v === "'" || v === "`") && group[i + 1].text === v) { 13383 group.splice(i, 2, { 13384 type: "textord", 13385 mode: "text", 13386 loc: SourceLocation.range(a, group[i + 1]), 13387 text: v + v 13388 }); 13389 n -= 1; 13390 } 13391 } 13392 } 13393 13394 /** 13395 * Parse a single symbol out of the string. Here, we handle single character 13396 * symbols and special functions like \verb. 13397 */ 13398 parseSymbol() { 13399 const nucleus = this.fetch(); 13400 let text = nucleus.text; 13401 13402 if (/^\\verb[^a-zA-Z]/.test(text)) { 13403 this.consume(); 13404 let arg = text.slice(5); 13405 const star = arg.charAt(0) === "*"; 13406 if (star) { 13407 arg = arg.slice(1); 13408 } 13409 // Lexer's tokenRegex is constructed to always have matching 13410 // first/last characters. 13411 if (arg.length < 2 || arg.charAt(0) !== arg.slice(-1)) { 13412 throw new ParseError(`\\verb assertion failed -- 13413 please report what input caused this bug`); 13414 } 13415 arg = arg.slice(1, -1); // remove first and last char 13416 return { 13417 type: "verb", 13418 mode: "text", 13419 body: arg, 13420 star 13421 }; 13422 } 13423 // At this point, we should have a symbol, possibly with accents. 13424 // First expand any accented base symbol according to unicodeSymbols. 13425 if (Object.prototype.hasOwnProperty.call(unicodeSymbols, text[0]) && 13426 this.mode === "math" && !symbols[this.mode][text[0]]) { 13427 // This behavior is not strict (XeTeX-compatible) in math mode. 13428 if (this.settings.strict && this.mode === "math") { 13429 throw new ParseError(`Accented Unicode text character "$text[0]}" used in ` + `math mode`, 13430 nucleus 13431 ); 13432 } 13433 text = unicodeSymbols[text[0]] + text.slice(1); 13434 } 13435 // Strip off any combining characters 13436 const match = this.mode === "math" 13437 ? combiningDiacriticalMarksEndRegex.exec(text) 13438 : null; 13439 if (match) { 13440 text = text.substring(0, match.index); 13441 if (text === "i") { 13442 text = "\u0131"; // dotless i, in math and text mode 13443 } else if (text === "j") { 13444 text = "\u0237"; // dotless j, in math and text mode 13445 } 13446 } 13447 // Recognize base symbol 13448 let symbol; 13449 if (symbols[this.mode][text]) { 13450 let group = symbols[this.mode][text].group; 13451 if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) { 13452 // Change from a binary operator to a unary (prefix) operator 13453 group = "open"; 13454 } 13455 const loc = SourceLocation.range(nucleus); 13456 let s; 13457 if (Object.prototype.hasOwnProperty.call(ATOMS, group )) { 13458 const family = group; 13459 s = { 13460 type: "atom", 13461 mode: this.mode, 13462 family, 13463 loc, 13464 text 13465 }; 13466 } else { 13467 if (asciiFromScript[text]) { 13468 // Unicode 14 disambiguates chancery from roundhand. 13469 // See https://www.unicode.org/charts/PDF/U1D400.pdf 13470 this.consume(); 13471 const nextCode = this.fetch().text.charCodeAt(0); 13472 // mathcal is Temml default. Use mathscript if called for. 13473 const font = nextCode === 0xfe01 ? "mathscr" : "mathcal"; 13474 if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume(); } 13475 return { 13476 type: "font", 13477 mode: "math", 13478 font, 13479 body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] } 13480 } 13481 } 13482 // Default ord character. No disambiguation necessary. 13483 s = { 13484 type: group, 13485 mode: this.mode, 13486 loc, 13487 text 13488 }; 13489 } 13490 symbol = s; 13491 } else if (text.charCodeAt(0) >= 0x80 || combiningDiacriticalMarksEndRegex.exec(text)) { 13492 // no symbol for e.g. ^ 13493 if (this.settings.strict && this.mode === "math") { 13494 throw new ParseError(`Unicode text character "$text[0]}" used in math mode`, nucleus) 13495 } 13496 // All nonmathematical Unicode characters are rendered as if they 13497 // are in text mode (wrapped in \text) because that's what it 13498 // takes to render them in LaTeX. 13499 symbol = { 13500 type: "textord", 13501 mode: "text", 13502 loc: SourceLocation.range(nucleus), 13503 text 13504 }; 13505 } else { 13506 return null; // EOF, ^, _, {, }, etc. 13507 } 13508 this.consume(); 13509 // Transform combining characters into accents 13510 if (match) { 13511 for (let i = 0; i < match[0].length; i++) { 13512 const accent = match[0][i]; 13513 if (!unicodeAccents[accent]) { 13514 throw new ParseError(`Unknown accent ' $accent}'`, nucleus); 13515 } 13516 const command = unicodeAccents[accent][this.mode] || 13517 unicodeAccents[accent].text; 13518 if (!command) { 13519 throw new ParseError(`Accent $accent} unsupported in $this.mode} mode`, nucleus); 13520 } 13521 symbol = { 13522 type: "accent", 13523 mode: this.mode, 13524 loc: SourceLocation.range(nucleus), 13525 label: command, 13526 isStretchy: false, 13527 base: symbol 13528 }; 13529 } 13530 } 13531 return symbol; 13532 } 13533 } 13534 13535 /** 13536 * Parses an expression using a Parser, then returns the parsed result. 13537 */ 13538 const parseTree = function(toParse, settings) { 13539 if (!(typeof toParse === "string" || toParse instanceof String)) { 13540 throw new TypeError("Temml can only parse string typed expression") 13541 } 13542 const parser = new Parser(toParse, settings); 13543 // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors 13544 delete parser.gullet.macros.current["\\df@tag"]; 13545 13546 let tree = parser.parse(); 13547 13548 // LaTeX ignores a \tag placed outside an AMS environment. 13549 if (!(tree.length > 0 && tree[0].type && tree[0].type === "array" && tree[0].addEqnNum)) { 13550 // If the input used \tag, it will set the \df@tag macro to the tag. 13551 // In this case, we separately parse the tag and wrap the tree. 13552 if (parser.gullet.macros.get("\\df@tag")) { 13553 if (!settings.displayMode) { 13554 throw new ParseError("\\tag works only in display mode") 13555 } 13556 parser.gullet.feed("\\df@tag"); 13557 tree = [ 13558 { 13559 type: "tag", 13560 mode: "text", 13561 body: tree, 13562 tag: parser.parse() 13563 } 13564 ]; 13565 } 13566 } 13567 13568 return tree 13569 }; 13570 13571 /** 13572 * This file contains information about the style that the mathmlBuilder carries 13573 * around with it. Data is held in an `Style` object, and when 13574 * recursing, a new `Style` object can be created with the `.with*` functions. 13575 */ 13576 13577 const subOrSupLevel = [2, 2, 3, 3]; 13578 13579 /** 13580 * This is the main Style class. It contains the current style.level, color, and font. 13581 * 13582 * Style objects should not be modified. To create a new Style with 13583 * different properties, call a `.with*` method. 13584 */ 13585 class Style { 13586 constructor(data) { 13587 // Style.level can be 0 | 1 | 2 | 3, which correspond to 13588 // displaystyle, textstyle, scriptstyle, and scriptscriptstyle. 13589 // style.level usually does not directly set MathML's script level. MathML does that itself. 13590 // However, Chromium does not stop shrinking after scriptscriptstyle, so we do explicitly 13591 // set a scriptlevel attribute in those conditions. 13592 // We also use style.level to track math style so that we can get the correct 13593 // scriptlevel when needed in supsub.js, mathchoice.js, or for dimensions in em. 13594 this.level = data.level; 13595 this.color = data.color; // string | void 13596 // A font family applies to a group of fonts (i.e. SansSerif), while a font 13597 // represents a specific font (i.e. SansSerif Bold). 13598 // See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm 13599 this.font = data.font || ""; // string 13600 this.fontFamily = data.fontFamily || ""; // string 13601 this.fontSize = data.fontSize || 1.0; // number 13602 this.fontWeight = data.fontWeight || ""; 13603 this.fontShape = data.fontShape || ""; 13604 this.maxSize = data.maxSize; // [number, number] 13605 } 13606 13607 /** 13608 * Returns a new style object with the same properties as "this". Properties 13609 * from "extension" will be copied to the new style object. 13610 */ 13611 extend(extension) { 13612 const data = { 13613 level: this.level, 13614 color: this.color, 13615 font: this.font, 13616 fontFamily: this.fontFamily, 13617 fontSize: this.fontSize, 13618 fontWeight: this.fontWeight, 13619 fontShape: this.fontShape, 13620 maxSize: this.maxSize 13621 }; 13622 13623 for (const key in extension) { 13624 if (Object.prototype.hasOwnProperty.call(extension, key)) { 13625 data[key] = extension[key]; 13626 } 13627 } 13628 13629 return new Style(data); 13630 } 13631 13632 withLevel(n) { 13633 return this.extend({ 13634 level: n 13635 }); 13636 } 13637 13638 incrementLevel() { 13639 return this.extend({ 13640 level: Math.min(this.level + 1, 3) 13641 }); 13642 } 13643 13644 inSubOrSup() { 13645 return this.extend({ 13646 level: subOrSupLevel[this.level] 13647 }) 13648 } 13649 13650 /** 13651 * Create a new style object with the given color. 13652 */ 13653 withColor(color) { 13654 return this.extend({ 13655 color: color 13656 }); 13657 } 13658 13659 /** 13660 * Creates a new style object with the given math font or old text font. 13661 * @type {[type]} 13662 */ 13663 withFont(font) { 13664 return this.extend({ 13665 font 13666 }); 13667 } 13668 13669 /** 13670 * Create a new style objects with the given fontFamily. 13671 */ 13672 withTextFontFamily(fontFamily) { 13673 return this.extend({ 13674 fontFamily, 13675 font: "" 13676 }); 13677 } 13678 13679 /** 13680 * Creates a new style object with the given font size 13681 */ 13682 withFontSize(num) { 13683 return this.extend({ 13684 fontSize: num 13685 }); 13686 } 13687 13688 /** 13689 * Creates a new style object with the given font weight 13690 */ 13691 withTextFontWeight(fontWeight) { 13692 return this.extend({ 13693 fontWeight, 13694 font: "" 13695 }); 13696 } 13697 13698 /** 13699 * Creates a new style object with the given font weight 13700 */ 13701 withTextFontShape(fontShape) { 13702 return this.extend({ 13703 fontShape, 13704 font: "" 13705 }); 13706 } 13707 13708 /** 13709 * Gets the CSS color of the current style object 13710 */ 13711 getColor() { 13712 return this.color; 13713 } 13714 } 13715 13716 /* Temml Post Process 13717 * Populate the text contents of each \ref & \eqref 13718 * 13719 * As with other Temml code, this file is released under terms of the MIT license. 13720 * https://mit-license.org/ 13721 */ 13722 13723 const version = "0.10.34"; 13724 13725 function postProcess(block) { 13726 const labelMap = {}; 13727 let i = 0; 13728 13729 // Get a collection of the parents of each \tag & auto-numbered equation 13730 const amsEqns = document.getElementsByClassName('tml-eqn'); 13731 for (let parent of amsEqns) { 13732 // AMS automatically numbered equation. 13733 // Assign an id. 13734 i += 1; 13735 parent.setAttribute("id", "tml-eqn-" + String(i)); 13736 // No need to write a number into the text content of the element. 13737 // A CSS counter has done that even if this postProcess() function is not used. 13738 13739 // Find any \label that refers to an AMS automatic eqn number. 13740 while (true) { 13741 if (parent.tagName === "mtable") { break } 13742 const labels = parent.getElementsByClassName("tml-label"); 13743 if (labels.length > 0) { 13744 const id = parent.attributes.id.value; 13745 labelMap[id] = String(i); 13746 break 13747 } else { 13748 parent = parent.parentElement; 13749 } 13750 } 13751 } 13752 13753 // Find \labels associated with \tag 13754 const taggedEqns = document.getElementsByClassName('tml-tageqn'); 13755 for (const parent of taggedEqns) { 13756 const labels = parent.getElementsByClassName("tml-label"); 13757 if (labels.length > 0) { 13758 const tags = parent.getElementsByClassName("tml-tag"); 13759 if (tags.length > 0) { 13760 const id = parent.attributes.id.value; 13761 labelMap[id] = tags[0].textContent; 13762 } 13763 } 13764 } 13765 13766 // Populate \ref & \eqref text content 13767 const refs = block.getElementsByClassName("tml-ref"); 13768 [...refs].forEach(ref => { 13769 const attr = ref.getAttribute("href"); 13770 let str = labelMap[attr.slice(1)]; 13771 if (ref.className.indexOf("tml-eqref") === -1) { 13772 // \ref. Omit parens. 13773 str = str.replace(/^\(/, ""); 13774 str = str.replace(/\)$/, ""); 13775 } else { 13776 // \eqref. Include parens 13777 if (str.charAt(0) !== "(") { str = "(" + str; } 13778 if (str.slice(-1) !== ")") { str = str + ")"; } 13779 } 13780 const mtext = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mtext"); 13781 mtext.appendChild(document.createTextNode(str)); 13782 const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"); 13783 math.appendChild(mtext); 13784 ref.appendChild(math); 13785 }); 13786 } 13787 13788 /* eslint no-console:0 */ 13789 /** 13790 * This is the main entry point for Temml. Here, we expose functions for 13791 * rendering expressions either to DOM nodes or to markup strings. 13792 * 13793 * We also expose the ParseError class to check if errors thrown from Temml are 13794 * errors in the expression, or errors in javascript handling. 13795 */ 13796 13797 13798 /** 13799 * @type {import('./temml').render} 13800 * Parse and build an expression, and place that expression in the DOM node 13801 * given. 13802 */ 13803 let render = function(expression, baseNode, options = {}) { 13804 baseNode.textContent = ""; 13805 const alreadyInMathElement = baseNode.tagName.toLowerCase() === "math"; 13806 if (alreadyInMathElement) { options.wrap = "none"; } 13807 const math = renderToMathMLTree(expression, options); 13808 if (alreadyInMathElement) { 13809 // The <math> element already exists. Populate it. 13810 baseNode.textContent = ""; 13811 math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); 13812 } else if (math.children.length > 1) { 13813 baseNode.textContent = ""; 13814 math.children.forEach(e => { baseNode.appendChild(e.toNode()); }); 13815 } else { 13816 baseNode.appendChild(math.toNode()); 13817 } 13818 }; 13819 13820 // Temml's styles don't work properly in quirks mode. Print out an error, and 13821 // disable rendering. 13822 if (typeof document !== "undefined") { 13823 if (document.compatMode !== "CSS1Compat") { 13824 typeof console !== "undefined" && 13825 console.warn( 13826 "Warning: Temml doesn't work in quirks mode. Make sure your " + 13827 "website has a suitable doctype." 13828 ); 13829 13830 render = function() { 13831 throw new ParseError("Temml doesn't work in quirks mode."); 13832 }; 13833 } 13834 } 13835 13836 /** 13837 * @type {import('./temml').renderToString} 13838 * Parse and build an expression, and return the markup for that. 13839 */ 13840 const renderToString = function(expression, options) { 13841 const markup = renderToMathMLTree(expression, options).toMarkup(); 13842 return markup; 13843 }; 13844 13845 /** 13846 * @type {import('./temml').generateParseTree} 13847 * Parse an expression and return the parse tree. 13848 */ 13849 const generateParseTree = function(expression, options) { 13850 const settings = new Settings(options); 13851 return parseTree(expression, settings); 13852 }; 13853 13854 /** 13855 * @type {import('./temml').definePreamble} 13856 * Take an expression which contains a preamble. 13857 * Parse it and return the macros. 13858 */ 13859 const definePreamble = function(expression, options) { 13860 const settings = new Settings(options); 13861 settings.macros = {}; 13862 if (!(typeof expression === "string" || expression instanceof String)) { 13863 throw new TypeError("Temml can only parse string typed expression") 13864 } 13865 const parser = new Parser(expression, settings, true); 13866 // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors 13867 delete parser.gullet.macros.current["\\df@tag"]; 13868 const macros = parser.parse(); 13869 return macros 13870 }; 13871 13872 /** 13873 * If the given error is a Temml ParseError, 13874 * renders the invalid LaTeX as a span with hover title giving the Temml 13875 * error message. Otherwise, simply throws the error. 13876 */ 13877 const renderError = function(error, expression, options) { 13878 if (options.throwOnError || !(error instanceof ParseError)) { 13879 throw error; 13880 } 13881 const node = new Span(["temml-error"], [new TextNode$1(expression + "\n" + error.toString())]); 13882 node.style.color = options.errorColor; 13883 node.style.whiteSpace = "pre-line"; 13884 return node; 13885 }; 13886 13887 /** 13888 * @type {import('./temml').renderToMathMLTree} 13889 * Generates and returns the Temml build tree. This is used for advanced 13890 * use cases (like rendering to custom output). 13891 */ 13892 const renderToMathMLTree = function(expression, options) { 13893 const settings = new Settings(options); 13894 try { 13895 const tree = parseTree(expression, settings); 13896 const style = new Style({ 13897 level: settings.displayMode ? StyleLevel.DISPLAY : StyleLevel.TEXT, 13898 maxSize: settings.maxSize 13899 }); 13900 return buildMathML(tree, expression, style, settings); 13901 } catch (error) { 13902 return renderError(error, expression, settings); 13903 } 13904 }; 13905 13906 /** @type {import('./temml').default} */ 13907 var temml = { 13908 /** 13909 * Current Temml version 13910 */ 13911 version: version, 13912 /** 13913 * Renders the given LaTeX into MathML, and adds 13914 * it as a child to the specified DOM node. 13915 */ 13916 render, 13917 /** 13918 * Renders the given LaTeX into MathML string, 13919 * for sending to the client. 13920 */ 13921 renderToString, 13922 /** 13923 * Post-process an entire HTML block. 13924 * Writes AMS auto-numbers and implements \ref{}. 13925 * Typcally called once, after a loop has rendered many individual spans. 13926 */ 13927 postProcess, 13928 /** 13929 * Temml error, usually during parsing. 13930 */ 13931 ParseError, 13932 /** 13933 * Creates a set of macros with document-wide scope. 13934 */ 13935 definePreamble, 13936 /** 13937 * Parses the given LaTeX into Temml's internal parse tree structure, 13938 * without rendering to HTML or MathML. 13939 * 13940 * NOTE: This method is not currently recommended for public use. 13941 * The internal tree representation is unstable and is very likely 13942 * to change. Use at your own risk. 13943 */ 13944 __parse: generateParseTree, 13945 /** 13946 * Renders the given LaTeX into a MathML internal DOM tree 13947 * representation, without flattening that representation to a string. 13948 * 13949 * NOTE: This method is not currently recommended for public use. 13950 * The internal tree representation is unstable and is very likely 13951 * to change. Use at your own risk. 13952 */ 13953 __renderToMathMLTree: renderToMathMLTree, 13954 /** 13955 * adds a new symbol to builtin symbols table 13956 */ 13957 __defineSymbol: defineSymbol, 13958 /** 13959 * adds a new macro to builtin macro list 13960 */ 13961 __defineMacro: defineMacro 13962 }; 13963 13964 13965 13966 ;// ./node_modules/@wordpress/latex-to-mathml/build-module/index.js 13967 13968 function latexToMathML(latex, { displayMode = true } = {}) { 13969 const mathML = temml.renderToString(latex, { 13970 displayMode, 13971 annotate: true, 13972 throwOnError: true 13973 }); 13974 const doc = document.implementation.createHTMLDocument(""); 13975 doc.body.innerHTML = mathML; 13976 return doc.body.querySelector("math")?.innerHTML ?? ""; 13977 } 13978 13979 13980 (window.wp = window.wp || {}).latexToMathml = __webpack_exports__; 13981 /******/ })() 13982 ;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Wed Oct 22 08:20:04 2025 | Cross-referenced by PHPXref |