nus-mtp/sashimi-note

View on GitHub
sashimi-webapp/src/logic/documentPackager/plugins/ASCIIMathTeXImg.js

Summary

Maintainability
F
2 wks
Test Coverage
/*
ASCIIMathTeXImg.js
Based on ASCIIMathML, Version 1.4.7 Aug 30, 2005, (c) Peter Jipsen http://www.chapman.edu/~jipsen
Modified with TeX conversion for IMG rendering Sept 6, 2006 (c) David Lippman http://www.pierce.ctc.edu/dlippman
  Updated to match ver 2.2 Mar 3, 2014
  Latest at https://github.com/mathjax/asciimathml

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/* eslint max-len:0 */

const AMTcgiloc = '';    // set to the URL of your LaTex renderer
// const noMathRender = false;

const config = {
  translateOnLoad: false,    // true to autotranslate
  mathcolor: '',             // defaults to back, or specify any other color
  displaystyle: true,         // puts limits above and below large operators
  showasciiformulaonhover: true, // helps students learn ASCIIMath
  decimalsign: ',',           // change to "," if you like, beware of `(1,2)`!
  AMdelimiter1: '`',
  AMescape1: '\\\\`', // can use other characters
  AMusedelimiter2: false,    // whether to use second delimiter below
  AMdelimiter2: '$',
  AMescape2: '\\\\\\$',
  AMdelimiter2regexp: '\\$',
  AMdocumentId: 'wikitext',   // PmWiki element containing math (default=body)
  doubleblankmathdelimiter: false // if true,  x+1  is equal to `x+1`
};                                // for IE this works only in <!--   -->

// token types
const CONST = 0;
const UNARY = 1;
const BINARY = 2;
const INFIX = 3;
const LEFTBRACKET = 4;
const RIGHTBRACKET = 5;
const SPACE = 6;
const UNDEROVER = 7;
const DEFINITION = 8;
const LEFTRIGHT = 9;
const TEXT = 10;

const AMsqrt = { input: 'sqrt', tag: 'msqrt', output: 'sqrt', tex: null, ttype: UNARY };
const AMroot = { input: 'root', tag: 'mroot', output: 'root', tex: null, ttype: BINARY };
const AMfrac = { input: 'frac', tag: 'mfrac', output: '/', tex: null, ttype: BINARY };
const AMdiv = { input: '/', tag: 'mfrac', output: '/', tex: null, ttype: INFIX };
const AMover = { input: 'stackrel', tag: 'mover', output: 'stackrel', tex: null, ttype: BINARY };
const AMsub = { input: '_', tag: 'msub', output: '_', tex: null, ttype: INFIX };
const AMsup = { input: '^', tag: 'msup', output: '^', tex: null, ttype: INFIX };
const AMtext = { input: 'text', tag: 'mtext', output: 'text', tex: null, ttype: TEXT };
const AMmbox = { input: 'mbox', tag: 'mtext', output: 'mbox', tex: null, ttype: TEXT };
const AMquote = { input: '"', tag: 'mtext', output: 'mbox', tex: null, ttype: TEXT };

