/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************

*
*  MathJax/jax/element/mml/jax.js
*  
*  Implements the MML ElementJax that holds the internal represetation
*  of the mathematics on the page.  Various InputJax will produce this
*  format, and the OutputJax will display it in various formats.
*
*  ---------------------------------------------------------------------
*  
*  Copyright (c) 2009-2018 The MathJax Consortium
* 
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
* 
*      http://www.apache.org/licenses/LICENSE-2.0
* 
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

MathJax.ElementJax.mml = MathJax.ElementJax({

mimeType: "jax/mml"

},{

id: "mml",
version: "2.7.5",
directory: MathJax.ElementJax.directory + "/mml",
extensionDir: MathJax.ElementJax.extensionDir + "/mml",
optableDir: MathJax.ElementJax.directory + "/mml/optable"

});

MathJax.ElementJax.mml.Augment({

Init: function () {
  if (arguments.length === 1 && arguments[0].type === "math") {this.root = arguments[0]}
    else {this.root = MathJax.ElementJax.mml.math.apply(this,arguments)}
  if (this.root.attr && this.root.attr.mode) {
    if (!this.root.display && this.root.attr.mode === "display") {
      this.root.display = "block";
      this.root.attrNames.push("display");
    }
    delete this.root.attr.mode;
    for (var i = 0, m = this.root.attrNames.length; i < m; i++) {
      if (this.root.attrNames[i] === "mode") {this.root.attrNames.splice(i,1); break}
    }
  }
}

},{

INHERIT: "_inherit_",
AUTO: "_auto_",
SIZE: {
  INFINITY: "infinity",
  SMALL: "small",
  NORMAL: "normal",
  BIG: "big"
},
COLOR: {
  TRANSPARENT: "transparent"
},
VARIANT: {
  NORMAL: "normal",
  BOLD: "bold",
  ITALIC: "italic",
  BOLDITALIC: "bold-italic",
  DOUBLESTRUCK: "double-struck",
  FRAKTUR: "fraktur",
  BOLDFRAKTUR: "bold-fraktur",
  SCRIPT: "script",
  BOLDSCRIPT: "bold-script",
  SANSSERIF: "sans-serif",
  BOLDSANSSERIF: "bold-sans-serif",
  SANSSERIFITALIC: "sans-serif-italic",
  SANSSERIFBOLDITALIC: "sans-serif-bold-italic",
  MONOSPACE: "monospace",
  INITIAL: "inital",
  TAILED: "tailed",
  LOOPED: "looped",
  STRETCHED: "stretched",
  CALIGRAPHIC: "-tex-caligraphic",
  OLDSTYLE: "-tex-oldstyle"
},
FORM: {
  PREFIX: "prefix",
  INFIX: "infix",
  POSTFIX: "postfix"
},
LINEBREAK: {
  AUTO: "auto",
  NEWLINE: "newline",
  NOBREAK: "nobreak",
  GOODBREAK: "goodbreak",
  BADBREAK: "badbreak"
},
LINEBREAKSTYLE: {
  BEFORE: "before",
  AFTER: "after",
  DUPLICATE: "duplicate",
  INFIXLINBREAKSTYLE: "infixlinebreakstyle"
},
INDENTALIGN: {
  LEFT: "left",
  CENTER: "center",
  RIGHT: "right",
  AUTO: "auto",
  ID: "id",
  INDENTALIGN: "indentalign"
},
INDENTSHIFT: {
  INDENTSHIFT: "indentshift"
},
LINETHICKNESS: {
  THIN: "thin",
  MEDIUM: "medium",
  THICK: "thick"
},
NOTATION: {
  LONGDIV: "longdiv",
  ACTUARIAL: "actuarial",
  RADICAL: "radical",
  BOX: "box",
  ROUNDEDBOX: "roundedbox",
  CIRCLE: "circle",
  LEFT: "left",
  RIGHT: "right",
  TOP: "top",
  BOTTOM: "bottom",
  UPDIAGONALSTRIKE: "updiagonalstrike",
  DOWNDIAGONALSTRIKE: "downdiagonalstrike",
  UPDIAGONALARROW: "updiagonalarrow",
  VERTICALSTRIKE: "verticalstrike",
  HORIZONTALSTRIKE: "horizontalstrike",
  PHASORANGLE: "phasorangle",
  MADRUWB: "madruwb"
},
ALIGN: {
  TOP: "top",
  BOTTOM: "bottom",
  CENTER: "center",
  BASELINE: "baseline",
  AXIS: "axis",
  LEFT: "left",
  RIGHT: "right"
},
LINES: {
  NONE: "none",
  SOLID: "solid",
  DASHED: "dashed"
},
SIDE: {
  LEFT: "left",
  RIGHT: "right",
  LEFTOVERLAP: "leftoverlap",
  RIGHTOVERLAP: "rightoverlap"
},
WIDTH: {
  AUTO: "auto",
  FIT: "fit"
},
ACTIONTYPE: {
  TOGGLE: "toggle",
  STATUSLINE: "statusline",
  TOOLTIP: "tooltip",
  INPUT: "input"
},
LENGTH: {
  VERYVERYTHINMATHSPACE: "veryverythinmathspace",
  VERYTHINMATHSPACE: "verythinmathspace",
  THINMATHSPACE: "thinmathspace",
  MEDIUMMATHSPACE: "mediummathspace",
  THICKMATHSPACE: "thickmathspace",
  VERYTHICKMATHSPACE: "verythickmathspace",
  VERYVERYTHICKMATHSPACE: "veryverythickmathspace",
  NEGATIVEVERYVERYTHINMATHSPACE: "negativeveryverythinmathspace",
  NEGATIVEVERYTHINMATHSPACE: "negativeverythinmathspace",
  NEGATIVETHINMATHSPACE: "negativethinmathspace",
  NEGATIVEMEDIUMMATHSPACE: "negativemediummathspace",
  NEGATIVETHICKMATHSPACE: "negativethickmathspace",
  NEGATIVEVERYTHICKMATHSPACE: "negativeverythickmathspace",
  NEGATIVEVERYVERYTHICKMATHSPACE: "negativeveryverythickmathspace"
},
OVERFLOW: {
  LINBREAK: "linebreak",
  SCROLL: "scroll",
  ELIDE: "elide",
  TRUNCATE: "truncate",
  SCALE: "scale"
},
UNIT: {
  EM: "em",
  EX: "ex",
  PX: "px",
  IN: "in",
  CM: "cm",
  MM: "mm",
  PT: "pt",
  PC: "pc"
},
TEXCLASS: {
  ORD:   0,
  OP:    1,
  BIN:   2,
  REL:   3,
  OPEN:  4,
  CLOSE: 5,
  PUNCT: 6,
  INNER: 7,
  VCENTER: 8,
  NONE:   -1
},
TEXCLASSNAMES: ["ORD", "OP", "BIN", "REL", "OPEN", "CLOSE", "PUNCT", "INNER", "VCENTER"],
skipAttributes: {
  texClass:true, useHeight:true, texprimestyle:true
},
copyAttributes: {
  displaystyle:1, scriptlevel:1, open:1, close:1, form:1,
  actiontype: 1,
  fontfamily:true, fontsize:true, fontweight:true, fontstyle:true,
  color:true, background:true,
  id:true, "class":1, href:true, style:true
},
copyAttributeNames: [
  "displaystyle", "scriptlevel", "open", "close", "form",  // force these to be copied
  "actiontype",
  "fontfamily", "fontsize", "fontweight", "fontstyle",
  "color", "background",
  "id", "class", "href", "style"
],
nocopyAttributes: {
  fontfamily: true, fontsize: true, fontweight: true, fontstyle: true,
  color: true, background: true,
  id: true, 'class': true, href: true, style: true,
  xmlns: true
},
Error: function (message,def) {
  var mml = this.merror(message),
      dir = MathJax.Localization.fontDirection(),
      font = MathJax.Localization.fontFamily();
  if (def) {mml = mml.With(def)}
  if (dir || font) {
    mml = this.mstyle(mml);
    if (dir) {mml.dir = dir}
    if (font) {mml.style.fontFamily = "font-family: "+font}
  }
  return mml;
}

});

