import katex from "../katex.mjs"; /* eslint-disable */ /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /************************************************************* * * KaTeX mhchem.js * * This file implements a KaTeX version of mhchem version 3.3.0. * It is adapted from MathJax/extensions/TeX/mhchem.js * It differs from the MathJax version as follows: * 1. The interface is changed so that it can be called from KaTeX, not MathJax. * 2. \rlap and \llap are replaced with \mathrlap and \mathllap. * 3. Four lines of code are edited in order to use \raisebox instead of \raise. * 4. The reaction arrow code is simplified. All reaction arrows are rendered * using KaTeX extensible arrows instead of building non-extensible arrows. * 5. \tripledash vertical alignment is slightly adjusted. * * This code, as other KaTeX code, is released under the MIT license. * * /************************************************************* * * MathJax/extensions/TeX/mhchem.js * * Implements the \ce command for handling chemical formulas * from the mhchem LaTeX package. * * --------------------------------------------------------------------- * * Copyright (c) 2011-2015 The MathJax Consortium * Copyright (c) 2015-2018 Martin Hensel * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Coding Style // - use '' for identifiers that can by minified/uglified // - use "" for strings that need to stay untouched // version: "3.3.0" for MathJax and KaTeX // Add \ce, \pu, and \tripledash to the KaTeX macros. katex.__defineMacro("\\ce", function (context) { return chemParse(context.consumeArgs(1)[0], "ce"); }); katex.__defineMacro("\\pu", function (context) { return chemParse(context.consumeArgs(1)[0], "pu"); }); // Needed for \bond for the ~ forms // Raise by 2.56mu, not 2mu. We're raising a hyphen-minus, U+002D, not // a mathematical minus, U+2212. So we need that extra 0.56. katex.__defineMacro( "\\tripledash", "{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu" + "\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}", ); // This is the main function for handing the \ce and \pu commands. // It takes the argument to \ce or \pu and returns the corresponding TeX string. // var chemParse = function chemParse(tokens, stateMachine) { // Recreate the argument string from KaTeX's array of tokens. var str = ""; var expectedLoc = tokens[tokens.length - 1].loc.start; for (var i = tokens.length - 1; i >= 0; i--) { if (tokens[i].loc.start > expectedLoc) { // context.consumeArgs has eaten a space. 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; }; // // Core parser for mhchem syntax (recursive) // /** @type {MhchemParser} */ var mhchemParser = { // // Parses mchem \ce syntax // // Call like // go("H2O"); // go: function go(input, stateMachine) { if (!input) { return []; } if (stateMachine === undefined) { stateMachine = "ce"; } var state = "0"; // // String buffers for parsing: // // buffer.a == amount // buffer.o == element // buffer.b == left-side superscript // buffer.p == left-side subscript // buffer.q == right-side subscript // buffer.d == right-side superscript // // buffer.r == arrow // buffer.rdt == arrow, script above, type // buffer.rd == arrow, script above, content // buffer.rqt == arrow, script below, type // buffer.rq == arrow, script below, content // // buffer.text_ // buffer.rm // etc. // // buffer.parenthesisLevel == int, starting at 0 // buffer.sb == bool, space before // buffer.beginsWithBond == bool // // These letters are also used as state names. // // Other states: // 0 == begin of main part (arrow/operator unlikely) // 1 == next entity // 2 == next entity (arrow/operator unlikely) // 3 == next atom // c == macro // /** @type {Buffer} */ var buffer = {}; buffer["parenthesisLevel"] = 0; input = input.replace(/\n/g, " "); input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-"); input = input.replace(/[\u2026]/g, "..."); // // Looks through mhchemParser.transitions, to execute a matching action // (recursive) // var lastInput; var watchdog = 10; /** @type {ParserOutput[]} */ var output = []; while (true) { if (lastInput !== input) { watchdog = 10; lastInput = input; } else { watchdog--; } // // Find actions in transition table // 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) { // // Execute actions // var task = t[i].task; for (var iA = 0; iA < task.action_.length; iA++) { var o; // // Find and execute action // 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_ + ")", ]; // Trying to use non-existing action } // // Add output // mhchemParser.concatArray(output, o); } // // Set next state, // Shorten input, // Continue with next character // (= apply only one transition per position) // state = task.nextState || state; if (input.length > 0) { if (!task.revisit) { input = matches.remainder; } if (!task.toContinue) { break iterateTransitions; } } else { return output; } } } // // Prevent infinite loop // if (watchdog <= 0) { throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character } } }, concatArray: function concatArray(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 e99(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 stateOfAggregation$(input) { // ... or crystal system var a = mhchemParser.patterns.findObserveGroups( input, "", /^\([a-z]{1,3}(?=[\),])/, ")", "", ); // (aq), (aq,$\infty$), (aq, sat) if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) { return a; } // AND end of 'phrase' var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/); // OR crystal system ($o$) (\ca$c$) 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 x(input) { return mhchemParser.patterns.findObserveGroups( input, "^", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true, ); }, "^\\x{}": function x(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 _X(input) { return mhchemParser.patterns.findObserveGroups( input, "_", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true, ); }, "_\\x{}": function _X(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 bond(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 x(input) { return mhchemParser.patterns.findObserveGroups( input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true, ); }, "\\x{}": function x(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 frac(input) { return mhchemParser.patterns.findObserveGroups( input, "\\frac{", "", "", "}", "{", "", "", "}", ); }, "\\overset{(...)}": function overset(input) { return mhchemParser.patterns.findObserveGroups( input, "\\overset{", "", "", "}", "{", "", "", "}", ); }, "\\underset{(...)}": function underset(input) { return mhchemParser.patterns.findObserveGroups( input, "\\underset{", "", "", "}", "{", "", "", "}", ); }, "\\underbrace{(...)}": function underbrace(input) { return mhchemParser.patterns.findObserveGroups( input, "\\underbrace{", "", "", "}_", "{", "", "", "}", ); }, "\\color{(...)}0": function color0(input) { return mhchemParser.patterns.findObserveGroups( input, "\\color{", "", "", "}", ); }, "\\color{(...)}{(...)}1": function color1(input) { return mhchemParser.patterns.findObserveGroups( input, "\\color{", "", "", "}", "{", "", "", "}", ); }, "\\color(...){(...)}2": function color2(input) { return mhchemParser.patterns.findObserveGroups( input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}", ); }, "\\ce{(...)}": function ce(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 amount(input) { var match; // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing 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) { // e.g. $2n-1$, $-$ 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 amount2(input) { return this["amount"](input); }, "(KV letters),": /^(?:[A-Z][a-z]{0,2}|i)(?=,)/, formula$: function formula$(input) { if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula 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 findObserveGroups( input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine, ) { /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */ var _match = function _match(input, pattern) { if (typeof pattern === "string") { if (input.indexOf(pattern) !== 0) { return null; } return pattern; } else { var match = input.match(pattern); if (!match) { return null; } return match[0]; } }; /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */ var _findObserveGroups = function _findObserveGroups(input, i, endChars) { var braces = 0; while (i < input.length) { var a = input.charAt(i); var match = _match(input.substr(i), endChars); if (match !== null && braces === 0) { return { endMatchBegin: i, endMatchEnd: i + match.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; } /** @type {string[]} */ 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 match_(m, input) { var pattern = mhchemParser.patterns.patterns[m]; if (pattern === undefined) { throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern } else if (typeof pattern === "function") { return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser } else { // RegExp 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 a(buffer, m) { buffer.a = (buffer.a || "") + m; }, "b=": function b(buffer, m) { buffer.b = (buffer.b || "") + m; }, "p=": function p(buffer, m) { buffer.p = (buffer.p || "") + m; }, "o=": function o(buffer, m) { buffer.o = (buffer.o || "") + m; }, "q=": function q(buffer, m) { buffer.q = (buffer.q || "") + m; }, "d=": function d(buffer, m) { buffer.d = (buffer.d || "") + m; }, "rm=": function rm(buffer, m) { buffer.rm = (buffer.rm || "") + m; }, "text=": function text(buffer, m) { buffer.text_ = (buffer.text_ || "") + m; }, insert: function insert(buffer, m, a) { return { type_: a, }; }, "insert+p1": function insertP1(buffer, m, a) { return { type_: a, p1: m, }; }, "insert+p1+p2": function insertP1P2(buffer, m, a) { return { type_: a, p1: m[0], p2: m[1], }; }, copy: function copy(buffer, m) { return m; }, rm: function rm(buffer, m) { return { type_: "rm", p1: m || "", }; }, text: function text(buffer, m) { return mhchemParser.go(m, "text"); }, "{text}": function text(buffer, m) { var ret = ["{"]; mhchemParser.concatArray(ret, mhchemParser.go(m, "text")); ret.push("}"); return ret; }, "tex-math": function texMath(buffer, m) { return mhchemParser.go(m, "tex-math"); }, "tex-math tight": function texMathTight(buffer, m) { return mhchemParser.go(m, "tex-math tight"); }, bond: function bond(buffer, m, k) { return { type_: "bond", kind_: k || m, }; }, "color0-output": function color0Output(buffer, m) { return { type_: "color0", color: m[0], }; }, ce: function ce(buffer, m) { return mhchemParser.go(m); }, "1/2": function _(buffer, m) { /** @type {ParserOutput[]} */ 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 createTransitions(o) { var pattern, state; /** @type {string[]} */ var stateArray; var i; // // 1. Collect all states // /** @type {Transitions} */ 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]] = []; } } } // // 2. Fill states // for (pattern in o) { for (state in o[pattern]) { stateArray = o[pattern][state].stateArray || []; for (i = 0; i < stateArray.length; i++) { // // 2a. Normalize actions into array: 'text=' ==> [{type_:'text='}] // (Note to myself: Resolving the function here would be problematic. It would need .bind (for *this*) and currying (for *option*).) // /** @type {any} */ 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_ = []; } // // 2.b Multi-insert // var patternArray = pattern.split("|"); for (var j = 0; j < patternArray.length; j++) { if (stateArray[i] === "*") { // insert into all 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: {}, }; // // Definition of state machines // 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 oAfterD(buffer, m) { var ret; if ((buffer.d || "").match(/^[0-9]+$/)) { var tmp = buffer.d; buffer.d = undefined; ret = this["output"](buffer); buffer.b = tmp; } else { ret = this["output"](buffer); } mhchemParser.actions["o="](buffer, m); return ret; }, "d= kv": function dKv(buffer, m) { buffer.d = m; buffer.dType = "kv"; }, "charge or bond": function chargeOrBond(buffer, m) { if (buffer["beginsWithBond"]) { /** @type {ParserOutput[]} */ 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 afterOD(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 + "$"; } /** @type {ParserOutput[]} */ 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 aToO(buffer) { buffer.o = buffer.a; buffer.a = undefined; }, "sb=true": function sbTrue(buffer) { buffer.sb = true; }, "sb=false": function sbFalse(buffer) { buffer.sb = false; }, "beginsWithBond=true": function beginsWithBondTrue(buffer) { buffer["beginsWithBond"] = true; }, "beginsWithBond=false": function beginsWithBondFalse(buffer) { buffer["beginsWithBond"] = false; }, "parenthesisLevel++": function parenthesisLevel(buffer) { buffer["parenthesisLevel"]++; }, "parenthesisLevel--": function parenthesisLevel(buffer) { buffer["parenthesisLevel"]--; }, "state of aggregation": function stateOfAggregation(buffer, m) { return { type_: "state of aggregation", p1: mhchemParser.go(m, "o"), }; }, comma: function comma(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 output(buffer, m, entityFollows) { // entityFollows: // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb) // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1) // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as) /** @type {ParserOutput | ParserOutput[]} */ 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 = undefined; } 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 = undefined; } 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 = undefined; } } 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 { // r /** @type {ParserOutput[]} */ 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); } /** @type {ParserOutput[]} */ 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: rd, rq: rq, }; } for (var p in buffer) { if (p !== "parenthesisLevel" && p !== "beginsWithBond") { delete buffer[p]; } } return ret; }, "oxidation-output": function oxidationOutput(buffer, m) { var ret = ["{"]; mhchemParser.concatArray(ret, mhchemParser.go(m, "oxidation")); ret.push("}"); return ret; }, "frac-output": function fracOutput(buffer, m) { return { type_: "frac-ce", p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, "overset-output": function oversetOutput(buffer, m) { return { type_: "overset", p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, "underset-output": function undersetOutput(buffer, m) { return { type_: "underset", p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, "underbrace-output": function underbraceOutput(buffer, m) { return { type_: "underbrace", p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]), }; }, "color-output": function colorOutput(buffer, m) { return { type_: "color", color1: m[0], color2: mhchemParser.go(m[1]), }; }, "r=": function r(buffer, m) { buffer.r = m; }, "rdt=": function rdt(buffer, m) { buffer.rdt = m; }, "rd=": function rd(buffer, m) { buffer.rd = m; }, "rqt=": function rqt(buffer, m) { buffer.rqt = m; }, "rq=": function rq(buffer, m) { buffer.rq = m; }, operator: function operator(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 output(buffer) { if (buffer.text_) { /** @type {ParserOutput} */ 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 stateOfAggregation(buffer, m) { return { type_: "state of aggregation subscript", p1: mhchemParser.go(m, "o"), }; }, "color-output": function colorOutput(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 colorOutput(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 romanNumeral(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 output(buffer) { if (buffer.o) { /** @type {ParserOutput} */ 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 tightOperator(buffer, m) { buffer.o = (buffer.o || "") + "{" + m + "}"; }, output: function output(buffer) { if (buffer.o) { /** @type {ParserOutput} */ 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 comma() { 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 enumber(buffer, m) { /** @type {ParserOutput[]} */ 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 number(buffer, m) { /** @type {ParserOutput[]} */ 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 operator(buffer, m, p1) { return { type_: "operator", kind_: p1 || m, }; }, space: function space() { return { type_: "pu-space-1", }; }, output: function output(buffer) { /** @type {ParserOutput | ParserOutput[]} */ 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) { // fraction 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 { // no fraction 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 cdot() { return { type_: "tight cdot", }; }, "^(-1)": function _(buffer, m) { buffer.rm += "^{" + m + "}"; }, space: function space() { return { type_: "pu-space-2", }; }, output: function output(buffer) { /** @type {ParserOutput | ParserOutput[]} */ 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 comma() { return { type_: "commaDecimal", }; }, "output-0": function output0(buffer) { /** @type {ParserOutput[]} */ 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 outputO(buffer) { /** @type {ParserOutput[]} */ 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 }, }; // // texify: Take MhchemParser output and convert it to TeX // /** @type {Texify} */ var texify = { go: function go(input, isInner) { // (recursive, max 4 levels) 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 _goInner(input) { if (!input) { return input; } return texify.go(input, true); }, _go2: function _go2(buf) { /** @type {undefined | string} */ 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), }; // // a // if (b5.a) { if (b5.a.match(/^[+\-]/)) { b5.a = "{" + b5.a + "}"; } res += b5.a + "\\,"; } // // b and p // 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 || "") + "}}}"; } // // o // if (b5.o) { if (b5.o.match(/^[+\-]/)) { b5.o = "{" + b5.o + "}"; } res += b5.o; } // // q and d // 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 + " "; // &, \\\\, \\hlin 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."]; // Missing texify rule or unknown MhchemParser output } return res; }, _getArrow: function _getArrow(a) { switch (a) { case "->": return "rightarrow"; case "\u2192": return "rightarrow"; case "\u27F6": return "rightarrow"; case "<-": return "leftarrow"; case "<->": return "leftrightarrow"; case "<-->": return "rightleftarrows"; case "<=>": return "rightleftharpoons"; case "\u21CC": return "rightleftharpoons"; case "<=>>": return "rightequilibrium"; case "<<=>": return "leftequilibrium"; default: throw ["MhchemBugT", "mhchem bug T. Please report."]; } }, _getBond: function _getBond(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 _getOperator(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."]; } }, }; //