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