(function (MML) {

MML.mbase = MathJax.Object.Subclass({
  type: "base", isToken: false,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    dir: MML.INHERIT
  },
  noInherit: {},
  noInheritAttribute: {
    texClass: true
  },
  getRemoved: {},
  linebreakContainer: false,

  Init: function () {
    this.data = [];

this.attrCache = {};

  if (this.inferRow && !(arguments.length === 1 && arguments[0].inferred))
    {this.Append(MML.mrow().With({inferred: true, notParent: true}))}
  this.Append.apply(this,arguments);
},
With: function (def) {
  for (var id in def) {if (def.hasOwnProperty(id)) {this[id] = def[id]}}
  return this;
},
Append: function () {
  if (this.inferRow && this.data.length) {
    this.data[0].Append.apply(this.data[0],arguments);
  } else {
    for (var i = 0, m = arguments.length; i < m; i++)
      {this.SetData(this.data.length,arguments[i])}
  }
},
SetData: function (i,item) {
  if (item != null) {
    if (!(item instanceof MML.mbase))
      {item = (this.isToken || this.isChars ? MML.chars(item) : MML.mtext(item))}

item.attrCache = {};

    item.parent = this;
    item.setInherit(this.inheritFromMe ? this : this.inherit);
  }
  this.data[i] = item;
},
Parent: function () {
  var parent = this.parent;
  while (parent && parent.notParent) {parent = parent.parent}
  return parent;
},
Get: function (name,nodefault,noself) {
  if (!noself) {
    if (this[name] != null) {return this[name]}
    if (this.attr && this.attr[name] != null) {return this.attr[name]}
  }

var cache = this.attrCache, value; if (cache) {return cache}

// FIXME: should cache these values and get from cache
// (clear cache when appended to a new object?)
var parent = this.Parent();
if (parent && parent["adjustChild_"+name] != null) {

// return (parent)(this.childPosition(),nodefault);

  value = (parent["adjustChild_"+name])(this.childPosition(),nodefault);
  cache[name] = value;
  return value;
}
var obj = this.inherit; var root = obj;
while (obj) {
  var value = obj[name]; if (value == null && obj.attr) {value = obj.attr[name]}
  if (obj.removedStyles && obj.getRemoved[name] && value == null) value = obj.removedStyles[obj.getRemoved[name]];
  if (value != null && obj.noInheritAttribute && !obj.noInheritAttribute[name]) {
    var noInherit = obj.noInherit[this.type];

// if (!(noInherit && noInherit)) {return value}

    if (!(noInherit && noInherit[name])) {cache[name] = value; return value}
  }
  root = obj; obj = obj.inherit;
}

value = null; if (!nodefault) {

if (this.defaults[name] === MML.AUTO) cache[name] = value = this.autoDefault(name);
else if (this.defaults[name] !== MML.INHERIT && this.defaults[name] != null)
  cache[name] = value = this.defaults[name];
else if (root) cache[name] = value = root.defaults[name];

} return value;

    /* 
     * if (!nodefault) {
     *   if (this.defaults[name] === MML.AUTO) {return this.autoDefault(name)}
     *   if (this.defaults[name] !== MML.INHERIT && this.defaults[name] != null)
     *     {return this.defaults[name]}
     *   if (root) {return root.defaults[name]}
     * }
     * return null;
     */
  },
  hasValue: function (name) {return (this.Get(name,true) != null)},
  getValues: function () {
    var values = {};
    for (var i = 0, m = arguments.length; i < m; i++)
      {values[arguments[i]] = this.Get(arguments[i])}
    return values;
  },
  adjustChild_scriptlevel:   function (i,nodef) {return this.Get("scriptlevel",nodef)},   // always inherit from parent
  adjustChild_displaystyle:  function (i,nodef) {return this.Get("displaystyle",nodef)},  // always inherit from parent
  adjustChild_texprimestyle: function (i,nodef) {return this.Get("texprimestyle",nodef)}, // always inherit from parent
  childPosition: function () {
    var child = this, parent = child.parent;
    while (parent.notParent) {child = parent; parent = child.parent}
    for (var i = 0, m = parent.data.length; i < m; i++) {if (parent.data[i] === child) {return i}}
    return null;
  },
  setInherit: function (obj) {
    if (obj !== this.inherit && this.inherit == null) {
      this.inherit = obj;
      for (var i = 0, m = this.data.length; i < m; i++) {
        if (this.data[i] && this.data[i].setInherit) {this.data[i].setInherit(obj)}
      }
    }
  },
  setTeXclass: function (prev) {
    this.getPrevClass(prev);
    return (typeof(this.texClass) !== "undefined" ? this : prev);
  },
  getPrevClass: function (prev) {
    if (prev) {
      this.prevClass = prev.Get("texClass");
      this.prevLevel = prev.Get("scriptlevel");
    }
  },
  updateTeXclass: function (core) {
    if (core) {
      this.prevClass = core.prevClass; delete core.prevClass;
      this.prevLevel = core.prevLevel; delete core.prevLevel;
      this.texClass = core.Get("texClass");
    }
  },
  texSpacing: function () {
    var prev = (this.prevClass != null ? this.prevClass : MML.TEXCLASS.NONE);
    var tex = (this.Get("texClass") || MML.TEXCLASS.ORD);
    if (prev === MML.TEXCLASS.NONE || tex === MML.TEXCLASS.NONE) {return ""}
    if (prev === MML.TEXCLASS.VCENTER) {prev = MML.TEXCLASS.ORD}
    if (tex  === MML.TEXCLASS.VCENTER) {tex  = MML.TEXCLASS.ORD}
    var space = this.TEXSPACE[prev][tex];
    if (this.prevLevel > 0 && this.Get("scriptlevel") > 0 && space >= 0) {return ""}
    return this.TEXSPACELENGTH[Math.abs(space)];
  },
  TEXSPACELENGTH:[
    "",
    MML.LENGTH.THINMATHSPACE,
    MML.LENGTH.MEDIUMMATHSPACE,
    MML.LENGTH.THICKMATHSPACE
  ],
  // See TeXBook Chapter 18 (p. 170)
  TEXSPACE: [
    [ 0,-1, 2, 3, 0, 0, 0, 1], // ORD
    [-1,-1, 0, 3, 0, 0, 0, 1], // OP
    [ 2, 2, 0, 0, 2, 0, 0, 2], // BIN
    [ 3, 3, 0, 0, 3, 0, 0, 3], // REL
    [ 0, 0, 0, 0, 0, 0, 0, 0], // OPEN
    [ 0,-1, 2, 3, 0, 0, 0, 1], // CLOSE
    [ 1, 1, 0, 1, 1, 1, 1, 1], // PUNCT
    [ 1,-1, 2, 3, 1, 0, 1, 1]  // INNER
  ],
  autoDefault: function (name) {return ""},
  isSpacelike: function () {return false},
  isEmbellished: function () {return false},
  Core: function () {return this},
  CoreMO: function () {return this},
  childIndex: function(child) {
    if (child == null) return;
    for (var i = 0, m = this.data.length; i < m; i++) if (child === this.data[i]) return i;
  },
  CoreIndex: function () {return this.childIndex(this.Core())},
  hasNewline: function () {
    if (this.isEmbellished()) {return this.CoreMO().hasNewline()}
    if (this.isToken || this.linebreakContainer) {return false}
    for (var i = 0, m = this.data.length; i < m; i++) {
      if (this.data[i] && this.data[i].hasNewline()) {return true}
    }
    return false;
  },
  array: function () {if (this.inferred) {return this.data} else {return [this]}},
  toString: function () {return this.type+"("+this.data.join(",")+")"},
  getAnnotation: function () { return null; }
},{
  childrenSpacelike: function () {
    for (var i = 0, m = this.data.length; i < m; i++)
      {if (!this.data[i].isSpacelike()) {return false}}
    return true;
  },
  childEmbellished: function () {
    return (this.data[0] && this.data[0].isEmbellished());
  },
  childCore: function () {return this.data[0]},
  childCoreMO: function () {return (this.data[0] ? this.data[0].CoreMO() : null)},
  setChildTeXclass: function (prev) {
    if (this.data[0]) {
      prev = this.data[0].setTeXclass(prev);
      this.updateTeXclass(this.data[0]);
    }
    return prev;
  },
  setBaseTeXclasses: function (prev) {
    this.getPrevClass(prev); this.texClass = null;
    if (this.data[0]) {
      if (this.isEmbellished() || this.data[0].isa(MML.mi)) {
        prev = this.data[0].setTeXclass(prev);
        this.updateTeXclass(this.Core());
      } else {this.data[0].setTeXclass(); prev = this}
    } else {prev = this}
    for (var i = 1, m = this.data.length; i < m; i++)
      {if (this.data[i]) {this.data[i].setTeXclass()}}
    return prev;
  },
  setSeparateTeXclasses: function (prev) {
    this.getPrevClass(prev);
    for (var i = 0, m = this.data.length; i < m; i++)
      {if (this.data[i]) {this.data[i].setTeXclass()}}
    if (this.isEmbellished()) {this.updateTeXclass(this.Core())}
    return this;
  }
});

MML.mi = MML.mbase.Subclass({
  type: "mi", isToken: true,
  texClass: MML.TEXCLASS.ORD,
  defaults: {
    mathvariant: MML.AUTO,
    mathsize: MML.INHERIT,
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    dir: MML.INHERIT
  },
  autoDefault: function (name) {
    if (name === "mathvariant") {
      var mi = (this.data[0]||"").toString();
      return (mi.length === 1 ||
             (mi.length === 2 && mi.charCodeAt(0) >= 0xD800 && mi.charCodeAt(0) < 0xDC00) ?
                MML.VARIANT.ITALIC : MML.VARIANT.NORMAL);
    }
    return "";
  },
  setTeXclass: function (prev) {
    this.getPrevClass(prev);
    var name = this.data.join("");
    if (name.length > 1 && name.match(/^[a-z][a-z0-9]*$/i) &&
        this.texClass === MML.TEXCLASS.ORD) {
      this.texClass = MML.TEXCLASS.OP;
      this.autoOP = true;
    }
    return this;
  }
});

MML.mn = MML.mbase.Subclass({
  type: "mn", isToken: true,
  texClass: MML.TEXCLASS.ORD,
  defaults: {
    mathvariant: MML.INHERIT,
    mathsize: MML.INHERIT,
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    dir: MML.INHERIT
  }
});

MML.mo = MML.mbase.Subclass({
  type: "mo", isToken: true,
  defaults: {
    mathvariant: MML.INHERIT,
    mathsize: MML.INHERIT,
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    dir: MML.INHERIT,
    form: MML.AUTO,
    fence: MML.AUTO,
    separator: MML.AUTO,
    lspace: MML.AUTO,
    rspace: MML.AUTO,
    stretchy: MML.AUTO,
    symmetric: MML.AUTO,
    maxsize: MML.AUTO,
    minsize: MML.AUTO,
    largeop: MML.AUTO,
    movablelimits: MML.AUTO,
    accent: MML.AUTO,
    linebreak: MML.LINEBREAK.AUTO,
    lineleading: MML.INHERIT,
    linebreakstyle: MML.AUTO,
    linebreakmultchar: MML.INHERIT,
    indentalign: MML.INHERIT,
    indentshift: MML.INHERIT,
    indenttarget: MML.INHERIT,
    indentalignfirst: MML.INHERIT,
    indentshiftfirst: MML.INHERIT,
    indentalignlast: MML.INHERIT,
    indentshiftlast: MML.INHERIT,
    texClass: MML.AUTO
  },
  defaultDef: {
    form: MML.FORM.INFIX,
    fence: false,
    separator: false,
    lspace: MML.LENGTH.THICKMATHSPACE,
    rspace: MML.LENGTH.THICKMATHSPACE,
    stretchy: false,
    symmetric: false,
    maxsize: MML.SIZE.INFINITY,
    minsize: '0em', //'1em',
    largeop: false,
    movablelimits: false,
    accent: false,
    linebreak: MML.LINEBREAK.AUTO,
    lineleading: "1ex",
    linebreakstyle: "before",
    indentalign: MML.INDENTALIGN.AUTO,
    indentshift: "0",
    indenttarget: "",
    indentalignfirst: MML.INDENTALIGN.INDENTALIGN,
    indentshiftfirst: MML.INDENTSHIFT.INDENTSHIFT,
    indentalignlast: MML.INDENTALIGN.INDENTALIGN,
    indentshiftlast: MML.INDENTSHIFT.INDENTSHIFT,
    texClass: MML.TEXCLASS.REL // for MML, but TeX sets ORD explicitly
  },
  SPACE_ATTR: {lspace: 0x01, rspace: 0x02, form: 0x04},
  useMMLspacing: 0x07,
  autoDefault: function (name,nodefault) {
    var def = this.def;
    if (!def) {
      if (name === "form") {this.useMMLspacing &= ~this.SPACE_ATTR.form; return this.getForm()}
      var mo = this.data.join("");
      var forms = [this.Get("form"),MML.FORM.INFIX,MML.FORM.POSTFIX,MML.FORM.PREFIX];
      for (var i = 0, m = forms.length; i < m; i++) {
        var data = this.OPTABLE[forms[i]][mo];
        if (data) {def = this.makeDef(data); break}
      }
      if (!def) {def = this.CheckRange(mo)}
      if (!def && nodefault) {def = {}} else {
        if (!def) {def = MathJax.Hub.Insert({},this.defaultDef)}
        if (this.parent) {this.def = def} else {def = MathJax.Hub.Insert({},def)}
        def.form = forms[0];
      }
    }
    this.useMMLspacing &= ~(this.SPACE_ATTR[name] || 0);
    if (def[name] != null) {return def[name]}
    else if (!nodefault) {return this.defaultDef[name]}
    return "";
  },
  CheckRange: function (mo) {
    var n = mo.charCodeAt(0);
    if (n >= 0xD800 && n < 0xDC00) {n = (((n-0xD800)<<10)+(mo.charCodeAt(1)-0xDC00))+0x10000}
    for (var i = 0, m = this.RANGES.length; i < m && this.RANGES[i][0] <= n; i++) {
      if (n <= this.RANGES[i][1]) {
        if (this.RANGES[i][3]) {
          var file = MML.optableDir+"/"+this.RANGES[i][3]+".js";
          this.RANGES[i][3] = null;
          MathJax.Hub.RestartAfter(MathJax.Ajax.Require(file));
        }
        var data = MML.TEXCLASSNAMES[this.RANGES[i][2]];
        data = this.OPTABLE.infix[mo] = MML.mo.OPTYPES[data === "BIN" ? "BIN3" : data];
        return this.makeDef(data);
      }
    }
    return null;
  },
  makeDef: function (data) {
    if (data[2] == null) {data[2] = this.defaultDef.texClass}
    if (!data[3]) {data[3] = {}}
    var def = MathJax.Hub.Insert({},data[3]);
    def.lspace = this.SPACE[data[0]]; def.rspace = this.SPACE[data[1]];
    def.texClass = data[2];
    if (def.texClass === MML.TEXCLASS.REL &&
       (this.movablelimits || this.data.join("").match(/^[a-z]+$/i)))
           {def.texClass = MML.TEXCLASS.OP} // mark named operators as OP
    return def;
  },
  getForm: function () {
    var core = this, parent = this.parent, Parent = this.Parent();
    while (Parent && Parent.isEmbellished())
      {core = parent; parent = Parent.parent; Parent = Parent.Parent()}
    if (parent && parent.type === "mrow" && parent.NonSpaceLength() !== 1) {
      if (parent.FirstNonSpace() === core) {return MML.FORM.PREFIX}
      if (parent.LastNonSpace() === core) {return MML.FORM.POSTFIX}
    }
    return MML.FORM.INFIX;
  },
  isEmbellished: function () {return true},
  hasNewline: function () {return (this.Get("linebreak") === MML.LINEBREAK.NEWLINE)},
  CoreParent: function () {
    var parent = this;
    while (parent && parent.isEmbellished() &&
           parent.CoreMO() === this && !parent.isa(MML.math)) {parent = parent.Parent()}
    return parent;
  },
  CoreText: function (parent) {
    if (!parent) {return ""}
    if (parent.isEmbellished()) {return parent.CoreMO().data.join("")}
    while ((((parent.isa(MML.mrow) || parent.isa(MML.TeXAtom) ||
              parent.isa(MML.mstyle) || parent.isa(MML.mphantom)) &&
              parent.data.length === 1) || parent.isa(MML.munderover)) &&
              parent.data[0]) {parent = parent.data[0]}
    if (!parent.isToken) {return ""} else {return parent.data.join("")}
  },
  remapChars: {
    '*':"\u2217",
    '"':"\u2033",
    "\u00B0":"\u2218",
    "\u00B2":"2",
    "\u00B3":"3",
    "\u00B4":"\u2032",
    "\u00B9":"1"
  },
  remap: function (text,map) {
    text = text.replace(/-/g,"\u2212");
    if (map) {
      text = text.replace(/'/g,"\u2032").replace(/`/g,"\u2035");
      if (text.length === 1) {text = map[text]||text}
    }
    return text;
  },
  setTeXclass: function (prev) {
    var values = this.getValues("form","lspace","rspace","fence"); // sets useMMLspacing
    if (this.useMMLspacing) {this.texClass = MML.TEXCLASS.NONE; return this}
    if (values.fence && !this.texClass) {
      if (values.form === MML.FORM.PREFIX) {this.texClass = MML.TEXCLASS.OPEN}
      if (values.form === MML.FORM.POSTFIX) {this.texClass = MML.TEXCLASS.CLOSE}
    }
    this.texClass = this.Get("texClass");
    if (this.data.join("") === "\u2061") {
      // force previous node to be texClass OP, and skip this node
      if (prev) {prev.texClass = MML.TEXCLASS.OP; prev.fnOP = true}
      this.texClass = this.prevClass = MML.TEXCLASS.NONE;
      return prev;
    }
    return this.adjustTeXclass(prev);
  },
  adjustTeXclass: function (prev) {
    if (this.texClass === MML.TEXCLASS.NONE) {return prev}
    if (prev) {
      if (prev.autoOP && (this.texClass === MML.TEXCLASS.BIN ||
                          this.texClass === MML.TEXCLASS.REL))
        {prev.texClass = MML.TEXCLASS.ORD}
      this.prevClass = prev.texClass || MML.TEXCLASS.ORD;
      this.prevLevel = prev.Get("scriptlevel")
    } else {this.prevClass = MML.TEXCLASS.NONE}
    if (this.texClass === MML.TEXCLASS.BIN &&
          (this.prevClass === MML.TEXCLASS.NONE ||
           this.prevClass === MML.TEXCLASS.BIN ||
           this.prevClass === MML.TEXCLASS.OP ||
           this.prevClass === MML.TEXCLASS.REL ||
           this.prevClass === MML.TEXCLASS.OPEN ||
           this.prevClass === MML.TEXCLASS.PUNCT)) {
      this.texClass = MML.TEXCLASS.ORD;
    } else if (this.prevClass === MML.TEXCLASS.BIN &&
                 (this.texClass === MML.TEXCLASS.REL ||
                  this.texClass === MML.TEXCLASS.CLOSE ||
                  this.texClass === MML.TEXCLASS.PUNCT)) {
      prev.texClass = this.prevClass = MML.TEXCLASS.ORD;
    }
    return this;
  }
});

MML.mtext = MML.mbase.Subclass({
  type: "mtext", isToken: true,
  isSpacelike: function () {return true},
  texClass: MML.TEXCLASS.ORD,
  defaults: {
    mathvariant: MML.INHERIT,
    mathsize: MML.INHERIT,
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    dir: MML.INHERIT
  }
});

MML.mspace = MML.mbase.Subclass({
  type: "mspace", isToken: true,
  isSpacelike: function () {return true},
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    width: "0em",
    height: "0ex",
    depth: "0ex",
    linebreak: MML.LINEBREAK.AUTO
  },
  hasDimAttr: function () {
    return (this.hasValue("width") || this.hasValue("height") ||
            this.hasValue("depth"));
  },
  hasNewline: function () {
    // The MathML spec says that the linebreak attribute should be ignored
    // if any dimensional attribute is set.
    return (!this.hasDimAttr() &&
            this.Get("linebreak") === MML.LINEBREAK.NEWLINE);
  }
});

MML.ms = MML.mbase.Subclass({
  type: "ms", isToken: true,
  texClass: MML.TEXCLASS.ORD,
  defaults: {
    mathvariant: MML.INHERIT,
    mathsize: MML.INHERIT,
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    dir: MML.INHERIT,
    lquote: '"',
    rquote: '"'
  }
});

MML.mglyph = MML.mbase.Subclass({
  type: "mglyph", isToken: true,
  texClass: MML.TEXCLASS.ORD,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    alt: "",
    src: "",
    width: MML.AUTO,
    height: MML.AUTO,
    valign: "0em"
  }
});

