/*
LaTeXMathML.js
==============
This file contains JavaScript functions to convert LaTeX math notation
to Presentation MathML. The conversion is done while the XHTML page 
loads, and should work with Internet Explorer 6 + MathPlayer 
(http://www.dessci.com/en/products/mathplayer/) and Mozilla/Netscape 7+.
This is a convenient and inexpensive way of authoring MathML from LaTeX.

Version 0.9 Oct 1, 2004, (c) Peter Jipsen http://www.chapman.edu/~jipsen
Latest version at http://www.chapman.edu/~jipsen/mathml/LaTeXMathML.js
If you use it on a webpage, please send the URL to jipsen@chapman.edu
This file is derived from the ASCIIMathML.js script by the same author.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License (at http://www.gnu.org/copyleft/gpl.html) 
for more details.
*/

var checkForMathML = true; // put note at top of page if no MathML capability
var mathcolor = "Red";     // change it to Black or any other preferred color
var displaystyle = true;   // puts limits above and below large operators
var separatetokens = false;// if true, letter tokens must be separated by nonletters
var doubleblankmathdelimiter = false; // if true,  x+1  is equal to `x+1`
                                      // for IE this works only in <!--   -->
var isIE = document.createElementNS==null;

if (document.getElementById==null) 
  alert("This webpage requires a recent browser such as\
\nMozilla/Netscape 7+ or Internet Explorer 6+MathPlayer")

// all further global variables start with "AM"

function AMcreateElementXHTML(t) {
  if (isIE) return document.createElement(t);
  else return document.createElementNS("http://www.w3.org/1999/xhtml",t);
}

function AMisMathMLavailable() {
  var nd = AMcreateElementXHTML("center");
  nd.appendChild(document.createTextNode("To view the "));
  var an = AMcreateElementXHTML("a");
  an.appendChild(document.createTextNode("LaTeX"));
  an.setAttribute("href","http://www.chapman.edu/~jipsen/asciimath.html");
  nd.appendChild(an);
  nd.appendChild(document.createTextNode(" notation use Internet Explorer 6+"));  
  an = AMcreateElementXHTML("a");
  an.appendChild(document.createTextNode("MathPlayer"));
  an.setAttribute("href","http://www.dessci.com/en/products/mathplayer/download.htm");
  nd.appendChild(an);
  nd.appendChild(document.createTextNode(" or Netscape/Mozilla/Firefox"));
  if (navigator.appName.slice(0,8)=="Netscape") 
    if (navigator.appVersion.slice(0,1)>="5") return null;
    else return nd;
  else if (navigator.appName.slice(0,9)=="Microsoft")
    try {
        var ActiveX = new ActiveXObject("MathPlayer.Factory.1");
        return null;
    } catch (e) {
        return nd;
    }
  else return nd;
}

// character lists for Mozilla/Netscape fonts
var AMcal = [0xEF35,0x212C,0xEF36,0xEF37,0x2130,0x2131,0xEF38,0x210B,0x2110,0xEF39,0xEF3A,0x2112,0x2133,0xEF3B,0xEF3C,0xEF3D,0xEF3E,0x211B,0xEF3F,0xEF40,0xEF41,0xEF42,0xEF43,0xEF44,0xEF45,0xEF46];
var AMfrk = [0xEF5D,0xEF5E,0x212D,0xEF5F,0xEF60,0xEF61,0xEF62,0x210C,0x2111,0xEF63,0xEF64,0xEF65,0xEF66,0xEF67,0xEF68,0xEF69,0xEF6A,0x211C,0xEF6B,0xEF6C,0xEF6D,0xEF6E,0xEF6F,0xEF70,0xEF71,0x2128];
var AMbbb = [0xEF8C,0xEF8D,0x2102,0xEF8E,0xEF8F,0xEF90,0xEF91,0x210D,0xEF92,0xEF93,0xEF94,0xEF95,0xEF96,0x2115,0xEF97,0x2119,0x211A,0x211D,0xEF98,0xEF99,0xEF9A,0xEF9B,0xEF9C,0xEF9D,0xEF9E,0x2124];

