Subversion Repositories wimsdev

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*  jqmath.js:  a JavaScript module for symbolic expressions, e.g. formatted mathematical
  2.         formulas.  This file uses charset UTF-8, and requires jQuery 1.0+, jsCurry, and jqmath.css.
  3.         By default, we use Presentation MathML when available, else simple HTML and CSS.
  4.         Expressions may be constructed programmatically, or using a simple TeX-like syntax.
  5.        
  6.         To use symbolic expressions in a web page or problem domain, one must choose a set of
  7.         symbols, ensure they can be viewed by users, and specify precedences for the operator
  8.         symbols.  We use Unicode character numbers for encoding, and fonts for display.  Normally
  9.         standard characters and fonts suffice, but "private use" character numbers and custom fonts
  10.         can be used when necessary.  By default, this module currently uses standard MathML 3
  11.         precedences for operator symbols, except we omit "multiple character operator"s like && or
  12.         <=, and choose a single precedence for each of |, ^, and _.
  13.        
  14.         The algorithm to detect MathML only works after some document.body is defined and available.
  15.         Thus it should probably not be used during document loading.
  16.        
  17.         See http://mathscribe.com/author/jqmath.html for usage documentation and examples, and
  18.         jscurry.js for some coding conventions and goals.
  19.        
  20.         Copyright 2013, Mathscribe, Inc.  Dual licensed under the MIT or GPL Version 2 licenses.
  21.         See e.g. http://jquery.org/license for an explanation of these licenses.  */
  22.  
  23.  
  24. "use strict";
  25.  
  26.  
  27. var jqMath = function() {
  28.         var $ = jQuery, F = jsCurry;
  29.        
  30.         function M(x, y, z) /* any number of arguments */ {
  31.                 if (typeof x == 'number')       x = String(x);  // e.g. for small integers
  32.                 if (typeof x == 'string' || Array.isArray(x))   return M.sToMathE(x, y, z);
  33.                 if (x.nodeType == 1 /* Element */ && x.tagName.toLowerCase() == 'math')
  34.                         return M.eToMathE(x);
  35.                 F.err(err_M_);
  36.         }
  37.        
  38.         M.toArray1 = function(g) { return Array.isArray(g) ? g : [g]; };
  39.        
  40.         M.sign = function(x) { return x > 0 ? 1 : x < 0 ? -1 : x == 0 ? 0 : NaN; }
  41.        
  42.         M.getSpecAttrP = function(e, attrName) {
  43.                 var attrP = e.getAttributeNode(attrName);
  44.                 return attrP && /* for IE6-7: */ attrP.specified !== false ? attrP.value : null;
  45.         };
  46.         M.objToAttrs = function(obj) {
  47.                 var res = [];
  48.                 for (var p in obj)      res.push({ name: p, value: obj[p] });
  49.                 return res;
  50.         };
  51.         M.setAttrs = function(e, attrsP) /* uses attr.name/value/[specified for IE6-7] */ {
  52.                 if (attrsP && attrsP.length == null)    attrsP = M.objToAttrs(attrsP);
  53.                
  54.                 F.iter(function(attr) {
  55.                                 if (attr.specified !== false /* for IE6-7 */)
  56.                                         e.setAttribute(attr.name, attr.value);
  57.                         }, attrsP || []);
  58.                 return e;
  59.         };
  60.        
  61.         M.replaceNode = function(newNode, oldNode) /* returns newNode (unlike node.replaceChild) */
  62.         {
  63.                 oldNode.parentNode.replaceChild(newNode, oldNode);
  64.                 return newNode;
  65.         };
  66.        
  67.         M.addClass = function(e, ws) {
  68.                 // $(e).addClass(ws) doesn't seem to work for XML elements
  69.                 if (typeof e.className != 'undefined') {        // needed for old IE, works for non-XML
  70.                         var classes = e.className;
  71.                         e.className = (classes ? classes+' ' : '')+ws;
  72.                 } else {        // needed for XML, would work for non-IE
  73.                         var classes = e.getAttribute('class');
  74.                         e.setAttribute('class', (classes ? classes+' ' : '')+ws);
  75.                 }
  76.                 return e;
  77.         };
  78.         M.eToClassesS = function(e) {
  79.                 var sP = typeof e.className != 'undefined' ? e.className : e.getAttribute('class');
  80.                 return sP || '';
  81.         };
  82.         M.hasClass = function(e, w) {   // works for HTML or XML elements, unlike jQuery e.g. 1.4.3
  83.                 return (' '+M.eToClassesS(e)+' ').replace(/[\n\t]/g, ' ').indexOf(' '+w+' ') != -1;
  84.                         // we replace /[\n\t]/g to be consistent with jQuery
  85.         };
  86.        
  87.         M.inlineBlock = function(/* ... */) /* each argument is a DOM elt, jQuery object, or HTML
  88.                         string */ {
  89.                 var res$ = $('<div/>').css('display', 'inline-block');
  90.                 if (arguments.length)   res$.append.apply(res$, arguments);
  91.                 return res$[0];
  92.         };
  93.        
  94.         M.tr$ = function(/* ... */) /* each argument is a DOM elt, jQuery object, HTML string, or
  95.                         Array; each argument produces one <td> */ {
  96.                 var appendMF = $().append;
  97.                 function td$(g) { return appendMF.apply($('<td/>'), M.toArray1(g)); }
  98.                 return appendMF.apply($('<tr/>'), F.map(td$, arguments));
  99.         };
  100.        
  101.         M.mathmlNS = "http://www.w3.org/1998/Math/MathML";      // MathML namespace
  102.        
  103.         function appendMeArgs(e, args /* null/undefined, string, node, or array-like of nodes incl.
  104.                         live NodeList */, attrsP) {
  105.                 if (args == null)       ;
  106.                 else if (typeof args == 'string')
  107.                         e.appendChild(e.ownerDocument.createTextNode(args));
  108.                 else if (args.nodeType) e.appendChild(args);
  109.                 else {
  110.                         if (args.constructor != Array)  args = F.slice(args);   // for live NodeList
  111.                         F.iter(function(x) { e.appendChild(x); }, args);
  112.                 }
  113.                 return M.setAttrs(e, attrsP);
  114.         }
  115.        
  116.         var fixMathMLQ_ = false;
  117.         (function() {   // M.webkitVersion, M.operaVersion, M.msieVersion, M.mozillaVersion
  118.                 var ua = navigator.userAgent.toLowerCase(), match = ua.match(/webkit[ \/](\d+)\.(\d+)/);
  119.                 if (match) {
  120.                         M.webkitVersion = [Number(match[1]), Number(match[2])];
  121.                         fixMathMLQ_ = M.webkitVersion[0] <= 540 /*@@*/;
  122.                 } else {
  123.                         match = ua.match(/(opera)(?:.*version)?[ \/]([\w.]+)/) ||
  124.                                 ua.match(/(msie) ([\w.]+)/) ||
  125.                                 ua.indexOf("compatible") < 0 && ua.match(/(mozilla)(?:.*? rv:([\w.]+))?/);
  126.                         if (match)      M[match[1]+'Version'] = match[2] || "0";
  127.                 }
  128.         })();
  129.         if (M.msieVersion)
  130.                 document.write(
  131.                         '<object id=MathPlayer classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">',
  132.                         '</object><?IMPORT namespace="m" implementation="#MathPlayer" ?>');
  133.         // M.MathPlayer controls whether the IE plugin MathPlayer can be used.
  134.         function checkMathMLAttrs(e) {
  135.                 if (M.MathML && ! fixMathMLQ_)  return e;
  136.                
  137.                 var tagName = e.tagName.toLowerCase(), doc = e.ownerDocument;
  138.                 function new1(k) { return doc.createElementNS(M.mathmlNS, k); }
  139.                 if (tagName == 'mi') {
  140.                         if (! e.getAttribute('mathvariant') && e.firstChild
  141.                         && e.firstChild.nodeType == 3 /* Text */)
  142.                                 e.setAttribute('mathvariant',
  143.                                         e.firstChild.data.length == 1 ? 'italic' : 'normal');
  144.                 } else if (tagName == 'mo') {
  145.                         if (e.childNodes.length == 1 && e.firstChild.nodeType == 3 /* Text */) {
  146.                                 var s = e.firstChild.data;
  147.                                 if (/^[\u2061-\u2064]$/.test(s))        M.addClass(e, 'ma-non-marking');
  148.                                 /*@@ move parse_mxP_tokP fm- operator spacing here!? */
  149.                         }
  150.                 } else if (tagName == 'mspace') {       //@@ combine with M.newMe 'mspace' clause
  151.                         if (M.webkitVersion && M.MathML) {
  152.                                 e.style.display = 'inline-block';
  153.                                 e.style.minWidth = e.getAttribute('width') || '0px';
  154.                         }
  155.                 } else if (tagName == 'menclose') {
  156.                         if (M.webkitVersion && M.MathML)        M.addClass(e, 'fm-menclose');
  157.                 } else if (tagName == 'mmultiscripts') {
  158.                         if (M.webkitVersion) {
  159.                                 var a = F.slice(e.childNodes);  // partly because e.childNodes is dynamic
  160.                                 if (a.length == 0)      throw 'Wrong number of <mmultiscripts> arguments: 0';
  161.                                 var rowA = [a[0]];
  162.                                 for (var i = 1; i < a.length; i++) {
  163.                                         if (a[i].tagName == 'mprescripts') {
  164.                                                 rowA.unshift(new1('none'));
  165.                                                 continue;
  166.                                         }
  167.                                         if (i + 1 == a.length)  throw 'Missing argument in <mmultiscripts>';
  168.                                         var a3 = [rowA[0], a[i], a[i + 1]];
  169.                                         i++;
  170.                                         rowA[0] = new1('msubsup');
  171.                                         F.iter(function(arg) { rowA[0].appendChild(arg); }, a3);
  172.                                 }
  173.                                 var oldE = e;
  174.                                 e = appendMeArgs(new1('mrow'), rowA, e.attributes);
  175.                                 if (oldE.parentNode)    oldE.parentNode.replaceChild(e, oldE);
  176.                         }
  177.                 }
  178.                
  179.                 var colorOpt = e.getAttribute('mathcolor'), hrefOpt = e.getAttribute('href');
  180.                 if (colorOpt && e.style)        e.style.color = colorOpt;
  181.                 if (hrefOpt && (! M.MathML || M.webkitVersion)) {
  182.                         var aE = doc.createElement('A'), parentP = e.parentNode, nextP = e.nextSibling;
  183.                         aE.appendChild(e);
  184.                         aE.href = hrefOpt;
  185.                         e = aE;
  186.                         if (parentP)    parentP.insertBefore(e, nextP);
  187.                 }
  188.                 return e;
  189.         }
  190.         function newMeNS(tagName, argsP /* for appendMeArgs() */, attrsP, doc)
  191.                         /* tries to use the MathML namespace, perhaps with prefix 'm' */ {
  192.                 if (! doc)      doc = document;
  193.                
  194.                 var e = M.MathPlayer ? doc.createElement('m:'+tagName) :
  195.                         doc.createElementNS(M.mathmlNS, tagName);
  196.                 return checkMathMLAttrs(appendMeArgs(e, argsP, attrsP));
  197.         }
  198.         // M.MathML controls whether MathML is used.
  199.         (function() {
  200.                 if (self.location) {
  201.                         var match = location.search.match(/[?&;]mathml=(?:(off|false)|(on|true))\b/i);
  202.                         if (match)      M.MathML = ! match[1];
  203.                         else if (M.webkitVersion && F.cmpLex(F.cmpX, M.webkitVersion, [537, 17]) < 0
  204.                         || M.operaVersion)
  205.                                 M.MathML = false;
  206.                 }
  207.         })();
  208.         M.canMathML = function() /* requires document.body */ {
  209.                 if (M.msieVersion && ! M.MathPlayer)
  210.                         try {
  211.                                 new ActiveXObject("MathPlayer.Factory.1");
  212.                                 if (M.MathPlayer == null)       M.MathPlayer = true;
  213.                                 else if (! M.MathPlayer)        return false;   // for Carlisle's MathPlayer 3
  214.                         } catch(exc) {
  215.                                 M.MathPlayer = false;
  216.                         }
  217.                 if (! M.MathPlayer && typeof document.createElementNS == 'undefined')   return false;
  218.                
  219.                 function mathMeToE(mathMe) {
  220.                         mathMe.setAttribute('display', 'block');
  221.                         return $('<div/>').append(mathMe)[0];
  222.                 }
  223.                 var e1 = newMeNS('math', newMeNS('mn', '1')),
  224.                         e2 = newMeNS('math', newMeNS('mfrac', [newMeNS('mn', '1'), newMeNS('mn', '2')])),
  225.                         es$ = $(F.map(mathMeToE, [e1, e2]));
  226.                 es$.css('visibility', 'hidden').appendTo(document.body);
  227.                 var res = $(es$[1]).height() > $(es$[0]).height() + 2;
  228.                 es$.remove();
  229.                 return res;
  230.         };
  231.        
  232.         // fmUp is mid-x to outer top, fmDn is mid-x to outer bottom, both approx. & in parent ems
  233.         function checkVertStretch(up, dn, g, doP) /* non-MathML */ {
  234.                 if (g.nodeName.toLowerCase() == 'mo' && g.childNodes.length == 1) {
  235.                         var c = g.firstChild, s = c.data;
  236.                         if (c.nodeType == 3 /* Text */ && (up > 0.9 || dn > 0.9)
  237.                         && (M.prefix_[s] < 25 || M.postfix_[s] < 25
  238.                                         || '|\u2016\u221A' /* ‖ &radic; */.indexOf(s) != -1 || doP)) {
  239.                                 var r = (up + dn) / 1.2, radicQ = s == '\u221A',
  240.                                         v = (radicQ ? 0.26 : 0.35) + ((radicQ ? 0.15 : 0.25) - dn) / r;
  241.                                 g.style.fontSize = r.toFixed(3)+'em';
  242.                                 g.style.verticalAlign = v.toFixed(3)+'em';
  243.                                 g.fmUp = up;
  244.                                 g.fmDn = dn;
  245.                                 g.style.display = 'inline-block';
  246.                                 g.style.transform = g.style.msTransform = g.style.MozTransform =
  247.                                         g.style.WebkitTransform = 'scaleX(0.5)';
  248.                         }
  249.                 }
  250.         }
  251.         function vertAlignE$(up, dn, fmVert) /* non-MathML */ {
  252.                 var e$ = $('<span/>').append(fmVert);
  253.                 e$[0].fmUp = up;
  254.                 e$[0].fmDn = dn;
  255.                 e$[0].style.verticalAlign = (0.5 * (up - dn)).toFixed(3)+'em';
  256.                 return e$;
  257.         }
  258.         M.mtagName = function(e) /* returns 'fmath' for non-MathML 'math' */ {
  259.                 if (e.tagName == 'A' && e.childNodes.length == 1)       e = e.firstChild;
  260.                 return e.getAttribute('mtagname') || e.tagName.toLowerCase().replace(/^m:/, '');
  261.         };
  262.         M.mchilds = function(e) {
  263.                 if (e.tagName == 'A' && e.childNodes.length == 1)       e = e.firstChild;
  264.                 var mSP = e.getAttribute('mtagname');
  265.                 while (e.tagName == 'SPAN')     e = e.firstChild;
  266.                 function span0(g) { g.tagName == 'SPAN' || F.err(err_span0_); return g.firstChild; }
  267.                 if (e.tagName == 'TABLE') {
  268.                         e = e.firstChild;
  269.                         e.tagName == 'TBODY' || F.err(err_mchilds_tbody_);
  270.                         if (mSP == 'mtable')    return e.childNodes;
  271.                         var a = e.childNodes;   // <tr>s for mfrac munder mover munderover
  272.                         if (mSP == 'mover')     a = [a[1], a[0]];
  273.                         else if (mSP == 'munderover')   a = [a[1], a[2], a[0]];
  274.                         return F.map(function(tr) { return tr.firstChild.firstChild; }, a);
  275.                 } else if (e.tagName == 'MROW' && mSP) {
  276.                         var a = e.childNodes;
  277.                         if (mSP == 'msqrt')     return [span0(span0(a[1]))];
  278.                         if (mSP == 'mroot')     return [span0(span0(a[2])), span0(a[0])];
  279.                         mSP == 'mmultiscripts' || F.err(err_mchilds_mrow_);
  280.                         var nPrescripts = Number(e.getAttribute('nprescripts'));
  281.                         0 <= nPrescripts && nPrescripts < a.length && nPrescripts % 2 == 0 ||
  282.                                 F.err(err_mchilds_mmultiscripts_);
  283.                         var res = [a[nPrescripts]];
  284.                         for (var i = nPrescripts + 1; i < a.length; i++)        res.push(span0(a[i]));
  285.                         if (nPrescripts) {
  286.                                 res.push(e.ownerDocument.createElement('mprescripts'));
  287.                                 for (var i = 0; i < nPrescripts; i++)   res.push(span0(a[i]));
  288.                         }
  289.                         return res;
  290.                 } else if (F.elem(e.tagName, ['MSUB', 'MSUP', 'MSUBSUP']))
  291.                         return F.map(function(c, i) { return i ? span0(c) : c; }, e.childNodes);
  292.                 else if (e.tagName == 'MSPACE') return [];
  293.                 else    return e.childNodes;
  294.         };
  295.         var mtokens_ = ['mn', 'mi', 'mo', 'mtext', 'mspace', 'ms'],
  296.                 impMRows_ =
  297.                         ['fmath', 'msqrt', 'mtd', 'mstyle', 'merror', 'mpadded', 'mphantom', 'menclose'],
  298.                 accentDsByS_ = {        // top and bottom space in ems
  299.                         '\xAF' /* ¯ macron */: [0, 0.85], '\u203E' /* ‾ overline */: [0, 0.85],
  300.                         '\u02D9' /* ˙ dot above */: [0, 0.75], '\u02C7' /* ˇ caron */: [0, 0.7],
  301.                         '^': [0, 0.5], '~': [0, 0.4], '\u2192' /* → rightwards arrow */: [0.25, 0.25],
  302.                         '_': [0.7, 0],
  303.                         // not accents in MathML 3 operator dictionary:
  304.                         '\u2212' /* − */: [0.25, 0.45], '.': [0.6, 0.1]
  305.                 };
  306.         M.newMe = function(tagName, argsP /* for appendMeArgs() */, attrsP, docP) {
  307.                 if (! docP) {
  308.                         if (attrsP && attrsP.nodeType == 9 /* Document */) {
  309.                                 docP = attrsP;
  310.                                 attrsP = null;
  311.                         } else  docP = document;
  312.                 }
  313.                 M.MathML != null || F.err(err_newMe_MathML_);
  314.                
  315.                 if (M.MathML)   return newMeNS(tagName, argsP, attrsP, docP);
  316.                
  317.                 if (tagName == 'math')  tagName = 'fmath';
  318.                 var e$ = $(appendMeArgs(docP.createElement(tagName.toUpperCase()), argsP)),
  319.                         a = F.slice(e$[0].childNodes);  // partly because e$[0].childNodes is dynamic
  320.                 if (F.elem(tagName, impMRows_) && a.length != 1) {
  321.                         a = [M.newMe('mrow', a, null, docP)];
  322.                         e$[0].childNodes.length == 0 || F.err(err_newMe_imp_mrow_);
  323.                         e$.append(a[0]);
  324.                 }
  325.                 var ups = F.map(function(g) { return Number(g.fmUp || 0.6); }, a),
  326.                         dns = F.map(function(g) { return Number(g.fmDn || 0.6); }, a);
  327.                
  328.                 if (tagName == 'fmath' || tagName == 'mn' || tagName == 'mtext' || tagName == 'mstyle'
  329.                 || tagName == 'merror' || tagName == 'mpadded' || tagName == 'mphantom'
  330.                 || tagName == 'menclose' || tagName == 'mprescripts' || tagName == 'none')
  331.                         ;
  332.                 else if (tagName == 'mi') {
  333.                         var c = a.length == 1 ? a[0] : {};
  334.                         if (c.nodeType == 3 /* Text */ && c.data.length == 1) {
  335.                                 e$.addClass('fm-mi-length-1');
  336.                                 if ('EFHIJKMNTUVWXYZdfl'.indexOf(c.data) != -1)
  337.                                         e$.css('padding-right', '0.44ex');
  338.                         }
  339.                 } else if (tagName == 'mo') {
  340.                         var c = a.length == 1 ? a[0] : {};
  341.                         if (c.nodeType == 3 /* Text */ && /[\]|([{‖)}]/.test(c.data))
  342.                                 e$.addClass('fm-mo-Luc');
  343.                 } else if (tagName == 'mspace') {
  344.                         var e = M.setAttrs(e$[0], attrsP);
  345.                         attrsP = null;
  346.                         e.style.marginRight = e.getAttribute('width') || '0px'; // may be negative
  347.                         e.style.paddingRight = '0.001em';       // Firefox work-around
  348.                         e$.append('\u200C' /* &zwnj; */);       // for Safari/Chrome
  349.                         e$.css('visibility', 'hidden'); // e.g. for iPad Mobile Safari 4.0.4
  350.                 } else if (tagName == 'mrow') {
  351.                         var up = F.applyF(Math.max, ups), dn = F.applyF(Math.max, dns);
  352.                         if (up > 0.65 || dn > 0.65) {
  353.                                 e$[0].fmUp = up;
  354.                                 e$[0].fmDn = dn;
  355.                                 F.iter(F([up, dn, F._, null], checkVertStretch), a);
  356.                         }
  357.                 } else if (tagName == 'mfrac') {
  358.                         if (a.length != 2)      throw 'Wrong number of <mfrac> arguments: '+a.length;
  359.                         var num$ = $('<td class="fm-num-frac fm-inline"></td>', docP).append(a[0]),
  360.                                 den$ = $('<td class="fm-den-frac fm-inline"></td>', docP).append(a[1]);
  361.                         e$ = vertAlignE$(ups[0] + dns[0] + 0.03, ups[1] + dns[1] + 0.03,
  362.                                 $('<span class="fm-vert fm-frac"></span>', docP).
  363.                                                 // <span> partly for IE6-7, see www.quirksmode.org/css/display.html
  364.                                         append($('<table/>', docP).
  365.                                                 append($('<tbody/>', docP).
  366.                                                         append($('<tr/>', docP).append(num$)).
  367.                                                         append($('<tr/>', docP).append(den$))))).
  368.                                 attr('mtagname', tagName);
  369.                 } else if (tagName == 'msqrt' || tagName == 'mroot') {
  370.                         if (a.length != (tagName == 'msqrt' ? 1 : 2))
  371.                                 throw 'Wrong number of <'+tagName+'> arguments: '+a.length;
  372.                         e$ = $('<mrow/>', docP).attr('mtagname', tagName);
  373.                         var t = 0.06*(ups[0] + dns[0]), up = ups[0] + t + 0.1, dn = dns[0];
  374.                         if (tagName == 'mroot') {
  375.                                 var ht = 0.6 * (ups[1] + dns[1]), d = 0.25/0.6 - 0.25;
  376.                                 if (up > ht)    d += up/0.6 - ups[1];
  377.                                 else {
  378.                                         d += dns[1];
  379.                                         up = ht;
  380.                                 }
  381.                                 e$.append($('<span class="fm-root fm-inline"></span>', docP).append(a[1]).
  382.                                                 css('verticalAlign', d.toFixed(2)+'em'));
  383.                         }
  384.                         var mo$ = $('<mo/>', docP).addClass('fm-radic').append('\u221A' /* &radic; */),
  385.                                         // IE8 doesn't like $('<mo class="fm-radic"></mo>').append(...)
  386.                                 y$ = vertAlignE$(up, dn,
  387.                                         $('<span class="fm-vert fm-radicand"></span>', docP).append(a[0]).
  388.                                                 css('borderTopWidth', t.toFixed(3)+'em'));
  389.                         checkVertStretch(up, dn, mo$[0]);
  390.                         e$.append(mo$).append(y$);
  391.                         e$[0].fmUp = up;
  392.                         e$[0].fmDn = dn;
  393.                 } else if (tagName == 'msub' || tagName == 'msup' || tagName == 'msubsup'
  394.                 || tagName == 'mmultiscripts') {
  395.                         if (tagName != 'mmultiscripts' && a.length != (tagName == 'msubsup' ? 3 : 2))
  396.                                 throw 'Wrong number of <'+tagName+'> arguments: '+a.length;
  397.                         var up = ups[0], dn = dns[0], oddQ = tagName == 'msup',
  398.                                 dUp = up/0.71 - 0.6, dDn = dn/0.71 - 0.6;
  399.                         for (var i = 1; i < a.length; i++) {
  400.                                 if (tagName == 'mmultiscripts') {
  401.                                         var w = M.mtagName(a[i]);
  402.                                         if (w == 'none')        continue;
  403.                                         if (w == 'mprescripts') {
  404.                                                 if (oddQ)       throw 'Repeated "mprescripts"';
  405.                                                 oddQ = true;
  406.                                                 continue;
  407.                                         }
  408.                                 }
  409.                                 if (i % 2 == (oddQ ? 0 : 1))    dDn = Math.max(dDn, ups[i]);
  410.                                 else    dUp = Math.max(dUp, dns[i]);
  411.                         }
  412.                         var preAP = null, postA = [], nPrescripts = 0;
  413.                         for (var i = 1; i < a.length; i++) {
  414.                                 if (tagName == 'mmultiscripts') {
  415.                                         var w = M.mtagName(a[i]);
  416.                                         if (w == 'mprescripts') {
  417.                                                 preAP = [];
  418.                                                 nPrescripts = a.length - i - 1;
  419.                                                 continue;
  420.                                         }
  421.                                 }
  422.                                 var d = 0.25/0.71 - 0.25;
  423.                                 if (i % 2 == (preAP ? 0 : 1) && tagName != 'msup') {
  424.                                         d -= dDn;
  425.                                         dn = Math.max(dn, 0.71*(dDn + dns[i]));
  426.                                 } else {
  427.                                         d += dUp;
  428.                                         up = Math.max(up, 0.71*(dUp + ups[i]));
  429.                                 }
  430.                                 $(a[i]).wrap('<span class="fm-script fm-inline"></span>').parent().
  431.                                         css('verticalAlign', d.toFixed(2)+'em');
  432.                                 if (M.msieVersion && (document.documentMode || M.msieVersion) < 8)
  433.                                         a[i].style.zoom = 1;    // to set hasLayout
  434.                                 if (tagName == 'mmultiscripts') (preAP || postA).push(a[i].parentNode);
  435.                         }
  436.                         if (tagName == 'mmultiscripts')
  437.                                 e$ = $('<mrow/>').      // $('<mrow mtagname="mmultiscripts"></mrow>') fails in IE8
  438.                                         append($((preAP || []).concat(a[0], postA))).
  439.                                         attr({ mtagname: 'mmultiscripts', nprescripts: nPrescripts });
  440.                         e$[0].fmUp = up;
  441.                         e$[0].fmDn = dn;
  442.                 } else if (tagName == 'munder' || tagName == 'mover' || tagName == 'munderover') {
  443.                         if (a.length != (tagName == 'munderover' ? 3 : 2))
  444.                                 throw 'Wrong number of <'+tagName+'> arguments: '+a.length;
  445.                         var tbody$ = $('<tbody/>', docP), td$, up = 0.85 * ups[0], dn = 0.85 * dns[0];
  446.                         if (tagName != 'munder') {
  447.                                 var overE = a[a.length - 1], accentDsP = null;
  448.                                 td$ = $('<td/>', docP).append(overE);
  449.                                 if (overE.nodeName == 'MO' && overE.childNodes.length == 1) {
  450.                                         var c = overE.firstChild;
  451.                                         if (c.nodeType == 3 /* Text */) accentDsP = accentDsByS_[c.data];
  452.                                 }
  453.                                 if (accentDsP) {
  454.                                         overE.style.display = 'block';
  455.                                         overE.style.marginTop = (- accentDsP[0]).toFixed(2) + 'em';
  456.                                         overE.style.marginBottom = (- accentDsP[1]).toFixed(2) + 'em';
  457.                                         up += 1.2 - F.sum(accentDsP);
  458.                                 } else {
  459.                                         td$.addClass("fm-script fm-inline");
  460.                                         up += 0.71 * (ups[a.length - 1] + dns[a.length - 1]);
  461.                                 }
  462.                                 tbody$.append($('<tr/>', docP).append(td$));
  463.                         }
  464.                         if (a[0].nodeName == 'MI' && a[0].childNodes.length == 1) {
  465.                                 var c = a[0].firstChild, s = c.data;
  466.                                 if (c.nodeType == 3 /* Text */ && s.length == 1) {
  467.                                         var d = 'acegmnopqrsuvwxyz'.indexOf(s) != -1 ? 0.25 : s == 't' ? 0.15 : 0;
  468.                                         if (d) {
  469.                                                 a[0].style.display = 'block';
  470.                                                 a[0].style.marginTop = (- d).toFixed(2) + 'em';
  471.                                                 up -= d;
  472.                                         }
  473.                                 }
  474.                         }
  475.                         td$ = $('<td class="fm-underover-base"></td>', docP).append(a[0]);
  476.                         tbody$.append($('<tr/>', docP).append(td$));
  477.                         if (tagName != 'mover') {
  478.                                 td$ = $('<td class="fm-script fm-inline"></td>', docP).append(a[1]);
  479.                                 tbody$.append($('<tr/>', docP).append(td$));
  480.                                 dn += 0.71 * (ups[1] + dns[1]);
  481.                         }
  482.                         e$ = vertAlignE$(up, dn, $('<span class="fm-vert"></span>', docP).
  483.                                         append($('<table/>', docP).append(tbody$))).
  484.                                 attr('mtagname', tagName);
  485.                 } else if (tagName == 'mtable') {
  486.                         var tbody$ = $('<tbody/>', docP).append($(a));
  487.                         e$ = $('<span class="fm-vert" mtagname="mtable"></span>', docP)
  488.                                 .append($('<table/>', docP).append(tbody$));
  489.                         var r = F.sum(ups) + F.sum(dns);
  490.                         e$[0].fmUp = e$[0].fmDn = 0.5 * r;      // binomScan() may modify
  491.                 } else if (tagName == 'mtr') {
  492.                         e$ = $('<tr class="fm-mtr" mtagname="mtr"></tr>', docP).append($(a));
  493.                         var up = 0.6, dn = 0.6;
  494.                         F.iter(function(e, i) {
  495.                                         if ((e.getAttribute(M.MathML ? 'rowspan' : 'rowSpan') || 1) == 1) {
  496.                                                 up = Math.max(up, ups[i]);
  497.                                                 dn = Math.max(dn, dns[i]);
  498.                                         }
  499.                                 }, a);
  500.                         e$[0].fmUp = up + 0.25;
  501.                         e$[0].fmDn = dn + 0.25;
  502.                 } else if (tagName == 'mtd') {  // return <td> partly so rowspan/colspan work
  503.                         e$ = $('<td class="fm-mtd" mtagname="mtd"></td>', docP).append($(a));
  504.                         if (ups[0] > 0.65)      e$[0].fmUp = ups[0];
  505.                         if (dns[0] > 0.65)      e$[0].fmDn = dns[0];
  506.                         var e = M.setAttrs(e$[0], attrsP);
  507.                         attrsP = null;
  508.                         var rowspan = e.getAttribute('rowspan'), colspan = e.getAttribute('columnspan');
  509.                         if (rowspan) {
  510.                                 e.setAttribute('rowSpan', rowspan);     // for IE6-7
  511.                                 if (! M.hasClass(e, 'middle'))  M.addClass(e, 'middle');
  512.                         }
  513.                         if (colspan)    e.setAttribute('colSpan', colspan);
  514.                 } else if (tagName == 'mfenced') {
  515.                         var e = M.setAttrs(e$[0], attrsP);
  516.                         return M.newMe('mrow', M.mfencedToMRowArgs(e), attrsP, docP);
  517.                 } else  throw 'Unrecognized or unimplemented MathML tagName: '+tagName;
  518.                
  519.                 return checkMathMLAttrs(M.setAttrs(e$[0], attrsP));
  520.         };
  521.         M.mfencedToMRowArgs = function(e) {
  522.                 e.tagName.toLowerCase() == 'mfenced' || F.err(err_mfencedToMRowArgs_);
  523.                 var doc = e.ownerDocument;
  524.                 function newMo(s) { return M.newMe('mo', s, null, doc); }
  525.                 var openSP = M.getSpecAttrP(e, 'open'), closeSP = M.getSpecAttrP(e, 'close'),
  526.                         res = [newMo(openSP == null ? '(' : openSP),
  527.                                 newMo(closeSP == null ? ')' : closeSP)],
  528.                         es = F.slice(e.childNodes);
  529.                 if (es.length == 0)     return res;
  530.                 var inner;
  531.                 if (es.length == 1)     inner = es[0];
  532.                 else {
  533.                         var sepsSP = M.getSpecAttrP(e, 'separators'),
  534.                                 sepsP = (sepsSP == null ? ',' : sepsSP).match(/\S/g),
  535.                                 n = sepsP ? es.length - 1 : 0;
  536.                         for (var i = 0; i < n; i++)
  537.                                 es.splice(2*i + 1, 0, newMo(sepsP[Math.min(i, sepsP.length - 1)]));
  538.                         inner = M.newMe('mrow', es, null, doc);
  539.                 }
  540.                 res.splice(1, 0, inner);
  541.                 return res;
  542.         };
  543.         M.spaceMe = function(widthS, docP) /* incl. avoid bad font support for \u2009 &thinsp; */
  544.                 /* E.g. in Firefox 3.6.12, the DOM/jQuery don't like '' as a <mi>/<mo>/<mtext> child,
  545.                         and also padding doesn't seem to work on e.g. <mn>/<mrow>/<mspace> elements;
  546.                         also any kind of unicode space inside an <mo> might be stripped by the browser: */
  547.                 { return M.newMe('mspace', null, { width: widthS }, docP); };
  548.         M.fenceMe = function(me1, leftP, rightP, docP)
  549.                 { return M.newMe('mrow',
  550.                         [M.newMe('mo', leftP == null ? '(' : leftP, docP), me1,
  551.                                 M.newMe('mo', rightP == null ? ')' : rightP, docP)],
  552.                         docP); };
  553.         F.iter(function(tagName) { M[tagName] = F(M.newMe, tagName); },
  554.                 ['mn', 'mi', 'mo', 'mtext', 'mspace', 'mrow', 'mfenced', 'mfrac', 'msqrt', 'mroot',
  555.                         'msub', 'msup', 'msubsup', 'mmultiscripts', 'mprescripts', 'none',
  556.                         'munder', 'mover', 'munderover', 'mtable', 'mtr', 'mtd']);
  557.         M.setMathBlockQ = function(e, blockQ) {
  558.                 if (blockQ) {
  559.                         e.setAttribute('display', 'block');
  560.                         M.addClass(e, 'ma-block');
  561.                 } else if (! M.MathML)  $(e).addClass('fm-inline');
  562.                 return e;
  563.         };
  564.         M.math = function(argsP /* for appendMeArgs() */, blockQ, docP)
  565.                 { return M.setMathBlockQ(M.newMe('math', argsP, docP), blockQ); };
  566.        
  567.         M.eToMathE = function(mathE) /* 'mathE' has been parsed from MathML syntax */ {
  568.                 if (M.MathML == null || mathE.tagName.toLowerCase() != 'math')  F.err(err_eToMathE_);
  569.                 function fixMathMLDeep(nod) {
  570.                         if (nod.nodeType != 1 /* Element */)    return nod;
  571.                         if (! F.elem(nod.tagName, mtokens_))    F.iter(fixMathMLDeep, nod.childNodes);
  572.                         return checkMathMLAttrs(nod);
  573.                 }
  574.                 if (M.MathML && mathE.tagName == 'math')
  575.                         return fixMathMLQ_ ? fixMathMLDeep(mathE) : mathE;
  576.                 var doc = mathE.ownerDocument;
  577.                 function newMeDeep(me) {
  578.                         var tagName = me.tagName.toLowerCase(), args = me.childNodes;
  579.                         function nodeToMes(nod) {
  580.                                 if (nod.nodeType == 3 /* Text */)
  581.                                         return /^\s*$/.test(nod.data) ? [] : [M.mtext(nod.data, doc)];
  582.                                 if (nod.nodeType == 8 /* Comment */)    return [];
  583.                                 me.nodeType == 1 /* Element */ || F.err(err_newMeDeep_);
  584.                                 return [newMeDeep(nod)];
  585.                         }
  586.                         if (F.elem(tagName, mtokens_)) {
  587.                                 if (tagName == 'mo' && args.length == 1 && args[0].nodeType == 3 /* Text */
  588.                                 && args[0].data == '-') args = M['-'];
  589.                         } else  args = F.concatMap(nodeToMes, args);
  590.                         var res = M.newMe(tagName, args, me.attributes, doc);
  591.                         if (tagName == 'math')  M.setMathBlockQ(res, me.getAttribute('display') == 'block');
  592.                         return res;
  593.                 }
  594.                 return newMeDeep(mathE);
  595.         };
  596.        
  597.         M['-'] = '\u2212';      // &minus; −
  598.         M.trimNumS = function(s) /* trims trailing 0s after decimal point, trailing ., -0 */ {
  599.                 return s.replace(/(\d\.\d*?)0+(?!\d)/g, '$1').replace(/(\d)\.(?!\d)/g, '$1').
  600.                         replace(/[-\u2212]0(?![.\d])/g, '0');
  601.         };
  602.         M.numS = function(s, trimQ) /* converts a numeric string to jqMath notation */ {
  603.                 if (trimQ)      s = M.trimNumS(s);
  604.                 return s.replace(/Infinity/ig, '\u221E' /* ∞ */).replace(/NaN/ig, '{?}').
  605.                         replace(/e(-\d+)/ig, '\xB710^{$1}').replace(/e\+?(\d+)/ig, '\xB710^$1').
  606.                         replace(/-/g, M['-']);  // \xB7 is ·
  607.         };
  608.        
  609.         /*  Like TeX, we use ^ for superscripts, _ for subscripts, {} for grouping, and \ (or `) as
  610.                 an escape character.  Spaces and newline characters are ignored.  We also use ↖ (\u2196)
  611.                 and ↙ (\u2199) to put limits above and below an operator or expression.  You can
  612.                 $.extend() or even replace M.infix_, M.prefix_, M.postfix_, M.macros_, M.macro1s_, and
  613.                 M.alias_ as needed.  */
  614.         M.combiningChar_ = '[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]';
  615.         M.surrPair_ = '[\uD800-\uDBFF][\uDC00-\uDFFF]';
  616.         var decComma_, escPat_ = '[\\\\`]([A-Za-z]+|.)';
  617.         M.decimalComma = function(qP) /* whether to allow decimal commas */ {
  618.                 if (qP != null) {
  619.                         decComma_ = qP;
  620.                         var numPat = (qP ? '\\d*,\\d+|' : '') + '\\d+\\.?\\d*|\\.\\d+';
  621.                         M.re_ /* .lastIndex set during use */ = RegExp(
  622.                                 '('+numPat+')|'+escPat_+'|'+M.surrPair_+'|\\S'+M.combiningChar_+'*', 'g');
  623.                 }
  624.                 return decComma_;
  625.         };
  626.         var commaLangs = 'af|an|ar|av|az|ba|be|bg|bs|ca|ce|co|cs|cu|cv|da|de|el|es|et|eu|fi|fo|fr|'+
  627.                 'gl|hr|hu|hy|id|is|it|jv|kk|kl|kv|lb|lt|lv|mk|mn|mo|nl|no|os|pl|pt|ro|ru|sc|sk|sq|sr|'+
  628.                 'su|sv|tr|tt|ug|uk|vi|yi';
  629.         M.decimalComma(RegExp('^('+commaLangs+')\\b', 'i').test(document.documentElement.lang));
  630.         M.infix_ = {    // operator precedences, see http://www.w3.org/TR/MathML3/appendixc.html
  631.                 '⊂⃒': 240, '⊃⃒': 240,
  632.                 '≪̸': 260, '≫̸': 260, '⪯̸': 260, '⪰̸': 260,
  633.                 '∽̱': 265, '≂̸': 265, '≎̸': 265, '≏̸': 265, '≦̸': 265, '≿̸': 265, '⊏̸': 265, '⊐̸': 265, '⧏̸': 265,
  634.                 '⧐̸': 265, '⩽̸': 265, '⩾̸': 265, '⪡̸': 265, '⪢̸': 265,
  635.                
  636.                 // if non-MathML and precedence <= 270, then class is 'fm-infix-loose' not 'fm-infix'
  637.                
  638.                 /* '-' is converted to '\u2212' &minus; − */
  639.                 '\u2009' /* &thinsp; ' ', currently generates an <mspace> */: 390,
  640.                
  641.                 '' /* no <mo> is generated */: 500 /* not 390 or 850 */
  642.                
  643.                 /* \^ or `^  880 not 780, \_ or `_ 880 not 900 */
  644.                
  645.                 // unescaped ^ _ ↖ (\u2196) ↙ (\u2199) have precedence 999
  646.         };
  647.         // If an infix op is also prefix or postfix, it must use the same precedence in each form.
  648.         // Also, we omit "multiple character operator"s like && or <=.
  649.         M.prefix_ = {};
  650.                 // prefix precedence < 25 => thin space not inserted between multi-letter <mi> and it;
  651.                 //      (prefix or postfix precedence < 25) and non-MathML => <mo> stretchy;
  652.                 //      precedence < 25 => can be a fence
  653.                
  654.                 // can use {|...|} for absolute value
  655.                
  656.                 // + - % and other infix ops can automatically be used as prefix and postfix ops
  657.                
  658.                 // if non-MathML and prefix and 290 <= precedence <= 350, then 'fm-large-op'
  659.         M.postfix_ = {
  660.                 // (unquoted) ' is converted to '\u2032' &prime; ′
  661.         };
  662.         function setPrecs(precs, precCharsA) {
  663.                 F.iter(function(prec_chars) {
  664.                                 var prec = prec_chars[0];
  665.                                 F.iter(function(c) { precs[c] = prec; }, prec_chars[1].split(''));
  666.                         }, precCharsA);
  667.         }
  668.         setPrecs(M.infix_, [
  669.                         [21, '|'],      // | not 20 or 270
  670.                         [30, ';'],
  671.                         [40, ',\u2063'],
  672.                         [70, '∴∵'],
  673.                         [100, ':'],
  674.                         [110, '϶'],
  675.                         [150, '…⋮⋯⋱'],
  676.                         [160, '∋'],
  677.                         [170, '⊢⊣⊤⊨⊩⊬⊭⊮⊯'],
  678.                         [190, '∨'],
  679.                         [200, '∧'],
  680.                         [240, '∁∈∉∌⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋'],
  681.                         [241, '≤'],
  682.                         [242, '≥'],
  683.                         [243, '>'],
  684.                         [244, '≯'],
  685.                         [245, '<'],
  686.                         [246, '≮'],
  687.                         [247, '≈'],
  688.                         [250, '∼≉'],
  689.                         [252, '≢'],
  690.                         [255, '≠'],
  691.                         [260, '=∝∤∥∦≁≃≄≅≆≇≍≔≗≙≚≜≟≡≨≩≪≫≭≰≱≺≻≼≽⊀⊁⊥⊴⊵⋉⋊⋋⋌⋔⋖⋗⋘⋙⋪⋫⋬⋭■□▪▫▭▮▯▰▱△▴▵▶▷▸▹▼▽▾▿◀◁◂◃'+
  692.                                 '◄◅◆◇◈◉◌◍◎●◖◗◦⧀⧁⧣⧤⧥⧦⧳⪇⪈⪯⪰'],
  693.                         [265, '⁄∆∊∍∎∕∗∘∙∟∣∶∷∸∹∺∻∽∾∿≂≊≋≌≎≏≐≑≒≓≕≖≘≝≞≣≦≧≬≲≳≴≵≶≷≸≹≾≿⊌⊍⊎⊏⊐⊑⊒⊓⊔⊚⊛⊜⊝⊦⊧⊪⊫⊰⊱⊲⊳⊶⊷⊹⊺⊻⊼⊽⊾⊿⋄⋆⋇'+
  694.                                 '⋈⋍⋎⋏⋐⋑⋒⋓⋕⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋰⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿▲❘⦁⦂⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧂⧃⧄'+
  695.                                 '⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧛⧜⧝⧞⧠⧡⧢⧧⧨⧩⧪⧫⧬⧭⧮⧰⧱⧲⧵⧶⧷⧸⧹⧺⧻⧾⧿⨝⨞⨟⨠⨡⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹'+
  696.                                 '⨺⨻⨼⨽⨾⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿⪀⪁⪂⪃⪄⪅⪆⪉⪊⪋⪌⪍⪎⪏'+
  697.                                 '⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦'+
  698.                                 '⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫽⫾'],
  699.                         [270, '←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑'+
  700.                                 '⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿⊸⟰⟱⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿⤀⤁⤂⤃⤄'+
  701.                                 '⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒'+
  702.                                 '⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿⦙⦚⦛⦜⦝⦞⦟⧟⧯⧴⭅⭆'],
  703.                         [275, '+-±−∓∔⊞⊟'],
  704.                         [300, '⊕⊖⊘'],
  705.                         [340, '≀'],
  706.                         [350, '∩∪'],
  707.                         [390, '*.ו\u2062⊠⊡⋅⨯⨿'],
  708.                         [400, '·'],
  709.                         [410, '⊗'],
  710.                         [640, '%'],
  711.                         [650, '\\∖'],
  712.                         [660, '/÷'],
  713.                         [710, '⊙'],
  714.                         [825, '@'],
  715.                         [835, '?'],
  716.                         [850, '\u2061'],
  717.                         [880, '^_\u2064']]);
  718.         setPrecs(M.prefix_, [
  719.                         [10, '‘“'],
  720.                         [20, '([{‖⌈⌊❲⟦⟨⟪⟬⟮⦀⦃⦅⦇⦉⦋⦍⦏⦑⦓⦕⦗⧼'],
  721.                         [230, '∀∃∄'],
  722.                         [290, '∑⨊⨋'],
  723.                         [300, '∬∭⨁'],
  724.                         [310, '∫∮∯∰∱∲∳⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜'],
  725.                         [320, '⋃⨃⨄'],
  726.                         [330, '⋀⋁⋂⨀⨂⨅⨆⨇⨈⨉⫼⫿'],
  727.                         [350, '∏∐'],
  728.                         [670, '∠∡∢'],
  729.                         [680, '¬'],
  730.                         [740, '∂∇'],
  731.                         [845, 'ⅅⅆ√∛∜']]);
  732.         setPrecs(M.postfix_, [
  733.                         [10, '’”'],
  734.                         [20, ')]}‖⌉⌋❳⟧⟩⟫⟭⟯⦀⦄⦆⦈⦊⦌⦎⦐⦒⦔⦖⦘⧽'],
  735.                         [800, '′♭♮♯'],
  736.                         [810, '!'],
  737.                         [880, '&\'`~¨¯°´¸ˆˇˉˊˋˍ˘˙˚˜˝˷\u0302\u0311‾\u20db\u20dc⎴⎵⏜⏝⏞⏟⏠⏡']]);
  738.        
  739.         var s_, s_or_mx_a_, s_or_mx_i_, docP_, precAdj_;
  740.         /*  A "tok" (scanner token or similar) here is an [meP, opSP], with at least one != null.
  741.                 The meP may be non-atomic.  Thus a tok is either an me, possibly with a precedence given
  742.                 by an operator, or else either a meta-operator or "macro1" for building an me.  */
  743.        
  744.         function newMe_(tagName, argsP) { return M.newMe(tagName, argsP, docP_); }
  745.         function emptyMe_() { return newMe_('mspace' /* or 'mrow'? */); }
  746.        
  747.         function scanWord(descP) /* use scanString() instead if any quotes should be stripped */ {
  748.                 var re = /\s*([-\w.]*)/g;       //+ could allow all unicode alphabetics
  749.                 re.lastIndex = M.re_.lastIndex;
  750.                 var match = re.exec(s_);
  751.                 if (! match[1]) throw 'Missing '+(descP || 'word');
  752.                 M.re_.lastIndex = re.lastIndex;
  753.                 return match[1];
  754.         }
  755.         function scanString(descP) /* scans a word or quoted string */ {
  756.                 var re = /\s*(?:(["'])|([-\w.]*))/g;
  757.                 re.lastIndex = M.re_.lastIndex;
  758.                 var match = re.exec(s_);
  759.                 if (match[2]) {
  760.                         M.re_.lastIndex = re.lastIndex;
  761.                         return match[2];
  762.                 }
  763.                 if (! match[1]) throw 'Missing '+(descP || 'string');
  764.                 var c = match[1], re2 = RegExp('[^\\`'+c+']+|[\\`](.|\n)|('+c+')', 'g'), res = '';
  765.                 re2.lastIndex = re.lastIndex;
  766.                 while (true) {
  767.                         match = re2.exec(s_);
  768.                         if (! match)    throw 'Missing closing '+c;
  769.                         if (match[2])   break;
  770.                         res += match[1] || match[0];
  771.                 }
  772.                 M.re_.lastIndex = re2.lastIndex;
  773.                 return res;
  774.         }
  775.         function scanMeTok(afterP) {
  776.                 var tokP = scanTokP();
  777.                 if (! tokP || ! tokP[0])
  778.                         throw 'Missing expression argument'+(afterP ? ' after '+afterP : '')+
  779.                                 ', before position '+M.re_.lastIndex;
  780.                 return tokP;
  781.         }
  782.        
  783.         function mtokenScan(tagName /* 'mn', 'mi', 'mo', or 'mtext' */) {
  784.                 var s = scanString(tagName == 'mtext' ? 'text' : tagName);
  785.                 return [newMe_(tagName, s), tagName == 'mo' ? s : null];
  786.         }
  787.         function htmlScan() {
  788.                 var h = scanString('html'),
  789.                         e = $('<div/>', docP_ || document).css('display', 'inline-block').html(h)[0];
  790.                 if (e.childNodes.length == 1)   e = e.childNodes[0];
  791.                 return [newMe_('mtext', e), null];
  792.         }
  793.         function spScan() {
  794.                 var widthS = scanString('\\sp width');
  795.                 return [M.spaceMe(widthS, docP_),
  796.                         /^[^-]*[1-9]/.test(widthS) ? '\u2009' /* &thinsp; */ : null];
  797.         }
  798.         function braceScan() {
  799.                 var tokP = scanTokP();
  800.                 if (tokP && tokP[1] == '↖' && ! tokP[0]) {    // grouped op
  801.                         var meTokP_tokP = parseEmbel();
  802.                         tokP = meTokP_tokP[1] || scanTokP();
  803.                         if (! (meTokP_tokP[0] && tokP && tokP[1] == '}' && ! tokP[0]))
  804.                                 throw 'Expected an embellished operator and "}" after "{↖", before position '+
  805.                                         M.re_.lastIndex;
  806.                         return meTokP_tokP[0];
  807.                 }
  808.                 var mxP_tokP = parse_mxP_tokP(0, tokP);
  809.                 tokP = mxP_tokP[1];
  810.                 // if (! tokP)  throw 'Missing "}"';
  811.                 ! tokP || tokP[1] == '}' && ! tokP[0] || F.err(err_braceScan_);
  812.                 return [mxP_tokP[0] || emptyMe_(), null];
  813.         }
  814.         function attrScan(nameP, mmlOnlyQ) {    // note usually doesn't affect non-MathML rendering
  815.                 if (! nameP)    nameP = scanWord('attribute name');
  816.                 var v = scanString(nameP+' attribute'), tok = scanMeTok(nameP);
  817.                 if (! mmlOnlyQ || M.MathML)     tok[0].setAttribute(nameP, v);
  818.                 return tok;
  819.         }
  820.         function clScan() {     // note currently ignored by MathPlayer
  821.                 var desc = 'CSS class name(s)', ws = scanString(desc), tok = scanMeTok(desc);
  822.                 M.addClass(tok[0], ws);
  823.                 return tok;
  824.         }
  825.         // see http://www.w3.org/TR/MathML3/chapter3.html#presm.commatt for mathvariant attr
  826.         function mvScan(sP) {
  827.                 var s = sP || scanString('mathvariant'), tok = scanMeTok(s), me = tok[0];
  828.                 if (! F.elem(M.mtagName(me), ['mi', 'mn', 'mo', 'mtext', 'mspace', 'ms']))
  829.                         throw 'Can only apply a mathvariant to a MathML token (atomic) element, at '+
  830.                                 'position '+M.re_.lastIndex;
  831.                
  832.                 me.setAttribute('mathvariant', s);
  833.                
  834.                 if (/bold/.test(s))     M.addClass(me, 'ma-bold');
  835.                 else if (s == 'normal' || s == 'italic')        M.addClass(me, 'ma-nonbold');
  836.                
  837.                 M.addClass(me, /italic/.test(s) ? 'ma-italic' : 'ma-upright');
  838.                
  839.                 if (/double-struck/.test(s))    M.addClass(me, 'ma-double-struck');
  840.                 else if (/fraktur/.test(s))     M.addClass(me, 'ma-fraktur');
  841.                 else if (/script/.test(s))      M.addClass(me, 'ma-script');
  842.                 else if (/sans-serif/.test(s))  M.addClass(me, 'ma-sans-serif');
  843.                
  844.                 // (monospace, initial, tailed, looped, stretched) currently don't add classes
  845.                
  846.                 return tok;
  847.         }
  848.         function ovScan() /* overbar */ {
  849.                 return [M.newMe('menclose', scanMeTok('\\ov')[0], { notation: 'top' }, docP_), null];
  850.         }
  851.         function minsizeScan() {
  852.                 var s = scanString('minsize'), tok = scanMeTok('minsize'), me = tok[0];
  853.                 if (M.mtagName(me) != 'mo')
  854.                         throw 'Can only stretch an operator symbol, before position '+M.re_.lastIndex;
  855.                 if (M.MathML)   me.setAttribute('minsize', s);
  856.                 else {
  857.                         var r = Number(s);      // may be NaN, 0, etc.
  858.                         if (r > 1)      checkVertStretch(0.6*r, 0.6*r, me, true);
  859.                         else    me.style.fontSize = s;
  860.                 }
  861.                 return tok;
  862.         }
  863.         function mrow1Scan() { return [newMe_('mrow', scanMeTok('\\mrowOne')[0]), null]; }
  864.         function binomScan() {
  865.                 function mtr1(e) { return newMe_('mtr', newMe_('mtd', e)); }
  866.                 var xMe = scanMeTok('\\binom')[0], yMe = scanMeTok('\\binom')[0],
  867.                         zMe = newMe_('mtable', F.map(mtr1, [xMe, yMe]));
  868.                 M.addClass(zMe, 'ma-binom');
  869.                 if (! M.MathML) {
  870.                         zMe.fmUp -= 0.41;
  871.                         zMe.fmDn -= 0.41;
  872.                 }
  873.                 return [newMe_('mrow', [newMe_('mo', '('), zMe, newMe_('mo', ')')]), null];
  874.         }
  875.         M.macros_ /* each returns a tokP */ = {
  876.                 mn: F(mtokenScan, 'mn'), mi: F(mtokenScan, 'mi'), mo: F(mtokenScan, 'mo'),
  877.                 text: F(mtokenScan, 'mtext'), html: htmlScan, sp: spScan,
  878.                 attr: attrScan, attrMML: F(attrScan, null, true),
  879.                 id: F(attrScan, 'id'), dir: F(attrScan, 'dir'), cl: clScan, mv: mvScan,
  880.                 bo: F(mvScan, 'bold'), it: F(mvScan, 'italic'), bi: F(mvScan, 'bold-italic'),
  881.                 sc: F(mvScan, 'script'), bs: F(mvScan, 'bold-script'), fr: F(mvScan, 'fraktur'),
  882.                 ds: F(mvScan, 'double-struck'), bf: F(mvScan, 'bold-fraktur'), ov: ovScan,
  883.                 minsize: minsizeScan, mrowOne: mrow1Scan, binom: binomScan
  884.         };
  885.        
  886.         M.alias_ = { '-': M['-'], '\'': '\u2032' /* &prime; */,
  887.                 '\u212D': ['C', 'fraktur'], '\u210C': ['H', 'fraktur'], '\u2111': ['I', 'fraktur'],
  888.                 '\u211C': ['R', 'fraktur'], '\u2128': ['Z', 'fraktur'],
  889.                 '\u212C': ['B', 'script'], '\u2130': ['E', 'script'], '\u2131': ['F', 'script'],
  890.                 '\u210B': ['H', 'script'], '\u2110': ['I', 'script'], '\u2112': ['L', 'script'],
  891.                 '\u2133': ['M', 'script'], '\u211B': ['R', 'script'], '\u212F': ['e', 'script'],
  892.                 '\u210A': ['g', 'script'], /* '\u2113': ['l', 'script'], */ '\u2134': ['o', 'script']
  893.         };
  894.         var spaces_ = { ',': '.17em', ':': '.22em', ';': '.28em', '!': '-.17em' };
  895.         function scanTokP() {
  896.                 var match = M.re_.exec(s_);
  897.                 while (! match) {
  898.                         M.re_.lastIndex = s_.length;
  899.                         if (s_or_mx_i_ == s_or_mx_a_.length)    return null;
  900.                         var g = s_or_mx_a_[s_or_mx_i_++];
  901.                         if (typeof g == 'string') {
  902.                                 M.re_.lastIndex = 0;
  903.                                 s_ = g;
  904.                                 match = M.re_.exec(s_);
  905.                         } else if (g.nodeType == 1 /* Element */)       return [g, null];
  906.                         else    F.err(err_scanTokP_);
  907.                 }
  908.                 var s1 = match[2] || match[0], mvP = null;
  909.                 if (/^[_^}\u2196\u2199]$/.test(match[0]) || match[2] && M.macro1s_[s1])
  910.                         return [null, s1];
  911.                 if (match[0] == '{')    return braceScan();
  912.                 if (match[2] && M.macros_[s1])  return M.macros_[s1]();
  913.                 if (match[1])   return [M.newMe('mn', s1, docP_), null];
  914.                 if (/^[,:;!]$/.test(match[2]))  s1 = '\u2009' /* &thinsp; */;
  915.                 /* else if (match[2] == '&') {
  916.                         if (M.mozillaVersion && M.MathML && false /* <maligngroup> not supported yet * /)
  917.                                 return [M.newMe('maligngroup', null, docP_), null];
  918.                         s1 = ',';
  919.                 } */ else if (match[2] == '/')  s1 = '\u2215' /* ∕ */;
  920.                 else if (M.alias_[s1] && ! match[2]) {
  921.                         var t = M.alias_[s1];
  922.                         if (typeof t == 'string')       s1 = t;
  923.                         else {
  924.                                 s1 = t[0];
  925.                                 mvP = t[1];     // 'double-struck', 'fraktur', or 'script'
  926.                         }
  927.                 }
  928.                 var opSP = M.infix_[s1] || M.prefix_[s1] || M.postfix_[s1] ? s1 : null, e;
  929.                 if (s1 == '\u2009' /* &thinsp; */)      // incl. avoid bad font support, incl. in MathML
  930.                         e = M.spaceMe(spaces_[match[2] || ','], docP_);
  931.                 else if (opSP) {
  932.                         e = M.newMe('mo', s1, docP_);
  933.                         if (/^[∀∃∄∂∇]$/.test(s1)) {   // Firefox work-arounds
  934.                                 e.setAttribute('lspace', '.11em');
  935.                                 e.setAttribute('rspace', '.06em');
  936.                         } else if (s1 == '!') {
  937.                                 e.setAttribute('lspace', '.06em');
  938.                                 e.setAttribute('rspace', '0');
  939.                         } else if (s1 == '×' /* '\u00D7' */) {
  940.                                 e.setAttribute('lspace', '.22em');
  941.                                 e.setAttribute('rspace', '.22em');
  942.                         }
  943.                 } else {
  944.                         e = M.newMe('mi', s1, docP_);
  945.                         if (match[2] && s1.length == 1) {
  946.                                 e.setAttribute('mathvariant', 'normal');
  947.                                 M.addClass(e, 'ma-upright');
  948.                                 if (! M.MathML) e.style.paddingRight = '0';
  949.                         } else if (mvP) {
  950.                                 e.setAttribute('mathvariant', mvP);
  951.                                 M.addClass(e, 'ma-upright');
  952.                                 M.addClass(e, 'ma-'+mvP);
  953.                         }
  954.                         if (/\w\w/.test(s1))    M.addClass(e, 'ma-repel-adj');
  955.                 }
  956.                 return [e, opSP];
  957.         }
  958.        
  959.         function parse_mtd_tokP(optQ /* => mtd can be null or an mtr */) {
  960.                 var mxP_tokP = parse_mxP_tokP(M.infix_[',']), tokP = mxP_tokP[1] || scanTokP(),
  961.                         mxP = mxP_tokP[0];
  962.                 if (! mxP) {
  963.                         if (optQ && ! (tokP && tokP[1] == ',')) return [null, tokP];
  964.                         mxP = emptyMe_();
  965.                 }
  966.                 var w = M.mtagName(mxP);
  967.                 if (w != 'mtd' && ! (w == 'mtr' && optQ))       mxP = M.newMe('mtd', mxP, docP_);
  968.                 return [mxP, tokP];
  969.         }
  970.         function parse_rowspan_tokP() {
  971.                 var v = scanString('rowspan'), mtd_tokP = parse_mtd_tokP(), mtd = mtd_tokP[0];
  972.                 mtd.setAttribute(M.MathML ? 'rowspan' : 'rowSpan' /* for IE6-7 */, v);
  973.                 if (! M.hasClass(mtd, 'middle'))        M.addClass(mtd, 'middle');
  974.                 return mtd_tokP;
  975.         }
  976.         function parse_colspan_tokP() {
  977.                 var v = scanString('colspan'), mtd_tokP = parse_mtd_tokP();
  978.                 mtd_tokP[0].setAttribute(M.MathML ? 'columnspan' : 'colSpan', v);
  979.                 return mtd_tokP;
  980.         }
  981.         function parse_mtr_tokP(optQ /* => mtr can be null */) {
  982.                 var mtds = [];
  983.                 while (true) {
  984.                         var mtdP_tokP = parse_mtd_tokP(mtds.length == 0), mtdP = mtdP_tokP[0],
  985.                                 tokP = mtdP_tokP[1] || scanTokP();
  986.                         if (mtdP) {
  987.                                 if (M.mtagName(mtdP) == 'mtr')  return [mtdP, tokP];
  988.                                 mtds.push(mtdP);
  989.                         }
  990.                         if (! (tokP && tokP[1] == ','))
  991.                                 return [mtds.length || ! optQ || tokP && tokP[1] == ';' ?
  992.                                                 M.newMe('mtr', mtds, docP_) : null, tokP];
  993.                 }
  994.         }
  995.         function parse_table_tokP() {
  996.                 var mtrs = [];
  997.                 while (true) {
  998.                         var mtrP_tokP = parse_mtr_tokP(mtrs.length == 0), mtrP = mtrP_tokP[0],
  999.                                 tokP = mtrP_tokP[1] || scanTokP();
  1000.                         if (mtrP)       mtrs.push(mtrP);
  1001.                         if (! (tokP && tokP[1] == ';')) return [M.newMe('mtable', mtrs, docP_), tokP];
  1002.                 }
  1003.         }
  1004.         function parse_math_tokP() {
  1005.                 var mxP_tokP = parse_mxP_tokP(0);
  1006.                 mxP_tokP[0] = M.newMe('math', mxP_tokP[0], docP_);
  1007.                 return mxP_tokP;
  1008.         }
  1009.         // An "mx" here is an "me" that is not just a bare or embellished operator.
  1010.         M.macro1s_ /* each returns mxP_tokP, so can do precedence-based look-ahead */ = {
  1011.                 mtd: parse_mtd_tokP, rowspan: parse_rowspan_tokP, colspan: parse_colspan_tokP,
  1012.                 mtr: parse_mtr_tokP, table: parse_table_tokP, math: parse_math_tokP
  1013.         };
  1014.        
  1015.         var embelWs_ = { '_': 'sub', '^': 'sup', '\u2199': 'under', '\u2196': 'over' };
  1016.         function embelKP(op) {
  1017.                 var wP = embelWs_[op];
  1018.                 return wP && (wP.length < 4 ? 'ss' : 'uo');
  1019.         }
  1020.         function parseEmbel(meTokP, tokP) /* checks sub/sup/under/over; returns [meTokP, tokP] */ {
  1021.                 while (true) {
  1022.                         if (! tokP)     tokP = scanTokP();
  1023.                         if (! tokP || tokP[0] || ! embelWs_[tokP[1]]) {
  1024.                                 if (tokP && ! meTokP) {
  1025.                                         meTokP = tokP;
  1026.                                         tokP = null;
  1027.                                         continue;
  1028.                                 }
  1029.                                 return [meTokP, tokP];
  1030.                         }
  1031.                         var k = embelKP(tokP[1]),
  1032.                                 parseMxs = function() /* returns 0-2 mxs of kind 'k', by op; sets tokP */ {
  1033.                                         var mxs = {}, doneQs = {};
  1034.                                         while (true) {
  1035.                                                 if (! tokP)     tokP = scanTokP();
  1036.                                                 if (! tokP || tokP[0])  break;
  1037.                                                 var op = tokP[1];
  1038.                                                 if (embelKP(op) != k || doneQs[op])     break;
  1039.                                                 doneQs[op] = true;
  1040.                                                 tokP = scanTokP();
  1041.                                                 if (tokP && embelKP(tokP[1]) == k && ! tokP[0]) continue;
  1042.                                                 var mxP_tokP = parse_mxP_tokP(999, tokP);
  1043.                                                 mxs[op] = mxP_tokP[0];  // null ok
  1044.                                                 tokP = mxP_tokP[1];
  1045.                                         }
  1046.                                         return mxs;
  1047.                                 }, mxs = parseMxs();
  1048.                         if (k == 'uo' || ! tokP || (tokP[0] ? meTokP : embelKP(tokP[1]) != 'ss')) {
  1049.                                 if (! meTokP)   meTokP = [emptyMe_(), null];
  1050.                                 var w = 'm', a = [meTokP[0]];
  1051.                                 F.iter(function(op) {
  1052.                                                 if (mxs[op]) {
  1053.                                                         w += embelWs_[op];
  1054.                                                         a.push(mxs[op]);
  1055.                                                 }
  1056.                                         }, ['_', '^', '↙', '↖']);
  1057.                                 if (a.length > 1)       meTokP = [M.newMe(w, a, docP_), meTokP[1]];
  1058.                         } else {
  1059.                                 var mxsPA = [mxs];
  1060.                                 while (tokP && ! tokP[0] && embelKP(tokP[1]) == 'ss')   mxsPA.push(parseMxs());
  1061.                                 if (! meTokP) {
  1062.                                         if (! tokP || ! tokP[0])        meTokP = [emptyMe_(), null];
  1063.                                         else {
  1064.                                                 meTokP = tokP;
  1065.                                                 tokP = scanTokP();
  1066.                                                 var postA = [];
  1067.                                                 while (tokP && ! tokP[0] && embelKP(tokP[1]) == 'ss')
  1068.                                                         postA.push(parseMxs());
  1069.                                                 mxsPA = postA.concat(null, mxsPA);
  1070.                                         }
  1071.                                 }
  1072.                                 var a = [meTokP[0]];
  1073.                                 F.iter(function(mxsP) {
  1074.                                                 if (! mxsP)     a.push(M.newMe('mprescripts', null, docP_));
  1075.                                                 else
  1076.                                                         a.push(mxsP['_'] || M.newMe('none', null, docP_),
  1077.                                                                 mxsP['^'] || M.newMe('none', null, docP_));
  1078.                                         }, mxsPA);
  1079.                                 meTokP = [M.newMe('mmultiscripts', a, docP_), meTokP[1]];
  1080.                         }
  1081.                 }
  1082.         }
  1083.         function parse_mxP_tokP(prec, tokP) /* tokP may be non-atomic */ {
  1084.                 var mx0p = null;
  1085.                 while (true) {
  1086.                         if (! tokP) {
  1087.                                 tokP = scanTokP();
  1088.                                 if (! tokP)     break;
  1089.                         }
  1090.                         var op = tokP[1];       // may be null/undefined
  1091.                         if (! op
  1092.                         || mx0p && (tokP[0] ? ! (M.infix_[op] || M.postfix_[op]) : M.macro1s_[op])) {
  1093.                                 if (! mx0p) {
  1094.                                         mx0p = tokP[0];
  1095.                                         tokP = null;
  1096.                                 } else {
  1097.                                         if (prec >= precAdj_)   break;
  1098.                                         var mxP_tokP = parse_mxP_tokP(precAdj_, tokP), mx1 = mxP_tokP[0];
  1099.                                         mx1 || F.err(err_parse_mxP_tokP_1_);
  1100.                                         var e = M.newMe('mrow', [mx0p, mx1], docP_);
  1101.                                         if (M.hasClass(mx0p, 'ma-repel-adj') || M.hasClass(mx1, 'ma-repel-adj')) {
  1102.                                                 /* setting padding on mx0p or mx1 doesn't work on e.g. <mn> or <mrow>
  1103.                                                         elements in Firefox 3.6.12 */
  1104.                                                 if (! (op && tokP[0] && M.prefix_[op] < 25))
  1105.                                                         $(mx0p).after(M.spaceMe('.17em', docP_));
  1106.                                                 M.addClass(e, 'ma-repel-adj');
  1107.                                         }
  1108.                                         mx0p = e;
  1109.                                         tokP = mxP_tokP[1];
  1110.                                 }
  1111.                         } else {
  1112.                                 var moP = tokP[0];      // could be an embellished <mo> in {↖ }
  1113.                                 if (moP) {
  1114.                                         var precL = M.infix_[op] || M.postfix_[op];
  1115.                                         if (precL && prec >= precL)     break;
  1116.                                         var precROpt = M.infix_[op] || ! (mx0p && M.postfix_[op]) && M.prefix_[op];
  1117.                                         if (! M.MathML && ! mx0p && 290 <= precROpt && precROpt <= 350) {
  1118.                                                 $(moP).addClass('fm-large-op');
  1119.                                                 //+ omit if fm-inline:
  1120.                                                 moP.fmUp = 0.85*1.3 - 0.25;
  1121.                                                 moP.fmDn = 0.35*1.3 + 0.25;
  1122.                                         }
  1123.                                         var meTok_tokP = parseEmbel(tokP), a = [];
  1124.                                         meTok_tokP[0] || F.err(err_parse_mxP_tokP_embel_);
  1125.                                         var extOp = meTok_tokP[0][0];
  1126.                                         tokP = meTok_tokP[1];
  1127.                                         if (mx0p)       a.push(mx0p);
  1128.                                         a.push(extOp);
  1129.                                         if (precROpt) {
  1130.                                                 var mxP_tokP = parse_mxP_tokP(precROpt, tokP);
  1131.                                                 if (mxP_tokP[0])        a.push(mxP_tokP[0]);
  1132.                                                 tokP = mxP_tokP[1];
  1133.                                                 if (precROpt < 25 && ! mx0p) {  // check for fences
  1134.                                                         if (! tokP)     tokP = scanTokP();
  1135.                                                         if (tokP && tokP[1] && tokP[0]
  1136.                                                         && (M.postfix_[tokP[1]] || M.infix_[tokP[1]]) == precROpt) {
  1137.                                                                 // don't parseEmbel() here [after fences]
  1138.                                                                 a.push(tokP[0]);
  1139.                                                                 tokP = null;
  1140.                                                         }
  1141.                                                 }
  1142.                                         }
  1143.                                         if (a.length == 1)      mx0p = a[0];
  1144.                                         else if (op == '/' && mx0p && a.length == 3
  1145.                                         || op == '\u221A' /* &radic; */ && ! mx0p && a.length == 2) {
  1146.                                                 if (op == '\u221A' && M.mtagName(a[0]) == 'msup')
  1147.                                                         mx0p = M.newMe('mroot', [a[1], M.mchilds(a[0])[1]], docP_);
  1148.                                                 else {
  1149.                                                         a.splice(a.length - 2, 1);
  1150.                                                         mx0p = M.newMe(op == '/' ? 'mfrac' : 'msqrt', a, docP_);
  1151.                                                 }
  1152.                                         } else {
  1153.                                                 var e = M.newMe('mrow', a, docP_);
  1154.                                                 if (op == '\u2009' /* &thinsp; */ || (precL || precROpt) >= precAdj_)
  1155.                                                         ;
  1156.                                                 else {
  1157.                                                         var k = '';
  1158.                                                         if (op == '=')  k = 'infix-loose';
  1159.                                                         else if (a.length == 2) {
  1160.                                                                 k = mx0p ? 'postfix' : 'prefix';
  1161.                                                                 if (M.infix_[op])       k += '-tight';
  1162.                                                                 else {
  1163.                                                                         if (/^[∀∃∄∂∇]$/.test(op))     k = 'quantifier';
  1164.                                                                         M.addClass(e, 'ma-repel-adj');
  1165.                                                                 }
  1166.                                                         } else if (mx0p) {      // a.length == 3 && not fences
  1167.                                                                 k = op == ',' || op == ';' ? 'separator' :
  1168.                                                                         precL <= 270 ? 'infix-loose' : 'infix';
  1169.                                                                 if (op == '|' && M.MathML && moP.tagName == 'mo') {
  1170.                                                                         // Firefox work-around
  1171.                                                                         moP.setAttribute('lspace', '.11em');
  1172.                                                                         moP.setAttribute('rspace', '.11em');
  1173.                                                                 }
  1174.                                                         }
  1175.                                                         if (! M.MathML && k && ! moP.style.fontSize)
  1176.                                                                 $(extOp).addClass('fm-'+k);
  1177.                                                 }
  1178.                                                 mx0p = e;
  1179.                                         }
  1180.                                 } else if (op == '}')   break;
  1181.                                 else if (M.macro1s_[op]) {
  1182.                                         ! mx0p || F.err(err_parse_mxP_tokP_macro_);
  1183.                                         var mxP_tokP = M.macro1s_[op]();
  1184.                                         mx0p = mxP_tokP[0];
  1185.                                         tokP = mxP_tokP[1];
  1186.                                 } else {
  1187.                                         embelWs_[op] || F.err(err_parse_mxP_tokP_script_);
  1188.                                         if (prec >= 999)        break;
  1189.                                         var meTok_tokP = parseEmbel(mx0p && [mx0p, null], tokP),
  1190.                                                 meTok = meTok_tokP[0];
  1191.                                         meTok || F.err(err_parse_mxP_tokP_embel_2_);
  1192.                                         tokP = meTok_tokP[1];
  1193.                                         var a = [meTok[0]], opP = meTok[1];
  1194.                                         if (opP) {
  1195.                                                 var precROpt = M.infix_[opP] || M.prefix_[opP];
  1196.                                                 if (precROpt) {
  1197.                                                         var mxP_tokP = parse_mxP_tokP(precROpt, tokP);
  1198.                                                         if (mxP_tokP[0])        a.push(mxP_tokP[0]);
  1199.                                                         tokP = mxP_tokP[1];
  1200.                                                 }
  1201.                                         }
  1202.                                         mx0p = a.length == 1 ? a[0] : M.newMe('mrow', a, docP_);
  1203.                                 }
  1204.                         }
  1205.                 }
  1206.                 return [mx0p, tokP];
  1207.         }
  1208.         M.sToMe = function(g, docP) {
  1209.                 if (! docP)     docP = document;
  1210.                 M.infix_[''] && M.infix_[','] || F.err(err_sToMe_1_);
  1211.                
  1212.                 if (M.MathML == null)   M.MathML = M.canMathML();
  1213.                 M.re_.lastIndex = 0;
  1214.                 s_ = '';
  1215.                 s_or_mx_a_ = Array.isArray(g) ? g : [g];
  1216.                 s_or_mx_i_ = 0;
  1217.                 docP_ = docP;
  1218.                 precAdj_ = M.infix_[''];
  1219.                
  1220.                 var mxP_tokP = parse_mxP_tokP(0);
  1221.                 if (mxP_tokP[1])
  1222.                         throw 'Extra input:  ' + mxP_tokP[1][1] + s_.substring(M.re_.lastIndex) +
  1223.                                 (s_or_mx_i_ < s_or_mx_a_.length ? '...' : '');
  1224.                 if (M.re_.lastIndex < s_.length || s_or_mx_i_ < s_or_mx_a_.length)      F.err(err_sToMe_2_);
  1225.                 return mxP_tokP[0];
  1226.         };
  1227.         M.sToMathE = function(g, blockQ, docP) /* parses strings and includes MathML subexpression
  1228.                         elements into an XML 'math' or HTML 'fmath' element */ {
  1229.                 var res = M.sToMe(g, docP);
  1230.                 if (! (res && F.elem(M.mtagName(res), ['math', 'fmath'])))
  1231.                         res = M.newMe('math', res, docP);
  1232.                 if (typeof g == 'string')       res.setAttribute('alttext', g);
  1233.                 return M.setMathBlockQ(res, blockQ);
  1234.         };
  1235.        
  1236.         /*  Like TeX, we use $ or \( \), and $$ or \[ \], to delimit inline and block ("display")
  1237.                 mathematics, respectively.  Use \$ for an actual $ instead, or \\ for \ if necessary.
  1238.                 */
  1239.         M.$mathQ = true;        // whether $ acts as an (inline) math delimiter
  1240.         M.inline$$Q = false;    /* whether $$ $$ or \[ \] in a <p> or <span> should be wrapped in an
  1241.                 inline-block */
  1242.         M.parseMath = function(nod) {
  1243.                 if (nod.nodeType == 1 /* Element */ && nod.tagName != 'SCRIPT') {
  1244.                         if (nod.tagName.toUpperCase() == 'MATH') {
  1245.                                 var newE = M.eToMathE(nod);
  1246.                                 if (newE != nod)        nod.parentNode.replaceChild(newE, nod);
  1247.                         } else
  1248.                                 for (var p = nod.firstChild; p; ) {
  1249.                                         var restP = p.nextSibling;      // do before splitting 'p'
  1250.                                         M.parseMath(p);
  1251.                                         p = restP;
  1252.                                 }
  1253.                 } else if (nod.nodeType == 3 /* Text */ && /[$\\]/.test(nod.data) /* for speed */) {
  1254.                         var doc = nod.ownerDocument, s = nod.data, a = [], t = '',
  1255.                                 re = /\\([$\\])|\$\$?|\\[([]/g;
  1256.                         while (true) {
  1257.                                 var j = re.lastIndex, m = re.exec(s), k = m ? m.index : s.length;
  1258.                                 if (j < k)      t += s.substring(j, k);
  1259.                                 if (m && m[1])  t += m[1];
  1260.                                 else {
  1261.                                         var i = -1, z;
  1262.                                         if (m) {
  1263.                                                 z = m[0] == '\\(' ? '\\)' : m[0] == '\\[' ? '\\]' : m[0];
  1264.                                                 if (re.lastIndex < s.length && (m[0] != '$' || M.$mathQ)) {
  1265.                                                         i = s.indexOf(z, re.lastIndex);
  1266.                                                         while (i != -1 && s.charAt(i - 1) == '\\')      i = s.indexOf(z, i + 1);
  1267.                                                 }
  1268.                                                 if (i == -1) {
  1269.                                                         t += m[0];
  1270.                                                         continue;
  1271.                                                 }
  1272.                                         }
  1273.                                         if (t) {
  1274.                                                 a.push(doc.createTextNode(t));
  1275.                                                 t = '';
  1276.                                         }
  1277.                                         if (! m)        break;
  1278.                                         var blockQ = m[0] == '$$' || m[0] == '\\[',
  1279.                                                 e = M.sToMathE(s.substring(re.lastIndex, i), blockQ, doc);
  1280.                                         if (blockQ && M.inline$$Q && F.elem(nod.parentNode.nodeName, ['P', 'SPAN']))
  1281.                                         {
  1282.                                                 var wrap$ = $('<div/>', doc).css('display', 'inline-block').append(e);
  1283.                                                 e = wrap$[0];
  1284.                                         }
  1285.                                         a.push(e);
  1286.                                         re.lastIndex = i + z.length;
  1287.                                 }
  1288.                         }
  1289.                         F.iter(function(x) { nod.parentNode.insertBefore(x, nod); }, a);
  1290.                         nod.parentNode.removeChild(nod);
  1291.                 }
  1292.         };
  1293.         M.parseMathQ = true;
  1294.         $(function() {
  1295.                 if (M.MathML == null)   M.MathML = M.canMathML();
  1296.                 if (M.parseMathQ)
  1297.                         try {
  1298.                                 M.parseMath(document.body);
  1299.                         } catch(exc) {
  1300.                                 alert(exc);
  1301.                         }
  1302.         });
  1303.        
  1304.         return M;
  1305. }();
  1306. var M;  if (M === undefined)    M = jqMath;
  1307.