const AMsymbols = [
// some greek symbols
{ input: 'alpha', tag: 'mi', output: '\u03B1', tex: null, ttype: CONST },
{ input: 'beta', tag: 'mi', output: '\u03B2', tex: null, ttype: CONST },
{ input: 'chi', tag: 'mi', output: '\u03C7', tex: null, ttype: CONST },
{ input: 'delta', tag: 'mi', output: '\u03B4', tex: null, ttype: CONST },
{ input: 'Delta', tag: 'mo', output: '\u0394', tex: null, ttype: CONST },
{ input: 'epsi', tag: 'mi', output: '\u03B5', tex: 'epsilon', ttype: CONST },
{ input: 'varepsilon', tag: 'mi', output: '\u025B', tex: null, ttype: CONST },
{ input: 'eta', tag: 'mi', output: '\u03B7', tex: null, ttype: CONST },
{ input: 'gamma', tag: 'mi', output: '\u03B3', tex: null, ttype: CONST },
{ input: 'Gamma', tag: 'mo', output: '\u0393', tex: null, ttype: CONST },
{ input: 'iota', tag: 'mi', output: '\u03B9', tex: null, ttype: CONST },
{ input: 'kappa', tag: 'mi', output: '\u03BA', tex: null, ttype: CONST },
{ input: 'lambda', tag: 'mi', output: '\u03BB', tex: null, ttype: CONST },
{ input: 'Lambda', tag: 'mo', output: '\u039B', tex: null, ttype: CONST },
{ input: 'lamda', tag: 'mi', output: 'lambda', tex: null, ttype: DEFINITION },
{ input: 'Lamda', tag: 'mi', output: 'Lambda', tex: null, ttype: DEFINITION },
{ input: 'mu', tag: 'mi', output: '\u03BC', tex: null, ttype: CONST },
{ input: 'nu', tag: 'mi', output: '\u03BD', tex: null, ttype: CONST },
{ input: 'omega', tag: 'mi', output: '\u03C9', tex: null, ttype: CONST },
{ input: 'Omega', tag: 'mo', output: '\u03A9', tex: null, ttype: CONST },
{ input: 'phi', tag: 'mi', output: '\u03C6', tex: null, ttype: CONST },
{ input: 'varphi', tag: 'mi', output: '\u03D5', tex: null, ttype: CONST },
{ input: 'Phi', tag: 'mo', output: '\u03A6', tex: null, ttype: CONST },
{ input: 'pi', tag: 'mi', output: '\u03C0', tex: null, ttype: CONST },
{ input: 'Pi', tag: 'mo', output: '\u03A0', tex: null, ttype: CONST },
{ input: 'psi', tag: 'mi', output: '\u03C8', tex: null, ttype: CONST },
{ input: 'Psi', tag: 'mi', output: '\u03A8', tex: null, ttype: CONST },
{ input: 'rho', tag: 'mi', output: '\u03C1', tex: null, ttype: CONST },
{ input: 'sigma', tag: 'mi', output: '\u03C3', tex: null, ttype: CONST },
{ input: 'Sigma', tag: 'mo', output: '\u03A3', tex: null, ttype: CONST },
{ input: 'tau', tag: 'mi', output: '\u03C4', tex: null, ttype: CONST },
{ input: 'theta', tag: 'mi', output: '\u03B8', tex: null, ttype: CONST },
{ input: 'vartheta', tag: 'mi', output: '\u03D1', tex: null, ttype: CONST },
{ input: 'Theta', tag: 'mo', output: '\u0398', tex: null, ttype: CONST },
{ input: 'upsilon', tag: 'mi', output: '\u03C5', tex: null, ttype: CONST },
{ input: 'xi', tag: 'mi', output: '\u03BE', tex: null, ttype: CONST },
{ input: 'Xi', tag: 'mo', output: '\u039E', tex: null, ttype: CONST },
{ input: 'zeta', tag: 'mi', output: '\u03B6', tex: null, ttype: CONST },

// binary operation symbols
{ input: '*', tag: 'mo', output: '\u22C5', tex: 'cdot', ttype: CONST },
{ input: '**', tag: 'mo', output: '\u2217', tex: 'ast', ttype: CONST },
{ input: '***', tag: 'mo', output: '\u22C6', tex: 'star', ttype: CONST },
{ input: '//', tag: 'mo', output: '/', tex: '/', ttype: CONST, val: true, notexcopy: true },
{ input: '\\\\', tag: 'mo', output: '\\', tex: 'backslash', ttype: CONST },
{ input: 'setminus', tag: 'mo', output: '\\', tex: null, ttype: CONST },
{ input: 'xx', tag: 'mo', output: '\u00D7', tex: 'times', ttype: CONST },
{ input: '|><', tag: 'mo', output: '\u22C9', tex: 'ltimes', ttype: CONST },
{ input: '><|', tag: 'mo', output: '\u22CA', tex: 'rtimes', ttype: CONST },
{ input: '|><|', tag: 'mo', output: '\u22C8', tex: 'bowtie', ttype: CONST },
{ input: '-:', tag: 'mo', output: '\u00F7', tex: 'div', ttype: CONST },
{ input: 'divide', tag: 'mo', output: '-:', tex: null, ttype: DEFINITION },
{ input: '@', tag: 'mo', output: '\u2218', tex: 'circ', ttype: CONST },
{ input: 'o+', tag: 'mo', output: '\u2295', tex: 'oplus', ttype: CONST },
{ input: 'ox', tag: 'mo', output: '\u2297', tex: 'otimes', ttype: CONST },
{ input: 'o.', tag: 'mo', output: '\u2299', tex: 'odot', ttype: CONST },
{ input: 'sum', tag: 'mo', output: '\u2211', tex: null, ttype: UNDEROVER },
{ input: 'prod', tag: 'mo', output: '\u220F', tex: null, ttype: UNDEROVER },
{ input: '^^', tag: 'mo', output: '\u2227', tex: 'wedge', ttype: CONST },
{ input: '^^^', tag: 'mo', output: '\u22C0', tex: 'bigwedge', ttype: UNDEROVER },
{ input: 'vv', tag: 'mo', output: '\u2228', tex: 'vee', ttype: CONST },
{ input: 'vvv', tag: 'mo', output: '\u22C1', tex: 'bigvee', ttype: UNDEROVER },
{ input: 'nn', tag: 'mo', output: '\u2229', tex: 'cap', ttype: CONST },
{ input: 'nnn', tag: 'mo', output: '\u22C2', tex: 'bigcap', ttype: UNDEROVER },
{ input: 'uu', tag: 'mo', output: '\u222A', tex: 'cup', ttype: CONST },
{ input: 'uuu', tag: 'mo', output: '\u22C3', tex: 'bigcup', ttype: UNDEROVER },
{ input: 'overset', tag: 'mover', output: 'stackrel', tex: null, ttype: BINARY },
{ input: 'underset', tag: 'munder', output: 'stackrel', tex: null, ttype: BINARY },

// binary relation symbols
{ input: '!=', tag: 'mo', output: '\u2260', tex: 'ne', ttype: CONST },
{ input: ':=', tag: 'mo', output: ':=', tex: null, ttype: CONST },
{ input: 'lt', tag: 'mo', output: '<', tex: null, ttype: CONST },
{ input: 'gt', tag: 'mo', output: '>', tex: null, ttype: CONST },
{ input: '<=', tag: 'mo', output: '\u2264', tex: 'le', ttype: CONST },
{ input: 'lt=', tag: 'mo', output: '\u2264', tex: 'leq', ttype: CONST },
{ input: 'gt=', tag: 'mo', output: '\u2265', tex: 'geq', ttype: CONST },
{ input: '>=', tag: 'mo', output: '\u2265', tex: 'ge', ttype: CONST },
{ input: '-<', tag: 'mo', output: '\u227A', tex: 'prec', ttype: CONST },
{ input: '-lt', tag: 'mo', output: '\u227A', tex: null, ttype: CONST },
{ input: '>-', tag: 'mo', output: '\u227B', tex: 'succ', ttype: CONST },
{ input: '-<=', tag: 'mo', output: '\u2AAF', tex: 'preceq', ttype: CONST },
{ input: '>-=', tag: 'mo', output: '\u2AB0', tex: 'succeq', ttype: CONST },
{ input: 'in', tag: 'mo', output: '\u2208', tex: null, ttype: CONST },
{ input: '!in', tag: 'mo', output: '\u2209', tex: 'notin', ttype: CONST },
{ input: 'sub', tag: 'mo', output: '\u2282', tex: 'subset', ttype: CONST },
{ input: 'sup', tag: 'mo', output: '\u2283', tex: 'supset', ttype: CONST },
{ input: 'sube', tag: 'mo', output: '\u2286', tex: 'subseteq', ttype: CONST },
{ input: 'supe', tag: 'mo', output: '\u2287', tex: 'supseteq', ttype: CONST },
{ input: '-=', tag: 'mo', output: '\u2261', tex: 'equiv', ttype: CONST },
{ input: '~=', tag: 'mo', output: '\u2245', tex: 'stackrel{\\sim}{=}', ttype: CONST }, // back hack b/c mimetex doesn't support /cong
{ input: 'cong', tag: 'mo', output: '~=', tex: null, ttype: DEFINITION },
{ input: '~~', tag: 'mo', output: '\u2248', tex: 'approx', ttype: CONST },
{ input: 'prop', tag: 'mo', output: '\u221D', tex: 'propto', ttype: CONST },

// logical symbols
{ input: 'and', tag: 'mtext', output: 'and', tex: null, ttype: SPACE },
{ input: 'or', tag: 'mtext', output: 'or', tex: null, ttype: SPACE },
{ input: 'not', tag: 'mo', output: '\u00AC', tex: 'neg', ttype: CONST },
{ input: '=>', tag: 'mo', output: '\u21D2', tex: 'Rightarrow', ttype: CONST },
{ input: 'implies', tag: 'mo', output: '=>', tex: null, ttype: DEFINITION },
{ input: 'if', tag: 'mo', output: 'if', tex: null, ttype: SPACE },
{ input: '<=>', tag: 'mo', output: '\u21D4', tex: 'Leftrightarrow', ttype: CONST },
{ input: 'iff', tag: 'mo', output: '<=>', tex: null, ttype: DEFINITION },
{ input: 'AA', tag: 'mo', output: '\u2200', tex: 'forall', ttype: CONST },
{ input: 'EE', tag: 'mo', output: '\u2203', tex: 'exists', ttype: CONST },
{ input: '_|_', tag: 'mo', output: '\u22A5', tex: 'bot', ttype: CONST },
{ input: 'TT', tag: 'mo', output: '\u22A4', tex: 'top', ttype: CONST },
{ input: '|--', tag: 'mo', output: '\u22A2', tex: 'vdash', ttype: CONST },
{ input: '|==', tag: 'mo', output: '\u22A8', tex: 'models', ttype: CONST }, // mimetex doesn't support

// grouping brackets
{ input: '(', tag: 'mo', output: '(', tex: null, ttype: LEFTBRACKET, val: true },
{ input: ')', tag: 'mo', output: ')', tex: null, ttype: RIGHTBRACKET, val: true },
{ input: '[', tag: 'mo', output: '[', tex: null, ttype: LEFTBRACKET, val: true },
{ input: ']', tag: 'mo', output: ']', tex: null, ttype: RIGHTBRACKET, val: true },
{ input: '{', tag: 'mo', output: '{', tex: 'lbrace', ttype: LEFTBRACKET },
{ input: '}', tag: 'mo', output: '}', tex: 'rbrace', ttype: RIGHTBRACKET },
{ input: '|', tag: 'mo', output: '|', tex: null, ttype: LEFTRIGHT, val: true },
// {input:"||", tag:"mo", output:"||", tex:null, ttype:LEFTRIGHT},
{ input: '(:', tag: 'mo', output: '\u2329', tex: 'langle', ttype: LEFTBRACKET },
{ input: ':)', tag: 'mo', output: '\u232A', tex: 'rangle', ttype: RIGHTBRACKET },
{ input: '<<', tag: 'mo', output: '\u2329', tex: 'langle', ttype: LEFTBRACKET },
{ input: '>>', tag: 'mo', output: '\u232A', tex: 'rangle', ttype: RIGHTBRACKET },
{ input: '{:', tag: 'mo', output: '{:', tex: null, ttype: LEFTBRACKET, invisible: true },
{ input: ':}', tag: 'mo', output: ':}', tex: null, ttype: RIGHTBRACKET, invisible: true },

// miscellaneous symbols
{ input: 'int', tag: 'mo', output: '\u222B', tex: null, ttype: CONST },
{ input: 'dx', tag: 'mi', output: '{:d x:}', tex: null, ttype: DEFINITION },
{ input: 'dy', tag: 'mi', output: '{:d y:}', tex: null, ttype: DEFINITION },
{ input: 'dz', tag: 'mi', output: '{:d z:}', tex: null, ttype: DEFINITION },
{ input: 'dt', tag: 'mi', output: '{:d t:}', tex: null, ttype: DEFINITION },
{ input: 'oint', tag: 'mo', output: '\u222E', tex: null, ttype: CONST },
{ input: 'del', tag: 'mo', output: '\u2202', tex: 'partial', ttype: CONST },
{ input: 'grad', tag: 'mo', output: '\u2207', tex: 'nabla', ttype: CONST },
{ input: '+-', tag: 'mo', output: '\u00B1', tex: 'pm', ttype: CONST },
{ input: 'O/', tag: 'mo', output: '\u2205', tex: 'emptyset', ttype: CONST },
{ input: 'oo', tag: 'mo', output: '\u221E', tex: 'infty', ttype: CONST },
{ input: 'aleph', tag: 'mo', output: '\u2135', tex: null, ttype: CONST },
{ input: '...', tag: 'mo', output: '...', tex: 'ldots', ttype: CONST },
{ input: ':.', tag: 'mo', output: '\u2234', tex: 'therefore', ttype: CONST },
{ input: '/_', tag: 'mo', output: '\u2220', tex: 'angle', ttype: CONST },
{ input: '/_\\', tag: 'mo', output: '\u25B3', tex: 'triangle', ttype: CONST },
{ input: '\\ ', tag: 'mo', output: '\u00A0', tex: null, ttype: CONST, val: true },
{ input: 'frown', tag: 'mo', output: '\u2322', tex: null, ttype: CONST },
{ input: '%', tag: 'mo', output: '%', tex: '%', ttype: CONST, notexcopy: true },
{ input: 'quad', tag: 'mo', output: '\u00A0\u00A0', tex: null, ttype: CONST },
{ input: 'qquad', tag: 'mo', output: '\u00A0\u00A0\u00A0\u00A0', tex: null, ttype: CONST },
{ input: 'cdots', tag: 'mo', output: '\u22EF', tex: null, ttype: CONST },
{ input: 'vdots', tag: 'mo', output: '\u22EE', tex: null, ttype: CONST },
{ input: 'ddots', tag: 'mo', output: '\u22F1', tex: null, ttype: CONST },
{ input: 'diamond', tag: 'mo', output: '\u22C4', tex: null, ttype: CONST },
{ input: 'square', tag: 'mo', output: '\u25A1', tex: 'boxempty', ttype: CONST },
{ input: '|__', tag: 'mo', output: '\u230A', tex: 'lfloor', ttype: CONST },
{ input: '__|', tag: 'mo', output: '\u230B', tex: 'rfloor', ttype: CONST },
{ input: '|~', tag: 'mo', output: '\u2308', tex: 'lceil', ttype: CONST },
{ input: 'lceiling', tag: 'mo', output: '|~', tex: null, ttype: DEFINITION },
{ input: '~|', tag: 'mo', output: '\u2309', tex: 'rceil', ttype: CONST },
{ input: 'rceiling', tag: 'mo', output: '~|', tex: null, ttype: DEFINITION },
{ input: 'CC', tag: 'mo', output: '\u2102', tex: 'mathbb{C}', ttype: CONST, notexcopy: true },
{ input: 'NN', tag: 'mo', output: '\u2115', tex: 'mathbb{N}', ttype: CONST, notexcopy: true },
{ input: 'QQ', tag: 'mo', output: '\u211A', tex: 'mathbb{Q}', ttype: CONST, notexcopy: true },
{ input: 'RR', tag: 'mo', output: '\u211D', tex: 'mathbb{R}', ttype: CONST, notexcopy: true },
{ input: 'ZZ', tag: 'mo', output: '\u2124', tex: 'mathbb{Z}', ttype: CONST, notexcopy: true },
{ input: 'f', tag: 'mi', output: 'f', tex: null, ttype: UNARY, func: true, val: true },
{ input: 'g', tag: 'mi', output: 'g', tex: null, ttype: UNARY, func: true, val: true },
{ input: '\'\'', tag: 'mo', output: '\'\'', tex: null, val: true },
{ input: '\'\'\'', tag: 'mo', output: '\'\'\'', tex: null, val: true },
{ input: '\'\'\'\'', tag: 'mo', output: '\'\'\'\'', tex: null, val: true },


// standard functions
{ input: 'lim', tag: 'mo', output: 'lim', tex: null, ttype: UNDEROVER },
{ input: 'Lim', tag: 'mo', output: 'Lim', tex: null, ttype: UNDEROVER },
{ input: 'sin', tag: 'mo', output: 'sin', tex: null, ttype: UNARY, func: true },
{ input: 'cos', tag: 'mo', output: 'cos', tex: null, ttype: UNARY, func: true },
{ input: 'tan', tag: 'mo', output: 'tan', tex: null, ttype: UNARY, func: true },
{ input: 'arcsin', tag: 'mo', output: 'arcsin', tex: null, ttype: UNARY, func: true },
{ input: 'arccos', tag: 'mo', output: 'arccos', tex: null, ttype: UNARY, func: true },
{ input: 'arctan', tag: 'mo', output: 'arctan', tex: null, ttype: UNARY, func: true },
{ input: 'sinh', tag: 'mo', output: 'sinh', tex: null, ttype: UNARY, func: true },
{ input: 'cosh', tag: 'mo', output: 'cosh', tex: null, ttype: UNARY, func: true },
{ input: 'tanh', tag: 'mo', output: 'tanh', tex: null, ttype: UNARY, func: true },
{ input: 'cot', tag: 'mo', output: 'cot', tex: null, ttype: UNARY, func: true },
{ input: 'coth', tag: 'mo', output: 'coth', tex: null, ttype: UNARY, func: true },
{ input: 'sech', tag: 'mo', output: 'sech', tex: null, ttype: UNARY, func: true },
{ input: 'csch', tag: 'mo', output: 'csch', tex: null, ttype: UNARY, func: true },
{ input: 'sec', tag: 'mo', output: 'sec', tex: null, ttype: UNARY, func: true },
{ input: 'csc', tag: 'mo', output: 'csc', tex: null, ttype: UNARY, func: true },
{ input: 'log', tag: 'mo', output: 'log', tex: null, ttype: UNARY, func: true },
{ input: 'ln', tag: 'mo', output: 'ln', tex: null, ttype: UNARY, func: true },
{ input: 'abs', tag: 'mo', output: 'abs', tex: null, ttype: UNARY, notexcopy: true, rewriteleftright: ['|', '|'] },
{ input: 'norm', tag: 'mo', output: 'norm', tex: null, ttype: UNARY, notexcopy: true, rewriteleftright: ['\\|', '\\|'] },
{ input: 'floor', tag: 'mo', output: 'floor', tex: null, ttype: UNARY, notexcopy: true, rewriteleftright: ['\\lfloor', '\\rfloor'] },
{ input: 'ceil', tag: 'mo', output: 'ceil', tex: null, ttype: UNARY, notexcopy: true, rewriteleftright: ['\\lceil', '\\rceil'] },
{ input: 'Sin', tag: 'mo', output: 'sin', tex: null, ttype: UNARY, func: true },
{ input: 'Cos', tag: 'mo', output: 'cos', tex: null, ttype: UNARY, func: true },
{ input: 'Tan', tag: 'mo', output: 'tan', tex: null, ttype: UNARY, func: true },
{ input: 'Arcsin', tag: 'mo', output: 'arcsin', tex: null, ttype: UNARY, func: true },
{ input: 'Arccos', tag: 'mo', output: 'arccos', tex: null, ttype: UNARY, func: true },
{ input: 'Arctan', tag: 'mo', output: 'arctan', tex: null, ttype: UNARY, func: true },
{ input: 'Sinh', tag: 'mo', output: 'sinh', tex: null, ttype: UNARY, func: true },
{ input: 'Sosh', tag: 'mo', output: 'cosh', tex: null, ttype: UNARY, func: true },
{ input: 'Tanh', tag: 'mo', output: 'tanh', tex: null, ttype: UNARY, func: true },
{ input: 'Cot', tag: 'mo', output: 'cot', tex: null, ttype: UNARY, func: true },
{ input: 'Sec', tag: 'mo', output: 'sec', tex: null, ttype: UNARY, func: true },
{ input: 'Csc', tag: 'mo', output: 'csc', tex: null, ttype: UNARY, func: true },
{ input: 'Log', tag: 'mo', output: 'log', tex: null, ttype: UNARY, func: true },
{ input: 'Ln', tag: 'mo', output: 'ln', tex: null, ttype: UNARY, func: true },
{ input: 'Abs', tag: 'mo', output: 'abs', tex: null, ttype: UNARY, notexcopy: true, rewriteleftright: ['|', '|'] },

{ input: 'det', tag: 'mo', output: 'det', tex: null, ttype: UNARY, func: true },
{ input: 'exp', tag: 'mo', output: 'exp', tex: null, ttype: UNARY, func: true },
{ input: 'dim', tag: 'mo', output: 'dim', tex: null, ttype: CONST },
{ input: 'mod', tag: 'mo', output: 'mod', tex: 'text{mod}', ttype: CONST, notexcopy: true },
{ input: 'gcd', tag: 'mo', output: 'gcd', tex: null, ttype: UNARY, func: true },
{ input: 'lcm', tag: 'mo', output: 'lcm', tex: 'text{lcm}', ttype: UNARY, func: true, notexcopy: true },
{ input: 'lub', tag: 'mo', output: 'lub', tex: null, ttype: CONST },
{ input: 'glb', tag: 'mo', output: 'glb', tex: null, ttype: CONST },
{ input: 'min', tag: 'mo', output: 'min', tex: null, ttype: UNDEROVER },
{ input: 'max', tag: 'mo', output: 'max', tex: null, ttype: UNDEROVER },

// arrows
{ input: 'uarr', tag: 'mo', output: '\u2191', tex: 'uparrow', ttype: CONST },
{ input: 'darr', tag: 'mo', output: '\u2193', tex: 'downarrow', ttype: CONST },
{ input: 'rarr', tag: 'mo', output: '\u2192', tex: 'rightarrow', ttype: CONST },
{ input: '->', tag: 'mo', output: '\u2192', tex: 'to', ttype: CONST },
{ input: '>->', tag: 'mo', output: '\u21A3', tex: 'rightarrowtail', ttype: CONST },
{ input: '->>', tag: 'mo', output: '\u21A0', tex: 'twoheadrightarrow', ttype: CONST },
{ input: '>->>', tag: 'mo', output: '\u2916', tex: 'twoheadrightarrowtail', ttype: CONST },
{ input: '|->', tag: 'mo', output: '\u21A6', tex: 'mapsto', ttype: CONST },
{ input: 'larr', tag: 'mo', output: '\u2190', tex: 'leftarrow', ttype: CONST },
{ input: 'harr', tag: 'mo', output: '\u2194', tex: 'leftrightarrow', ttype: CONST },
{ input: 'rArr', tag: 'mo', output: '\u21D2', tex: 'Rightarrow', ttype: CONST },
{ input: 'lArr', tag: 'mo', output: '\u21D0', tex: 'Leftarrow', ttype: CONST },
{ input: 'hArr', tag: 'mo', output: '\u21D4', tex: 'Leftrightarrow', ttype: CONST },

// commands with argument
  AMsqrt, AMroot, AMfrac, AMdiv, AMover, AMsub, AMsup,
{ input: 'cancel', tag: 'menclose', output: 'cancel', tex: null, ttype: UNARY },
{ input: 'Sqrt', tag: 'msqrt', output: 'sqrt', tex: null, ttype: UNARY },
{ input: 'hat', tag: 'mover', output: '\u005E', tex: null, ttype: UNARY, acc: true },
{ input: 'bar', tag: 'mover', output: '\u00AF', tex: 'overline', ttype: UNARY, acc: true },
{ input: 'vec', tag: 'mover', output: '\u2192', tex: null, ttype: UNARY, acc: true },
{ input: 'tilde', tag: 'mover', output: '~', tex: null, ttype: UNARY, acc: true },
{ input: 'dot', tag: 'mover', output: '.', tex: null, ttype: UNARY, acc: true },
{ input: 'ddot', tag: 'mover', output: '..', tex: null, ttype: UNARY, acc: true },
{ input: 'ul', tag: 'munder', output: '\u0332', tex: 'underline', ttype: UNARY, acc: true },
{ input: 'ubrace', tag: 'munder', output: '\u23DF', tex: 'underbrace', ttype: UNARY, acc: true },
{ input: 'obrace', tag: 'mover', output: '\u23DE', tex: 'overbrace', ttype: UNARY, acc: true },
  AMtext, AMmbox, AMquote,
// {input:"var", tag:"mstyle", atname:"fontstyle", atval:"italic", output:"var", tex:null, ttype:UNARY},
{ input: 'color', tag: 'mstyle', ttype: BINARY },
{ input: 'bb', tag: 'mstyle', atname: 'mathvariant', atval: 'bold', output: 'bb', tex: 'mathbf', ttype: UNARY, notexcopy: true },
{ input: 'mathbf', tag: 'mstyle', atname: 'mathvariant', atval: 'bold', output: 'mathbf', tex: null, ttype: UNARY },
{ input: 'sf', tag: 'mstyle', atname: 'mathvariant', atval: 'sans-serif', output: 'sf', tex: 'mathsf', ttype: UNARY, notexcopy: true },
{ input: 'mathsf', tag: 'mstyle', atname: 'mathvariant', atval: 'sans-serif', output: 'mathsf', tex: null, ttype: UNARY },
{ input: 'bbb', tag: 'mstyle', atname: 'mathvariant', atval: 'double-struck', output: 'bbb', tex: 'mathbb', ttype: UNARY, notexcopy: true },
{ input: 'mathbb', tag: 'mstyle', atname: 'mathvariant', atval: 'double-struck', output: 'mathbb', tex: null, ttype: UNARY },
{ input: 'cc', tag: 'mstyle', atname: 'mathvariant', atval: 'script', output: 'cc', tex: 'mathcal', ttype: UNARY, notexcopy: true },
{ input: 'mathcal', tag: 'mstyle', atname: 'mathvariant', atval: 'script', output: 'mathcal', tex: null, ttype: UNARY },
{ input: 'tt', tag: 'mstyle', atname: 'mathvariant', atval: 'monospace', output: 'tt', tex: 'mathtt', ttype: UNARY, notexcopy: true },
{ input: 'mathtt', tag: 'mstyle', atname: 'mathvariant', atval: 'monospace', output: 'mathtt', tex: null, ttype: UNARY },
{ input: 'fr', tag: 'mstyle', atname: 'mathvariant', atval: 'fraktur', output: 'fr', tex: 'mathfrak', ttype: UNARY, notexcopy: true },
{ input: 'mathfrak', tag: 'mstyle', atname: 'mathvariant', atval: 'fraktur', output: 'mathfrak', tex: null, ttype: UNARY }
];


