• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const Tokenizer = require('../tokenizer');
4const HTML = require('./html');
5
6//Aliases
7const $ = HTML.TAG_NAMES;
8const NS = HTML.NAMESPACES;
9const ATTRS = HTML.ATTRS;
10
11//MIME types
12const MIME_TYPES = {
13    TEXT_HTML: 'text/html',
14    APPLICATION_XML: 'application/xhtml+xml'
15};
16
17//Attributes
18const DEFINITION_URL_ATTR = 'definitionurl';
19const ADJUSTED_DEFINITION_URL_ATTR = 'definitionURL';
20const SVG_ATTRS_ADJUSTMENT_MAP = {
21    attributename: 'attributeName',
22    attributetype: 'attributeType',
23    basefrequency: 'baseFrequency',
24    baseprofile: 'baseProfile',
25    calcmode: 'calcMode',
26    clippathunits: 'clipPathUnits',
27    diffuseconstant: 'diffuseConstant',
28    edgemode: 'edgeMode',
29    filterunits: 'filterUnits',
30    glyphref: 'glyphRef',
31    gradienttransform: 'gradientTransform',
32    gradientunits: 'gradientUnits',
33    kernelmatrix: 'kernelMatrix',
34    kernelunitlength: 'kernelUnitLength',
35    keypoints: 'keyPoints',
36    keysplines: 'keySplines',
37    keytimes: 'keyTimes',
38    lengthadjust: 'lengthAdjust',
39    limitingconeangle: 'limitingConeAngle',
40    markerheight: 'markerHeight',
41    markerunits: 'markerUnits',
42    markerwidth: 'markerWidth',
43    maskcontentunits: 'maskContentUnits',
44    maskunits: 'maskUnits',
45    numoctaves: 'numOctaves',
46    pathlength: 'pathLength',
47    patterncontentunits: 'patternContentUnits',
48    patterntransform: 'patternTransform',
49    patternunits: 'patternUnits',
50    pointsatx: 'pointsAtX',
51    pointsaty: 'pointsAtY',
52    pointsatz: 'pointsAtZ',
53    preservealpha: 'preserveAlpha',
54    preserveaspectratio: 'preserveAspectRatio',
55    primitiveunits: 'primitiveUnits',
56    refx: 'refX',
57    refy: 'refY',
58    repeatcount: 'repeatCount',
59    repeatdur: 'repeatDur',
60    requiredextensions: 'requiredExtensions',
61    requiredfeatures: 'requiredFeatures',
62    specularconstant: 'specularConstant',
63    specularexponent: 'specularExponent',
64    spreadmethod: 'spreadMethod',
65    startoffset: 'startOffset',
66    stddeviation: 'stdDeviation',
67    stitchtiles: 'stitchTiles',
68    surfacescale: 'surfaceScale',
69    systemlanguage: 'systemLanguage',
70    tablevalues: 'tableValues',
71    targetx: 'targetX',
72    targety: 'targetY',
73    textlength: 'textLength',
74    viewbox: 'viewBox',
75    viewtarget: 'viewTarget',
76    xchannelselector: 'xChannelSelector',
77    ychannelselector: 'yChannelSelector',
78    zoomandpan: 'zoomAndPan'
79};
80
81const XML_ATTRS_ADJUSTMENT_MAP = {
82    'xlink:actuate': { prefix: 'xlink', name: 'actuate', namespace: NS.XLINK },
83    'xlink:arcrole': { prefix: 'xlink', name: 'arcrole', namespace: NS.XLINK },
84    'xlink:href': { prefix: 'xlink', name: 'href', namespace: NS.XLINK },
85    'xlink:role': { prefix: 'xlink', name: 'role', namespace: NS.XLINK },
86    'xlink:show': { prefix: 'xlink', name: 'show', namespace: NS.XLINK },
87    'xlink:title': { prefix: 'xlink', name: 'title', namespace: NS.XLINK },
88    'xlink:type': { prefix: 'xlink', name: 'type', namespace: NS.XLINK },
89    'xml:base': { prefix: 'xml', name: 'base', namespace: NS.XML },
90    'xml:lang': { prefix: 'xml', name: 'lang', namespace: NS.XML },
91    'xml:space': { prefix: 'xml', name: 'space', namespace: NS.XML },
92    xmlns: { prefix: '', name: 'xmlns', namespace: NS.XMLNS },
93    'xmlns:xlink': { prefix: 'xmlns', name: 'xlink', namespace: NS.XMLNS }
94};
95
96//SVG tag names adjustment map
97const SVG_TAG_NAMES_ADJUSTMENT_MAP = (exports.SVG_TAG_NAMES_ADJUSTMENT_MAP = {
98    altglyph: 'altGlyph',
99    altglyphdef: 'altGlyphDef',
100    altglyphitem: 'altGlyphItem',
101    animatecolor: 'animateColor',
102    animatemotion: 'animateMotion',
103    animatetransform: 'animateTransform',
104    clippath: 'clipPath',
105    feblend: 'feBlend',
106    fecolormatrix: 'feColorMatrix',
107    fecomponenttransfer: 'feComponentTransfer',
108    fecomposite: 'feComposite',
109    feconvolvematrix: 'feConvolveMatrix',
110    fediffuselighting: 'feDiffuseLighting',
111    fedisplacementmap: 'feDisplacementMap',
112    fedistantlight: 'feDistantLight',
113    feflood: 'feFlood',
114    fefunca: 'feFuncA',
115    fefuncb: 'feFuncB',
116    fefuncg: 'feFuncG',
117    fefuncr: 'feFuncR',
118    fegaussianblur: 'feGaussianBlur',
119    feimage: 'feImage',
120    femerge: 'feMerge',
121    femergenode: 'feMergeNode',
122    femorphology: 'feMorphology',
123    feoffset: 'feOffset',
124    fepointlight: 'fePointLight',
125    fespecularlighting: 'feSpecularLighting',
126    fespotlight: 'feSpotLight',
127    fetile: 'feTile',
128    feturbulence: 'feTurbulence',
129    foreignobject: 'foreignObject',
130    glyphref: 'glyphRef',
131    lineargradient: 'linearGradient',
132    radialgradient: 'radialGradient',
133    textpath: 'textPath'
134});
135
136//Tags that causes exit from foreign content
137const EXITS_FOREIGN_CONTENT = {
138    [$.B]: true,
139    [$.BIG]: true,
140    [$.BLOCKQUOTE]: true,
141    [$.BODY]: true,
142    [$.BR]: true,
143    [$.CENTER]: true,
144    [$.CODE]: true,
145    [$.DD]: true,
146    [$.DIV]: true,
147    [$.DL]: true,
148    [$.DT]: true,
149    [$.EM]: true,
150    [$.EMBED]: true,
151    [$.H1]: true,
152    [$.H2]: true,
153    [$.H3]: true,
154    [$.H4]: true,
155    [$.H5]: true,
156    [$.H6]: true,
157    [$.HEAD]: true,
158    [$.HR]: true,
159    [$.I]: true,
160    [$.IMG]: true,
161    [$.LI]: true,
162    [$.LISTING]: true,
163    [$.MENU]: true,
164    [$.META]: true,
165    [$.NOBR]: true,
166    [$.OL]: true,
167    [$.P]: true,
168    [$.PRE]: true,
169    [$.RUBY]: true,
170    [$.S]: true,
171    [$.SMALL]: true,
172    [$.SPAN]: true,
173    [$.STRONG]: true,
174    [$.STRIKE]: true,
175    [$.SUB]: true,
176    [$.SUP]: true,
177    [$.TABLE]: true,
178    [$.TT]: true,
179    [$.U]: true,
180    [$.UL]: true,
181    [$.VAR]: true
182};
183
184//Check exit from foreign content
185exports.causesExit = function(startTagToken) {
186    const tn = startTagToken.tagName;
187    const isFontWithAttrs =
188        tn === $.FONT &&
189        (Tokenizer.getTokenAttr(startTagToken, ATTRS.COLOR) !== null ||
190            Tokenizer.getTokenAttr(startTagToken, ATTRS.SIZE) !== null ||
191            Tokenizer.getTokenAttr(startTagToken, ATTRS.FACE) !== null);
192
193    return isFontWithAttrs ? true : EXITS_FOREIGN_CONTENT[tn];
194};
195
196//Token adjustments
197exports.adjustTokenMathMLAttrs = function(token) {
198    for (let i = 0; i < token.attrs.length; i++) {
199        if (token.attrs[i].name === DEFINITION_URL_ATTR) {
200            token.attrs[i].name = ADJUSTED_DEFINITION_URL_ATTR;
201            break;
202        }
203    }
204};
205
206exports.adjustTokenSVGAttrs = function(token) {
207    for (let i = 0; i < token.attrs.length; i++) {
208        const adjustedAttrName = SVG_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name];
209
210        if (adjustedAttrName) {
211            token.attrs[i].name = adjustedAttrName;
212        }
213    }
214};
215
216exports.adjustTokenXMLAttrs = function(token) {
217    for (let i = 0; i < token.attrs.length; i++) {
218        const adjustedAttrEntry = XML_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name];
219
220        if (adjustedAttrEntry) {
221            token.attrs[i].prefix = adjustedAttrEntry.prefix;
222            token.attrs[i].name = adjustedAttrEntry.name;
223            token.attrs[i].namespace = adjustedAttrEntry.namespace;
224        }
225    }
226};
227
228exports.adjustTokenSVGTagName = function(token) {
229    const adjustedTagName = SVG_TAG_NAMES_ADJUSTMENT_MAP[token.tagName];
230
231    if (adjustedTagName) {
232        token.tagName = adjustedTagName;
233    }
234};
235
236//Integration points
237function isMathMLTextIntegrationPoint(tn, ns) {
238    return ns === NS.MATHML && (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS || tn === $.MTEXT);
239}
240
241function isHtmlIntegrationPoint(tn, ns, attrs) {
242    if (ns === NS.MATHML && tn === $.ANNOTATION_XML) {
243        for (let i = 0; i < attrs.length; i++) {
244            if (attrs[i].name === ATTRS.ENCODING) {
245                const value = attrs[i].value.toLowerCase();
246
247                return value === MIME_TYPES.TEXT_HTML || value === MIME_TYPES.APPLICATION_XML;
248            }
249        }
250    }
251
252    return ns === NS.SVG && (tn === $.FOREIGN_OBJECT || tn === $.DESC || tn === $.TITLE);
253}
254
255exports.isIntegrationPoint = function(tn, ns, attrs, foreignNS) {
256    if ((!foreignNS || foreignNS === NS.HTML) && isHtmlIntegrationPoint(tn, ns, attrs)) {
257        return true;
258    }
259
260    if ((!foreignNS || foreignNS === NS.MATHML) && isMathMLTextIntegrationPoint(tn, ns)) {
261        return true;
262    }
263
264    return false;
265};
266