diff --git a/lib/get-property-info/svg/index.js b/lib/get-property-info/svg/index.js new file mode 100644 index 0000000..73bebeb --- /dev/null +++ b/lib/get-property-info/svg/index.js @@ -0,0 +1,21 @@ +var SVG_NAMESPACE = require('./svg-namespaces.js').SVG_NAMESPACE; +var SVG_ELEMENTS = require('./svg-namespaces.js').SVG_ELEMENTS; +var SVG_PROPERTIES = require('./svg-namespaces.js').SVG_PROPERTIES; + +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; diff --git a/lib/get-property-info/svg/svg-attribute-hook.js b/lib/get-property-info/svg/svg-attribute-hook.js new file mode 100644 index 0000000..28933cc --- /dev/null +++ b/lib/get-property-info/svg/svg-attribute-hook.js @@ -0,0 +1,35 @@ +'use strict'; + +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'; + +module.exports = AttributeHook; diff --git a/lib/get-property-info/svg/svg-namespaces.js b/lib/get-property-info/svg/svg-namespaces.js new file mode 100644 index 0000000..6ddc7b5 --- /dev/null +++ b/lib/get-property-info/svg/svg-namespaces.js @@ -0,0 +1,409 @@ +'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 +}; + +module.exports.SVG_NAMESPACE = SVG_NAMESPACE; +module.exports.SVG_ELEMENTS = SVG_ELEMENTS; +module.exports.SVG_PROPERTIES = SVG_PROPERTIES; + + + + + + + + + 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..9761687 100644 --- a/lib/htmlparser-to-vdom.js +++ b/lib/htmlparser-to-vdom.js @@ -1,7 +1,31 @@ var decode = require('ent').decode; var convertTagAttributes = require('./convert-tag-attributes'); +var svg = require('./get-property-info/svg'); +var SVGAttributeHook = require('./get-property-info/svg/svg-attribute-hook'); module.exports = function createConverter (VNode, VText) { + function convertSvg(tag, attributes, children, key) { + var _attributes = attributes.attributes; + + for(var _key in _attributes) { + var namespace = svg.SVGAttributeNamespace(_key); + + if (namespace === void 0) { // not a svg attribute + continue; + } + + var value = _attributes[_key]; + + if (namespace !== null) { // namespaced attribute + attributes[_key] = new SVGAttributeHook(namespace, value); + _attributes[_key] = void 0; + continue; + } + } + + return new VNode(tag.name, attributes, children, key, svg.getSVGNamespace()); + } + var converter = { convert: function (node, getVNodeKey) { if (node.type === 'tag' || node.type === 'script' || node.type === 'style') { @@ -25,8 +49,13 @@ module.exports = function createConverter (VNode, VText) { return converter.convert(node, getVNodeKey); }); + if(svg.thisIsSVGTag(tag.name)) { + return convertSvg(tag, attributes, children, key); + } + return new VNode(tag.name, attributes, children, key); } }; + return converter; }; diff --git a/package.json b/package.json index 73bdbf9..56da1f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "html-to-vdom", - "version": "0.7.0", + "version": "0.7.2", "description": "Converts html into a vtree", "main": "index.js", "scripts": { diff --git a/test/get-property-info/svg/svg-attribute-hook.js b/test/get-property-info/svg/svg-attribute-hook.js new file mode 100644 index 0000000..9f0e44b --- /dev/null +++ b/test/get-property-info/svg/svg-attribute-hook.js @@ -0,0 +1,102 @@ +var SVGAttributeHook = require('../../../lib/get-property-info/svg/svg-attribute-hook'); + + +describe('SVGAttributeHook', function () { + var testNamespace = "http://www.testnamespace.com"; + var testValue = "test-value"; + var attributeHook = SVGAttributeHook(testNamespace, testValue); + + it('creates new AttributeBook with namespace and value', function () { + attributeHook.namespace.should.equal(testNamespace) + attributeHook.value.should.equal(testValue) + }); + + describe('hook', function() { + context('prev is the same AttributeHook', function() { + it('does nothing', function() { + var prev = attributeHook; + var node = {}; + var prop = {}; + + var hooked = attributeHook.hook(node, prop, prev) + + should.equal(hooked, undefined) + }); + }); + + context('prev is different AttributeHook', function() { + it('sets the attribute to the input node', function() { + var otherNamespace = "othernamespace"; + var otherValue = "otherValue"; + var testProp = "testProp"; + var prev = SVGAttributeHook(otherNamespace, otherValue); + var node = { + setAttributeNS: function(namespace, prop, value) { + this.namespace = namespace; + this.value = value; + this.prop = prop; + }, + namespace: null, + value: null, + prop: null, + }; + + attributeHook.hook(node, testProp, prev) + + node.namespace.should.equal(testNamespace) + node.value.should.equal(testValue) + node.prop.should.equal(testProp) + }); + }); + }); + + describe('unhook', function() { + context('node is has same namespace as next', function() { + it('does nothing', function() { + var next = attributeHook; + var node = {}; + var prop = {}; + + var hooked = attributeHook.unhook(node, prop, next) + + should.equal(hooked, undefined) + }); + }); + + context('node has different namespace as next', function() { + it('removes attribute namespace from the node', function() { + var otherNamespace = "othernamespace"; + var otherValue = "otherValue"; + var testProp = "color:red"; + var next = SVGAttributeHook(otherNamespace, otherValue); + var node = { + removeAttributeNS: function(namespace, name) { + this.namespace = name; + }, + namespace: testNamespace + }; + + attributeHook.unhook(node, testProp, next) + + node.namespace.should.equal('red') + }); + + it('removes attribute namespace without colon in prop from the node', function() { + var otherNamespace = "othernamespace"; + var otherValue = "otherValue"; + var testProp = "color"; + var next = SVGAttributeHook(otherNamespace, otherValue); + var node = { + removeAttributeNS: function(namespace, name) { + this.namespace = name; + }, + namespace: testNamespace + }; + + attributeHook.unhook(node, testProp, next) + + node.namespace.should.equal('color') + }); + }); + }); +}); diff --git a/test/html-to-vdom/lib/convert-tag-attributes/attributes.js b/test/html-to-vdom/lib/convert-tag-attributes/attributes.js index fd7c62e..ea58f90 100644 --- a/test/html-to-vdom/lib/convert-tag-attributes/attributes.js +++ b/test/html-to-vdom/lib/convert-tag-attributes/attributes.js @@ -45,6 +45,30 @@ describe('convertTagAttributes', function () { }); }); }); + + describe('when converting an svg', function() { + + it('sets them', function() { + var tag = parseHTML('')[0]; + + var converted = convertTagAttributes(tag); + converted.should.eql({ + attributes: { + 'xmlns:rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'xmlns': 'http://www.w3.org/2000/svg', + 'xml:space': 'preserve', + 'height': '180.903', + 'width': '220.34801', + 'version': '1.1', + 'xmlns:cc': 'http://creativecommons.org/ns#', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/' + } + }); + + }); + + }); + }); }); diff --git a/test/html-to-vdom/lib/html-to-vdom/index.js b/test/html-to-vdom/lib/html-to-vdom/index.js index 2a09fba..b4c2172 100644 --- a/test/html-to-vdom/lib/html-to-vdom/index.js +++ b/test/html-to-vdom/lib/html-to-vdom/index.js @@ -173,4 +173,64 @@ describe('html-to-vdom', function () { comment.text.should.eql(''); }); }); + + describe('when converting an svg tag', function () { + var svg1 = convertHTML( + '' + + '' + + '' + + '' + + 'image/svg+xml' + + '' + + '' + + '' + + '' + + '' + + '' + + ''); + + var svg2 = convertHTML( + '' + + 'Menu' + + '' + + ''); + + it('converts to the fill tag correctly', function () { + svg1.tagName.should.eql('svg'); + should.exist(svg1.children); + + svg1.children.length.should.eql(2); + svg1.children[0].tagName.should.eql('metadata'); + svg1.children[1].tagName.should.eql('path'); + }); + + it('processes attributes correctly', function() { + svg2.tagName.should.eql('svg'); + + var attrs = svg2.properties.attributes; + + attrs.height.should.eql('32px'); + attrs.width.should.eql('32px'); + attrs['aria-labelledby'].should.eql('navigation-svg-title'); + attrs.viewBox.should.eql('0 0 32 32'); + + svg2.properties.style['enable-background'].should.eql('new 0 0 32 32'); + }); + + it('processes title and path correctly', function() { + should.exist(svg2.children); + svg2.children.length.should.eql(2); + var title = svg2.children[0]; + var path = svg2.children[1]; + + title.tagName.should.eql('title'); + title.properties.id.should.eql('navigation-svg-title'); + + path.tagName.should.eql('path'); + path.properties.attributes['class'].should.eql('c-small-navigation__icon'); + path.properties.attributes.d.should.eql('M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z'); + }); + + }); });