MML.mrow = MML.mbase.Subclass({
  type: "mrow",
  isSpacelike: MML.mbase.childrenSpacelike,
  inferred: false, notParent: false,
  isEmbellished: function () {
    var isEmbellished = false;
    for (var i = 0, m = this.data.length; i < m; i++) {
      if (this.data[i] == null) continue;
      if (this.data[i].isEmbellished()) {
        if (isEmbellished) {return false}
        isEmbellished = true; this.core = i;
      } else if (!this.data[i].isSpacelike()) {return false}
    }
    return isEmbellished;
  },
  NonSpaceLength: function () {
    var n = 0;
    for (var i = 0, m = this.data.length; i < m; i++)
      {if (this.data[i] && !this.data[i].isSpacelike()) {n++}}
    return n;
  },
  FirstNonSpace: function () {
    for (var i = 0, m = this.data.length; i < m; i++)
      {if (this.data[i] && !this.data[i].isSpacelike()) {return this.data[i]}}
    return null;
  },
  LastNonSpace: function () {
    for (var i = this.data.length-1; i >= 0; i--)
      {if (this.data[0] && !this.data[i].isSpacelike()) {return this.data[i]}}
    return null;
  },
  Core: function () {
    if (!(this.isEmbellished()) || typeof(this.core) === "undefined") {return this}
    return this.data[this.core];
  },
  CoreMO: function () {
    if (!(this.isEmbellished()) || typeof(this.core) === "undefined") {return this}
    return this.data[this.core].CoreMO();
  },
  toString: function () {
    if (this.inferred) {return '[' + this.data.join(',') + ']'}
    return this.SUPER(arguments).toString.call(this);
  },
  setTeXclass: function (prev) {
    var i, m = this.data.length;
    if ((this.open || this.close) && (!prev || !prev.fnOP)) {
      //
      // <mrow> came from \left...\right
      // so treat as subexpression (tex class INNER)
      //
      this.getPrevClass(prev); prev = null;
      for (i = 0; i < m; i++)
        {if (this.data[i]) {prev = this.data[i].setTeXclass(prev)}}
      if (!this.hasOwnProperty("texClass")) this.texClass = MML.TEXCLASS.INNER;
      return this;
    } else {
      //
      //  Normal <mrow>, so treat as
      //  thorugh mrow is not there
      //
      for (i = 0; i < m; i++)
        {if (this.data[i]) {prev = this.data[i].setTeXclass(prev)}}
      if (this.data[0]) {this.updateTeXclass(this.data[0])}
      return prev;
    }
  },
  getAnnotation: function (name) {
    if (this.data.length != 1) return null;
    return this.data[0].getAnnotation(name);
  }
});

