| 'use strict'; | 
|   | 
| const Tokenizer = require('../tokenizer'); | 
| const HTML = require('./html'); | 
|   | 
| //Aliases | 
| const $ = HTML.TAG_NAMES; | 
| const NS = HTML.NAMESPACES; | 
| const ATTRS = HTML.ATTRS; | 
|   | 
| //MIME types | 
| const MIME_TYPES = { | 
|     TEXT_HTML: 'text/html', | 
|     APPLICATION_XML: 'application/xhtml+xml' | 
| }; | 
|   | 
| //Attributes | 
| const DEFINITION_URL_ATTR = 'definitionurl'; | 
| const ADJUSTED_DEFINITION_URL_ATTR = 'definitionURL'; | 
| const SVG_ATTRS_ADJUSTMENT_MAP = { | 
|     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' | 
| }; | 
|   | 
| const XML_ATTRS_ADJUSTMENT_MAP = { | 
|     'xlink:actuate': { prefix: 'xlink', name: 'actuate', namespace: NS.XLINK }, | 
|     'xlink:arcrole': { prefix: 'xlink', name: 'arcrole', namespace: NS.XLINK }, | 
|     'xlink:href': { prefix: 'xlink', name: 'href', namespace: NS.XLINK }, | 
|     'xlink:role': { prefix: 'xlink', name: 'role', namespace: NS.XLINK }, | 
|     'xlink:show': { prefix: 'xlink', name: 'show', namespace: NS.XLINK }, | 
|     'xlink:title': { prefix: 'xlink', name: 'title', namespace: NS.XLINK }, | 
|     'xlink:type': { prefix: 'xlink', name: 'type', namespace: NS.XLINK }, | 
|     'xml:base': { prefix: 'xml', name: 'base', namespace: NS.XML }, | 
|     'xml:lang': { prefix: 'xml', name: 'lang', namespace: NS.XML }, | 
|     'xml:space': { prefix: 'xml', name: 'space', namespace: NS.XML }, | 
|     xmlns: { prefix: '', name: 'xmlns', namespace: NS.XMLNS }, | 
|     'xmlns:xlink': { prefix: 'xmlns', name: 'xlink', namespace: NS.XMLNS } | 
| }; | 
|   | 
| //SVG tag names adjustment map | 
| const SVG_TAG_NAMES_ADJUSTMENT_MAP = (exports.SVG_TAG_NAMES_ADJUSTMENT_MAP = { | 
|     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' | 
| }); | 
|   | 
| //Tags that causes exit from foreign content | 
| const EXITS_FOREIGN_CONTENT = { | 
|     [$.B]: true, | 
|     [$.BIG]: true, | 
|     [$.BLOCKQUOTE]: true, | 
|     [$.BODY]: true, | 
|     [$.BR]: true, | 
|     [$.CENTER]: true, | 
|     [$.CODE]: true, | 
|     [$.DD]: true, | 
|     [$.DIV]: true, | 
|     [$.DL]: true, | 
|     [$.DT]: true, | 
|     [$.EM]: true, | 
|     [$.EMBED]: true, | 
|     [$.H1]: true, | 
|     [$.H2]: true, | 
|     [$.H3]: true, | 
|     [$.H4]: true, | 
|     [$.H5]: true, | 
|     [$.H6]: true, | 
|     [$.HEAD]: true, | 
|     [$.HR]: true, | 
|     [$.I]: true, | 
|     [$.IMG]: true, | 
|     [$.LI]: true, | 
|     [$.LISTING]: true, | 
|     [$.MENU]: true, | 
|     [$.META]: true, | 
|     [$.NOBR]: true, | 
|     [$.OL]: true, | 
|     [$.P]: true, | 
|     [$.PRE]: true, | 
|     [$.RUBY]: true, | 
|     [$.S]: true, | 
|     [$.SMALL]: true, | 
|     [$.SPAN]: true, | 
|     [$.STRONG]: true, | 
|     [$.STRIKE]: true, | 
|     [$.SUB]: true, | 
|     [$.SUP]: true, | 
|     [$.TABLE]: true, | 
|     [$.TT]: true, | 
|     [$.U]: true, | 
|     [$.UL]: true, | 
|     [$.VAR]: true | 
| }; | 
|   | 
| //Check exit from foreign content | 
| exports.causesExit = function(startTagToken) { | 
|     const tn = startTagToken.tagName; | 
|     const isFontWithAttrs = | 
|         tn === $.FONT && | 
|         (Tokenizer.getTokenAttr(startTagToken, ATTRS.COLOR) !== null || | 
|             Tokenizer.getTokenAttr(startTagToken, ATTRS.SIZE) !== null || | 
|             Tokenizer.getTokenAttr(startTagToken, ATTRS.FACE) !== null); | 
|   | 
|     return isFontWithAttrs ? true : EXITS_FOREIGN_CONTENT[tn]; | 
| }; | 
|   | 
| //Token adjustments | 
| exports.adjustTokenMathMLAttrs = function(token) { | 
|     for (let i = 0; i < token.attrs.length; i++) { | 
|         if (token.attrs[i].name === DEFINITION_URL_ATTR) { | 
|             token.attrs[i].name = ADJUSTED_DEFINITION_URL_ATTR; | 
|             break; | 
|         } | 
|     } | 
| }; | 
|   | 
| exports.adjustTokenSVGAttrs = function(token) { | 
|     for (let i = 0; i < token.attrs.length; i++) { | 
|         const adjustedAttrName = SVG_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name]; | 
|   | 
|         if (adjustedAttrName) { | 
|             token.attrs[i].name = adjustedAttrName; | 
|         } | 
|     } | 
| }; | 
|   | 
| exports.adjustTokenXMLAttrs = function(token) { | 
|     for (let i = 0; i < token.attrs.length; i++) { | 
|         const adjustedAttrEntry = XML_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name]; | 
|   | 
|         if (adjustedAttrEntry) { | 
|             token.attrs[i].prefix = adjustedAttrEntry.prefix; | 
|             token.attrs[i].name = adjustedAttrEntry.name; | 
|             token.attrs[i].namespace = adjustedAttrEntry.namespace; | 
|         } | 
|     } | 
| }; | 
|   | 
| exports.adjustTokenSVGTagName = function(token) { | 
|     const adjustedTagName = SVG_TAG_NAMES_ADJUSTMENT_MAP[token.tagName]; | 
|   | 
|     if (adjustedTagName) { | 
|         token.tagName = adjustedTagName; | 
|     } | 
| }; | 
|   | 
| //Integration points | 
| function isMathMLTextIntegrationPoint(tn, ns) { | 
|     return ns === NS.MATHML && (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS || tn === $.MTEXT); | 
| } | 
|   | 
| function isHtmlIntegrationPoint(tn, ns, attrs) { | 
|     if (ns === NS.MATHML && tn === $.ANNOTATION_XML) { | 
|         for (let i = 0; i < attrs.length; i++) { | 
|             if (attrs[i].name === ATTRS.ENCODING) { | 
|                 const value = attrs[i].value.toLowerCase(); | 
|   | 
|                 return value === MIME_TYPES.TEXT_HTML || value === MIME_TYPES.APPLICATION_XML; | 
|             } | 
|         } | 
|     } | 
|   | 
|     return ns === NS.SVG && (tn === $.FOREIGN_OBJECT || tn === $.DESC || tn === $.TITLE); | 
| } | 
|   | 
| exports.isIntegrationPoint = function(tn, ns, attrs, foreignNS) { | 
|     if ((!foreignNS || foreignNS === NS.HTML) && isHtmlIntegrationPoint(tn, ns, attrs)) { | 
|         return true; | 
|     } | 
|   | 
|     if ((!foreignNS || foreignNS === NS.MATHML) && isMathMLTextIntegrationPoint(tn, ns)) { | 
|         return true; | 
|     } | 
|   | 
|     return false; | 
| }; |