shithub: mycel

ref: 52fda39dd18e9e6f1397c477980cf20fcf8e172f
dir: /domino-lib/HTMLParser.js/

View raw version
"use strict";
module.exports = HTMLParser;

var Document = require('./Document');
var DocumentType = require('./DocumentType');
var Node = require('./Node');
var NAMESPACE = require('./utils').NAMESPACE;
var html = require('./htmlelts');
var impl = html.elements;

var pushAll = Function.prototype.apply.bind(Array.prototype.push);

/*
 * This file contains an implementation of the HTML parsing algorithm.
 * The algorithm and the implementation are complex because HTML
 * explicitly defines how the parser should behave for all possible
 * valid and invalid inputs.
 *
 * Usage:
 *
 * The file defines a single HTMLParser() function, which dom.js exposes
 * publicly as document.implementation.mozHTMLParser(). This is a
 * factory function, not a constructor.
 *
 * When you call document.implementation.mozHTMLParser(), it returns
 * an object that has parse() and document() methods. To parse HTML text,
 * pass the text (in one or more chunks) to the parse() method.  When
 * you've passed all the text (on the last chunk, or afterward) pass
 * true as the second argument to parse() to tell the parser that there
 * is no more coming. Call document() to get the document object that
 * the parser is parsing into.  You can call this at any time, before
 * or after calling parse().
 *
 * The first argument to mozHTMLParser is the absolute URL of the document.
 *
 * The second argument is optional and is for internal use only.  Pass an
 * element as the fragmentContext to do innerHTML parsing for the
 * element.  To do innerHTML parsing on a document, pass null. Otherwise,
 * omit the 2nd argument. See HTMLElement.innerHTML for an example.  Note
 * that if you pass a context element, the end() method will return an
 * unwrapped document instead of a wrapped one.
 *
 * Implementation details:
 *
 * This is a long file of almost 7000 lines. It is structured as one
 * big function nested within another big function.  The outer
 * function defines a bunch of constant data, utility functions
 * that use that data, and a couple of classes used by the parser.
 * The outer function also defines and returns the
 * inner function. This inner function is the HTMLParser factory
 * function that implements the parser and holds all the parser state
 * as local variables.  The HTMLParser function is quite big because
 * it defines many nested functions that use those local variables.
 *
 * There are three tightly coupled parser stages: a scanner, a
 * tokenizer and a tree builder. In a (possibly misguided) attempt at
 * efficiency, the stages are not implemented as separate classes:
 * everything shares state and is (mostly) implemented in imperative
 * (rather than OO) style.
 *
 * The stages of the parser work like this: When the client code calls
 * the parser's parse() method, the specified string is passed to
 * scanChars(). The scanner loops through that string and passes characters
 * (sometimes one at a time, sometimes in chunks) to the tokenizer stage.
 * The tokenizer groups the characters into tokens: tags, endtags, runs
 * of text, comments, doctype declarations, and the end-of-file (EOF)
 * token.  These tokens are then passed to the tree building stage via
 * the insertToken() function.  The tree building stage builds up the
 * document tree.
 *
 * The tokenizer stage is a finite state machine.  Each state is
 * implemented as a function with a name that ends in "_state".  The
 * initial state is data_state(). The current tokenizer state is stored
 * in the variable 'tokenizer'.  Most state functions expect a single
 * integer argument which represents a single UTF-16 codepoint.  Some
 * states want more characters and set a lookahead property on
 * themselves.  The scanChars() function in the scanner checks for this
 * lookahead property.  If it doesn't exist, then scanChars() just passes
 * the next input character to the current tokenizer state function.
 * Otherwise, scanChars() looks ahead (a given # of characters, or for a
 * matching string, or for a matching regexp) and passes a string of
 * characters to the current tokenizer state function.
 *
 * As a shortcut, certain states of the tokenizer use regular expressions
 * to look ahead in the scanner's input buffer for runs of text, simple
 * tags and attributes.  For well-formed input, these shortcuts skip a
 * lot of state transitions and speed things up a bit.
 *
 * When a tokenizer state function has consumed a complete token, it
 * emits that token, by calling insertToken(), or by calling a utility
 * function that itself calls insertToken().  These tokens are passed to
 * the tree building stage, which is also a state machine.  Like the
 * tokenizer, the tree building states are implemented as functions, and
 * these functions have names that end with _mode (because the HTML spec
 * refers to them as insertion modes). The current insertion mode is held
 * by the 'parser' variable.  Each insertion mode function takes up to 4
 * arguments.  The first is a token type, represented by the constants
 * TAG, ENDTAG, TEXT, COMMENT, DOCTYPE and EOF.  The second argument is
 * the value of the token: the text or comment data, or tagname or
 * doctype.  For tags, the 3rd argument is an array of attributes.  For
 * DOCTYPES it is the optional public id.  For tags, the 4th argument is
 * true if the tag is self-closing. For doctypes, the 4th argument is the
 * optional system id.
 *
 * Search for "***" to find the major sub-divisions in the code.
 */


/***
 * Data prolog.  Lots of constants declared here, including some
 * very large objects.  They're used throughout the code that follows
 */
// Token types for the tree builder.
var EOF = -1;
var TEXT = 1;
var TAG = 2;
var ENDTAG = 3;
var COMMENT = 4;
var DOCTYPE = 5;

// A re-usable empty array
var NOATTRS = [];

// These DTD public ids put the browser in quirks mode
var quirkyPublicIds = /^HTML$|^-\/\/W3O\/\/DTD W3 HTML Strict 3\.0\/\/EN\/\/$|^-\/W3C\/DTD HTML 4\.0 Transitional\/EN$|^\+\/\/Silmaril\/\/dtd html Pro v0r11 19970101\/\/|^-\/\/AdvaSoft Ltd\/\/DTD HTML 3\.0 asWedit \+ extensions\/\/|^-\/\/AS\/\/DTD HTML 3\.0 asWedit \+ extensions\/\/|^-\/\/IETF\/\/DTD HTML 2\.0 Level 1\/\/|^-\/\/IETF\/\/DTD HTML 2\.0 Level 2\/\/|^-\/\/IETF\/\/DTD HTML 2\.0 Strict Level 1\/\/|^-\/\/IETF\/\/DTD HTML 2\.0 Strict Level 2\/\/|^-\/\/IETF\/\/DTD HTML 2\.0 Strict\/\/|^-\/\/IETF\/\/DTD HTML 2\.0\/\/|^-\/\/IETF\/\/DTD HTML 2\.1E\/\/|^-\/\/IETF\/\/DTD HTML 3\.0\/\/|^-\/\/IETF\/\/DTD HTML 3\.2 Final\/\/|^-\/\/IETF\/\/DTD HTML 3\.2\/\/|^-\/\/IETF\/\/DTD HTML 3\/\/|^-\/\/IETF\/\/DTD HTML Level 0\/\/|^-\/\/IETF\/\/DTD HTML Level 1\/\/|^-\/\/IETF\/\/DTD HTML Level 2\/\/|^-\/\/IETF\/\/DTD HTML Level 3\/\/|^-\/\/IETF\/\/DTD HTML Strict Level 0\/\/|^-\/\/IETF\/\/DTD HTML Strict Level 1\/\/|^-\/\/IETF\/\/DTD HTML Strict Level 2\/\/|^-\/\/IETF\/\/DTD HTML Strict Level 3\/\/|^-\/\/IETF\/\/DTD HTML Strict\/\/|^-\/\/IETF\/\/DTD HTML\/\/|^-\/\/Metrius\/\/DTD Metrius Presentational\/\/|^-\/\/Microsoft\/\/DTD Internet Explorer 2\.0 HTML Strict\/\/|^-\/\/Microsoft\/\/DTD Internet Explorer 2\.0 HTML\/\/|^-\/\/Microsoft\/\/DTD Internet Explorer 2\.0 Tables\/\/|^-\/\/Microsoft\/\/DTD Internet Explorer 3\.0 HTML Strict\/\/|^-\/\/Microsoft\/\/DTD Internet Explorer 3\.0 HTML\/\/|^-\/\/Microsoft\/\/DTD Internet Explorer 3\.0 Tables\/\/|^-\/\/Netscape Comm\. Corp\.\/\/DTD HTML\/\/|^-\/\/Netscape Comm\. Corp\.\/\/DTD Strict HTML\/\/|^-\/\/O'Reilly and Associates\/\/DTD HTML 2\.0\/\/|^-\/\/O'Reilly and Associates\/\/DTD HTML Extended 1\.0\/\/|^-\/\/O'Reilly and Associates\/\/DTD HTML Extended Relaxed 1\.0\/\/|^-\/\/SoftQuad Software\/\/DTD HoTMetaL PRO 6\.0::19990601::extensions to HTML 4\.0\/\/|^-\/\/SoftQuad\/\/DTD HoTMetaL PRO 4\.0::19971010::extensions to HTML 4\.0\/\/|^-\/\/Spyglass\/\/DTD HTML 2\.0 Extended\/\/|^-\/\/SQ\/\/DTD HTML 2\.0 HoTMetaL \+ extensions\/\/|^-\/\/Sun Microsystems Corp\.\/\/DTD HotJava HTML\/\/|^-\/\/Sun Microsystems Corp\.\/\/DTD HotJava Strict HTML\/\/|^-\/\/W3C\/\/DTD HTML 3 1995-03-24\/\/|^-\/\/W3C\/\/DTD HTML 3\.2 Draft\/\/|^-\/\/W3C\/\/DTD HTML 3\.2 Final\/\/|^-\/\/W3C\/\/DTD HTML 3\.2\/\/|^-\/\/W3C\/\/DTD HTML 3\.2S Draft\/\/|^-\/\/W3C\/\/DTD HTML 4\.0 Frameset\/\/|^-\/\/W3C\/\/DTD HTML 4\.0 Transitional\/\/|^-\/\/W3C\/\/DTD HTML Experimental 19960712\/\/|^-\/\/W3C\/\/DTD HTML Experimental 970421\/\/|^-\/\/W3C\/\/DTD W3 HTML\/\/|^-\/\/W3O\/\/DTD W3 HTML 3\.0\/\/|^-\/\/WebTechs\/\/DTD Mozilla HTML 2\.0\/\/|^-\/\/WebTechs\/\/DTD Mozilla HTML\/\//i;

var quirkySystemId = "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd";

var conditionallyQuirkyPublicIds = /^-\/\/W3C\/\/DTD HTML 4\.01 Frameset\/\/|^-\/\/W3C\/\/DTD HTML 4\.01 Transitional\/\//i;

// These DTD public ids put the browser in limited quirks mode
var limitedQuirkyPublicIds = /^-\/\/W3C\/\/DTD XHTML 1\.0 Frameset\/\/|^-\/\/W3C\/\/DTD XHTML 1\.0 Transitional\/\//i;


// Element sets below. See the isA() function for a way to test
// whether an element is a member of a set
var specialSet = Object.create(null);
specialSet[NAMESPACE.HTML] = {
  __proto__: null,
  "address":true, "applet":true, "area":true, "article":true,
  "aside":true, "base":true, "basefont":true, "bgsound":true,
  "blockquote":true, "body":true, "br":true, "button":true,
  "caption":true, "center":true, "col":true, "colgroup":true,
  "dd":true, "details":true, "dir":true,
  "div":true, "dl":true, "dt":true, "embed":true,
  "fieldset":true, "figcaption":true, "figure":true, "footer":true,
  "form":true, "frame":true, "frameset":true, "h1":true,
  "h2":true, "h3":true, "h4":true, "h5":true,
  "h6":true, "head":true, "header":true, "hgroup":true,
  "hr":true, "html":true, "iframe":true, "img":true,
  "input":true, "li":true, "link":true,
  "listing":true, "main":true, "marquee":true, "menu":true, "meta":true,
  "nav":true, "noembed":true, "noframes":true, "noscript":true,
  "object":true, "ol":true, "p":true, "param":true,
  "plaintext":true, "pre":true, "script":true, "section":true,
  "select":true, "source":true, "style":true, "summary":true, "table":true,
  "tbody":true, "td":true, "template":true, "textarea":true, "tfoot":true,
  "th":true, "thead":true, "title":true, "tr":true, "track":true,
  // Note that "xmp" was removed from the "special" set in the latest
  // spec, apparently by accident; see
  // https://github.com/whatwg/html/pull/1919
  "ul":true, "wbr":true, "xmp":true
};
specialSet[NAMESPACE.SVG] = {
  __proto__: null,
  "foreignObject": true, "desc": true, "title": true
};
specialSet[NAMESPACE.MATHML] = {
  __proto__: null,
  "mi":true, "mo":true, "mn":true, "ms":true,
  "mtext":true, "annotation-xml":true
};

// The set of address, div, and p HTML tags
var addressdivpSet = Object.create(null);
addressdivpSet[NAMESPACE.HTML] = {
  __proto__: null,
  "address":true, "div":true, "p":true
};

var dddtSet = Object.create(null);
dddtSet[NAMESPACE.HTML] = {
  __proto__: null,
  "dd":true, "dt":true
};

var tablesectionrowSet = Object.create(null);
tablesectionrowSet[NAMESPACE.HTML] = {
  __proto__: null,
  "table":true, "thead":true, "tbody":true, "tfoot":true, "tr":true
};

var impliedEndTagsSet = Object.create(null);
impliedEndTagsSet[NAMESPACE.HTML] = {
  __proto__: null,
  "dd": true, "dt": true, "li": true, "menuitem": true, "optgroup": true,
  "option": true, "p": true, "rb": true, "rp": true, "rt": true, "rtc": true
};

var thoroughImpliedEndTagsSet = Object.create(null);
thoroughImpliedEndTagsSet[NAMESPACE.HTML] = {
  __proto__: null,
  "caption": true, "colgroup": true, "dd": true, "dt": true, "li": true,
  "optgroup": true, "option": true, "p": true, "rb": true, "rp": true,
  "rt": true, "rtc": true, "tbody": true, "td": true, "tfoot": true,
  "th": true, "thead": true, "tr": true
};

var tableContextSet = Object.create(null);
tableContextSet[NAMESPACE.HTML] = {
  __proto__: null,
  "table": true, "template": true, "html": true
};

var tableBodyContextSet = Object.create(null);
tableBodyContextSet[NAMESPACE.HTML] = {
  __proto__: null,
  "tbody": true, "tfoot": true, "thead": true, "template": true, "html": true
};

var tableRowContextSet = Object.create(null);
tableRowContextSet[NAMESPACE.HTML] = {
  __proto__: null,
  "tr": true, "template": true, "html": true
};

// See http://www.w3.org/TR/html5/forms.html#form-associated-element
var formassociatedSet = Object.create(null);
formassociatedSet[NAMESPACE.HTML] = {
  __proto__: null,
  "button": true, "fieldset": true, "input": true, "keygen": true,
  "object": true, "output": true, "select": true, "textarea": true,
  "img": true
};

var inScopeSet = Object.create(null);
inScopeSet[NAMESPACE.HTML]= {
  __proto__: null,
  "applet":true, "caption":true, "html":true, "table":true,
  "td":true, "th":true, "marquee":true, "object":true,
  "template":true
};
inScopeSet[NAMESPACE.MATHML] = {
  __proto__: null,
  "mi":true, "mo":true, "mn":true, "ms":true,
  "mtext":true, "annotation-xml":true
};
inScopeSet[NAMESPACE.SVG] = {
  __proto__: null,
  "foreignObject":true, "desc":true, "title":true
};

var inListItemScopeSet = Object.create(inScopeSet);
inListItemScopeSet[NAMESPACE.HTML] =
  Object.create(inScopeSet[NAMESPACE.HTML]);
inListItemScopeSet[NAMESPACE.HTML].ol = true;
inListItemScopeSet[NAMESPACE.HTML].ul = true;

var inButtonScopeSet = Object.create(inScopeSet);
inButtonScopeSet[NAMESPACE.HTML] =
  Object.create(inScopeSet[NAMESPACE.HTML]);
inButtonScopeSet[NAMESPACE.HTML].button = true;

var inTableScopeSet = Object.create(null);
inTableScopeSet[NAMESPACE.HTML] = {
  __proto__: null,
  "html":true, "table":true, "template":true
};

// The set of elements for select scope is the everything *except* these
var invertedSelectScopeSet = Object.create(null);
invertedSelectScopeSet[NAMESPACE.HTML] = {
  __proto__: null,
  "optgroup":true, "option":true
};

var mathmlTextIntegrationPointSet = Object.create(null);
mathmlTextIntegrationPointSet[NAMESPACE.MATHML] = {
  __proto__: null,
  mi: true,
  mo: true,
  mn: true,
  ms: true,
  mtext: true
};

var htmlIntegrationPointSet = Object.create(null);
htmlIntegrationPointSet[NAMESPACE.SVG] = {
  __proto__: null,
  foreignObject: true,
  desc: true,
  title: true
};

var foreignAttributes = {
  __proto__: null,
  "xlink:actuate": NAMESPACE.XLINK, "xlink:arcrole": NAMESPACE.XLINK,
  "xlink:href":   NAMESPACE.XLINK,  "xlink:role":    NAMESPACE.XLINK,
  "xlink:show":   NAMESPACE.XLINK,  "xlink:title":   NAMESPACE.XLINK,
  "xlink:type":   NAMESPACE.XLINK,  "xml:base":      NAMESPACE.XML,
  "xml:lang":     NAMESPACE.XML,    "xml:space":     NAMESPACE.XML,
  "xmlns":        NAMESPACE.XMLNS,  "xmlns:xlink":   NAMESPACE.XMLNS
};


// Lowercase to mixed case mapping for SVG attributes and tagnames
var svgAttrAdjustments = {
  __proto__: null,
  attributename: "attributeName", attributetype: "attributeType",
  basefrequency: "baseFrequency", baseprofile: "baseProfile",
  calcmode: "calcMode", clippathunits: "clipPathUnits",
  diffuseconstant: "diffuseConstant",
  edgemode: "edgeMode",
  filterunits: "filterUnits",
  glyphref: "glyphRef", gradienttransform: "gradientTransform",
  gradientunits: "gradientUnits", kernelmatrix: "kernelMatrix",
  kernelunitlength: "kernelUnitLength", keypoints: "keyPoints",
  keysplines: "keySplines", keytimes: "keyTimes",
  lengthadjust: "lengthAdjust", limitingconeangle: "limitingConeAngle",
  markerheight: "markerHeight", markerunits: "markerUnits",
  markerwidth: "markerWidth", maskcontentunits: "maskContentUnits",
  maskunits: "maskUnits", numoctaves: "numOctaves",
  pathlength: "pathLength", patterncontentunits: "patternContentUnits",
  patterntransform: "patternTransform", patternunits: "patternUnits",
  pointsatx: "pointsAtX", pointsaty: "pointsAtY",
  pointsatz: "pointsAtZ", preservealpha: "preserveAlpha",
  preserveaspectratio: "preserveAspectRatio",
  primitiveunits: "primitiveUnits", refx: "refX",
  refy: "refY", repeatcount: "repeatCount",
  repeatdur: "repeatDur", requiredextensions: "requiredExtensions",
  requiredfeatures: "requiredFeatures",
  specularconstant: "specularConstant",
  specularexponent: "specularExponent", spreadmethod: "spreadMethod",
  startoffset: "startOffset", stddeviation: "stdDeviation",
  stitchtiles: "stitchTiles", surfacescale: "surfaceScale",
  systemlanguage: "systemLanguage", tablevalues: "tableValues",
  targetx: "targetX", targety: "targetY",
  textlength: "textLength", viewbox: "viewBox",
  viewtarget: "viewTarget", xchannelselector: "xChannelSelector",
  ychannelselector: "yChannelSelector", zoomandpan: "zoomAndPan"
};

var svgTagNameAdjustments = {
  __proto__: null,
  altglyph: "altGlyph", altglyphdef: "altGlyphDef",
  altglyphitem: "altGlyphItem", animatecolor: "animateColor",
  animatemotion: "animateMotion", animatetransform: "animateTransform",
  clippath: "clipPath", feblend: "feBlend",
  fecolormatrix: "feColorMatrix",
  fecomponenttransfer: "feComponentTransfer", fecomposite: "feComposite",
  feconvolvematrix: "feConvolveMatrix",
  fediffuselighting: "feDiffuseLighting",
  fedisplacementmap: "feDisplacementMap",
  fedistantlight: "feDistantLight", feflood: "feFlood",
  fefunca: "feFuncA", fefuncb: "feFuncB",
  fefuncg: "feFuncG", fefuncr: "feFuncR",
  fegaussianblur: "feGaussianBlur", feimage: "feImage",
  femerge: "feMerge", femergenode: "feMergeNode",
  femorphology: "feMorphology", feoffset: "feOffset",
  fepointlight: "fePointLight", fespecularlighting: "feSpecularLighting",
  fespotlight: "feSpotLight", fetile: "feTile",
  feturbulence: "feTurbulence", foreignobject: "foreignObject",
  glyphref: "glyphRef", lineargradient: "linearGradient",
  radialgradient: "radialGradient", textpath: "textPath"
};


// Data for parsing numeric and named character references
// These next 3 objects are direct translations of tables
// in the HTML spec into JavaScript object format
var numericCharRefReplacements = {
  __proto__: null,
  0x00:0xFFFD, 0x80:0x20AC, 0x82:0x201A, 0x83:0x0192, 0x84:0x201E,
  0x85:0x2026, 0x86:0x2020, 0x87:0x2021, 0x88:0x02C6, 0x89:0x2030,
  0x8A:0x0160, 0x8B:0x2039, 0x8C:0x0152, 0x8E:0x017D, 0x91:0x2018,
  0x92:0x2019, 0x93:0x201C, 0x94:0x201D, 0x95:0x2022, 0x96:0x2013,
  0x97:0x2014, 0x98:0x02DC, 0x99:0x2122, 0x9A:0x0161, 0x9B:0x203A,
  0x9C:0x0153, 0x9E:0x017E, 0x9F:0x0178
};

/*
 * This table is generated with test/tools/update-entities.js
 */
var namedCharRefs = {
  __proto__: null,
  "AElig":0xc6, "AElig;":0xc6,
  "AMP":0x26, "AMP;":0x26,
  "Aacute":0xc1, "Aacute;":0xc1,
  "Abreve;":0x102, "Acirc":0xc2,
  "Acirc;":0xc2, "Acy;":0x410,
  "Afr;":[0xd835,0xdd04], "Agrave":0xc0,
  "Agrave;":0xc0, "Alpha;":0x391,
  "Amacr;":0x100, "And;":0x2a53,
  "Aogon;":0x104, "Aopf;":[0xd835,0xdd38],
  "ApplyFunction;":0x2061, "Aring":0xc5,
  "Aring;":0xc5, "Ascr;":[0xd835,0xdc9c],
  "Assign;":0x2254, "Atilde":0xc3,
  "Atilde;":0xc3, "Auml":0xc4,
  "Auml;":0xc4, "Backslash;":0x2216,
  "Barv;":0x2ae7, "Barwed;":0x2306,
  "Bcy;":0x411, "Because;":0x2235,
  "Bernoullis;":0x212c, "Beta;":0x392,
  "Bfr;":[0xd835,0xdd05], "Bopf;":[0xd835,0xdd39],
  "Breve;":0x2d8, "Bscr;":0x212c,
  "Bumpeq;":0x224e, "CHcy;":0x427,
  "COPY":0xa9, "COPY;":0xa9,
  "Cacute;":0x106, "Cap;":0x22d2,
  "CapitalDifferentialD;":0x2145, "Cayleys;":0x212d,
  "Ccaron;":0x10c, "Ccedil":0xc7,
  "Ccedil;":0xc7, "Ccirc;":0x108,
  "Cconint;":0x2230, "Cdot;":0x10a,
  "Cedilla;":0xb8, "CenterDot;":0xb7,
  "Cfr;":0x212d, "Chi;":0x3a7,
  "CircleDot;":0x2299, "CircleMinus;":0x2296,
  "CirclePlus;":0x2295, "CircleTimes;":0x2297,
  "ClockwiseContourIntegral;":0x2232, "CloseCurlyDoubleQuote;":0x201d,
  "CloseCurlyQuote;":0x2019, "Colon;":0x2237,
  "Colone;":0x2a74, "Congruent;":0x2261,
  "Conint;":0x222f, "ContourIntegral;":0x222e,
  "Copf;":0x2102, "Coproduct;":0x2210,
  "CounterClockwiseContourIntegral;":0x2233, "Cross;":0x2a2f,
  "Cscr;":[0xd835,0xdc9e], "Cup;":0x22d3,
  "CupCap;":0x224d, "DD;":0x2145,
  "DDotrahd;":0x2911, "DJcy;":0x402,
  "DScy;":0x405, "DZcy;":0x40f,
  "Dagger;":0x2021, "Darr;":0x21a1,
  "Dashv;":0x2ae4, "Dcaron;":0x10e,
  "Dcy;":0x414, "Del;":0x2207,
  "Delta;":0x394, "Dfr;":[0xd835,0xdd07],
  "DiacriticalAcute;":0xb4, "DiacriticalDot;":0x2d9,
  "DiacriticalDoubleAcute;":0x2dd, "DiacriticalGrave;":0x60,
  "DiacriticalTilde;":0x2dc, "Diamond;":0x22c4,
  "DifferentialD;":0x2146, "Dopf;":[0xd835,0xdd3b],
  "Dot;":0xa8, "DotDot;":0x20dc,
  "DotEqual;":0x2250, "DoubleContourIntegral;":0x222f,
  "DoubleDot;":0xa8, "DoubleDownArrow;":0x21d3,
  "DoubleLeftArrow;":0x21d0, "DoubleLeftRightArrow;":0x21d4,
  "DoubleLeftTee;":0x2ae4, "DoubleLongLeftArrow;":0x27f8,
  "DoubleLongLeftRightArrow;":0x27fa, "DoubleLongRightArrow;":0x27f9,
  "DoubleRightArrow;":0x21d2, "DoubleRightTee;":0x22a8,
  "DoubleUpArrow;":0x21d1, "DoubleUpDownArrow;":0x21d5,
  "DoubleVerticalBar;":0x2225, "DownArrow;":0x2193,
  "DownArrowBar;":0x2913, "DownArrowUpArrow;":0x21f5,
  "DownBreve;":0x311, "DownLeftRightVector;":0x2950,
  "DownLeftTeeVector;":0x295e, "DownLeftVector;":0x21bd,
  "DownLeftVectorBar;":0x2956, "DownRightTeeVector;":0x295f,
  "DownRightVector;":0x21c1, "DownRightVectorBar;":0x2957,
  "DownTee;":0x22a4, "DownTeeArrow;":0x21a7,
  "Downarrow;":0x21d3, "Dscr;":[0xd835,0xdc9f],
  "Dstrok;":0x110, "ENG;":0x14a,
  "ETH":0xd0, "ETH;":0xd0,
  "Eacute":0xc9, "Eacute;":0xc9,
  "Ecaron;":0x11a, "Ecirc":0xca,
  "Ecirc;":0xca, "Ecy;":0x42d,
  "Edot;":0x116, "Efr;":[0xd835,0xdd08],
  "Egrave":0xc8, "Egrave;":0xc8,
  "Element;":0x2208, "Emacr;":0x112,
  "EmptySmallSquare;":0x25fb, "EmptyVerySmallSquare;":0x25ab,
  "Eogon;":0x118, "Eopf;":[0xd835,0xdd3c],
  "Epsilon;":0x395, "Equal;":0x2a75,
  "EqualTilde;":0x2242, "Equilibrium;":0x21cc,
  "Escr;":0x2130, "Esim;":0x2a73,
  "Eta;":0x397, "Euml":0xcb,
  "Euml;":0xcb, "Exists;":0x2203,
  "ExponentialE;":0x2147, "Fcy;":0x424,
  "Ffr;":[0xd835,0xdd09], "FilledSmallSquare;":0x25fc,
  "FilledVerySmallSquare;":0x25aa, "Fopf;":[0xd835,0xdd3d],
  "ForAll;":0x2200, "Fouriertrf;":0x2131,
  "Fscr;":0x2131, "GJcy;":0x403,
  "GT":0x3e, "GT;":0x3e,
  "Gamma;":0x393, "Gammad;":0x3dc,
  "Gbreve;":0x11e, "Gcedil;":0x122,
  "Gcirc;":0x11c, "Gcy;":0x413,
  "Gdot;":0x120, "Gfr;":[0xd835,0xdd0a],
  "Gg;":0x22d9, "Gopf;":[0xd835,0xdd3e],
  "GreaterEqual;":0x2265, "GreaterEqualLess;":0x22db,
  "GreaterFullEqual;":0x2267, "GreaterGreater;":0x2aa2,
  "GreaterLess;":0x2277, "GreaterSlantEqual;":0x2a7e,
  "GreaterTilde;":0x2273, "Gscr;":[0xd835,0xdca2],
  "Gt;":0x226b, "HARDcy;":0x42a,
  "Hacek;":0x2c7, "Hat;":0x5e,
  "Hcirc;":0x124, "Hfr;":0x210c,
  "HilbertSpace;":0x210b, "Hopf;":0x210d,
  "HorizontalLine;":0x2500, "Hscr;":0x210b,
  "Hstrok;":0x126, "HumpDownHump;":0x224e,
  "HumpEqual;":0x224f, "IEcy;":0x415,
  "IJlig;":0x132, "IOcy;":0x401,
  "Iacute":0xcd, "Iacute;":0xcd,
  "Icirc":0xce, "Icirc;":0xce,
  "Icy;":0x418, "Idot;":0x130,
  "Ifr;":0x2111, "Igrave":0xcc,
  "Igrave;":0xcc, "Im;":0x2111,
  "Imacr;":0x12a, "ImaginaryI;":0x2148,
  "Implies;":0x21d2, "Int;":0x222c,
  "Integral;":0x222b, "Intersection;":0x22c2,
  "InvisibleComma;":0x2063, "InvisibleTimes;":0x2062,
  "Iogon;":0x12e, "Iopf;":[0xd835,0xdd40],
  "Iota;":0x399, "Iscr;":0x2110,
  "Itilde;":0x128, "Iukcy;":0x406,
  "Iuml":0xcf, "Iuml;":0xcf,
  "Jcirc;":0x134, "Jcy;":0x419,
  "Jfr;":[0xd835,0xdd0d], "Jopf;":[0xd835,0xdd41],
  "Jscr;":[0xd835,0xdca5], "Jsercy;":0x408,
  "Jukcy;":0x404, "KHcy;":0x425,
  "KJcy;":0x40c, "Kappa;":0x39a,
  "Kcedil;":0x136, "Kcy;":0x41a,
  "Kfr;":[0xd835,0xdd0e], "Kopf;":[0xd835,0xdd42],
  "Kscr;":[0xd835,0xdca6], "LJcy;":0x409,
  "LT":0x3c, "LT;":0x3c,
  "Lacute;":0x139, "Lambda;":0x39b,
  "Lang;":0x27ea, "Laplacetrf;":0x2112,
  "Larr;":0x219e, "Lcaron;":0x13d,
  "Lcedil;":0x13b, "Lcy;":0x41b,
  "LeftAngleBracket;":0x27e8, "LeftArrow;":0x2190,
  "LeftArrowBar;":0x21e4, "LeftArrowRightArrow;":0x21c6,
  "LeftCeiling;":0x2308, "LeftDoubleBracket;":0x27e6,
  "LeftDownTeeVector;":0x2961, "LeftDownVector;":0x21c3,
  "LeftDownVectorBar;":0x2959, "LeftFloor;":0x230a,
  "LeftRightArrow;":0x2194, "LeftRightVector;":0x294e,
  "LeftTee;":0x22a3, "LeftTeeArrow;":0x21a4,
  "LeftTeeVector;":0x295a, "LeftTriangle;":0x22b2,
  "LeftTriangleBar;":0x29cf, "LeftTriangleEqual;":0x22b4,
  "LeftUpDownVector;":0x2951, "LeftUpTeeVector;":0x2960,
  "LeftUpVector;":0x21bf, "LeftUpVectorBar;":0x2958,
  "LeftVector;":0x21bc, "LeftVectorBar;":0x2952,
  "Leftarrow;":0x21d0, "Leftrightarrow;":0x21d4,
  "LessEqualGreater;":0x22da, "LessFullEqual;":0x2266,
  "LessGreater;":0x2276, "LessLess;":0x2aa1,
  "LessSlantEqual;":0x2a7d, "LessTilde;":0x2272,
  "Lfr;":[0xd835,0xdd0f], "Ll;":0x22d8,
  "Lleftarrow;":0x21da, "Lmidot;":0x13f,
  "LongLeftArrow;":0x27f5, "LongLeftRightArrow;":0x27f7,
  "LongRightArrow;":0x27f6, "Longleftarrow;":0x27f8,
  "Longleftrightarrow;":0x27fa, "Longrightarrow;":0x27f9,
  "Lopf;":[0xd835,0xdd43], "LowerLeftArrow;":0x2199,
  "LowerRightArrow;":0x2198, "Lscr;":0x2112,
  "Lsh;":0x21b0, "Lstrok;":0x141,
  "Lt;":0x226a, "Map;":0x2905,
  "Mcy;":0x41c, "MediumSpace;":0x205f,
  "Mellintrf;":0x2133, "Mfr;":[0xd835,0xdd10],
  "MinusPlus;":0x2213, "Mopf;":[0xd835,0xdd44],
  "Mscr;":0x2133, "Mu;":0x39c,
  "NJcy;":0x40a, "Nacute;":0x143,
  "Ncaron;":0x147, "Ncedil;":0x145,
  "Ncy;":0x41d, "NegativeMediumSpace;":0x200b,
  "NegativeThickSpace;":0x200b, "NegativeThinSpace;":0x200b,
  "NegativeVeryThinSpace;":0x200b, "NestedGreaterGreater;":0x226b,
  "NestedLessLess;":0x226a, "NewLine;":0xa,
  "Nfr;":[0xd835,0xdd11], "NoBreak;":0x2060,
  "NonBreakingSpace;":0xa0, "Nopf;":0x2115,
  "Not;":0x2aec, "NotCongruent;":0x2262,
  "NotCupCap;":0x226d, "NotDoubleVerticalBar;":0x2226,
  "NotElement;":0x2209, "NotEqual;":0x2260,
  "NotEqualTilde;":[0x2242,0x338], "NotExists;":0x2204,
  "NotGreater;":0x226f, "NotGreaterEqual;":0x2271,
  "NotGreaterFullEqual;":[0x2267,0x338], "NotGreaterGreater;":[0x226b,0x338],
  "NotGreaterLess;":0x2279, "NotGreaterSlantEqual;":[0x2a7e,0x338],
  "NotGreaterTilde;":0x2275, "NotHumpDownHump;":[0x224e,0x338],
  "NotHumpEqual;":[0x224f,0x338], "NotLeftTriangle;":0x22ea,
  "NotLeftTriangleBar;":[0x29cf,0x338], "NotLeftTriangleEqual;":0x22ec,
  "NotLess;":0x226e, "NotLessEqual;":0x2270,
  "NotLessGreater;":0x2278, "NotLessLess;":[0x226a,0x338],
  "NotLessSlantEqual;":[0x2a7d,0x338], "NotLessTilde;":0x2274,
  "NotNestedGreaterGreater;":[0x2aa2,0x338], "NotNestedLessLess;":[0x2aa1,0x338],
  "NotPrecedes;":0x2280, "NotPrecedesEqual;":[0x2aaf,0x338],
  "NotPrecedesSlantEqual;":0x22e0, "NotReverseElement;":0x220c,
  "NotRightTriangle;":0x22eb, "NotRightTriangleBar;":[0x29d0,0x338],
  "NotRightTriangleEqual;":0x22ed, "NotSquareSubset;":[0x228f,0x338],
  "NotSquareSubsetEqual;":0x22e2, "NotSquareSuperset;":[0x2290,0x338],
  "NotSquareSupersetEqual;":0x22e3, "NotSubset;":[0x2282,0x20d2],
  "NotSubsetEqual;":0x2288, "NotSucceeds;":0x2281,
  "NotSucceedsEqual;":[0x2ab0,0x338], "NotSucceedsSlantEqual;":0x22e1,
  "NotSucceedsTilde;":[0x227f,0x338], "NotSuperset;":[0x2283,0x20d2],
  "NotSupersetEqual;":0x2289, "NotTilde;":0x2241,
  "NotTildeEqual;":0x2244, "NotTildeFullEqual;":0x2247,
  "NotTildeTilde;":0x2249, "NotVerticalBar;":0x2224,
  "Nscr;":[0xd835,0xdca9], "Ntilde":0xd1,
  "Ntilde;":0xd1, "Nu;":0x39d,
  "OElig;":0x152, "Oacute":0xd3,
  "Oacute;":0xd3, "Ocirc":0xd4,
  "Ocirc;":0xd4, "Ocy;":0x41e,
  "Odblac;":0x150, "Ofr;":[0xd835,0xdd12],
  "Ograve":0xd2, "Ograve;":0xd2,
  "Omacr;":0x14c, "Omega;":0x3a9,
  "Omicron;":0x39f, "Oopf;":[0xd835,0xdd46],
  "OpenCurlyDoubleQuote;":0x201c, "OpenCurlyQuote;":0x2018,
  "Or;":0x2a54, "Oscr;":[0xd835,0xdcaa],
  "Oslash":0xd8, "Oslash;":0xd8,
  "Otilde":0xd5, "Otilde;":0xd5,
  "Otimes;":0x2a37, "Ouml":0xd6,
  "Ouml;":0xd6, "OverBar;":0x203e,
  "OverBrace;":0x23de, "OverBracket;":0x23b4,
  "OverParenthesis;":0x23dc, "PartialD;":0x2202,
  "Pcy;":0x41f, "Pfr;":[0xd835,0xdd13],
  "Phi;":0x3a6, "Pi;":0x3a0,
  "PlusMinus;":0xb1, "Poincareplane;":0x210c,
  "Popf;":0x2119, "Pr;":0x2abb,
  "Precedes;":0x227a, "PrecedesEqual;":0x2aaf,
  "PrecedesSlantEqual;":0x227c, "PrecedesTilde;":0x227e,
  "Prime;":0x2033, "Product;":0x220f,
  "Proportion;":0x2237, "Proportional;":0x221d,
  "Pscr;":[0xd835,0xdcab], "Psi;":0x3a8,
  "QUOT":0x22, "QUOT;":0x22,
  "Qfr;":[0xd835,0xdd14], "Qopf;":0x211a,
  "Qscr;":[0xd835,0xdcac], "RBarr;":0x2910,
  "REG":0xae, "REG;":0xae,
  "Racute;":0x154, "Rang;":0x27eb,
  "Rarr;":0x21a0, "Rarrtl;":0x2916,
  "Rcaron;":0x158, "Rcedil;":0x156,
  "Rcy;":0x420, "Re;":0x211c,
  "ReverseElement;":0x220b, "ReverseEquilibrium;":0x21cb,
  "ReverseUpEquilibrium;":0x296f, "Rfr;":0x211c,
  "Rho;":0x3a1, "RightAngleBracket;":0x27e9,
  "RightArrow;":0x2192, "RightArrowBar;":0x21e5,
  "RightArrowLeftArrow;":0x21c4, "RightCeiling;":0x2309,
  "RightDoubleBracket;":0x27e7, "RightDownTeeVector;":0x295d,
  "RightDownVector;":0x21c2, "RightDownVectorBar;":0x2955,
  "RightFloor;":0x230b, "RightTee;":0x22a2,
  "RightTeeArrow;":0x21a6, "RightTeeVector;":0x295b,
  "RightTriangle;":0x22b3, "RightTriangleBar;":0x29d0,
  "RightTriangleEqual;":0x22b5, "RightUpDownVector;":0x294f,
  "RightUpTeeVector;":0x295c, "RightUpVector;":0x21be,
  "RightUpVectorBar;":0x2954, "RightVector;":0x21c0,
  "RightVectorBar;":0x2953, "Rightarrow;":0x21d2,
  "Ropf;":0x211d, "RoundImplies;":0x2970,
  "Rrightarrow;":0x21db, "Rscr;":0x211b,
  "Rsh;":0x21b1, "RuleDelayed;":0x29f4,
  "SHCHcy;":0x429, "SHcy;":0x428,
  "SOFTcy;":0x42c, "Sacute;":0x15a,
  "Sc;":0x2abc, "Scaron;":0x160,
  "Scedil;":0x15e, "Scirc;":0x15c,
  "Scy;":0x421, "Sfr;":[0xd835,0xdd16],
  "ShortDownArrow;":0x2193, "ShortLeftArrow;":0x2190,
  "ShortRightArrow;":0x2192, "ShortUpArrow;":0x2191,
  "Sigma;":0x3a3, "SmallCircle;":0x2218,
  "Sopf;":[0xd835,0xdd4a], "Sqrt;":0x221a,
  "Square;":0x25a1, "SquareIntersection;":0x2293,
  "SquareSubset;":0x228f, "SquareSubsetEqual;":0x2291,
  "SquareSuperset;":0x2290, "SquareSupersetEqual;":0x2292,
  "SquareUnion;":0x2294, "Sscr;":[0xd835,0xdcae],
  "Star;":0x22c6, "Sub;":0x22d0,
  "Subset;":0x22d0, "SubsetEqual;":0x2286,
  "Succeeds;":0x227b, "SucceedsEqual;":0x2ab0,
  "SucceedsSlantEqual;":0x227d, "SucceedsTilde;":0x227f,
  "SuchThat;":0x220b, "Sum;":0x2211,
  "Sup;":0x22d1, "Superset;":0x2283,
  "SupersetEqual;":0x2287, "Supset;":0x22d1,
  "THORN":0xde, "THORN;":0xde,
  "TRADE;":0x2122, "TSHcy;":0x40b,
  "TScy;":0x426, "Tab;":0x9,
  "Tau;":0x3a4, "Tcaron;":0x164,
  "Tcedil;":0x162, "Tcy;":0x422,
  "Tfr;":[0xd835,0xdd17], "Therefore;":0x2234,
  "Theta;":0x398, "ThickSpace;":[0x205f,0x200a],
  "ThinSpace;":0x2009, "Tilde;":0x223c,
  "TildeEqual;":0x2243, "TildeFullEqual;":0x2245,
  "TildeTilde;":0x2248, "Topf;":[0xd835,0xdd4b],
  "TripleDot;":0x20db, "Tscr;":[0xd835,0xdcaf],
  "Tstrok;":0x166, "Uacute":0xda,
  "Uacute;":0xda, "Uarr;":0x219f,
  "Uarrocir;":0x2949, "Ubrcy;":0x40e,
  "Ubreve;":0x16c, "Ucirc":0xdb,
  "Ucirc;":0xdb, "Ucy;":0x423,
  "Udblac;":0x170, "Ufr;":[0xd835,0xdd18],
  "Ugrave":0xd9, "Ugrave;":0xd9,
  "Umacr;":0x16a, "UnderBar;":0x5f,
  "UnderBrace;":0x23df, "UnderBracket;":0x23b5,
  "UnderParenthesis;":0x23dd, "Union;":0x22c3,
  "UnionPlus;":0x228e, "Uogon;":0x172,
  "Uopf;":[0xd835,0xdd4c], "UpArrow;":0x2191,
  "UpArrowBar;":0x2912, "UpArrowDownArrow;":0x21c5,
  "UpDownArrow;":0x2195, "UpEquilibrium;":0x296e,
  "UpTee;":0x22a5, "UpTeeArrow;":0x21a5,
  "Uparrow;":0x21d1, "Updownarrow;":0x21d5,
  "UpperLeftArrow;":0x2196, "UpperRightArrow;":0x2197,
  "Upsi;":0x3d2, "Upsilon;":0x3a5,
  "Uring;":0x16e, "Uscr;":[0xd835,0xdcb0],
  "Utilde;":0x168, "Uuml":0xdc,
  "Uuml;":0xdc, "VDash;":0x22ab,
  "Vbar;":0x2aeb, "Vcy;":0x412,
  "Vdash;":0x22a9, "Vdashl;":0x2ae6,
  "Vee;":0x22c1, "Verbar;":0x2016,
  "Vert;":0x2016, "VerticalBar;":0x2223,
  "VerticalLine;":0x7c, "VerticalSeparator;":0x2758,
  "VerticalTilde;":0x2240, "VeryThinSpace;":0x200a,
  "Vfr;":[0xd835,0xdd19], "Vopf;":[0xd835,0xdd4d],
  "Vscr;":[0xd835,0xdcb1], "Vvdash;":0x22aa,
  "Wcirc;":0x174, "Wedge;":0x22c0,
  "Wfr;":[0xd835,0xdd1a], "Wopf;":[0xd835,0xdd4e],
  "Wscr;":[0xd835,0xdcb2], "Xfr;":[0xd835,0xdd1b],
  "Xi;":0x39e, "Xopf;":[0xd835,0xdd4f],
  "Xscr;":[0xd835,0xdcb3], "YAcy;":0x42f,
  "YIcy;":0x407, "YUcy;":0x42e,
  "Yacute":0xdd, "Yacute;":0xdd,
  "Ycirc;":0x176, "Ycy;":0x42b,
  "Yfr;":[0xd835,0xdd1c], "Yopf;":[0xd835,0xdd50],
  "Yscr;":[0xd835,0xdcb4], "Yuml;":0x178,
  "ZHcy;":0x416, "Zacute;":0x179,
  "Zcaron;":0x17d, "Zcy;":0x417,
  "Zdot;":0x17b, "ZeroWidthSpace;":0x200b,
  "Zeta;":0x396, "Zfr;":0x2128,
  "Zopf;":0x2124, "Zscr;":[0xd835,0xdcb5],
  "aacute":0xe1, "aacute;":0xe1,
  "abreve;":0x103, "ac;":0x223e,
  "acE;":[0x223e,0x333], "acd;":0x223f,
  "acirc":0xe2, "acirc;":0xe2,
  "acute":0xb4, "acute;":0xb4,
  "acy;":0x430, "aelig":0xe6,
  "aelig;":0xe6, "af;":0x2061,
  "afr;":[0xd835,0xdd1e], "agrave":0xe0,
  "agrave;":0xe0, "alefsym;":0x2135,
  "aleph;":0x2135, "alpha;":0x3b1,
  "amacr;":0x101, "amalg;":0x2a3f,
  "amp":0x26, "amp;":0x26,
  "and;":0x2227, "andand;":0x2a55,
  "andd;":0x2a5c, "andslope;":0x2a58,
  "andv;":0x2a5a, "ang;":0x2220,
  "ange;":0x29a4, "angle;":0x2220,
  "angmsd;":0x2221, "angmsdaa;":0x29a8,
  "angmsdab;":0x29a9, "angmsdac;":0x29aa,
  "angmsdad;":0x29ab, "angmsdae;":0x29ac,
  "angmsdaf;":0x29ad, "angmsdag;":0x29ae,
  "angmsdah;":0x29af, "angrt;":0x221f,
  "angrtvb;":0x22be, "angrtvbd;":0x299d,
  "angsph;":0x2222, "angst;":0xc5,
  "angzarr;":0x237c, "aogon;":0x105,
  "aopf;":[0xd835,0xdd52], "ap;":0x2248,
  "apE;":0x2a70, "apacir;":0x2a6f,
  "ape;":0x224a, "apid;":0x224b,
  "apos;":0x27, "approx;":0x2248,
  "approxeq;":0x224a, "aring":0xe5,
  "aring;":0xe5, "ascr;":[0xd835,0xdcb6],
  "ast;":0x2a, "asymp;":0x2248,
  "asympeq;":0x224d, "atilde":0xe3,
  "atilde;":0xe3, "auml":0xe4,
  "auml;":0xe4, "awconint;":0x2233,
  "awint;":0x2a11, "bNot;":0x2aed,
  "backcong;":0x224c, "backepsilon;":0x3f6,
  "backprime;":0x2035, "backsim;":0x223d,
  "backsimeq;":0x22cd, "barvee;":0x22bd,
  "barwed;":0x2305, "barwedge;":0x2305,
  "bbrk;":0x23b5, "bbrktbrk;":0x23b6,
  "bcong;":0x224c, "bcy;":0x431,
  "bdquo;":0x201e, "becaus;":0x2235,
  "because;":0x2235, "bemptyv;":0x29b0,
  "bepsi;":0x3f6, "bernou;":0x212c,
  "beta;":0x3b2, "beth;":0x2136,
  "between;":0x226c, "bfr;":[0xd835,0xdd1f],
  "bigcap;":0x22c2, "bigcirc;":0x25ef,
  "bigcup;":0x22c3, "bigodot;":0x2a00,
  "bigoplus;":0x2a01, "bigotimes;":0x2a02,
  "bigsqcup;":0x2a06, "bigstar;":0x2605,
  "bigtriangledown;":0x25bd, "bigtriangleup;":0x25b3,
  "biguplus;":0x2a04, "bigvee;":0x22c1,
  "bigwedge;":0x22c0, "bkarow;":0x290d,
  "blacklozenge;":0x29eb, "blacksquare;":0x25aa,
  "blacktriangle;":0x25b4, "blacktriangledown;":0x25be,
  "blacktriangleleft;":0x25c2, "blacktriangleright;":0x25b8,
  "blank;":0x2423, "blk12;":0x2592,
  "blk14;":0x2591, "blk34;":0x2593,
  "block;":0x2588, "bne;":[0x3d,0x20e5],
  "bnequiv;":[0x2261,0x20e5], "bnot;":0x2310,
  "bopf;":[0xd835,0xdd53], "bot;":0x22a5,
  "bottom;":0x22a5, "bowtie;":0x22c8,
  "boxDL;":0x2557, "boxDR;":0x2554,
  "boxDl;":0x2556, "boxDr;":0x2553,
  "boxH;":0x2550, "boxHD;":0x2566,
  "boxHU;":0x2569, "boxHd;":0x2564,
  "boxHu;":0x2567, "boxUL;":0x255d,
  "boxUR;":0x255a, "boxUl;":0x255c,
  "boxUr;":0x2559, "boxV;":0x2551,
  "boxVH;":0x256c, "boxVL;":0x2563,
  "boxVR;":0x2560, "boxVh;":0x256b,
  "boxVl;":0x2562, "boxVr;":0x255f,
  "boxbox;":0x29c9, "boxdL;":0x2555,
  "boxdR;":0x2552, "boxdl;":0x2510,
  "boxdr;":0x250c, "boxh;":0x2500,
  "boxhD;":0x2565, "boxhU;":0x2568,
  "boxhd;":0x252c, "boxhu;":0x2534,
  "boxminus;":0x229f, "boxplus;":0x229e,
  "boxtimes;":0x22a0, "boxuL;":0x255b,
  "boxuR;":0x2558, "boxul;":0x2518,
  "boxur;":0x2514, "boxv;":0x2502,
  "boxvH;":0x256a, "boxvL;":0x2561,
  "boxvR;":0x255e, "boxvh;":0x253c,
  "boxvl;":0x2524, "boxvr;":0x251c,
  "bprime;":0x2035, "breve;":0x2d8,
  "brvbar":0xa6, "brvbar;":0xa6,
  "bscr;":[0xd835,0xdcb7], "bsemi;":0x204f,
  "bsim;":0x223d, "bsime;":0x22cd,
  "bsol;":0x5c, "bsolb;":0x29c5,
  "bsolhsub;":0x27c8, "bull;":0x2022,
  "bullet;":0x2022, "bump;":0x224e,
  "bumpE;":0x2aae, "bumpe;":0x224f,
  "bumpeq;":0x224f, "cacute;":0x107,
  "cap;":0x2229, "capand;":0x2a44,
  "capbrcup;":0x2a49, "capcap;":0x2a4b,
  "capcup;":0x2a47, "capdot;":0x2a40,
  "caps;":[0x2229,0xfe00], "caret;":0x2041,
  "caron;":0x2c7, "ccaps;":0x2a4d,
  "ccaron;":0x10d, "ccedil":0xe7,
  "ccedil;":0xe7, "ccirc;":0x109,
  "ccups;":0x2a4c, "ccupssm;":0x2a50,
  "cdot;":0x10b, "cedil":0xb8,
  "cedil;":0xb8, "cemptyv;":0x29b2,
  "cent":0xa2, "cent;":0xa2,
  "centerdot;":0xb7, "cfr;":[0xd835,0xdd20],
  "chcy;":0x447, "check;":0x2713,
  "checkmark;":0x2713, "chi;":0x3c7,
  "cir;":0x25cb, "cirE;":0x29c3,
  "circ;":0x2c6, "circeq;":0x2257,
  "circlearrowleft;":0x21ba, "circlearrowright;":0x21bb,
  "circledR;":0xae, "circledS;":0x24c8,
  "circledast;":0x229b, "circledcirc;":0x229a,
  "circleddash;":0x229d, "cire;":0x2257,
  "cirfnint;":0x2a10, "cirmid;":0x2aef,
  "cirscir;":0x29c2, "clubs;":0x2663,
  "clubsuit;":0x2663, "colon;":0x3a,
  "colone;":0x2254, "coloneq;":0x2254,
  "comma;":0x2c, "commat;":0x40,
  "comp;":0x2201, "compfn;":0x2218,
  "complement;":0x2201, "complexes;":0x2102,
  "cong;":0x2245, "congdot;":0x2a6d,
  "conint;":0x222e, "copf;":[0xd835,0xdd54],
  "coprod;":0x2210, "copy":0xa9,
  "copy;":0xa9, "copysr;":0x2117,
  "crarr;":0x21b5, "cross;":0x2717,
  "cscr;":[0xd835,0xdcb8], "csub;":0x2acf,
  "csube;":0x2ad1, "csup;":0x2ad0,
  "csupe;":0x2ad2, "ctdot;":0x22ef,
  "cudarrl;":0x2938, "cudarrr;":0x2935,
  "cuepr;":0x22de, "cuesc;":0x22df,
  "cularr;":0x21b6, "cularrp;":0x293d,
  "cup;":0x222a, "cupbrcap;":0x2a48,
  "cupcap;":0x2a46, "cupcup;":0x2a4a,
  "cupdot;":0x228d, "cupor;":0x2a45,
  "cups;":[0x222a,0xfe00], "curarr;":0x21b7,
  "curarrm;":0x293c, "curlyeqprec;":0x22de,
  "curlyeqsucc;":0x22df, "curlyvee;":0x22ce,
  "curlywedge;":0x22cf, "curren":0xa4,
  "curren;":0xa4, "curvearrowleft;":0x21b6,
  "curvearrowright;":0x21b7, "cuvee;":0x22ce,
  "cuwed;":0x22cf, "cwconint;":0x2232,
  "cwint;":0x2231, "cylcty;":0x232d,
  "dArr;":0x21d3, "dHar;":0x2965,
  "dagger;":0x2020, "daleth;":0x2138,
  "darr;":0x2193, "dash;":0x2010,
  "dashv;":0x22a3, "dbkarow;":0x290f,
  "dblac;":0x2dd, "dcaron;":0x10f,
  "dcy;":0x434, "dd;":0x2146,
  "ddagger;":0x2021, "ddarr;":0x21ca,
  "ddotseq;":0x2a77, "deg":0xb0,
  "deg;":0xb0, "delta;":0x3b4,
  "demptyv;":0x29b1, "dfisht;":0x297f,
  "dfr;":[0xd835,0xdd21], "dharl;":0x21c3,
  "dharr;":0x21c2, "diam;":0x22c4,
  "diamond;":0x22c4, "diamondsuit;":0x2666,
  "diams;":0x2666, "die;":0xa8,
  "digamma;":0x3dd, "disin;":0x22f2,
  "div;":0xf7, "divide":0xf7,
  "divide;":0xf7, "divideontimes;":0x22c7,
  "divonx;":0x22c7, "djcy;":0x452,
  "dlcorn;":0x231e, "dlcrop;":0x230d,
  "dollar;":0x24, "dopf;":[0xd835,0xdd55],
  "dot;":0x2d9, "doteq;":0x2250,
  "doteqdot;":0x2251, "dotminus;":0x2238,
  "dotplus;":0x2214, "dotsquare;":0x22a1,
  "doublebarwedge;":0x2306, "downarrow;":0x2193,
  "downdownarrows;":0x21ca, "downharpoonleft;":0x21c3,
  "downharpoonright;":0x21c2, "drbkarow;":0x2910,
  "drcorn;":0x231f, "drcrop;":0x230c,
  "dscr;":[0xd835,0xdcb9], "dscy;":0x455,
  "dsol;":0x29f6, "dstrok;":0x111,
  "dtdot;":0x22f1, "dtri;":0x25bf,
  "dtrif;":0x25be, "duarr;":0x21f5,
  "duhar;":0x296f, "dwangle;":0x29a6,
  "dzcy;":0x45f, "dzigrarr;":0x27ff,
  "eDDot;":0x2a77, "eDot;":0x2251,
  "eacute":0xe9, "eacute;":0xe9,
  "easter;":0x2a6e, "ecaron;":0x11b,
  "ecir;":0x2256, "ecirc":0xea,
  "ecirc;":0xea, "ecolon;":0x2255,
  "ecy;":0x44d, "edot;":0x117,
  "ee;":0x2147, "efDot;":0x2252,
  "efr;":[0xd835,0xdd22], "eg;":0x2a9a,
  "egrave":0xe8, "egrave;":0xe8,
  "egs;":0x2a96, "egsdot;":0x2a98,
  "el;":0x2a99, "elinters;":0x23e7,
  "ell;":0x2113, "els;":0x2a95,
  "elsdot;":0x2a97, "emacr;":0x113,
  "empty;":0x2205, "emptyset;":0x2205,
  "emptyv;":0x2205, "emsp13;":0x2004,
  "emsp14;":0x2005, "emsp;":0x2003,
  "eng;":0x14b, "ensp;":0x2002,
  "eogon;":0x119, "eopf;":[0xd835,0xdd56],
  "epar;":0x22d5, "eparsl;":0x29e3,
  "eplus;":0x2a71, "epsi;":0x3b5,
  "epsilon;":0x3b5, "epsiv;":0x3f5,
  "eqcirc;":0x2256, "eqcolon;":0x2255,
  "eqsim;":0x2242, "eqslantgtr;":0x2a96,
  "eqslantless;":0x2a95, "equals;":0x3d,
  "equest;":0x225f, "equiv;":0x2261,
  "equivDD;":0x2a78, "eqvparsl;":0x29e5,
  "erDot;":0x2253, "erarr;":0x2971,
  "escr;":0x212f, "esdot;":0x2250,
  "esim;":0x2242, "eta;":0x3b7,
  "eth":0xf0, "eth;":0xf0,
  "euml":0xeb, "euml;":0xeb,
  "euro;":0x20ac, "excl;":0x21,
  "exist;":0x2203, "expectation;":0x2130,
  "exponentiale;":0x2147, "fallingdotseq;":0x2252,
  "fcy;":0x444, "female;":0x2640,
  "ffilig;":0xfb03, "fflig;":0xfb00,
  "ffllig;":0xfb04, "ffr;":[0xd835,0xdd23],
  "filig;":0xfb01, "fjlig;":[0x66,0x6a],
  "flat;":0x266d, "fllig;":0xfb02,
  "fltns;":0x25b1, "fnof;":0x192,
  "fopf;":[0xd835,0xdd57], "forall;":0x2200,
  "fork;":0x22d4, "forkv;":0x2ad9,
  "fpartint;":0x2a0d, "frac12":0xbd,
  "frac12;":0xbd, "frac13;":0x2153,
  "frac14":0xbc, "frac14;":0xbc,
  "frac15;":0x2155, "frac16;":0x2159,
  "frac18;":0x215b, "frac23;":0x2154,
  "frac25;":0x2156, "frac34":0xbe,
  "frac34;":0xbe, "frac35;":0x2157,
  "frac38;":0x215c, "frac45;":0x2158,
  "frac56;":0x215a, "frac58;":0x215d,
  "frac78;":0x215e, "frasl;":0x2044,
  "frown;":0x2322, "fscr;":[0xd835,0xdcbb],
  "gE;":0x2267, "gEl;":0x2a8c,
  "gacute;":0x1f5, "gamma;":0x3b3,
  "gammad;":0x3dd, "gap;":0x2a86,
  "gbreve;":0x11f, "gcirc;":0x11d,
  "gcy;":0x433, "gdot;":0x121,
  "ge;":0x2265, "gel;":0x22db,
  "geq;":0x2265, "geqq;":0x2267,
  "geqslant;":0x2a7e, "ges;":0x2a7e,
  "gescc;":0x2aa9, "gesdot;":0x2a80,
  "gesdoto;":0x2a82, "gesdotol;":0x2a84,
  "gesl;":[0x22db,0xfe00], "gesles;":0x2a94,
  "gfr;":[0xd835,0xdd24], "gg;":0x226b,
  "ggg;":0x22d9, "gimel;":0x2137,
  "gjcy;":0x453, "gl;":0x2277,
  "glE;":0x2a92, "gla;":0x2aa5,
  "glj;":0x2aa4, "gnE;":0x2269,
  "gnap;":0x2a8a, "gnapprox;":0x2a8a,
  "gne;":0x2a88, "gneq;":0x2a88,
  "gneqq;":0x2269, "gnsim;":0x22e7,
  "gopf;":[0xd835,0xdd58], "grave;":0x60,
  "gscr;":0x210a, "gsim;":0x2273,
  "gsime;":0x2a8e, "gsiml;":0x2a90,
  "gt":0x3e, "gt;":0x3e,
  "gtcc;":0x2aa7, "gtcir;":0x2a7a,
  "gtdot;":0x22d7, "gtlPar;":0x2995,
  "gtquest;":0x2a7c, "gtrapprox;":0x2a86,
  "gtrarr;":0x2978, "gtrdot;":0x22d7,
  "gtreqless;":0x22db, "gtreqqless;":0x2a8c,
  "gtrless;":0x2277, "gtrsim;":0x2273,
  "gvertneqq;":[0x2269,0xfe00], "gvnE;":[0x2269,0xfe00],
  "hArr;":0x21d4, "hairsp;":0x200a,
  "half;":0xbd, "hamilt;":0x210b,
  "hardcy;":0x44a, "harr;":0x2194,
  "harrcir;":0x2948, "harrw;":0x21ad,
  "hbar;":0x210f, "hcirc;":0x125,
  "hearts;":0x2665, "heartsuit;":0x2665,
  "hellip;":0x2026, "hercon;":0x22b9,
  "hfr;":[0xd835,0xdd25], "hksearow;":0x2925,
  "hkswarow;":0x2926, "hoarr;":0x21ff,
  "homtht;":0x223b, "hookleftarrow;":0x21a9,
  "hookrightarrow;":0x21aa, "hopf;":[0xd835,0xdd59],
  "horbar;":0x2015, "hscr;":[0xd835,0xdcbd],
  "hslash;":0x210f, "hstrok;":0x127,
  "hybull;":0x2043, "hyphen;":0x2010,
  "iacute":0xed, "iacute;":0xed,
  "ic;":0x2063, "icirc":0xee,
  "icirc;":0xee, "icy;":0x438,
  "iecy;":0x435, "iexcl":0xa1,
  "iexcl;":0xa1, "iff;":0x21d4,
  "ifr;":[0xd835,0xdd26], "igrave":0xec,
  "igrave;":0xec, "ii;":0x2148,
  "iiiint;":0x2a0c, "iiint;":0x222d,
  "iinfin;":0x29dc, "iiota;":0x2129,
  "ijlig;":0x133, "imacr;":0x12b,
  "image;":0x2111, "imagline;":0x2110,
  "imagpart;":0x2111, "imath;":0x131,
  "imof;":0x22b7, "imped;":0x1b5,
  "in;":0x2208, "incare;":0x2105,
  "infin;":0x221e, "infintie;":0x29dd,
  "inodot;":0x131, "int;":0x222b,
  "intcal;":0x22ba, "integers;":0x2124,
  "intercal;":0x22ba, "intlarhk;":0x2a17,
  "intprod;":0x2a3c, "iocy;":0x451,
  "iogon;":0x12f, "iopf;":[0xd835,0xdd5a],
  "iota;":0x3b9, "iprod;":0x2a3c,
  "iquest":0xbf, "iquest;":0xbf,
  "iscr;":[0xd835,0xdcbe], "isin;":0x2208,
  "isinE;":0x22f9, "isindot;":0x22f5,
  "isins;":0x22f4, "isinsv;":0x22f3,
  "isinv;":0x2208, "it;":0x2062,
  "itilde;":0x129, "iukcy;":0x456,
  "iuml":0xef, "iuml;":0xef,
  "jcirc;":0x135, "jcy;":0x439,
  "jfr;":[0xd835,0xdd27], "jmath;":0x237,
  "jopf;":[0xd835,0xdd5b], "jscr;":[0xd835,0xdcbf],
  "jsercy;":0x458, "jukcy;":0x454,
  "kappa;":0x3ba, "kappav;":0x3f0,
  "kcedil;":0x137, "kcy;":0x43a,
  "kfr;":[0xd835,0xdd28], "kgreen;":0x138,
  "khcy;":0x445, "kjcy;":0x45c,
  "kopf;":[0xd835,0xdd5c], "kscr;":[0xd835,0xdcc0],
  "lAarr;":0x21da, "lArr;":0x21d0,
  "lAtail;":0x291b, "lBarr;":0x290e,
  "lE;":0x2266, "lEg;":0x2a8b,
  "lHar;":0x2962, "lacute;":0x13a,
  "laemptyv;":0x29b4, "lagran;":0x2112,
  "lambda;":0x3bb, "lang;":0x27e8,
  "langd;":0x2991, "langle;":0x27e8,
  "lap;":0x2a85, "laquo":0xab,
  "laquo;":0xab, "larr;":0x2190,
  "larrb;":0x21e4, "larrbfs;":0x291f,
  "larrfs;":0x291d, "larrhk;":0x21a9,
  "larrlp;":0x21ab, "larrpl;":0x2939,
  "larrsim;":0x2973, "larrtl;":0x21a2,
  "lat;":0x2aab, "latail;":0x2919,
  "late;":0x2aad, "lates;":[0x2aad,0xfe00],
  "lbarr;":0x290c, "lbbrk;":0x2772,
  "lbrace;":0x7b, "lbrack;":0x5b,
  "lbrke;":0x298b, "lbrksld;":0x298f,
  "lbrkslu;":0x298d, "lcaron;":0x13e,
  "lcedil;":0x13c, "lceil;":0x2308,
  "lcub;":0x7b, "lcy;":0x43b,
  "ldca;":0x2936, "ldquo;":0x201c,
  "ldquor;":0x201e, "ldrdhar;":0x2967,
  "ldrushar;":0x294b, "ldsh;":0x21b2,
  "le;":0x2264, "leftarrow;":0x2190,
  "leftarrowtail;":0x21a2, "leftharpoondown;":0x21bd,
  "leftharpoonup;":0x21bc, "leftleftarrows;":0x21c7,
  "leftrightarrow;":0x2194, "leftrightarrows;":0x21c6,
  "leftrightharpoons;":0x21cb, "leftrightsquigarrow;":0x21ad,
  "leftthreetimes;":0x22cb, "leg;":0x22da,
  "leq;":0x2264, "leqq;":0x2266,
  "leqslant;":0x2a7d, "les;":0x2a7d,
  "lescc;":0x2aa8, "lesdot;":0x2a7f,
  "lesdoto;":0x2a81, "lesdotor;":0x2a83,
  "lesg;":[0x22da,0xfe00], "lesges;":0x2a93,
  "lessapprox;":0x2a85, "lessdot;":0x22d6,
  "lesseqgtr;":0x22da, "lesseqqgtr;":0x2a8b,
  "lessgtr;":0x2276, "lesssim;":0x2272,
  "lfisht;":0x297c, "lfloor;":0x230a,
  "lfr;":[0xd835,0xdd29], "lg;":0x2276,
  "lgE;":0x2a91, "lhard;":0x21bd,
  "lharu;":0x21bc, "lharul;":0x296a,
  "lhblk;":0x2584, "ljcy;":0x459,
  "ll;":0x226a, "llarr;":0x21c7,
  "llcorner;":0x231e, "llhard;":0x296b,
  "lltri;":0x25fa, "lmidot;":0x140,
  "lmoust;":0x23b0, "lmoustache;":0x23b0,
  "lnE;":0x2268, "lnap;":0x2a89,
  "lnapprox;":0x2a89, "lne;":0x2a87,
  "lneq;":0x2a87, "lneqq;":0x2268,
  "lnsim;":0x22e6, "loang;":0x27ec,
  "loarr;":0x21fd, "lobrk;":0x27e6,
  "longleftarrow;":0x27f5, "longleftrightarrow;":0x27f7,
  "longmapsto;":0x27fc, "longrightarrow;":0x27f6,
  "looparrowleft;":0x21ab, "looparrowright;":0x21ac,
  "lopar;":0x2985, "lopf;":[0xd835,0xdd5d],
  "loplus;":0x2a2d, "lotimes;":0x2a34,
  "lowast;":0x2217, "lowbar;":0x5f,
  "loz;":0x25ca, "lozenge;":0x25ca,
  "lozf;":0x29eb, "lpar;":0x28,
  "lparlt;":0x2993, "lrarr;":0x21c6,
  "lrcorner;":0x231f, "lrhar;":0x21cb,
  "lrhard;":0x296d, "lrm;":0x200e,
  "lrtri;":0x22bf, "lsaquo;":0x2039,
  "lscr;":[0xd835,0xdcc1], "lsh;":0x21b0,
  "lsim;":0x2272, "lsime;":0x2a8d,
  "lsimg;":0x2a8f, "lsqb;":0x5b,
  "lsquo;":0x2018, "lsquor;":0x201a,
  "lstrok;":0x142, "lt":0x3c,
  "lt;":0x3c, "ltcc;":0x2aa6,
  "ltcir;":0x2a79, "ltdot;":0x22d6,
  "lthree;":0x22cb, "ltimes;":0x22c9,
  "ltlarr;":0x2976, "ltquest;":0x2a7b,
  "ltrPar;":0x2996, "ltri;":0x25c3,
  "ltrie;":0x22b4, "ltrif;":0x25c2,
  "lurdshar;":0x294a, "luruhar;":0x2966,
  "lvertneqq;":[0x2268,0xfe00], "lvnE;":[0x2268,0xfe00],
  "mDDot;":0x223a, "macr":0xaf,
  "macr;":0xaf, "male;":0x2642,
  "malt;":0x2720, "maltese;":0x2720,
  "map;":0x21a6, "mapsto;":0x21a6,
  "mapstodown;":0x21a7, "mapstoleft;":0x21a4,
  "mapstoup;":0x21a5, "marker;":0x25ae,
  "mcomma;":0x2a29, "mcy;":0x43c,
  "mdash;":0x2014, "measuredangle;":0x2221,
  "mfr;":[0xd835,0xdd2a], "mho;":0x2127,
  "micro":0xb5, "micro;":0xb5,
  "mid;":0x2223, "midast;":0x2a,
  "midcir;":0x2af0, "middot":0xb7,
  "middot;":0xb7, "minus;":0x2212,
  "minusb;":0x229f, "minusd;":0x2238,
  "minusdu;":0x2a2a, "mlcp;":0x2adb,
  "mldr;":0x2026, "mnplus;":0x2213,
  "models;":0x22a7, "mopf;":[0xd835,0xdd5e],
  "mp;":0x2213, "mscr;":[0xd835,0xdcc2],
  "mstpos;":0x223e, "mu;":0x3bc,
  "multimap;":0x22b8, "mumap;":0x22b8,
  "nGg;":[0x22d9,0x338], "nGt;":[0x226b,0x20d2],
  "nGtv;":[0x226b,0x338], "nLeftarrow;":0x21cd,
  "nLeftrightarrow;":0x21ce, "nLl;":[0x22d8,0x338],
  "nLt;":[0x226a,0x20d2], "nLtv;":[0x226a,0x338],
  "nRightarrow;":0x21cf, "nVDash;":0x22af,
  "nVdash;":0x22ae, "nabla;":0x2207,
  "nacute;":0x144, "nang;":[0x2220,0x20d2],
  "nap;":0x2249, "napE;":[0x2a70,0x338],
  "napid;":[0x224b,0x338], "napos;":0x149,
  "napprox;":0x2249, "natur;":0x266e,
  "natural;":0x266e, "naturals;":0x2115,
  "nbsp":0xa0, "nbsp;":0xa0,
  "nbump;":[0x224e,0x338], "nbumpe;":[0x224f,0x338],
  "ncap;":0x2a43, "ncaron;":0x148,
  "ncedil;":0x146, "ncong;":0x2247,
  "ncongdot;":[0x2a6d,0x338], "ncup;":0x2a42,
  "ncy;":0x43d, "ndash;":0x2013,
  "ne;":0x2260, "neArr;":0x21d7,
  "nearhk;":0x2924, "nearr;":0x2197,
  "nearrow;":0x2197, "nedot;":[0x2250,0x338],
  "nequiv;":0x2262, "nesear;":0x2928,
  "nesim;":[0x2242,0x338], "nexist;":0x2204,
  "nexists;":0x2204, "nfr;":[0xd835,0xdd2b],
  "ngE;":[0x2267,0x338], "nge;":0x2271,
  "ngeq;":0x2271, "ngeqq;":[0x2267,0x338],
  "ngeqslant;":[0x2a7e,0x338], "nges;":[0x2a7e,0x338],
  "ngsim;":0x2275, "ngt;":0x226f,
  "ngtr;":0x226f, "nhArr;":0x21ce,
  "nharr;":0x21ae, "nhpar;":0x2af2,
  "ni;":0x220b, "nis;":0x22fc,
  "nisd;":0x22fa, "niv;":0x220b,
  "njcy;":0x45a, "nlArr;":0x21cd,
  "nlE;":[0x2266,0x338], "nlarr;":0x219a,
  "nldr;":0x2025, "nle;":0x2270,
  "nleftarrow;":0x219a, "nleftrightarrow;":0x21ae,
  "nleq;":0x2270, "nleqq;":[0x2266,0x338],
  "nleqslant;":[0x2a7d,0x338], "nles;":[0x2a7d,0x338],
  "nless;":0x226e, "nlsim;":0x2274,
  "nlt;":0x226e, "nltri;":0x22ea,
  "nltrie;":0x22ec, "nmid;":0x2224,
  "nopf;":[0xd835,0xdd5f], "not":0xac,
  "not;":0xac, "notin;":0x2209,
  "notinE;":[0x22f9,0x338], "notindot;":[0x22f5,0x338],
  "notinva;":0x2209, "notinvb;":0x22f7,
  "notinvc;":0x22f6, "notni;":0x220c,
  "notniva;":0x220c, "notnivb;":0x22fe,
  "notnivc;":0x22fd, "npar;":0x2226,
  "nparallel;":0x2226, "nparsl;":[0x2afd,0x20e5],
  "npart;":[0x2202,0x338], "npolint;":0x2a14,
  "npr;":0x2280, "nprcue;":0x22e0,
  "npre;":[0x2aaf,0x338], "nprec;":0x2280,
  "npreceq;":[0x2aaf,0x338], "nrArr;":0x21cf,
  "nrarr;":0x219b, "nrarrc;":[0x2933,0x338],
  "nrarrw;":[0x219d,0x338], "nrightarrow;":0x219b,
  "nrtri;":0x22eb, "nrtrie;":0x22ed,
  "nsc;":0x2281, "nsccue;":0x22e1,
  "nsce;":[0x2ab0,0x338], "nscr;":[0xd835,0xdcc3],
  "nshortmid;":0x2224, "nshortparallel;":0x2226,
  "nsim;":0x2241, "nsime;":0x2244,
  "nsimeq;":0x2244, "nsmid;":0x2224,
  "nspar;":0x2226, "nsqsube;":0x22e2,
  "nsqsupe;":0x22e3, "nsub;":0x2284,
  "nsubE;":[0x2ac5,0x338], "nsube;":0x2288,
  "nsubset;":[0x2282,0x20d2], "nsubseteq;":0x2288,
  "nsubseteqq;":[0x2ac5,0x338], "nsucc;":0x2281,
  "nsucceq;":[0x2ab0,0x338], "nsup;":0x2285,
  "nsupE;":[0x2ac6,0x338], "nsupe;":0x2289,
  "nsupset;":[0x2283,0x20d2], "nsupseteq;":0x2289,
  "nsupseteqq;":[0x2ac6,0x338], "ntgl;":0x2279,
  "ntilde":0xf1, "ntilde;":0xf1,
  "ntlg;":0x2278, "ntriangleleft;":0x22ea,
  "ntrianglelefteq;":0x22ec, "ntriangleright;":0x22eb,
  "ntrianglerighteq;":0x22ed, "nu;":0x3bd,
  "num;":0x23, "numero;":0x2116,
  "numsp;":0x2007, "nvDash;":0x22ad,
  "nvHarr;":0x2904, "nvap;":[0x224d,0x20d2],
  "nvdash;":0x22ac, "nvge;":[0x2265,0x20d2],
  "nvgt;":[0x3e,0x20d2], "nvinfin;":0x29de,
  "nvlArr;":0x2902, "nvle;":[0x2264,0x20d2],
  "nvlt;":[0x3c,0x20d2], "nvltrie;":[0x22b4,0x20d2],
  "nvrArr;":0x2903, "nvrtrie;":[0x22b5,0x20d2],
  "nvsim;":[0x223c,0x20d2], "nwArr;":0x21d6,
  "nwarhk;":0x2923, "nwarr;":0x2196,
  "nwarrow;":0x2196, "nwnear;":0x2927,
  "oS;":0x24c8, "oacute":0xf3,
  "oacute;":0xf3, "oast;":0x229b,
  "ocir;":0x229a, "ocirc":0xf4,
  "ocirc;":0xf4, "ocy;":0x43e,
  "odash;":0x229d, "odblac;":0x151,
  "odiv;":0x2a38, "odot;":0x2299,
  "odsold;":0x29bc, "oelig;":0x153,
  "ofcir;":0x29bf, "ofr;":[0xd835,0xdd2c],
  "ogon;":0x2db, "ograve":0xf2,
  "ograve;":0xf2, "ogt;":0x29c1,
  "ohbar;":0x29b5, "ohm;":0x3a9,
  "oint;":0x222e, "olarr;":0x21ba,
  "olcir;":0x29be, "olcross;":0x29bb,
  "oline;":0x203e, "olt;":0x29c0,
  "omacr;":0x14d, "omega;":0x3c9,
  "omicron;":0x3bf, "omid;":0x29b6,
  "ominus;":0x2296, "oopf;":[0xd835,0xdd60],
  "opar;":0x29b7, "operp;":0x29b9,
  "oplus;":0x2295, "or;":0x2228,
  "orarr;":0x21bb, "ord;":0x2a5d,
  "order;":0x2134, "orderof;":0x2134,
  "ordf":0xaa, "ordf;":0xaa,
  "ordm":0xba, "ordm;":0xba,
  "origof;":0x22b6, "oror;":0x2a56,
  "orslope;":0x2a57, "orv;":0x2a5b,
  "oscr;":0x2134, "oslash":0xf8,
  "oslash;":0xf8, "osol;":0x2298,
  "otilde":0xf5, "otilde;":0xf5,
  "otimes;":0x2297, "otimesas;":0x2a36,
  "ouml":0xf6, "ouml;":0xf6,
  "ovbar;":0x233d, "par;":0x2225,
  "para":0xb6, "para;":0xb6,
  "parallel;":0x2225, "parsim;":0x2af3,
  "parsl;":0x2afd, "part;":0x2202,
  "pcy;":0x43f, "percnt;":0x25,
  "period;":0x2e, "permil;":0x2030,
  "perp;":0x22a5, "pertenk;":0x2031,
  "pfr;":[0xd835,0xdd2d], "phi;":0x3c6,
  "phiv;":0x3d5, "phmmat;":0x2133,
  "phone;":0x260e, "pi;":0x3c0,
  "pitchfork;":0x22d4, "piv;":0x3d6,
  "planck;":0x210f, "planckh;":0x210e,
  "plankv;":0x210f, "plus;":0x2b,
  "plusacir;":0x2a23, "plusb;":0x229e,
  "pluscir;":0x2a22, "plusdo;":0x2214,
  "plusdu;":0x2a25, "pluse;":0x2a72,
  "plusmn":0xb1, "plusmn;":0xb1,
  "plussim;":0x2a26, "plustwo;":0x2a27,
  "pm;":0xb1, "pointint;":0x2a15,
  "popf;":[0xd835,0xdd61], "pound":0xa3,
  "pound;":0xa3, "pr;":0x227a,
  "prE;":0x2ab3, "prap;":0x2ab7,
  "prcue;":0x227c, "pre;":0x2aaf,
  "prec;":0x227a, "precapprox;":0x2ab7,
  "preccurlyeq;":0x227c, "preceq;":0x2aaf,
  "precnapprox;":0x2ab9, "precneqq;":0x2ab5,
  "precnsim;":0x22e8, "precsim;":0x227e,
  "prime;":0x2032, "primes;":0x2119,
  "prnE;":0x2ab5, "prnap;":0x2ab9,
  "prnsim;":0x22e8, "prod;":0x220f,
  "profalar;":0x232e, "profline;":0x2312,
  "profsurf;":0x2313, "prop;":0x221d,
  "propto;":0x221d, "prsim;":0x227e,
  "prurel;":0x22b0, "pscr;":[0xd835,0xdcc5],
  "psi;":0x3c8, "puncsp;":0x2008,
  "qfr;":[0xd835,0xdd2e], "qint;":0x2a0c,
  "qopf;":[0xd835,0xdd62], "qprime;":0x2057,
  "qscr;":[0xd835,0xdcc6], "quaternions;":0x210d,
  "quatint;":0x2a16, "quest;":0x3f,
  "questeq;":0x225f, "quot":0x22,
  "quot;":0x22, "rAarr;":0x21db,
  "rArr;":0x21d2, "rAtail;":0x291c,
  "rBarr;":0x290f, "rHar;":0x2964,
  "race;":[0x223d,0x331], "racute;":0x155,
  "radic;":0x221a, "raemptyv;":0x29b3,
  "rang;":0x27e9, "rangd;":0x2992,
  "range;":0x29a5, "rangle;":0x27e9,
  "raquo":0xbb, "raquo;":0xbb,
  "rarr;":0x2192, "rarrap;":0x2975,
  "rarrb;":0x21e5, "rarrbfs;":0x2920,
  "rarrc;":0x2933, "rarrfs;":0x291e,
  "rarrhk;":0x21aa, "rarrlp;":0x21ac,
  "rarrpl;":0x2945, "rarrsim;":0x2974,
  "rarrtl;":0x21a3, "rarrw;":0x219d,
  "ratail;":0x291a, "ratio;":0x2236,
  "rationals;":0x211a, "rbarr;":0x290d,
  "rbbrk;":0x2773, "rbrace;":0x7d,
  "rbrack;":0x5d, "rbrke;":0x298c,
  "rbrksld;":0x298e, "rbrkslu;":0x2990,
  "rcaron;":0x159, "rcedil;":0x157,
  "rceil;":0x2309, "rcub;":0x7d,
  "rcy;":0x440, "rdca;":0x2937,
  "rdldhar;":0x2969, "rdquo;":0x201d,
  "rdquor;":0x201d, "rdsh;":0x21b3,
  "real;":0x211c, "realine;":0x211b,
  "realpart;":0x211c, "reals;":0x211d,
  "rect;":0x25ad, "reg":0xae,
  "reg;":0xae, "rfisht;":0x297d,
  "rfloor;":0x230b, "rfr;":[0xd835,0xdd2f],
  "rhard;":0x21c1, "rharu;":0x21c0,
  "rharul;":0x296c, "rho;":0x3c1,
  "rhov;":0x3f1, "rightarrow;":0x2192,
  "rightarrowtail;":0x21a3, "rightharpoondown;":0x21c1,
  "rightharpoonup;":0x21c0, "rightleftarrows;":0x21c4,
  "rightleftharpoons;":0x21cc, "rightrightarrows;":0x21c9,
  "rightsquigarrow;":0x219d, "rightthreetimes;":0x22cc,
  "ring;":0x2da, "risingdotseq;":0x2253,
  "rlarr;":0x21c4, "rlhar;":0x21cc,
  "rlm;":0x200f, "rmoust;":0x23b1,
  "rmoustache;":0x23b1, "rnmid;":0x2aee,
  "roang;":0x27ed, "roarr;":0x21fe,
  "robrk;":0x27e7, "ropar;":0x2986,
  "ropf;":[0xd835,0xdd63], "roplus;":0x2a2e,
  "rotimes;":0x2a35, "rpar;":0x29,
  "rpargt;":0x2994, "rppolint;":0x2a12,
  "rrarr;":0x21c9, "rsaquo;":0x203a,
  "rscr;":[0xd835,0xdcc7], "rsh;":0x21b1,
  "rsqb;":0x5d, "rsquo;":0x2019,
  "rsquor;":0x2019, "rthree;":0x22cc,
  "rtimes;":0x22ca, "rtri;":0x25b9,
  "rtrie;":0x22b5, "rtrif;":0x25b8,
  "rtriltri;":0x29ce, "ruluhar;":0x2968,
  "rx;":0x211e, "sacute;":0x15b,
  "sbquo;":0x201a, "sc;":0x227b,
  "scE;":0x2ab4, "scap;":0x2ab8,
  "scaron;":0x161, "sccue;":0x227d,
  "sce;":0x2ab0, "scedil;":0x15f,
  "scirc;":0x15d, "scnE;":0x2ab6,
  "scnap;":0x2aba, "scnsim;":0x22e9,
  "scpolint;":0x2a13, "scsim;":0x227f,
  "scy;":0x441, "sdot;":0x22c5,
  "sdotb;":0x22a1, "sdote;":0x2a66,
  "seArr;":0x21d8, "searhk;":0x2925,
  "searr;":0x2198, "searrow;":0x2198,
  "sect":0xa7, "sect;":0xa7,
  "semi;":0x3b, "seswar;":0x2929,
  "setminus;":0x2216, "setmn;":0x2216,
  "sext;":0x2736, "sfr;":[0xd835,0xdd30],
  "sfrown;":0x2322, "sharp;":0x266f,
  "shchcy;":0x449, "shcy;":0x448,
  "shortmid;":0x2223, "shortparallel;":0x2225,
  "shy":0xad, "shy;":0xad,
  "sigma;":0x3c3, "sigmaf;":0x3c2,
  "sigmav;":0x3c2, "sim;":0x223c,
  "simdot;":0x2a6a, "sime;":0x2243,
  "simeq;":0x2243, "simg;":0x2a9e,
  "simgE;":0x2aa0, "siml;":0x2a9d,
  "simlE;":0x2a9f, "simne;":0x2246,
  "simplus;":0x2a24, "simrarr;":0x2972,
  "slarr;":0x2190, "smallsetminus;":0x2216,
  "smashp;":0x2a33, "smeparsl;":0x29e4,
  "smid;":0x2223, "smile;":0x2323,
  "smt;":0x2aaa, "smte;":0x2aac,
  "smtes;":[0x2aac,0xfe00], "softcy;":0x44c,
  "sol;":0x2f, "solb;":0x29c4,
  "solbar;":0x233f, "sopf;":[0xd835,0xdd64],
  "spades;":0x2660, "spadesuit;":0x2660,
  "spar;":0x2225, "sqcap;":0x2293,
  "sqcaps;":[0x2293,0xfe00], "sqcup;":0x2294,
  "sqcups;":[0x2294,0xfe00], "sqsub;":0x228f,
  "sqsube;":0x2291, "sqsubset;":0x228f,
  "sqsubseteq;":0x2291, "sqsup;":0x2290,
  "sqsupe;":0x2292, "sqsupset;":0x2290,
  "sqsupseteq;":0x2292, "squ;":0x25a1,
  "square;":0x25a1, "squarf;":0x25aa,
  "squf;":0x25aa, "srarr;":0x2192,
  "sscr;":[0xd835,0xdcc8], "ssetmn;":0x2216,
  "ssmile;":0x2323, "sstarf;":0x22c6,
  "star;":0x2606, "starf;":0x2605,
  "straightepsilon;":0x3f5, "straightphi;":0x3d5,
  "strns;":0xaf, "sub;":0x2282,
  "subE;":0x2ac5, "subdot;":0x2abd,
  "sube;":0x2286, "subedot;":0x2ac3,
  "submult;":0x2ac1, "subnE;":0x2acb,
  "subne;":0x228a, "subplus;":0x2abf,
  "subrarr;":0x2979, "subset;":0x2282,
  "subseteq;":0x2286, "subseteqq;":0x2ac5,
  "subsetneq;":0x228a, "subsetneqq;":0x2acb,
  "subsim;":0x2ac7, "subsub;":0x2ad5,
  "subsup;":0x2ad3, "succ;":0x227b,
  "succapprox;":0x2ab8, "succcurlyeq;":0x227d,
  "succeq;":0x2ab0, "succnapprox;":0x2aba,
  "succneqq;":0x2ab6, "succnsim;":0x22e9,
  "succsim;":0x227f, "sum;":0x2211,
  "sung;":0x266a, "sup1":0xb9,
  "sup1;":0xb9, "sup2":0xb2,
  "sup2;":0xb2, "sup3":0xb3,
  "sup3;":0xb3, "sup;":0x2283,
  "supE;":0x2ac6, "supdot;":0x2abe,
  "supdsub;":0x2ad8, "supe;":0x2287,
  "supedot;":0x2ac4, "suphsol;":0x27c9,
  "suphsub;":0x2ad7, "suplarr;":0x297b,
  "supmult;":0x2ac2, "supnE;":0x2acc,
  "supne;":0x228b, "supplus;":0x2ac0,
  "supset;":0x2283, "supseteq;":0x2287,
  "supseteqq;":0x2ac6, "supsetneq;":0x228b,
  "supsetneqq;":0x2acc, "supsim;":0x2ac8,
  "supsub;":0x2ad4, "supsup;":0x2ad6,
  "swArr;":0x21d9, "swarhk;":0x2926,
  "swarr;":0x2199, "swarrow;":0x2199,
  "swnwar;":0x292a, "szlig":0xdf,
  "szlig;":0xdf, "target;":0x2316,
  "tau;":0x3c4, "tbrk;":0x23b4,
  "tcaron;":0x165, "tcedil;":0x163,
  "tcy;":0x442, "tdot;":0x20db,
  "telrec;":0x2315, "tfr;":[0xd835,0xdd31],
  "there4;":0x2234, "therefore;":0x2234,
  "theta;":0x3b8, "thetasym;":0x3d1,
  "thetav;":0x3d1, "thickapprox;":0x2248,
  "thicksim;":0x223c, "thinsp;":0x2009,
  "thkap;":0x2248, "thksim;":0x223c,
  "thorn":0xfe, "thorn;":0xfe,
  "tilde;":0x2dc, "times":0xd7,
  "times;":0xd7, "timesb;":0x22a0,
  "timesbar;":0x2a31, "timesd;":0x2a30,
  "tint;":0x222d, "toea;":0x2928,
  "top;":0x22a4, "topbot;":0x2336,
  "topcir;":0x2af1, "topf;":[0xd835,0xdd65],
  "topfork;":0x2ada, "tosa;":0x2929,
  "tprime;":0x2034, "trade;":0x2122,
  "triangle;":0x25b5, "triangledown;":0x25bf,
  "triangleleft;":0x25c3, "trianglelefteq;":0x22b4,
  "triangleq;":0x225c, "triangleright;":0x25b9,
  "trianglerighteq;":0x22b5, "tridot;":0x25ec,
  "trie;":0x225c, "triminus;":0x2a3a,
  "triplus;":0x2a39, "trisb;":0x29cd,
  "tritime;":0x2a3b, "trpezium;":0x23e2,
  "tscr;":[0xd835,0xdcc9], "tscy;":0x446,
  "tshcy;":0x45b, "tstrok;":0x167,
  "twixt;":0x226c, "twoheadleftarrow;":0x219e,
  "twoheadrightarrow;":0x21a0, "uArr;":0x21d1,
  "uHar;":0x2963, "uacute":0xfa,
  "uacute;":0xfa, "uarr;":0x2191,
  "ubrcy;":0x45e, "ubreve;":0x16d,
  "ucirc":0xfb, "ucirc;":0xfb,
  "ucy;":0x443, "udarr;":0x21c5,
  "udblac;":0x171, "udhar;":0x296e,
  "ufisht;":0x297e, "ufr;":[0xd835,0xdd32],
  "ugrave":0xf9, "ugrave;":0xf9,
  "uharl;":0x21bf, "uharr;":0x21be,
  "uhblk;":0x2580, "ulcorn;":0x231c,
  "ulcorner;":0x231c, "ulcrop;":0x230f,
  "ultri;":0x25f8, "umacr;":0x16b,
  "uml":0xa8, "uml;":0xa8,
  "uogon;":0x173, "uopf;":[0xd835,0xdd66],
  "uparrow;":0x2191, "updownarrow;":0x2195,
  "upharpoonleft;":0x21bf, "upharpoonright;":0x21be,
  "uplus;":0x228e, "upsi;":0x3c5,
  "upsih;":0x3d2, "upsilon;":0x3c5,
  "upuparrows;":0x21c8, "urcorn;":0x231d,
  "urcorner;":0x231d, "urcrop;":0x230e,
  "uring;":0x16f, "urtri;":0x25f9,
  "uscr;":[0xd835,0xdcca], "utdot;":0x22f0,
  "utilde;":0x169, "utri;":0x25b5,
  "utrif;":0x25b4, "uuarr;":0x21c8,
  "uuml":0xfc, "uuml;":0xfc,
  "uwangle;":0x29a7, "vArr;":0x21d5,
  "vBar;":0x2ae8, "vBarv;":0x2ae9,
  "vDash;":0x22a8, "vangrt;":0x299c,
  "varepsilon;":0x3f5, "varkappa;":0x3f0,
  "varnothing;":0x2205, "varphi;":0x3d5,
  "varpi;":0x3d6, "varpropto;":0x221d,
  "varr;":0x2195, "varrho;":0x3f1,
  "varsigma;":0x3c2, "varsubsetneq;":[0x228a,0xfe00],
  "varsubsetneqq;":[0x2acb,0xfe00], "varsupsetneq;":[0x228b,0xfe00],
  "varsupsetneqq;":[0x2acc,0xfe00], "vartheta;":0x3d1,
  "vartriangleleft;":0x22b2, "vartriangleright;":0x22b3,
  "vcy;":0x432, "vdash;":0x22a2,
  "vee;":0x2228, "veebar;":0x22bb,
  "veeeq;":0x225a, "vellip;":0x22ee,
  "verbar;":0x7c, "vert;":0x7c,
  "vfr;":[0xd835,0xdd33], "vltri;":0x22b2,
  "vnsub;":[0x2282,0x20d2], "vnsup;":[0x2283,0x20d2],
  "vopf;":[0xd835,0xdd67], "vprop;":0x221d,
  "vrtri;":0x22b3, "vscr;":[0xd835,0xdccb],
  "vsubnE;":[0x2acb,0xfe00], "vsubne;":[0x228a,0xfe00],
  "vsupnE;":[0x2acc,0xfe00], "vsupne;":[0x228b,0xfe00],
  "vzigzag;":0x299a, "wcirc;":0x175,
  "wedbar;":0x2a5f, "wedge;":0x2227,
  "wedgeq;":0x2259, "weierp;":0x2118,
  "wfr;":[0xd835,0xdd34], "wopf;":[0xd835,0xdd68],
  "wp;":0x2118, "wr;":0x2240,
  "wreath;":0x2240, "wscr;":[0xd835,0xdccc],
  "xcap;":0x22c2, "xcirc;":0x25ef,
  "xcup;":0x22c3, "xdtri;":0x25bd,
  "xfr;":[0xd835,0xdd35], "xhArr;":0x27fa,
  "xharr;":0x27f7, "xi;":0x3be,
  "xlArr;":0x27f8, "xlarr;":0x27f5,
  "xmap;":0x27fc, "xnis;":0x22fb,
  "xodot;":0x2a00, "xopf;":[0xd835,0xdd69],
  "xoplus;":0x2a01, "xotime;":0x2a02,
  "xrArr;":0x27f9, "xrarr;":0x27f6,
  "xscr;":[0xd835,0xdccd], "xsqcup;":0x2a06,
  "xuplus;":0x2a04, "xutri;":0x25b3,
  "xvee;":0x22c1, "xwedge;":0x22c0,
  "yacute":0xfd, "yacute;":0xfd,
  "yacy;":0x44f, "ycirc;":0x177,
  "ycy;":0x44b, "yen":0xa5,
  "yen;":0xa5, "yfr;":[0xd835,0xdd36],
  "yicy;":0x457, "yopf;":[0xd835,0xdd6a],
  "yscr;":[0xd835,0xdcce], "yucy;":0x44e,
  "yuml":0xff, "yuml;":0xff,
  "zacute;":0x17a, "zcaron;":0x17e,
  "zcy;":0x437, "zdot;":0x17c,
  "zeetrf;":0x2128, "zeta;":0x3b6,
  "zfr;":[0xd835,0xdd37], "zhcy;":0x436,
  "zigrarr;":0x21dd, "zopf;":[0xd835,0xdd6b],
  "zscr;":[0xd835,0xdccf], "zwj;":0x200d,
  "zwnj;":0x200c,
};
/*
 * This regexp is generated with test/tools/update-entities.js
 * It will always match at least one character -- but note that there
 * are no entities whose names are a single character long.
 */
var NAMEDCHARREF = /(A(?:Elig;?|MP;?|acute;?|breve;|c(?:irc;?|y;)|fr;|grave;?|lpha;|macr;|nd;|o(?:gon;|pf;)|pplyFunction;|ring;?|s(?:cr;|sign;)|tilde;?|uml;?)|B(?:a(?:ckslash;|r(?:v;|wed;))|cy;|e(?:cause;|rnoullis;|ta;)|fr;|opf;|reve;|scr;|umpeq;)|C(?:Hcy;|OPY;?|a(?:cute;|p(?:;|italDifferentialD;)|yleys;)|c(?:aron;|edil;?|irc;|onint;)|dot;|e(?:dilla;|nterDot;)|fr;|hi;|ircle(?:Dot;|Minus;|Plus;|Times;)|lo(?:ckwiseContourIntegral;|seCurly(?:DoubleQuote;|Quote;))|o(?:lon(?:;|e;)|n(?:gruent;|int;|tourIntegral;)|p(?:f;|roduct;)|unterClockwiseContourIntegral;)|ross;|scr;|up(?:;|Cap;))|D(?:D(?:;|otrahd;)|Jcy;|Scy;|Zcy;|a(?:gger;|rr;|shv;)|c(?:aron;|y;)|el(?:;|ta;)|fr;|i(?:a(?:critical(?:Acute;|Do(?:t;|ubleAcute;)|Grave;|Tilde;)|mond;)|fferentialD;)|o(?:pf;|t(?:;|Dot;|Equal;)|uble(?:ContourIntegral;|Do(?:t;|wnArrow;)|L(?:eft(?:Arrow;|RightArrow;|Tee;)|ong(?:Left(?:Arrow;|RightArrow;)|RightArrow;))|Right(?:Arrow;|Tee;)|Up(?:Arrow;|DownArrow;)|VerticalBar;)|wn(?:Arrow(?:;|Bar;|UpArrow;)|Breve;|Left(?:RightVector;|TeeVector;|Vector(?:;|Bar;))|Right(?:TeeVector;|Vector(?:;|Bar;))|Tee(?:;|Arrow;)|arrow;))|s(?:cr;|trok;))|E(?:NG;|TH;?|acute;?|c(?:aron;|irc;?|y;)|dot;|fr;|grave;?|lement;|m(?:acr;|pty(?:SmallSquare;|VerySmallSquare;))|o(?:gon;|pf;)|psilon;|qu(?:al(?:;|Tilde;)|ilibrium;)|s(?:cr;|im;)|ta;|uml;?|x(?:ists;|ponentialE;))|F(?:cy;|fr;|illed(?:SmallSquare;|VerySmallSquare;)|o(?:pf;|rAll;|uriertrf;)|scr;)|G(?:Jcy;|T;?|amma(?:;|d;)|breve;|c(?:edil;|irc;|y;)|dot;|fr;|g;|opf;|reater(?:Equal(?:;|Less;)|FullEqual;|Greater;|Less;|SlantEqual;|Tilde;)|scr;|t;)|H(?:ARDcy;|a(?:cek;|t;)|circ;|fr;|ilbertSpace;|o(?:pf;|rizontalLine;)|s(?:cr;|trok;)|ump(?:DownHump;|Equal;))|I(?:Ecy;|Jlig;|Ocy;|acute;?|c(?:irc;?|y;)|dot;|fr;|grave;?|m(?:;|a(?:cr;|ginaryI;)|plies;)|n(?:t(?:;|e(?:gral;|rsection;))|visible(?:Comma;|Times;))|o(?:gon;|pf;|ta;)|scr;|tilde;|u(?:kcy;|ml;?))|J(?:c(?:irc;|y;)|fr;|opf;|s(?:cr;|ercy;)|ukcy;)|K(?:Hcy;|Jcy;|appa;|c(?:edil;|y;)|fr;|opf;|scr;)|L(?:Jcy;|T;?|a(?:cute;|mbda;|ng;|placetrf;|rr;)|c(?:aron;|edil;|y;)|e(?:ft(?:A(?:ngleBracket;|rrow(?:;|Bar;|RightArrow;))|Ceiling;|Do(?:ubleBracket;|wn(?:TeeVector;|Vector(?:;|Bar;)))|Floor;|Right(?:Arrow;|Vector;)|T(?:ee(?:;|Arrow;|Vector;)|riangle(?:;|Bar;|Equal;))|Up(?:DownVector;|TeeVector;|Vector(?:;|Bar;))|Vector(?:;|Bar;)|arrow;|rightarrow;)|ss(?:EqualGreater;|FullEqual;|Greater;|Less;|SlantEqual;|Tilde;))|fr;|l(?:;|eftarrow;)|midot;|o(?:ng(?:Left(?:Arrow;|RightArrow;)|RightArrow;|left(?:arrow;|rightarrow;)|rightarrow;)|pf;|wer(?:LeftArrow;|RightArrow;))|s(?:cr;|h;|trok;)|t;)|M(?:ap;|cy;|e(?:diumSpace;|llintrf;)|fr;|inusPlus;|opf;|scr;|u;)|N(?:Jcy;|acute;|c(?:aron;|edil;|y;)|e(?:gative(?:MediumSpace;|Thi(?:ckSpace;|nSpace;)|VeryThinSpace;)|sted(?:GreaterGreater;|LessLess;)|wLine;)|fr;|o(?:Break;|nBreakingSpace;|pf;|t(?:;|C(?:ongruent;|upCap;)|DoubleVerticalBar;|E(?:lement;|qual(?:;|Tilde;)|xists;)|Greater(?:;|Equal;|FullEqual;|Greater;|Less;|SlantEqual;|Tilde;)|Hump(?:DownHump;|Equal;)|Le(?:ftTriangle(?:;|Bar;|Equal;)|ss(?:;|Equal;|Greater;|Less;|SlantEqual;|Tilde;))|Nested(?:GreaterGreater;|LessLess;)|Precedes(?:;|Equal;|SlantEqual;)|R(?:everseElement;|ightTriangle(?:;|Bar;|Equal;))|S(?:quareSu(?:bset(?:;|Equal;)|perset(?:;|Equal;))|u(?:bset(?:;|Equal;)|cceeds(?:;|Equal;|SlantEqual;|Tilde;)|perset(?:;|Equal;)))|Tilde(?:;|Equal;|FullEqual;|Tilde;)|VerticalBar;))|scr;|tilde;?|u;)|O(?:Elig;|acute;?|c(?:irc;?|y;)|dblac;|fr;|grave;?|m(?:acr;|ega;|icron;)|opf;|penCurly(?:DoubleQuote;|Quote;)|r;|s(?:cr;|lash;?)|ti(?:lde;?|mes;)|uml;?|ver(?:B(?:ar;|rac(?:e;|ket;))|Parenthesis;))|P(?:artialD;|cy;|fr;|hi;|i;|lusMinus;|o(?:incareplane;|pf;)|r(?:;|ecedes(?:;|Equal;|SlantEqual;|Tilde;)|ime;|o(?:duct;|portion(?:;|al;)))|s(?:cr;|i;))|Q(?:UOT;?|fr;|opf;|scr;)|R(?:Barr;|EG;?|a(?:cute;|ng;|rr(?:;|tl;))|c(?:aron;|edil;|y;)|e(?:;|verse(?:E(?:lement;|quilibrium;)|UpEquilibrium;))|fr;|ho;|ight(?:A(?:ngleBracket;|rrow(?:;|Bar;|LeftArrow;))|Ceiling;|Do(?:ubleBracket;|wn(?:TeeVector;|Vector(?:;|Bar;)))|Floor;|T(?:ee(?:;|Arrow;|Vector;)|riangle(?:;|Bar;|Equal;))|Up(?:DownVector;|TeeVector;|Vector(?:;|Bar;))|Vector(?:;|Bar;)|arrow;)|o(?:pf;|undImplies;)|rightarrow;|s(?:cr;|h;)|uleDelayed;)|S(?:H(?:CHcy;|cy;)|OFTcy;|acute;|c(?:;|aron;|edil;|irc;|y;)|fr;|hort(?:DownArrow;|LeftArrow;|RightArrow;|UpArrow;)|igma;|mallCircle;|opf;|q(?:rt;|uare(?:;|Intersection;|Su(?:bset(?:;|Equal;)|perset(?:;|Equal;))|Union;))|scr;|tar;|u(?:b(?:;|set(?:;|Equal;))|c(?:ceeds(?:;|Equal;|SlantEqual;|Tilde;)|hThat;)|m;|p(?:;|erset(?:;|Equal;)|set;)))|T(?:HORN;?|RADE;|S(?:Hcy;|cy;)|a(?:b;|u;)|c(?:aron;|edil;|y;)|fr;|h(?:e(?:refore;|ta;)|i(?:ckSpace;|nSpace;))|ilde(?:;|Equal;|FullEqual;|Tilde;)|opf;|ripleDot;|s(?:cr;|trok;))|U(?:a(?:cute;?|rr(?:;|ocir;))|br(?:cy;|eve;)|c(?:irc;?|y;)|dblac;|fr;|grave;?|macr;|n(?:der(?:B(?:ar;|rac(?:e;|ket;))|Parenthesis;)|ion(?:;|Plus;))|o(?:gon;|pf;)|p(?:Arrow(?:;|Bar;|DownArrow;)|DownArrow;|Equilibrium;|Tee(?:;|Arrow;)|arrow;|downarrow;|per(?:LeftArrow;|RightArrow;)|si(?:;|lon;))|ring;|scr;|tilde;|uml;?)|V(?:Dash;|bar;|cy;|dash(?:;|l;)|e(?:e;|r(?:bar;|t(?:;|ical(?:Bar;|Line;|Separator;|Tilde;))|yThinSpace;))|fr;|opf;|scr;|vdash;)|W(?:circ;|edge;|fr;|opf;|scr;)|X(?:fr;|i;|opf;|scr;)|Y(?:Acy;|Icy;|Ucy;|acute;?|c(?:irc;|y;)|fr;|opf;|scr;|uml;)|Z(?:Hcy;|acute;|c(?:aron;|y;)|dot;|e(?:roWidthSpace;|ta;)|fr;|opf;|scr;)|a(?:acute;?|breve;|c(?:;|E;|d;|irc;?|ute;?|y;)|elig;?|f(?:;|r;)|grave;?|l(?:e(?:fsym;|ph;)|pha;)|m(?:a(?:cr;|lg;)|p;?)|n(?:d(?:;|and;|d;|slope;|v;)|g(?:;|e;|le;|msd(?:;|a(?:a;|b;|c;|d;|e;|f;|g;|h;))|rt(?:;|vb(?:;|d;))|s(?:ph;|t;)|zarr;))|o(?:gon;|pf;)|p(?:;|E;|acir;|e;|id;|os;|prox(?:;|eq;))|ring;?|s(?:cr;|t;|ymp(?:;|eq;))|tilde;?|uml;?|w(?:conint;|int;))|b(?:Not;|a(?:ck(?:cong;|epsilon;|prime;|sim(?:;|eq;))|r(?:vee;|wed(?:;|ge;)))|brk(?:;|tbrk;)|c(?:ong;|y;)|dquo;|e(?:caus(?:;|e;)|mptyv;|psi;|rnou;|t(?:a;|h;|ween;))|fr;|ig(?:c(?:ap;|irc;|up;)|o(?:dot;|plus;|times;)|s(?:qcup;|tar;)|triangle(?:down;|up;)|uplus;|vee;|wedge;)|karow;|l(?:a(?:ck(?:lozenge;|square;|triangle(?:;|down;|left;|right;))|nk;)|k(?:1(?:2;|4;)|34;)|ock;)|n(?:e(?:;|quiv;)|ot;)|o(?:pf;|t(?:;|tom;)|wtie;|x(?:D(?:L;|R;|l;|r;)|H(?:;|D;|U;|d;|u;)|U(?:L;|R;|l;|r;)|V(?:;|H;|L;|R;|h;|l;|r;)|box;|d(?:L;|R;|l;|r;)|h(?:;|D;|U;|d;|u;)|minus;|plus;|times;|u(?:L;|R;|l;|r;)|v(?:;|H;|L;|R;|h;|l;|r;)))|prime;|r(?:eve;|vbar;?)|s(?:cr;|emi;|im(?:;|e;)|ol(?:;|b;|hsub;))|u(?:ll(?:;|et;)|mp(?:;|E;|e(?:;|q;))))|c(?:a(?:cute;|p(?:;|and;|brcup;|c(?:ap;|up;)|dot;|s;)|r(?:et;|on;))|c(?:a(?:ps;|ron;)|edil;?|irc;|ups(?:;|sm;))|dot;|e(?:dil;?|mptyv;|nt(?:;|erdot;|))|fr;|h(?:cy;|eck(?:;|mark;)|i;)|ir(?:;|E;|c(?:;|eq;|le(?:arrow(?:left;|right;)|d(?:R;|S;|ast;|circ;|dash;)))|e;|fnint;|mid;|scir;)|lubs(?:;|uit;)|o(?:lon(?:;|e(?:;|q;))|m(?:ma(?:;|t;)|p(?:;|fn;|le(?:ment;|xes;)))|n(?:g(?:;|dot;)|int;)|p(?:f;|rod;|y(?:;|sr;|)))|r(?:arr;|oss;)|s(?:cr;|u(?:b(?:;|e;)|p(?:;|e;)))|tdot;|u(?:darr(?:l;|r;)|e(?:pr;|sc;)|larr(?:;|p;)|p(?:;|brcap;|c(?:ap;|up;)|dot;|or;|s;)|r(?:arr(?:;|m;)|ly(?:eq(?:prec;|succ;)|vee;|wedge;)|ren;?|vearrow(?:left;|right;))|vee;|wed;)|w(?:conint;|int;)|ylcty;)|d(?:Arr;|Har;|a(?:gger;|leth;|rr;|sh(?:;|v;))|b(?:karow;|lac;)|c(?:aron;|y;)|d(?:;|a(?:gger;|rr;)|otseq;)|e(?:g;?|lta;|mptyv;)|f(?:isht;|r;)|har(?:l;|r;)|i(?:am(?:;|ond(?:;|suit;)|s;)|e;|gamma;|sin;|v(?:;|ide(?:;|ontimes;|)|onx;))|jcy;|lc(?:orn;|rop;)|o(?:llar;|pf;|t(?:;|eq(?:;|dot;)|minus;|plus;|square;)|ublebarwedge;|wn(?:arrow;|downarrows;|harpoon(?:left;|right;)))|r(?:bkarow;|c(?:orn;|rop;))|s(?:c(?:r;|y;)|ol;|trok;)|t(?:dot;|ri(?:;|f;))|u(?:arr;|har;)|wangle;|z(?:cy;|igrarr;))|e(?:D(?:Dot;|ot;)|a(?:cute;?|ster;)|c(?:aron;|ir(?:;|c;?)|olon;|y;)|dot;|e;|f(?:Dot;|r;)|g(?:;|rave;?|s(?:;|dot;))|l(?:;|inters;|l;|s(?:;|dot;))|m(?:acr;|pty(?:;|set;|v;)|sp(?:1(?:3;|4;)|;))|n(?:g;|sp;)|o(?:gon;|pf;)|p(?:ar(?:;|sl;)|lus;|si(?:;|lon;|v;))|q(?:c(?:irc;|olon;)|s(?:im;|lant(?:gtr;|less;))|u(?:als;|est;|iv(?:;|DD;))|vparsl;)|r(?:Dot;|arr;)|s(?:cr;|dot;|im;)|t(?:a;|h;?)|u(?:ml;?|ro;)|x(?:cl;|ist;|p(?:ectation;|onentiale;)))|f(?:allingdotseq;|cy;|emale;|f(?:ilig;|l(?:ig;|lig;)|r;)|ilig;|jlig;|l(?:at;|lig;|tns;)|nof;|o(?:pf;|r(?:all;|k(?:;|v;)))|partint;|r(?:a(?:c(?:1(?:2;?|3;|4;?|5;|6;|8;)|2(?:3;|5;)|3(?:4;?|5;|

var NAMEDCHARREF_MAXLEN = 32;

// Regular expression constants used by the tokenizer and parser

// Note that \r is included in all of these regexps because it will need
// to be converted to LF by the scanChars() function.
var DBLQUOTEATTRVAL = /[^\r"&\u0000]+/g;
var SINGLEQUOTEATTRVAL = /[^\r'&\u0000]+/g;
var UNQUOTEDATTRVAL = /[^\r\t\n\f &>\u0000]+/g;
var TAGNAME = /[^\r\t\n\f \/>A-Z\u0000]+/g;
var ATTRNAME = /[^\r\t\n\f \/=>A-Z\u0000]+/g;

var CDATATEXT = /[^\]\r\u0000\uffff]*/g;
var DATATEXT = /[^&<\r\u0000\uffff]*/g;
var RAWTEXT = /[^<\r\u0000\uffff]*/g;
var PLAINTEXT = /[^\r\u0000\uffff]*/g;
// Since we don't have the 'sticky tag', add '|.' to the end of SIMPLETAG
// and SIMPLEATTR so that we are guaranteed to always match.  This prevents
// us from scanning past the lastIndex set. (Note that the desired matches
// are always greater than 1 char long, so longest-match will ensure that .
// is not matched unless the desired match fails.)
var SIMPLETAG = /(?:(\/)?([a-z]+)>)|[\s\S]/g;
var SIMPLEATTR = /(?:([-a-z]+)[ \t\n\f]*=[ \t\n\f]*('[^'&\r\u0000]*'|"[^"&\r\u0000]*"|[^\t\n\r\f "&'\u0000>][^&> \t\n\r\f\u0000]*[ \t\n\f]))|[\s\S]/g;

var NONWS = /[^\x09\x0A\x0C\x0D\x20]/;
var ALLNONWS = /[^\x09\x0A\x0C\x0D\x20]/g; // like above, with g flag
var NONWSNONNUL = /[^\x00\x09\x0A\x0C\x0D\x20]/; // don't allow NUL either
var LEADINGWS = /^[\x09\x0A\x0C\x0D\x20]+/;
var NULCHARS = /\x00/g;

/***
 * These are utility functions that don't use any of the parser's
 * internal state.
 */
function buf2str(buf) {
  var CHUNKSIZE=16384;
  if (buf.length < CHUNKSIZE) {
    return String.fromCharCode.apply(String, buf);
  }
  // special case for large strings, to avoid busting the stack.
  var result = '';
  for (var i = 0; i < buf.length; i += CHUNKSIZE) {
    result += String.fromCharCode.apply(String, buf.slice(i, i+CHUNKSIZE));
  }
  return result;
}

function str2buf(s) {
  var result = [];
  for (var i=0; i<s.length; i++) {
    result[i] = s.charCodeAt(i);
  }
  return result;
}

// Determine whether the element is a member of the set.
// The set is an object that maps namespaces to objects. The objects
// then map local tagnames to the value true if that tag is part of the set
function isA(elt, set) {
  if (typeof set === 'string') {
    // convenience case for testing a particular HTML element
    return elt.namespaceURI === NAMESPACE.HTML &&
      elt.localName === set;
  }
  var tagnames = set[elt.namespaceURI];
  return tagnames && tagnames[elt.localName];
}

function isMathmlTextIntegrationPoint(n) {
  return isA(n, mathmlTextIntegrationPointSet);
}

function isHTMLIntegrationPoint(n) {
  if (isA(n, htmlIntegrationPointSet)) return true;
  if (n.namespaceURI === NAMESPACE.MATHML &&
    n.localName === "annotation-xml") {
    var encoding = n.getAttribute("encoding");
    if (encoding) encoding = encoding.toLowerCase();
    if (encoding === "text/html" ||
      encoding === "application/xhtml+xml")
      return true;
  }
  return false;
}

function adjustSVGTagName(name) {
  if (name in svgTagNameAdjustments)
    return svgTagNameAdjustments[name];
  else
    return name;
}

function adjustSVGAttributes(attrs) {
  for(var i = 0, n = attrs.length; i < n; i++) {
    if (attrs[i][0] in svgAttrAdjustments) {
      attrs[i][0] = svgAttrAdjustments[attrs[i][0]];
    }
  }
}

function adjustMathMLAttributes(attrs) {
  for(var i = 0, n = attrs.length; i < n; i++) {
    if (attrs[i][0] === "definitionurl") {
      attrs[i][0] = "definitionURL";
      break;
    }
  }
}

function adjustForeignAttributes(attrs) {
  for(var i = 0, n = attrs.length; i < n; i++) {
    if (attrs[i][0] in foreignAttributes) {
      // Attributes with namespaces get a 3rd element:
      // [Qname, value, namespace]
      attrs[i].push(foreignAttributes[attrs[i][0]]);
    }
  }
}

// For each attribute in attrs, if elt doesn't have an attribute
// by that name, add the attribute to elt
// XXX: I'm ignoring namespaces for now
function transferAttributes(attrs, elt) {
  for(var i = 0, n = attrs.length; i < n; i++) {
    var name = attrs[i][0], value = attrs[i][1];
    if (elt.hasAttribute(name)) continue;
    elt._setAttribute(name, value);
  }
}

/***
 * The ElementStack class
 */
HTMLParser.ElementStack = function ElementStack() {
  this.elements = [];
  this.top = null; // stack.top is the "current node" in the spec
};

/*
// This is for debugging only
HTMLParser.ElementStack.prototype.toString = function(e) {
  return "STACK: " +
  this.elements.map(function(e) {return e.localName;}).join("-");
}
*/

HTMLParser.ElementStack.prototype.push = function(e) {
  this.elements.push(e);
  this.top = e;
};

HTMLParser.ElementStack.prototype.pop = function(e) {
  this.elements.pop();
  this.top = this.elements[this.elements.length-1];
};

// Pop elements off the stack up to and including the first
// element with the specified (HTML) tagname
HTMLParser.ElementStack.prototype.popTag = function(tag) {
  for(var i = this.elements.length-1; i > 0; i--) {
    var e = this.elements[i];
    if (isA(e, tag)) break;
  }
  this.elements.length = i;
  this.top = this.elements[i-1];
};

// Pop elements off the stack up to and including the first
// element that is an instance of the specified type
HTMLParser.ElementStack.prototype.popElementType = function(type) {
  for(var i = this.elements.length-1; i > 0; i--) {
    if (this.elements[i] instanceof type) break;
  }
  this.elements.length = i;
  this.top = this.elements[i-1];
};

// Pop elements off the stack up to and including the element e.
// Note that this is very different from removeElement()
// This requires that e is on the stack.
HTMLParser.ElementStack.prototype.popElement = function(e) {
  for(var i = this.elements.length-1; i > 0; i--) {
    if (this.elements[i] === e) break;
  }
  this.elements.length = i;
  this.top = this.elements[i-1];
};

// Remove a specific element from the stack.
// Do nothing if the element is not on the stack
HTMLParser.ElementStack.prototype.removeElement = function(e) {
  if (this.top === e) this.pop();
  else {
    var idx = this.elements.lastIndexOf(e);
    if (idx !== -1)
      this.elements.splice(idx, 1);
  }
};

HTMLParser.ElementStack.prototype.clearToContext = function(set) {
  // Note that we don't loop to 0. Never pop the <html> elt off.
  for(var i = this.elements.length-1; i > 0; i--) {
    if (isA(this.elements[i], set)) break;
  }
  this.elements.length = i+1;
  this.top = this.elements[i];
};

HTMLParser.ElementStack.prototype.contains = function(tag) {
  return this.inSpecificScope(tag, Object.create(null));
};

HTMLParser.ElementStack.prototype.inSpecificScope = function(tag, set) {
  for(var i = this.elements.length-1; i >= 0; i--) {
    var elt = this.elements[i];
    if (isA(elt, tag)) return true;
    if (isA(elt, set)) return false;
  }
  return false;
};

// Like the above, but for a specific element, not a tagname
HTMLParser.ElementStack.prototype.elementInSpecificScope = function(target, set) {
  for(var i = this.elements.length-1; i >= 0; i--) {
    var elt = this.elements[i];
    if (elt === target) return true;
    if (isA(elt, set)) return false;
  }
  return false;
};

// Like the above, but for an element interface, not a tagname
HTMLParser.ElementStack.prototype.elementTypeInSpecificScope = function(target, set) {
  for(var i = this.elements.length-1; i >= 0; i--) {
    var elt = this.elements[i];
    if (elt instanceof target) return true;
    if (isA(elt, set)) return false;
  }
  return false;
};

HTMLParser.ElementStack.prototype.inScope = function(tag) {
  return this.inSpecificScope(tag, inScopeSet);
};

HTMLParser.ElementStack.prototype.elementInScope = function(e) {
  return this.elementInSpecificScope(e, inScopeSet);
};

HTMLParser.ElementStack.prototype.elementTypeInScope = function(type) {
  return this.elementTypeInSpecificScope(type, inScopeSet);
};

HTMLParser.ElementStack.prototype.inButtonScope = function(tag) {
  return this.inSpecificScope(tag, inButtonScopeSet);
};

HTMLParser.ElementStack.prototype.inListItemScope = function(tag) {
  return this.inSpecificScope(tag, inListItemScopeSet);
};

HTMLParser.ElementStack.prototype.inTableScope = function(tag) {
  return this.inSpecificScope(tag, inTableScopeSet);
};

HTMLParser.ElementStack.prototype.inSelectScope = function(tag) {
  // Can't implement this one with inSpecificScope, since it involves
  // a set defined by inverting another set. So implement manually.
  for(var i = this.elements.length-1; i >= 0; i--) {
    var elt = this.elements[i];
    if (elt.namespaceURI !== NAMESPACE.HTML) return false;
    var localname = elt.localName;
    if (localname === tag) return true;
    if (localname !== "optgroup" && localname !== "option")
      return false;
  }
  return false;
};

HTMLParser.ElementStack.prototype.generateImpliedEndTags = function(butnot, thorough) {
  var endTagSet = thorough ? thoroughImpliedEndTagsSet : impliedEndTagsSet;
  for(var i = this.elements.length-1; i >= 0; i--) {
    var e = this.elements[i];
    if (butnot && isA(e, butnot)) break;
    if (!isA(this.elements[i], endTagSet)) break;
  }

  this.elements.length = i+1;
  this.top = this.elements[i];
};

/***
 * The ActiveFormattingElements class
 */
HTMLParser.ActiveFormattingElements = function AFE() {
  this.list = []; // elements
  this.attrs = []; // attribute tokens for cloning
};

HTMLParser.ActiveFormattingElements.prototype.MARKER = { localName: "|" };

/*
// For debugging
HTMLParser.ActiveFormattingElements.prototype.toString = function() {
  return "AFE: " +
  this.list.map(function(e) { return e.localName; }).join("-");
}
*/

HTMLParser.ActiveFormattingElements.prototype.insertMarker = function() {
  this.list.push(this.MARKER);
  this.attrs.push(this.MARKER);
};

HTMLParser.ActiveFormattingElements.prototype.push = function(elt, attrs) {
  // Scan backwards: if there are already 3 copies of this element
  // before we encounter a marker, then drop the last one
  var count = 0;
  for(var i = this.list.length-1; i >= 0; i--) {
    if (this.list[i] === this.MARKER) break;
    // equal() is defined below
    if (equal(elt, this.list[i], this.attrs[i])) {
      count++;
      if (count === 3) {
        this.list.splice(i, 1);
        this.attrs.splice(i, 1);
        break;
      }
    }
  }


  // Now push the element onto the list
  this.list.push(elt);

  // Copy the attributes and push those on, too
  var attrcopy = [];
  for(var ii = 0; ii < attrs.length; ii++) {
    attrcopy[ii] = attrs[ii];
  }

  this.attrs.push(attrcopy);

  // This function defines equality of two elements for the purposes
  // of the AFE list.  Note that it compares the new elements
  // attributes to the saved array of attributes associated with
  // the old element because a script could have changed the
  // old element's set of attributes
  function equal(newelt, oldelt, oldattrs) {
    if (newelt.localName !== oldelt.localName) return false;
    if (newelt._numattrs !== oldattrs.length) return false;
    for(var i = 0, n = oldattrs.length; i < n; i++) {
      var oldname = oldattrs[i][0];
      var oldval = oldattrs[i][1];
      if (!newelt.hasAttribute(oldname)) return false;
      if (newelt.getAttribute(oldname) !== oldval) return false;
    }
    return true;
  }
};

HTMLParser.ActiveFormattingElements.prototype.clearToMarker = function() {
  for(var i = this.list.length-1; i >= 0; i--) {
    if (this.list[i] === this.MARKER) break;
  }
  if (i < 0) i = 0;
  this.list.length = i;
  this.attrs.length = i;
};

// Find and return the last element with the specified tag between the
// end of the list and the last marker on the list.
// Used when parsing <a> in_body_mode()
HTMLParser.ActiveFormattingElements.prototype.findElementByTag = function(tag) {
  for(var i = this.list.length-1; i >= 0; i--) {
    var elt = this.list[i];
    if (elt === this.MARKER) break;
    if (elt.localName === tag) return elt;
  }
  return null;
};

HTMLParser.ActiveFormattingElements.prototype.indexOf = function(e) {
  return this.list.lastIndexOf(e);
};

// Find the element e in the list and remove it
// Used when parsing <a> in_body()
HTMLParser.ActiveFormattingElements.prototype.remove = function(e) {
  var idx = this.list.lastIndexOf(e);
  if (idx !== -1) {
    this.list.splice(idx, 1);
    this.attrs.splice(idx, 1);
  }
};

// Find element a in the list and replace it with element b
// XXX: Do I need to handle attributes here?
HTMLParser.ActiveFormattingElements.prototype.replace = function(a, b, attrs) {
  var idx = this.list.lastIndexOf(a);
  if (idx !== -1) {
    this.list[idx] = b;
    this.attrs[idx] = attrs;
  }
};

// Find a in the list and insert b after it
// This is only used for insert a bookmark object, so the
// attrs array doesn't really matter
HTMLParser.ActiveFormattingElements.prototype.insertAfter = function(a,b) {
  var idx = this.list.lastIndexOf(a);
  if (idx !== -1) {
    this.list.splice(idx, 0, b);
    this.attrs.splice(idx, 0, b);
  }
};




/***
 * This is the parser factory function. It is the return value of
 * the outer closure that it is defined within.  Most of the parser
 * implementation details are inside this function.
 */
function HTMLParser(address, fragmentContext, options) {
  /***
   * These are the parser's state variables
   */
  // Scanner state
  var chars = null;
  var numchars = 0; // Length of chars
  var nextchar = 0; // Index of next char
  var input_complete = false; // Becomes true when end() called.
  var scanner_skip_newline = false; // If previous char was CR
  var reentrant_invocations = 0;
  var saved_scanner_state = [];
  var leftovers = "";
  var first_batch = true;
  var paused = 0; // Becomes non-zero while loading scripts


  // Tokenizer state
  var tokenizer = data_state; // Current tokenizer state
  var return_state;
  var character_reference_code;
  var tagnamebuf = "";
  var lasttagname = ""; // holds the target end tag for text states
  var tempbuf = [];
  var attrnamebuf = "";
  var attrvaluebuf = "";
  var commentbuf = [];
  var doctypenamebuf = [];
  var doctypepublicbuf = [];
  var doctypesystembuf = [];
  var attributes = [];
  var is_end_tag = false;

  // Tree builder state
  var parser = initial_mode; // Current insertion mode
  var originalInsertionMode = null; // A saved insertion mode
  var templateInsertionModes = []; // Stack of template insertion modes.
  var stack = new HTMLParser.ElementStack(); // Stack of open elements
  var afe = new HTMLParser.ActiveFormattingElements(); // mis-nested tags
  var fragment = (fragmentContext!==undefined); // For innerHTML, etc.
  var head_element_pointer = null;
  var form_element_pointer = null;
  var scripting_enabled = true;
  if (fragmentContext) {
	scripting_enabled = fragmentContext.ownerDocument._scripting_enabled;
  }
  if (options && options.scripting_enabled === false)
    scripting_enabled = false;
  var frameset_ok = true;
  var force_quirks = false;
  var pending_table_text;
  var text_integration_mode; // XXX a spec bug workaround?

  // A single run of characters, buffered up to be sent to
  // the parser as a single string.
  var textrun = [];
  var textIncludesNUL = false;
  var ignore_linefeed = false;

  /***
   * This is the parser object that will be the return value of this
   * factory function, which is some 5000 lines below.
   * Note that the variable "parser" is the current state of the
   * parser's state machine.  This variable "htmlparser" is the
   * return value and defines the public API of the parser
   */
  var htmlparser = {
    document: function() {
      return doc;
    },

    // Convenience function for internal use. Can only be called once,
    // as it removes the nodes from `doc` to add them to fragment.
    _asDocumentFragment: function() {
      var frag = doc.createDocumentFragment();
      var root = doc.firstChild;
      while(root.hasChildNodes()) {
        frag.appendChild(root.firstChild);
      }
      return frag;
    },

    // Internal function used from HTMLScriptElement to pause the
    // parser while a script is being loaded from the network
    pause: function() {
      // print("pausing parser");
      paused++;
    },

    // Called when a script finishes loading
    resume: function() {
      // print("resuming parser");
      paused--;
      // XXX: added this to force a resumption.
      // Is this the right thing to do?
      this.parse("");
    },

    // Parse the HTML text s.
    // The second argument should be true if there is no more
    // text to be parsed, and should be false or omitted otherwise.
    // The second argument must not be set for recursive invocations
    // from document.write()
    parse: function(s, end, shouldPauseFunc) {
      var moreToDo;

      // If we're paused, remember the text to parse, but
      // don't parse it now.
      // (Don't invoke shouldPauseFunc because we haven't handled 'end' yet.)
      if (paused > 0) {
        leftovers += s;
        return true; // more to do
      }


      if (reentrant_invocations === 0) {
        // A normal, top-level invocation
        if (leftovers) {
          s = leftovers + s;
          leftovers = "";
        }

        // Add a special marker character to the end of
        // the buffer.  If the scanner is at the end of
        // the buffer and input_complete is set, then this
        // character will transform into an EOF token.
        // Having an actual character that represents EOF
        // in the character buffer makes lookahead regexp
        // matching work more easily, and this is
        // important for character references.
        if (end) {
          s += "\uFFFF";
          input_complete = true; // Makes scanChars() send EOF
        }

        chars = s;
        numchars = s.length;
        nextchar = 0;

        if (first_batch) {
          // We skip a leading Byte Order Mark (\uFEFF)
          // on first batch of text we're given
          first_batch = false;
          if (chars.charCodeAt(0) === 0xFEFF) nextchar = 1;
        }

        reentrant_invocations++;
        moreToDo = scanChars(shouldPauseFunc);
        leftovers = chars.substring(nextchar, numchars);
        reentrant_invocations--;
      }
      else {
        // This is the re-entrant case, which we have to
        // handle a little differently.
        reentrant_invocations++;

        // Save current scanner state
        saved_scanner_state.push(chars, numchars, nextchar);

        // Set new scanner state
        chars = s;
        numchars = s.length;
        nextchar = 0;

        // Now scan as many of these new chars as we can
        scanChars();
        moreToDo = false;

        leftovers = chars.substring(nextchar, numchars);

        // restore old scanner state
        nextchar = saved_scanner_state.pop();
        numchars = saved_scanner_state.pop();
        chars = saved_scanner_state.pop();

        // If there were leftover chars from this invocation
        // insert them into the pending invocation's buffer
        // and trim already processed chars at the same time
        if (leftovers) {
          chars = leftovers + chars.substring(nextchar);
          numchars = chars.length;
          nextchar = 0;
          leftovers = "";
        }

        // Decrement the counter
        reentrant_invocations--;
      }
      return moreToDo;
    }
  };


  // This is the document we'll be building up
  var doc = new Document(true, address);

  // The document needs to know about the parser, for document.write().
  // This _parser property will be deleted when we're done parsing.
  doc._parser = htmlparser;

  // XXX I think that any document we use this parser on should support
  // scripts. But I may need to configure that through a parser parameter
  // Only documents with windows ("browsing contexts" to be precise)
  // allow scripting.
  doc._scripting_enabled = scripting_enabled;


  /***
   * The actual code of the HTMLParser() factory function begins here.
   */

  if (fragmentContext) { // for innerHTML parsing
    if (fragmentContext.ownerDocument._quirks)
      doc._quirks = true;
    if (fragmentContext.ownerDocument._limitedQuirks)
      doc._limitedQuirks = true;

    // Set the initial tokenizer state
    if (fragmentContext.namespaceURI === NAMESPACE.HTML) {
      switch(fragmentContext.localName) {
      case "title":
      case "textarea":
        tokenizer = rcdata_state;
        break;
      case "style":
      case "xmp":
      case "iframe":
      case "noembed":
      case "noframes":
      case "script":
      case "plaintext":
        tokenizer = plaintext_state;
        break;
      case "noscript":
        if (scripting_enabled)
          tokenizer = plaintext_state;
      }
    }

    var root = doc.createElement("html");
    doc._appendChild(root);
    stack.push(root);
    if (fragmentContext instanceof impl.HTMLTemplateElement) {
      templateInsertionModes.push(in_template_mode);
    }
    resetInsertionMode();

    for(var e = fragmentContext; e !== null; e = e.parentElement) {
      if (e instanceof impl.HTMLFormElement) {
        form_element_pointer = e;
        break;
      }
    }
  }

  /***
   * Scanner functions
   */
  // Loop through the characters in chars, and pass them one at a time
  // to the tokenizer FSM. Return when no more characters can be processed
  // (This may leave 1 or more characters in the buffer: like a CR
  // waiting to see if the next char is LF, or for states that require
  // lookahead...)
  function scanChars(shouldPauseFunc) {
    var codepoint, s, pattern, eof;

    while(nextchar < numchars) {

      // If we just tokenized a </script> tag, then the paused flag
      // may have been set to tell us to stop tokenizing while
      // the script is loading
      if (paused > 0 || (shouldPauseFunc && shouldPauseFunc())) {
        return true;
      }


      switch(typeof tokenizer.lookahead) {
      case 'undefined':
        codepoint = chars.charCodeAt(nextchar++);
        if (scanner_skip_newline) {
          scanner_skip_newline = false;
          if (codepoint === 0x000A) {
            nextchar++;
            continue;
          }
        }
        switch(codepoint) {
        case 0x000D:
          // CR always turns into LF, but if the next character
          // is LF, then that second LF is skipped.
          if (nextchar < numchars) {
            if (chars.charCodeAt(nextchar) === 0x000A)
              nextchar++;
          }
          else {
            // We don't know the next char right now, so we
            // can't check if it is a LF.  So set a flag
            scanner_skip_newline = true;
          }

          // In either case, emit a LF
          tokenizer(0x000A);

          break;
        case 0xFFFF:
          if (input_complete && nextchar === numchars) {
            tokenizer(EOF); // codepoint will be 0xFFFF here
            break;
          }
          /* falls through */
        default:
          tokenizer(codepoint);
          break;
        }
        break;

      case 'number':
        codepoint = chars.charCodeAt(nextchar);

        // The only tokenizer states that require fixed lookahead
        // only consume alphanum characters, so we don't have
        // to worry about CR and LF in this case

        // tokenizer wants n chars of lookahead
        var n = tokenizer.lookahead;
        var needsString = true;
        if (n < 0) {
          needsString = false;
          n = -n;
        }

        if (n < numchars - nextchar) {
          // If we can look ahead that far
          s = needsString ? chars.substring(nextchar, nextchar+n) : null;
          eof = false;
        }
        else { // if we don't have that many characters
          if (input_complete) { // If no more are coming
            // Just return what we have
            s = needsString ? chars.substring(nextchar, numchars) : null;
            eof = true;
            if (codepoint === 0xFFFF && nextchar === numchars-1)
              codepoint = EOF;
          }
          else {
            // Return now and wait for more chars later
            return true;
          }
        }
        tokenizer(codepoint, s, eof);
        break;
      case 'string':
        codepoint = chars.charCodeAt(nextchar);

        // tokenizer wants characters up to a matching string
        pattern = tokenizer.lookahead;
        var pos = chars.indexOf(pattern, nextchar);
        if (pos !== -1) {
          s = chars.substring(nextchar, pos + pattern.length);
          eof = false;
        }
        else {  // No match
          // If more characters coming, wait for them
          if (!input_complete) return true;

          // Otherwise, we've got to return what we've got
          s = chars.substring(nextchar, numchars);
          if (codepoint === 0xFFFF && nextchar === numchars-1)
            codepoint = EOF;
          eof = true;
        }

        // The tokenizer states that require this kind of
        // lookahead have to be careful to handle CR characters
        // correctly
        tokenizer(codepoint, s, eof);
        break;
      }
    }
    return false; // no more characters to scan!
  }


  /***
   * Tokenizer utility functions
   */
  function addAttribute(name,value) {
    // Make sure there isn't already an attribute with this name
    // If there is, ignore this one.
    for(var i = 0; i < attributes.length; i++) {
      if (attributes[i][0] === name) return;
    }

    if (value !== undefined) {
      attributes.push([name, value]);
    }
    else {
      attributes.push([name]);
    }
  }

  // Shortcut for simple attributes
  function handleSimpleAttribute() {
    SIMPLEATTR.lastIndex = nextchar-1;
    var matched = SIMPLEATTR.exec(chars);
    if (!matched) throw new Error("should never happen");
    var name = matched[1];
    if (!name) return false;
    var value = matched[2];
    var len = value.length;
    switch(value[0]) {
    case '"':
    case "'":
      value = value.substring(1, len-1);
      nextchar += (matched[0].length-1);
      tokenizer = after_attribute_value_quoted_state;
      break;
    default:
      tokenizer = before_attribute_name_state;
      nextchar += (matched[0].length-1);
      value = value.substring(0, len-1);
      break;
    }

    // Make sure there isn't already an attribute with this name
    // If there is, ignore this one.
    for(var i = 0; i < attributes.length; i++) {
      if (attributes[i][0] === name) return true;
    }

    attributes.push([name, value]);
    return true;
  }

  function beginTagName() {
    is_end_tag = false;
    tagnamebuf = "";
    attributes.length = 0;
  }
  function beginEndTagName() {
    is_end_tag = true;
    tagnamebuf = "";
    attributes.length = 0;
  }

  function beginTempBuf() { tempbuf.length = 0; }
  function beginAttrName() { attrnamebuf = ""; }
  function beginAttrValue() { attrvaluebuf = ""; }
  function beginComment() { commentbuf.length = 0; }
  function beginDoctype() {
    doctypenamebuf.length = 0;
    doctypepublicbuf = null;
    doctypesystembuf = null;
  }
  function beginDoctypePublicId() { doctypepublicbuf = []; }
  function beginDoctypeSystemId() { doctypesystembuf = []; }
  function forcequirks() { force_quirks = true; }
  function cdataAllowed() {
    return stack.top &&
      stack.top.namespaceURI !== "http://www.w3.org/1999/xhtml";
  }

  // Return true if the codepoints in the specified buffer match the
  // characters of lasttagname
  function appropriateEndTag(buf) {
    return lasttagname === buf;
  }

  function flushText() {
    if (textrun.length > 0) {
      var s = buf2str(textrun);
      textrun.length = 0;

      if (ignore_linefeed) {
        ignore_linefeed = false;
        if (s[0] === "\n") s = s.substring(1);
        if (s.length === 0) return;
      }

      insertToken(TEXT, s);
      textIncludesNUL = false;
    }
    ignore_linefeed = false;
  }

  // Consume chars matched by the pattern and return them as a string. Starts
  // matching at the current position, so users should drop the current char
  // otherwise.
  function getMatchingChars(pattern) {
    pattern.lastIndex = nextchar - 1;
    var match = pattern.exec(chars);
    if (match && match.index === nextchar - 1) {
      match = match[0];
      nextchar += match.length - 1;
      /* Careful!  Make sure we haven't matched the EOF character! */
      if (input_complete && nextchar === numchars) {
        // Oops, backup one.
        match = match.slice(0, -1);
        nextchar--;
      }
      return match;
    } else {
      throw new Error("should never happen");
    }
  }

  // emit a string of chars that match a regexp
  // Returns false if no chars matched.
  function emitCharsWhile(pattern) {
    pattern.lastIndex = nextchar-1;
    var match = pattern.exec(chars)[0];
    if (!match) return false;
    emitCharString(match);
    nextchar += match.length - 1;
    return true;
  }

  // This is used by CDATA sections
  function emitCharString(s) {
    if (textrun.length > 0) flushText();

    if (ignore_linefeed) {
      ignore_linefeed = false;
      if (s[0] === "\n") s = s.substring(1);
      if (s.length === 0) return;
    }

    insertToken(TEXT, s);
  }

  function emitTag() {
    if (is_end_tag) insertToken(ENDTAG, tagnamebuf);
    else {
      // Remember the last open tag we emitted
      var tagname = tagnamebuf;
      tagnamebuf = "";
      lasttagname = tagname;
      insertToken(TAG, tagname, attributes);
    }
  }


  // A shortcut: look ahead and if this is a open or close tag
  // in lowercase with no spaces and no attributes, just emit it now.
  function emitSimpleTag() {
    if (nextchar === numchars) { return false; /* not even 1 char left */ }
    SIMPLETAG.lastIndex = nextchar;
    var matched = SIMPLETAG.exec(chars);
    if (!matched) throw new Error("should never happen");
    var tagname = matched[2];
    if (!tagname) return false;
    var endtag = matched[1];
    if (endtag) {
      nextchar += (tagname.length+2);
      insertToken(ENDTAG, tagname);
    }
    else {
      nextchar += (tagname.length+1);
      lasttagname = tagname;
      insertToken(TAG, tagname, NOATTRS);
    }
    return true;
  }

  function emitSelfClosingTag() {
    if (is_end_tag) insertToken(ENDTAG, tagnamebuf, null, true);
    else {
      insertToken(TAG, tagnamebuf, attributes, true);
    }
  }

  function emitDoctype() {
    insertToken(DOCTYPE,
          buf2str(doctypenamebuf),
          doctypepublicbuf ? buf2str(doctypepublicbuf) : undefined,
          doctypesystembuf ? buf2str(doctypesystembuf) : undefined);
  }

  function emitEOF() {
    flushText();
    parser(EOF); // EOF never goes to insertForeignContent()
    doc.modclock = 1; // Start tracking modifications
  }

  // Insert a token, either using the current parser insertion mode
  // (for HTML stuff) or using the insertForeignToken() method.
  var insertToken = htmlparser.insertToken = function insertToken(t, value, arg3, arg4) {
    flushText();
    var current = stack.top;

    if (!current || current.namespaceURI === NAMESPACE.HTML) {
      // This is the common case
      parser(t, value, arg3, arg4);
    }
    else {
      // Otherwise we may need to insert this token as foreign content
      if (t !== TAG && t !== TEXT) {
        insertForeignToken(t, value, arg3, arg4);
      }
      else {
        // But in some cases we treat it as regular content
        if ((isMathmlTextIntegrationPoint(current) &&
           (t === TEXT ||
            (t === TAG &&
             value !== "mglyph" && value !== "malignmark"))) ||
          (t === TAG &&
           value === "svg" &&
           current.namespaceURI === NAMESPACE.MATHML &&
           current.localName === "annotation-xml") ||
          isHTMLIntegrationPoint(current)) {

          // XXX: the text_integration_mode stuff is an
          // attempted bug workaround of mine
          text_integration_mode = true;
          parser(t, value, arg3, arg4);
          text_integration_mode = false;
        }
        // Otherwise it is foreign content
        else {
          insertForeignToken(t, value, arg3, arg4);
        }
      }
    }
  };


  /***
   * Tree building utility functions
   */
  function insertComment(data) {
    var parent = stack.top;
    if (foster_parent_mode && isA(parent, tablesectionrowSet)) {
      fosterParent(function(doc) { return doc.createComment(data); });
    } else {
      // "If the adjusted insertion location is inside a template element,
      // let it instead be inside the template element's template contents"
      if (parent instanceof impl.HTMLTemplateElement) {
        parent = parent.content;
      }
      parent._appendChild(parent.ownerDocument.createComment(data));
    }
  }

  function insertText(s) {
    var parent = stack.top;
    if (foster_parent_mode && isA(parent, tablesectionrowSet)) {
      fosterParent(function(doc) { return doc.createTextNode(s); });
    } else {
      // "If the adjusted insertion location is inside a template element,
      // let it instead be inside the template element's template contents"
      if (parent instanceof impl.HTMLTemplateElement) {
        parent = parent.content;
      }
      // "If there is a Text node immediately before the adjusted insertion
      // location, then append data to that Text node's data."
      var lastChild = parent.lastChild;
      if (lastChild && lastChild.nodeType === Node.TEXT_NODE) {
        lastChild.appendData(s);
      } else {
        parent._appendChild(parent.ownerDocument.createTextNode(s));
      }
    }
  }

  function createHTMLElt(doc, name, attrs) {
    // Create the element this way, rather than with
    // doc.createElement because createElement() does error
    // checking on the element name that we need to avoid here.
    var elt = html.createElement(doc, name, null);

    if (attrs) {
      for(var i = 0, n = attrs.length; i < n; i++) {
        // Use the _ version to avoid testing the validity
        // of the attribute name
        elt._setAttribute(attrs[i][0], attrs[i][1]);
      }
    }
    // XXX
    // If the element is a resettable form element,
    // run its reset algorithm now
    // XXX
    // handle case where form-element-pointer is not null
    return elt;
  }

  // The in_table insertion mode turns on this flag, and that makes
  // insertHTMLElement use the foster parenting algorithm for elements
  // tags inside a table
  var foster_parent_mode = false;

  function insertHTMLElement(name, attrs) {
    var elt = insertElement(function(doc) {
      return createHTMLElt(doc, name, attrs);
    });

    // XXX
    // If this is a form element, set its form attribute property here
    if (isA(elt, formassociatedSet)) {
      elt._form = form_element_pointer;
    }

    return elt;
  }

  // Insert the element into the open element or foster parent it
  function insertElement(eltFunc) {
    var elt;
    if (foster_parent_mode && isA(stack.top, tablesectionrowSet)) {
      elt = fosterParent(eltFunc);
    }
    else if (stack.top instanceof impl.HTMLTemplateElement) {
      // "If the adjusted insertion location is inside a template element,
      // let it instead be inside the template element's template contents"
      elt = eltFunc(stack.top.content.ownerDocument);
      stack.top.content._appendChild(elt);
    } else {
      elt = eltFunc(stack.top.ownerDocument);
      stack.top._appendChild(elt);
    }

    stack.push(elt);
    return elt;
  }

  function insertForeignElement(name, attrs, ns) {
    return insertElement(function(doc) {
      // We need to prevent createElementNS from trying to parse `name` as a
      // `qname`, so use an internal Document#_createElementNS() interface.
      var elt = doc._createElementNS(name, ns, null);
      if (attrs) {
        for(var i = 0, n = attrs.length; i < n; i++) {
          var attr = attrs[i];
          if (attr.length === 2)
            elt._setAttribute(attr[0], attr[1]);
          else {
            elt._setAttributeNS(attr[2], attr[0], attr[1]);
          }
        }
      }
      return elt;
    });
  }

  function lastElementOfType(type) {
    for(var i = stack.elements.length-1; i >= 0; i--) {
      if (stack.elements[i] instanceof type) {
        return i;
      }
    }
    return -1;
  }

  function fosterParent(eltFunc) {
    var parent, before, lastTable = -1, lastTemplate = -1, elt;

    lastTable = lastElementOfType(impl.HTMLTableElement);
    lastTemplate = lastElementOfType(impl.HTMLTemplateElement);

    if (lastTemplate >= 0 && (lastTable < 0 || lastTemplate > lastTable)) {
      parent = stack.elements[lastTemplate];
    } else if (lastTable >= 0) {
      parent = stack.elements[lastTable].parentNode;
      if (parent) {
        before = stack.elements[lastTable];
      } else {
        parent = stack.elements[lastTable - 1];
      }
    }
    if (!parent) parent = stack.elements[0]; // the `html` element.

    // "If the adjusted insertion location is inside a template element,
    // let it instead be inside the template element's template contents"
    if (parent instanceof impl.HTMLTemplateElement) {
      parent = parent.content;
    }
    // Create element in the appropriate document.
    elt = eltFunc(parent.ownerDocument);

    if (elt.nodeType === Node.TEXT_NODE) {
      var prev;
      if (before) prev = before.previousSibling;
      else prev = parent.lastChild;
      if (prev && prev.nodeType === Node.TEXT_NODE) {
        prev.appendData(elt.data);
        return elt;
      }
    }
    if (before)
      parent.insertBefore(elt, before);
    else
      parent._appendChild(elt);
    return elt;
  }


  function resetInsertionMode() {
    var last = false;
    for(var i = stack.elements.length-1; i >= 0; i--) {
      var node = stack.elements[i];
      if (i === 0) {
        last = true;
        if (fragment) {
          node = fragmentContext;
        }
      }
      if (node.namespaceURI === NAMESPACE.HTML) {
        var tag = node.localName;
        switch(tag) {
        case "select":
          for(var j = i; j > 0; ) {
            var ancestor = stack.elements[--j];
            if (ancestor instanceof impl.HTMLTemplateElement) {
              break;
            } else if (ancestor instanceof impl.HTMLTableElement) {
              parser = in_select_in_table_mode;
              return;
            }
          }
          parser = in_select_mode;
          return;
        case "tr":
          parser = in_row_mode;
          return;
        case "tbody":
        case "tfoot":
        case "thead":
          parser = in_table_body_mode;
          return;
        case "caption":
          parser = in_caption_mode;
          return;
        case "colgroup":
          parser = in_column_group_mode;
          return;
        case "table":
          parser = in_table_mode;
          return;
        case "template":
          parser = templateInsertionModes[templateInsertionModes.length-1];
          return;
        case "body":
          parser = in_body_mode;
          return;
        case "frameset":
          parser = in_frameset_mode;
          return;
        case "html":
          if (head_element_pointer === null) {
            parser = before_head_mode;
          } else {
            parser = after_head_mode;
          }
          return;
        default:
          if (!last) {
            if (tag === "head") {
              parser = in_head_mode;
              return;
            }
            if (tag === "td" || tag === "th") {
              parser = in_cell_mode;
              return;
            }
          }
        }
      }
      if (last) {
        parser = in_body_mode;
        return;
      }
    }
  }


  function parseRawText(name, attrs) {
    insertHTMLElement(name, attrs);
    tokenizer = rawtext_state;
    originalInsertionMode = parser;
    parser = text_mode;
  }

  function parseRCDATA(name, attrs) {
    insertHTMLElement(name, attrs);
    tokenizer = rcdata_state;
    originalInsertionMode = parser;
    parser = text_mode;
  }

  // Make a copy of element i on the list of active formatting
  // elements, using its original attributes, not current
  // attributes (which may have been modified by a script)
  function afeclone(doc, i) {
    return {
      elt: createHTMLElt(doc, afe.list[i].localName, afe.attrs[i]),
      attrs: afe.attrs[i],
    };
  }


  function afereconstruct() {
    if (afe.list.length === 0) return;
    var entry = afe.list[afe.list.length-1];
    // If the last is a marker , do nothing
    if (entry === afe.MARKER) return;
    // Or if it is an open element, do nothing
    if (stack.elements.lastIndexOf(entry) !== -1) return;

    // Loop backward through the list until we find a marker or an
    // open element, and then move forward one from there.
    for(var i = afe.list.length-2; i >= 0; i--) {
      entry = afe.list[i];
      if (entry === afe.MARKER) break;
      if (stack.elements.lastIndexOf(entry) !== -1) break;
    }

    // Now loop forward, starting from the element after the current
    // one, recreating formatting elements and pushing them back onto
    // the list of open elements
    for(i = i+1; i < afe.list.length; i++) {
      var newelt = insertElement(function(doc) { return afeclone(doc, i).elt; });
      afe.list[i] = newelt;
    }
  }

  // Used by the adoptionAgency() function
  var BOOKMARK = {localName:"BM"};

  function adoptionAgency(tag) {
    // If the current node is an HTML element whose tag name is subject,
    // and the current node is not in the list of active formatting
    // elements, then pop the current node off the stack of open
    // elements and abort these steps.
    if (isA(stack.top, tag) && afe.indexOf(stack.top) === -1) {
      stack.pop();
      return true; // no more handling required
    }

    // Let outer loop counter be zero.
    var outer = 0;

    // Outer loop: If outer loop counter is greater than or
    // equal to eight, then abort these steps.
    while(outer < 8) {
      // Increment outer loop counter by one.
      outer++;

      // Let the formatting element be the last element in the list
      // of active formatting elements that: is between the end of
      // the list and the last scope marker in the list, if any, or
      // the start of the list otherwise, and has the same tag name
      // as the token.
      var fmtelt = afe.findElementByTag(tag);

      // If there is no such node, then abort these steps and instead
      // act as described in the "any other end tag" entry below.
      if (!fmtelt) {
        return false; // false means handle by the default case
      }

      // Otherwise, if there is such a node, but that node is not in
      // the stack of open elements, then this is a parse error;
      // remove the element from the list, and abort these steps.
      var index = stack.elements.lastIndexOf(fmtelt);
      if (index === -1) {
        afe.remove(fmtelt);
        return true;   // true means no more handling required
      }

      // Otherwise, if there is such a node, and that node is also in
      // the stack of open elements, but the element is not in scope,
      // then this is a parse error; ignore the token, and abort
      // these steps.
      if (!stack.elementInScope(fmtelt)) {
        return true;
      }

      // Let the furthest block be the topmost node in the stack of
      // open elements that is lower in the stack than the formatting
      // element, and is an element in the special category. There
      // might not be one.
      var furthestblock = null, furthestblockindex;
      for(var i = index+1; i < stack.elements.length; i++) {
        if (isA(stack.elements[i], specialSet)) {
          furthestblock = stack.elements[i];
          furthestblockindex = i;
          break;
        }
      }

      // If there is no furthest block, then the UA must skip the
      // subsequent steps and instead just pop all the nodes from the
      // bottom of the stack of open elements, from the current node
      // up to and including the formatting element, and remove the
      // formatting element from the list of active formatting
      // elements.
      if (!furthestblock) {
        stack.popElement(fmtelt);
        afe.remove(fmtelt);
        return true;
      }
      else {
        // Let the common ancestor be the element immediately above
        // the formatting element in the stack of open elements.
        var ancestor = stack.elements[index-1];

        // Let a bookmark note the position of the formatting
        // element in the list of active formatting elements
        // relative to the elements on either side of it in the
        // list.
        afe.insertAfter(fmtelt, BOOKMARK);

        // Let node and last node be the furthest block.
        var node = furthestblock;
        var lastnode = furthestblock;
        var nodeindex = furthestblockindex;
        var nodeafeindex;

        // Let inner loop counter be zero.
        var inner = 0;

        while (true) {

          // Increment inner loop counter by one.
          inner++;

          // Let node be the element immediately above node in
          // the stack of open elements, or if node is no longer
          // in the stack of open elements (e.g. because it got
          // removed by this algorithm), the element that was
          // immediately above node in the stack of open elements
          // before node was removed.
          node = stack.elements[--nodeindex];

          // If node is the formatting element, then go
          // to the next step in the overall algorithm.
          if (node === fmtelt) break;

          // If the inner loop counter is greater than three and node
          // is in the list of active formatting elements, then remove
          // node from the list of active formatting elements.
          nodeafeindex = afe.indexOf(node);
          if (inner > 3 && nodeafeindex !== -1) {
            afe.remove(node);
            nodeafeindex = -1;
          }

          // If node is not in the list of active formatting
          // elements, then remove node from the stack of open
          // elements and then go back to the step labeled inner
          // loop.
          if (nodeafeindex === -1) {
            stack.removeElement(node);
            continue;
          }

          // Create an element for the token for which the
          // element node was created with common ancestor as
          // the intended parent, replace the entry for node
          // in the list of active formatting elements with an
          // entry for the new element, replace the entry for
          // node in the stack of open elements with an entry for
          // the new element, and let node be the new element.
          var newelt = afeclone(ancestor.ownerDocument, nodeafeindex);
          afe.replace(node, newelt.elt, newelt.attrs);
          stack.elements[nodeindex] = newelt.elt;
          node = newelt.elt;

          // If last node is the furthest block, then move the
          // aforementioned bookmark to be immediately after the
          // new node in the list of active formatting elements.
          if (lastnode === furthestblock) {
            afe.remove(BOOKMARK);
            afe.insertAfter(newelt.elt, BOOKMARK);
          }

          // Insert last node into node, first removing it from
          // its previous parent node if any.
          node._appendChild(lastnode);

          // Let last node be node.
          lastnode = node;
        }

        // If the common ancestor node is a table, tbody, tfoot,
        // thead, or tr element, then, foster parent whatever last
        // node ended up being in the previous step, first removing
        // it from its previous parent node if any.
        if (foster_parent_mode && isA(ancestor, tablesectionrowSet)) {
          fosterParent(function() { return lastnode; });
        }
        // Otherwise, append whatever last node ended up being in
        // the previous step to the common ancestor node, first
        // removing it from its previous parent node if any.
        else if (ancestor instanceof impl.HTMLTemplateElement) {
          ancestor.content._appendChild(lastnode);
        } else {
          ancestor._appendChild(lastnode);
        }

        // Create an element for the token for which the
        // formatting element was created, with furthest block
        // as the intended parent.
        var newelt2 = afeclone(furthestblock.ownerDocument, afe.indexOf(fmtelt));

        // Take all of the child nodes of the furthest block and
        // append them to the element created in the last step.
        while(furthestblock.hasChildNodes()) {
          newelt2.elt._appendChild(furthestblock.firstChild);
        }

        // Append that new element to the furthest block.
        furthestblock._appendChild(newelt2.elt);

        // Remove the formatting element from the list of active
        // formatting elements, and insert the new element into the
        // list of active formatting elements at the position of
        // the aforementioned bookmark.
        afe.remove(fmtelt);
        afe.replace(BOOKMARK, newelt2.elt, newelt2.attrs);

        // Remove the formatting element from the stack of open
        // elements, and insert the new element into the stack of
        // open elements immediately below the position of the
        // furthest block in that stack.
        stack.removeElement(fmtelt);
        var pos = stack.elements.lastIndexOf(furthestblock);
        stack.elements.splice(pos+1, 0, newelt2.elt);
      }
    }

    return true;
  }

  // We do this when we get /script in in_text_mode
  function handleScriptEnd() {
    // XXX:
    // This is just a stub implementation right now and doesn't run scripts.
    // Getting this method right involves the event loop, URL resolution
    // script fetching etc. For now I just want to be able to parse
    // documents and test the parser.

    //var script = stack.top;
    stack.pop();
    parser = originalInsertionMode;
    //script._prepare();
    return;

    // XXX: here is what this method is supposed to do

    // Provide a stable state.

    // Let script be the current node (which will be a script
    // element).

    // Pop the current node off the stack of open elements.

    // Switch the insertion mode to the original insertion mode.

    // Let the old insertion point have the same value as the current
    // insertion point. Let the insertion point be just before the
    // next input character.

    // Increment the parser's script nesting level by one.

    // Prepare the script. This might cause some script to execute,
    // which might cause new characters to be inserted into the
    // tokenizer, and might cause the tokenizer to output more tokens,
    // resulting in a reentrant invocation of the parser.

    // Decrement the parser's script nesting level by one. If the
    // parser's script nesting level is zero, then set the parser
    // pause flag to false.

    // Let the insertion point have the value of the old insertion
    // point. (In other words, restore the insertion point to its
    // previous value. This value might be the "undefined" value.)

    // At this stage, if there is a pending parsing-blocking script,
    // then:

    // If the script nesting level is not zero:

    //   Set the parser pause flag to true, and abort the processing
    //   of any nested invocations of the tokenizer, yielding
    //   control back to the caller. (Tokenization will resume when
    //   the caller returns to the "outer" tree construction stage.)

    //   The tree construction stage of this particular parser is
    //   being called reentrantly, say from a call to
    //   document.write().

    // Otherwise:

    //     Run these steps:

    //       Let the script be the pending parsing-blocking
    //       script. There is no longer a pending
    //       parsing-blocking script.

    //       Block the tokenizer for this instance of the HTML
    //       parser, such that the event loop will not run tasks
    //       that invoke the tokenizer.

    //       If the parser's Document has a style sheet that is
    //       blocking scripts or the script's "ready to be
    //       parser-executed" flag is not set: spin the event
    //       loop until the parser's Document has no style sheet
    //       that is blocking scripts and the script's "ready to
    //       be parser-executed" flag is set.

    //       Unblock the tokenizer for this instance of the HTML
    //       parser, such that tasks that invoke the tokenizer
    //       can again be run.

    //       Let the insertion point be just before the next
    //       input character.

    //       Increment the parser's script nesting level by one
    //       (it should be zero before this step, so this sets
    //       it to one).

    //       Execute the script.

    //       Decrement the parser's script nesting level by
    //       one. If the parser's script nesting level is zero
    //       (which it always should be at this point), then set
    //       the parser pause flag to false.

    //       Let the insertion point be undefined again.

    //       If there is once again a pending parsing-blocking
    //       script, then repeat these steps from step 1.


  }

  function stopParsing() {
    // XXX This is just a temporary implementation to get the parser working.
    // A full implementation involves scripts and events and the event loop.

    // Remove the link from document to parser.
    // This is instead of "set the insertion point to undefined".
    // It means that document.write() can't write into the doc anymore.
    delete doc._parser;

    stack.elements.length = 0; // pop everything off

    // If there is a window object associated with the document
    // then trigger an load event on it
    if (doc.defaultView) {
      doc.defaultView.dispatchEvent(new impl.Event("load",{}));
    }

  }

  /****
   * Tokenizer states
   */

  /**
   * This file was partially mechanically generated from
   * http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html
   *
   * After mechanical conversion, it was further converted from
   * prose to JS by hand, but the intent is that it is a very
   * faithful rendering of the HTML tokenization spec in
   * JavaScript.
   *
   * It is not a goal of this tokenizer to detect or report
   * parse errors.
   *
   * XXX The tokenizer is supposed to work with straight UTF32
   * codepoints. But I don't think it has any dependencies on
   * any character outside of the BMP so I think it is safe to
   * pass it UTF16 characters. I don't think it will ever change
   * state in the middle of a surrogate pair.
   */

  /*
   * Each state is represented by a function.  For most states, the
   * scanner simply passes the next character (as an integer
   * codepoint) to the current state function and automatically
   * consumes the character.  If the state function can't process
   * the character it can call pushback() to push it back to the
   * scanner.
   *
   * Some states require lookahead, though.  If a state function has
   * a lookahead property, then it is invoked differently.  In this
   * case, the scanner invokes the function with 3 arguments: 1) the
   * next codepoint 2) a string of lookahead text 3) a boolean that
   * is true if the lookahead goes all the way to the EOF. (XXX
   * actually maybe this third is not necessary... the lookahead
   * could just include \uFFFF?)
   *
   * If the lookahead property of a state function is an integer, it
   * specifies the number of characters required. If it is a string,
   * then the scanner will scan for that string and return all
   * characters up to and including that sequence, or up to EOF.  If
   * the lookahead property is a regexp, then the scanner will match
   * the regexp at the current point and return the matching string.
   *
   * States that require lookahead are responsible for explicitly
   * consuming the characters they process. They do this by
   * incrementing nextchar by the number of processed characters.
   */
  function reconsume(c, new_state) {
    tokenizer = new_state;
    nextchar--; // pushback
  }

  function data_state(c) {
    switch(c) {
    case 0x0026: // AMPERSAND
      return_state = data_state;
      tokenizer = character_reference_state;
      break;
    case 0x003C: // LESS-THAN SIGN
      if (emitSimpleTag()) // Shortcut for <p>, <dl>, </div> etc.
        break;
      tokenizer = tag_open_state;
      break;
    case 0x0000: // NULL
      // Usually null characters emitted by the tokenizer will be
      // ignored by the tree builder, but sometimes they'll be
      // converted to \uFFFD.  I don't want to have the search every
      // string emitted to replace NULs, so I'll set a flag
      // if I've emitted a NUL.
      textrun.push(c);
      textIncludesNUL = true;
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      // Instead of just pushing a single character and then
      // coming back to the very same place, lookahead and
      // emit everything we can at once.
      /*jshint -W030 */
      emitCharsWhile(DATATEXT) || textrun.push(c);
      break;
    }
  }

  function rcdata_state(c) {
    // Save the open tag so we can find a matching close tag
    switch(c) {
    case 0x0026: // AMPERSAND
      return_state = rcdata_state;
      tokenizer = character_reference_state;
      break;
    case 0x003C: // LESS-THAN SIGN
      tokenizer = rcdata_less_than_sign_state;
      break;
    case 0x0000: // NULL
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      textIncludesNUL = true;
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      textrun.push(c);
      break;
    }
  }

  function rawtext_state(c) {
    switch(c) {
    case 0x003C: // LESS-THAN SIGN
      tokenizer = rawtext_less_than_sign_state;
      break;
    case 0x0000: // NULL
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      /*jshint -W030 */
      emitCharsWhile(RAWTEXT) || textrun.push(c);
      break;
    }
  }

  function script_data_state(c) {
    switch(c) {
    case 0x003C: // LESS-THAN SIGN
      tokenizer = script_data_less_than_sign_state;
      break;
    case 0x0000: // NULL
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      /*jshint -W030 */
      emitCharsWhile(RAWTEXT) || textrun.push(c);
      break;
    }
  }

  function plaintext_state(c) {
    switch(c) {
    case 0x0000: // NULL
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      /*jshint -W030 */
      emitCharsWhile(PLAINTEXT) || textrun.push(c);
      break;
    }
  }

  function tag_open_state(c) {
    switch(c) {
    case 0x0021: // EXCLAMATION MARK
      tokenizer = markup_declaration_open_state;
      break;
    case 0x002F: // SOLIDUS
      tokenizer = end_tag_open_state;
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      beginTagName();
      reconsume(c, tag_name_state);
      break;
    case 0x003F: // QUESTION MARK
      reconsume(c, bogus_comment_state);
      break;
    default:
      textrun.push(0x003C); // LESS-THAN SIGN
      reconsume(c, data_state);
      break;
    }
  }

  function end_tag_open_state(c) {
    switch(c) {
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      beginEndTagName();
      reconsume(c, tag_name_state);
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      break;
    case -1: // EOF
      textrun.push(0x003C); // LESS-THAN SIGN
      textrun.push(0x002F); // SOLIDUS
      emitEOF();
      break;
    default:
      reconsume(c, bogus_comment_state);
      break;
    }
  }

  function tag_name_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      tokenizer = before_attribute_name_state;
      break;
    case 0x002F: // SOLIDUS
      tokenizer = self_closing_start_tag_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitTag();
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      tagnamebuf += String.fromCharCode(c + 0x0020);
      break;
    case 0x0000: // NULL
      tagnamebuf += String.fromCharCode(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      tagnamebuf += getMatchingChars(TAGNAME);
      break;
    }
  }

  function rcdata_less_than_sign_state(c) {
    /* identical to the RAWTEXT less-than sign state, except s/RAWTEXT/RCDATA/g */
    if (c === 0x002F) {  // SOLIDUS
      beginTempBuf();
      tokenizer = rcdata_end_tag_open_state;
    }
    else {
      textrun.push(0x003C); // LESS-THAN SIGN
      reconsume(c, rcdata_state);
    }
  }

  function rcdata_end_tag_open_state(c) {
    /* identical to the RAWTEXT (and Script data) end tag open state, except s/RAWTEXT/RCDATA/g */
    switch(c) {
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      beginEndTagName();
      reconsume(c, rcdata_end_tag_name_state);
      break;
    default:
      textrun.push(0x003C); // LESS-THAN SIGN
      textrun.push(0x002F); // SOLIDUS
      reconsume(c, rcdata_state);
      break;
    }
  }

  function rcdata_end_tag_name_state(c) {
    /* identical to the RAWTEXT (and Script data) end tag name state, except s/RAWTEXT/RCDATA/g */
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = before_attribute_name_state;
        return;
      }
      break;
    case 0x002F: // SOLIDUS
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = self_closing_start_tag_state;
        return;
      }
      break;
    case 0x003E: // GREATER-THAN SIGN
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = data_state;
        emitTag();
        return;
      }
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:

      tagnamebuf += String.fromCharCode(c + 0x0020);
      tempbuf.push(c);
      return;
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:

      tagnamebuf += String.fromCharCode(c);
      tempbuf.push(c);
      return;
    default:
      break;
    }

    // If we don't return in one of the cases above, then this was not
    // an appropriately matching close tag, so back out by emitting all
    // the characters as text
    textrun.push(0x003C); // LESS-THAN SIGN
    textrun.push(0x002F); // SOLIDUS
    pushAll(textrun, tempbuf);
    reconsume(c, rcdata_state);
  }

  function rawtext_less_than_sign_state(c) {
    /* identical to the RCDATA less-than sign state, except s/RCDATA/RAWTEXT/g
     */
    if (c === 0x002F) { // SOLIDUS
      beginTempBuf();
      tokenizer = rawtext_end_tag_open_state;
    }
    else {
      textrun.push(0x003C); // LESS-THAN SIGN
      reconsume(c, rawtext_state);
    }
  }

  function rawtext_end_tag_open_state(c) {
    /* identical to the RCDATA (and Script data) end tag open state, except s/RCDATA/RAWTEXT/g */
    switch(c) {
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      beginEndTagName();
      reconsume(c, rawtext_end_tag_name_state);
      break;
    default:
      textrun.push(0x003C); // LESS-THAN SIGN
      textrun.push(0x002F); // SOLIDUS
      reconsume(c, rawtext_state);
      break;
    }
  }

  function rawtext_end_tag_name_state(c) {
    /* identical to the RCDATA (and Script data) end tag name state, except s/RCDATA/RAWTEXT/g */
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = before_attribute_name_state;
        return;
      }
      break;
    case 0x002F: // SOLIDUS
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = self_closing_start_tag_state;
        return;
      }
      break;
    case 0x003E: // GREATER-THAN SIGN
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = data_state;
        emitTag();
        return;
      }
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      tagnamebuf += String.fromCharCode(c + 0x0020);
      tempbuf.push(c);
      return;
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      tagnamebuf += String.fromCharCode(c);
      tempbuf.push(c);
      return;
    default:
      break;
    }

    // If we don't return in one of the cases above, then this was not
    // an appropriately matching close tag, so back out by emitting all
    // the characters as text
    textrun.push(0x003C); // LESS-THAN SIGN
    textrun.push(0x002F); // SOLIDUS
    pushAll(textrun,tempbuf);
    reconsume(c, rawtext_state);
  }

  function script_data_less_than_sign_state(c) {
    switch(c) {
    case 0x002F: // SOLIDUS
      beginTempBuf();
      tokenizer = script_data_end_tag_open_state;
      break;
    case 0x0021: // EXCLAMATION MARK
      tokenizer = script_data_escape_start_state;
      textrun.push(0x003C); // LESS-THAN SIGN
      textrun.push(0x0021); // EXCLAMATION MARK
      break;
    default:
      textrun.push(0x003C); // LESS-THAN SIGN
      reconsume(c, script_data_state);
      break;
    }
  }

  function script_data_end_tag_open_state(c) {
    /* identical to the RCDATA (and RAWTEXT) end tag open state, except s/RCDATA/Script data/g */
    switch(c) {
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      beginEndTagName();
      reconsume(c, script_data_end_tag_name_state);
      break;
    default:
      textrun.push(0x003C); // LESS-THAN SIGN
      textrun.push(0x002F); // SOLIDUS
      reconsume(c, script_data_state);
      break;
    }
  }

  function script_data_end_tag_name_state(c) {
    /* identical to the RCDATA (and RAWTEXT) end tag name state, except s/RCDATA/Script data/g */
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = before_attribute_name_state;
        return;
      }
      break;
    case 0x002F: // SOLIDUS
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = self_closing_start_tag_state;
        return;
      }
      break;
    case 0x003E: // GREATER-THAN SIGN
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = data_state;
        emitTag();
        return;
      }
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:

      tagnamebuf += String.fromCharCode(c + 0x0020);
      tempbuf.push(c);
      return;
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:

      tagnamebuf += String.fromCharCode(c);
      tempbuf.push(c);
      return;
    default:
      break;
    }

    // If we don't return in one of the cases above, then this was not
    // an appropriately matching close tag, so back out by emitting all
    // the characters as text
    textrun.push(0x003C); // LESS-THAN SIGN
    textrun.push(0x002F); // SOLIDUS
    pushAll(textrun,tempbuf);
    reconsume(c, script_data_state);
  }

  function script_data_escape_start_state(c) {
    if (c === 0x002D) { // HYPHEN-MINUS
      tokenizer = script_data_escape_start_dash_state;
      textrun.push(0x002D); // HYPHEN-MINUS
    }
    else {
      reconsume(c, script_data_state);
    }
  }

  function script_data_escape_start_dash_state(c) {
    if (c === 0x002D) { // HYPHEN-MINUS
      tokenizer = script_data_escaped_dash_dash_state;
      textrun.push(0x002D); // HYPHEN-MINUS
    }
    else {
      reconsume(c, script_data_state);
    }
  }

  function script_data_escaped_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = script_data_escaped_dash_state;
      textrun.push(0x002D); // HYPHEN-MINUS
      break;
    case 0x003C: // LESS-THAN SIGN
      tokenizer = script_data_escaped_less_than_sign_state;
      break;
    case 0x0000: // NULL
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      textrun.push(c);
      break;
    }
  }

  function script_data_escaped_dash_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = script_data_escaped_dash_dash_state;
      textrun.push(0x002D); // HYPHEN-MINUS
      break;
    case 0x003C: // LESS-THAN SIGN
      tokenizer = script_data_escaped_less_than_sign_state;
      break;
    case 0x0000: // NULL
      tokenizer = script_data_escaped_state;
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      tokenizer = script_data_escaped_state;
      textrun.push(c);
      break;
    }
  }

  function script_data_escaped_dash_dash_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      textrun.push(0x002D); // HYPHEN-MINUS
      break;
    case 0x003C: // LESS-THAN SIGN
      tokenizer = script_data_escaped_less_than_sign_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = script_data_state;
      textrun.push(0x003E); // GREATER-THAN SIGN
      break;
    case 0x0000: // NULL
      tokenizer = script_data_escaped_state;
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      tokenizer = script_data_escaped_state;
      textrun.push(c);
      break;
    }
  }

  function script_data_escaped_less_than_sign_state(c) {
    switch(c) {
    case 0x002F: // SOLIDUS
      beginTempBuf();
      tokenizer = script_data_escaped_end_tag_open_state;
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      beginTempBuf();
      textrun.push(0x003C); // LESS-THAN SIGN
      reconsume(c, script_data_double_escape_start_state);
      break;
    default:
      textrun.push(0x003C); // LESS-THAN SIGN
      reconsume(c, script_data_escaped_state);
      break;
    }
  }

  function script_data_escaped_end_tag_open_state(c) {
    switch(c) {
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      beginEndTagName();
      reconsume(c, script_data_escaped_end_tag_name_state);
      break;
    default:
      textrun.push(0x003C); // LESS-THAN SIGN
      textrun.push(0x002F); // SOLIDUS
      reconsume(c, script_data_escaped_state);
      break;
    }
  }

  function script_data_escaped_end_tag_name_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = before_attribute_name_state;
        return;
      }
      break;
    case 0x002F: // SOLIDUS
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = self_closing_start_tag_state;
        return;
      }
      break;
    case 0x003E: // GREATER-THAN SIGN
      if (appropriateEndTag(tagnamebuf)) {
        tokenizer = data_state;
        emitTag();
        return;
      }
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      tagnamebuf += String.fromCharCode(c + 0x0020);
      tempbuf.push(c);
      return;
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      tagnamebuf += String.fromCharCode(c);
      tempbuf.push(c);
      return;
    default:
      break;
    }

    // We get here in the default case, and if the closing tagname
    // is not an appropriate tagname.
    textrun.push(0x003C); // LESS-THAN SIGN
    textrun.push(0x002F); // SOLIDUS
    pushAll(textrun,tempbuf);
    reconsume(c, script_data_escaped_state);
  }

  function script_data_double_escape_start_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
    case 0x002F: // SOLIDUS
    case 0x003E: // GREATER-THAN SIGN
      if (buf2str(tempbuf) === "script") {
        tokenizer = script_data_double_escaped_state;
      }
      else {
        tokenizer = script_data_escaped_state;
      }
      textrun.push(c);
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      tempbuf.push(c + 0x0020);
      textrun.push(c);
      break;
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      tempbuf.push(c);
      textrun.push(c);
      break;
    default:
      reconsume(c, script_data_escaped_state);
      break;
    }
  }

  function script_data_double_escaped_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = script_data_double_escaped_dash_state;
      textrun.push(0x002D); // HYPHEN-MINUS
      break;
    case 0x003C: // LESS-THAN SIGN
      tokenizer = script_data_double_escaped_less_than_sign_state;
      textrun.push(0x003C); // LESS-THAN SIGN
      break;
    case 0x0000: // NULL
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      textrun.push(c);
      break;
    }
  }

  function script_data_double_escaped_dash_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = script_data_double_escaped_dash_dash_state;
      textrun.push(0x002D); // HYPHEN-MINUS
      break;
    case 0x003C: // LESS-THAN SIGN
      tokenizer = script_data_double_escaped_less_than_sign_state;
      textrun.push(0x003C); // LESS-THAN SIGN
      break;
    case 0x0000: // NULL
      tokenizer = script_data_double_escaped_state;
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      tokenizer = script_data_double_escaped_state;
      textrun.push(c);
      break;
    }
  }

  function script_data_double_escaped_dash_dash_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      textrun.push(0x002D); // HYPHEN-MINUS
      break;
    case 0x003C: // LESS-THAN SIGN
      tokenizer = script_data_double_escaped_less_than_sign_state;
      textrun.push(0x003C); // LESS-THAN SIGN
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = script_data_state;
      textrun.push(0x003E); // GREATER-THAN SIGN
      break;
    case 0x0000: // NULL
      tokenizer = script_data_double_escaped_state;
      textrun.push(0xFFFD); // REPLACEMENT CHARACTER
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      tokenizer = script_data_double_escaped_state;
      textrun.push(c);
      break;
    }
  }

  function script_data_double_escaped_less_than_sign_state(c) {
    if (c === 0x002F) { // SOLIDUS
      beginTempBuf();
      tokenizer = script_data_double_escape_end_state;
      textrun.push(0x002F); // SOLIDUS
    }
    else {
      reconsume(c, script_data_double_escaped_state);
    }
  }

  function script_data_double_escape_end_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
    case 0x002F: // SOLIDUS
    case 0x003E: // GREATER-THAN SIGN
      if (buf2str(tempbuf) === "script") {
        tokenizer = script_data_escaped_state;
      }
      else {
        tokenizer = script_data_double_escaped_state;
      }
      textrun.push(c);
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      tempbuf.push(c + 0x0020);
      textrun.push(c);
      break;
    case 0x0061:  // [a-z]
    case 0x0062:case 0x0063:case 0x0064:case 0x0065:case 0x0066:
    case 0x0067:case 0x0068:case 0x0069:case 0x006A:case 0x006B:
    case 0x006C:case 0x006D:case 0x006E:case 0x006F:case 0x0070:
    case 0x0071:case 0x0072:case 0x0073:case 0x0074:case 0x0075:
    case 0x0076:case 0x0077:case 0x0078:case 0x0079:case 0x007A:
      tempbuf.push(c);
      textrun.push(c);
      break;
    default:
      reconsume(c, script_data_double_escaped_state);
      break;
    }
  }

  function before_attribute_name_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      /* Ignore the character. */
      break;
    // For SOLIDUS, GREATER-THAN SIGN, and EOF, spec says "reconsume in
    // the after attribute name state", but in our implementation that
    // state always has an active attribute in attrnamebuf.  Just clone
    // the rules here, without the addAttribute business.
    case 0x002F: // SOLIDUS
      tokenizer = self_closing_start_tag_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitTag();
      break;
    case -1: // EOF
      emitEOF();
      break;
    case 0x003D: // EQUALS SIGN
      beginAttrName();
      attrnamebuf += String.fromCharCode(c);
      tokenizer = attribute_name_state;
      break;
    default:
      if (handleSimpleAttribute()) break;
      beginAttrName();
      reconsume(c, attribute_name_state);
      break;
    }
  }

  // beginAttrName() must have been called before this point
  // There is an active attribute in attrnamebuf (but not attrvaluebuf)
  function attribute_name_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
    case 0x002F: // SOLIDUS
    case 0x003E: // GREATER-THAN SIGN
    case -1: // EOF
      reconsume(c, after_attribute_name_state);
      break;
    case 0x003D: // EQUALS SIGN
      tokenizer = before_attribute_value_state;
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      attrnamebuf += String.fromCharCode(c + 0x0020);
      break;
    case 0x0000: // NULL
      attrnamebuf += String.fromCharCode(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case 0x0022: // QUOTATION MARK
    case 0x0027: // APOSTROPHE
    case 0x003C: // LESS-THAN SIGN
      /* falls through */
    default:
      attrnamebuf += getMatchingChars(ATTRNAME);
      break;
    }
  }

  // There is an active attribute in attrnamebuf, but not yet in attrvaluebuf.
  function after_attribute_name_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      /* Ignore the character. */
      break;
    case 0x002F: // SOLIDUS
      // Keep in sync with before_attribute_name_state.
      addAttribute(attrnamebuf);
      tokenizer = self_closing_start_tag_state;
      break;
    case 0x003D: // EQUALS SIGN
      tokenizer = before_attribute_value_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      // Keep in sync with before_attribute_name_state.
      tokenizer = data_state;
      addAttribute(attrnamebuf);
      emitTag();
      break;
    case -1: // EOF
      // Keep in sync with before_attribute_name_state.
      addAttribute(attrnamebuf);
      emitEOF();
      break;
    default:
      addAttribute(attrnamebuf);
      beginAttrName();
      reconsume(c, attribute_name_state);
      break;
    }
  }

  function before_attribute_value_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      /* Ignore the character. */
      break;
    case 0x0022: // QUOTATION MARK
      beginAttrValue();
      tokenizer = attribute_value_double_quoted_state;
      break;
    case 0x0027: // APOSTROPHE
      beginAttrValue();
      tokenizer = attribute_value_single_quoted_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      /* falls through */
    default:
      beginAttrValue();
      reconsume(c, attribute_value_unquoted_state);
      break;
    }
  }

  function attribute_value_double_quoted_state(c) {
    switch(c) {
    case 0x0022: // QUOTATION MARK
      addAttribute(attrnamebuf, attrvaluebuf);
      tokenizer = after_attribute_value_quoted_state;
      break;
    case 0x0026: // AMPERSAND
      return_state = attribute_value_double_quoted_state;
      tokenizer = character_reference_state;
      break;
    case 0x0000: // NULL
      attrvaluebuf += String.fromCharCode(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case -1: // EOF
      emitEOF();
      break;
    case 0x000A: // LF
      // this could be a converted \r, so don't use getMatchingChars
      attrvaluebuf += String.fromCharCode(c);
      break;
    default:
      attrvaluebuf += getMatchingChars(DBLQUOTEATTRVAL);
      break;
    }
  }

  function attribute_value_single_quoted_state(c) {
    switch(c) {
    case 0x0027: // APOSTROPHE
      addAttribute(attrnamebuf, attrvaluebuf);
      tokenizer = after_attribute_value_quoted_state;
      break;
    case 0x0026: // AMPERSAND
      return_state = attribute_value_single_quoted_state;
      tokenizer = character_reference_state;
      break;
    case 0x0000: // NULL
      attrvaluebuf += String.fromCharCode(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case -1: // EOF
      emitEOF();
      break;
    case 0x000A: // LF
      // this could be a converted \r, so don't use getMatchingChars
      attrvaluebuf += String.fromCharCode(c);
      break;
    default:
      attrvaluebuf += getMatchingChars(SINGLEQUOTEATTRVAL);
      break;
    }
  }

  function attribute_value_unquoted_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      addAttribute(attrnamebuf, attrvaluebuf);
      tokenizer = before_attribute_name_state;
      break;
    case 0x0026: // AMPERSAND
      return_state = attribute_value_unquoted_state;
      tokenizer = character_reference_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      addAttribute(attrnamebuf, attrvaluebuf);
      tokenizer = data_state;
      emitTag();
      break;
    case 0x0000: // NULL
      attrvaluebuf += String.fromCharCode(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case -1: // EOF
      nextchar--; // pushback
      tokenizer = data_state;
      break;
    case 0x0022: // QUOTATION MARK
    case 0x0027: // APOSTROPHE
    case 0x003C: // LESS-THAN SIGN
    case 0x003D: // EQUALS SIGN
    case 0x0060: // GRAVE ACCENT
      /* falls through */
    default:
      attrvaluebuf += getMatchingChars(UNQUOTEDATTRVAL);
      break;
    }
  }

  function after_attribute_value_quoted_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      tokenizer = before_attribute_name_state;
      break;
    case 0x002F: // SOLIDUS
      tokenizer = self_closing_start_tag_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitTag();
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      reconsume(c, before_attribute_name_state);
      break;
    }
  }

  function self_closing_start_tag_state(c) {
    switch(c) {
    case 0x003E: // GREATER-THAN SIGN
      // Set the <i>self-closing flag</i> of the current tag token.
      tokenizer = data_state;
      emitSelfClosingTag(true);
      break;
    case -1: // EOF
      emitEOF();
      break;
    default:
      reconsume(c, before_attribute_name_state);
      break;
    }
  }

  function bogus_comment_state(c, lookahead, eof) {
    var len = lookahead.length;

    if (eof) {
      nextchar += len-1; // don't consume the eof
    }
    else {
      nextchar += len;
    }

    var comment = lookahead.substring(0, len-1);

    comment = comment.replace(/\u0000/g,"\uFFFD");
    comment = comment.replace(/\u000D\u000A/g,"\u000A");
    comment = comment.replace(/\u000D/g,"\u000A");

    insertToken(COMMENT, comment);
    tokenizer = data_state;
  }
  bogus_comment_state.lookahead = ">";

  function markup_declaration_open_state(c, lookahead, eof) {
    if (lookahead[0] === "-" && lookahead[1] === "-") {
      nextchar += 2;
      beginComment();
      tokenizer = comment_start_state;
      return;
    }

    if (lookahead.toUpperCase() === "DOCTYPE") {
      nextchar += 7;
      tokenizer = doctype_state;
    }
    else if (lookahead === "[CDATA[" && cdataAllowed()) {
      nextchar += 7;
      tokenizer = cdata_section_state;
    }
    else {
      tokenizer = bogus_comment_state;
    }
  }
  markup_declaration_open_state.lookahead = 7;

  function comment_start_state(c) {
    beginComment();
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = comment_start_dash_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      insertToken(COMMENT, buf2str(commentbuf));
      break; /* see comment in comment end state */
    default:
      reconsume(c, comment_state);
      break;
    }
  }

  function comment_start_dash_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = comment_end_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      insertToken(COMMENT, buf2str(commentbuf));
      break;
    case -1: // EOF
      insertToken(COMMENT, buf2str(commentbuf));
      emitEOF();
      break; /* see comment in comment end state */
    default:
      commentbuf.push(0x002D /* HYPHEN-MINUS */);
      reconsume(c, comment_state);
      break;
    }
  }

  function comment_state(c) {
    switch(c) {
    case 0x003C: // LESS-THAN SIGN
      commentbuf.push(c);
      tokenizer = comment_less_than_sign_state;
      break;
    case 0x002D: // HYPHEN-MINUS
      tokenizer = comment_end_dash_state;
      break;
    case 0x0000: // NULL
      commentbuf.push(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case -1: // EOF
      insertToken(COMMENT, buf2str(commentbuf));
      emitEOF();
      break; /* see comment in comment end state */
    default:
      commentbuf.push(c);
      break;
    }
  }

  function comment_less_than_sign_state(c) {
    switch(c) {
    case 0x0021: // EXCLAMATION MARK
      commentbuf.push(c);
      tokenizer = comment_less_than_sign_bang_state;
      break;
    case 0x003C: // LESS-THAN SIGN
      commentbuf.push(c);
      break;
    default:
      reconsume(c, comment_state);
      break;
    }
  }

  function comment_less_than_sign_bang_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = comment_less_than_sign_bang_dash_state;
      break;
    default:
      reconsume(c, comment_state);
      break;
    }
  }

  function comment_less_than_sign_bang_dash_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = comment_less_than_sign_bang_dash_dash_state;
      break;
    default:
      reconsume(c, comment_end_dash_state);
      break;
    }
  }

  function comment_less_than_sign_bang_dash_dash_state(c) {
    switch(c) {
    case 0x003E: // GREATER-THAN SIGN
    case -1: // EOF
      reconsume(c, comment_end_state);
      break;
    default:
      // parse error
      reconsume(c, comment_end_state);
      break;
    }
  }

  function comment_end_dash_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      tokenizer = comment_end_state;
      break;
    case -1: // EOF
      insertToken(COMMENT, buf2str(commentbuf));
      emitEOF();
      break; /* see comment in comment end state */
    default:
      commentbuf.push(0x002D /* HYPHEN-MINUS */);
      reconsume(c, comment_state);
      break;
    }
  }

  function comment_end_state(c) {
    switch(c) {
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      insertToken(COMMENT, buf2str(commentbuf));
      break;
    case 0x0021: // EXCLAMATION MARK
      tokenizer = comment_end_bang_state;
      break;
    case 0x002D: // HYPHEN-MINUS
      commentbuf.push(0x002D);
      break;
    case -1: // EOF
      insertToken(COMMENT, buf2str(commentbuf));
      emitEOF();
      break; /* For security reasons: otherwise, hostile user could put a script in a comment e.g. in a blog comment and then DOS the server so that the end tag isn't read, and then the commented script tag would be treated as live code */
    default:
      commentbuf.push(0x002D);
      commentbuf.push(0x002D);
      reconsume(c, comment_state);
      break;
    }
  }

  function comment_end_bang_state(c) {
    switch(c) {
    case 0x002D: // HYPHEN-MINUS
      commentbuf.push(0x002D);
      commentbuf.push(0x002D);
      commentbuf.push(0x0021);
      tokenizer = comment_end_dash_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      insertToken(COMMENT, buf2str(commentbuf));
      break;
    case -1: // EOF
      insertToken(COMMENT, buf2str(commentbuf));
      emitEOF();
      break; /* see comment in comment end state */
    default:
      commentbuf.push(0x002D);
      commentbuf.push(0x002D);
      commentbuf.push(0x0021);
      reconsume(c, comment_state);
      break;
    }
  }

  function doctype_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      tokenizer = before_doctype_name_state;
      break;
    case -1: // EOF
      beginDoctype();
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      reconsume(c, before_doctype_name_state);
      break;
    }
  }

  function before_doctype_name_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      /* Ignore the character. */
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      beginDoctype();
      doctypenamebuf.push(c + 0x0020);
      tokenizer = doctype_name_state;
      break;
    case 0x0000: // NULL
      beginDoctype();
      doctypenamebuf.push(0xFFFD);
      tokenizer = doctype_name_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      beginDoctype();
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      beginDoctype();
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      beginDoctype();
      doctypenamebuf.push(c);
      tokenizer = doctype_name_state;
      break;
    }
  }

  function doctype_name_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      tokenizer = after_doctype_name_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitDoctype();
      break;
    case 0x0041:  // [A-Z]
    case 0x0042:case 0x0043:case 0x0044:case 0x0045:case 0x0046:
    case 0x0047:case 0x0048:case 0x0049:case 0x004A:case 0x004B:
    case 0x004C:case 0x004D:case 0x004E:case 0x004F:case 0x0050:
    case 0x0051:case 0x0052:case 0x0053:case 0x0054:case 0x0055:
    case 0x0056:case 0x0057:case 0x0058:case 0x0059:case 0x005A:
      doctypenamebuf.push(c + 0x0020);
      break;
    case 0x0000: // NULL
      doctypenamebuf.push(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      doctypenamebuf.push(c);
      break;
    }
  }

  function after_doctype_name_state(c, lookahead, eof) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      /* Ignore the character. */
      nextchar += 1;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      nextchar += 1;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      lookahead = lookahead.toUpperCase();
      if (lookahead === "PUBLIC") {
        nextchar += 6;
        tokenizer = after_doctype_public_keyword_state;
      }
      else if (lookahead === "SYSTEM") {
        nextchar += 6;
        tokenizer = after_doctype_system_keyword_state;
      }
      else {
        forcequirks();
        tokenizer = bogus_doctype_state;
      }
      break;
    }
  }
  after_doctype_name_state.lookahead = 6;

  function after_doctype_public_keyword_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      tokenizer = before_doctype_public_identifier_state;
      break;
    case 0x0022: // QUOTATION MARK
      beginDoctypePublicId();
      tokenizer = doctype_public_identifier_double_quoted_state;
      break;
    case 0x0027: // APOSTROPHE
      beginDoctypePublicId();
      tokenizer = doctype_public_identifier_single_quoted_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      forcequirks();
      tokenizer = bogus_doctype_state;
      break;
    }
  }

  function before_doctype_public_identifier_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      /* Ignore the character. */
      break;
    case 0x0022: // QUOTATION MARK
      beginDoctypePublicId();
      tokenizer = doctype_public_identifier_double_quoted_state;
      break;
    case 0x0027: // APOSTROPHE
      beginDoctypePublicId();
      tokenizer = doctype_public_identifier_single_quoted_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      forcequirks();
      tokenizer = bogus_doctype_state;
      break;
    }
  }

  function doctype_public_identifier_double_quoted_state(c) {
    switch(c) {
    case 0x0022: // QUOTATION MARK
      tokenizer = after_doctype_public_identifier_state;
      break;
    case 0x0000: // NULL
      doctypepublicbuf.push(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      doctypepublicbuf.push(c);
      break;
    }
  }

  function doctype_public_identifier_single_quoted_state(c) {
    switch(c) {
    case 0x0027: // APOSTROPHE
      tokenizer = after_doctype_public_identifier_state;
      break;
    case 0x0000: // NULL
      doctypepublicbuf.push(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      doctypepublicbuf.push(c);
      break;
    }
  }

  function after_doctype_public_identifier_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      tokenizer = between_doctype_public_and_system_identifiers_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitDoctype();
      break;
    case 0x0022: // QUOTATION MARK
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_double_quoted_state;
      break;
    case 0x0027: // APOSTROPHE
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_single_quoted_state;
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      forcequirks();
      tokenizer = bogus_doctype_state;
      break;
    }
  }

  function between_doctype_public_and_system_identifiers_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE Ignore the character.
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitDoctype();
      break;
    case 0x0022: // QUOTATION MARK
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_double_quoted_state;
      break;
    case 0x0027: // APOSTROPHE
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_single_quoted_state;
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      forcequirks();
      tokenizer = bogus_doctype_state;
      break;
    }
  }

  function after_doctype_system_keyword_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      tokenizer = before_doctype_system_identifier_state;
      break;
    case 0x0022: // QUOTATION MARK
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_double_quoted_state;
      break;
    case 0x0027: // APOSTROPHE
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_single_quoted_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      forcequirks();
      tokenizer = bogus_doctype_state;
      break;
    }
  }

  function before_doctype_system_identifier_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE Ignore the character.
      break;
    case 0x0022: // QUOTATION MARK
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_double_quoted_state;
      break;
    case 0x0027: // APOSTROPHE
      beginDoctypeSystemId();
      tokenizer = doctype_system_identifier_single_quoted_state;
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      forcequirks();
      tokenizer = bogus_doctype_state;
      break;
    }
  }

  function doctype_system_identifier_double_quoted_state(c) {
    switch(c) {
    case 0x0022: // QUOTATION MARK
      tokenizer = after_doctype_system_identifier_state;
      break;
    case 0x0000: // NULL
      doctypesystembuf.push(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      doctypesystembuf.push(c);
      break;
    }
  }

  function doctype_system_identifier_single_quoted_state(c) {
    switch(c) {
    case 0x0027: // APOSTROPHE
      tokenizer = after_doctype_system_identifier_state;
      break;
    case 0x0000: // NULL
      doctypesystembuf.push(0xFFFD /* REPLACEMENT CHARACTER */);
      break;
    case 0x003E: // GREATER-THAN SIGN
      forcequirks();
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      doctypesystembuf.push(c);
      break;
    }
  }

  function after_doctype_system_identifier_state(c) {
    switch(c) {
    case 0x0009: // CHARACTER TABULATION (tab)
    case 0x000A: // LINE FEED (LF)
    case 0x000C: // FORM FEED (FF)
    case 0x0020: // SPACE
      /* Ignore the character. */
      break;
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      forcequirks();
      emitDoctype();
      emitEOF();
      break;
    default:
      tokenizer = bogus_doctype_state;
      /* This does *not* set the DOCTYPE token's force-quirks flag. */
      break;
    }
  }

  function bogus_doctype_state(c) {
    switch(c) {
    case 0x003E: // GREATER-THAN SIGN
      tokenizer = data_state;
      emitDoctype();
      break;
    case -1: // EOF
      emitDoctype();
      emitEOF();
      break;
    default:
      /* Ignore the character. */
      break;
    }
  }

  function cdata_section_state(c) {
    switch(c) {
    case 0x005D: // RIGHT SQUARE BRACKET
      tokenizer = cdata_section_bracket_state;
      break;
    case -1: // EOF
      emitEOF();
      break;
    case 0x0000: // NULL
      textIncludesNUL = true;
      /* fall through */
    default:
      // Instead of just pushing a single character and then
      // coming back to the very same place, lookahead and
      // emit everything we can at once.
      /*jshint -W030 */
      emitCharsWhile(CDATATEXT) || textrun.push(c);
      break;
    }
  }

  function cdata_section_bracket_state(c) {
    switch(c) {
    case 0x005D: // RIGHT SQUARE BRACKET
      tokenizer = cdata_section_end_state;
      break;
    default:
      textrun.push(0x005D);
      reconsume(c, cdata_section_state);
      break;
    }
  }

  function cdata_section_end_state(c) {
    switch(c) {
    case 0x005D: // RIGHT SQUARE BRACKET
      textrun.push(0x005D);
      break;
    case 0x003E: // GREATER-THAN SIGN
      flushText();
      tokenizer = data_state;
      break;
    default:
      textrun.push(0x005D);
      textrun.push(0x005D);
      reconsume(c, cdata_section_state);
      break;
    }
  }

  function character_reference_state(c) {
    beginTempBuf();
    tempbuf.push(0x0026);
    switch(c) {
    case 0x0009: // TAB
    case 0x000A: // LINE FEED
    case 0x000C: // FORM FEED
    case 0x0020: // SPACE
    case 0x003C: // LESS-THAN SIGN
    case 0x0026: // AMPERSAND
    case -1: // EOF
      reconsume(c, character_reference_end_state);
      break;
    case 0x0023: // NUMBER SIGN
      tempbuf.push(c);
      tokenizer = numeric_character_reference_state;
      break;
    default:
      reconsume(c, named_character_reference_state);
      break;
    }
  }

  function named_character_reference_state(c) {
    NAMEDCHARREF.lastIndex = nextchar; // w/ lookahead no char has been consumed
    var matched = NAMEDCHARREF.exec(chars);
    if (!matched) throw new Error("should never happen");
    var name = matched[1];
    if (!name) {
      // If no match can be made, switch to the character reference end state
      tokenizer = character_reference_end_state;
      return;
    }

    // Consume the matched characters and append them to temporary buffer
    nextchar += name.length;
    pushAll(tempbuf, str2buf(name));

    switch(return_state) {
    case attribute_value_double_quoted_state:
    case attribute_value_single_quoted_state:
    case attribute_value_unquoted_state:
      // If the character reference was consumed as part of an attribute...
      if (name[name.length-1] !== ';') { // ...and the last char is not ;
        if (/[=A-Za-z0-9]/.test(chars[nextchar])) {
          tokenizer = character_reference_end_state;
          return;
        }
      }
      break;
    default:
      break;
    }

    beginTempBuf();
    var rv = namedCharRefs[name];
    if (typeof rv === 'number') {
      tempbuf.push(rv);
    } else {
      pushAll(tempbuf, rv);
    }
    tokenizer = character_reference_end_state;
  }
  // We might need to pause tokenization until we have enough characters
  // in the buffer for longest possible character reference.
  named_character_reference_state.lookahead = -NAMEDCHARREF_MAXLEN;

  function numeric_character_reference_state(c) {
    character_reference_code = 0;
    switch(c) {
    case 0x0078: // x
    case 0x0058: // X
      tempbuf.push(c);
      tokenizer = hexadecimal_character_reference_start_state;
      break;
    default:
      reconsume(c, decimal_character_reference_start_state);
      break;
    }
  }

  function hexadecimal_character_reference_start_state(c) {
    switch(c) {
    case 0x0030: case 0x0031: case 0x0032: case 0x0033: case 0x0034:
    case 0x0035: case 0x0036: case 0x0037: case 0x0038: case 0x0039: // [0-9]
    case 0x0041: case 0x0042: case 0x0043: case 0x0044: case 0x0045:
    case 0x0046: // [A-F]
    case 0x0061: case 0x0062: case 0x0063: case 0x0064: case 0x0065:
    case 0x0066: // [a-f]
      reconsume(c, hexadecimal_character_reference_state);
      break;
    default:
      reconsume(c, character_reference_end_state);
      break;
    }
  }

  function decimal_character_reference_start_state(c) {
    switch(c) {
    case 0x0030: case 0x0031: case 0x0032: case 0x0033: case 0x0034:
    case 0x0035: case 0x0036: case 0x0037: case 0x0038: case 0x0039: // [0-9]
      reconsume(c, decimal_character_reference_state);
      break;
    default:
      reconsume(c, character_reference_end_state);
      break;
    }
  }

  function hexadecimal_character_reference_state(c) {
    switch(c) {
    case 0x0041: case 0x0042: case 0x0043: case 0x0044: case 0x0045:
    case 0x0046: // [A-F]
      character_reference_code *= 16;
      character_reference_code += (c - 0x0037);
      break;
    case 0x0061: case 0x0062: case 0x0063: case 0x0064: case 0x0065:
    case 0x0066: // [a-f]
      character_reference_code *= 16;
      character_reference_code += (c - 0x0057);
      break;
    case 0x0030: case 0x0031: case 0x0032: case 0x0033: case 0x0034:
    case 0x0035: case 0x0036: case 0x0037: case 0x0038: case 0x0039: // [0-9]
      character_reference_code *= 16;
      character_reference_code += (c - 0x0030);
      break;
    case 0x003B: // SEMICOLON
      tokenizer = numeric_character_reference_end_state;
      break;
    default:
      reconsume(c, numeric_character_reference_end_state);
      break;
    }
  }

  function decimal_character_reference_state(c) {
    switch(c) {
    case 0x0030: case 0x0031: case 0x0032: case 0x0033: case 0x0034:
    case 0x0035: case 0x0036: case 0x0037: case 0x0038: case 0x0039: // [0-9]
      character_reference_code *= 10;
      character_reference_code += (c - 0x0030);
      break;
    case 0x003B: // SEMICOLON
      tokenizer = numeric_character_reference_end_state;
      break;
    default:
      reconsume(c, numeric_character_reference_end_state);
      break;
    }
  }

  function numeric_character_reference_end_state(c) {
    if (character_reference_code in numericCharRefReplacements) {
      character_reference_code = numericCharRefReplacements[character_reference_code];
    } else if (character_reference_code > 0x10FFFF || (character_reference_code >= 0xD800 && character_reference_code < 0xE000)) {
      character_reference_code = 0xFFFD;
    }

    beginTempBuf();
    if (character_reference_code <= 0xFFFF) {
      tempbuf.push(character_reference_code);
    } else {
      character_reference_code = character_reference_code - 0x10000;
      /* jshint bitwise: false */
      tempbuf.push(0xD800 + (character_reference_code >> 10));
      tempbuf.push(0xDC00 + (character_reference_code & 0x03FF));
    }
    reconsume(c, character_reference_end_state);
  }

  function character_reference_end_state(c) {
    switch(return_state) {
    case attribute_value_double_quoted_state:
    case attribute_value_single_quoted_state:
    case attribute_value_unquoted_state:
      // append each character to the current attribute's value
      attrvaluebuf += buf2str(tempbuf);
      break;
    default:
      pushAll(textrun, tempbuf);
      break;
    }
    reconsume(c, return_state);
  }

  /***
   * The tree builder insertion modes
   */

  // 11.2.5.4.1 The "initial" insertion mode
  function initial_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      value = value.replace(LEADINGWS, ""); // Ignore spaces
      if (value.length === 0) return; // Are we done?
      break; // Handle anything non-space text below
    case 4: // COMMENT
      doc._appendChild(doc.createComment(value));
      return;
    case 5: // DOCTYPE
      var name = value;
      var publicid = arg3;
      var systemid = arg4;
      // Use the constructor directly instead of
      // implementation.createDocumentType because the create
      // function throws errors on invalid characters, and
      // we don't want the parser to throw them.
      doc.appendChild(new DocumentType(doc, name, publicid, systemid));

      // Note that there is no public API for setting quirks mode We can
      // do this here because we have access to implementation details
      if (force_quirks ||
        name.toLowerCase() !== "html" ||
        quirkyPublicIds.test(publicid) ||
        (systemid && systemid.toLowerCase() === quirkySystemId) ||
        (systemid === undefined &&
         conditionallyQuirkyPublicIds.test(publicid)))
        doc._quirks = true;
      else if (limitedQuirkyPublicIds.test(publicid) ||
           (systemid !== undefined &&
            conditionallyQuirkyPublicIds.test(publicid)))
        doc._limitedQuirks = true;
      parser = before_html_mode;
      return;
    }

    // tags or non-whitespace text
    doc._quirks = true;
    parser = before_html_mode;
    parser(t,value,arg3,arg4);
  }

  // 11.2.5.4.2 The "before html" insertion mode
  function before_html_mode(t,value,arg3,arg4) {
    var elt;
    switch(t) {
    case 1: // TEXT
      value = value.replace(LEADINGWS, ""); // Ignore spaces
      if (value.length === 0) return; // Are we done?
      break; // Handle anything non-space text below
    case 5: // DOCTYPE
      /* ignore the token */
      return;
    case 4: // COMMENT
      doc._appendChild(doc.createComment(value));
      return;
    case 2: // TAG
      if (value === "html") {
        elt = createHTMLElt(doc, value, arg3);
        stack.push(elt);
        doc.appendChild(elt);
        // XXX: handle application cache here
        parser = before_head_mode;
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "html":
      case "head":
      case "body":
      case "br":
        break;  // fall through on these
      default:
        return; // ignore most end tags
      }
    }

    // Anything that didn't get handled above is handled like this:
    elt = createHTMLElt(doc, "html", null);
    stack.push(elt);
    doc.appendChild(elt);
    // XXX: handle application cache here
    parser = before_head_mode;
    parser(t,value,arg3,arg4);
  }

  // 11.2.5.4.3 The "before head" insertion mode
  function before_head_mode(t,value,arg3,arg4) {
    switch(t) {
    case 1: // TEXT
      value = value.replace(LEADINGWS, "");  // Ignore spaces
      if (value.length === 0) return; // Are we done?
      break;  // Handle anything non-space text below
    case 5: // DOCTYPE
      /* ignore the token */
      return;
    case 4: // COMMENT
      insertComment(value);
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t,value,arg3,arg4);
        return;
      case "head":
        var elt = insertHTMLElement(value, arg3);
        head_element_pointer = elt;
        parser = in_head_mode;
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "html":
      case "head":
      case "body":
      case "br":
        break;
      default:
        return; // ignore most end tags
      }
    }

    // If not handled explicitly above
    before_head_mode(TAG, "head", null); // create a head tag
    parser(t, value, arg3, arg4); // then try again with this token
  }

  function in_head_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      var ws = value.match(LEADINGWS);
      if (ws) {
        insertText(ws[0]);
        value = value.substring(ws[0].length);
      }
      if (value.length === 0) return;
      break; // Handle non-whitespace below
    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "meta":
        // XXX:
        // May need to change the encoding based on this tag
        /* falls through */
      case "base":
      case "basefont":
      case "bgsound":
      case "link":
        insertHTMLElement(value, arg3);
        stack.pop();
        return;
      case "title":
        parseRCDATA(value, arg3);
        return;
      case "noscript":
        if (!scripting_enabled) {
          insertHTMLElement(value, arg3);
          parser = in_head_noscript_mode;
          return;
        }
        // Otherwise, if scripting is enabled...
        /* falls through */
      case "noframes":
      case "style":
        parseRawText(value,arg3);
        return;
      case "script":
        insertElement(function(doc) {
          var elt = createHTMLElt(doc, value, arg3);
          elt._parser_inserted = true;
          elt._force_async = false;
          if (fragment) elt._already_started = true;
          flushText();
          return elt;
        });
        tokenizer = script_data_state;
        originalInsertionMode = parser;
        parser = text_mode;
        return;
      case "template":
        insertHTMLElement(value, arg3);
        afe.insertMarker();
        frameset_ok = false;
        parser = in_template_mode;
        templateInsertionModes.push(parser);
        return;
      case "head":
        return; // ignore it
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "head":
        stack.pop();
        parser = after_head_mode;
        return;
      case "body":
      case "html":
      case "br":
        break; // handle these at the bottom of the function
      case "template":
        if (!stack.contains("template")) {
          return;
        }
        stack.generateImpliedEndTags(null, "thorough");
        stack.popTag("template");
        afe.clearToMarker();
        templateInsertionModes.pop();
        resetInsertionMode();
        return;
      default:
        // ignore any other end tag
        return;
      }
      break;
    }

    // If not handled above
    in_head_mode(ENDTAG, "head", null);   // synthetic </head>
    parser(t, value, arg3, arg4);   // Then redo this one
  }

  // 13.2.5.4.5 The "in head noscript" insertion mode
  function in_head_noscript_mode(t, value, arg3, arg4) {
    switch(t) {
    case 5: // DOCTYPE
      return;
    case 4: // COMMENT
      in_head_mode(t, value);
      return;
    case 1: // TEXT
      var ws = value.match(LEADINGWS);
      if (ws) {
        in_head_mode(t, ws[0]);
        value = value.substring(ws[0].length);
      }
      if (value.length === 0) return; // no more text
      break; // Handle non-whitespace below
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "basefont":
      case "bgsound":
      case "link":
      case "meta":
      case "noframes":
      case "style":
        in_head_mode(t, value, arg3);
        return;
      case "head":
      case "noscript":
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "noscript":
        stack.pop();
        parser = in_head_mode;
        return;
      case "br":
        break;  // goes to the outer default
      default:
        return; // ignore other end tags
      }
      break;
    }

    // If not handled above
    in_head_noscript_mode(ENDTAG, "noscript", null);
    parser(t, value, arg3, arg4);
  }

  function after_head_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      var ws = value.match(LEADINGWS);
      if (ws) {
        insertText(ws[0]);
        value = value.substring(ws[0].length);
      }
      if (value.length === 0) return;
      break; // Handle non-whitespace below
    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "body":
        insertHTMLElement(value, arg3);
        frameset_ok = false;
        parser = in_body_mode;
        return;
      case "frameset":
        insertHTMLElement(value, arg3);
        parser = in_frameset_mode;
        return;
      case "base":
      case "basefont":
      case "bgsound":
      case "link":
      case "meta":
      case "noframes":
      case "script":
      case "style":
      case "template":
      case "title":
        stack.push(head_element_pointer);
        in_head_mode(TAG, value, arg3);
        stack.removeElement(head_element_pointer);
        return;
      case "head":
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "template":
        return in_head_mode(t, value, arg3, arg4);
      case "body":
      case "html":
      case "br":
        break;
      default:
        return;  // ignore any other end tag
      }
      break;
    }

    after_head_mode(TAG, "body", null);
    frameset_ok = true;
    parser(t, value, arg3, arg4);
  }

  // 13.2.5.4.7 The "in body" insertion mode
  function in_body_mode(t,value,arg3,arg4) {
    var body, i, node, elt;
    switch(t) {
    case 1: // TEXT
      if (textIncludesNUL) {
        value = value.replace(NULCHARS, "");
        if (value.length === 0) return;
      }
      // If any non-space characters
      if (frameset_ok && NONWS.test(value))
        frameset_ok = false;
      afereconstruct();
      insertText(value);
      return;
    case 5: // DOCTYPE
      return;
    case 4: // COMMENT
      insertComment(value);
      return;
    case -1: // EOF
      if (templateInsertionModes.length) {
        return in_template_mode(t);
      }
      stopParsing();
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        if (stack.contains("template")) {
          return;
        }
        transferAttributes(arg3, stack.elements[0]);
        return;
      case "base":
      case "basefont":
      case "bgsound":
      case "link":
      case "meta":
      case "noframes":
      case "script":
      case "style":
      case "template":
      case "title":
        in_head_mode(TAG, value, arg3);
        return;
      case "body":
        body = stack.elements[1];
        if (!body || !(body instanceof impl.HTMLBodyElement) ||
            stack.contains("template"))
          return;
        frameset_ok = false;
        transferAttributes(arg3, body);
        return;
      case "frameset":
        if (!frameset_ok) return;
        body = stack.elements[1];
        if (!body || !(body instanceof impl.HTMLBodyElement))
          return;
        if (body.parentNode) body.parentNode.removeChild(body);
        while(!(stack.top instanceof impl.HTMLHtmlElement))
          stack.pop();
        insertHTMLElement(value, arg3);
        parser = in_frameset_mode;
        return;

      case "address":
      case "article":
      case "aside":
      case "blockquote":
      case "center":
      case "details":
      case "dialog":
      case "dir":
      case "div":
      case "dl":
      case "fieldset":
      case "figcaption":
      case "figure":
      case "footer":
      case "header":
      case "hgroup":
      case "main":
      case "nav":
      case "ol":
      case "p":
      case "section":
      case "summary":
      case "ul":
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        insertHTMLElement(value, arg3);
        return;

      case "menu":
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        if (isA(stack.top, 'menuitem')) {
          stack.pop();
        }
        insertHTMLElement(value, arg3);
        return;

      case "h1":
      case "h2":
      case "h3":
      case "h4":
      case "h5":
      case "h6":
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        if (stack.top instanceof impl.HTMLHeadingElement)
          stack.pop();
        insertHTMLElement(value, arg3);
        return;

      case "pre":
      case "listing":
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        insertHTMLElement(value, arg3);
        ignore_linefeed = true;
        frameset_ok = false;
        return;

      case "form":
        if (form_element_pointer && !stack.contains("template")) return;
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        elt = insertHTMLElement(value, arg3);
        if (!stack.contains("template"))
          form_element_pointer = elt;
        return;

      case "li":
        frameset_ok = false;
        for(i = stack.elements.length-1; i >= 0; i--) {
          node = stack.elements[i];
          if (node instanceof impl.HTMLLIElement) {
            in_body_mode(ENDTAG, "li");
            break;
          }
          if (isA(node, specialSet) && !isA(node, addressdivpSet))
            break;
        }
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        insertHTMLElement(value, arg3);
        return;

      case "dd":
      case "dt":
        frameset_ok = false;
        for(i = stack.elements.length-1; i >= 0; i--) {
          node = stack.elements[i];
          if (isA(node, dddtSet)) {
            in_body_mode(ENDTAG, node.localName);
            break;
          }
          if (isA(node, specialSet) && !isA(node, addressdivpSet))
            break;
        }
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        insertHTMLElement(value, arg3);
        return;

      case "plaintext":
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        insertHTMLElement(value, arg3);
        tokenizer = plaintext_state;
        return;

      case "button":
        if (stack.inScope("button")) {
          in_body_mode(ENDTAG, "button");
          parser(t, value, arg3, arg4);
        }
        else {
          afereconstruct();
          insertHTMLElement(value, arg3);
          frameset_ok = false;
        }
        return;

      case "a":
        var activeElement = afe.findElementByTag("a");
        if (activeElement) {
          in_body_mode(ENDTAG, value);
          afe.remove(activeElement);
          stack.removeElement(activeElement);
        }
        /* falls through */
      case "b":
      case "big":
      case "code":
      case "em":
      case "font":
      case "i":
      case "s":
      case "small":
      case "strike":
      case "strong":
      case "tt":
      case "u":
        afereconstruct();
        afe.push(insertHTMLElement(value,arg3), arg3);
        return;

      case "nobr":
        afereconstruct();

        if (stack.inScope(value)) {
          in_body_mode(ENDTAG, value);
          afereconstruct();
        }
        afe.push(insertHTMLElement(value,arg3), arg3);
        return;

      case "applet":
      case "marquee":
      case "object":
        afereconstruct();
        insertHTMLElement(value,arg3);
        afe.insertMarker();
        frameset_ok = false;
        return;

      case "table":
        if (!doc._quirks && stack.inButtonScope("p")) {
          in_body_mode(ENDTAG, "p");
        }
        insertHTMLElement(value,arg3);
        frameset_ok = false;
        parser = in_table_mode;
        return;

      case "area":
      case "br":
      case "embed":
      case "img":
      case "keygen":
      case "wbr":
        afereconstruct();
        insertHTMLElement(value,arg3);
        stack.pop();
        frameset_ok = false;
        return;

      case "input":
        afereconstruct();
        elt = insertHTMLElement(value,arg3);
        stack.pop();
        var type = elt.getAttribute("type");
        if (!type || type.toLowerCase() !== "hidden")
          frameset_ok = false;
        return;

      case "param":
      case "source":
      case "track":
        insertHTMLElement(value,arg3);
        stack.pop();
        return;

      case "hr":
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        if (isA(stack.top, 'menuitem')) {
          stack.pop();
        }
        insertHTMLElement(value,arg3);
        stack.pop();
        frameset_ok = false;
        return;

      case "image":
        in_body_mode(TAG, "img", arg3, arg4);
        return;

      case "textarea":
        insertHTMLElement(value,arg3);
        ignore_linefeed = true;
        frameset_ok = false;
        tokenizer = rcdata_state;
        originalInsertionMode = parser;
        parser = text_mode;
        return;

      case "xmp":
        if (stack.inButtonScope("p")) in_body_mode(ENDTAG, "p");
        afereconstruct();
        frameset_ok = false;
        parseRawText(value, arg3);
        return;

      case "iframe":
        frameset_ok = false;
        parseRawText(value, arg3);
        return;

      case "noembed":
        parseRawText(value,arg3);
        return;

      case "noscript":
        if (scripting_enabled) {
          parseRawText(value,arg3);
          return;
        }
        break;  // XXX Otherwise treat it as any other open tag?

      case "select":
        afereconstruct();
        insertHTMLElement(value,arg3);
        frameset_ok = false;
        if (parser === in_table_mode ||
          parser === in_caption_mode ||
          parser === in_table_body_mode ||
          parser === in_row_mode ||
          parser === in_cell_mode)
          parser = in_select_in_table_mode;
        else
          parser = in_select_mode;
        return;

      case "optgroup":
      case "option":
        if (stack.top instanceof impl.HTMLOptionElement) {
          in_body_mode(ENDTAG, "option");
        }
        afereconstruct();
        insertHTMLElement(value,arg3);
        return;

      case "menuitem":
        if (isA(stack.top, 'menuitem')) {
          stack.pop();
        }
        afereconstruct();
        insertHTMLElement(value, arg3);
        return;

      case "rb":
      case "rtc":
        if (stack.inScope("ruby")) {
          stack.generateImpliedEndTags();
        }
        insertHTMLElement(value,arg3);
        return;

      case "rp":
      case "rt":
        if (stack.inScope("ruby")) {
          stack.generateImpliedEndTags("rtc");
        }
        insertHTMLElement(value,arg3);
        return;

      case "math":
        afereconstruct();
        adjustMathMLAttributes(arg3);
        adjustForeignAttributes(arg3);
        insertForeignElement(value, arg3, NAMESPACE.MATHML);
        if (arg4) // self-closing flag
          stack.pop();
        return;

      case "svg":
        afereconstruct();
        adjustSVGAttributes(arg3);
        adjustForeignAttributes(arg3);
        insertForeignElement(value, arg3, NAMESPACE.SVG);
        if (arg4) // self-closing flag
          stack.pop();
        return;

      case "caption":
      case "col":
      case "colgroup":
      case "frame":
      case "head":
      case "tbody":
      case "td":
      case "tfoot":
      case "th":
      case "thead":
      case "tr":
        // Ignore table tags if we're not in_table mode
        return;
      }

      // Handle any other start tag here
      // (and also noscript tags when scripting is disabled)
      afereconstruct();
      insertHTMLElement(value,arg3);
      return;

    case 3: // ENDTAG
      switch(value) {
      case "template":
        in_head_mode(ENDTAG, value, arg3);
        return;
      case "body":
        if (!stack.inScope("body")) return;
        parser = after_body_mode;
        return;
      case "html":
        if (!stack.inScope("body")) return;
        parser = after_body_mode;
        parser(t, value, arg3);
        return;

      case "address":
      case "article":
      case "aside":
      case "blockquote":
      case "button":
      case "center":
      case "details":
      case "dialog":
      case "dir":
      case "div":
      case "dl":
      case "fieldset":
      case "figcaption":
      case "figure":
      case "footer":
      case "header":
      case "hgroup":
      case "listing":
      case "main":
      case "menu":
      case "nav":
      case "ol":
      case "pre":
      case "section":
      case "summary":
      case "ul":
        // Ignore if there is not a matching open tag
        if (!stack.inScope(value)) return;
        stack.generateImpliedEndTags();
        stack.popTag(value);
        return;

      case "form":
        if (!stack.contains("template")) {
          var openform = form_element_pointer;
          form_element_pointer = null;
          if (!openform || !stack.elementInScope(openform)) return;
          stack.generateImpliedEndTags();
          stack.removeElement(openform);
        } else {
          if (!stack.inScope("form")) return;
          stack.generateImpliedEndTags();
          stack.popTag("form");
        }
        return;

      case "p":
        if (!stack.inButtonScope(value)) {
          in_body_mode(TAG, value, null);
          parser(t, value, arg3, arg4);
        }
        else {
          stack.generateImpliedEndTags(value);
          stack.popTag(value);
        }
        return;

      case "li":
        if (!stack.inListItemScope(value)) return;
        stack.generateImpliedEndTags(value);
        stack.popTag(value);
        return;

      case "dd":
      case "dt":
        if (!stack.inScope(value)) return;
        stack.generateImpliedEndTags(value);
        stack.popTag(value);
        return;

      case "h1":
      case "h2":
      case "h3":
      case "h4":
      case "h5":
      case "h6":
        if (!stack.elementTypeInScope(impl.HTMLHeadingElement)) return;
        stack.generateImpliedEndTags();
        stack.popElementType(impl.HTMLHeadingElement);
        return;

      case "sarcasm":
        // Take a deep breath, and then:
        break;

      case "a":
      case "b":
      case "big":
      case "code":
      case "em":
      case "font":
      case "i":
      case "nobr":
      case "s":
      case "small":
      case "strike":
      case "strong":
      case "tt":
      case "u":
        var result = adoptionAgency(value);
        if (result) return;  // If we did something we're done
        break;         // Go to the "any other end tag" case

      case "applet":
      case "marquee":
      case "object":
        if (!stack.inScope(value)) return;
        stack.generateImpliedEndTags();
        stack.popTag(value);
        afe.clearToMarker();
        return;

      case "br":
        in_body_mode(TAG, value, null);  // Turn </br> into <br>
        return;
      }

      // Any other end tag goes here
      for(i = stack.elements.length-1; i >= 0; i--) {
        node = stack.elements[i];
        if (isA(node, value)) {
          stack.generateImpliedEndTags(value);
          stack.popElement(node);
          break;
        }
        else if (isA(node, specialSet)) {
          return;
        }
      }

      return;
    }
  }

  function text_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      insertText(value);
      return;
    case -1: // EOF
      if (stack.top instanceof impl.HTMLScriptElement)
        stack.top._already_started = true;
      stack.pop();
      parser = originalInsertionMode;
      parser(t);
      return;
    case 3: // ENDTAG
      if (value === "script") {
        handleScriptEnd();
      }
      else {
        stack.pop();
        parser = originalInsertionMode;
      }
      return;
    default:
      // We should never get any other token types
      return;
    }
  }

  function in_table_mode(t, value, arg3, arg4) {
    function getTypeAttr(attrs) {
      for(var i = 0, n = attrs.length; i < n; i++) {
        if (attrs[i][0] === "type")
          return attrs[i][1].toLowerCase();
      }
      return null;
    }

    switch(t) {
    case 1: // TEXT
      // XXX the text_integration_mode stuff is
      // just a hack I made up
      if (text_integration_mode) {
        in_body_mode(t, value, arg3, arg4);
        return;
      }
      else if (isA(stack.top, tablesectionrowSet)) {
        pending_table_text = [];
        originalInsertionMode = parser;
        parser = in_table_text_mode;
        parser(t, value, arg3, arg4);
        return;
      }
      break;
    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      return;
    case 2: // TAG
      switch(value) {
      case "caption":
        stack.clearToContext(tableContextSet);
        afe.insertMarker();
        insertHTMLElement(value,arg3);
        parser = in_caption_mode;
        return;
      case "colgroup":
        stack.clearToContext(tableContextSet);
        insertHTMLElement(value,arg3);
        parser = in_column_group_mode;
        return;
      case "col":
        in_table_mode(TAG, "colgroup", null);
        parser(t, value, arg3, arg4);
        return;
      case "tbody":
      case "tfoot":
      case "thead":
        stack.clearToContext(tableContextSet);
        insertHTMLElement(value,arg3);
        parser = in_table_body_mode;
        return;
      case "td":
      case "th":
      case "tr":
        in_table_mode(TAG, "tbody", null);
        parser(t, value, arg3, arg4);
        return;

      case "table":
        if (!stack.inTableScope(value)) {
          return; // Ignore the token
        }
        in_table_mode(ENDTAG, value);
        parser(t, value, arg3, arg4);
        return;

      case "style":
      case "script":
      case "template":
        in_head_mode(t, value, arg3, arg4);
        return;

      case "input":
        var type = getTypeAttr(arg3);
        if (type !== "hidden") break;  // to the anything else case
        insertHTMLElement(value,arg3);
        stack.pop();
        return;

      case "form":
        if (form_element_pointer || stack.contains("template")) return;
        form_element_pointer = insertHTMLElement(value, arg3);
        stack.popElement(form_element_pointer);
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "table":
        if (!stack.inTableScope(value)) return;
        stack.popTag(value);
        resetInsertionMode();
        return;
      case "body":
      case "caption":
      case "col":
      case "colgroup":
      case "html":
      case "tbody":
      case "td":
      case "tfoot":
      case "th":
      case "thead":
      case "tr":
        return;
      case "template":
        in_head_mode(t, value, arg3, arg4);
        return;
      }

      break;
    case -1: // EOF
      in_body_mode(t, value, arg3, arg4);
      return;
    }

    // This is the anything else case
    foster_parent_mode = true;
    in_body_mode(t, value, arg3, arg4);
    foster_parent_mode = false;
  }

  function in_table_text_mode(t, value, arg3, arg4) {
    if (t === TEXT) {
      if (textIncludesNUL) {
        value = value.replace(NULCHARS, "");
        if (value.length === 0) return;
      }
      pending_table_text.push(value);
    }
    else {
      var s = pending_table_text.join("");
      pending_table_text.length = 0;
      if (NONWS.test(s)) { // If any non-whitespace characters
        // This must be the same code as the "anything else"
        // case of the in_table mode above.
        foster_parent_mode = true;
        in_body_mode(TEXT, s);
        foster_parent_mode = false;
      }
      else {
        insertText(s);
      }
      parser = originalInsertionMode;
      parser(t, value, arg3, arg4);
    }
  }


  function in_caption_mode(t, value, arg3, arg4) {
    function end_caption() {
      if (!stack.inTableScope("caption")) return false;
      stack.generateImpliedEndTags();
      stack.popTag("caption");
      afe.clearToMarker();
      parser = in_table_mode;
      return true;
    }

    switch(t) {
    case 2: // TAG
      switch(value) {
      case "caption":
      case "col":
      case "colgroup":
      case "tbody":
      case "td":
      case "tfoot":
      case "th":
      case "thead":
      case "tr":
        if (end_caption()) parser(t, value, arg3, arg4);
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "caption":
        end_caption();
        return;
      case "table":
        if (end_caption()) parser(t, value, arg3, arg4);
        return;
      case "body":
      case "col":
      case "colgroup":
      case "html":
      case "tbody":
      case "td":
      case "tfoot":
      case "th":
      case "thead":
      case "tr":
        return;
      }
      break;
    }

    // The Anything Else case
    in_body_mode(t, value, arg3, arg4);
  }

  function in_column_group_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      var ws = value.match(LEADINGWS);
      if (ws) {
        insertText(ws[0]);
        value = value.substring(ws[0].length);
      }
      if (value.length === 0) return;
      break; // Handle non-whitespace below

    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "col":
        insertHTMLElement(value, arg3);
        stack.pop();
        return;
      case "template":
        in_head_mode(t, value, arg3, arg4);
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "colgroup":
        if (!isA(stack.top, 'colgroup')) {
          return; // Ignore the token.
        }
        stack.pop();
        parser = in_table_mode;
        return;
      case "col":
        return;
      case "template":
        in_head_mode(t, value, arg3, arg4);
        return;
      }
      break;
    case -1: // EOF
      in_body_mode(t, value, arg3, arg4);
      return;
    }

    // Anything else
    if (!isA(stack.top, 'colgroup')) {
      return; // Ignore the token.
    }
    in_column_group_mode(ENDTAG, "colgroup");
    parser(t, value, arg3, arg4);
  }

  function in_table_body_mode(t, value, arg3, arg4) {
    function endsect() {
      if (!stack.inTableScope("tbody") &&
        !stack.inTableScope("thead") &&
        !stack.inTableScope("tfoot"))
        return;
      stack.clearToContext(tableBodyContextSet);
      in_table_body_mode(ENDTAG, stack.top.localName, null);
      parser(t, value, arg3, arg4);
    }

    switch(t) {
    case 2: // TAG
      switch(value) {
      case "tr":
        stack.clearToContext(tableBodyContextSet);
        insertHTMLElement(value, arg3);
        parser = in_row_mode;
        return;
      case "th":
      case "td":
        in_table_body_mode(TAG, "tr", null);
        parser(t, value, arg3, arg4);
        return;
      case "caption":
      case "col":
      case "colgroup":
      case "tbody":
      case "tfoot":
      case "thead":
        endsect();
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "table":
        endsect();
        return;
      case "tbody":
      case "tfoot":
      case "thead":
        if (stack.inTableScope(value)) {
          stack.clearToContext(tableBodyContextSet);
          stack.pop();
          parser = in_table_mode;
        }
        return;
      case "body":
      case "caption":
      case "col":
      case "colgroup":
      case "html":
      case "td":
      case "th":
      case "tr":
        return;
      }
      break;
    }

    // Anything else:
    in_table_mode(t, value, arg3, arg4);
  }

  function in_row_mode(t, value, arg3, arg4) {
    function endrow() {
      if (!stack.inTableScope("tr")) return false;
      stack.clearToContext(tableRowContextSet);
      stack.pop();
      parser = in_table_body_mode;
      return true;
    }

    switch(t) {
    case 2: // TAG
      switch(value) {
      case "th":
      case "td":
        stack.clearToContext(tableRowContextSet);
        insertHTMLElement(value, arg3);
        parser = in_cell_mode;
        afe.insertMarker();
        return;
      case "caption":
      case "col":
      case "colgroup":
      case "tbody":
      case "tfoot":
      case "thead":
      case "tr":
        if (endrow()) parser(t, value, arg3, arg4);
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "tr":
        endrow();
        return;
      case "table":
        if (endrow()) parser(t, value, arg3, arg4);
        return;
      case "tbody":
      case "tfoot":
      case "thead":
        if (stack.inTableScope(value)) {
          if (endrow()) parser(t, value, arg3, arg4);
        }
        return;
      case "body":
      case "caption":
      case "col":
      case "colgroup":
      case "html":
      case "td":
      case "th":
        return;
      }
      break;
    }

    // anything else
    in_table_mode(t, value, arg3, arg4);
  }

  function in_cell_mode(t, value, arg3, arg4) {
    switch(t) {
    case 2: // TAG
      switch(value) {
      case "caption":
      case "col":
      case "colgroup":
      case "tbody":
      case "td":
      case "tfoot":
      case "th":
      case "thead":
      case "tr":
        if (stack.inTableScope("td")) {
          in_cell_mode(ENDTAG, "td");
          parser(t, value, arg3, arg4);
        }
        else if (stack.inTableScope("th")) {
          in_cell_mode(ENDTAG, "th");
          parser(t, value, arg3, arg4);
        }
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "td":
      case "th":
        if (!stack.inTableScope(value)) return;
        stack.generateImpliedEndTags();
        stack.popTag(value);
        afe.clearToMarker();
        parser = in_row_mode;
        return;

      case "body":
      case "caption":
      case "col":
      case "colgroup":
      case "html":
        return;

      case "table":
      case "tbody":
      case "tfoot":
      case "thead":
      case "tr":
        if (!stack.inTableScope(value)) return;
        in_cell_mode(ENDTAG, stack.inTableScope("td") ? "td" : "th");
        parser(t, value, arg3, arg4);
        return;
      }
      break;
    }

    // anything else
    in_body_mode(t, value, arg3, arg4);
  }

  function in_select_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      if (textIncludesNUL) {
        value = value.replace(NULCHARS, "");
        if (value.length === 0) return;
      }
      insertText(value);
      return;
    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      return;
    case -1: // EOF
      in_body_mode(t, value, arg3, arg4);
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "option":
        if (stack.top instanceof impl.HTMLOptionElement)
          in_select_mode(ENDTAG, value);
        insertHTMLElement(value, arg3);
        return;
      case "optgroup":
        if (stack.top instanceof impl.HTMLOptionElement)
          in_select_mode(ENDTAG, "option");
        if (stack.top instanceof impl.HTMLOptGroupElement)
          in_select_mode(ENDTAG, value);
        insertHTMLElement(value, arg3);
        return;
      case "select":
        in_select_mode(ENDTAG, value); // treat it as a close tag
        return;

      case "input":
      case "keygen":
      case "textarea":
        if (!stack.inSelectScope("select")) return;
        in_select_mode(ENDTAG, "select");
        parser(t, value, arg3, arg4);
        return;

      case "script":
      case "template":
        in_head_mode(t, value, arg3, arg4);
        return;
      }
      break;
    case 3: // ENDTAG
      switch(value) {
      case "optgroup":
        if (stack.top instanceof impl.HTMLOptionElement &&
          stack.elements[stack.elements.length-2] instanceof
          impl.HTMLOptGroupElement) {
          in_select_mode(ENDTAG, "option");
        }
        if (stack.top instanceof impl.HTMLOptGroupElement)
          stack.pop();

        return;

      case "option":
        if (stack.top instanceof impl.HTMLOptionElement)
          stack.pop();
        return;

      case "select":
        if (!stack.inSelectScope(value)) return;
        stack.popTag(value);
        resetInsertionMode();
        return;

      case "template":
        in_head_mode(t, value, arg3, arg4);
        return;
      }

      break;
    }

    // anything else: just ignore the token
  }

  function in_select_in_table_mode(t, value, arg3, arg4) {
    switch(value) {
    case "caption":
    case "table":
    case "tbody":
    case "tfoot":
    case "thead":
    case "tr":
    case "td":
    case "th":
      switch(t) {
      case 2: // TAG
        in_select_in_table_mode(ENDTAG, "select");
        parser(t, value, arg3, arg4);
        return;
      case 3: // ENDTAG
        if (stack.inTableScope(value)) {
          in_select_in_table_mode(ENDTAG, "select");
          parser(t, value, arg3, arg4);
        }
        return;
      }
    }

    // anything else
    in_select_mode(t, value, arg3, arg4);
  }

  function in_template_mode(t, value, arg3, arg4) {
    function switchModeAndReprocess(mode) {
      parser = mode;
      templateInsertionModes[templateInsertionModes.length-1] = parser;
      parser(t, value, arg3, arg4);
    }
    switch(t) {
    case 1: // TEXT
    case 4: // COMMENT
    case 5: // DOCTYPE
      in_body_mode(t, value, arg3, arg4);
      return;
    case -1: // EOF
      if (!stack.contains("template")) {
        stopParsing();
      } else {
        stack.popTag("template");
        afe.clearToMarker();
        templateInsertionModes.pop();
        resetInsertionMode();
        parser(t, value, arg3, arg4);
      }
      return;
    case 2: // TAG
      switch(value) {
      case "base":
      case "basefont":
      case "bgsound":
      case "link":
      case "meta":
      case "noframes":
      case "script":
      case "style":
      case "template":
      case "title":
        in_head_mode(t, value, arg3, arg4);
        return;
      case "caption":
      case "colgroup":
      case "tbody":
      case "tfoot":
      case "thead":
        switchModeAndReprocess(in_table_mode);
        return;
      case "col":
        switchModeAndReprocess(in_column_group_mode);
        return;
      case "tr":
        switchModeAndReprocess(in_table_body_mode);
        return;
      case "td":
      case "th":
        switchModeAndReprocess(in_row_mode);
        return;
      }
      switchModeAndReprocess(in_body_mode);
      return;
    case 3: // ENDTAG
      switch(value) {
      case "template":
        in_head_mode(t, value, arg3, arg4);
        return;
      default:
        return;
      }
    }
  }

  function after_body_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      // If any non-space chars, handle below
      if (NONWS.test(value)) break;
      in_body_mode(t, value);
      return;
    case 4: // COMMENT
      // Append it to the <html> element
      stack.elements[0]._appendChild(doc.createComment(value));
      return;
    case 5: // DOCTYPE
      return;
    case -1: // EOF
      stopParsing();
      return;
    case 2: // TAG
      if (value === "html") {
        in_body_mode(t, value, arg3, arg4);
        return;
      }
      break; // for any other tags
    case 3: // ENDTAG
      if (value === "html") {
        if (fragment) return;
        parser = after_after_body_mode;
        return;
      }
      break; // for any other tags
    }

    // anything else
    parser = in_body_mode;
    parser(t, value, arg3, arg4);
  }

  function in_frameset_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      // Ignore any non-space characters
      value = value.replace(ALLNONWS, "");
      if (value.length > 0) insertText(value);
      return;
    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      return;
    case -1: // EOF
      stopParsing();
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "frameset":
        insertHTMLElement(value, arg3);
        return;
      case "frame":
        insertHTMLElement(value, arg3);
        stack.pop();
        return;
      case "noframes":
        in_head_mode(t, value, arg3, arg4);
        return;
      }
      break;
    case 3: // ENDTAG
      if (value === "frameset") {
        if (fragment && stack.top instanceof impl.HTMLHtmlElement)
          return;
        stack.pop();
        if (!fragment &&
          !(stack.top instanceof impl.HTMLFrameSetElement))
          parser = after_frameset_mode;
        return;
      }
      break;
    }

    // ignore anything else
  }

  function after_frameset_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      // Ignore any non-space characters
      value = value.replace(ALLNONWS, "");
      if (value.length > 0) insertText(value);
      return;
    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      return;
    case -1: // EOF
      stopParsing();
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "noframes":
        in_head_mode(t, value, arg3, arg4);
        return;
      }
      break;
    case 3: // ENDTAG
      if (value === "html") {
        parser = after_after_frameset_mode;
        return;
      }
      break;
    }

    // ignore anything else
  }

  function after_after_body_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      // If any non-space chars, handle below
      if (NONWS.test(value)) break;
      in_body_mode(t, value, arg3, arg4);
      return;
    case 4: // COMMENT
      doc._appendChild(doc.createComment(value));
      return;
    case 5: // DOCTYPE
      in_body_mode(t, value, arg3, arg4);
      return;
    case -1: // EOF
      stopParsing();
      return;
    case 2: // TAG
      if (value === "html") {
        in_body_mode(t, value, arg3, arg4);
        return;
      }
      break;
    }

    // anything else
    parser = in_body_mode;
    parser(t, value, arg3, arg4);
  }

  function after_after_frameset_mode(t, value, arg3, arg4) {
    switch(t) {
    case 1: // TEXT
      // Ignore any non-space characters
      value = value.replace(ALLNONWS, "");
      if (value.length > 0)
        in_body_mode(t, value, arg3, arg4);
      return;
    case 4: // COMMENT
      doc._appendChild(doc.createComment(value));
      return;
    case 5: // DOCTYPE
      in_body_mode(t, value, arg3, arg4);
      return;
    case -1: // EOF
      stopParsing();
      return;
    case 2: // TAG
      switch(value) {
      case "html":
        in_body_mode(t, value, arg3, arg4);
        return;
      case "noframes":
        in_head_mode(t, value, arg3, arg4);
        return;
      }
      break;
    }

    // ignore anything else
  }


  // 13.2.5.5 The rules for parsing tokens in foreign content
  //
  // This is like one of the insertion modes above, but is
  // invoked somewhat differently when the current token is not HTML.
  // See the insertToken() function.
  function insertForeignToken(t, value, arg3, arg4) {
    // A <font> tag is an HTML font tag if it has a color, font, or size
    // attribute.  Otherwise we assume it is foreign content
    function isHTMLFont(attrs) {
      for(var i = 0, n = attrs.length; i < n; i++) {
        switch(attrs[i][0]) {
        case "color":
        case "face":
        case "size":
          return true;
        }
      }
      return false;
    }

    var current;

    switch(t) {
    case 1: // TEXT
      // If any non-space, non-nul characters
      if (frameset_ok && NONWSNONNUL.test(value))
        frameset_ok = false;
      if (textIncludesNUL) {
        value = value.replace(NULCHARS, "\uFFFD");
      }
      insertText(value);
      return;
    case 4: // COMMENT
      insertComment(value);
      return;
    case 5: // DOCTYPE
      // ignore it
      return;
    case 2: // TAG
      switch(value) {
      case "font":
        if (!isHTMLFont(arg3)) break;
        /* falls through */
      case "b":
      case "big":
      case "blockquote":
      case "body":
      case "br":
      case "center":
      case "code":
      case "dd":
      case "div":
      case "dl":
      case "dt":
      case "em":
      case "embed":
      case "h1":
      case "h2":
      case "h3":
      case "h4":
      case "h5":
      case "h6":
      case "head":
      case "hr":
      case "i":
      case "img":
      case "li":
      case "listing":
      case "menu":
      case "meta":
      case "nobr":
      case "ol":
      case "p":
      case "pre":
      case "ruby":
      case "s":
      case "small":
      case "span":
      case "strong":
      case "strike":
      case "sub":
      case "sup":
      case "table":
      case "tt":
      case "u":
      case "ul":
      case "var":
        if (fragment) {
          break;
        }
        do {
          stack.pop();
          current = stack.top;
        } while(current.namespaceURI !== NAMESPACE.HTML &&
            !isMathmlTextIntegrationPoint(current) &&
            !isHTMLIntegrationPoint(current));

        insertToken(t, value, arg3, arg4);  // reprocess
        return;
      }

      // Any other start tag case goes here
      current = (stack.elements.length===1 && fragment) ? fragmentContext :
        stack.top;
      if (current.namespaceURI === NAMESPACE.MATHML) {
        adjustMathMLAttributes(arg3);
      }
      else if (current.namespaceURI === NAMESPACE.SVG) {
        value = adjustSVGTagName(value);
        adjustSVGAttributes(arg3);
      }
      adjustForeignAttributes(arg3);

      insertForeignElement(value, arg3, current.namespaceURI);
      if (arg4) { // the self-closing flag
        if (value === 'script' && current.namespaceURI === NAMESPACE.SVG) {
          // XXX deal with SVG scripts here
        }
        stack.pop();
      }
      return;

    case 3: // ENDTAG
      current = stack.top;
      if (value === "script" &&
        current.namespaceURI === NAMESPACE.SVG &&
        current.localName === "script") {

        stack.pop();

        // XXX
        // Deal with SVG scripts here
      }
      else {
        // The any other end tag case
        var i = stack.elements.length-1;
        var node = stack.elements[i];
        for(;;) {
          if (node.localName.toLowerCase() === value) {
            stack.popElement(node);
            break;
          }
          node = stack.elements[--i];
          // If non-html, keep looping
          if (node.namespaceURI !== NAMESPACE.HTML)
            continue;
          // Otherwise process the end tag as html
          parser(t, value, arg3, arg4);
          break;
        }
      }
      return;
    }
  }

  /***
   * Finally, this is the end of the HTMLParser() factory function.
   * It returns the htmlparser object with the append() and end() methods.
   */

  // Sneak another method into the htmlparser object to allow us to run
  // tokenizer tests.  This can be commented out in production code.
  // This is a hook for testing the tokenizer. It has to be here
  // because the tokenizer details are all hidden away within the closure.
  // It should return an array of tokens generated while parsing the
  // input string.
  htmlparser.testTokenizer = function(input, initialState, lastStartTag, charbychar) {
    var tokens = [];

    switch(initialState) {
    case "PCDATA state":
      tokenizer = data_state;
      break;
    case "RCDATA state":
      tokenizer = rcdata_state;
      break;
    case "RAWTEXT state":
      tokenizer = rawtext_state;
      break;
    case "PLAINTEXT state":
      tokenizer = plaintext_state;
      break;
    }

    if (lastStartTag) {
      lasttagname = lastStartTag;
    }

    insertToken = function(t, value, arg3, arg4) {
      flushText();
      switch(t) {
      case 1: // TEXT
        if (tokens.length > 0 &&
          tokens[tokens.length-1][0] === "Character") {
          tokens[tokens.length-1][1] += value;
        }
        else tokens.push(["Character", value]);
        break;
      case 4: // COMMENT
        tokens.push(["Comment", value]);
        break;
      case 5: // DOCTYPE
        tokens.push(["DOCTYPE", value,
               arg3 === undefined ? null : arg3,
               arg4 === undefined ? null : arg4,
               !force_quirks]);
        break;
      case 2: // TAG
        var attrs = Object.create(null);
        for(var i = 0; i < arg3.length; i++) {
          // XXX: does attribute order matter?
          var a = arg3[i];
          if (a.length === 1) {
            attrs[a[0]] = "";
          }
          else {
            attrs[a[0]] = a[1];
          }
        }
        var token = ["StartTag", value, attrs];
        if (arg4) token.push(true);
        tokens.push(token);
        break;
      case 3: // ENDTAG
        tokens.push(["EndTag", value]);
        break;
      case -1: // EOF
        break;
      }
    };

    if (!charbychar) {
      this.parse(input, true);
    }
    else {
      for(var i = 0; i < input.length; i++) {
        this.parse(input[i]);
      }
      this.parse("", true);
    }
    return tokens;
  };

  // Return the parser object from the HTMLParser() factory function
  return htmlparser;
}