MML.mfrac = MML.mbase.Subclass({
  type: "mfrac", num: 0, den: 1,
  linebreakContainer: true,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    linethickness: MML.LINETHICKNESS.MEDIUM,
    numalign: MML.ALIGN.CENTER,
    denomalign: MML.ALIGN.CENTER,
    bevelled: false
  },
  adjustChild_displaystyle: function (n) {return false},
  adjustChild_scriptlevel: function (n) {
    var level = this.Get("scriptlevel");
    if (!this.Get("displaystyle") || level > 0) {level++}
    return level;
  },
  adjustChild_texprimestyle: function (n) {
    if (n == this.den) {return true}
    return this.Get("texprimestyle");
  },
  setTeXclass: MML.mbase.setSeparateTeXclasses
});

MML.msqrt = MML.mbase.Subclass({
  type: "msqrt",
  inferRow: true,
  linebreakContainer: true,
  texClass: MML.TEXCLASS.ORD,
  setTeXclass: MML.mbase.setSeparateTeXclasses,
  adjustChild_texprimestyle: function (n) {return true}
});

MML.mroot = MML.mbase.Subclass({
  type: "mroot",
  linebreakContainer: true,
  texClass: MML.TEXCLASS.ORD,
  adjustChild_displaystyle: function (n) {
    if (n === 1) {return false}
    return this.Get("displaystyle");
  },
  adjustChild_scriptlevel: function (n) {
    var level = this.Get("scriptlevel");
    if (n === 1) {level += 2}
    return level;
  },
  adjustChild_texprimestyle: function (n) {
    if (n === 0) {return true};
    return this.Get("texprimestyle");
  },
  setTeXclass: MML.mbase.setSeparateTeXclasses
});

MML.mstyle = MML.mbase.Subclass({
  type: "mstyle",
  isSpacelike: MML.mbase.childrenSpacelike,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  inferRow: true,
  defaults: {
    scriptlevel: MML.INHERIT,
    displaystyle: MML.INHERIT,
    scriptsizemultiplier: Math.sqrt(1/2),
    scriptminsize: "8pt",
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    dir: MML.INHERIT,
    infixlinebreakstyle: MML.LINEBREAKSTYLE.BEFORE,
    decimalseparator: "."
  },
  adjustChild_scriptlevel: function (n) {
    var level = this.scriptlevel;
    if (level == null) {
      level = this.Get("scriptlevel");
    } else if (String(level).match(/^ *[-+]/)) {
      var LEVEL = this.Get("scriptlevel",null,true);
      level = LEVEL + parseInt(level);
    }
    return level;
  },
  inheritFromMe: true,
  noInherit: {
    mpadded: {width: true, height: true, depth: true, lspace: true, voffset: true},
    mtable:  {width: true, height: true, depth: true, align: true}
  },
  getRemoved: {fontfamily:"fontFamily", fontweight:"fontWeight", fontstyle:"fontStyle", fontsize:"fontSize"},
  setTeXclass: MML.mbase.setChildTeXclass
});

MML.merror = MML.mbase.Subclass({
  type: "merror",
  inferRow: true,
  linebreakContainer: true,
  texClass: MML.TEXCLASS.ORD
});

MML.mpadded = MML.mbase.Subclass({
  type: "mpadded",
  inferRow: true,
  isSpacelike: MML.mbase.childrenSpacelike,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    width: "",
    height: "",
    depth: "",
    lspace: 0,
    voffset: 0
  },
  setTeXclass: MML.mbase.setChildTeXclass
});

MML.mphantom = MML.mbase.Subclass({
  type: "mphantom",
  texClass: MML.TEXCLASS.ORD,
  inferRow: true,
  isSpacelike: MML.mbase.childrenSpacelike,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  setTeXclass: MML.mbase.setChildTeXclass
});