var CONST = 0, UNARY = 1, BINARY = 2, INFIX = 3, LEFTBRACKET = 4, 
    RIGHTBRACKET = 5, SPACE = 6, UNDEROVER = 7, DEFINITION = 8; // token types

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

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

//binary operation symbols
{input:"\\cdot",  tag:"mo", output:"\u22C5", ttype:CONST},
{input:"\\star", tag:"mo", output:"\u22C6", ttype:CONST},
{input:"\\backslash", tag:"mo", output:"\\",   ttype:CONST},
{input:"\\setminus", tag:"mo", output:"\\",   ttype:CONST},
{input:"\\times", tag:"mo", output:"\u00D7", ttype:CONST},
{input:"\\divide", tag:"mo", output:"\u00F7", ttype:CONST},
{input:"\\circ",  tag:"mo", output:"\u2218", ttype:CONST},
{input:"\\oplus", tag:"mo", output:"\u2295", ttype:CONST},
{input:"\\otimes", tag:"mo", output:"\u2297", ttype:CONST},
{input:"\\odot", tag:"mo", output:"\u2299", ttype:CONST},
{input:"\\sum", tag:"mo", output:"\u2211", ttype:UNDEROVER},
{input:"\\prod", tag:"mo", output:"\u220F", ttype:UNDEROVER},
{input:"\\wedge",  tag:"mo", output:"\u2227", ttype:CONST},
{input:"\\bigwedge", tag:"mo", output:"\u22C0", ttype:UNDEROVER},
{input:"\\vee",  tag:"mo", output:"\u2228", ttype:CONST},
{input:"\\bigvee", tag:"mo", output:"\u22C1", ttype:UNDEROVER},
{input:"\\cap",  tag:"mo", output:"\u2229", ttype:CONST},
{input:"\\bigcap", tag:"mo", output:"\u22C2", ttype:UNDEROVER},
{input:"\\cup",  tag:"mo", output:"\u222A", ttype:CONST},
{input:"\\bigcup", tag:"mo", output:"\u22C3", ttype:UNDEROVER},

//binary relation symbols
{input:"\\neq",  tag:"mo", output:"\u2260", ttype:CONST},
{input:"\\ne",  tag:"mo", output:"\u2260", ttype:CONST},
{input:"\\lt",  tag:"mo", output:"<",      ttype:CONST},
{input:"\\le",  tag:"mo", output:"\u2264", ttype:CONST},
{input:"\\leq",  tag:"mo", output:"\u2264", ttype:CONST},
{input:"\\ge",  tag:"mo", output:"\u2265", ttype:CONST},
{input:"\\geq",  tag:"mo", output:"\u2265", ttype:CONST},
{input:"\\prec",  tag:"mo", output:"\u227A", ttype:CONST},
{input:"\\succ",  tag:"mo", output:"\u227B", ttype:CONST},
{input:"\\in",  tag:"mo", output:"\u2208", ttype:CONST},
{input:"\\notin", tag:"mo", output:"\u2209", ttype:CONST},
{input:"\\subset", tag:"mo", output:"\u2282", ttype:CONST},
{input:"\\supset", tag:"mo", output:"\u2283", ttype:CONST},
{input:"\\subseteq", tag:"mo", output:"\u2286", ttype:CONST},
{input:"\\supseteq", tag:"mo", output:"\u2287", ttype:CONST},
{input:"\\equiv",  tag:"mo", output:"\u2261", ttype:CONST},
{input:"\\cong",  tag:"mo", output:"\u2245", ttype:CONST},
{input:"\\approx",  tag:"mo", output:"\u2248", ttype:CONST},
{input:"\\propto", tag:"mo", output:"\u221D", ttype:CONST},