function compareNames(s1, s2) {
  if (s1.input > s2.input) return 1;
  else return -1;
}

const AMnames = []; // list of input symbols

function refreshSymbols() {
  let i;
  AMsymbols.sort(compareNames);
  for (i=0; i<AMsymbols.length; i+=1) AMnames[i] = AMsymbols[i].input;
}

function AMinitSymbols() {
  let i;
  const symlen = AMsymbols.length;
  for (i=0; i<symlen; i+=1) {
    if (AMsymbols[i].tex && !(typeof AMsymbols[i].notexcopy == 'boolean' && AMsymbols[i].notexcopy)) {
      AMsymbols.push({ input: AMsymbols[i].tex,
        tag: AMsymbols[i].tag,
        output: AMsymbols[i].output,
        ttype: AMsymbols[i].ttype,
        acc: (AMsymbols[i].acc||false) });
    }
  }
  refreshSymbols();
}


// function newcommand(oldstr, newstr) {
//   AMsymbols.push({ input: oldstr, tag: 'mo', output: newstr, tex: null, ttype: DEFINITION });
//   refreshSymbols();
// }

// function newsymbol(symbolobj) {
//   AMsymbols.push(symbolobj);
//   refreshSymbols();
// }

function AMremoveCharsAndBlanks(str, n) {
// remove n characters and any following blanks
  let st;
  let i;
  if (str.charAt(n)==='\\' && str.charAt(n+1)!=='\\' && str.charAt(n+1)!==' ') { st = str.slice(n+1); } else st = str.slice(n);
  for (i=0; i<st.length && st.charCodeAt(i)<=32; i+=1);
  return st.slice(i);
}

function AMposition(arr, str, n) {
// return position >=n where str appears or would be inserted
// assumes arr is sorted
  let i;
  if (n===0) {
    let h;
    let m;
    n = -1;
    h = arr.length;
    while (n+1<h) {
      // Substitute bitwise operator
      if ((n+h)%2 !== 0) {
        m = ((n+h)-1)/2;
      } else {
        m = (n+h)/2;
      }
      if (arr[m]<str) n = m; else h = m;
    }
    return h;
  } else { for (i=n; i<arr.length && arr[i]<str; i+=1); }
  return i; // i=arr.length || arr[i]>=str
}


function AMTremoveBrackets(node) {
  let st;
  if (node.charAt(0)==='{' && node.charAt(node.length-1)==='}') {
    let leftchop = 0;

    st = node.substr(1, 5);
    if (st==='\\left') {
      st = node.charAt(6);
      if (st==='(' || st==='[' || st==='{') {
        leftchop = 7;
      } else {
        st = node.substr(6, 7);
        if (st==='\\lbrace') {
          leftchop = 13;
        }
      }
    } else {
      st = node.charAt(1);
      if (st==='(' || st==='[') {
        leftchop = 2;
      }
    }
    if (leftchop>0) {
      // st = node.charAt(node.length-7);
      st = node.substr(node.length-8);
      if (st==='\\right)}' || st==='\\right]}' || st==='\\right.}') {
        node = `{${node.substr(leftchop)}`;
        node = `${node.substr(0, node.length-8)}}`;
      } else if (st==='\\rbrace}') {
        node = `{${node.substr(leftchop)}`;
        node = `${node.substr(0, node.length-14)}}`;
      }
    }
  }
  return node;
}

/* Parsing ASCII math expressions with the following grammar
v ::= [A-Za-z] | greek letters | numbers | other constant symbols
u ::= sqrt | text | bb | other unary symbols for font commands
b ::= frac | root | stackrel         binary symbols
l ::= ( | [ | { | (: | {:            left brackets
r ::= ) | ] | } | :) | :}            right brackets
S ::= v | lEr | uS | bSS             Simple expression
I ::= S_S | S^S | S_S^S | S          Intermediate expression
E ::= IE | I/I                       Expression
Each terminal symbol is translated into a corresponding mathml node.*/

let AMnestingDepth;
let AMpreviousSymbol;
let AMcurrentSymbol;

function AMTgetTeXsymbol(symb) {
  let pre;
  if (typeof symb.val == 'boolean' && symb.val) {
    pre = '';
  } else {
    pre = '\\';
  }
  if (symb.tex==null) {
  // can't remember why this was here.  Breaks /delta /Delta to removed
  // return (pre+(pre==''?symb.input:symb.input.toLowerCase()));
    return (pre+symb.input);
  } else {
    return (pre+symb.tex);
  }
}

function AMgetSymbol(str) {
// return maximal initial substring of str that appears in names
// return null if there is none
  let k = 0; // new pos
  let j = 0; // old pos
  let mk; // match pos
  let st;
  let tagst;
  let match = '';
  let more = true;
  for (let i=1; i<=str.length && more; i+=1) {
    st = str.slice(0, i); // initial substring of length i
    j = k;
    k = AMposition(AMnames, st, j);
    if (k<AMnames.length && str.slice(0, AMnames[k].length)===AMnames[k]) {
      match = AMnames[k];
      mk = k;
      i = match.length;
    }
    more = k<AMnames.length && str.slice(0, AMnames[k].length)>=AMnames[k];
  }
  AMpreviousSymbol=AMcurrentSymbol;
  if (match!=='') {
    AMcurrentSymbol=AMsymbols[mk].ttype;
    return AMsymbols[mk];
  }
// if str[0] is a digit or - return maxsubstring of digits.digits
  AMcurrentSymbol=CONST;
  k = 1;
  st = str.slice(0, 1);
  let integ = true;

  while (st>='0' && st<='9' && k<=str.length) {
    st = str.slice(k, k+1);
    k+=1;
  }
  if (st === config.decimalsign) {
    st = str.slice(k, k+1);
    if (st>='0' && st<='9') {
      integ = false;
      k+=1;
      while (st>='0' && st<='9' && k<=str.length) {
        st = str.slice(k, k+1);
        k+=1;
      }
    }
  }
  if ((integ && k>1) || k>2) {
    st = str.slice(0, k-1);
    tagst = 'mn';
  } else {
    k = 2;
    st = str.slice(0, 1); // take 1 character
    tagst = ((st<'A' || st>'Z') && (st<'a' || st>'z')?'mo':'mi');
  }
  if (st==='-' && AMpreviousSymbol===INFIX) {
    AMcurrentSymbol = INFIX;
    return { input: st, tag: tagst, output: st, ttype: UNARY, func: true, val: true };
  }
  return { input: st, tag: tagst, output: st, ttype: CONST, val: true }; // added val bit
}