MML.mfenced = MML.mbase.Subclass({
  type: "mfenced",
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    open: '(',
    close: ')',
    separators: ','
  },
  addFakeNodes: function () {
    var values = this.getValues("open","close","separators");
    values.open = values.open.replace(/[ \t\n\r]/g,"");
    values.close = values.close.replace(/[ \t\n\r]/g,"");
    values.separators = values.separators.replace(/[ \t\n\r]/g,"");
    //
    //  Create a fake node for the open item
    //
    if (values.open !== "") {
      this.SetData("open",MML.mo(values.open).With({
        fence:true, form:MML.FORM.PREFIX, texClass:MML.TEXCLASS.OPEN
      }));
      //
      //  Clear flag for using MML spacing even though form is specified
      //
      this.data.open.useMMLspacing = 0;
    }
    //
    //  Create fake nodes for the separators
    //
    if (values.separators !== "") {
      while (values.separators.length < this.data.length)
        {values.separators += values.separators.charAt(values.separators.length-1)}
      for (var i = 1, m = this.data.length; i < m; i++) {
        if (this.data[i]) {
          this.SetData("sep"+i,MML.mo(values.separators.charAt(i-1)).With({separator:true}))
          this.data["sep"+i].useMMLspacing = 0;
        }
      }
    }
    //
    //  Create fake node for the close item
    //
    if (values.close !== "") {
      this.SetData("close",MML.mo(values.close).With({
        fence:true, form:MML.FORM.POSTFIX, texClass:MML.TEXCLASS.CLOSE
      }));
      //
      //  Clear flag for using MML spacing even though form is specified
      //
      this.data.close.useMMLspacing = 0;
    }
  },
  texClass: MML.TEXCLASS.OPEN,
  setTeXclass: function (prev) {
    this.addFakeNodes();
    this.getPrevClass(prev);
    if (this.data.open) {prev = this.data.open.setTeXclass(prev)}
    if (this.data[0]) {prev = this.data[0].setTeXclass(prev)}
    for (var i = 1, m = this.data.length; i < m; i++) {
      if (this.data["sep"+i]) {prev = this.data["sep"+i].setTeXclass(prev)}
      if (this.data[i]) {prev = this.data[i].setTeXclass(prev)}
    }
    if (this.data.close) {prev = this.data.close.setTeXclass(prev)}
    this.updateTeXclass(this.data.open);
    this.texClass = MML.TEXCLASS.INNER;
    return prev;
  }
});

MML.menclose = MML.mbase.Subclass({
  type: "menclose",
  inferRow: true,
  linebreakContainer: true,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    notation: MML.NOTATION.LONGDIV,
    texClass: MML.TEXCLASS.ORD
  },
  setTeXclass: MML.mbase.setSeparateTeXclasses
});

MML.msubsup = MML.mbase.Subclass({
  type: "msubsup", base: 0, sub: 1, sup: 2,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    subscriptshift: "",
    superscriptshift: "",
    texClass: MML.AUTO
  },
  autoDefault: function (name) {
    if (name === "texClass")
      {return (this.isEmbellished() ? this.CoreMO().Get(name) : MML.TEXCLASS.ORD)}
    return 0;
  },
  adjustChild_displaystyle: function (n) {
    if (n > 0) {return false}
    return this.Get("displaystyle");
  },
  adjustChild_scriptlevel: function (n) {
    var level = this.Get("scriptlevel");
    if (n > 0) {level++}
    return level;
  },
  adjustChild_texprimestyle: function (n) {
    if (n === this.sub) {return true}
    return this.Get("texprimestyle");
  },
  setTeXclass: MML.mbase.setBaseTeXclasses
});

MML.msub = MML.msubsup.Subclass({type: "msub"});
MML.msup = MML.msubsup.Subclass({type: "msup", sub:2, sup:1});
MML.mmultiscripts = MML.msubsup.Subclass({
  type: "mmultiscripts",
  adjustChild_texprimestyle: function (n) {
    if (n % 2 === 1) {return true}
    return this.Get("texprimestyle");
  }
});
MML.mprescripts = MML.mbase.Subclass({type: "mprescripts"});
MML.none = MML.mbase.Subclass({type: "none"});

MML.munderover = MML.mbase.Subclass({
  type: "munderover",
  base: 0, under: 1, over: 2, sub: 1, sup: 2,
  ACCENTS: ["", "accentunder", "accent"],
  linebreakContainer: true,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    accent: MML.AUTO,
    accentunder: MML.AUTO,
    align: MML.ALIGN.CENTER,
    texClass: MML.AUTO,
    subscriptshift: "",  // when converted to msubsup by moveablelimits
    superscriptshift: "" // when converted to msubsup by moveablelimits
  },
  autoDefault: function (name) {
    if (name === "texClass")
      {return (this.isEmbellished() ? this.CoreMO().Get(name) : MML.TEXCLASS.ORD)}
    if (name === "accent" && this.data[this.over]) {return this.data[this.over].CoreMO().Get("accent")}
    if (name === "accentunder" && this.data[this.under]) {return this.data[this.under].CoreMO().Get("accent")}
    return false;
  },
  adjustChild_displaystyle: function (n) {
    if (n > 0) {return false}
    return this.Get("displaystyle");
  },
  adjustChild_scriptlevel: function (n) {
    var level = this.Get("scriptlevel");
    var force = (this.data[this.base] && !this.Get("displaystyle") &&
                 this.data[this.base].CoreMO().Get("movablelimits"));
    if (n == this.under && (force || !this.Get("accentunder"))) {level++}
    if (n == this.over  && (force || !this.Get("accent"))) {level++}
    return level;
  },
  adjustChild_texprimestyle: function (n) {
    if (n === this.base && this.data[this.over]) {return true}
    return this.Get("texprimestyle");
  },
  setTeXclass: MML.mbase.setBaseTeXclasses
});

MML.munder = MML.munderover.Subclass({type: "munder"});
MML.mover = MML.munderover.Subclass({
  type: "mover", over: 1, under: 2, sup: 1, sub: 2,
  ACCENTS: ["", "accent", "accentunder"]
});

MML.mtable = MML.mbase.Subclass({
  type: "mtable",
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    align: MML.ALIGN.AXIS,
    rowalign: MML.ALIGN.BASELINE,
    columnalign: MML.ALIGN.CENTER,
    groupalign: "{left}",
    alignmentscope: true,
    columnwidth: MML.WIDTH.AUTO,
    width: MML.WIDTH.AUTO,
    rowspacing: "1ex",
    columnspacing: ".8em",
    rowlines: MML.LINES.NONE,
    columnlines: MML.LINES.NONE,
    frame: MML.LINES.NONE,
    framespacing: "0.4em 0.5ex",
    equalrows: false,
    equalcolumns: false,
    displaystyle: false,
    side: MML.SIDE.RIGHT,
    minlabelspacing: "0.8em",
    texClass: MML.TEXCLASS.ORD,
    useHeight: 1
  },
  adjustChild_displaystyle: function () {
    return (this.displaystyle != null ? this.displaystyle : this.defaults.displaystyle);
  },
  inheritFromMe: true,
  noInherit: {
    mover: {align: true},
    munder: {align: true},
    munderover: {align: true},
    mtable: {
      align: true, rowalign: true, columnalign: true, groupalign: true,
      alignmentscope: true, columnwidth: true, width: true, rowspacing: true,
      columnspacing: true, rowlines: true, columnlines: true, frame: true,
      framespacing: true, equalrows: true, equalcolumns: true, displaystyle: true,
      side: true, minlabelspacing: true, texClass: true, useHeight: 1
    }
  },
  linebreakContainer: true,
  Append: function () {
    for (var i = 0, m = arguments.length; i < m; i++) {
      if (!((arguments[i] instanceof MML.mtr) ||
            (arguments[i] instanceof MML.mlabeledtr))) {arguments[i] = MML.mtr(arguments[i])}
    }
    this.SUPER(arguments).Append.apply(this,arguments);
  },
  setTeXclass: MML.mbase.setSeparateTeXclasses
});

MML.mtr = MML.mbase.Subclass({
  type: "mtr",
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    rowalign: MML.INHERIT,
    columnalign: MML.INHERIT,
    groupalign: MML.INHERIT
  },
  inheritFromMe: true,
  noInherit: {
    mrow: {rowalign: true, columnalign: true, groupalign: true},
    mtable: {rowalign: true, columnalign: true, groupalign: true}
  },
  linebreakContainer: true,
  Append: function () {
    for (var i = 0, m = arguments.length; i < m; i++) {
      if (!(arguments[i] instanceof MML.mtd)) {arguments[i] = MML.mtd(arguments[i])}
    }
    this.SUPER(arguments).Append.apply(this,arguments);
  },
  setTeXclass: MML.mbase.setSeparateTeXclasses
});

MML.mtd = MML.mbase.Subclass({
  type: "mtd",
  inferRow: true,
  linebreakContainer: true,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    rowspan: 1,
    columnspan: 1,
    rowalign: MML.INHERIT,
    columnalign: MML.INHERIT,
    groupalign: MML.INHERIT
  },
  setTeXclass: MML.mbase.setSeparateTeXclasses
});

MML.maligngroup = MML.mbase.Subclass({
  type: "malign",
  isSpacelike: function () {return true},
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    groupalign: MML.INHERIT
  },
  inheritFromMe: true,
  noInherit: {
    mrow: {groupalign: true},
    mtable: {groupalign: true}
  }
});

MML.malignmark = MML.mbase.Subclass({
  type: "malignmark",
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    edge: MML.SIDE.LEFT
  },
  isSpacelike: function () {return true}
});

MML.mlabeledtr = MML.mtr.Subclass({
  type: "mlabeledtr"
});

MML.maction = MML.mbase.Subclass({
  type: "maction",
  defaults: {
    mathbackground: MML.INHERIT,
    mathcolor: MML.INHERIT,
    actiontype: MML.ACTIONTYPE.TOGGLE,
    selection: 1
  },
  selected: function () {return this.data[this.Get("selection")-1] || MML.NULL},
  isEmbellished: function () {return this.selected().isEmbellished()},
  isSpacelike: function () {return this.selected().isSpacelike()},
  Core: function () {return this.selected().Core()},
  CoreMO: function () {return this.selected().CoreMO()},
  setTeXclass: function (prev) {
    if (this.Get("actiontype") === MML.ACTIONTYPE.TOOLTIP && this.data[1]) {
      // Make sure tooltip has proper spacing when typeset (see issue #412)
      this.data[1].setTeXclass();
    }
    var selected = this.selected();
    prev = selected.setTeXclass(prev);
    this.updateTeXclass(selected);
    return prev;
  }
});

MML.semantics = MML.mbase.Subclass({
  type: "semantics", notParent: true,
  isEmbellished: MML.mbase.childEmbellished,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  defaults: {
    definitionURL: null,
    encoding: null
  },
  setTeXclass: MML.mbase.setChildTeXclass,
  getAnnotation: function (name) {
    var encodingList = MathJax.Hub.config.MathMenu.semanticsAnnotations[name];
    if (encodingList) {
      for (var i = 0, m = this.data.length; i < m; i++) {
        var encoding = this.data[i].Get("encoding");
        if (encoding) {
          for (var j = 0, n = encodingList.length; j < n; j++) {
            if (encodingList[j] === encoding) return this.data[i];
          }
        }
      }
    }
    return null;
  }
});
MML.annotation = MML.mbase.Subclass({
  type: "annotation", isChars: true,
  linebreakContainer: true,
  defaults: {
    definitionURL: null,
    encoding: null,
    cd: "mathmlkeys",
    name: "",
    src: null
  }
});
MML["annotation-xml"] = MML.mbase.Subclass({
  type: "annotation-xml",
  linebreakContainer: true,
  defaults: {
    definitionURL: null,
    encoding: null,
    cd: "mathmlkeys",
    name: "",
    src: null
  }
});

MML.math = MML.mstyle.Subclass({
  type: "math",
  defaults: {
    mathvariant: MML.VARIANT.NORMAL,
    mathsize: MML.SIZE.NORMAL,
    mathcolor: "", // should be "black", but allow it to inherit from surrounding text
    mathbackground: MML.COLOR.TRANSPARENT,
    dir: "ltr",
    scriptlevel: 0,
    displaystyle: MML.AUTO,
    display: "inline",
    maxwidth: "",
    overflow: MML.OVERFLOW.LINEBREAK,
    altimg: "",
    'altimg-width': "",
    'altimg-height': "",
    'altimg-valign': "",
    alttext: "",
    cdgroup: "",
    scriptsizemultiplier: Math.sqrt(1/2),
    scriptminsize: "8px",    // should be 8pt, but that's too big
    infixlinebreakstyle: MML.LINEBREAKSTYLE.BEFORE,
    lineleading: "1ex",
    indentshift: "auto",     // use user configuration
    indentalign: MML.INDENTALIGN.AUTO,
    indentalignfirst: MML.INDENTALIGN.INDENTALIGN,
    indentshiftfirst: MML.INDENTSHIFT.INDENTSHIFT,
    indentalignlast:  MML.INDENTALIGN.INDENTALIGN,
    indentshiftlast:  MML.INDENTSHIFT.INDENTSHIFT,
    decimalseparator: ".",
    texprimestyle: false     // is it in TeX's C' style?
  },
  autoDefault: function (name) {
    if (name === "displaystyle") {return this.Get("display") === "block"}
    return "";
  },
  linebreakContainer: true,
  setTeXclass: MML.mbase.setChildTeXclass,
  getAnnotation: function (name) {
    if (this.data.length != 1) return null;
    return this.data[0].getAnnotation(name);
  }
});

MML.chars = MML.mbase.Subclass({
  type: "chars",
  Append: function () {this.data.push.apply(this.data,arguments)},
  value: function () {return this.data.join("")},
  toString: function () {return this.data.join("")}
});

MML.entity = MML.mbase.Subclass({
  type: "entity",
  Append: function () {this.data.push.apply(this.data,arguments)},
  value: function () {
    if (this.data[0].substr(0,2) === "#x") {return parseInt(this.data[0].substr(2),16)}
    else if (this.data[0].substr(0,1) === "#") {return parseInt(this.data[0].substr(1))}
    else {return 0}  // FIXME: look up named entities from table
  },
  toString: function () {
    var n = this.value();
    if (n <= 0xFFFF) {return String.fromCharCode(n)}
    n -= 0x10000;
    return String.fromCharCode((n>>10)+0xD800)
         + String.fromCharCode((n&0x3FF)+0xDC00);
  }
});

MML.xml = MML.mbase.Subclass({
  type: "xml",
  Init: function () {
    this.div = document.createElement("div");
    return this.SUPER(arguments).Init.apply(this,arguments);
  },
  Append: function () {
    for (var i = 0, m = arguments.length; i < m; i++) {
      var node = this.Import(arguments[i]);
      this.data.push(node);
      this.div.appendChild(node);
    }
  },
  Import: function (node) {
    if (document.importNode) {return document.importNode(node,true)}
    //
    //  IE < 9 doesn't have importNode, so fake it.
    //
    var nNode, i, m;
    if (node.nodeType === 1) { // ELEMENT_NODE
      nNode = document.createElement(node.nodeName);
      for (i = 0, m = node.attributes.length; i < m; i++) {
        var attribute = node.attributes[i];
        if (attribute.specified && attribute.nodeValue != null && attribute.nodeValue != '')
          {nNode.setAttribute(attribute.nodeName,attribute.nodeValue)}
        if (attribute.nodeName === "style") {nNode.style.cssText = attribute.nodeValue}
      }
      if (node.className) {nNode.className = node.className}
    } else if (node.nodeType === 3 || node.nodeType === 4) { // TEXT_NODE or CDATA_SECTION_NODE
      nNode = document.createTextNode(node.nodeValue);
    } else if (node.nodeType === 8) { // COMMENT_NODE
      nNode = document.createComment(node.nodeValue);
    } else {
      return document.createTextNode('');
    }
    for (i = 0, m = node.childNodes.length; i < m; i++)
      {nNode.appendChild(this.Import(node.childNodes[i]))}
    return nNode;
  },
  value: function () {return this.div},
  toString: function () {return this.div.innerHTML}
});

MML.TeXAtom = MML.mbase.Subclass({
  type: "texatom",
  inferRow: true, notParent: true,
  texClass: MML.TEXCLASS.ORD,
  Core: MML.mbase.childCore,
  CoreMO: MML.mbase.childCoreMO,
  isEmbellished: MML.mbase.childEmbellished,
  setTeXclass: function (prev) {
    this.data[0].setTeXclass();
    return this.adjustTeXclass(prev);
  },
  adjustTeXclass: MML.mo.prototype.adjustTeXclass
});

MML.NULL = MML.mbase().With({type:"null"});

var TEXCLASS = MML.TEXCLASS;

var MO = {
  ORD:        [0,0,TEXCLASS.ORD],
  ORD11:      [1,1,TEXCLASS.ORD],
  ORD21:      [2,1,TEXCLASS.ORD],
  ORD02:      [0,2,TEXCLASS.ORD],
  ORD55:      [5,5,TEXCLASS.ORD],
  OP:         [1,2,TEXCLASS.OP,{largeop: true, movablelimits: true, symmetric: true}],
  OPFIXED:    [1,2,TEXCLASS.OP,{largeop: true, movablelimits: true}],
  INTEGRAL:   [0,1,TEXCLASS.OP,{largeop: true, symmetric: true}],
  INTEGRAL2:  [1,2,TEXCLASS.OP,{largeop: true, symmetric: true}],
  BIN3:       [3,3,TEXCLASS.BIN],
  BIN4:       [4,4,TEXCLASS.BIN],
  BIN01:      [0,1,TEXCLASS.BIN],
  BIN5:       [5,5,TEXCLASS.BIN],
  TALLBIN:    [4,4,TEXCLASS.BIN,{stretchy: true}],
  BINOP:      [4,4,TEXCLASS.BIN,{largeop: true, movablelimits: true}],
  REL:        [5,5,TEXCLASS.REL],
  REL1:       [1,1,TEXCLASS.REL,{stretchy: true}],
  REL4:       [4,4,TEXCLASS.REL],
  RELSTRETCH: [5,5,TEXCLASS.REL,{stretchy: true}],
  RELACCENT:  [5,5,TEXCLASS.REL,{accent: true}],
  WIDEREL:    [5,5,TEXCLASS.REL,{accent: true, stretchy: true}],
  OPEN:       [0,0,TEXCLASS.OPEN,{fence: true, stretchy: true, symmetric: true}],
  CLOSE:      [0,0,TEXCLASS.CLOSE,{fence: true, stretchy: true, symmetric: true}],
  INNER:      [0,0,TEXCLASS.INNER],
  PUNCT:      [0,3,TEXCLASS.PUNCT],
  ACCENT:     [0,0,TEXCLASS.ORD,{accent: true}],
  WIDEACCENT: [0,0,TEXCLASS.ORD,{accent: true, stretchy: true}]
};

