[mocks] Add some support for parsing SVG using the DOMParser API and innerHTML
This commit is contained in:
parent
f58da124e5
commit
fdd34f9726
3 changed files with 148 additions and 28 deletions
|
|
@ -29,7 +29,7 @@
|
||||||
- API: Event handlers may also be objects with `handleEvent` methods ([#1939](https://github.com/MithrilJS/mithril.js/issues/1939)).
|
- 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.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))
|
- API: `m.request` supports `timeout` as attr - ([#1966](https://github.com/MithrilJS/mithril.js/pull/1966))
|
||||||
|
- Mocks: add limited support for the DOMParser API
|
||||||
|
|
||||||
#### Bug fixes
|
#### Bug fixes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Known limitations:
|
Known limitations:
|
||||||
|
- the innerHTML setter and the DOMParser only support a small subset of the true HTML/XML syntax.
|
||||||
- `option.selected` can't be set/read when the option doesn't have a `select` parent
|
- `option.selected` can't be set/read when the option doesn't have a `select` parent
|
||||||
- `element.attributes` is just a map of attribute names => Attr objects stubs
|
- `element.attributes` is just a map of attribute names => Attr objects stubs
|
||||||
- ...
|
- ...
|
||||||
|
|
@ -183,9 +183,43 @@ module.exports = function(options) {
|
||||||
res.unshift(declList)
|
res.unshift(declList)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
function parseMarkup(value, root, voidElements, xmlns) {
|
||||||
|
var depth = 0, stack = [root]
|
||||||
|
value.replace(/<([a-z0-9\-]+?)((?:\s+?[^=]+?=(?:"[^"]*?"|'[^']*?'|[^\s>]*))*?)(\s*\/)?>|<\/([a-z0-9\-]+?)>|([^<]+)/g, function(match, startTag, attrs, selfClosed, endTag, text) {
|
||||||
|
if (startTag) {
|
||||||
|
var element = xmlns == null ? $window.document.createElement(startTag) : $window.document.createElementNS(xmlns, startTag)
|
||||||
|
attrs.replace(/\s+?([^=]+?)=(?:"([^"]*?)"|'([^']*?)'|([^\s>]*))/g, function(match, key, doubleQuoted, singleQuoted, unquoted) {
|
||||||
|
var keyParts = key.split(":")
|
||||||
|
var name = keyParts.pop()
|
||||||
|
var ns = keyParts[0]
|
||||||
|
var value = doubleQuoted || singleQuoted || unquoted || ""
|
||||||
|
if (ns != null) element.setAttributeNS(ns, name, value)
|
||||||
|
else element.setAttribute(name, value)
|
||||||
|
})
|
||||||
|
stack[depth].appendChild(element)
|
||||||
|
if (!selfClosed && voidElements.indexOf(startTag.toLowerCase()) < 0) stack[++depth] = element
|
||||||
|
}
|
||||||
|
else if (endTag) {
|
||||||
|
depth--
|
||||||
|
}
|
||||||
|
else if (text) {
|
||||||
|
stack[depth].appendChild($window.document.createTextNode(text)) // FIXME handle html entities
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function DOMParser() {}
|
||||||
|
DOMParser.prototype.parseFromString = function(src, mime) {
|
||||||
|
if (mime !== "image/svg+xml") throw new Error("The DOMParser mock only supports the \"image/svg+xml\" MIME type")
|
||||||
|
var match = src.match(/^<svg xmlns="http:\/\/www\.w3\.org\/2000\/svg">(.*)<\/svg>$/)
|
||||||
|
if (!match) throw new Error("Please provide a bare SVG tag with the xmlns as only attribute")
|
||||||
|
var value = match[1]
|
||||||
|
var root = $window.document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||||
|
parseMarkup(value, root, [], "http://www.w3.org/2000/svg")
|
||||||
|
return {documentElement: root}
|
||||||
|
}
|
||||||
var activeElement
|
var activeElement
|
||||||
var $window = {
|
var $window = {
|
||||||
|
DOMParser: DOMParser,
|
||||||
document: {
|
document: {
|
||||||
createElement: function(tag) {
|
createElement: function(tag) {
|
||||||
var cssText = ""
|
var cssText = ""
|
||||||
|
|
@ -244,30 +278,18 @@ module.exports = function(options) {
|
||||||
if (value !== "") this.appendChild($window.document.createTextNode(value))
|
if (value !== "") this.appendChild($window.document.createTextNode(value))
|
||||||
},
|
},
|
||||||
set innerHTML(value) {
|
set innerHTML(value) {
|
||||||
|
var voidElements = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]
|
||||||
while (this.firstChild) this.removeChild(this.firstChild)
|
while (this.firstChild) this.removeChild(this.firstChild)
|
||||||
|
var match = value.match(/^<svg xmlns="http:\/\/www\.w3\.org\/2000\/svg">(.*)<\/svg>$/), root, ns
|
||||||
var stack = [this], depth = 0, voidElements = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]
|
if (match) {
|
||||||
value.replace(/<([a-z0-9\-]+?)((?:\s+?[^=]+?=(?:"[^"]*?"|'[^']*?'|[^\s>]*))*?)(\s*\/)?>|<\/([a-z0-9\-]+?)>|([^<]+)/g, function(match, startTag, attrs, selfClosed, endTag, text) {
|
var value = match[1]
|
||||||
if (startTag) {
|
root = $window.document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||||
var element = $window.document.createElement(startTag)
|
ns = "http://www.w3.org/2000/svg"
|
||||||
attrs.replace(/\s+?([^=]+?)=(?:"([^"]*?)"|'([^']*?)'|([^\s>]*))/g, function(match, key, doubleQuoted, singleQuoted, unquoted) {
|
this.appendChild(root)
|
||||||
var keyParts = key.split(":")
|
} else {
|
||||||
var name = keyParts.pop()
|
root = this
|
||||||
var ns = keyParts[0]
|
}
|
||||||
var value = doubleQuoted || singleQuoted || unquoted || ""
|
parseMarkup(value, root, voidElements, ns)
|
||||||
if (ns != null) element.setAttributeNS(ns, name, value)
|
|
||||||
else element.setAttribute(name, value)
|
|
||||||
})
|
|
||||||
stack[depth].appendChild(element)
|
|
||||||
if (!selfClosed && voidElements.indexOf(startTag.toLowerCase()) < 0) stack[++depth] = element
|
|
||||||
}
|
|
||||||
else if (endTag) {
|
|
||||||
depth--
|
|
||||||
}
|
|
||||||
else if (text) {
|
|
||||||
stack[depth].appendChild($window.document.createTextNode(text)) // FIXME handle html entities
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
get style() {
|
get style() {
|
||||||
return style
|
return style
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@ var o = require("../../ospec/ospec")
|
||||||
var domMock = require("../../test-utils/domMock")
|
var domMock = require("../../test-utils/domMock")
|
||||||
|
|
||||||
o.spec("domMock", function() {
|
o.spec("domMock", function() {
|
||||||
var $document
|
var $document, $window
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
$document = domMock().document
|
$window = domMock()
|
||||||
|
$document = $window.document
|
||||||
})
|
})
|
||||||
|
|
||||||
o.spec("createElement", function() {
|
o.spec("createElement", function() {
|
||||||
|
|
@ -497,6 +498,45 @@ o.spec("domMock", function() {
|
||||||
|
|
||||||
o(a.parentNode).equals(null)
|
o(a.parentNode).equals(null)
|
||||||
})
|
})
|
||||||
|
o("empty SVG document", function() {
|
||||||
|
var div = $document.createElement("div")
|
||||||
|
div.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>"
|
||||||
|
|
||||||
|
o(typeof div.firstChild).notEquals(undefined)
|
||||||
|
o(div.firstChild.nodeName).equals("svg")
|
||||||
|
o(div.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(div.firstChild.childNodes.length).equals(0)
|
||||||
|
})
|
||||||
|
o("text elements", function() {
|
||||||
|
var div = $document.createElement("div")
|
||||||
|
div.innerHTML =
|
||||||
|
"<svg xmlns=\"http://www.w3.org/2000/svg\">"
|
||||||
|
+ "<text>hello</text>"
|
||||||
|
+ "<text> </text>"
|
||||||
|
+ "<text>world</text>"
|
||||||
|
+ "</svg>"
|
||||||
|
|
||||||
|
o(div.firstChild.nodeName).equals("svg")
|
||||||
|
o(div.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
|
||||||
|
var nodes = div.firstChild.childNodes
|
||||||
|
o(nodes.length).equals(3)
|
||||||
|
o(nodes[0].nodeName).equals("text")
|
||||||
|
o(nodes[0].namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(nodes[0].childNodes.length).equals(1)
|
||||||
|
o(nodes[0].childNodes[0].nodeName).equals("#text")
|
||||||
|
o(nodes[0].childNodes[0].nodeValue).equals("hello")
|
||||||
|
o(nodes[1].nodeName).equals("text")
|
||||||
|
o(nodes[1].namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(nodes[1].childNodes.length).equals(1)
|
||||||
|
o(nodes[1].childNodes[0].nodeName).equals("#text")
|
||||||
|
o(nodes[1].childNodes[0].nodeValue).equals(" ")
|
||||||
|
o(nodes[2].nodeName).equals("text")
|
||||||
|
o(nodes[2].namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(nodes[2].childNodes.length).equals(1)
|
||||||
|
o(nodes[2].childNodes[0].nodeName).equals("#text")
|
||||||
|
o(nodes[2].childNodes[0].nodeValue).equals("world")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
o.spec("focus", function() {
|
o.spec("focus", function() {
|
||||||
o("body is active by default", function() {
|
o("body is active by default", function() {
|
||||||
|
|
@ -1792,4 +1832,62 @@ o.spec("domMock", function() {
|
||||||
o(spies.valueSetter.args[0]).equals("aaa")
|
o(spies.valueSetter.args[0]).equals("aaa")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
o.spec("DOMParser for SVG", function(){
|
||||||
|
var $DOMParser
|
||||||
|
o.beforeEach(function() {
|
||||||
|
$DOMParser = $window.DOMParser
|
||||||
|
})
|
||||||
|
o("basics", function(){
|
||||||
|
o(typeof $DOMParser).equals("function")
|
||||||
|
|
||||||
|
var parser = new $DOMParser()
|
||||||
|
|
||||||
|
o(parser instanceof $DOMParser).equals(true)
|
||||||
|
o(typeof parser.parseFromString).equals("function")
|
||||||
|
})
|
||||||
|
o("empty document", function() {
|
||||||
|
var parser = new $DOMParser()
|
||||||
|
var doc = parser.parseFromString(
|
||||||
|
"<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>",
|
||||||
|
"image/svg+xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
o(typeof doc.documentElement).notEquals(undefined)
|
||||||
|
o(doc.documentElement.nodeName).equals("svg")
|
||||||
|
o(doc.documentElement.namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(doc.documentElement.childNodes.length).equals(0)
|
||||||
|
})
|
||||||
|
o("text elements", function() {
|
||||||
|
var parser = new $DOMParser()
|
||||||
|
var doc = parser.parseFromString(
|
||||||
|
"<svg xmlns=\"http://www.w3.org/2000/svg\">"
|
||||||
|
+ "<text>hello</text>"
|
||||||
|
+ "<text> </text>"
|
||||||
|
+ "<text>world</text>"
|
||||||
|
+ "</svg>",
|
||||||
|
"image/svg+xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
o(doc.documentElement.nodeName).equals("svg")
|
||||||
|
o(doc.documentElement.namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
|
||||||
|
var nodes = doc.documentElement.childNodes
|
||||||
|
o(nodes.length).equals(3)
|
||||||
|
o(nodes[0].nodeName).equals("text")
|
||||||
|
o(nodes[0].namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(nodes[0].childNodes.length).equals(1)
|
||||||
|
o(nodes[0].childNodes[0].nodeName).equals("#text")
|
||||||
|
o(nodes[0].childNodes[0].nodeValue).equals("hello")
|
||||||
|
o(nodes[1].nodeName).equals("text")
|
||||||
|
o(nodes[1].namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(nodes[1].childNodes.length).equals(1)
|
||||||
|
o(nodes[1].childNodes[0].nodeName).equals("#text")
|
||||||
|
o(nodes[1].childNodes[0].nodeValue).equals(" ")
|
||||||
|
o(nodes[2].nodeName).equals("text")
|
||||||
|
o(nodes[2].namespaceURI).equals("http://www.w3.org/2000/svg")
|
||||||
|
o(nodes[2].childNodes.length).equals(1)
|
||||||
|
o(nodes[2].childNodes[0].nodeName).equals("#text")
|
||||||
|
o(nodes[2].childNodes[0].nodeValue).equals("world")
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue