UNPKG

qwik-latex

Version:

Katex based LaTeX rendering for Qwik

2,260 lines 63.4 kB
import katex from "katex";
katex.__defineMacro("\\ce", function(context) {
  return chemParse(context.consumeArgs(1)[0], "ce");
});
katex.__defineMacro("\\pu", function(context) {
  return chemParse(context.consumeArgs(1)[0], "pu");
});
katex.__defineMacro(
  "\\tripledash",
  "{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}"
);
var chemParse = function(tokens, stateMachine) {
  var str = "";
  var expectedLoc = tokens.length && tokens[tokens.length - 1].loc.start;
  for (var i = tokens.length - 1; i >= 0; i--) {
    if (tokens[i].loc.start > expectedLoc) {
      str += " ";
      expectedLoc = tokens[i].loc.start;
    }
    str += tokens[i].text;
    expectedLoc += tokens[i].text.length;
  }
  var tex = texify.go(mhchemParser.go(str, stateMachine));
  return tex;
};
var mhchemParser = {
  //
  // Parses mchem \ce syntax
  //
  // Call like
  //   go("H2O");
  //
  go: function(input, stateMachine) {
    if (!input) {
      return [];
    }
    if (stateMachine === void 0) {
      stateMachine = "ce";
    }
    var state = "0";
    var buffer = {};
    buffer["parenthesisLevel"] = 0;
    input = input.replace(/\n/g, " ");
    input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-");
    input = input.replace(/[\u2026]/g, "...");
    var lastInput;
    var watchdog = 10;
    var output = [];
    while (true) {
      if (lastInput !== input) {
        watchdog = 10;
        lastInput = input;
      } else {
        watchdog--;
      }
      var machine = mhchemParser.stateMachines[stateMachine];
      var t = machine.transitions[state] || machine.transitions["*"];
      iterateTransitions: for (var i = 0; i < t.length; i++) {
        var matches = mhchemParser.patterns.match_(t[i].pattern, input);
        if (matches) {
          var task = t[i].task;
          for (var iA = 0; iA < task.action_.length; iA++) {
            var o;
            if (machine.actions[task.action_[iA].type_]) {
              o = machine.actions[task.action_[iA].type_](
                buffer,
                matches.match_,
                task.action_[iA].option
              );
            } else if (mhchemParser.actions[task.action_[iA].type_]) {
              o = mhchemParser.actions[task.action_[iA].type_](
                buffer,
                matches.match_,
                task.action_[iA].option
              );
            } else {
              throw [
                "MhchemBugA",
                "mhchem bug A. Please report. (" + task.action_[iA].type_ + ")"
              ];
            }
            mhchemParser.concatArray(output, o);
          }
          state = task.nextState || state;
          if (input.length > 0) {
            if (!task.revisit) {
              input = matches.remainder;
            }
            if (!task.toContinue) {
              break iterateTransitions;
            }
          } else {
            return output;
          }
        }
      }
      if (watchdog <= 0) {
        throw ["MhchemBugU", "mhchem bug U. Please report."];
      }
    }
  },
  concatArray: function(a, b) {
    if (b) {
      if (Array.isArray(b)) {
        for (var iB = 0; iB < b.length; iB++) {
          a.push(b[iB]);
        }
      } else {
        a.push(b);
      }
    }
  },
  patterns: {
    //
    // Matching patterns
    // either regexps or function that return null or {match_:"a", remainder:"bc"}
    //
    patterns: {
      // property names must not look like integers ("2") for correct property traversal order, later on
      empty: /^$/,
      else: /^./,
      else2: /^./,
      space: /^\s/,
      "space A": /^\s(?=[A-Z\\$])/,
      space$: /^\s$/,
      "a-z": /^[a-z]/,
      x: /^x/,
      x$: /^x$/,
      i$: /^i$/,
      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]))))+/,
      "\\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]))/,
      "one lowercase latin letter $": /^(?:([a-z])(?:$|[^a-zA-Z]))$/,
      "$one lowercase latin letter$ $": /^\$(?:([a-z])(?:$|[^a-zA-Z]))\$$/,
      "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]))$/,
      digits: /^[0-9]+/,
      "-9.,9": /^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/,
      "-9.,9 no missing 0": /^[+\-]?[0-9]+(?:[.,][0-9]+)?/,
      "(-)(9.,9)(e)(99)": function(input) {
        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]+\}))?/
        );
        if (m && m[0]) {
          return { match_: m.splice(1), remainder: input.substr(m[0].length) };
        }
        return null;
      },
      "(-)(9)^(-9)": function(input) {
        var m = input.match(
          /^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/
        );
        if (m && m[0]) {
          return { match_: m.splice(1), remainder: input.substr(m[0].length) };
        }
        return null;
      },
      "state of aggregation $": function(input) {
        var a = mhchemParser.patterns.findObserveGroups(
          input,
          "",
          /^\([a-z]{1,3}(?=[\),])/,
          ")",
          ""
        );
        if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) {
          return a;
        }
        var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/);
        if (m) {
          return { match_: m[0], remainder: input.substr(m[0].length) };
        }
        return null;
      },
      "_{(state of aggregation)}$": /^_\{(\([a-z]{1,3}\))\}/,
      "{[(": /^(?:\\\{|\[|\()/,
      ")]}": /^(?:\)|\]|\\\})/,
      ", ": /^[,;]\s*/,
      ",": /^[,;]/,
      ".": /^[.]/,
      ". ": /^([.\u22C5\u00B7\u2022])\s*/,
      "...": /^\.\.\.(?=$|[^.])/,
      "* ": /^([*])\s*/,
      "^{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^{",
          "",
          "",
          "}"
        );
      },
      "^($...$)": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^",
          "$",
          "$",
          ""
        );
      },
      "^a": /^\^([0-9]+|[^\\_])/,
      "^\\x{}{}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^",
          /^\\[a-zA-Z]+\{/,
          "}",
          "",
          "",
          "{",
          "}",
          "",
          true
        );
      },
      "^\\x{}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^",
          /^\\[a-zA-Z]+\{/,
          "}",
          ""
        );
      },
      "^\\x": /^\^(\\[a-zA-Z]+)\s*/,
      "^(-1)": /^\^(-?\d+)/,
      "'": /^'/,
      "_{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_{",
          "",
          "",
          "}"
        );
      },
      "_($...$)": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_",
          "$",
          "$",
          ""
        );
      },
      _9: /^_([+\-]?[0-9]+|[^\\])/,
      "_\\x{}{}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_",
          /^\\[a-zA-Z]+\{/,
          "}",
          "",
          "",
          "{",
          "}",
          "",
          true
        );
      },
      "_\\x{}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_",
          /^\\[a-zA-Z]+\{/,
          "}",
          ""
        );
      },
      "_\\x": /^_(\\[a-zA-Z]+)\s*/,
      "^_": /^(?:\^(?=_)|\_(?=\^)|[\^_]$)/,
      "{}": /^\{\}/,
      "{...}": function(input) {
        return mhchemParser.patterns.findObserveGroups(input, "", "{", "}", "");
      },
      "{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(input, "{", "", "", "}");
      },
      "$...$": function(input) {
        return mhchemParser.patterns.findObserveGroups(input, "", "$", "$", "");
      },
      "${(...)}$": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "${",
          "",
          "",
          "}$"
        );
      },
      "$(...)$": function(input) {
        return mhchemParser.patterns.findObserveGroups(input, "$", "", "", "$");
      },
      "=<>": /^[=<>]/,
      "#": /^[#\u2261]/,
      "+": /^\+/,
      "-$": /^-(?=[\s_},;\]/]|$|\([a-z]+\))/,
      // -space -, -; -] -/ -$ -state-of-aggregation
      "-9": /^-(?=[0-9])/,
      "- orbital overlap": /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,
      "-": /^-/,
      "pm-operator": /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,
      operator: /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,
      arrowUpDown: /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,
      "\\bond{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\bond{",
          "",
          "",
          "}"
        );
      },
      "->": /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,
      CMT: /^[CMT](?=\[)/,
      "[(...)]": function(input) {
        return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]");
      },
      "1st-level escape": /^(&|\\\\|\\hline)\s*/,
      "\\,": /^(?:\\[,\ ;:])/,
      // \\x - but output no space before
      "\\x{}{}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "",
          /^\\[a-zA-Z]+\{/,
          "}",
          "",
          "",
          "{",
          "}",
          "",
          true
        );
      },
      "\\x{}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "",
          /^\\[a-zA-Z]+\{/,
          "}",
          ""
        );
      },
      "\\ca": /^\\ca(?:\s+|(?![a-zA-Z]))/,
      "\\x": /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/,
      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
      others: /^[\/~|]/,
      "\\frac{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\frac{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\overset{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\overset{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\underset{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\underset{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\underbrace{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\underbrace{",
          "",
          "",
          "}_",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\color{(...)}0": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\color{",
          "",
          "",
          "}"
        );
      },
      "\\color{(...)}{(...)}1": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\color{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\color(...){(...)}2": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\color",
          "\\",
          "",
          /^(?=\{)/,
          "{",
          "",
          "",
          "}"
        );
      },
      "\\ce{(...)}": function(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\ce{",
          "",
          "",
          "}"
        );
      },
      oxidation$: /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,
      "d-oxidation$": /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,
      // 0 could be oxidation or charge
      "roman numeral": /^[IVX]+/,
      "1/2$": /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,
      amount: function(input) {
        var match;
        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))/
        );
        if (match) {
          return { match_: match[0], remainder: input.substr(match[0].length) };
        }
        var a = mhchemParser.patterns.findObserveGroups(
          input,
          "",
          "$",
          "$",
          ""
        );
        if (a) {
          match = a.match_.match(
            /^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/
          );
          if (match) {
            return {
              match_: match[0],
              remainder: input.substr(match[0].length)
            };
          }
        }
        return null;
      },
      amount2: function(input) {
        return this["amount"](input);
      },
      "(KV letters),": /^(?:[A-Z][a-z]{0,2}|i)(?=,)/,
      formula$: function(input) {
        if (input.match(/^\([a-z]+\)$/)) {
          return null;
        }
        var match = input.match(
          /^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/
        );
        if (match) {
          return { match_: match[0], remainder: input.substr(match[0].length) };
        }
        return null;
      },
      uprightEntities: /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,
      "/": /^\s*(\/)\s*/,
      "//": /^\s*(\/\/)\s*/,
      "*": /^\s*[*.]\s*/
    },
    findObserveGroups: function(input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) {
      var _match = function(input2, pattern) {
        if (typeof pattern === "string") {
          if (input2.indexOf(pattern) !== 0) {
            return null;
          }
          return pattern;
        } else {
          var match2 = input2.match(pattern);
          if (!match2) {
            return null;
          }
          return match2[0];
        }
      };
      var _findObserveGroups = function(input2, i, endChars) {
        var braces = 0;
        while (i < input2.length) {
          var a = input2.charAt(i);
          var match2 = _match(input2.substr(i), endChars);
          if (match2 !== null && braces === 0) {
            return { endMatchBegin: i, endMatchEnd: i + match2.length };
          } else if (a === "{") {
            braces++;
          } else if (a === "}") {
            if (braces === 0) {
              throw [
                "ExtraCloseMissingOpen",
                "Extra close brace or missing open brace"
              ];
            } else {
              braces--;
            }
          }
          i++;
        }
        if (braces > 0) {
          return null;
        }
        return null;
      };
      var match = _match(input, begExcl);
      if (match === null) {
        return null;
      }
      input = input.substr(match.length);
      match = _match(input, begIncl);
      if (match === null) {
        return null;
      }
      var e = _findObserveGroups(input, match.length, endIncl || endExcl);
      if (e === null) {
        return null;
      }
      var match1 = input.substring(
        0,
        endIncl ? e.endMatchEnd : e.endMatchBegin
      );
      if (!(beg2Excl || beg2Incl)) {
        return {
          match_: match1,
          remainder: input.substr(e.endMatchEnd)
        };
      } else {
        var group2 = this.findObserveGroups(
          input.substr(e.endMatchEnd),
          beg2Excl,
          beg2Incl,
          end2Incl,
          end2Excl
        );
        if (group2 === null) {
          return null;
        }
        var matchRet = [match1, group2.match_];
        return {
          match_: combine ? matchRet.join("") : matchRet,
          remainder: group2.remainder
        };
      }
    },
    //
    // Matching function
    // e.g. match("a", input) will look for the regexp called "a" and see if it matches
    // returns null or {match_:"a", remainder:"bc"}
    //
    match_: function(m, input) {
      var pattern = mhchemParser.patterns.patterns[m];
      if (pattern === void 0) {
        throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"];
      } else if (typeof pattern === "function") {
        return mhchemParser.patterns.patterns[m](input);
      } else {
        var match = input.match(pattern);
        if (match) {
          var mm;
          if (match[2]) {
            mm = [match[1], match[2]];
          } else if (match[1]) {
            mm = match[1];
          } else {
            mm = match[0];
          }
          return { match_: mm, remainder: input.substr(match[0].length) };
        }
        return null;
      }
    }
  },
  //
  // Generic state machine actions
  //
  actions: {
    "a=": function(buffer, m) {
      buffer.a = (buffer.a || "") + m;
    },
    "b=": function(buffer, m) {
      buffer.b = (buffer.b || "") + m;
    },
    "p=": function(buffer, m) {
      buffer.p = (buffer.p || "") + m;
    },
    "o=": function(buffer, m) {
      buffer.o = (buffer.o || "") + m;
    },
    "q=": function(buffer, m) {
      buffer.q = (buffer.q || "") + m;
    },
    "d=": function(buffer, m) {
      buffer.d = (buffer.d || "") + m;
    },
    "rm=": function(buffer, m) {
      buffer.rm = (buffer.rm || "") + m;
    },
    "text=": function(buffer, m) {
      buffer.text_ = (buffer.text_ || "") + m;
    },
    insert: function(buffer, m, a) {
      return { type_: a };
    },
    "insert+p1": function(buffer, m, a) {
      return { type_: a, p1: m };
    },
    "insert+p1+p2": function(buffer, m, a) {
      return { type_: a, p1: m[0], p2: m[1] };
    },
    copy: function(buffer, m) {
      return m;
    },
    rm: function(buffer, m) {
      return { type_: "rm", p1: m || "" };
    },
    text: function(buffer, m) {
      return mhchemParser.go(m, "text");
    },
    "{text}": function(buffer, m) {
      var ret = ["{"];
      mhchemParser.concatArray(ret, mhchemParser.go(m, "text"));
      ret.push("}");
      return ret;
    },
    "tex-math": function(buffer, m) {
      return mhchemParser.go(m, "tex-math");
    },
    "tex-math tight": function(buffer, m) {
      return mhchemParser.go(m, "tex-math tight");
    },
    bond: function(buffer, m, k) {
      return { type_: "bond", kind_: k || m };
    },
    "color0-output": function(buffer, m) {
      return { type_: "color0", color: m[0] };
    },
    ce: function(buffer, m) {
      return mhchemParser.go(m);
    },
    "1/2": function(buffer, m) {
      var ret = [];
      if (m.match(/^[+\-]/)) {
        ret.push(m.substr(0, 1));
        m = m.substr(1);
      }
      var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/);
      n[1] = n[1].replace(/\$/g, "");
      ret.push({ type_: "frac", p1: n[1], p2: n[2] });
      if (n[3]) {
        n[3] = n[3].replace(/\$/g, "");
        ret.push({ type_: "tex-math", p1: n[3] });
      }
      return ret;
    },
    "9,9": function(buffer, m) {
      return mhchemParser.go(m, "9,9");
    }
  },
  //
  // createTransitions
  // convert  { 'letter': { 'state': { action_: 'output' } } }  to  { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] }
  // with expansion of 'a|b' to 'a' and 'b' (at 2 places)
  //
  createTransitions: function(o) {
    var pattern, state;
    var stateArray;
    var i;
    var transitions = {};
    for (pattern in o) {
      for (state in o[pattern]) {
        stateArray = state.split("|");
        o[pattern][state].stateArray = stateArray;
        for (i = 0; i < stateArray.length; i++) {
          transitions[stateArray[i]] = [];
        }
      }
    }
    for (pattern in o) {
      for (state in o[pattern]) {
        stateArray = o[pattern][state].stateArray || [];
        for (i = 0; i < stateArray.length; i++) {
          var p = o[pattern][state];
          if (p.action_) {
            p.action_ = [].concat(p.action_);
            for (var k = 0; k < p.action_.length; k++) {
              if (typeof p.action_[k] === "string") {
                p.action_[k] = { type_: p.action_[k] };
              }
            }
          } else {
            p.action_ = [];
          }
          var patternArray = pattern.split("|");
          for (var j = 0; j < patternArray.length; j++) {
            if (stateArray[i] === "*") {
              for (var t in transitions) {
                transitions[t].push({ pattern: patternArray[j], task: p });
              }
            } else {
              transitions[stateArray[i]].push({
                pattern: patternArray[j],
                task: p
              });
            }
          }
        }
      }
    }
    return transitions;
  },
  stateMachines: {}
};
mhchemParser.stateMachines = {
  //
  // \ce state machines
  //
  //#region ce
  ce: {
    // main parser
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": { action_: "output" }
      },
      else: {
        "0|1|2": {
          action_: "beginsWithBond=false",
          revisit: true,
          toContinue: true
        }
      },
      oxidation$: {
        0: { action_: "oxidation-output" }
      },
      CMT: {
        r: { action_: "rdt=", nextState: "rt" },
        rd: { action_: "rqt=", nextState: "rdt" }
      },
      arrowUpDown: {
        "0|1|2|as": {
          action_: ["sb=false", "output", "operator"],
          nextState: "1"
        }
      },
      uprightEntities: {
        "0|1|2": { action_: ["o=", "output"], nextState: "1" }
      },
      orbital: {
        "0|1|2|3": { action_: "o=", nextState: "o" }
      },
      "->": {
        "0|1|2|3": { action_: "r=", nextState: "r" },
        "a|as": { action_: ["output", "r="], nextState: "r" },
        "*": { action_: ["output", "r="], nextState: "r" }
      },
      "+": {
        o: { action_: "d= kv", nextState: "d" },
        "d|D": { action_: "d=", nextState: "d" },
        q: { action_: "d=", nextState: "qd" },
        "qd|qD": { action_: "d=", nextState: "qd" },
        dq: { action_: ["output", "d="], nextState: "d" },
        3: { action_: ["sb=false", "output", "operator"], nextState: "0" }
      },
      amount: {
        "0|2": { action_: "a=", nextState: "a" }
      },
      "pm-operator": {
        "0|1|2|a|as": {
          action_: [
            "sb=false",
            "output",
            { type_: "operator", option: "\\pm" }
          ],
          nextState: "0"
        }
      },
      operator: {
        "0|1|2|a|as": {
          action_: ["sb=false", "output", "operator"],
          nextState: "0"
        }
      },
      "-$": {
        "o|q": { action_: ["charge or bond", "output"], nextState: "qd" },
        d: { action_: "d=", nextState: "d" },
        D: {
          action_: ["output", { type_: "bond", option: "-" }],
          nextState: "3"
        },
        q: { action_: "d=", nextState: "qd" },
        qd: { action_: "d=", nextState: "qd" },
        "qD|dq": {
          action_: ["output", { type_: "bond", option: "-" }],
          nextState: "3"
        }
      },
      "-9": {
        "3|o": {
          action_: ["output", { type_: "insert", option: "hyphen" }],
          nextState: "3"
        }
      },
      "- orbital overlap": {
        o: {
          action_: ["output", { type_: "insert", option: "hyphen" }],
          nextState: "2"
        },
        d: {
          action_: ["output", { type_: "insert", option: "hyphen" }],
          nextState: "2"
        }
      },
      "-": {
        "0|1|2": {
          action_: [
            { type_: "output", option: 1 },
            "beginsWithBond=true",
            { type_: "bond", option: "-" }
          ],
          nextState: "3"
        },
        3: { action_: { type_: "bond", option: "-" } },
        a: {
          action_: ["output", { type_: "insert", option: "hyphen" }],
          nextState: "2"
        },
        as: {
          action_: [
            { type_: "output", option: 2 },
            { type_: "bond", option: "-" }
          ],
          nextState: "3"
        },
        b: { action_: "b=" },
        o: { action_: { type_: "- after o/d", option: false }, nextState: "2" },
        q: { action_: { type_: "- after o/d", option: false }, nextState: "2" },
        "d|qd|dq": {
          action_: { type_: "- after o/d", option: true },
          nextState: "2"
        },
        "D|qD|p": {
          action_: ["output", { type_: "bond", option: "-" }],
          nextState: "3"
        }
      },
      amount2: {
        "1|3": { action_: "a=", nextState: "a" }
      },
      letters: {
        "0|1|2|3|a|as|b|p|bp|o": { action_: "o=", nextState: "o" },
        "q|dq": { action_: ["output", "o="], nextState: "o" },
        "d|D|qd|qD": { action_: "o after d", nextState: "o" }
      },
      digits: {
        o: { action_: "q=", nextState: "q" },
        "d|D": { action_: "q=", nextState: "dq" },
        q: { action_: ["output", "o="], nextState: "o" },
        a: { action_: "o=", nextState: "o" }
      },
      "space A": {
        "b|p|bp": {}
      },
      space: {
        a: { nextState: "as" },
        0: { action_: "sb=false" },
        "1|2": { action_: "sb=true" },
        "r|rt|rd|rdt|rdq": { action_: "output", nextState: "0" },
        "*": { action_: ["output", "sb=true"], nextState: "1" }
      },
      "1st-level escape": {
        "1|2": {
          action_: [
            "output",
            { type_: "insert+p1", option: "1st-level escape" }
          ]
        },
        "*": {
          action_: [
            "output",
            { type_: "insert+p1", option: "1st-level escape" }
          ],
          nextState: "0"
        }
      },
      "[(...)]": {
        "r|rt": { action_: "rd=", nextState: "rd" },
        "rd|rdt": { action_: "rq=", nextState: "rdq" }
      },
      "...": {
        "o|d|D|dq|qd|qD": {
          action_: ["output", { type_: "bond", option: "..." }],
          nextState: "3"
        },
        "*": {
          action_: [
            { type_: "output", option: 1 },
            { type_: "insert", option: "ellipsis" }
          ],
          nextState: "1"
        }
      },
      ". |* ": {
        "*": {
          action_: ["output", { type_: "insert", option: "addition compound" }],
          nextState: "1"
        }
      },
      "state of aggregation $": {
        "*": { action_: ["output", "state of aggregation"], nextState: "1" }
      },
      "{[(": {
        "a|as|o": {
          action_: ["o=", "output", "parenthesisLevel++"],
          nextState: "2"
        },
        "0|1|2|3": {
          action_: ["o=", "output", "parenthesisLevel++"],
          nextState: "2"
        },
        "*": {
          action_: ["output", "o=", "output", "parenthesisLevel++"],
          nextState: "2"
        }
      },
      ")]}": {
        "0|1|2|3|b|p|bp|o": {
          action_: ["o=", "parenthesisLevel--"],
          nextState: "o"
        },
        "a|as|d|D|q|qd|qD|dq": {
          action_: ["output", "o=", "parenthesisLevel--"],
          nextState: "o"
        }
      },
      ", ": {
        "*": { action_: ["output", "comma"], nextState: "0" }
      },
      "^_": {
        // ^ and _ without a sensible argument
        "*": {}
      },
      "^{(...)}|^($...$)": {
        "0|1|2|as": { action_: "b=", nextState: "b" },
        p: { action_: "b=", nextState: "bp" },
        "3|o": { action_: "d= kv", nextState: "D" },
        q: { action_: "d=", nextState: "qD" },
        "d|D|qd|qD|dq": { action_: ["output", "d="], nextState: "D" }
      },
      "^a|^\\x{}{}|^\\x{}|^\\x|'": {
        "0|1|2|as": { action_: "b=", nextState: "b" },
        p: { action_: "b=", nextState: "bp" },
        "3|o": { action_: "d= kv", nextState: "d" },
        q: { action_: "d=", nextState: "qd" },
        "d|qd|D|qD": { action_: "d=" },
        dq: { action_: ["output", "d="], nextState: "d" }
      },
      "_{(state of aggregation)}$": {
        "d|D|q|qd|qD|dq": { action_: ["output", "q="], nextState: "q" }
      },
      "_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x": {
        "0|1|2|as": { action_: "p=", nextState: "p" },
        b: { action_: "p=", nextState: "bp" },
        "3|o": { action_: "q=", nextState: "q" },
        "d|D": { action_: "q=", nextState: "dq" },
        "q|qd|qD|dq": { action_: ["output", "q="], nextState: "q" }
      },
      "=<>": {
        "0|1|2|3|a|as|o|q|d|D|qd|qD|dq": {
          action_: [{ type_: "output", option: 2 }, "bond"],
          nextState: "3"
        }
      },
      "#": {
        "0|1|2|3|a|as|o": {
          action_: [
            { type_: "output", option: 2 },
            { type_: "bond", option: "#" }
          ],
          nextState: "3"
        }
      },
      "{}": {
        "*": { action_: { type_: "output", option: 1 }, nextState: "1" }
      },
      "{...}": {
        "0|1|2|3|a|as|b|p|bp": { action_: "o=", nextState: "o" },
        "o|d|D|q|qd|qD|dq": { action_: ["output", "o="], nextState: "o" }
      },
      "$...$": {
        a: { action_: "a=" },
        // 2$n$
        "0|1|2|3|as|b|p|bp|o": { action_: "o=", nextState: "o" },
        // not 'amount'
        "as|o": { action_: "o=" },
        "q|d|D|qd|qD|dq": { action_: ["output", "o="], nextState: "o" }
      },
      "\\bond{(...)}": {
        "*": {
          action_: [{ type_: "output", option: 2 }, "bond"],
          nextState: "3"
        }
      },
      "\\frac{(...)}": {
        "*": {
          action_: [{ type_: "output", option: 1 }, "frac-output"],
          nextState: "3"
        }
      },
      "\\overset{(...)}": {
        "*": {
          action_: [{ type_: "output", option: 2 }, "overset-output"],
          nextState: "3"
        }
      },
      "\\underset{(...)}": {
        "*": {
          action_: [{ type_: "output", option: 2 }, "underset-output"],
          nextState: "3"
        }
      },
      "\\underbrace{(...)}": {
        "*": {
          action_: [{ type_: "output", option: 2 }, "underbrace-output"],
          nextState: "3"
        }
      },
      "\\color{(...)}{(...)}1|\\color(...){(...)}2": {
        "*": {
          action_: [{ type_: "output", option: 2 }, "color-output"],
          nextState: "3"
        }
      },
      "\\color{(...)}0": {
        "*": { action_: [{ type_: "output", option: 2 }, "color0-output"] }
      },
      "\\ce{(...)}": {
        "*": {
          action_: [{ type_: "output", option: 2 }, "ce"],
          nextState: "3"
        }
      },
      "\\,": {
        "*": {
          action_: [{ type_: "output", option: 1 }, "copy"],
          nextState: "1"
        }
      },
      "\\x{}{}|\\x{}|\\x": {
        "0|1|2|3|a|as|b|p|bp|o|c0": {
          action_: ["o=", "output"],
          nextState: "3"
        },
        "*": { action_: ["output", "o=", "output"], nextState: "3" }
      },
      others: {
        "*": {
          action_: [{ type_: "output", option: 1 }, "copy"],
          nextState: "3"
        }
      },
      else2: {
        a: { action_: "a to o", nextState: "o", revisit: true },
        as: { action_: ["output", "sb=true"], nextState: "1", revisit: true },
        "r|rt|rd|rdt|rdq": {
          action_: ["output"],
          nextState: "0",
          revisit: true
        },
        "*": { action_: ["output", "copy"], nextState: "3" }
      }
    }),
    actions: {
      "o after d": function(buffer, m) {
        var ret;
        if ((buffer.d || "").match(/^[0-9]+$/)) {
          var tmp = buffer.d;
          buffer.d = void 0;
          ret = this["output"](buffer);
          buffer.b = tmp;
        } else {
          ret = this["output"](buffer);
        }
        mhchemParser.actions["o="](buffer, m);
        return ret;
      },
      "d= kv": function(buffer, m) {
        buffer.d = m;
        buffer.dType = "kv";
      },
      "charge or bond": function(buffer, m) {
        if (buffer["beginsWithBond"]) {
          var ret = [];
          mhchemParser.concatArray(ret, this["output"](buffer));
          mhchemParser.concatArray(
            ret,
            mhchemParser.actions["bond"](buffer, m, "-")
          );
          return ret;
        } else {
          buffer.d = m;
        }
      },
      "- after o/d": function(buffer, m, isAfterD) {
        var c1 = mhchemParser.patterns.match_("orbital", buffer.o || "");
        var c2 = mhchemParser.patterns.match_(
          "one lowercase greek letter $",
          buffer.o || ""
        );
        var c3 = mhchemParser.patterns.match_(
          "one lowercase latin letter $",
          buffer.o || ""
        );
        var c4 = mhchemParser.patterns.match_(
          "$one lowercase latin letter$ $",
          buffer.o || ""
        );
        var hyphenFollows = m === "-" && (c1 && c1.remainder === "" || c2 || c3 || c4);
        if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) {
          buffer.o = "$" + buffer.o + "$";
        }
        var ret = [];
        if (hyphenFollows) {
          mhchemParser.concatArray(ret, this["output"](buffer));
          ret.push({ type_: "hyphen" });
        } else {
          c1 = mhchemParser.patterns.match_("digits", buffer.d || "");
          if (isAfterD && c1 && c1.remainder === "") {
            mhchemParser.concatArray(
              ret,
              mhchemParser.actions["d="](buffer, m)
            );
            mhchemParser.concatArray(ret, this["output"](buffer));
          } else {
            mhchemParser.concatArray(ret, this["output"](buffer));
            mhchemParser.concatArray(
              ret,
              mhchemParser.actions["bond"](buffer, m, "-")
            );
          }
        }
        return ret;
      },
      "a to o": function(buffer) {
        buffer.o = buffer.a;
        buffer.a = void 0;
      },
      "sb=true": function(buffer) {
        buffer.sb = true;
      },
      "sb=false": function(buffer) {
        buffer.sb = false;
      },
      "beginsWithBond=true": function(buffer) {
        buffer["beginsWithBond"] = true;
      },
      "beginsWithBond=false": function(buffer) {
        buffer["beginsWithBond"] = false;
      },
      "parenthesisLevel++": function(buffer) {
        buffer["parenthesisLevel"]++;
      },
      "parenthesisLevel--": function(buffer) {
        buffer["parenthesisLevel"]--;
      },
      "state of aggregation": function(buffer, m) {
        return { type_: "state of aggregation", p1: mhchemParser.go(m, "o") };
      },
      comma: function(buffer, m) {
        var a = m.replace(/\s*$/, "");
        var withSpace = a !== m;
        if (withSpace && buffer["parenthesisLevel"] === 0) {
          return { type_: "comma enumeration L", p1: a };
        } else {
          return { type_: "comma enumeration M", p1: a };
        }
      },
      output: function(buffer, m, entityFollows) {
        var ret;
        if (!buffer.r) {
          ret = [];
          if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) ;
          else {
            if (buffer.sb) {
              ret.push({ type_: "entitySkip" });
            }
            if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows !== 2) {
              buffer.o = buffer.a;
              buffer.a = void 0;
            } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) {
              buffer.o = buffer.a;
              buffer.d = buffer.b;
              buffer.q = buffer.p;
              buffer.a = buffer.b = buffer.p = void 0;
            } else {
              if (buffer.o && buffer.dType === "kv" && mhchemParser.patterns.match_("d-oxidation$", buffer.d || "")) {
                buffer.dType = "oxidation";
              } else if (buffer.o && buffer.dType === "kv" && !buffer.q) {
                buffer.dType = void 0;
              }
            }
            ret.push({
              type_: "chemfive",
              a: mhchemParser.go(buffer.a, "a"),
              b: mhchemParser.go(buffer.b, "bd"),
              p: mhchemParser.go(buffer.p, "pq"),
              o: mhchemParser.go(buffer.o, "o"),
              q: mhchemParser.go(buffer.q, "pq"),
              d: mhchemParser.go(
                buffer.d,
                buffer.dType === "oxidation" ? "oxidation" : "bd"
              ),
              dType: buffer.dType
            });
          }
        } else {
          var rd;
          if (buffer.rdt === "M") {
            rd = mhchemParser.go(buffer.rd, "tex-math");
          } else if (buffer.rdt === "T") {
            rd = [{ type_: "text", p1: buffer.rd || "" }];
          } else {
            rd = mhchemParser.go(buffer.rd);
          }
          var rq;
          if (buffer.rqt === "M") {
            rq = mhchemParser.go(buffer.rq, "tex-math");
          } else if (buffer.rqt === "T") {
            rq = [{ type_: "text", p1: buffer.rq || "" }];
          } else {
            rq = mhchemParser.go(buffer.rq);
          }
          ret = {
            type_: "arrow",
            r: buffer.r,
            rd,
            rq
          };
        }
        for (var p in buffer) {
          if (p !== "parenthesisLevel" && p !== "beginsWithBond") {
            delete buffer[p];
          }
        }
        return ret;
      },
      "oxidation-output": function(buffer, m) {
        var ret = ["{"];
        mhchemParser.concatArray(ret, mhchemParser.go(m, "oxidation"));
        ret.push("}");
        return ret;
      },
      "frac-output": function(buffer, m) {
        return {
          type_: "frac-ce",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1])
        };
      },
      "overset-output": function(buffer, m) {
        return {
          type_: "overset",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1])
        };
      },
      "underset-output": function(buffer, m) {
        return {
          type_: "underset",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1])
        };
      },
      "underbrace-output": function(buffer, m) {
        return {
          type_: "underbrace",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1])
        };
      },
      "color-output": function(buffer, m) {
        return { type_: "color", color1: m[0], color2: mhchemParser.go(m[1]) };
      },
      "r=": function(buffer, m) {
        buffer.r = m;
      },
      "rdt=": function(buffer, m) {
        buffer.rdt = m;
      },
      "rd=": function(buffer, m) {
        buffer.rd = m;
      },
      "rqt=": function(buffer, m) {
        buffer.rqt = m;
      },
      "rq=": function(buffer, m) {
        buffer.rq = m;
      },
      operator: function(buffer, m, p1) {
        return { type_: "operator", kind_: p1 || m };
      }
    }
  },
  a: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {}
      },
      "1/2$": {
        0: { action_: "1/2" }
      },
      else: {
        0: { nextState: "1", revisit: true }
      },
      "$(...)$": {
        "*": { action_: "tex-math tight", nextState: "1" }
      },
      ",": {
        "*": { action_: { type_: "insert", option: "commaDecimal" } }
      },
      else2: {
        "*": { action_: "copy" }
      }
    }),
    actions: {}
  },
  o: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {}
      },
      "1/2$": {
        0: { action_: "1/2" }
      },
      else: {
        0: { nextState: "1", revisit: true }
      },
      letters: {
        "*": { action_: "rm" }
      },
      "\\ca": {
        "*": { action_: { type_: "insert", option: "circa" } }
      },
      "\\x{}{}|\\x{}|\\x": {
        "*": { action_: "copy" }
      },
      "${(...)}$|$(...)$": {
        "*": { action_: "tex-math" }
      },
      "{(...)}": {
        "*": { action_: "{text}" }
      },
      else2: {
        "*": { action_: "copy" }
      }
    }),
    actions: {}
  },
  text: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": { action_: "output" }
      },
      "{...}": {
        "*": { action_: "text=" }
      },
      "${(...)}$|$(...)$": {
        "*": { action_: "tex-math" }
      },
      "\\greek": {
        "*": { action_: ["output", "rm"] }
      },
      "\\,|\\x{}{}|\\x{}|\\x": {
        "*": { action_: ["output", "copy"] }
      },
      else: {
        "*": { action_: "text=" }
      }
    }),
    actions: {
      output: function(buffer) {
        if (buffer.text_) {
          var ret = { type_: "text", p1: buffer.text_ };
          for (var p in buffer) {
            delete buffer[p];
          }
          return ret;
        }
      }
    }
  },
  pq: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {}
      },
      "state of aggregation $": {
        "*": { action_: "state of aggregation" }
      },
      i$: {
        0: { nextState: "!f", revisit: true }
      },
      "(KV letters),": {
        0: { action_: "rm", nextState: "0" }
      },
      formula$: {
        0: { nextState: "f", revisit: true }
      },
      "1/2$": {
        0: { action_: "1/2" }
      },
      else: {
        0: { nextState: "!f", revisit: true }
      },
      "${(...)}$|$(...)$": {
        "*": { action_: "tex-math" }
      },
      "{(...)}": {
        "*": { action_: "text" }
      },
      "a-z": {
        f: { action_: "tex-math" }
      },
      letters: {
        "*": { action_: "rm" }
      },
      "-9.,9": {
        "*": { action_: "9,9" }
      },
      ",": {
        "*": { action_: { type_: "insert+p1", option: "comma enumeration S" } }
      },
      "\\color{(...)}{(...)}1|\\color(...){(...)}2": {
        "*": { action_: "color-output" }
      },
      "\\color{(...)}0": {
        "*": { action_: "color0-output" }
      },
      "\\ce{(...)}": {
        "*": { action_: "ce" }
      },
      "\\,|\\x{}{}|\\x{}|\\x": {
        "*": { action_: "copy" }
      },
      else2: {
        "*": { action_: "copy" }
      }
    }),
    actions: {
      "state of aggregation": function(buffer, m) {
        return {
          type_: "state of aggregation subscript",
          p1: mhchemParser.go(m, "o")
        };
      },
      "color-output": function(buffer, m) {
        return {
          type_: "color",
          color1: m[0],
          color2: mhchemParser.go(m[1], "pq")
        };
      }
    }
  },
  bd: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {}
      },
      x$: {
        0: { nextState: "!f", revisit: true }
      },
      formula$: {
        0: { nextState: "f", revisit: true }
      },
      else: {
        0: { nextState: "!f", revisit: true }
      },
      "-9.,9 no missing 0": {
        "*": { action_: "9,9" }
      },
      ".": {
        "*": { action_: { type_: "insert", option: "electron dot" } }
      },
      "a-z": {
        f: { action_: "tex-math" }
      },
      x: {
        "*": { action_: { type_: "insert", option: "KV x" } }
      },
      letters: {
        "*": { action_: "rm" }
      },
      "'": {
        "*": { action_: { type_: "insert", option: "prime" } }
      },
      "${(...)}$|$(...)$": {
        "*": { action_: "tex-math" }
      },
      "{(...)}": {
        "*": { action_: "text" }
      },
      "\\color{(...)}{(...)}1|\\color(...){(...)}2": {
        "*": { action_: "color-output" }
      },
      "\\color{(...)}0": {
        "*": { action_: "color0-output" }
      },
      "\\ce{(...)}": {
        "*": { action_: "ce" }
      },
      "\\,|\\x{}{}|\\x{}|\\x": {
        "*": { action_: "copy" }
      },
      else2: {
        "*": { action_: "copy" }
      }
    }),
    actions: {
      "color-output": function(buffer, m) {
        return {
          type_: "color",
          color1: m[0],
          color2: mhchemParser.go(m[1], "bd")
        };
      }
    }
  },
  oxidation: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {}
      },
      "roman numeral": {
        "*": { action_: "roman-numeral" }
      },
      "${(...)}$|$(...)$": {
        "*": { action_: "tex-math" }
      },
      else: {
        "*": { action_: "copy" }
      }
    }),
    actions: {
      "roman-numeral": function(buffer, m) {
        return { type_: "roman numeral", p1: m || "" };
      }
    }
  },
  "tex-math": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": { action_: "output" }
      },
      "\\ce{(...)}": {
        "*": { action_: ["output", "ce"] }
      },
      "{...}|\\,|\\x{}{}|\\x{}|\\x": {
        "*": { action_: "o=" }
      },
      else: {
        "*": { action_: "o=" }
      }
    }),
    actions: {
      output: function(buffer) {
        if (buffer.o) {
          var ret = { type_: "tex-math", p1: buffer.o };
          for (var p in buffer) {
            delete buffer[p];
          }
          return ret;
        }
      }
    }
  },
  "tex-math tight": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": { action_: "output" }
      },
      "\\ce{(...)}": {
        "*": { action_: ["output", "ce"] }
      },
      "{...}|\\,|\\x{}{}|\\x{}|\\x": {
        "*": { action_: "o=" }
      },
      "-|+": {
        "*": { action_: "tight operator" }
      },
      else: {
        "*": { action_: "o=" }
      }
    }),
    actions: {
      "tight operator": function(buffer, m) {
        buffer.o = (buffer.o || "") + "{" + m + "}";
      },
      output: function(buffer) {
        if (buffer.o) {
          var ret = { type_: "tex-math", p1: buffer.o };
          for (var p in buffer) {
            delete buffer[p];
          }
          return ret;
        }
      }
    }
  },
  "9,9": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {}
      },
      ",": {
        "*": { action_: "comma" }
      },
      else: {
        "*": { action_: "copy" }
      }
    }),
    actions: {
      comma: function() {
        return { type_: "commaDecimal" };
      }
    }
  },
  //#endregion
  //
  // \pu state machines
  //
  //#region pu
  pu: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": { action_: "output" }
      },
      space$: {
        "*": { action_: ["output", "space"] }
      },
      "{[(|)]}": {
        "0|a": { action_: "copy" }
      },
      "(-)(9)^(-9)": {
        0: { action_: "number^", nextState: "a" }
      },
      "(-)(9.,9)(e)(99)": {
        0: { action_: "enumber", nextState: "a" }
      },
      space: {
        "0|a": {}
      },
      "pm-operator": {
        "0|a": {
          action_: { type_: "operator", option: "\\pm" },
          nextState: "0"
        }
      },
      operator: {
        "0|a": { action_: "copy", nextState: "0" }
      },
      "//": {
        d: { action_: "o=", nextState: "/" }
      },
      "/": {
        d: { action_: "o=", nextState: "/" }
      },
      "{...}|else": {
        "0|d": { action_: "d=", nextState: "d" },
        a: { action_: ["space", "d="], nextState: "d" },
        "/|q": { action_: "q=", nextState: "q" }
      }
    }),
    actions: {
      enumber: function(buffer, m) {
        var ret = [];
        if (m[0] === "+-" || m[0] === "+/-") {
          ret.push("\\pm ");
        } else if (m[0]) {
          ret.push(m[0]);
        }
        if (m[1]) {
          mhchemParser.concatArray(ret, mhchemParser.go(m[1], "pu-9,9"));
          if (m[2]) {
            if (m[2].match(/[,.]/)) {
              mhchemParser.concatArray(ret, mhchemParser.go(m[2], "pu-9,9"));
            } else {
              ret.push(m[2]);
            }
          }
          m[3] = m[4] || m[3];
          if (m[3]) {
            m[3] = m[3].trim();
            if (m[3] === "e" || m[3].substr(0, 1) === "*") {
              ret.push({ type_: "cdot" });
            } else {
              ret.push({ type_: "times" });
            }
          }
        }
        if (m[3]) {
          ret.push("10^{" + m[5] + "}");
        }
        return ret;
      },
      "number^": function(buffer, m) {
        var ret = [];
        if (m[0] === "+-" || m[0] === "+/-") {
          ret.push("\\pm ");
        } else if (m[0]) {
          ret.push(m[0]);
        }
        mhchemParser.concatArray(ret, mhchemParser.go(m[1], "pu-9,9"));
        ret.push("^{" + m[2] + "}");
        return ret;
      },
      operator: function(buffer, m, p1) {
        return { type_: "operator", kind_: p1 || m };
      },
      space: function() {
        return { type_: "pu-space-1" };
      },
      output: function(buffer) {
        var ret;
        var md = mhchemParser.patterns.match_("{(...)}", buffer.d || "");
        if (md && md.remainder === "") {
          buffer.d = md.match_;
        }
        var mq = mhchemParser.patterns.match_("{(...)}", buffer.q || "");
        if (mq && mq.remainder === "") {
          buffer.q = mq.match_;
        }
        if (buffer.d) {
          buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
          buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
        }
        if (buffer.q) {
          buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
          buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
          var b5 = {
            d: mhchemParser.go(buffer.d, "pu"),
            q: mhchemParser.go(buffer.q, "pu")
          };
          if (buffer.o === "//") {
            ret = { type_: "pu-frac", p1: b5.d, p2: b5.q };
          } else {
            ret = b5.d;
            if (b5.d.length > 1 || b5.q.length > 1) {
              ret.push({ type_: " / " });
            } else {
              ret.push({ type_: "/" });
            }
            mhchemParser.concatArray(ret, b5.q);
          }
        } else {
          ret = mhchemParser.go(buffer.d, "pu-2");
        }
        for (var p in buffer) {
          delete buffer[p];
        }
        return ret;
      }
    }
  },
  "pu-2": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": { action_: "output" }
      },
      "*": {
        "*": { action_: ["output", "cdot"], nextState: "0" }
      },
      "\\x": {
        "*": { action_: "rm=" }
      },
      space: {
        "*": { action_: ["output", "space"], nextState: "0" }
      },
      "^{(...)}|^(-1)": {
        1: { action_: "^(-1)" }
      },
      "-9.,9": {
        0: { action_: "rm=", nextState: "0" },
        1: { action_: "^(-1)", nextState: "0" }
      },
      "{...}|else": {
        "*": { action_: "rm=", nextState: "1" }
      }
    }),
    actions: {
      cdot: function() {
        return { type_: "tight cdot" };
      },
      "^(-1)": function(buffer, m) {
        buffer.rm += "^{" + m + "}";
      },
      space: function() {
        return { type_: "pu-space-2" };
      },
      output: function(buffer) {
        var ret = [];
        if (buffer.rm) {
          var mrm = mhchemParser.patterns.match_("{(...)}", buffer.rm || "");
          if (mrm && mrm.remainder === "") {
            ret = mhchemParser.go(mrm.match_, "pu");
          } else {
            ret = { type_: "rm", p1: buffer.rm };
          }
        }
        for (var p in buffer) {
          delete buffer[p];
        }
        return ret;
      }
    }
  },
  "pu-9,9": {
    transitions: mhchemParser.createTransitions({
      empty: {
        0: { action_: "output-0" },
        o: { action_: "output-o" }
      },
      ",": {
        0: { action_: ["output-0", "comma"], nextState: "o" }
      },
      ".": {
        0: { action_: ["output-0", "copy"], nextState: "o" }
      },
      else: {
        "*": { action_: "text=" }
      }
    }),
    actions: {
      comma: function() {
        return { type_: "commaDecimal" };
      },
      "output-0": function(buffer) {
        var ret = [];
        buffer.text_ = buffer.text_ || "";
        if (buffer.text_.length > 4) {
          var a = buffer.text_.length % 3;
          if (a === 0) {
            a = 3;
          }
          for (var i = buffer.text_.length - 3; i > 0; i -= 3) {
            ret.push(buffer.text_.substr(i, 3));
            ret.push({ type_: "1000 separator" });
          }
          ret.push(buffer.text_.substr(0, a));
          ret.reverse();
        } else {
          ret.push(buffer.text_);
        }
        for (var p in buffer) {
          delete buffer[p];
        }
        return ret;
      },
      "output-o": function(buffer) {
        var ret = [];
        buffer.text_ = buffer.text_ || "";
        if (buffer.text_.length > 4) {
          var a = buffer.text_.length - 3;
          for (var i = 0; i < a; i += 3) {
            ret.push(buffer.text_.substr(i, 3));
            ret.push({ type_: "1000 separator" });
          }
          ret.push(buffer.text_.substr(i));
        } else {
          ret.push(buffer.text_);
        }
        for (var p in buffer) {
          delete buffer[p];
        }
        return ret;
      }
    }
  }
  //#endregion
};
var texify = {
  go: function(input, isInner) {
    if (!input) {
      return "";
    }
    var res = "";
    var cee = false;
    for (var i = 0; i < input.length; i++) {
      var inputi = input[i];
      if (typeof inputi === "string") {
        res += inputi;
      } else {
        res += texify._go2(inputi);
        if (inputi.type_ === "1st-level escape") {
          cee = true;
        }
      }
    }
    if (!isInner && !cee && res) {
      res = "{" + res + "}";
    }
    return res;
  },
  _goInner: function(input) {
    if (!input) {
      return input;
    }
    return texify.go(input, true);
  },
  _go2: function(buf) {
    var res;
    switch (buf.type_) {
      case "chemfive":
        res = "";
        var b5 = {
          a: texify._goInner(buf.a),
          b: texify._goInner(buf.b),
          p: texify._goInner(buf.p),
          o: texify._goInner(buf.o),
          q: texify._goInner(buf.q),
          d: texify._goInner(buf.d)
        };
        if (b5.a) {
          if (b5.a.match(/^[+\-]/)) {
            b5.a = "{" + b5.a + "}";
          }
          res += b5.a + "\\,";
        }
        if (b5.b || b5.p) {
          res += "{\\vphantom{X}}";
          res += "^{\\hphantom{" + (b5.b || "") + "}}_{\\hphantom{" + (b5.p || "") + "}}";
          res += "{\\vphantom{X}}";
          res += "^{\\smash[t]{\\vphantom{2}}\\mathllap{" + (b5.b || "") + "}}";
          res += "_{\\vphantom{2}\\mathllap{\\smash[t]{" + (b5.p || "") + "}}}";
        }
        if (b5.o) {
          if (b5.o.match(/^[+\-]/)) {
            b5.o = "{" + b5.o + "}";
          }
          res += b5.o;
        }
        if (buf.dType === "kv") {
          if (b5.d || b5.q) {
            res += "{\\vphantom{X}}";
          }
          if (b5.d) {
            res += "^{" + b5.d + "}";
          }
          if (b5.q) {
            res += "_{\\smash[t]{" + b5.q + "}}";
          }
        } else if (buf.dType === "oxidation") {
          if (b5.d) {
            res += "{\\vphantom{X}}";
            res += "^{" + b5.d + "}";
          }
          if (b5.q) {
            res += "{\\vphantom{X}}";
            res += "_{\\smash[t]{" + b5.q + "}}";
          }
        } else {
          if (b5.q) {
            res += "{\\vphantom{X}}";
            res += "_{\\smash[t]{" + b5.q + "}}";
          }
          if (b5.d) {
            res += "{\\vphantom{X}}";
            res += "^{" + b5.d + "}";
          }
        }
        break;
      case "rm":
        res = "\\mathrm{" + buf.p1 + "}";
        break;
      case "text":
        if (buf.p1.match(/[\^_]/)) {
          buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}");
          res = "\\mathrm{" + buf.p1 + "}";
        } else {
          res = "\\text{" + buf.p1 + "}";
        }
        break;
      case "roman numeral":
        res = "\\mathrm{" + buf.p1 + "}";
        break;
      case "state of aggregation":
        res = "\\mskip2mu " + texify._goInner(buf.p1);
        break;
      case "state of aggregation subscript":
        res = "\\mskip1mu " + texify._goInner(buf.p1);
        break;
      case "bond":
        res = texify._getBond(buf.kind_);
        if (!res) {
          throw [
            "MhchemErrorBond",
            "mhchem Error. Unknown bond type (" + buf.kind_ + ")"
          ];
        }
        break;
      case "frac":
        var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}";
        res = "\\mathchoice{\\textstyle" + c + "}{" + c + "}{" + c + "}{" + c + "}";
        break;
      case "pu-frac":
        var d = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
        res = "\\mathchoice{\\textstyle" + d + "}{" + d + "}{" + d + "}{" + d + "}";
        break;
      case "tex-math":
        res = buf.p1 + " ";
        break;
      case "frac-ce":
        res = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
        break;
      case "overset":
        res = "\\overset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
        break;
      case "underset":
        res = "\\underset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}";
        break;
      case "underbrace":
        res = "\\underbrace{" + texify._goInner(buf.p1) + "}_{" + texify._goInner(buf.p2) + "}";
        break;
      case "color":
        res = "{\\color{" + buf.color1 + "}{" + texify._goInner(buf.color2) + "}}";
        break;
      case "color0":
        res = "\\color{" + buf.color + "}";
        break;
      case "arrow":
        var b6 = {
          rd: texify._goInner(buf.rd),
          rq: texify._goInner(buf.rq)
        };
        var arrow = "\\x" + texify._getArrow(buf.r);
        if (b6.rq) {
          arrow += "[{" + b6.rq + "}]";
        }
        if (b6.rd) {
          arrow += "{" + b6.rd + "}";
        } else {
          arrow += "{}";
        }
        res = arrow;
        break;
      case "operator":
        res = texify._getOperator(buf.kind_);
        break;
      case "1st-level escape":
        res = buf.p1 + " ";
        break;
      case "space":
        res = " ";
        break;
      case "entitySkip":
        res = "~";
        break;
      case "pu-space-1":
        res = "~";
        break;
      case "pu-space-2":
        res = "\\mkern3mu ";
        break;
      case "1000 separator":
        res = "\\mkern2mu ";
        break;
      case "commaDecimal":
        res = "{,}";
        break;
      case "comma enumeration L":
        res = "{" + buf.p1 + "}\\mkern6mu ";
        break;
      case "comma enumeration M":
        res = "{" + buf.p1 + "}\\mkern3mu ";
        break;
      case "comma enumeration S":
        res = "{" + buf.p1 + "}\\mkern1mu ";
        break;
      case "hyphen":
        res = "\\text{-}";
        break;
      case "addition compound":
        res = "\\,{\\cdot}\\,";
        break;
      case "electron dot":
        res = "\\mkern1mu \\bullet\\mkern1mu ";
        break;
      case "KV x":
        res = "{\\times}";
        break;
      case "prime":
        res = "\\prime ";
        break;
      case "cdot":
        res = "\\cdot ";
        break;
      case "tight cdot":
        res = "\\mkern1mu{\\cdot}\\mkern1mu ";
        break;
      case "times":
        res = "\\times ";
        break;
      case "circa":
        res = "{\\sim}";
        break;
      case "^":
        res = "uparrow";
        break;
      case "v":
        res = "downarrow";
        break;
      case "ellipsis":
        res = "\\ldots ";
        break;
      case "/":
        res = "/";
        break;
      case " / ":
        res = "\\,/\\,";
        break;
      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
    }
    return res;
  },
  _getArrow: function(a) {
    switch (a) {
      case "->":
        return "rightarrow";
      case "→":
        return "rightarrow";
      case "⟶":
        return "rightarrow";
      case "<-":
        return "leftarrow";
      case "<->":
        return "leftrightarrow";
      case "<-->":
        return "rightleftarrows";
      case "<=>":
        return "rightleftharpoons";
      case "⇌":
        return "rightleftharpoons";
      case "<=>>":
        return "rightequilibrium";
      case "<<=>":
        return "leftequilibrium";
      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
    }
  },
  _getBond: function(a) {
    switch (a) {
      case "-":
        return "{-}";
      case "1":
        return "{-}";
      case "=":
        return "{=}";
      case "2":
        return "{=}";
      case "#":
        return "{\\equiv}";
      case "3":
        return "{\\equiv}";
      case "~":
        return "{\\tripledash}";
      case "~-":
        return "{\\mathrlap{\\raisebox{-.1em}{$-$}}\\raisebox{.1em}{$\\tripledash$}}";
      case "~=":
        return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";
      case "~--":
        return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";
      case "-~-":
        return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$-$}}\\tripledash}";
      case "...":
        return "{{\\cdot}{\\cdot}{\\cdot}}";
      case "....":
        return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}";
      case "->":
        return "{\\rightarrow}";
      case "<-":
        return "{\\leftarrow}";
      case "<":
        return "{<}";
      case ">":
        return "{>}";
      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
    }
  },
  _getOperator: function(a) {
    switch (a) {
      case "+":
        return " {}+{} ";
      case "-":
        return " {}-{} ";
      case "=":
        return " {}={} ";
      case "<":
        return " {}<{} ";
      case ">":
        return " {}>{} ";
      case "<<":
        return " {}\\ll{} ";
      case ">>":
        return " {}\\gg{} ";
      case "\\pm":
        return " {}\\pm{} ";
      case "\\approx":
        return " {}\\approx{} ";
      case "$\\approx$":
        return " {}\\approx{} ";
      case "v":
        return " \\downarrow{} ";
      case "(v)":
        return " \\downarrow{} ";
      case "^":
        return " \\uparrow{} ";
      case "(^)":
        return " \\uparrow{} ";
      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
    }
  }
};