Remove dependance on global window and document (#2897)
* Remove dependance on global window and document * Use any available document, prioritising parent.ownerDocument * Fix mockDom for DocumentFragment, revert to better ownerDocument implementation * Simplify activeElement usage
This commit is contained in:
parent
5488953436
commit
8e7ef427e0
2 changed files with 22 additions and 17 deletions
|
|
@ -5,9 +5,7 @@ var df = require("../render/domFor")
|
||||||
var delayedRemoval = df.delayedRemoval
|
var delayedRemoval = df.delayedRemoval
|
||||||
var domFor = df.domFor
|
var domFor = df.domFor
|
||||||
|
|
||||||
module.exports = function($window) {
|
module.exports = function() {
|
||||||
var $doc = $window && $window.document
|
|
||||||
|
|
||||||
var nameSpace = {
|
var nameSpace = {
|
||||||
svg: "http://www.w3.org/2000/svg",
|
svg: "http://www.w3.org/2000/svg",
|
||||||
math: "http://www.w3.org/1998/Math/MathML"
|
math: "http://www.w3.org/1998/Math/MathML"
|
||||||
|
|
@ -16,6 +14,10 @@ module.exports = function($window) {
|
||||||
var currentRedraw
|
var currentRedraw
|
||||||
var currentRender
|
var currentRender
|
||||||
|
|
||||||
|
function getDocument(dom) {
|
||||||
|
return dom.ownerDocument;
|
||||||
|
}
|
||||||
|
|
||||||
function getNameSpace(vnode) {
|
function getNameSpace(vnode) {
|
||||||
return vnode.attrs && vnode.attrs.xmlns || nameSpace[vnode.tag]
|
return vnode.attrs && vnode.attrs.xmlns || nameSpace[vnode.tag]
|
||||||
}
|
}
|
||||||
|
|
@ -40,9 +42,9 @@ module.exports = function($window) {
|
||||||
|
|
||||||
// IE11 (at least) throws an UnspecifiedError when accessing document.activeElement when
|
// IE11 (at least) throws an UnspecifiedError when accessing document.activeElement when
|
||||||
// inside an iframe. Catch and swallow this error, and heavy-handidly return null.
|
// inside an iframe. Catch and swallow this error, and heavy-handidly return null.
|
||||||
function activeElement() {
|
function activeElement(dom) {
|
||||||
try {
|
try {
|
||||||
return $doc.activeElement
|
return getDocument(dom).activeElement
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +73,7 @@ module.exports = function($window) {
|
||||||
else createComponent(parent, vnode, hooks, ns, nextSibling)
|
else createComponent(parent, vnode, hooks, ns, nextSibling)
|
||||||
}
|
}
|
||||||
function createText(parent, vnode, nextSibling) {
|
function createText(parent, vnode, nextSibling) {
|
||||||
vnode.dom = $doc.createTextNode(vnode.children)
|
vnode.dom = getDocument(parent).createTextNode(vnode.children)
|
||||||
insertDOM(parent, vnode.dom, nextSibling)
|
insertDOM(parent, vnode.dom, nextSibling)
|
||||||
}
|
}
|
||||||
var possibleParents = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}
|
var possibleParents = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}
|
||||||
|
|
@ -82,7 +84,7 @@ module.exports = function($window) {
|
||||||
// div.innerHTML = "<td>i</td><td>j</td>"
|
// div.innerHTML = "<td>i</td><td>j</td>"
|
||||||
// console.log(div.innerHTML)
|
// console.log(div.innerHTML)
|
||||||
// --> "ij", no <td> in sight.
|
// --> "ij", no <td> in sight.
|
||||||
var temp = $doc.createElement(possibleParents[match[1]] || "div")
|
var temp = getDocument(parent).createElement(possibleParents[match[1]] || "div")
|
||||||
if (ns === "http://www.w3.org/2000/svg") {
|
if (ns === "http://www.w3.org/2000/svg") {
|
||||||
temp.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\">" + vnode.children + "</svg>"
|
temp.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\">" + vnode.children + "</svg>"
|
||||||
temp = temp.firstChild
|
temp = temp.firstChild
|
||||||
|
|
@ -92,7 +94,7 @@ module.exports = function($window) {
|
||||||
vnode.dom = temp.firstChild
|
vnode.dom = temp.firstChild
|
||||||
vnode.domSize = temp.childNodes.length
|
vnode.domSize = temp.childNodes.length
|
||||||
// Capture nodes to remove, so we don't confuse them.
|
// Capture nodes to remove, so we don't confuse them.
|
||||||
var fragment = $doc.createDocumentFragment()
|
var fragment = getDocument(parent).createDocumentFragment()
|
||||||
var child
|
var child
|
||||||
while (child = temp.firstChild) {
|
while (child = temp.firstChild) {
|
||||||
fragment.appendChild(child)
|
fragment.appendChild(child)
|
||||||
|
|
@ -100,7 +102,7 @@ module.exports = function($window) {
|
||||||
insertDOM(parent, fragment, nextSibling)
|
insertDOM(parent, fragment, nextSibling)
|
||||||
}
|
}
|
||||||
function createFragment(parent, vnode, hooks, ns, nextSibling) {
|
function createFragment(parent, vnode, hooks, ns, nextSibling) {
|
||||||
var fragment = $doc.createDocumentFragment()
|
var fragment = getDocument(parent).createDocumentFragment()
|
||||||
if (vnode.children != null) {
|
if (vnode.children != null) {
|
||||||
var children = vnode.children
|
var children = vnode.children
|
||||||
createNodes(fragment, children, 0, children.length, hooks, null, ns)
|
createNodes(fragment, children, 0, children.length, hooks, null, ns)
|
||||||
|
|
@ -117,8 +119,8 @@ module.exports = function($window) {
|
||||||
ns = getNameSpace(vnode) || ns
|
ns = getNameSpace(vnode) || ns
|
||||||
|
|
||||||
var element = ns ?
|
var element = ns ?
|
||||||
is ? $doc.createElementNS(ns, tag, {is: is}) : $doc.createElementNS(ns, tag) :
|
is ? getDocument(parent).createElementNS(ns, tag, {is: is}) : getDocument(parent).createElementNS(ns, tag) :
|
||||||
is ? $doc.createElement(tag, {is: is}) : $doc.createElement(tag)
|
is ? getDocument(parent).createElement(tag, {is: is}) : getDocument(parent).createElement(tag)
|
||||||
vnode.dom = element
|
vnode.dom = element
|
||||||
|
|
||||||
if (attrs != null) {
|
if (attrs != null) {
|
||||||
|
|
@ -553,7 +555,7 @@ module.exports = function($window) {
|
||||||
// don't allocate for the common case
|
// don't allocate for the common case
|
||||||
target = vnode.dom
|
target = vnode.dom
|
||||||
} else {
|
} else {
|
||||||
target = $doc.createDocumentFragment()
|
target = getDocument(parent).createDocumentFragment()
|
||||||
for (var dom of domFor(vnode)) target.appendChild(dom)
|
for (var dom of domFor(vnode)) target.appendChild(dom)
|
||||||
}
|
}
|
||||||
insertDOM(parent, target, nextSibling)
|
insertDOM(parent, target, nextSibling)
|
||||||
|
|
@ -693,7 +695,7 @@ module.exports = function($window) {
|
||||||
/* eslint-disable no-implicit-coercion */
|
/* eslint-disable no-implicit-coercion */
|
||||||
//setting input[value] to same value by typing on focused element moves cursor to end in Chrome
|
//setting input[value] to same value by typing on focused element moves cursor to end in Chrome
|
||||||
//setting input[type=file][value] to same value causes an error to be generated if it's non-empty
|
//setting input[type=file][value] to same value causes an error to be generated if it's non-empty
|
||||||
if ((vnode.tag === "input" || vnode.tag === "textarea") && vnode.dom.value === "" + value && (isFileInput || vnode.dom === activeElement())) return
|
if ((vnode.tag === "input" || vnode.tag === "textarea") && vnode.dom.value === "" + value && (isFileInput || vnode.dom === activeElement(vnode.dom))) return
|
||||||
//setting select[value] to same value while having select open blinks select dropdown in Chrome
|
//setting select[value] to same value while having select open blinks select dropdown in Chrome
|
||||||
if (vnode.tag === "select" && old !== null && vnode.dom.value === "" + value) return
|
if (vnode.tag === "select" && old !== null && vnode.dom.value === "" + value) return
|
||||||
//setting option[value] to same value while having select open blinks select dropdown in Chrome
|
//setting option[value] to same value while having select open blinks select dropdown in Chrome
|
||||||
|
|
@ -722,7 +724,7 @@ module.exports = function($window) {
|
||||||
&& key !== "title" // creates "null" as title
|
&& key !== "title" // creates "null" as title
|
||||||
&& !(key === "value" && (
|
&& !(key === "value" && (
|
||||||
vnode.tag === "option"
|
vnode.tag === "option"
|
||||||
|| vnode.tag === "select" && vnode.dom.selectedIndex === -1 && vnode.dom === activeElement()
|
|| vnode.tag === "select" && vnode.dom.selectedIndex === -1 && vnode.dom === activeElement(vnode.dom)
|
||||||
))
|
))
|
||||||
&& !(vnode.tag === "input" && key === "type")
|
&& !(vnode.tag === "input" && key === "type")
|
||||||
) {
|
) {
|
||||||
|
|
@ -771,7 +773,7 @@ module.exports = function($window) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function isFormAttribute(vnode, attr) {
|
function isFormAttribute(vnode, attr) {
|
||||||
return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === activeElement() || vnode.tag === "option" && vnode.dom.parentNode === $doc.activeElement
|
return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === activeElement(vnode.dom) || vnode.tag === "option" && vnode.dom.parentNode === activeElement(vnode.dom)
|
||||||
}
|
}
|
||||||
function isLifecycleMethod(attr) {
|
function isLifecycleMethod(attr) {
|
||||||
return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate"
|
return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate"
|
||||||
|
|
@ -923,7 +925,7 @@ module.exports = function($window) {
|
||||||
var prevRedraw = currentRedraw
|
var prevRedraw = currentRedraw
|
||||||
var prevDOM = currentDOM
|
var prevDOM = currentDOM
|
||||||
var hooks = []
|
var hooks = []
|
||||||
var active = activeElement()
|
var active = activeElement(dom)
|
||||||
var namespace = dom.namespaceURI
|
var namespace = dom.namespaceURI
|
||||||
|
|
||||||
currentDOM = dom
|
currentDOM = dom
|
||||||
|
|
@ -936,7 +938,7 @@ module.exports = function($window) {
|
||||||
updateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace)
|
updateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace)
|
||||||
dom.vnodes = vnodes
|
dom.vnodes = vnodes
|
||||||
// `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement
|
// `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement
|
||||||
if (active != null && activeElement() !== active && typeof active.focus === "function") active.focus()
|
if (active != null && activeElement(dom) !== active && typeof active.focus === "function") active.focus()
|
||||||
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
||||||
} finally {
|
} finally {
|
||||||
currentRedraw = prevRedraw
|
currentRedraw = prevRedraw
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,7 @@ module.exports = function(options) {
|
||||||
parentNode: null,
|
parentNode: null,
|
||||||
childNodes: [],
|
childNodes: [],
|
||||||
attributes: {},
|
attributes: {},
|
||||||
|
ownerDocument: $window.document,
|
||||||
contains: function(child) {
|
contains: function(child) {
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
if (child === this) return true
|
if (child === this) return true
|
||||||
|
|
@ -717,6 +718,7 @@ module.exports = function(options) {
|
||||||
},
|
},
|
||||||
createDocumentFragment: function() {
|
createDocumentFragment: function() {
|
||||||
return {
|
return {
|
||||||
|
ownerDocument: $window.document,
|
||||||
nodeType: 11,
|
nodeType: 11,
|
||||||
nodeName: "#document-fragment",
|
nodeName: "#document-fragment",
|
||||||
appendChild: appendChild,
|
appendChild: appendChild,
|
||||||
|
|
@ -738,6 +740,7 @@ module.exports = function(options) {
|
||||||
get activeElement() {return activeElement},
|
get activeElement() {return activeElement},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
$window.document.defaultView = $window
|
||||||
$window.document.documentElement = $window.document.createElement("html")
|
$window.document.documentElement = $window.document.createElement("html")
|
||||||
appendChild.call($window.document.documentElement, $window.document.createElement("head"))
|
appendChild.call($window.document.documentElement, $window.document.createElement("head"))
|
||||||
$window.document.body = $window.document.createElement("body")
|
$window.document.body = $window.document.createElement("body")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue