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."]; } } }; //