/* eslint no-use-before-define: ["error", { "functions": false }]*/
function AMTparseIexpr(str) {
  let sym2;
  let node;
  let result;
  str = AMremoveCharsAndBlanks(str, 0);
  const sym1 = AMgetSymbol(str);
  result = AMTparseSexpr(str);
  node = result[0];
  str = result[1];
  const symbol = AMgetSymbol(str);
  if (symbol.ttype === INFIX && symbol.input !== '/') {
    str = AMremoveCharsAndBlanks(str, symbol.input.length);
   // if (symbol.input == "/") result = AMTparseIexpr(str); else
    result = AMTparseSexpr(str);
    if (result[0] == null) { // show box in place of missing argument
      result[0] = '{}';
    } else result[0] = AMTremoveBrackets(result[0]);
    str = result[1];
//    if (symbol.input == "/") AMTremoveBrackets(node);
    if (symbol.input === '_') {
      sym2 = AMgetSymbol(str);
      if (sym2.input === '^') {
        str = AMremoveCharsAndBlanks(str, sym2.input.length);
        const res2 = AMTparseSexpr(str);
        res2[0] = AMTremoveBrackets(res2[0]);
        str = res2[1];
        node = `{${node}`;
        node += `_{${result[0]}}`;
        node += `^{${res2[0]}}`;
        node += '}';
      } else {
        node += `_{${result[0]}}`;
      }
    } else { // must be ^
      // node = '{'+node+'}^{'+result[0]+'}';
      node = `${node}^{${result[0]}}`;
    }
    if (typeof sym1.func != 'undefined' && sym1.func) {
      sym2 = AMgetSymbol(str);
      if (sym2.ttype !== INFIX && sym2.ttype !== RIGHTBRACKET) {
        result = AMTparseIexpr(str);
        node = `{${node}${result[0]}}`;
        str = result[1];
      }
    }
  }

  return [node, str];
}

function AMTparseExpr(str, rightbracket) {
  let symbol;
  let node;
  let result;
  let i;
  // const nodeList = [];
  let newFrag = '';
  let addedright = false;
  do {
    str = AMremoveCharsAndBlanks(str, 0);
    result = AMTparseIexpr(str);
    node = result[0];
    str = result[1];
    symbol = AMgetSymbol(str);
    if (symbol.ttype === INFIX && symbol.input === '/') {
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      result = AMTparseIexpr(str);

      if (result[0] == null) { // show box in place of missing argument
        result[0] = '{}';
      } else result[0] = AMTremoveBrackets(result[0]);
      str = result[1];
      node = AMTremoveBrackets(node);
      node = `${'\\frac{'}${node}}`;
      node += `{${result[0]}}`;
      newFrag += node;
      symbol = AMgetSymbol(str);
    } else if (node!==undefined) newFrag += node;
  } while (((symbol.ttype !== RIGHTBRACKET &&
           (symbol.ttype !== LEFTRIGHT || rightbracket))
           || AMnestingDepth === 0) && symbol!=null && symbol.output!=='');
  if (symbol.ttype === RIGHTBRACKET || symbol.ttype === LEFTRIGHT) {
//    if (AMnestingDepth > 0) AMnestingDepth--;
    const len = newFrag.length;
    if (len>2 && newFrag.charAt(0)==='{' && newFrag.indexOf(',')>0) { // could be matrix (total rewrite from .js)
      const right = newFrag.charAt(len - 2);
      if (right===')' || right===']') {
        const left = newFrag.charAt(6);
        if ((left==='(' && right===')' && symbol.output !== '}') || (left==='[' && right===']')) {
          let mxout = '\\matrix{';
          const pos = []; // position of commas
          pos.push(0);
          let matrix = true;
          let mxnestingd = 0;
          const subpos = [];
          subpos[0] = [0];
          let lastsubposstart = 0;
          let mxanynestingd = 0;
          for (i=1; i<len-1; i+=1) {
            if (newFrag.charAt(i)===left) mxnestingd+=1;
            if (newFrag.charAt(i)===right) {
              mxnestingd-=1;
              if (mxnestingd===0 && newFrag.charAt(i+2)===',' && newFrag.charAt(i+3)==='{') {
                pos.push(i+2);
                lastsubposstart = i+2;
                subpos[lastsubposstart] = [i+2];
              }
            }
            if (newFrag.charAt(i)==='[' || newFrag.charAt(i)==='(' || newFrag.charAt(i)==='{') { mxanynestingd+=1; }
            if (newFrag.charAt(i)===']' || newFrag.charAt(i)===')' || newFrag.charAt(i)==='}') { mxanynestingd-=1; }
            if (newFrag.charAt(i)===',' && mxanynestingd===1) {
              subpos[lastsubposstart].push(i);
            }
            if (mxanynestingd<0) {  // happens at the end of the row
              if (lastsubposstart === i+1) { // if at end of row, skip to next row
                i+=1;
              } else { // misformed something - abandon treating as a matrix
                matrix = false;
              }
            }
          }

          pos.push(len);
          let lastmxsubcnt = -1;
          if (mxnestingd===0 && pos.length>0 && matrix) {
            for (i=0; i<pos.length-1; i+=1) {
              let subarr;
              if (i>0) mxout += '\\\\';
              if (i===0) {
              // var subarr = newFrag.substr(pos[i]+7,pos[i+1]-pos[i]-15).split(',');
                if (subpos[pos[i]].length===1) {
                  subarr = [newFrag.substr(pos[i]+7, pos[i+1]-pos[i]-15)];
                } else {
                  subarr = [newFrag.substring(pos[i]+7, subpos[pos[i]][1])];
                  for (let j=2; j<subpos[pos[i]].length; j+=1) {
                    subarr.push(newFrag.substring(subpos[pos[i]][j-1]+1, subpos[pos[i]][j]));
                  }
                  subarr.push(newFrag.substring(subpos[pos[i]][subpos[pos[i]].length-1]+1, pos[i+1]-8));
                }
              } else if (subpos[pos[i]].length===1) { // var subarr = newFrag.substr(pos[i]+8,pos[i+1]-pos[i]-16).split(',');
                subarr = [newFrag.substr(pos[i]+8, pos[i+1]-pos[i]-16)];
              } else {
                subarr = [newFrag.substring(pos[i]+8, subpos[pos[i]][1])];
                for (let j=2; j<subpos[pos[i]].length; j+=1) {
                  subarr.push(newFrag.substring(subpos[pos[i]][j-1]+1, subpos[pos[i]][j]));
                }
                subarr.push(newFrag.substring(subpos[pos[i]][subpos[pos[i]].length-1]+1, pos[i+1]-8));
              }
              if (lastmxsubcnt>0 && subarr.length!==lastmxsubcnt) {
                matrix = false;
              } else if (lastmxsubcnt===-1) {
                lastmxsubcnt=subarr.length;
              }
              mxout += subarr.join('&');
            }
          }
          mxout += '}';

          if (matrix) { newFrag = mxout; }
        }
      }
    }

    str = AMremoveCharsAndBlanks(str, symbol.input.length);
    if (typeof symbol.invisible != 'boolean' || !symbol.invisible) {
      node = `\\right${AMTgetTeXsymbol(symbol)}`; // AMcreateMmlNode("mo",document.createTextNode(symbol.output));
      newFrag += node;
      addedright = true;
    } else {
      newFrag += '\\right.';
      addedright = true;
    }
  }
  if (AMnestingDepth>0 && !addedright) {
    newFrag += '\\right.'; // adjust for non-matching left brackets
    // todo: adjust for non-matching right brackets
  }

  return [newFrag, str];
}