MML.mo.Augment({
  SPACE: [
    '0em',
    '0.1111em',
    '0.1667em',
    '0.2222em',
    '0.2667em',
    '0.3333em'
  ],
  RANGES: [
    [0x20,0x7F,TEXCLASS.REL,"BasicLatin"],
    [0xA0,0xFF,TEXCLASS.ORD,"Latin1Supplement"],
    [0x100,0x17F,TEXCLASS.ORD],
    [0x180,0x24F,TEXCLASS.ORD],
    [0x2B0,0x2FF,TEXCLASS.ORD,"SpacingModLetters"],
    [0x300,0x36F,TEXCLASS.ORD,"CombDiacritMarks"],
    [0x370,0x3FF,TEXCLASS.ORD,"GreekAndCoptic"],
    [0x1E00,0x1EFF,TEXCLASS.ORD],
    [0x2000,0x206F,TEXCLASS.PUNCT,"GeneralPunctuation"],
    [0x2070,0x209F,TEXCLASS.ORD],
    [0x20A0,0x20CF,TEXCLASS.ORD],
    [0x20D0,0x20FF,TEXCLASS.ORD,"CombDiactForSymbols"],
    [0x2100,0x214F,TEXCLASS.ORD,"LetterlikeSymbols"],
    [0x2150,0x218F,TEXCLASS.ORD],
    [0x2190,0x21FF,TEXCLASS.REL,"Arrows"],
    [0x2200,0x22FF,TEXCLASS.BIN,"MathOperators"],
    [0x2300,0x23FF,TEXCLASS.ORD,"MiscTechnical"],
    [0x2460,0x24FF,TEXCLASS.ORD],
    [0x2500,0x259F,TEXCLASS.ORD],
    [0x25A0,0x25FF,TEXCLASS.ORD,"GeometricShapes"],
    [0x2700,0x27BF,TEXCLASS.ORD,"Dingbats"],
    [0x27C0,0x27EF,TEXCLASS.ORD,"MiscMathSymbolsA"],
    [0x27F0,0x27FF,TEXCLASS.REL,"SupplementalArrowsA"],
    [0x2900,0x297F,TEXCLASS.REL,"SupplementalArrowsB"],
    [0x2980,0x29FF,TEXCLASS.ORD,"MiscMathSymbolsB"],
    [0x2A00,0x2AFF,TEXCLASS.BIN,"SuppMathOperators"],
    [0x2B00,0x2BFF,TEXCLASS.ORD,"MiscSymbolsAndArrows"],
    [0x1D400,0x1D7FF,TEXCLASS.ORD]
  ],
  OPTABLE: {
    prefix: {
      '\u2200': MO.ORD21,    // for all
      '\u2202': MO.ORD21,    // partial differential
      '\u2203': MO.ORD21,    // there exists
      '\u2207': MO.ORD21,    // nabla
      '\u220F': MO.OP,       // n-ary product
      '\u2210': MO.OP,       // n-ary coproduct
      '\u2211': MO.OP,       // n-ary summation
      '\u2212': MO.BIN01,    // minus sign
      '\u2213': MO.BIN01,    // minus-or-plus sign
      '\u221A': [1,1,TEXCLASS.ORD,{stretchy: true}], // square root
      '\u2220': MO.ORD,      // angle
      '\u222B': MO.INTEGRAL, // integral
      '\u222E': MO.INTEGRAL, // contour integral
      '\u22C0': MO.OP,       // n-ary logical and
      '\u22C1': MO.OP,       // n-ary logical or
      '\u22C2': MO.OP,       // n-ary intersection
      '\u22C3': MO.OP,       // n-ary union
      '\u2308': MO.OPEN,     // left ceiling
      '\u230A': MO.OPEN,     // left floor
      '\u27E8': MO.OPEN,     // mathematical left angle bracket
      '\u27EE': MO.OPEN,     // mathematical left flattened parenthesis
      '\u2A00': MO.OP,       // n-ary circled dot operator
      '\u2A01': MO.OP,       // n-ary circled plus operator
      '\u2A02': MO.OP,       // n-ary circled times operator
      '\u2A04': MO.OP,       // n-ary union operator with plus
      '\u2A06': MO.OP,       // n-ary square union operator
      '\u00AC': MO.ORD21,    // not sign
      '\u00B1': MO.BIN01,    // plus-minus sign
      '(': MO.OPEN,          // left parenthesis
      '+': MO.BIN01,         // plus sign
      '-': MO.BIN01,         // hyphen-minus
      '[': MO.OPEN,          // left square bracket
      '{': MO.OPEN,          // left curly bracket
      '|': MO.OPEN           // vertical line
    },
    postfix: {
      '!': [1,0,TEXCLASS.CLOSE], // exclamation mark
      '&': MO.ORD,           // ampersand
      '\u2032': MO.ORD02,    // prime
      '\u203E': MO.WIDEACCENT, // overline
      '\u2309': MO.CLOSE,    // right ceiling
      '\u230B': MO.CLOSE,    // right floor
      '\u23DE': MO.WIDEACCENT, // top curly bracket
      '\u23DF': MO.WIDEACCENT, // bottom curly bracket
      '\u266D': MO.ORD02,    // music flat sign
      '\u266E': MO.ORD02,    // music natural sign
      '\u266F': MO.ORD02,    // music sharp sign
      '\u27E9': MO.CLOSE,    // mathematical right angle bracket
      '\u27EF': MO.CLOSE,    // mathematical right flattened parenthesis
      '\u02C6': MO.WIDEACCENT, // modifier letter circumflex accent
      '\u02C7': MO.WIDEACCENT, // caron
      '\u02C9': MO.WIDEACCENT, // modifier letter macron
      '\u02CA': MO.ACCENT,   // modifier letter acute accent
      '\u02CB': MO.ACCENT,   // modifier letter grave accent
      '\u02D8': MO.ACCENT,   // breve
      '\u02D9': MO.ACCENT,   // dot above
      '\u02DC': MO.WIDEACCENT, // small tilde
      '\u0302': MO.WIDEACCENT, // combining circumflex accent
      '\u00A8': MO.ACCENT,   // diaeresis
      '\u00AF': MO.WIDEACCENT, // macron
      ')': MO.CLOSE,         // right parenthesis
      ']': MO.CLOSE,         // right square bracket
      '^': MO.WIDEACCENT,    // circumflex accent
      '_': MO.WIDEACCENT,    // low line
      '`': MO.ACCENT,        // grave accent
      '|': MO.CLOSE,         // vertical line
      '}': MO.CLOSE,         // right curly bracket
      '~': MO.WIDEACCENT     // tilde
    },
    infix: {
      '': MO.ORD,            // empty <mo>
      '%': [3,3,TEXCLASS.ORD], // percent sign
      '\u2022': MO.BIN4,     // bullet
      '\u2026': MO.INNER,    // horizontal ellipsis
      '\u2044': MO.TALLBIN,  // fraction slash
      '\u2061': MO.ORD,      // function application
      '\u2062': MO.ORD,      // invisible times
      '\u2063': [0,0,TEXCLASS.ORD,{linebreakstyle:"after", separator: true}], // invisible separator
      '\u2064': MO.ORD,      // invisible plus
      '\u2190': MO.WIDEREL,  // leftwards arrow
      '\u2191': MO.RELSTRETCH, // upwards arrow
      '\u2192': MO.WIDEREL,  // rightwards arrow
      '\u2193': MO.RELSTRETCH, // downwards arrow
      '\u2194': MO.WIDEREL,  // left right arrow
      '\u2195': MO.RELSTRETCH, // up down arrow
      '\u2196': MO.RELSTRETCH, // north west arrow
      '\u2197': MO.RELSTRETCH, // north east arrow
      '\u2198': MO.RELSTRETCH, // south east arrow
      '\u2199': MO.RELSTRETCH, // south west arrow
      '\u21A6': MO.WIDEREL,  // rightwards arrow from bar
      '\u21A9': MO.WIDEREL,  // leftwards arrow with hook
      '\u21AA': MO.WIDEREL,  // rightwards arrow with hook
      '\u21BC': MO.WIDEREL,  // leftwards harpoon with barb upwards
      '\u21BD': MO.WIDEREL,  // leftwards harpoon with barb downwards
      '\u21C0': MO.WIDEREL,  // rightwards harpoon with barb upwards
      '\u21C1': MO.WIDEREL,  // rightwards harpoon with barb downwards
      '\u21CC': MO.WIDEREL,  // rightwards harpoon over leftwards harpoon
      '\u21D0': MO.WIDEREL,  // leftwards double arrow
      '\u21D1': MO.RELSTRETCH, // upwards double arrow
      '\u21D2': MO.WIDEREL,  // rightwards double arrow
      '\u21D3': MO.RELSTRETCH, // downwards double arrow
      '\u21D4': MO.WIDEREL,  // left right double arrow
      '\u21D5': MO.RELSTRETCH, // up down double arrow
      '\u2208': MO.REL,      // element of
      '\u2209': MO.REL,      // not an element of
      '\u220B': MO.REL,      // contains as member
      '\u2212': MO.BIN4,     // minus sign
      '\u2213': MO.BIN4,     // minus-or-plus sign
      '\u2215': MO.TALLBIN,  // division slash
      '\u2216': MO.BIN4,     // set minus
      '\u2217': MO.BIN4,     // asterisk operator
      '\u2218': MO.BIN4,     // ring operator
      '\u2219': MO.BIN4,     // bullet operator
      '\u221D': MO.REL,      // proportional to
      '\u2223': MO.REL,      // divides
      '\u2225': MO.REL,      // parallel to
      '\u2227': MO.BIN4,     // logical and
      '\u2228': MO.BIN4,     // logical or
      '\u2229': MO.BIN4,     // intersection
      '\u222A': MO.BIN4,     // union
      '\u223C': MO.REL,      // tilde operator
      '\u2240': MO.BIN4,     // wreath product
      '\u2243': MO.REL,      // asymptotically equal to
      '\u2245': MO.REL,      // approximately equal to
      '\u2248': MO.REL,      // almost equal to
      '\u224D': MO.REL,      // equivalent to
      '\u2250': MO.REL,      // approaches the limit
      '\u2260': MO.REL,      // not equal to
      '\u2261': MO.REL,      // identical to
      '\u2264': MO.REL,      // less-than or equal to
      '\u2265': MO.REL,      // greater-than or equal to
      '\u226A': MO.REL,      // much less-than
      '\u226B': MO.REL,      // much greater-than
      '\u227A': MO.REL,      // precedes
      '\u227B': MO.REL,      // succeeds
      '\u2282': MO.REL,      // subset of
      '\u2283': MO.REL,      // superset of
      '\u2286': MO.REL,      // subset of or equal to
      '\u2287': MO.REL,      // superset of or equal to
      '\u228E': MO.BIN4,     // multiset union
      '\u2291': MO.REL,      // square image of or equal to
      '\u2292': MO.REL,      // square original of or equal to
      '\u2293': MO.BIN4,     // square cap
      '\u2294': MO.BIN4,     // square cup
      '\u2295': MO.BIN4,     // circled plus
      '\u2296': MO.BIN4,     // circled minus
      '\u2297': MO.BIN4,     // circled times
      '\u2298': MO.BIN4,     // circled division slash
      '\u2299': MO.BIN4,     // circled dot operator
      '\u22A2': MO.REL,      // right tack
      '\u22A3': MO.REL,      // left tack
      '\u22A4': MO.ORD55,    // down tack
      '\u22A5': MO.REL,      // up tack
      '\u22A8': MO.REL,      // true
      '\u22C4': MO.BIN4,     // diamond operator
      '\u22C5': MO.BIN4,     // dot operator
      '\u22C6': MO.BIN4,     // star operator
      '\u22C8': MO.REL,      // bowtie
      '\u22EE': MO.ORD55,    // vertical ellipsis
      '\u22EF': MO.INNER,    // midline horizontal ellipsis
      '\u22F1': [5,5,TEXCLASS.INNER], // down right diagonal ellipsis
      '\u25B3': MO.BIN4,     // white up-pointing triangle
      '\u25B5': MO.BIN4,     // white up-pointing small triangle
      '\u25B9': MO.BIN4,     // white right-pointing small triangle
      '\u25BD': MO.BIN4,     // white down-pointing triangle
      '\u25BF': MO.BIN4,     // white down-pointing small triangle
      '\u25C3': MO.BIN4,     // white left-pointing small triangle
      '\u2758': MO.REL,      // light vertical bar
      '\u27F5': MO.WIDEREL,  // long leftwards arrow
      '\u27F6': MO.WIDEREL,  // long rightwards arrow
      '\u27F7': MO.WIDEREL,  // long left right arrow
      '\u27F8': MO.WIDEREL,  // long leftwards double arrow
      '\u27F9': MO.WIDEREL,  // long rightwards double arrow
      '\u27FA': MO.WIDEREL,  // long left right double arrow
      '\u27FC': MO.WIDEREL,  // long rightwards arrow from bar
      '\u2A2F': MO.BIN4,     // vector or cross product
      '\u2A3F': MO.BIN4,     // amalgamation or coproduct
      '\u2AAF': MO.REL,      // precedes above single-line equals sign
      '\u2AB0': MO.REL,      // succeeds above single-line equals sign
      '\u00B1': MO.BIN4,     // plus-minus sign
      '\u00B7': MO.BIN4,     // middle dot
      '\u00D7': MO.BIN4,     // multiplication sign
      '\u00F7': MO.BIN4,     // division sign
      '*': MO.BIN3,          // asterisk
      '+': MO.BIN4,          // plus sign
      ',': [0,3,TEXCLASS.PUNCT,{linebreakstyle:"after", separator: true}], // comma
      '-': MO.BIN4,          // hyphen-minus
      '.': [3,3,TEXCLASS.ORD], // full stop
      '/': MO.ORD11,         // solidus
      ':': [1,2,TEXCLASS.REL], // colon
      ';': [0,3,TEXCLASS.PUNCT,{linebreakstyle:"after", separator: true}], // semicolon
      '<': MO.REL,           // less-than sign
      '=': MO.REL,           // equals sign
      '>': MO.REL,           // greater-than sign
      '?': [1,1,TEXCLASS.CLOSE], // question mark
      '\\': MO.ORD,          // reverse solidus
      '^': MO.ORD11,         // circumflex accent
      '_': MO.ORD11,         // low line
      '|': [2,2,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}], // vertical line
      '#': MO.ORD,           // #
      '$': MO.ORD,           // $
      '\u002E': [0,3,TEXCLASS.PUNCT,{separator: true}], // \ldotp
      '\u02B9': MO.ORD,      // prime
      '\u0300': MO.ACCENT,   // \grave
      '\u0301': MO.ACCENT,   // \acute
      '\u0303': MO.WIDEACCENT, // \tilde
      '\u0304': MO.ACCENT,   // \bar
      '\u0306': MO.ACCENT,   // \breve
      '\u0307': MO.ACCENT,   // \dot
      '\u0308': MO.ACCENT,   // \ddot
      '\u030C': MO.ACCENT,   // \check
      '\u0332': MO.WIDEACCENT, // horizontal line
      '\u0338': MO.REL4,     // \not
      '\u2015': [0,0,TEXCLASS.ORD,{stretchy: true}], // horizontal line
      '\u2017': [0,0,TEXCLASS.ORD,{stretchy: true}], // horizontal line
      '\u2020': MO.BIN3,     // \dagger
      '\u2021': MO.BIN3,     // \ddagger
      '\u20D7': MO.ACCENT,   // \vec
      '\u2111': MO.ORD,      // \Im
      '\u2113': MO.ORD,      // \ell
      '\u2118': MO.ORD,      // \wp
      '\u211C': MO.ORD,      // \Re
      '\u2205': MO.ORD,      // \emptyset
      '\u221E': MO.ORD,      // \infty
      '\u2305': MO.BIN3,     // barwedge
      '\u2306': MO.BIN3,     // doublebarwedge
      '\u2322': MO.REL4,     // \frown
      '\u2323': MO.REL4,     // \smile
      '\u2329': MO.OPEN,     // langle
      '\u232A': MO.CLOSE,    // rangle
      '\u23AA': MO.ORD,      // \bracevert
      '\u23AF': [0,0,TEXCLASS.ORD,{stretchy: true}], // \underline
      '\u23B0': MO.OPEN,     // \lmoustache
      '\u23B1': MO.CLOSE,    // \rmoustache
      '\u2500': MO.ORD,      // horizontal line
      '\u25EF': MO.BIN3,     // \bigcirc
      '\u2660': MO.ORD,      // \spadesuit
      '\u2661': MO.ORD,      // \heartsuit
      '\u2662': MO.ORD,      // \diamondsuit
      '\u2663': MO.ORD,      // \clubsuit
      '\u3008': MO.OPEN,     // langle
      '\u3009': MO.CLOSE,    // rangle
      '\uFE37': MO.WIDEACCENT, // horizontal brace down
      '\uFE38': MO.WIDEACCENT  // horizontal brace up
    }
  }
},{
  OPTYPES: MO
});

//
//  These are not in the W3C table, but FF works this way,
//  and it makes sense, so add it here
//
var OPTABLE = MML.mo.prototype.OPTABLE;
OPTABLE.infix["^"] = MO.WIDEREL;
OPTABLE.infix["_"] = MO.WIDEREL;
OPTABLE.prefix["\u2223"] = MO.OPEN;
OPTABLE.prefix["\u2225"] = MO.OPEN;
OPTABLE.postfix["\u2223"] = MO.CLOSE;
OPTABLE.postfix["\u2225"] = MO.CLOSE;

})(MathJax.ElementJax.mml);

MathJax.ElementJax.mml.loadComplete(“jax.js”);