You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
8.0 KiB
265 lines
8.0 KiB
'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; |
|
};
|
|
|