diff --git a/docs/change-log.md b/docs/change-log.md index 87343422..19b701a9 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -29,7 +29,8 @@ - API: Event handlers may also be objects with `handleEvent` methods ([#1939](https://github.com/MithrilJS/mithril.js/issues/1939)). - API: `m.route.link` accepts an optional `options` object ([#1930](https://github.com/MithrilJS/mithril.js/pull/1930)) - API: `m.request` supports `timeout` as attr - ([#1966](https://github.com/MithrilJS/mithril.js/pull/1966)) -- Mocks: add limited support for the DOMParser API +- Mocks: add limited support for the DOMParser API ([#2097](https://github.com/MithrilJS/mithril.js/pull/2097)) +- API: add support for raw SVG in `m.trust()` string ([#2097](https://github.com/MithrilJS/mithril.js/pull/2097)) #### Bug fixes diff --git a/docs/trust.md b/docs/trust.md index d960e743..34e6ace8 100644 --- a/docs/trust.md +++ b/docs/trust.md @@ -11,7 +11,7 @@ ### Description -Turns an HTML string into unescaped HTML. **Do not use `m.trust` on unsanitized user input.** +Turns an HTML or SVG string into unescaped HTML or SVG. **Do not use `m.trust` on unsanitized user input.** Always try to use an [alternative method](#avoid-trusting-html) first, before considering using `m.trust`. @@ -23,7 +23,7 @@ Always try to use an [alternative method](#avoid-trusting-html) first, before co Argument | Type | Required | Description ----------- | -------------------- | -------- | --- -`html` | `String` | Yes | A string containing HTML text +`html` | `String` | Yes | A string containing HTML or SVG text **returns** | `Vnode` | | A trusted HTML [vnode](vnodes.md) that represents the input string [How to read signatures](signatures.md) diff --git a/render/render.js b/render/render.js index 9efbde96..98e0f4d1 100644 --- a/render/render.js +++ b/render/render.js @@ -52,7 +52,7 @@ module.exports = function($window) { if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks) switch (tag) { case "#": return createText(parent, vnode, nextSibling) - case "<": return createHTML(parent, vnode, nextSibling) + case "<": return createHTML(parent, vnode, ns, nextSibling) case "[": return createFragment(parent, vnode, hooks, ns, nextSibling) default: return createElement(parent, vnode, hooks, ns, nextSibling) } @@ -64,12 +64,16 @@ module.exports = function($window) { insertNode(parent, vnode.dom, nextSibling) return vnode.dom } - function createHTML(parent, vnode, nextSibling) { + var possibleParents = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"} + function createHTML(parent, vnode, ns, nextSibling) { var match = vnode.children.match(/^\s*?<(\w+)/im) || [] - var parent1 = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match[1]] || "div" - var temp = $doc.createElement(parent1) - - temp.innerHTML = vnode.children + var temp = $doc.createElement(possibleParents[match[1]] || "div") + if (ns === "http://www.w3.org/2000/svg") { + temp.innerHTML = "" + vnode.children + "" + temp = temp.firstChild + } else { + temp.innerHTML = vnode.children + } vnode.dom = temp.firstChild vnode.domSize = temp.childNodes.length var fragment = $doc.createDocumentFragment() @@ -404,7 +408,7 @@ module.exports = function($window) { } switch (oldTag) { case "#": updateText(old, vnode); break - case "<": updateHTML(parent, old, vnode, nextSibling); break + case "<": updateHTML(parent, old, vnode, ns, nextSibling); break case "[": updateFragment(parent, old, vnode, recycling, hooks, nextSibling, ns); break default: updateElement(old, vnode, recycling, hooks, ns) } @@ -422,10 +426,10 @@ module.exports = function($window) { } vnode.dom = old.dom } - function updateHTML(parent, old, vnode, nextSibling) { + function updateHTML(parent, old, vnode, ns, nextSibling) { if (old.children !== vnode.children) { toFragment(old) - createHTML(parent, vnode, nextSibling) + createHTML(parent, vnode, ns, nextSibling) } else vnode.dom = old.dom, vnode.domSize = old.domSize } diff --git a/render/tests/test-createHTML.js b/render/tests/test-createHTML.js index bb0d508c..a337213b 100644 --- a/render/tests/test-createHTML.js +++ b/render/tests/test-createHTML.js @@ -31,7 +31,7 @@ o.spec("createHTML", function() { o(vnode.dom).equals(null) o(vnode.domSize).equals(0) }) - o("handles multiple children", function() { + o("handles multiple children in HTML", function() { var vnode = {tag: "<", children: ""} render(root, [vnode]) @@ -51,4 +51,34 @@ o.spec("createHTML", function() { o(vnode.dom.nodeName).equals(tag.toUpperCase()) }) }) + o("creates SVG", function() { + var vnode = {tag: "<", children: ""} + render(root, [{tag:"svg", children: [vnode]}]) + + o(vnode.dom.nodeName).equals("g") + o(vnode.dom.namespaceURI).equals("http://www.w3.org/2000/svg") + }) + o("creates text SVG", function() { + var vnode = {tag: "<", children: "a"} + render(root, [{tag:"svg", children: [vnode]}]) + + o(vnode.dom.nodeValue).equals("a") + }) + o("handles empty SVG", function() { + var vnode = {tag: "<", children: ""} + render(root, [{tag:"svg", children: [vnode]}]) + + o(vnode.dom).equals(null) + o(vnode.domSize).equals(0) + }) + o("handles multiple children in SVG", function() { + var vnode = {tag: "<", children: ""} + render(root, [{tag:"svg", children: [vnode]}]) + + o(vnode.domSize).equals(2) + o(vnode.dom.nodeName).equals("g") + o(vnode.dom.namespaceURI).equals("http://www.w3.org/2000/svg") + o(vnode.dom.nextSibling.nodeName).equals("text") + o(vnode.dom.nextSibling.namespaceURI).equals("http://www.w3.org/2000/svg") + }) })