From de045542a59c5e58693a5492275a262ca0803777 Mon Sep 17 00:00:00 2001 From: savelichalex Date: Thu, 13 Aug 2015 16:13:33 +0300 Subject: [PATCH] Support inline SVG --- lib/convert-tag-attributes.js | 277 ++++++++++++++++++++-- lib/html-to-vdom.js | 2 +- lib/htmlparser-to-vdom.js | 37 +++ lib/svg-attribute-hook.js | 35 +++ lib/svg-namespaces.js | 416 ++++++++++++++++++++++++++++++++++ 5 files changed, 750 insertions(+), 17 deletions(-) create mode 100644 lib/svg-attribute-hook.js create mode 100644 lib/svg-namespaces.js diff --git a/lib/convert-tag-attributes.js b/lib/convert-tag-attributes.js index c59c7c4..6c8e48f 100644 --- a/lib/convert-tag-attributes.js +++ b/lib/convert-tag-attributes.js @@ -1,32 +1,277 @@ -var getPropertyInfo = require('./get-property-info/htmldom'); -var propertySetters = require('./property-setters'); - -var getPropertySetter = function (propInfo) { - if (propInfo.mustUseAttribute) { - return propertySetters.attribute; - } - else { - // Anything we don't set as an attribute is treated as a property - return propertySetters.property; - } +/* + Adapted from https://github.com/facebook/react/blob/c265504fe2fdeadf0e5358879a3c141628b37a23/src/renderers/dom/shared/HTMLDOMPropertyConfig.js + */ +var decode = require('ent').decode; + +var MUST_USE_ATTRIBUTE = 0x1; +var MUST_USE_PROPERTY = 0x2; +var HAS_BOOLEAN_VALUE = 0x8; +var HAS_NUMERIC_VALUE = 0x10; +var HAS_POSITIVE_NUMERIC_VALUE = 0x20 | 0x10; +var HAS_OVERLOADED_BOOLEAN_VALUE = 0x40; + +function checkMask(value, bitmask) { + return (value & bitmask) === bitmask; +} + +var isCustomAttribute = RegExp.prototype.test.bind( + /^(data|aria)-[a-z_][a-z\d_.\-]*$/ +); + +var HTMLDOMPropertyConfig = { + + Properties: { + /** + * Standard Properties + */ + accept: null, + acceptCharset: null, + accessKey: null, + action: null, + allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + allowTransparency: MUST_USE_ATTRIBUTE, + alt: null, + async: HAS_BOOLEAN_VALUE, + autoComplete: null, + autoFocus: HAS_BOOLEAN_VALUE, + autoPlay: HAS_BOOLEAN_VALUE, + capture: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + cellPadding: null, + cellSpacing: null, + charSet: MUST_USE_ATTRIBUTE, + challenge: MUST_USE_ATTRIBUTE, + checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + classID: MUST_USE_ATTRIBUTE, + // To set className on SVG elements, it's necessary to use .setAttribute; + // this works on HTML elements too in all browsers except IE8. + className: MUST_USE_ATTRIBUTE, + cols: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, + colSpan: null, + content: null, + contentEditable: null, + contextMenu: MUST_USE_ATTRIBUTE, + controls: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + coords: null, + crossOrigin: null, + data: null, // For `` acts as `src`. + dateTime: MUST_USE_ATTRIBUTE, + defer: HAS_BOOLEAN_VALUE, + dir: null, + disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + download: HAS_OVERLOADED_BOOLEAN_VALUE, + draggable: null, + encType: null, + form: MUST_USE_ATTRIBUTE, + formAction: MUST_USE_ATTRIBUTE, + formEncType: MUST_USE_ATTRIBUTE, + formMethod: MUST_USE_ATTRIBUTE, + formNoValidate: HAS_BOOLEAN_VALUE, + formTarget: MUST_USE_ATTRIBUTE, + frameBorder: MUST_USE_ATTRIBUTE, + headers: null, + height: MUST_USE_ATTRIBUTE, + hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + high: null, + href: null, + hrefLang: null, + htmlFor: null, + httpEquiv: null, + icon: null, + id: MUST_USE_PROPERTY, + is: MUST_USE_ATTRIBUTE, + keyParams: MUST_USE_ATTRIBUTE, + keyType: MUST_USE_ATTRIBUTE, + label: null, + lang: null, + list: MUST_USE_ATTRIBUTE, + loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + low: null, + manifest: MUST_USE_ATTRIBUTE, + marginHeight: null, + marginWidth: null, + max: null, + maxLength: MUST_USE_ATTRIBUTE, + media: MUST_USE_ATTRIBUTE, + mediaGroup: null, + method: null, + min: null, + minLength: MUST_USE_ATTRIBUTE, + multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + name: null, + noValidate: HAS_BOOLEAN_VALUE, + open: HAS_BOOLEAN_VALUE, + optimum: null, + pattern: null, + placeholder: null, + poster: null, + preload: null, + radioGroup: null, + readOnly: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + rel: null, + required: HAS_BOOLEAN_VALUE, + role: MUST_USE_ATTRIBUTE, + rows: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, + rowSpan: null, + sandbox: null, + scope: null, + scoped: HAS_BOOLEAN_VALUE, + scrolling: null, + seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + shape: null, + size: MUST_USE_ATTRIBUTE | HAS_POSITIVE_NUMERIC_VALUE, + sizes: MUST_USE_ATTRIBUTE, + span: HAS_POSITIVE_NUMERIC_VALUE, + spellCheck: null, + src: null, + srcDoc: MUST_USE_PROPERTY, + srcSet: MUST_USE_ATTRIBUTE, + start: HAS_NUMERIC_VALUE, + step: null, + style: null, + tabIndex: null, + target: null, + title: null, + type: null, + useMap: null, + value: MUST_USE_PROPERTY, + width: MUST_USE_ATTRIBUTE, + wmode: MUST_USE_ATTRIBUTE, + + /** + * Non-standard Properties + */ + // autoCapitalize and autoCorrect are supported in Mobile Safari for + // keyboard hints. + autoCapitalize: null, + autoCorrect: null, + // itemProp, itemScope, itemType are for + // Microdata support. See http://schema.org/docs/gs.html + itemProp: MUST_USE_ATTRIBUTE, + itemScope: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, + itemType: MUST_USE_ATTRIBUTE, + // itemID and itemRef are for Microdata support as well but + // only specified in the the WHATWG spec document. See + // https://html.spec.whatwg.org/multipage/microdata.html#microdata-dom-api + itemID: MUST_USE_ATTRIBUTE, + itemRef: MUST_USE_ATTRIBUTE, + // property is supported for OpenGraph in meta tags. + property: null, + // IE-only attribute that controls focus behavior + unselectable: MUST_USE_ATTRIBUTE + } +}; + +var parseStyles = function(input) { + var attributes = input.split(';'); + var styles = attributes.reduce(function(object, attribute){ + var entry = attribute.split(/:(.+)/); + if (entry[0] && entry[1]) { + object[entry[0].trim()] = entry[1].trim(); + } + return object; + },{}); + return styles; +}; + +var propertyToAttributeMapping = { + 'className': 'class', + 'htmlFor': 'for', + 'httpEquiv': 'http-equiv', + 'acceptCharset': 'accept-charset' +}; + +var propertyValueConversions = { + 'style': parseStyles, + 'placeholder': decode, + 'title': decode, + 'alt': decode }; +var getPropertyInfo = (function () { + var propInfoByAttributeName = {}; + + Object.keys(HTMLDOMPropertyConfig.Properties).forEach(function (propName) { + var propConfig = HTMLDOMPropertyConfig.Properties[propName]; + var attributeName = propertyToAttributeMapping[propName] || propName.toLowerCase(); + + var propertyInfo = { + attributeName: attributeName, + propertyName: propName, + + mustUseAttribute: checkMask(propConfig, MUST_USE_ATTRIBUTE), + mustUseProperty: checkMask(propConfig, MUST_USE_PROPERTY), + hasBooleanValue: checkMask(propConfig, HAS_BOOLEAN_VALUE), + hasNumericValue: checkMask(propConfig, HAS_NUMERIC_VALUE), + hasPositiveNumericValue: + checkMask(propConfig, HAS_POSITIVE_NUMERIC_VALUE), + hasOverloadedBooleanValue: + checkMask(propConfig, HAS_OVERLOADED_BOOLEAN_VALUE), + }; + + propInfoByAttributeName[attributeName] = propertyInfo; + }); + + return function (attributeName) { + return propInfoByAttributeName[attributeName]; + }; +})(); + + var convertTagAttributes = function (tag) { var attributes = tag.attribs; - var vNodeProperties = { + var vdomProperties = { attributes: {} }; Object.keys(attributes).forEach(function (attributeName) { + var lowerCased = attributeName.toLowerCase(); + var propInfo = getPropertyInfo(lowerCased); + var value = attributes[attributeName]; - var propInfo = getPropertyInfo(attributeName); + if (isCustomAttribute(attributeName) || !propInfo) { + vdomProperties.attributes[attributeName] = value; + return; + } + + var valueConverter = propertyValueConversions[propInfo.propertyName]; + if (valueConverter) { + value = valueConverter(value); + } + + if (propInfo.mustUseAttribute) { + if (propInfo.hasBooleanValue) { + // Boolean attributes come in as an empty string or the + vdomProperties.attributes[propInfo.attributeName] = ''; + } + else { + vdomProperties.attributes[propInfo.attributeName] = value; + } + } + // Anything we don't set as an attribute is treated as a property + else { + var isTrue; + if (propInfo.hasBooleanValue) { + isTrue = (value === '' || value.toLowerCase() === propInfo.attributeName); + vdomProperties[propInfo.propertyName] = isTrue ? true : false; + } + else if (propInfo.hasOverloadedBooleanValue) { + isTrue = (value === ''); + vdomProperties[propInfo.propertyName] = isTrue ? true : value; + } + else if (propInfo.hasNumericValue || propInfo.hasPositiveNumericValue) { + vdomProperties[propInfo.propertyName] = Number(value); + } + else { + vdomProperties[propInfo.propertyName] = value; + } + } - var propertySetter = getPropertySetter(propInfo); - propertySetter.set(vNodeProperties, propInfo, value); }); - return vNodeProperties; + return vdomProperties; }; module.exports = convertTagAttributes; diff --git a/lib/html-to-vdom.js b/lib/html-to-vdom.js index 5135d39..f37ee87 100644 --- a/lib/html-to-vdom.js +++ b/lib/html-to-vdom.js @@ -22,7 +22,7 @@ module.exports = function initializeHtmlToVdom (VTree, VText) { else { convertedHTML = htmlparserToVdom.convert(tags[0], getVNodeKey); } - + return convertedHTML; }; }; diff --git a/lib/htmlparser-to-vdom.js b/lib/htmlparser-to-vdom.js index 9993453..8a6d9c6 100644 --- a/lib/htmlparser-to-vdom.js +++ b/lib/htmlparser-to-vdom.js @@ -1,5 +1,9 @@ var decode = require('ent').decode; var convertTagAttributes = require('./convert-tag-attributes'); +var thisIsSVGTag = require('./svg-namespaces').thisIsSVGTag, + getSVGNamespace = require('./svg-namespaces').getSVGNamespace, + SVGAttributeNamespace = require('./svg-namespaces').SVGAttributeNamespace, + SVGAttributeHook = require('./svg-attribute-hook'); module.exports = function createConverter (VNode, VText) { var converter = { @@ -25,6 +29,39 @@ module.exports = function createConverter (VNode, VText) { return converter.convert(node, getVNodeKey); }); + if(thisIsSVGTag(tag.name)) { + var _attributes = attributes.attributes; + + for(var _key in _attributes) { + if (!_attributes.hasOwnProperty(_key)) { + continue; + } + + var namespace = SVGAttributeNamespace(_key); + + if (namespace === void 0) { // not a svg attribute + continue; + } + + var value = _attributes[_key]; + + if (typeof value !== 'string' && + typeof value !== 'number' && + typeof value !== 'boolean' + ) { + continue; + } + + if (namespace !== null) { // namespaced attribute + attributes[_key] = SVGAttributeHook(namespace, value); + _attributes[_key] = void 0; + continue; + } + } + + return new VNode(tag.name, attributes, children, key, getSVGNamespace()); + } + return new VNode(tag.name, attributes, children, key); } }; diff --git a/lib/svg-attribute-hook.js b/lib/svg-attribute-hook.js new file mode 100644 index 0000000..64347a3 --- /dev/null +++ b/lib/svg-attribute-hook.js @@ -0,0 +1,35 @@ +'use strict'; + +module.exports = AttributeHook; + +function AttributeHook(namespace, value) { + if (!(this instanceof AttributeHook)) { + return new AttributeHook(namespace, value); + } + + this.namespace = namespace; + this.value = value; +} + +AttributeHook.prototype.hook = function (node, prop, prev) { + if (prev && prev.type === 'AttributeHook' && + prev.value === this.value && + prev.namespace === this.namespace) { + return; + } + + node.setAttributeNS(this.namespace, prop, this.value); +}; + +AttributeHook.prototype.unhook = function (node, prop, next) { + if (next && next.type === 'AttributeHook' && + next.namespace === this.namespace) { + return; + } + + var colonPosition = prop.indexOf(':'); + var localName = colonPosition > -1 ? prop.substr(colonPosition + 1) : prop; + node.removeAttributeNS(this.namespace, localName); +}; + +AttributeHook.prototype.type = 'AttributeHook'; diff --git a/lib/svg-namespaces.js b/lib/svg-namespaces.js new file mode 100644 index 0000000..7f5cbb4 --- /dev/null +++ b/lib/svg-namespaces.js @@ -0,0 +1,416 @@ +'use strict'; + +/* + Adapted from https://github.com/Matt-Esch/virtual-dom/blob/master/virtual-hyperscript/svg-attribute-namespace.js + */ + +var DEFAULT_NAMESPACE = null; +var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; +var EV_NAMESPACE = 'http://www.w3.org/2001/xml-events'; +var XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink'; +var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'; + +// http://www.w3.org/TR/SVGTiny12/elementTable.html +// http://www.w3.org/TR/SVG/eltindex.html +var SVG_ELEMENTS = { + 'listener': SVG_NAMESPACE, +// 'a': SVG_NAMESPACE, + 'animate': SVG_NAMESPACE, + 'altGlyph': SVG_NAMESPACE, + 'altGlyphDef': SVG_NAMESPACE, + 'altGlyphItem': SVG_NAMESPACE, + 'animateColor': SVG_NAMESPACE, + 'animateMotion': SVG_NAMESPACE, + 'animateTransform': SVG_NAMESPACE, + 'animation': SVG_NAMESPACE, +// 'audio': SVG_NAMESPACE, + 'circle': SVG_NAMESPACE, + 'defs': SVG_NAMESPACE, + 'desc': SVG_NAMESPACE, + 'discard': SVG_NAMESPACE, + 'ellipse': SVG_NAMESPACE, + 'feBlend': SVG_NAMESPACE, + 'feColorMatrix': SVG_NAMESPACE, + 'feComponentTransfer': SVG_NAMESPACE, + 'feComposite': SVG_NAMESPACE, + 'feConvolveMatrix': SVG_NAMESPACE, + 'feDiffuseLighting': SVG_NAMESPACE, + 'feDisplacementMap': SVG_NAMESPACE, + 'feDistantLight': SVG_NAMESPACE, + 'feFlood': SVG_NAMESPACE, + 'feFuncA': SVG_NAMESPACE, + 'feFuncB': SVG_NAMESPACE, + 'feFuncG': SVG_NAMESPACE, + 'feFuncR': SVG_NAMESPACE, + 'feGaussianBlur': SVG_NAMESPACE, + 'feImage': SVG_NAMESPACE, + 'feMerge': SVG_NAMESPACE, + 'feMergeNode': SVG_NAMESPACE, + 'feMorphology': SVG_NAMESPACE, + 'feOffset': SVG_NAMESPACE, + 'fePointLight': SVG_NAMESPACE, + 'feSpecularLighting': SVG_NAMESPACE, + 'feSpotLight': SVG_NAMESPACE, + 'feTile': SVG_NAMESPACE, + 'feTurbulence': SVG_NAMESPACE, + 'filter': SVG_NAMESPACE, + 'font': SVG_NAMESPACE, + 'font-face': SVG_NAMESPACE, + 'font-face-format': SVG_NAMESPACE, + 'font-face-name': SVG_NAMESPACE, + 'font-face-src': SVG_NAMESPACE, + 'font-face-uri': SVG_NAMESPACE, + 'foreignObject': SVG_NAMESPACE, + 'g': SVG_NAMESPACE, + 'glyph': SVG_NAMESPACE, + 'glyphRef': SVG_NAMESPACE, + 'handler': SVG_NAMESPACE, + 'hkern': SVG_NAMESPACE, + 'image': SVG_NAMESPACE, + 'line': SVG_NAMESPACE, + 'linearGradient': SVG_NAMESPACE, + 'metadata': SVG_NAMESPACE, + 'missing-glyph': SVG_NAMESPACE, + 'mpath': SVG_NAMESPACE, + 'path': SVG_NAMESPACE, + 'polygon': SVG_NAMESPACE, + 'polyline': SVG_NAMESPACE, + 'prefetch': SVG_NAMESPACE, + 'radialGradient': SVG_NAMESPACE, + 'rect': SVG_NAMESPACE, +// 'script': SVG_NAMESPACE, + 'set': SVG_NAMESPACE, + 'solidColor': SVG_NAMESPACE, + 'stop': SVG_NAMESPACE, + 'svg': SVG_NAMESPACE, + 'switch': SVG_NAMESPACE, + 'symbol': SVG_NAMESPACE, + 'tbreak': SVG_NAMESPACE, + 'text': SVG_NAMESPACE, + 'textArea': SVG_NAMESPACE, + 'title': SVG_NAMESPACE, + 'tspan': SVG_NAMESPACE, + 'tref': SVG_NAMESPACE, + 'use': SVG_NAMESPACE, + 'vkern': SVG_NAMESPACE, +// 'video': SVG_NAMESPACE, +}; + +// http://www.w3.org/TR/SVGTiny12/attributeTable.html +// http://www.w3.org/TR/SVG/attindex.html +var SVG_PROPERTIES = { + 'about': DEFAULT_NAMESPACE, + 'accent-height': DEFAULT_NAMESPACE, + 'accumulate': DEFAULT_NAMESPACE, + 'additive': DEFAULT_NAMESPACE, + 'alignment-baseline': DEFAULT_NAMESPACE, + 'alphabetic': DEFAULT_NAMESPACE, + 'amplitude': DEFAULT_NAMESPACE, + 'arabic-form': DEFAULT_NAMESPACE, + 'ascent': DEFAULT_NAMESPACE, + 'attributeName': DEFAULT_NAMESPACE, + 'attributeType': DEFAULT_NAMESPACE, + 'azimuth': DEFAULT_NAMESPACE, + 'bandwidth': DEFAULT_NAMESPACE, + 'baseFrequency': DEFAULT_NAMESPACE, + 'baseProfile': DEFAULT_NAMESPACE, + 'baseline-shift': DEFAULT_NAMESPACE, + 'bbox': DEFAULT_NAMESPACE, + 'begin': DEFAULT_NAMESPACE, + 'bias': DEFAULT_NAMESPACE, + 'by': DEFAULT_NAMESPACE, + 'calcMode': DEFAULT_NAMESPACE, + 'cap-height': DEFAULT_NAMESPACE, + 'class': DEFAULT_NAMESPACE, + 'clip': DEFAULT_NAMESPACE, + 'clip-path': DEFAULT_NAMESPACE, + 'clip-rule': DEFAULT_NAMESPACE, + 'clipPathUnits': DEFAULT_NAMESPACE, + 'color': DEFAULT_NAMESPACE, + 'color-interpolation': DEFAULT_NAMESPACE, + 'color-interpolation-filters': DEFAULT_NAMESPACE, + 'color-profile': DEFAULT_NAMESPACE, + 'color-rendering': DEFAULT_NAMESPACE, + 'content': DEFAULT_NAMESPACE, + 'contentScriptType': DEFAULT_NAMESPACE, + 'contentStyleType': DEFAULT_NAMESPACE, + 'cursor': DEFAULT_NAMESPACE, + 'cx': DEFAULT_NAMESPACE, + 'cy': DEFAULT_NAMESPACE, + 'd': DEFAULT_NAMESPACE, + 'datatype': DEFAULT_NAMESPACE, + 'defaultAction': DEFAULT_NAMESPACE, + 'descent': DEFAULT_NAMESPACE, + 'diffuseConstant': DEFAULT_NAMESPACE, + 'direction': DEFAULT_NAMESPACE, + 'display': DEFAULT_NAMESPACE, + 'divisor': DEFAULT_NAMESPACE, + 'dominant-baseline': DEFAULT_NAMESPACE, + 'dur': DEFAULT_NAMESPACE, + 'dx': DEFAULT_NAMESPACE, + 'dy': DEFAULT_NAMESPACE, + 'edgeMode': DEFAULT_NAMESPACE, + 'editable': DEFAULT_NAMESPACE, + 'elevation': DEFAULT_NAMESPACE, + 'enable-background': DEFAULT_NAMESPACE, + 'end': DEFAULT_NAMESPACE, + 'ev:event': EV_NAMESPACE, + 'event': DEFAULT_NAMESPACE, + 'exponent': DEFAULT_NAMESPACE, + 'externalResourcesRequired': DEFAULT_NAMESPACE, + 'fill': DEFAULT_NAMESPACE, + 'fill-opacity': DEFAULT_NAMESPACE, + 'fill-rule': DEFAULT_NAMESPACE, + 'filter': DEFAULT_NAMESPACE, + 'filterRes': DEFAULT_NAMESPACE, + 'filterUnits': DEFAULT_NAMESPACE, + 'flood-color': DEFAULT_NAMESPACE, + 'flood-opacity': DEFAULT_NAMESPACE, + 'focusHighlight': DEFAULT_NAMESPACE, + 'focusable': DEFAULT_NAMESPACE, + 'font-family': DEFAULT_NAMESPACE, + 'font-size': DEFAULT_NAMESPACE, + 'font-size-adjust': DEFAULT_NAMESPACE, + 'font-stretch': DEFAULT_NAMESPACE, + 'font-style': DEFAULT_NAMESPACE, + 'font-variant': DEFAULT_NAMESPACE, + 'font-weight': DEFAULT_NAMESPACE, + 'format': DEFAULT_NAMESPACE, + 'from': DEFAULT_NAMESPACE, + 'fx': DEFAULT_NAMESPACE, + 'fy': DEFAULT_NAMESPACE, + 'g1': DEFAULT_NAMESPACE, + 'g2': DEFAULT_NAMESPACE, + 'glyph-name': DEFAULT_NAMESPACE, + 'glyph-orientation-horizontal': DEFAULT_NAMESPACE, + 'glyph-orientation-vertical': DEFAULT_NAMESPACE, + 'glyphRef': DEFAULT_NAMESPACE, + 'gradientTransform': DEFAULT_NAMESPACE, + 'gradientUnits': DEFAULT_NAMESPACE, + 'handler': DEFAULT_NAMESPACE, + 'hanging': DEFAULT_NAMESPACE, + 'height': DEFAULT_NAMESPACE, + 'horiz-adv-x': DEFAULT_NAMESPACE, + 'horiz-origin-x': DEFAULT_NAMESPACE, + 'horiz-origin-y': DEFAULT_NAMESPACE, + 'id': DEFAULT_NAMESPACE, + 'ideographic': DEFAULT_NAMESPACE, + 'image-rendering': DEFAULT_NAMESPACE, + 'in': DEFAULT_NAMESPACE, + 'in2': DEFAULT_NAMESPACE, + 'initialVisibility': DEFAULT_NAMESPACE, + 'intercept': DEFAULT_NAMESPACE, + 'k': DEFAULT_NAMESPACE, + 'k1': DEFAULT_NAMESPACE, + 'k2': DEFAULT_NAMESPACE, + 'k3': DEFAULT_NAMESPACE, + 'k4': DEFAULT_NAMESPACE, + 'kernelMatrix': DEFAULT_NAMESPACE, + 'kernelUnitLength': DEFAULT_NAMESPACE, + 'kerning': DEFAULT_NAMESPACE, + 'keyPoints': DEFAULT_NAMESPACE, + 'keySplines': DEFAULT_NAMESPACE, + 'keyTimes': DEFAULT_NAMESPACE, + 'lang': DEFAULT_NAMESPACE, + 'lengthAdjust': DEFAULT_NAMESPACE, + 'letter-spacing': DEFAULT_NAMESPACE, + 'lighting-color': DEFAULT_NAMESPACE, + 'limitingConeAngle': DEFAULT_NAMESPACE, + 'local': DEFAULT_NAMESPACE, + 'marker-end': DEFAULT_NAMESPACE, + 'marker-mid': DEFAULT_NAMESPACE, + 'marker-start': DEFAULT_NAMESPACE, + 'markerHeight': DEFAULT_NAMESPACE, + 'markerUnits': DEFAULT_NAMESPACE, + 'markerWidth': DEFAULT_NAMESPACE, + 'mask': DEFAULT_NAMESPACE, + 'maskContentUnits': DEFAULT_NAMESPACE, + 'maskUnits': DEFAULT_NAMESPACE, + 'mathematical': DEFAULT_NAMESPACE, + 'max': DEFAULT_NAMESPACE, + 'media': DEFAULT_NAMESPACE, + 'mediaCharacterEncoding': DEFAULT_NAMESPACE, + 'mediaContentEncodings': DEFAULT_NAMESPACE, + 'mediaSize': DEFAULT_NAMESPACE, + 'mediaTime': DEFAULT_NAMESPACE, + 'method': DEFAULT_NAMESPACE, + 'min': DEFAULT_NAMESPACE, + 'mode': DEFAULT_NAMESPACE, + 'name': DEFAULT_NAMESPACE, + 'nav-down': DEFAULT_NAMESPACE, + 'nav-down-left': DEFAULT_NAMESPACE, + 'nav-down-right': DEFAULT_NAMESPACE, + 'nav-left': DEFAULT_NAMESPACE, + 'nav-next': DEFAULT_NAMESPACE, + 'nav-prev': DEFAULT_NAMESPACE, + 'nav-right': DEFAULT_NAMESPACE, + 'nav-up': DEFAULT_NAMESPACE, + 'nav-up-left': DEFAULT_NAMESPACE, + 'nav-up-right': DEFAULT_NAMESPACE, + 'numOctaves': DEFAULT_NAMESPACE, + 'observer': DEFAULT_NAMESPACE, + 'offset': DEFAULT_NAMESPACE, + 'opacity': DEFAULT_NAMESPACE, + 'operator': DEFAULT_NAMESPACE, + 'order': DEFAULT_NAMESPACE, + 'orient': DEFAULT_NAMESPACE, + 'orientation': DEFAULT_NAMESPACE, + 'origin': DEFAULT_NAMESPACE, + 'overflow': DEFAULT_NAMESPACE, + 'overlay': DEFAULT_NAMESPACE, + 'overline-position': DEFAULT_NAMESPACE, + 'overline-thickness': DEFAULT_NAMESPACE, + 'panose-1': DEFAULT_NAMESPACE, + 'path': DEFAULT_NAMESPACE, + 'pathLength': DEFAULT_NAMESPACE, + 'patternContentUnits': DEFAULT_NAMESPACE, + 'patternTransform': DEFAULT_NAMESPACE, + 'patternUnits': DEFAULT_NAMESPACE, + 'phase': DEFAULT_NAMESPACE, + 'playbackOrder': DEFAULT_NAMESPACE, + 'pointer-events': DEFAULT_NAMESPACE, + 'points': DEFAULT_NAMESPACE, + 'pointsAtX': DEFAULT_NAMESPACE, + 'pointsAtY': DEFAULT_NAMESPACE, + 'pointsAtZ': DEFAULT_NAMESPACE, + 'preserveAlpha': DEFAULT_NAMESPACE, + 'preserveAspectRatio': DEFAULT_NAMESPACE, + 'primitiveUnits': DEFAULT_NAMESPACE, + 'propagate': DEFAULT_NAMESPACE, + 'property': DEFAULT_NAMESPACE, + 'r': DEFAULT_NAMESPACE, + 'radius': DEFAULT_NAMESPACE, + 'refX': DEFAULT_NAMESPACE, + 'refY': DEFAULT_NAMESPACE, + 'rel': DEFAULT_NAMESPACE, + 'rendering-intent': DEFAULT_NAMESPACE, + 'repeatCount': DEFAULT_NAMESPACE, + 'repeatDur': DEFAULT_NAMESPACE, + 'requiredExtensions': DEFAULT_NAMESPACE, + 'requiredFeatures': DEFAULT_NAMESPACE, + 'requiredFonts': DEFAULT_NAMESPACE, + 'requiredFormats': DEFAULT_NAMESPACE, + 'resource': DEFAULT_NAMESPACE, + 'restart': DEFAULT_NAMESPACE, + 'result': DEFAULT_NAMESPACE, + 'rev': DEFAULT_NAMESPACE, + 'role': DEFAULT_NAMESPACE, + 'rotate': DEFAULT_NAMESPACE, + 'rx': DEFAULT_NAMESPACE, + 'ry': DEFAULT_NAMESPACE, + 'scale': DEFAULT_NAMESPACE, + 'seed': DEFAULT_NAMESPACE, + 'shape-rendering': DEFAULT_NAMESPACE, + 'slope': DEFAULT_NAMESPACE, + 'snapshotTime': DEFAULT_NAMESPACE, + 'spacing': DEFAULT_NAMESPACE, + 'specularConstant': DEFAULT_NAMESPACE, + 'specularExponent': DEFAULT_NAMESPACE, + 'spreadMethod': DEFAULT_NAMESPACE, + 'startOffset': DEFAULT_NAMESPACE, + 'stdDeviation': DEFAULT_NAMESPACE, + 'stemh': DEFAULT_NAMESPACE, + 'stemv': DEFAULT_NAMESPACE, + 'stitchTiles': DEFAULT_NAMESPACE, + 'stop-color': DEFAULT_NAMESPACE, + 'stop-opacity': DEFAULT_NAMESPACE, + 'strikethrough-position': DEFAULT_NAMESPACE, + 'strikethrough-thickness': DEFAULT_NAMESPACE, + 'string': DEFAULT_NAMESPACE, + 'stroke': DEFAULT_NAMESPACE, + 'stroke-dasharray': DEFAULT_NAMESPACE, + 'stroke-dashoffset': DEFAULT_NAMESPACE, + 'stroke-linecap': DEFAULT_NAMESPACE, + 'stroke-linejoin': DEFAULT_NAMESPACE, + 'stroke-miterlimit': DEFAULT_NAMESPACE, + 'stroke-opacity': DEFAULT_NAMESPACE, + 'stroke-width': DEFAULT_NAMESPACE, + 'surfaceScale': DEFAULT_NAMESPACE, + 'syncBehavior': DEFAULT_NAMESPACE, + 'syncBehaviorDefault': DEFAULT_NAMESPACE, + 'syncMaster': DEFAULT_NAMESPACE, + 'syncTolerance': DEFAULT_NAMESPACE, + 'syncToleranceDefault': DEFAULT_NAMESPACE, + 'systemLanguage': DEFAULT_NAMESPACE, + 'tableValues': DEFAULT_NAMESPACE, + 'target': DEFAULT_NAMESPACE, + 'targetX': DEFAULT_NAMESPACE, + 'targetY': DEFAULT_NAMESPACE, + 'text-anchor': DEFAULT_NAMESPACE, + 'text-decoration': DEFAULT_NAMESPACE, + 'text-rendering': DEFAULT_NAMESPACE, + 'textLength': DEFAULT_NAMESPACE, + 'timelineBegin': DEFAULT_NAMESPACE, + 'title': DEFAULT_NAMESPACE, + 'to': DEFAULT_NAMESPACE, + 'transform': DEFAULT_NAMESPACE, + 'transformBehavior': DEFAULT_NAMESPACE, + 'type': DEFAULT_NAMESPACE, + 'typeof': DEFAULT_NAMESPACE, + 'u1': DEFAULT_NAMESPACE, + 'u2': DEFAULT_NAMESPACE, + 'underline-position': DEFAULT_NAMESPACE, + 'underline-thickness': DEFAULT_NAMESPACE, + 'unicode': DEFAULT_NAMESPACE, + 'unicode-bidi': DEFAULT_NAMESPACE, + 'unicode-range': DEFAULT_NAMESPACE, + 'units-per-em': DEFAULT_NAMESPACE, + 'v-alphabetic': DEFAULT_NAMESPACE, + 'v-hanging': DEFAULT_NAMESPACE, + 'v-ideographic': DEFAULT_NAMESPACE, + 'v-mathematical': DEFAULT_NAMESPACE, + 'values': DEFAULT_NAMESPACE, + 'version': DEFAULT_NAMESPACE, + 'vert-adv-y': DEFAULT_NAMESPACE, + 'vert-origin-x': DEFAULT_NAMESPACE, + 'vert-origin-y': DEFAULT_NAMESPACE, + 'viewBox': DEFAULT_NAMESPACE, + 'viewTarget': DEFAULT_NAMESPACE, + 'visibility': DEFAULT_NAMESPACE, + 'width': DEFAULT_NAMESPACE, + 'widths': DEFAULT_NAMESPACE, + 'word-spacing': DEFAULT_NAMESPACE, + 'writing-mode': DEFAULT_NAMESPACE, + 'x': DEFAULT_NAMESPACE, + 'x-height': DEFAULT_NAMESPACE, + 'x1': DEFAULT_NAMESPACE, + 'x2': DEFAULT_NAMESPACE, + 'xChannelSelector': DEFAULT_NAMESPACE, + 'xlink:actuate': XLINK_NAMESPACE, + 'xlink:arcrole': XLINK_NAMESPACE, + 'xlink:href': XLINK_NAMESPACE, + 'xlink:role': XLINK_NAMESPACE, + 'xlink:show': XLINK_NAMESPACE, + 'xlink:title': XLINK_NAMESPACE, + 'xlink:type': XLINK_NAMESPACE, + 'xml:base': XML_NAMESPACE, + 'xml:id': XML_NAMESPACE, + 'xml:lang': XML_NAMESPACE, + 'xml:space': XML_NAMESPACE, + 'y': DEFAULT_NAMESPACE, + 'y1': DEFAULT_NAMESPACE, + 'y2': DEFAULT_NAMESPACE, + 'yChannelSelector': DEFAULT_NAMESPACE, + 'z': DEFAULT_NAMESPACE, + 'zoomAndPan': DEFAULT_NAMESPACE +}; + +function thisIsSVGTag(tag) { + return SVG_ELEMENTS.hasOwnProperty(tag); +} + +function getSVGNamespace() { + return SVG_NAMESPACE; +} + +function SVGAttributeNamespace(value) { + if (SVG_PROPERTIES.hasOwnProperty(value)) { + return SVG_PROPERTIES[value]; + } +} + +module.exports.thisIsSVGTag = thisIsSVGTag; + +module.exports.getSVGNamespace = getSVGNamespace; + +module.exports.SVGAttributeNamespace = SVGAttributeNamespace;