//logical symbols
{input:"\\neg", tag:"mo", output:"\u00AC", ttype:CONST},
{input:"\\implies",  tag:"mo", output:"\u21D2", ttype:CONST},
{input:"\\iff", tag:"mo", output:"\u21D4", ttype:CONST},
{input:"\\forall",  tag:"mo", output:"\u2200", ttype:CONST},
{input:"\\exists",  tag:"mo", output:"\u2203", ttype:CONST},
{input:"\\bot", tag:"mo", output:"\u22A5", ttype:CONST},
{input:"\\top",  tag:"mo", output:"\u22A4", ttype:CONST},
{input:"\\vdash",  tag:"mo", output:"\u22A2", ttype:CONST},
{input:"\\models",  tag:"mo", output:"\u22A8", ttype:CONST},

//grouping brackets
{input:"(", tag:"mo", output:"(", ttype:LEFTBRACKET},
{input:")", tag:"mo", output:")", ttype:RIGHTBRACKET},
{input:"[", tag:"mo", output:"[", ttype:LEFTBRACKET},
{input:"]", tag:"mo", output:"]", ttype:RIGHTBRACKET},
{input:"\\{", tag:"mo", output:"{", ttype:LEFTBRACKET},
{input:"\\}", tag:"mo", output:"}", ttype:RIGHTBRACKET},
{input:"\\langle", tag:"mo", output:"\u2329", ttype:LEFTBRACKET},
{input:"\\rangle", tag:"mo", output:"\u232A", ttype:RIGHTBRACKET},
{input:"{", tag:"mo", output:"{:", ttype:LEFTBRACKET, invisible:true},
{input:"}", tag:"mo", output:":}", ttype:RIGHTBRACKET, invisible:true},

//miscellaneous symbols
{input:"\\int",  tag:"mo", output:"\u222B", ttype:CONST},
{input:"\\oint", tag:"mo", output:"\u222E", ttype:CONST},
{input:"\\partial",  tag:"mo", output:"\u2202", ttype:CONST},
{input:"\\nabla", tag:"mo", output:"\u2207", ttype:CONST},
{input:"\\pm",   tag:"mo", output:"\u00B1", ttype:CONST},
{input:"\\emptyset",   tag:"mo", output:"\u2205", ttype:CONST},
{input:"\\infty",   tag:"mo", output:"\u221E", ttype:CONST},
{input:"\\aleph", tag:"mo", output:"\u2135", ttype:CONST},
{input:"\\ldots",  tag:"mo", output:"...",    ttype:CONST},
{input:"\\ ",  tag:"mo", output:"\u00A0", ttype:CONST},
{input:"\\quad", tag:"mo", output:"\u00A0\u00A0", ttype:CONST},
{input:"\\qquad", tag:"mo", output:"\u00A0\u00A0\u00A0\u00A0", ttype:CONST},
{input:"\\cdots", tag:"mo", output:"\u22EF", ttype:CONST},
{input:"\\diamond", tag:"mo", output:"\u22C4", ttype:CONST},
{input:"\\square", tag:"mo", output:"\u25A1", ttype:CONST},
{input:"\\lfloor", tag:"mo", output:"\u230A",  ttype:CONST},
{input:"\\rfloor", tag:"mo", output:"\u230B",  ttype:CONST},
{input:"\\lceiling", tag:"mo", output:"\u2308",  ttype:CONST},
{input:"\\rceiling", tag:"mo", output:"\u2309",  ttype:CONST},