function AMTparseSexpr(str) { // parses str and returns [node,tailstr]
  let symbol;
  let node;
  let result;
  let i;
  let st; // rightvert = false,
  let newFrag = '';
  str = AMremoveCharsAndBlanks(str, 0);
  symbol = AMgetSymbol(str);             // either a token or a bracket or empty
  if (symbol == null || (symbol.ttype === RIGHTBRACKET && AMnestingDepth > 0)) {
    return [null, str];
  }
  if (symbol.ttype === DEFINITION) {
    str = symbol.output+AMremoveCharsAndBlanks(str, symbol.input.length);
    symbol = AMgetSymbol(str);
  }
  switch (symbol.ttype) {
    case UNDEROVER:
    case CONST: {
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      const texsymbol = AMTgetTeXsymbol(symbol);
      if (texsymbol.charAt(0)==='\\' || symbol.tag==='mo') return [texsymbol, str];
      else return [`{${texsymbol}}`, str];
    }

    case LEFTBRACKET: {   // read (expr+)
      AMnestingDepth+=1;
      str = AMremoveCharsAndBlanks(str, symbol.input.length);

      result = AMTparseExpr(str, true);
      AMnestingDepth-=1;
      let leftchop = 0;
      if (result[0].substr(0, 6)==='\\right') {
        st = result[0].charAt(6);
        if (st===')' || st===']' || st==='}') {
          leftchop = 6;
        } else if (st==='.') {
          leftchop = 7;
        } else {
          st = result[0].substr(6, 7);
          if (st==='\\rbrace') {
            leftchop = 13;
          }
        }
      }
      if (leftchop>0) {
        result[0] = result[0].substr(leftchop);
        if (typeof symbol.invisible == 'boolean' && symbol.invisible) { node = `{${result[0]}}`; } else {
          node = `{${AMTgetTeXsymbol(symbol)}${result[0]}}`;
        }
      } else if (typeof symbol.invisible == 'boolean' && symbol.invisible) { node = `{\\left.${result[0]}}`; } else {
        node = `{\\left${AMTgetTeXsymbol(symbol)}${result[0]}}`;
      }
      return [node, result[1]];
    }

    case TEXT:
      if (symbol!==AMquote) str = AMremoveCharsAndBlanks(str, symbol.input.length);
      if (str.charAt(0)==='{') i=str.indexOf('}');
      else if (str.charAt(0)==='(') i=str.indexOf(')');
      else if (str.charAt(0)==='[') i=str.indexOf(']');
      else if (symbol===AMquote) i=str.slice(1).indexOf('"')+1;
      else i = 0;
      if (i===-1) i = str.length;
      st = str.slice(1, i);
      if (st.charAt(0) === ' ') {
        newFrag = '\\ ';
      }
      newFrag += `\\text{${st}}`;
      if (st.charAt(st.length-1) === ' ') {
        newFrag += '\\ ';
      }
      str = AMremoveCharsAndBlanks(str, i+1);
      return [newFrag, str];
    case UNARY:
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      result = AMTparseSexpr(str);
      if (result[0]==null) return [`{${AMTgetTeXsymbol(symbol)}}`, str];
      if (typeof symbol.func == 'boolean' && symbol.func) { // functions hack
        st = str.charAt(0);
        if (st==='^' || st==='_' || st==='/' || st==='|' || st===','
           || (symbol.input.length===1 && symbol.input.match(/\w/) && st!=='(')) {
          return [`{${AMTgetTeXsymbol(symbol)}}`, str];
        } else {
          node = `{${AMTgetTeXsymbol(symbol)}{${result[0]}}}`;
          return [node, result[1]];
        }
      }
      result[0] = AMTremoveBrackets(result[0]);
      if (symbol.input === 'sqrt') {           // sqrt
        return [`\\sqrt{${result[0]}}`, result[1]];
      } else if (symbol.input === 'cancel') {           // cancel
        return [`\\cancel{${result[0]}}`, result[1]];
      } else if (typeof symbol.rewriteleftright != 'undefined') {  // abs, floor, ceil
        return [`{\\left${symbol.rewriteleftright[0]}${result[0]}\\right${symbol.rewriteleftright[1]}}`, result[1]];
      } else if (typeof symbol.acc == 'boolean' && symbol.acc) {   // accent
        // return ['{'+AMTgetTeXsymbol(symbol)+'{'+result[0]+'}}',result[1]];
        return [`${AMTgetTeXsymbol(symbol)}{${result[0]}}`, result[1]];
      } else {                        // font change command
        return [`{${AMTgetTeXsymbol(symbol)}{${result[0]}}}`, result[1]];
      }
    case BINARY: {
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      result = AMTparseSexpr(str);
      if (result[0]==null) return [`{${AMTgetTeXsymbol(symbol)}}`, str];
      result[0] = AMTremoveBrackets(result[0]);
      const result2 = AMTparseSexpr(result[1]);
      if (result2[0]==null) return [`{${AMTgetTeXsymbol(symbol)}}`, str];
      result2[0] = AMTremoveBrackets(result2[0]);
      if (symbol.input==='color') {
        newFrag = `{\\color{${result[0].replace(/[{}]/g, '')}}${result2[0]}}`;
      } else if (symbol.input==='root') {
        newFrag = `{\\sqrt[${result[0]}]{${result2[0]}}}`;
      } else {
        newFrag = `{${AMTgetTeXsymbol(symbol)}{${result[0]}}{${result2[0]}}}`;
      }
      return [newFrag, result2[1]];
    }
    case INFIX:
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      return [symbol.output, str];
    case SPACE:
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      return [`{\\quad\\text{${symbol.input}}\\quad}`, str];
    case LEFTRIGHT:
//    if (rightvert) return [null,str]; else rightvert = true;
      AMnestingDepth+=1;
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      result = AMTparseExpr(str, false);
      AMnestingDepth-=1;
      st = '';
      st = result[0].charAt(result[0].length -1);
// alert(result[0].lastChild+"***"+st);
      if (st === '|') { // its an absolute value subterm
        node = `{\\left|${result[0]}}`;
        return [node, result[1]];
      } else { // the "|" is a \mid
        node = '{\\mid}';
        return [node, str];
      }

    default:
// alert("default");
      str = AMremoveCharsAndBlanks(str, symbol.input.length);
      return [`{${AMTgetTeXsymbol(symbol)}}`, str];
  }
}

function AMTparseAMtoTeX(str) {
  AMnestingDepth = 0;
  str = str.replace(/(&nbsp;|\u00a0|&#160;)/g, '');
  str = str.replace(/&gt;/g, '>');
  str = str.replace(/&lt;/g, '<');
  if (str.match(/\S/)==null) {
    return '';
  }
  return AMTparseExpr(str.replace(/^\s+/g, ''), false)[0];
}

function AMparseMath(str) {
 // DLMOD to remove &nbsp;, which editor adds on multiple spaces
  str = str.replace(/(&nbsp;|\u00a0|&#160;)/g, '');
  str = str.replace(/&gt;/g, '>');
  str = str.replace(/&lt;/g, '<');
  if (str.match(/\S/)==null) {
    return document.createTextNode(' ');
  }
  let texstring = AMTparseAMtoTeX(str);
  let mathbg;
  if (typeof mathbg !== 'undefined' && mathbg==='dark') {
    texstring = `\\reverse ${texstring}`;
  }
  if (config.mathcolor!=='') {
    texstring = `\\${config.mathcolor}${texstring}`;
  }
  if (config.displaystyle) {
    texstring = `\\displaystyle${texstring}`;
  } else {
    texstring = `\\textstyle${texstring}`;
  }
  texstring = texstring.replace('$', '\\$');

  const node = document.createElement('img');
  if (typeof encodeURIComponent == 'function') {
    texstring = encodeURIComponent(texstring);
  } else {
    texstring = escape(texstring);
  }
  node.src = `${AMTcgiloc}?${texstring}`;
  node.style.verticalAlign = 'middle';
  if (config.showasciiformulaonhover) { // fixed by djhsu so newline
    node.setAttribute('title', str.replace(/\s+/g, ' '));
  }// does not show in Gecko

  const snode = document.createElement('span');
  snode.appendChild(node); // chg
  return snode;
}
// alias to align with wFallback function
function AMTparseMath(str) {
  return AMparseMath(str);
}

function AMstrarr2docFrag(arr, linebreaks) {
  const newFrag=document.createDocumentFragment();
  let expr = false;
  for (let i=0; i<arr.length; i+=1) {
    if (expr) newFrag.appendChild(AMparseMath(arr[i]));
    else {
      const arri = (linebreaks ? arr[i].split('\n\n') : [arr[i]]);
      newFrag.appendChild(document.createElement('span')
      .appendChild(document.createTextNode(arri[0])));
      for (let j=1; j<arri.length; j+=1) {
        newFrag.appendChild(document.createElement('p'));
        newFrag.appendChild(document.createElement('span')
        .appendChild(document.createTextNode(arri[j])));
      }
    }
    expr = !expr;
  }
  return newFrag;
}

function AMprocessNodeR(n, linebreaks) {
  let mtch;
  let str;
  let arr;
  let frg;
  let i;
  if (n.childNodes.length === 0) {
    if ((n.nodeType!==8 || linebreaks) &&
    n.parentNode.nodeName!=='form' && n.parentNode.nodeName!=='FORM' &&
    n.parentNode.nodeName!=='textarea' && n.parentNode.nodeName!=='TEXTAREA' &&
    n.parentNode.nodeName!=='pre' && n.parentNode.nodeName!=='PRE') {
      str = n.nodeValue;
      if (!(str === null)) {
        str = str.replace(/\r\n\r\n/g, '\n\n');
        if (config.doubleblankmathdelimiter) {
          str = str.replace(/\x20\x20\./g, ` ${config.AMdelimiter1}.`);
          str = str.replace(/\x20\x20,/g, ` ${config.AMdelimiter1},`);
          str = str.replace(/\x20\x20/g, ` ${config.AMdelimiter1} `);
        }
        str = str.replace(/\x20+/g, ' ');
        str = str.replace(/\s*\r\n/g, ' ');
        mtch = false;
        if (config.AMusedelimiter2) {
          str = str.replace(new RegExp(config.AMescape2, 'g'),
              (st) => { mtch=true; return 'AMescape2'; });
        }
        str = str.replace(new RegExp(config.AMescape1, 'g'),
              (st) => { mtch=true; return 'AMescape1'; });
        if (config.AMusedelimiter2) str = str.replace(new RegExp(config.AMdelimiter2regexp, 'g'), config.AMdelimiter1);
        arr = str.split(config.AMdelimiter1);
        for (i=0; i<arr.length; i+=1) {
          if (config.AMusedelimiter2) {
            arr[i]=arr[i].replace(/AMescape2/g, config.AMdelimiter2).replace(/AMescape1/g, config.AMdelimiter1);
          } else {
            arr[i]=arr[i].replace(/AMescape1/g, config.AMdelimiter1);
          }
        }
        if (arr.length>1 || mtch) {
          frg = AMstrarr2docFrag(arr, n.nodeType===8);
          const len = frg.childNodes.length;
          n.parentNode.replaceChild(frg, n);
          return len-1;
        }
      }
    } else return 0;
  } else if (n.nodeName!=='math') { // should this change to img?
    for (i=0; i<n.childNodes.length; i+=1) { i += AMprocessNodeR(n.childNodes[i], linebreaks); }
  }
  return 0;
}

function AMprocessNode(n, linebreaks, spanclassAM) {
  let frag;
  let st;
  if (spanclassAM!=null) {
    frag = document.getElementsByTagName('span');
    for (let i=0; i<frag.length; i+=1) {
      if (frag[i].className === 'AM') { AMprocessNodeR(frag[i], linebreaks); }
    }
  } else {
    try {
      st = n.innerHTML;
    } catch (err) { /* empty */ }
    if (st==null ||
        st.indexOf(config.AMdelimiter1)!==-1) { AMprocessNodeR(n, linebreaks); }// || st.indexOf(config.AMdelimiter2)!=-1)
  }
}

// let AMbody;
// let AMtranslated = false;
// const AMnoMathML = true;

// function translate(spanclassAM) {
//   if (!AMtranslated) { // run this only once
//     AMtranslated = true;
//     const body = document.getElementsByTagName('body')[0];
//     const processN = document.getElementById(config.AMdocumentId);
//     AMprocessNode((processN!=null?processN:body), false, spanclassAM);
//   }
// }

AMinitSymbols();

// function generic() {
//   if (config.translateOnLoad) {
//     translate();
//   }
// }

// // setup onload function
// if (typeof window.addEventListener != 'undefined') {
//   // .. gecko, safari, konqueror and standard
//   window.addEventListener('load', generic, false);
// } else if (typeof document.addEventListener != 'undefined') {
//   // .. opera 7
//   document.addEventListener('load', generic, false);
// } else if (typeof window.attachEvent != 'undefined') {
//   // .. win/ie
//   window.attachEvent('onload', generic);
// } else if (typeof window.onload == 'function') {
//   // .. mac/ie5 and anything else that gets this far
//   // if there's an existing onload function
//   // store it
//   const existing = onload;
//   // add new onload handler
//   window.onload = function() {
//     // call existing onload function
//     existing();
//     // call generic onload function
//     generic();
//   };
// } else {
//   window.onload = generic;
// }

export default {
  // translate,
  AMTconfig: config,
  AMprocessNode,
  AMparseMath,
  AMTparseMath,
  AMTparseAMtoTeX
};