From 48025dd16a96c885f2c2967d4283309b2f41f7ef Mon Sep 17 00:00:00 2001 From: Leo Horie Date: Wed, 26 Mar 2014 23:05:14 -0400 Subject: [PATCH] add experimental svg support - supports nested svg elements - supports svg attributes - supports xlink --- archive/v0.1.2/mithril-tests.js | 38 +++++++++++++++++++++++++------- archive/v0.1.2/mithril.min.js | 2 +- archive/v0.1.2/mithril.min.map | 2 +- archive/v0.1.2/mithril.min.zip | Bin 20069 -> 20535 bytes mithril.js | 18 ++++++++------- tests/mithril-tests.js | 14 ++++++++++++ tests/mock.js | 6 +++++ tests/svg.html | 25 +++++++++++++++++++++ 8 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 tests/svg.html diff --git a/archive/v0.1.2/mithril-tests.js b/archive/v0.1.2/mithril-tests.js index 9bec759a..72882e28 100644 --- a/archive/v0.1.2/mithril-tests.js +++ b/archive/v0.1.2/mithril-tests.js @@ -32,7 +32,7 @@ new function(window) { } return cell } - function build(parent, data, cached) { + function build(parent, data, cached, namespace) { if (data === null || data === undefined) return var cachedType = type.call(cached), dataType = type.call(data) @@ -45,7 +45,7 @@ new function(window) { if (dataType == "[object Array]") { var nodes = [], intact = cached.length === data.length for (var i = 0; i < data.length; i++) { - var item = build(parent, data[i], cached[i]) + var item = build(parent, data[i], cached[i], namespace) if (item === undefined) continue if (!item.nodes.intact) intact = false cached[i] = item @@ -63,15 +63,16 @@ new function(window) { if (data.tag != cached.tag || Object.keys(data.attrs).join() != Object.keys(cached.attrs).join()) clear(cached.nodes) var node, isNew = cached.nodes.length === 0 + if (data.tag === "svg") namespace = "http://www.w3.org/2000/svg" if (isNew) { - node = window.document.createElement(data.tag) - cached = {tag: data.tag, attrs: setAttributes(node, data.attrs, {}), children: build(node, data.children, cached.children), nodes: [node]} + node = namespace === undefined ? window.document.createElement(data.tag) : window.document.createElementNS(namespace, data.tag) + cached = {tag: data.tag, attrs: setAttributes(node, data.attrs, {}, namespace), children: build(node, data.children, cached.children, namespace), nodes: [node]} parent.appendChild(node) } else { node = cached.nodes[0] - setAttributes(node, data.attrs, cached.attrs) - cached.children = build(node, data.children, cached.children) + setAttributes(node, data.attrs, cached.attrs, namespace) + cached.children = build(node, data.children, cached.children, namespace) cached.nodes.intact = true } if (type.call(data.attrs["config"]) == "[object Function]") data.attrs["config"](node, !isNew) @@ -116,7 +117,7 @@ new function(window) { return cached } - function setAttributes(node, dataAttrs, cachedAttrs) { + function setAttributes(node, dataAttrs, cachedAttrs, namespace) { for (var attrName in dataAttrs) { var dataAttr = dataAttrs[attrName] if (!(attrName in cachedAttrs) || (cachedAttrs[attrName] !== dataAttr)) { @@ -124,7 +125,8 @@ new function(window) { if (attrName == "config") continue if (attrName.indexOf("on") == 0 && typeof dataAttr == "function") dataAttr = autoredraw(dataAttr, node) if (attrName == "style") for (var rule in dataAttr) node.style[rule] = dataAttr[rule] - else if (attrName in node) node[attrName] = dataAttr + else if (attrName in node && namespace === undefined) node[attrName] = dataAttr + else if (namespace !== undefined && attrName === "href") node.setAttributeNS("http://www.w3.org/1999/xlink", "href", dataAttr) else node.setAttribute(attrName, dataAttr) } } @@ -453,11 +455,17 @@ mock.window = new function() { setAttribute: function(name, value) { this[name] = value.toString() }, + setAttributeNS: function(namespace, name, value) { + this[name] = value.toString() + }, getAttribute: function(name, value) { return this[name] }, } } + window.document.createElementNS = function(namespace, tag) { + return window.document.createElement(tag) + } window.document.createTextNode = function(text) { return {nodeValue: text.toString()} } @@ -537,6 +545,8 @@ function testMithril(mock) { test(function() {return m("div", m("div")).attrs.tag === "div"}) //yes, this is expected behavior: see method signature test(function() {return m("div", [undefined]).tag === "div"}) test(function() {return m("div", [{foo: "bar"}])}) //as long as it doesn't throw errors, it's fine + test(function() {return m("svg", [m("g")])}) + test(function() {return m("svg", [m("a[href='http://google.com']")])}) //m.module test(function() { @@ -621,6 +631,18 @@ function testMithril(mock) { m.render(root, m("div", [undefined])) return root.childNodes[0].childNodes.length === 0 }) + test(function() { + var root = mock.document.createElement("div") + m.render(root, m("svg", [m("g")])) + console.log(root.childNodes[0].childNodes[0]) + return root.childNodes[0].childNodes[0].nodeName === "G" + }) + test(function() { + var root = mock.document.createElement("div") + m.render(root, m("svg", [m("a[href='http://google.com']")])) + console.log(root.childNodes[0].childNodes[0]) + return root.childNodes[0].childNodes[0].nodeName === "A" + }) //m.redraw test(function() { diff --git a/archive/v0.1.2/mithril.min.js b/archive/v0.1.2/mithril.min.js index de690171..0c7152e0 100644 --- a/archive/v0.1.2/mithril.min.js +++ b/archive/v0.1.2/mithril.min.js @@ -4,5 +4,5 @@ http://github.com/lhorie/mithril.js (c) Leo Horie License: MIT */ -!new function(a){function b(e,f,g){if(null!==f&&void 0!==f){var h=s.call(g),i=s.call(f);if(h!=i&&(null!==g&&void 0!==g&&d(g.nodes),g=new f.constructor,g.nodes=[]),"[object Array]"==i){for(var j=[],k=g.length===f.length,l=0;l-1?new f.constructor(f):f,g.nodes=[n]}else if(g.valueOf()!==f.valueOf()){if(f.$trusted){var q=g.nodes[0],j=[q];if(q){for(;q=q.nextSibling;)j.push(q);d(j);var p=e.lastChild;e.insertAdjacentHTML("beforeend",f),n=p?p.nextSibling:e.firstChild}else e.innerHTML=f}else n=g.nodes[0],e.appendChild(n),n.nodeValue=f;g=new f.constructor(f),g.nodes=[n]}else g.nodes.intact=!0}return g}}function c(a,b,c){for(var d in b){var e=b[d];if(!(d in c)||c[d]!==e){if(c[d]=e,"config"==d)continue;if(0==d.indexOf("on")&&"function"==typeof e&&(e=f(e,a)),"style"==d)for(var g in e)a.style[g]=e[g];else d in a?a[d]=e:a.setAttribute(d,e)}}return c}function d(a){for(var b=0;b16)m.redraw();else{var b=a.cancelAnimationFrame||a.clearTimeout,c=a.requestAnimationFrame||a.setTimeout;b(E),E=c(m.redraw,0)}}function h(a,b,c){J={};for(var d in b){if(d==c)return!void m.module(a,b[d]);var e=new RegExp("^"+d.replace(/:[^\/]+/g,"([^\\/]+)")+"$");if(e.test(c))return!void c.replace(e,function(){for(var c=d.match(/:[^\/]+/g),e=[].slice.call(arguments,1,-2),f=0;f0&&("GET"==a.method?a.url=a.url+(a.url.indexOf("?")<0?"?":"&")+n(b):a.data=c(b)),a}function p(a,b){var c=a.match(/:\w+/g);if(c&&b)for(var d=0;d0&&(f.attrs[d]=h.join(" "))}f=e(f),f.attrs=e(f.attrs),f.children=b?a[2]:a[1];for(var j in c)f.attrs[j]=j==d?(f.attrs[j]||"")+" "+c[j]:c[j];return f};var v,w={insertAdjacentHTML:function(b,c){a.document.write(c),a.document.close()},appendChild:function(b){void 0===v&&(v=a.document.createElement("html")),"HTML"==b.nodeName?v=b:v.appendChild(b),a.document.documentElement!==v&&a.document.replaceChild(v,a.document.documentElement)}},x=[],y={};m.render=function(c,d){var e=x.indexOf(c),f=0>e?x.push(c)-1:e,g=c==a.document||c==a.document.documentElement?w:c;y[f]=b(g,d,y[f])},m.trust=function(a){return a=new String(a),a.$trusted=!0,a};var z,A={view:function(){}},B={},C=0,D=0,E=0;m.module=function(a,b){m.startComputation(),z=a,A=b,B=new b.controller,m.endComputation()},m.redraw=function(){m.render(z||a.document,A.view(B)),D=C};var F=0,G=null;m.startComputation=function(){F++},m.endComputation=function(){F=Math.max(F-1,0),0==F&&(g(),G&&(G(),G=null))},m.withAttr=function(a,b){return function(c){b(a in c.currentTarget?c.currentTarget[a]:c.currentTarget.getAttribute(a))}};var H={pathname:"",hash:"#",search:"?"},I=function(){},J={};m.route=function(){if(3==arguments.length){var b=arguments[0],c=arguments[1],d=arguments[2];I=function(a){var e=a.slice(H[m.route.mode].length);h(b,d,e)||m.route(c,!0)};var e="hash"==m.route.mode?"onhashchange":"onpopstate";a[e]=function(){I(a.location[m.route.mode])},G=j,a[e]()}else if(arguments[0].addEventListener){var f=arguments[0],g=arguments[1];g||(f.removeEventListener("click",i),f.addEventListener("click",i))}else if("string"==typeof arguments[0]){var k=arguments[0],l=arguments[1]===!0;a.history.pushState?(G=function(){a.history[l?"replaceState":"pushState"](null,a.document.title,H[m.route.mode]+k),j()},I(H[m.route.mode]+k)):a.location[m.route.mode]=k}},m.route.param=function(a){return J[a]},m.route.mode="search",m.prop=function(a){return function(){return arguments.length&&(a=arguments[0]),a}},m.deferred=function(){var a=[],b=[],c={resolve:function(b){for(var c=0;c-1?new f.constructor(f):f,g.nodes=[o]}else if(g.valueOf()!==f.valueOf()){if(f.$trusted){var r=g.nodes[0],k=[r];if(r){for(;r=r.nextSibling;)k.push(r);d(k);var q=e.lastChild;e.insertAdjacentHTML("beforeend",f),o=q?q.nextSibling:e.firstChild}else e.innerHTML=f}else o=g.nodes[0],e.appendChild(o),o.nodeValue=f;g=new f.constructor(f),g.nodes=[o]}else g.nodes.intact=!0}return g}}function c(a,b,c,d){for(var e in b){var g=b[e];if(!(e in c)||c[e]!==g){if(c[e]=g,"config"==e)continue;if(0==e.indexOf("on")&&"function"==typeof g&&(g=f(g,a)),"style"==e)for(var h in g)a.style[h]=g[h];else e in a&&void 0===d?a[e]=g:void 0!==d&&"href"===e?a.setAttributeNS("http://www.w3.org/1999/xlink","href",g):a.setAttribute(e,g)}}return c}function d(a){for(var b=0;b16)m.redraw();else{var b=a.cancelAnimationFrame||a.clearTimeout,c=a.requestAnimationFrame||a.setTimeout;b(E),E=c(m.redraw,0)}}function h(a,b,c){J={};for(var d in b){if(d==c)return!void m.module(a,b[d]);var e=new RegExp("^"+d.replace(/:[^\/]+/g,"([^\\/]+)")+"$");if(e.test(c))return!void c.replace(e,function(){for(var c=d.match(/:[^\/]+/g),e=[].slice.call(arguments,1,-2),f=0;f0&&("GET"==a.method?a.url=a.url+(a.url.indexOf("?")<0?"?":"&")+n(b):a.data=c(b)),a}function p(a,b){var c=a.match(/:\w+/g);if(c&&b)for(var d=0;d0&&(f.attrs[d]=h.join(" "))}f=e(f),f.attrs=e(f.attrs),f.children=b?a[2]:a[1];for(var j in c)f.attrs[j]=j==d?(f.attrs[j]||"")+" "+c[j]:c[j];return f};var v,w={insertAdjacentHTML:function(b,c){a.document.write(c),a.document.close()},appendChild:function(b){void 0===v&&(v=a.document.createElement("html")),"HTML"==b.nodeName?v=b:v.appendChild(b),a.document.documentElement!==v&&a.document.replaceChild(v,a.document.documentElement)}},x=[],y={};m.render=function(c,d){var e=x.indexOf(c),f=0>e?x.push(c)-1:e,g=c==a.document||c==a.document.documentElement?w:c;y[f]=b(g,d,y[f])},m.trust=function(a){return a=new String(a),a.$trusted=!0,a};var z,A={view:function(){}},B={},C=0,D=0,E=0;m.module=function(a,b){m.startComputation(),z=a,A=b,B=new b.controller,m.endComputation()},m.redraw=function(){m.render(z||a.document,A.view(B)),D=C};var F=0,G=null;m.startComputation=function(){F++},m.endComputation=function(){F=Math.max(F-1,0),0==F&&(g(),G&&(G(),G=null))},m.withAttr=function(a,b){return function(c){b(a in c.currentTarget?c.currentTarget[a]:c.currentTarget.getAttribute(a))}};var H={pathname:"",hash:"#",search:"?"},I=function(){},J={};m.route=function(){if(3==arguments.length){var b=arguments[0],c=arguments[1],d=arguments[2];I=function(a){var e=a.slice(H[m.route.mode].length);h(b,d,e)||m.route(c,!0)};var e="hash"==m.route.mode?"onhashchange":"onpopstate";a[e]=function(){I(a.location[m.route.mode])},G=j,a[e]()}else if(arguments[0].addEventListener){var f=arguments[0],g=arguments[1];g||(f.removeEventListener("click",i),f.addEventListener("click",i))}else if("string"==typeof arguments[0]){var k=arguments[0],l=arguments[1]===!0;a.history.pushState?(G=function(){a.history[l?"replaceState":"pushState"](null,a.document.title,H[m.route.mode]+k),j()},I(H[m.route.mode]+k)):a.location[m.route.mode]=k}},m.route.param=function(a){return J[a]},m.route.mode="search",m.prop=function(a){return function(){return arguments.length&&(a=arguments[0]),a}},m.deferred=function(){var a=[],b=[],c={resolve:function(b){for(var c=0;c)o~2aW^yLS=;N(P9~1maaNJ)%lNkT zjMta7y-6?+AcRy=ML$9nP^r8X!4nZ`#pMMgBq-v6NJ!;{2cS|BNFc;pA2{dy<6UnW z-q?rP`ETcZ=R24GjQ{#w$9wNM&Rt7BapK7d{5yF6AbRiJm;V;LI582tc>mnjCxy!W z@6F6qBW+J1SFbzEE}=GhFS)|ilCwG&sonq6_ORx6X(-2BKjoU&Q9~D=4->Md8eRGkBxv=0gJ*HJO z+McS}$rUg&|IlqhcMoibeYH8@4s~;hFjG9AnC?6#5Ms_^~%PizNPMqQLU9RxR z2n(?8+OWeG7()PHCxJG*^*TVaJk4CoFgsh;R?%2kI2slWPs?an`-WVp){9Q-xLjM< zk@VehxxkmjhLbNkEcTtr^NwBi{^SKmjs0QrZpIRt5g)8SB4EC~U!EtLJGnC0R*B44 zI-Ta4*W2s$JiR54)hc@zeLkNTH@bx8y3>kqLf}+ootaO}IM(2KZn%|lR)mrVl1P77A)UAS`(+$0pxK;U34PfMerQ1%?Y!Y(3jZO6KOPcS^WX45c> zBW7G{0A@t*72j=PTu0f~1tN|FLaZQ)L-|K5LWPA+=ucb1T+?;WsJ9IYE{lEpgxj&l zes#is(u(Y@@3Bu#%mBORPYUo%oYax&lR>$42m??+5Vvk)fVX&9gmbGGh!7ste79pj0EHhDeg04=VxN> z!a8(m|FhAv{)vgRe)gjaZ=N=D4Wr%66%6*;#ebbCw2WNGQ0hhlvoG~PnE8xj68<}9 zm!^h_DC)8h6Xhm8#hjpt;*AY_^+_=y1%tQ%ds>npi;9Tzi3DwwaXKjrNqAnxnUsn% znT@geEqK=sqC!I4;-?BRc$YU&$;~Z1BnBk~wcI3?ZArt^>NR|7I&72u(d#%!k>)5q zlW8F<%5~`m-^8AGdWPHdo^e79 zMA7iR<64<2(Ywyxa&1p>H9Gr`YjwF!STsulo5d`;LiWZ=h(OCAi>WbdTM~i--dOHc z-|!l71qZ>#swC*5yec(GS`sF~YnsVrH~2_KaT@n$hwtl3_M9$?>G;?>L<1^nkJiC5 z-DYj%I`*{t!m!Wx>4FM;#^5Ozk%c@54KQHd;3j5;poD~iP6)<0Id@OW6KPS@=98|H z(iI8kQnFAG#ljK?Y8$lA)!91~VOW+#u`Ky%+73+GDlf&scpxzi#$yZ|5rZQ{DrywY zg*+-%xp7GV2f8z0KAg=4Xq2u{h^V`yWwbMhYeO-;Iu0ql%nxcvrLJKr_%~~O2jJo! zPKq&8qFB}Jpdj{;m8Jx{0M4gCAzbb=^a+j#VnPnd#GS?|$^37qk3}knqWrkdz9d|F z5wlrD4nRQxiCC04w2Xi&A2 zJ2pLsx1D ztXtZVIb=+`|JRM}_lP zm(hE2E$Stzsi4VQ1ctIq{J+qCjP;?YgzTF{^O14c0~x@^`V#mHeL5Z_Faa^m>*k>g zV9t*Jq))=?n)S;-kW!b3p+_$Q&OSsLF`B1KW5heJlbkFCDj?h4khz=FisXa}Fe0!LK90=j*VJzNY&`uyZT5!#eo2Yl>9 zxw4mE*7_2k3@AbuHVvd@K3;i{T$*u+t3-*mNcdEhCj#^XCIkSG4*Y?Is<}b&-;H7b zWe_|y9u;7g{U9*!ZIH~YpQI#jAjB9b#G~jSmF6`8+Ja=Xi3U1euh6*L8+3oo-9 z0ruO~#OtfvJXlcR`oO?0lT1Jbre+VQWmA1|(4#?3RH6Oy$I&ny`==WzUIdVE&KhTtjUP7KOC)m%{Qu8h97wy)z z!3W5WU#gR7dV~4H7lTcbrz8E;k*>;nw5(CXT1X-+Gz@P*K%VO)Fr{w{*w@0B9DVk~ z@cBtduiu0>r~Bj^wZYC`c1^e96ox&fUiM8xECHoSH~Z@4>}X}cKDs==QcH}yCdSGf zqkYmMTvh0+0e}vZC;?wYNb)VG!vxVNQZ0_x;?xwteF1Eyc8T(sNu|##aR=BpJp>{qbd%OP$JZ9)a2%4So}k`> zWFc?z^*nhgNYQRlBQ_K@kbyyF3+BBi?34R4y1&L7?_`@iY}t(}cnG_7smi?*ilZiI z;Xz4VvU_h^;7VNBh3vK@{3e3%KCbS3Fb?%m%u#EN=8QpEVbS%?X$VDtq4(Ig)@M{8 z2t5v+1~^8?%?UTgg1js*6BKQQYN%cz&!bUz%QhaJM0!`)RbdJF%TG4(BM`eeg*}u@ z^H5x2QESkbWLC%^N94>@g}40{+ljiT>%1Z~*f*konXd4wWxx)U7pMDN`+&WzTsYG| z_NfqLzgE)Ipb&Yp*uus;(*O(1(}V1H8^-cJ1uaoye~UsWgiSZm1yHGaJYxErJay3#0k#ugp6*jt z-fXb9;-S;E4eU(IQkVTP-g#+>Uu6~v%#d$jio_l84BumWiRSbw7aCw6C+5x`T&3y- z;RnQ=~`T7dyaGaIE;kFSg@V#bFLNQ0<8tlEzKfB-}fU3G2<@Z94kl9xg z6?XfkP<`x|N#%5hs!LU}*ypd798K20IzOyZQBT#?F$dUlHR^l$jS--z$w2sH#1Hr( z*r1CF+}ekW09WOU8AO=?y5Zm0F`((d{#r;ugQ2r8s58%wa8Y*PBRybAbs%);hl^Aj wJ838Y^8CGPNyp@K6Oa2u}j_vpn z=i2cjk9lDJsv-nb2Y#TGO3)7wNJxWJ1V~7HAS6UT0P%tNL2Z$Es6y%w5`3`sKG%5+ zMdE{e9Nn|eIeV|Y_d0tY=j}K9-haLC!pq4sk34pY{(kfQgXq_v`_7s(ctiX)+>eK0DXJ!?&g}qG*%;3 zU%u|-bM0}GDE(ejdj;UmjEy<+51tE_b5e(nxDtfsxRSB4qA%BI6rF-7S89cEM}Fh<#lEWi$?3tqiv0cQdlwz89tq&oLu%`b z<4#0=@63?=`cK5zl%aba=T-X?=h2B&v9e#Q>_HN;(bL+nL5CX4v z0jhic2X1M3)}qvxlo@2=P#5xe{=b-!-+CnVgrkkrz4wc?R*{M2$o~F`zJ?6;2m75! zW3eGi{X>0@e5GH*_=Elw#OE*ip9KV_0=eS1`Jb6 zW3r@~y52N2p_@9%6EWT>NSqW}65}#S(n*qJdAlpW3WRGyy zoJQEolio?A&3KY6Ip>>x{uo|l0Y2vGe!w;&n^4RO$uIfNcl|0J69i7|8kk~-jcjfk zx~?LTV<8+Vl2Mz-bq-6$B=Y8~e7)=~p%wL5EvoX@BcYq=G}iQDjjpC>3w3{>zS|?e z*)W*=X>AXN_Y5clHHr??Dn+1@jGb#2*A9Jsl_Y%eL~u!Apkkw^X*6#zMO7fS1p{rU zI#3tHl`KVMaaNTKcO}F*%Fv4PBEhCJY8j=BSbc;e9g=_Y`?~uaLu-z&TatVe)u@U! z*B`OMP(V@OUH^Ya-0aQ^j;n6(v19AI0yty)Mv?3tr>@IfbrTP1j*Pf8PaddSv@Z|= zvxcG$1rOJ36?br{7xC;-$W{G1!B|s|p(oL`SVa`fYs}*Shr+!(n1EMVrUszXHRlk^ zcJN1!&r%f-wI$;CjcNJP`YnnOYm*@|sP__ohDeXp@PW3Pt7R=^0Tr$*^59Nnx>iI3o^I zchs$;9ZE*Ug?+&S|H64yL)ybBrwV3N|HSdsK;aANrwXcK+rq;ox1LKaG2ObR0 z4E5<#+(YdH#)qy0e=O>!4zhv{fDn+Ry2O2bC#v`k@Z7k|VqVDmp;13Pfoy%BwIumE z#R8^MM((iOX3JvM49IsvpAVE{DhM&k2nsk{v}zB@xCgm1`MKFxU{_7{VpIiCmJy4@ zK0iS0kCGk8zPS%Q4!4iBy8PFiJU3&KD=4WYL}PVQzA>M;J*C{kf{AWQ^>L1JN~p-K zHZ@h<>ckVbAg;7Ld8{3xjInzLmD)AOV`OLum1QkZ*;5 zeSZE1Cp|%Q5&A46&&TJOM9o0IzM))6BErC&os?^fFLV_h*O^Wt>+W!^fw+iz zTX6eY4>3(U>ZbPqVGD(UedGa($TA-lP4j4K>L%z6j@r!kR{R?6?kEE_{ zLkA_;RjeL&*eB3Wz=Eow9v*d0blu>JY$HST^fe|_c4p#a3GZPTsTn6ZhLL9~#!lwMqH3zD&%ZtK5-geaJ$9DC2kwATcSzotPl{iWw1P3fhxg zV^gzCYI2Gmn3acx6y8bsTm90xc|K;jug-uxYotHcFngs}ppb1Ee?I3w9-~bC!KcNy z`=0c3I&<{y2&&>R5tYxHpM4CCkADE+3cO`*j-vPitzF_DUF%wwV#IMv&+>h;EAsNv zgvZZhwM+8$(tDm+)hb%@YV280Q*CR>dTj7qQ)R$S*R=dvEaO4TpcGc*KVols01NJ= zrsbRQ;!KlC%RcrB6D!NiCy9%0VG1c3-~ecBM=m5j`4K1Q)Gq(=ldcTf)X6U=%E3Eq zGuh#oSwH}6DTf?FCEH^x$=zjjbyXrRFVA?|d@HY4WOaG|QNKwK$&#@nzq{PN6jaq} zn-yj`H{430ZeQK-x)jNVXGSTVls`_6oZr35MGW+V`Q#Mc38X0`KTO^ZwK;~zqY^mW z){BZL3s)HAq7$pgSFgV7MLk2&oQbNwkz-_T>*D8(+exgB{EB5h+TdnaF}CEp)@EN# zF0YJsC)jwp%)dI_1h_IkQ-oug2rz51D9FV44tI()Hl%bDUHxzoa4oe+KaTZ;O8Bv3 zi29u4OXS(Usr}c(Zj{JgN95;4%!DEhu=}@ZTW`%H2>$~=)aQkXSQ$n + + + +

Since it's not possible to test SVG functionality from a NodeJS environment, this page can be used to test it in a browser.

+

There should be an HTML link labeled "HTML link", a SVG link labeled "SVG link", and a tilted blue square. The links should open in a new tab. All three items should display title tooltips.

+
+ + + + \ No newline at end of file