//standard functions
{input:"\\lim",  tag:"mo", output:"lim", ttype:UNDEROVER},
{input:"\\sin",  tag:"mo", output:"sin", ttype:CONST},
{input:"\\cos",  tag:"mo", output:"cos", ttype:CONST},
{input:"\\tan",  tag:"mo", output:"tan", ttype:CONST},
{input:"\\sinh", tag:"mo", output:"sinh", ttype:CONST},
{input:"\\cosh", tag:"mo", output:"cosh", ttype:CONST},
{input:"\\tanh", tag:"mo", output:"tanh", ttype:CONST},
{input:"\\cot",  tag:"mo", output:"cot", ttype:CONST},
{input:"\\sec",  tag:"mo", output:"sec", ttype:CONST},
{input:"\\csc",  tag:"mo", output:"csc", ttype:CONST},
{input:"\\log",  tag:"mo", output:"log", ttype:CONST},
{input:"\\ln",   tag:"mo", output:"ln",  ttype:CONST},
{input:"\\det",  tag:"mo", output:"det", ttype:CONST},
{input:"\\dim",  tag:"mo", output:"dim", ttype:CONST},
{input:"\\mod",  tag:"mo", output:"mod", ttype:CONST},
{input:"\\gcd",  tag:"mo", output:"gcd", ttype:CONST},
{input:"\\lcm",  tag:"mo", output:"lcm", ttype:CONST},
{input:"\\lub",  tag:"mo", output:"lub", ttype:CONST},
{input:"\\glb",  tag:"mo", output:"glb", ttype:CONST},
{input:"\\min",  tag:"mo", output:"min", ttype:UNDEROVER},
{input:"\\max",  tag:"mo", output:"max", ttype:UNDEROVER},

//arrows
{input:"\\uparrow", tag:"mo", output:"\u2191", ttype:CONST},
{input:"\\downarrow", tag:"mo", output:"\u2193", ttype:CONST},
{input:"\\rightarrow", tag:"mo", output:"\u2192", ttype:CONST},
{input:"\\to",   tag:"mo", output:"\u2192", ttype:CONST},
{input:"\\leftarrow", tag:"mo", output:"\u2190", ttype:CONST},
{input:"\\leftrightarrow", tag:"mo", output:"\u2194", ttype:CONST},
{input:"\\Rightarrow", tag:"mo", output:"\u21D2", ttype:CONST},
{input:"\\Leftarrow", tag:"mo", output:"\u21D0", ttype:CONST},
{input:"\\Leftrightarrow", tag:"mo", output:"\u21D4", ttype:CONST},

//commands with argument
AMsqrt, AMroot, AMfrac, AMover, AMsub, AMsup,
{input:"\\hat", tag:"mover", output:"\u005E", ttype:UNARY, acc:true},
{input:"\\bar", tag:"mover", output:"\u00AF", ttype:UNARY, acc:true},
{input:"\\overline", tag:"mover", output:"\u00AF", ttype:UNARY, acc:true},
{input:"\\vec", tag:"mover", output:"\u2192", ttype:UNARY, acc:true},
{input:"\\dot", tag:"mover", output:".",      ttype:UNARY, acc:true},
{input:"\\ddot", tag:"mover", output:"..",    ttype:UNARY, acc:true},
{input:"\\underline", tag:"munder", output:"\u0332", ttype:UNARY, acc:true},
AMtext, AMmbox,
{input:"\\mathbf", tag:"mstyle", atname:"fontweight", atval:"bold", output:"mathbf", ttype:UNARY},
{input:"\\mathsf", tag:"mstyle", atname:"fontfamily", atval:"sans-serif", output:"mathsf", ttype:UNARY},
{input:"\\mathbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", output:"mathbb", ttype:UNARY, codes:AMbbb},
{input:"\\mathcal", tag:"mstyle", atname:"mathvariant", atval:"script", output:"mathcal", ttype:UNARY, codes:AMcal},
{input:"\\mathtt", tag:"mstyle", atname:"fontfamily", atval:"monospace", output:"mathtt", ttype:UNARY},
{input:"\\mathfrak",  tag:"mstyle", atname:"mathvariant", atval:"fraktur", output:"mathfrak", ttype:UNARY, codes:AMfrk}
];

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

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

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

var AMmathml = "http://www.w3.org/1998/Math/MathML";

function AMcreateElementMathML(t) {
  if (isIE) return document.createElement("mml:"+t);
  else return document.createElementNS(AMmathml,t);
}

function AMcreateMmlNode(name,frag) {
  var node = AMcreateElementMathML(name);
  node.appendChild(frag);
  return node;
}

