[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/dist/ -> latex-to-mathml.js (source)

   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    "&": "&amp;",
 131    ">": "&gt;",
 132    "<": "&lt;",
 133    '"': "&quot;",
 134    "'": "&#x27;"
 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]*([^\\/#?]*?)(:|&#0*58|&#x0*3a|&colon)/i.exec(url);
 201    if (!protocol) {
 202      return "_relative";
 203    }
 204    // Reject weird colons
 205    if (protocol[2] !== ":") {
 206      return null;
 207    }
 208    // Reject invalid characters in scheme according to
 209    // https://datatracker.ietf.org/doc/html/rfc3986#section-3.1
 210    if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(protocol[1])) {
 211      return null;
 212    }
 213    // Lowercase the protocol
 214    return protocol[1].toLowerCase();
 215  };
 216  
 217  /**
 218   * Round `n` to 4 decimal places, or to the nearest 1/10,000th em. The TeXbook
 219   * gives an acceptable rounding error of 100sp (which would be the nearest
 220   * 1/6551.6em with our ptPerEm = 10):
 221   * http://www.ctex.org/documents/shredder/src/texbook.pdf#page=69
 222   */
 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"; // &VeryThinSpace;
9131    } else if (width >= 0.1666 && width <= 0.1667) {
9132      return "\u2009"; // &ThinSpace;
9133    } else if (width >= 0.2222 && width <= 0.2223) {
9134      return "\u2005"; // &MediumSpace;
9135    } else if (width >= 0.2777 && width <= 0.2778) {
9136      return "\u2005\u200a"; // &ThickSpace;
9137    } else {
9138      return "";
9139    }
9140  };
9141  
9142  // Limit valid characters to a small set, for safety.
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>&ApplyFunction;</mo>.
9691        // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
9692        const operator = new MathNode("mo", [makeText("\u2061", "text")]);
9693        const row = [node, operator];
9694        // Set spacing
9695        if (group.needsLeadingSpace) {
9696          const lead = new MathNode("mspace");
9697          lead.setAttribute("width", "0.1667em"); // thin space.
9698          row.unshift(lead);
9699        }
9700        if (!group.isFollowedByDelimiter) {
9701          const trail = new MathNode("mspace");
9702          trail.setAttribute("width", "0.1667em"); // thin space.
9703          row.push(trail);
9704        }
9705        node = new MathNode("mrow", row);
9706      }
9707    }
9708  
9709    return node;
9710  };
9711  
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>&ApplyFunction;</mo>.
10079      // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
10080      const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]);
10081      const fragment = [wrapper, operator];
10082      if (group.needsLeadingSpace) {
10083        // LaTeX gives operator spacing, but a <mi> gets ord spacing.
10084        // So add a leading space.
10085        const space = new mathMLTree.MathNode("mspace");
10086        space.setAttribute("width", "0.1667em"); // thin space.
10087        fragment.unshift(space);
10088      }
10089      if (!group.isFollowedByDelimiter) {
10090        const trail = new mathMLTree.MathNode("mspace");
10091        trail.setAttribute("width", "0.1667em"); // thin space.
10092        fragment.push(trail);
10093      }
10094      return mathMLTree.newDocumentFragment(fragment)
10095    }
10096  
10097    return wrapper
10098  };
10099  
10100  // \operatorname
10101  // amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@
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>&ApplyFunction;</mo>.
10742        // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
10743        const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]);
10744        if (needsLeadingSpace) {
10745          const space = new mathMLTree.MathNode("mspace");
10746          space.setAttribute("width", "0.1667em"); // thin space.
10747          node = mathMLTree.newDocumentFragment([space, node, operator]);
10748        } else {
10749          node = mathMLTree.newDocumentFragment([node, operator]);
10750        }
10751        if (appendSpace) {
10752          const space = new mathMLTree.MathNode("mspace");
10753          space.setAttribute("width", "0.1667em"); // thin space.
10754          node.children.push(space);
10755        }
10756      } else if (symbolRegEx.test(nodeType)) {
10757        // Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
10758        node = new mathMLTree.MathNode("mrow", [node]);
10759      }
10760  
10761      return node
10762    }
10763  });
10764  
10765  // Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js.
10766  
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  ;


Generated : Wed Oct 22 08:20:04 2025 Cross-referenced by PHPXref