function AMremoveCharsAndBlanks(str,n) {
//remove n characters and any following blanks
  var st;
  st = str.slice(n);
  for (var i=0; i<st.length && st.charCodeAt(i)<=32; i=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
  if (n==0) {
    var h,m;
    n = -1;
    h = arr.length;
    while (n+1<h) {
      m = (n+h) >> 1;
      if (arr[m]<str) n = m; else h = m;
    }
    return h;
  } else
    for (var i=n; i<arr.length && arr[i]<str; i++);
  return i; // i=arr.length || arr[i]>=str
}

var AMseparated = true;

function AMgetSymbol(str) {
//return maximal initial substring of str that appears in names
//return null if there is none
  var k = 0; //new pos
  var j = 0; //old pos
  var mk; //match pos
  var st,i=1;
  var tagst;
  var match = "";
  var more = true;
  if (str.charAt(0)=="\\") {
    while (i<str.length && more) {
      st = str.charAt(i);
      more = ("A"<=st && st<="Z" || "a"<=st && st<="z");
      i++;
    }
    st = str.slice(0,i-1); //letter name
    if (st.length==1 && str.length>1) {
      st = str.slice(0,2);
//      if (st!="\\ " && st!="\\{" && st!="\\}") st = "\\";
    }
  } else st = str.charAt(0);
  k = AMposition(AMnames, st, 0);
  if (k<AMnames.length && str.slice(0,AMnames[k].length)==AMnames[k]){
    match = AMnames[k];
  }
  if (match!="") return AMsymbols[k];
// if str[0] is a digit or - return maxsubstring of digits.digits
  k = 1;
  st = str.slice(0,1);
  var pos = true;
  var integ = true;
  if (st == "-") {
    pos = false;
    st = str.slice(k,k+1);
    k++;
  }
  while ("0"<=st && st<="9" && k<=str.length) {
    st = str.slice(k,k+1);
    k++;
  }
  if (st == ".") {
    integ = false;
    st = str.slice(k,k+1);
    k++;
    while ("0"<=st && st<="9" && k<=str.length) {
      st = str.slice(k,k+1);
      k++;
    }
  }
  if ((pos && integ && k>1) || ((pos || integ) && k>2) || k>3) {
    st = str.slice(0,k-1);
    tagst = "mn";
    AMseparated = true;
  } else {
    k = 2;
    st = str.slice(0,1); //take 1 character
    AMseparated = ("A">st || st>"Z") && ("a">st || st>"z");
    tagst = (AMseparated?"mo":"mi");
    AMseparated = AMseparated || str.charAt(1)<"a" || str.charAt(1)>"z";
  }
  return {input:str.slice(0,k-1), tag:tagst, output:st, ttype:CONST};
}

function AMremoveBrackets(node) {
  var st;
  if (node.nodeName=="mrow") {
    st = node.firstChild.firstChild.nodeValue;
    if (st=="(" || st=="[" || st=="{") node.removeChild(node.firstChild);
  }
  if (node.nodeName=="mrow") {
    st = node.lastChild.firstChild.nodeValue;
    if (st==")" || st=="]" || st=="}") node.removeChild(node.lastChild);
  }
}

/*Parsing LaTeX math expressions with the following grammar
V ::= [A-Za-z] | greek letters | numbers | other constant symbols
U ::= \sqrt | \text | \mathbb | other unary symbols for font commands
B ::= \frac | \root | \stackrel      binary symbols
L ::= ( | [ | \{ | \langle | {       left brackets
R ::= ) | ] | \} | \rangle | }       right brackets
S ::= V | LER | US | BSS             simple expression
E ::= SE | S_S | S^S | S_S^S   expression
Each terminal symbol is translated into a corresponding mathml node.*/

var AMnestingDepth;

function AMparseSexpr(str) { //parses str and returns [node,tailstr]
  var symbol, node, result, i, st, newFrag = document.createDocumentFragment();
  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); 
    return [AMcreateMmlNode(symbol.tag,        //its a constant
                             document.createTextNode(symbol.output)),str];
  case LEFTBRACKET:   //read (expr+)
    AMnestingDepth++;
    str = AMremoveCharsAndBlanks(str,symbol.input.length); 
    result = AMparseExpr(str);
    if (typeof symbol.invisible == "boolean" && symbol.invisible) 
      node = AMcreateMmlNode("mrow",result[0]);
    else {
      node = AMcreateMmlNode("mo",document.createTextNode(symbol.output));
      node = AMcreateMmlNode("mrow",node);
      node.appendChild(result[0]);
    }
    return [node,result[1]];
  case UNARY:
    if (symbol == AMtext || symbol == AMmbox) {
      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 i = 0;
      if (i==-1) i = str.length;
      st = str.slice(1,i);
      if (st.charAt(0) == " ") {
        node = AMcreateElementMathML("mspace");
        node.setAttribute("width","1ex");
        newFrag.appendChild(node);
      }
      newFrag.appendChild(
        AMcreateMmlNode(symbol.tag,document.createTextNode(st)));
      if (st.charAt(st.length-1) == " ") {
        node = AMcreateElementMathML("mspace");
        node.setAttribute("width","1ex");
        newFrag.appendChild(node);
      }
      str = AMremoveCharsAndBlanks(str,i+1);
      return [AMcreateMmlNode("mrow",newFrag),str];
    } else {
      str = AMremoveCharsAndBlanks(str,symbol.input.length); 
      result = AMparseSexpr(str);
      if (result[0]==null) return [AMcreateMmlNode("mo",
                             document.createTextNode(symbol.input)),str];
      AMremoveBrackets(result[0]);
      if (symbol == AMsqrt) {           // sqrt
        return [AMcreateMmlNode(symbol.tag,result[0]),result[1]];
      } else if (typeof symbol.acc == "boolean" && symbol.acc) {   // accent
        node = AMcreateMmlNode(symbol.tag,result[0]);
        node.appendChild(AMcreateMmlNode("mo",document.createTextNode(symbol.output)));
        return [node,result[1]];
      } else {                        // font change command
        if (!isIE && typeof symbol.codes != "undefined") {
          for (i=0; i<result[0].childNodes.length; i++)
            if (result[0].childNodes[i].nodeName=="mi" || result[0].nodeName=="mi") {
              st = (result[0].nodeName=="mi"?result[0].firstChild.nodeValue:
                              result[0].childNodes[i].firstChild.nodeValue);
              var newst = [];
              for (var j=0; j<st.length; j++)
                if (st.charCodeAt(j)>64 && st.charCodeAt(j)<91) newst = newst +
                  String.fromCharCode(symbol.codes[st.charCodeAt(j)-65]);
                else newst = newst + st.charAt(j);
              if (result[0].nodeName=="mi")
                result[0]=AMcreateElementMathML("mo").
                          appendChild(document.createTextNode(newst));
              else result[0].replaceChild(AMcreateElementMathML("mo").
          appendChild(document.createTextNode(newst)),result[0].childNodes[i]);
            }
        }
        node = AMcreateMmlNode(symbol.tag,result[0]);
        node.setAttribute(symbol.atname,symbol.atval);
        return [node,result[1]];
      }
    }
  case BINARY:
    str = AMremoveCharsAndBlanks(str,symbol.input.length); 
    result = AMparseSexpr(str);
    if (result[0]==null) return [AMcreateMmlNode("mo",
                           document.createTextNode(symbol.input)),str];
    AMremoveBrackets(result[0]);
    var result2 = AMparseSexpr(result[1]);
    if (result2[0]==null) return [AMcreateMmlNode("mo",
                           document.createTextNode(symbol.input)),str];
    AMremoveBrackets(result2[0]);
    if (symbol==AMroot || symbol==AMover) newFrag.appendChild(result2[0]);
    newFrag.appendChild(result[0]);
    if (symbol==AMfrac) newFrag.appendChild(result2[0]);
    return [AMcreateMmlNode(symbol.tag,newFrag),result2[1]];
  case INFIX:
    str = AMremoveCharsAndBlanks(str,symbol.input.length); 
    return [AMcreateMmlNode("mo",document.createTextNode(symbol.output)),str];
  case SPACE:
    str = AMremoveCharsAndBlanks(str,symbol.input.length); 
    node = AMcreateElementMathML("mspace");
    node.setAttribute("width","1ex");
    newFrag.appendChild(node);
    newFrag.appendChild(
      AMcreateMmlNode(symbol.tag,document.createTextNode(symbol.output)));
    node = AMcreateElementMathML("mspace");
    node.setAttribute("width","1ex");
    newFrag.appendChild(node);
    return [AMcreateMmlNode("mrow",newFrag),str];
  default:
    str = AMremoveCharsAndBlanks(str,symbol.input.length); 
    return [AMcreateMmlNode(symbol.tag,        //its a constant
                             document.createTextNode(symbol.output)),str];
  }
}

function AMparseExpr(str) {
  var symbol, sym1, sym2, node, result, i, underover, nodeList = [],
  newFrag = document.createDocumentFragment();
  do {
    str = AMremoveCharsAndBlanks(str,0);
    sym1 = AMgetSymbol(str);
    result = AMparseSexpr(str);
    node = result[0];
    str = result[1];
    symbol = AMgetSymbol(str);
    if (symbol.ttype == INFIX) {
      str = AMremoveCharsAndBlanks(str,symbol.input.length);
      result = AMparseSexpr(str);
      AMremoveBrackets(result[0]);
      str = result[1];
      if (symbol == AMsub) {
        sym2 = AMgetSymbol(str);
        underover = (sym1.ttype == UNDEROVER);
        if (sym2 == AMsup) {
          str = AMremoveCharsAndBlanks(str,sym2.input.length);
          var res2 = AMparseSexpr(str);
          AMremoveBrackets(res2[0]);
          str = res2[1];
          node = AMcreateMmlNode((underover?"munderover":"msubsup"),node);
          node.appendChild(result[0]);
          node.appendChild(res2[0]);
          node = AMcreateMmlNode("mrow",node); // so sum does not stretch
        } else {
          node = AMcreateMmlNode((underover?"munder":"msub"),node);
          node.appendChild(result[0]);
        }
      } else {
        node = AMcreateMmlNode(symbol.tag,node);
        node.appendChild(result[0]);
      }
      newFrag.appendChild(node);
    } 
    else if (node!=undefined) newFrag.appendChild(node);
  } while ((symbol.ttype != RIGHTBRACKET || AMnestingDepth == 0) && 
           symbol!=null && symbol.output!="");
  if (symbol.ttype == RIGHTBRACKET) {
    if (AMnestingDepth > 0) AMnestingDepth--;
    var len = newFrag.childNodes.length;
    if (len>0 && newFrag.childNodes[len-1].nodeName == "mrow" && len>1 &&
      newFrag.childNodes[len-2].nodeName == "mo" &&
      newFrag.childNodes[len-2].firstChild.nodeValue == ",") { //matrix
      var right = newFrag.childNodes[len-1].lastChild.firstChild.nodeValue;
      if (right==")" || right=="]") {
        var left = newFrag.childNodes[len-1].firstChild.firstChild.nodeValue;
        if (left=="(" && right==")" && symbol.output != "}" || 
            left=="[" && right=="]") {
        var pos = []; // positions of commas
        var matrix = true;
        var m = newFrag.childNodes.length;
        for (i=0; matrix && i<m; i=i+2) {
          pos[i] = [];
          node = newFrag.childNodes[i];
          if (matrix) matrix = node.nodeName=="mrow" && 
            (i==m-1 || node.nextSibling.nodeName=="mo" && 
            node.nextSibling.firstChild.nodeValue==",")&&
            node.firstChild.firstChild.nodeValue==left &&
            node.lastChild.firstChild.nodeValue==right;
          if (matrix) 
            for (var j=0; j<node.childNodes.length; j++)
              if (node.childNodes[j].firstChild.nodeValue==",")
                pos[i][pos[i].length]=j;
          if (matrix && i>1) matrix = pos[i].length == pos[i-2].length;
        }
        if (matrix) {
          var row, frag, n, k, table = document.createDocumentFragment();
          for (i=0; i<m; i=i+2) {
            row = document.createDocumentFragment();
            frag = document.createDocumentFragment();
            node = newFrag.firstChild; // <mrow>(-,-,...,-,-)</mrow>
            n = node.childNodes.length;
            k = 0;
            node.removeChild(node.firstChild); //remove (
            for (j=1; j<n-1; j++) {
              if (typeof pos[i][k] != "undefined" && j==pos[i][k]){
                node.removeChild(node.firstChild); //remove ,
                row.appendChild(AMcreateMmlNode("mtd",frag));
                k++;
              } else frag.appendChild(node.firstChild);
            }
            row.appendChild(AMcreateMmlNode("mtd",frag));
            if (newFrag.childNodes.length>2) {
              newFrag.removeChild(newFrag.firstChild); //remove <mrow>)</mrow>
              newFrag.removeChild(newFrag.firstChild); //remove <mo>,</mo>
            }
            table.appendChild(AMcreateMmlNode("mtr",row));
          }
          node = AMcreateMmlNode("mtable",table);
          if (typeof symbol.invisible == "boolean" && symbol.invisible) node.setAttribute("columnalign","left");
          newFrag.replaceChild(node,newFrag.firstChild);
        }
       }
      }
    }
    str = AMremoveCharsAndBlanks(str,symbol.input.length);
    if (typeof symbol.invisible != "boolean" || !symbol.invisible) {
      node = AMcreateMmlNode("mo",document.createTextNode(symbol.output));
      newFrag.appendChild(node);
    }
  }
  return [newFrag,str];
}

function AMparseMath(str) {
  var result, node = AMcreateElementMathML("mstyle");
  node.setAttribute("mathcolor",mathcolor);
  if (displaystyle) node.setAttribute("displaystyle","true");
  AMnestingDepth = 0;
  node.appendChild(AMparseExpr(str.replace(/^\s+/g,""))[0]);
  return AMcreateMmlNode("math",node);
}

function AMstrarr2docFrag(arr, linebreaks) {
  var newFrag=document.createDocumentFragment();
  var expr = false;
  for (var i=0; i<arr.length; i++) {
    if (expr) newFrag.appendChild(AMparseMath(arr[i]));
    else {
      var arri = (linebreaks ? arr[i].split("\n\n") : [arr[i]]);
      newFrag.appendChild(AMcreateElementXHTML("span").
      appendChild(document.createTextNode(arri[0].
        replace(/\\dollar/g,"$"))));
      for (var j=1; j<arri.length; j++) {
        newFrag.appendChild(AMcreateElementXHTML("p"));
        newFrag.appendChild(AMcreateElementXHTML("span").
        appendChild(document.createTextNode(arri[j].
          replace(/\\dollar/g,"$"))));
      }
    }
    expr = !expr;
  }
  return newFrag;
}

function AMprocessNode(n, linebreaks) {
  var mtch, str, arr;
  if (n.childNodes.length == 0 && (n.nodeType!=8 || linebreaks) &&
    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");
      str = str.replace(/\x20+/g," ");
      str = str.replace(/\s*\r\n/g," ");
      mtch = false;
      str = str.replace(/\\\$/g,function(st){mtch=true;return "\\dollar"});
      arr = str.split("$");
      if (arr.length>1 || mtch) {
        if (checkForMathML) {
          checkForMathML = false;
          var nd = AMisMathMLavailable();
          AMnoMathML = nd != null
          if (AMnoMathML) AMbody.insertBefore(nd,AMbody.childNodes[0]);
        }
        if (!AMnoMathML)
          n.parentNode.replaceChild(AMstrarr2docFrag(arr,n.nodeType==8),n);
      }
    }
  } else if (n.nodeName!="math") 
    for (var i=0; i<n.childNodes.length; i++)
      AMprocessNode(n.childNodes[i], linebreaks);
}

var AMbody;
var AMnoMathML = false;

function translate() {
  AMinitSymbols();
  AMbody = document.getElementsByTagName("body")[0];
  AMprocessNode(AMbody, false);
}
