mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
573 lines
20 KiB
JavaScript
573 lines
20 KiB
JavaScript
"use strict"
|
|
|
|
var Vnode = require("../render/vnode")
|
|
|
|
module.exports = function($window) {
|
|
var $doc = $window.document
|
|
var $emptyFragment = $doc.createDocumentFragment()
|
|
|
|
var onevent
|
|
function setEventCallback(callback) {return onevent = callback}
|
|
|
|
//create
|
|
function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) {
|
|
for (var i = start; i < end; i++) {
|
|
var vnode = vnodes[i]
|
|
if (vnode != null) {
|
|
insertNode(parent, createNode(vnode, hooks, ns), nextSibling)
|
|
}
|
|
}
|
|
}
|
|
function createNode(vnode, hooks, ns) {
|
|
var tag = vnode.tag
|
|
if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
|
|
if (typeof tag === "string") {
|
|
switch (tag) {
|
|
case "#": return createText(vnode)
|
|
case "<": return createHTML(vnode)
|
|
case "[": return createFragment(vnode, hooks, ns)
|
|
default: return createElement(vnode, hooks, ns)
|
|
}
|
|
}
|
|
else return createComponent(vnode, hooks, ns)
|
|
}
|
|
function createText(vnode) {
|
|
return vnode.dom = $doc.createTextNode(vnode.children)
|
|
}
|
|
function createHTML(vnode) {
|
|
var match = vnode.children.match(/^\s*?<(\w+)/im) || []
|
|
var parent = {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(parent)
|
|
|
|
temp.innerHTML = vnode.children
|
|
vnode.dom = temp.firstChild
|
|
vnode.domSize = temp.childNodes.length
|
|
var fragment = $doc.createDocumentFragment()
|
|
var child
|
|
while (child = temp.firstChild) {
|
|
fragment.appendChild(child)
|
|
}
|
|
return fragment
|
|
}
|
|
function createFragment(vnode, hooks, ns) {
|
|
var fragment = $doc.createDocumentFragment()
|
|
if (vnode.children != null) {
|
|
var children = vnode.children
|
|
createNodes(fragment, children, 0, children.length, hooks, null, ns)
|
|
}
|
|
vnode.dom = fragment.firstChild
|
|
vnode.domSize = fragment.childNodes.length
|
|
return fragment
|
|
}
|
|
function createElement(vnode, hooks, ns) {
|
|
var tag = vnode.tag
|
|
switch (vnode.tag) {
|
|
case "svg": ns = "http://www.w3.org/2000/svg"; break
|
|
case "math": ns = "http://www.w3.org/1998/Math/MathML"; break
|
|
}
|
|
|
|
var attrs = vnode.attrs
|
|
var is = attrs && attrs.is
|
|
|
|
var element = ns ?
|
|
is ? $doc.createElementNS(ns, tag, {is: is}) : $doc.createElementNS(ns, tag) :
|
|
is ? $doc.createElement(tag, {is: is}) : $doc.createElement(tag)
|
|
vnode.dom = element
|
|
|
|
if (attrs != null) {
|
|
setAttrs(vnode, attrs, ns)
|
|
}
|
|
|
|
if (vnode.attrs != null && vnode.attrs.contenteditable != null) {
|
|
setContentEditable(vnode)
|
|
}
|
|
else {
|
|
if (vnode.text != null) {
|
|
if (vnode.text !== "") element.textContent = vnode.text
|
|
else vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)]
|
|
}
|
|
if (vnode.children != null) {
|
|
var children = vnode.children
|
|
createNodes(element, children, 0, children.length, hooks, null, ns)
|
|
setLateAttrs(vnode)
|
|
}
|
|
}
|
|
return element
|
|
}
|
|
function createComponent(vnode, hooks, ns) {
|
|
vnode.state = Object.create(vnode.tag)
|
|
var view = vnode.tag.view
|
|
if (view.reentrantLock != null) return $emptyFragment
|
|
view.reentrantLock = true
|
|
initLifecycle(vnode.tag, vnode, hooks)
|
|
vnode.instance = Vnode.normalize(view.call(vnode.state, vnode))
|
|
view.reentrantLock = null
|
|
if (vnode.instance != null) {
|
|
if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments")
|
|
var element = createNode(vnode.instance, hooks, ns)
|
|
vnode.dom = vnode.instance.dom
|
|
vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0
|
|
return element
|
|
}
|
|
else {
|
|
vnode.domSize = 0
|
|
return $emptyFragment
|
|
}
|
|
}
|
|
|
|
//update
|
|
function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {
|
|
if (old === vnodes || old == null && vnodes == null) return
|
|
else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, undefined)
|
|
else if (vnodes == null) removeNodes(old, 0, old.length, vnodes)
|
|
else {
|
|
if (old.length === vnodes.length) {
|
|
var isUnkeyed = false
|
|
for (var i = 0; i < vnodes.length; i++) {
|
|
if (vnodes[i] != null && old[i] != null) {
|
|
isUnkeyed = vnodes[i].key == null && old[i].key == null
|
|
break
|
|
}
|
|
}
|
|
if (isUnkeyed) {
|
|
for (var i = 0; i < old.length; i++) {
|
|
if (old[i] === vnodes[i]) continue
|
|
else if (old[i] == null && vnodes[i] != null) insertNode(parent, createNode(vnodes[i], hooks, ns), getNextSibling(old, i + 1, nextSibling))
|
|
else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes)
|
|
else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), false, ns)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
var recycling = isRecyclable(old, vnodes)
|
|
if (recycling) old = old.concat(old.pool)
|
|
|
|
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
|
|
while (oldEnd >= oldStart && end >= start) {
|
|
var o = old[oldStart], v = vnodes[start]
|
|
if (o === v && !recycling) oldStart++, start++
|
|
else if (o == null) oldStart++
|
|
else if (v == null) start++
|
|
else if (o.key === v.key) {
|
|
oldStart++, start++
|
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
|
|
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
|
}
|
|
else {
|
|
var o = old[oldEnd]
|
|
if (o === v && !recycling) oldEnd--, start++
|
|
else if (o == null) oldEnd--
|
|
else if (v == null) start++
|
|
else if (o.key === v.key) {
|
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
|
if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
|
oldEnd--, start++
|
|
}
|
|
else break
|
|
}
|
|
}
|
|
while (oldEnd >= oldStart && end >= start) {
|
|
var o = old[oldEnd], v = vnodes[end]
|
|
if (o === v && !recycling) oldEnd--, end--
|
|
else if (o == null) oldEnd--
|
|
else if (v == null) end--
|
|
else if (o.key === v.key) {
|
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
|
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
|
if (o.dom != null) nextSibling = o.dom
|
|
oldEnd--, end--
|
|
}
|
|
else {
|
|
if (!map) map = getKeyMap(old, oldEnd)
|
|
if (v != null) {
|
|
var oldIndex = map[v.key]
|
|
if (oldIndex != null) {
|
|
var movable = old[oldIndex]
|
|
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
|
insertNode(parent, toFragment(movable), nextSibling)
|
|
old[oldIndex].skip = true
|
|
if (movable.dom != null) nextSibling = movable.dom
|
|
}
|
|
else {
|
|
var dom = createNode(v, hooks, undefined)
|
|
insertNode(parent, dom, nextSibling)
|
|
nextSibling = dom
|
|
}
|
|
}
|
|
end--
|
|
}
|
|
if (end < start) break
|
|
}
|
|
createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
|
|
removeNodes(old, oldStart, oldEnd + 1, vnodes)
|
|
}
|
|
}
|
|
function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) {
|
|
var oldTag = old.tag, tag = vnode.tag
|
|
if (oldTag === tag) {
|
|
vnode.state = old.state
|
|
vnode.events = old.events
|
|
if (shouldUpdate(vnode, old)) return
|
|
if (vnode.attrs != null) {
|
|
updateLifecycle(vnode.attrs, vnode, hooks, recycling)
|
|
}
|
|
if (typeof oldTag === "string") {
|
|
switch (oldTag) {
|
|
case "#": updateText(old, vnode); break
|
|
case "<": updateHTML(parent, old, vnode, nextSibling); break
|
|
case "[": updateFragment(parent, old, vnode, hooks, nextSibling, ns); break
|
|
default: updateElement(old, vnode, hooks, ns)
|
|
}
|
|
}
|
|
else updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns)
|
|
}
|
|
else {
|
|
removeNode(old, null)
|
|
insertNode(parent, createNode(vnode, hooks, ns), nextSibling)
|
|
}
|
|
}
|
|
function updateText(old, vnode) {
|
|
if (old.children.toString() !== vnode.children.toString()) {
|
|
old.dom.nodeValue = vnode.children
|
|
}
|
|
vnode.dom = old.dom
|
|
}
|
|
function updateHTML(parent, old, vnode, nextSibling) {
|
|
if (old.children !== vnode.children) {
|
|
toFragment(old)
|
|
insertNode(parent, createHTML(vnode), nextSibling)
|
|
}
|
|
else vnode.dom = old.dom, vnode.domSize = old.domSize
|
|
}
|
|
function updateFragment(parent, old, vnode, hooks, nextSibling, ns) {
|
|
updateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns)
|
|
var domSize = 0, children = vnode.children
|
|
vnode.dom = null
|
|
if (children != null) {
|
|
for (var i = 0; i < children.length; i++) {
|
|
var child = children[i]
|
|
if (child != null && child.dom != null) {
|
|
if (vnode.dom == null) vnode.dom = child.dom
|
|
domSize += child.domSize || 1
|
|
}
|
|
}
|
|
if (domSize !== 1) vnode.domSize = domSize
|
|
}
|
|
}
|
|
function updateElement(old, vnode, hooks, ns) {
|
|
var element = vnode.dom = old.dom
|
|
switch (vnode.tag) {
|
|
case "svg": ns = "http://www.w3.org/2000/svg"; break
|
|
case "math": ns = "http://www.w3.org/1998/Math/MathML"; break
|
|
}
|
|
if (vnode.tag === "textarea") {
|
|
if (vnode.attrs == null) vnode.attrs = {}
|
|
if (vnode.text != null) {
|
|
vnode.attrs.value = vnode.text //FIXME handle multiple children
|
|
vnode.text = undefined
|
|
}
|
|
}
|
|
updateAttrs(vnode, old.attrs, vnode.attrs, ns)
|
|
if (vnode.attrs != null && vnode.attrs.contenteditable != null) {
|
|
setContentEditable(vnode)
|
|
}
|
|
else if (old.text != null && vnode.text != null && vnode.text !== "") {
|
|
if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text
|
|
}
|
|
else {
|
|
if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)]
|
|
if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)]
|
|
updateNodes(element, old.children, vnode.children, hooks, null, ns)
|
|
}
|
|
}
|
|
function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) {
|
|
vnode.instance = Vnode.normalize(vnode.tag.view.call(vnode.state, vnode))
|
|
updateLifecycle(vnode.tag, vnode, hooks, recycling)
|
|
if (vnode.instance != null) {
|
|
if (old.instance == null) insertNode(parent, createNode(vnode.instance, hooks, ns), nextSibling)
|
|
else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns)
|
|
vnode.dom = vnode.instance.dom
|
|
vnode.domSize = vnode.instance.domSize
|
|
}
|
|
else if (old.instance != null) {
|
|
removeNode(old.instance, null)
|
|
vnode.dom = undefined
|
|
vnode.domSize = 0
|
|
}
|
|
else {
|
|
vnode.dom = old.dom
|
|
vnode.domSize = old.domSize
|
|
}
|
|
}
|
|
function isRecyclable(old, vnodes) {
|
|
if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) {
|
|
var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0
|
|
var poolChildrenLength = old.pool[0] && old.pool[0].children && old.pool[0].children.length || 0
|
|
var vnodesChildrenLength = vnodes[0] && vnodes[0].children && vnodes[0].children.length || 0
|
|
if (Math.abs(poolChildrenLength - vnodesChildrenLength) <= Math.abs(oldChildrenLength - vnodesChildrenLength)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
function getKeyMap(vnodes, end) {
|
|
var map = {}, i = 0
|
|
for (var i = 0; i < end; i++) {
|
|
var vnode = vnodes[i]
|
|
if (vnode != null) {
|
|
var key = vnode.key
|
|
if (key != null) map[key] = i
|
|
}
|
|
}
|
|
return map
|
|
}
|
|
function toFragment(vnode) {
|
|
var count = vnode.domSize
|
|
if (count != null || vnode.dom == null) {
|
|
var fragment = $doc.createDocumentFragment()
|
|
if (count > 0) {
|
|
var dom = vnode.dom
|
|
while (--count) fragment.appendChild(dom.nextSibling)
|
|
fragment.insertBefore(dom, fragment.firstChild)
|
|
}
|
|
return fragment
|
|
}
|
|
else return vnode.dom
|
|
}
|
|
function getNextSibling(vnodes, i, nextSibling) {
|
|
for (; i < vnodes.length; i++) {
|
|
if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom
|
|
}
|
|
return nextSibling
|
|
}
|
|
|
|
function insertNode(parent, dom, nextSibling) {
|
|
if (nextSibling && nextSibling.parentNode) parent.insertBefore(dom, nextSibling)
|
|
else parent.appendChild(dom)
|
|
}
|
|
|
|
function setContentEditable(vnode) {
|
|
var children = vnode.children
|
|
if (children != null && children.length === 1 && children[0].tag === "<") {
|
|
var content = children[0].children
|
|
if (vnode.dom.innerHTML !== content) vnode.dom.innerHTML = content
|
|
}
|
|
else if (vnode.text != null || children != null && children.length !== 0) throw new Error("Child node of a contenteditable must be trusted")
|
|
}
|
|
|
|
//remove
|
|
function removeNodes(vnodes, start, end, context) {
|
|
for (var i = start; i < end; i++) {
|
|
var vnode = vnodes[i]
|
|
if (vnode != null) {
|
|
if (vnode.skip) vnode.skip = false
|
|
else removeNode(vnode, context)
|
|
}
|
|
}
|
|
}
|
|
function removeNode(vnode, context) {
|
|
var expected = 1, called = 0
|
|
if (vnode.attrs && vnode.attrs.onbeforeremove) {
|
|
var result = vnode.attrs.onbeforeremove.call(vnode.state, vnode)
|
|
if (result != null && typeof result.then === "function") {
|
|
expected++
|
|
result.then(continuation, continuation)
|
|
}
|
|
}
|
|
if (typeof vnode.tag !== "string" && vnode.tag.onbeforeremove) {
|
|
var result = vnode.tag.onbeforeremove.call(vnode.state, vnode)
|
|
if (result != null && typeof result.then === "function") {
|
|
expected++
|
|
result.then(continuation, continuation)
|
|
}
|
|
}
|
|
continuation()
|
|
function continuation() {
|
|
if (++called === expected) {
|
|
onremove(vnode)
|
|
if (vnode.dom) {
|
|
var count = vnode.domSize || 1
|
|
if (count > 1) {
|
|
var dom = vnode.dom
|
|
while (--count) {
|
|
removeNodeFromDOM(dom.nextSibling)
|
|
}
|
|
}
|
|
removeNodeFromDOM(vnode.dom)
|
|
if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string") { //TODO test custom elements
|
|
if (!context.pool) context.pool = [vnode]
|
|
else context.pool.push(vnode)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function removeNodeFromDOM(node) {
|
|
var parent = node.parentNode
|
|
if (parent != null) parent.removeChild(node)
|
|
}
|
|
function onremove(vnode) {
|
|
if (vnode.attrs && vnode.attrs.onremove) vnode.attrs.onremove.call(vnode.state, vnode)
|
|
if (typeof vnode.tag !== "string" && vnode.tag.onremove) vnode.tag.onremove.call(vnode.state, vnode)
|
|
if (vnode.instance != null) onremove(vnode.instance)
|
|
else {
|
|
var children = vnode.children
|
|
if (Array.isArray(children)) {
|
|
for (var i = 0; i < children.length; i++) {
|
|
var child = children[i]
|
|
if (child != null) onremove(child)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//attrs
|
|
function setAttrs(vnode, attrs, ns) {
|
|
for (var key in attrs) {
|
|
setAttr(vnode, key, null, attrs[key], ns)
|
|
}
|
|
}
|
|
function setAttr(vnode, key, old, value, ns) {
|
|
var element = vnode.dom
|
|
if (key === "key" || key === "is" || (old === value && !isFormAttribute(vnode, key)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key)) return
|
|
var nsLastIndex = key.indexOf(":")
|
|
if (nsLastIndex > -1 && key.substr(0, nsLastIndex) === "xlink") {
|
|
element.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(nsLastIndex + 1), value)
|
|
}
|
|
else if (key[0] === "o" && key[1] === "n" && typeof value === "function") updateEvent(vnode, key, value)
|
|
else if (key === "style") updateStyle(element, old, value)
|
|
else if (key in element && !isAttribute(key) && ns === undefined && !isCustomElement(vnode)) {
|
|
//setting input[value] to same value by typing on focused element moves cursor to end in Chrome
|
|
if (vnode.tag === "input" && key === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
|
|
//setting select[value] to same value while having select open blinks select dropdown in Chrome
|
|
if (vnode.tag === "select" && key === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
|
|
//setting option[value] to same value while having select open blinks select dropdown in Chrome
|
|
if (vnode.tag === "option" && key === "value" && vnode.dom.value === value) return
|
|
element[key] = value
|
|
}
|
|
else {
|
|
if (typeof value === "boolean") {
|
|
if (value) element.setAttribute(key, "")
|
|
else element.removeAttribute(key)
|
|
}
|
|
else element.setAttribute(key === "className" ? "class" : key, value)
|
|
}
|
|
}
|
|
function setLateAttrs(vnode) {
|
|
var attrs = vnode.attrs
|
|
if (vnode.tag === "select" && attrs != null) {
|
|
if ("value" in attrs) setAttr(vnode, "value", null, attrs.value, undefined)
|
|
if ("selectedIndex" in attrs) setAttr(vnode, "selectedIndex", null, attrs.selectedIndex, undefined)
|
|
}
|
|
}
|
|
function updateAttrs(vnode, old, attrs, ns) {
|
|
if (attrs != null) {
|
|
for (var key in attrs) {
|
|
setAttr(vnode, key, old && old[key], attrs[key], ns)
|
|
}
|
|
}
|
|
if (old != null) {
|
|
for (var key in old) {
|
|
if (attrs == null || !(key in attrs)) {
|
|
if (key === "className") key = "class"
|
|
if (key[0] === "o" && key[1] === "n" && !isLifecycleMethod(key)) updateEvent(vnode, key, undefined)
|
|
else if (key !== "key") vnode.dom.removeAttribute(key)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function isFormAttribute(vnode, attr) {
|
|
return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === $doc.activeElement
|
|
}
|
|
function isLifecycleMethod(attr) {
|
|
return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate"
|
|
}
|
|
function isAttribute(attr) {
|
|
return attr === "href" || attr === "list" || attr === "form" || attr === "width" || attr === "height"// || attr === "type"
|
|
}
|
|
function isCustomElement(vnode){
|
|
return vnode.attrs.is || vnode.tag.indexOf("-") > -1
|
|
}
|
|
function hasIntegrationMethods(source) {
|
|
return source != null && (source.oncreate || source.onupdate || source.onbeforeremove || source.onremove)
|
|
}
|
|
|
|
//style
|
|
function updateStyle(element, old, style) {
|
|
if (old === style) element.style.cssText = "", old = null
|
|
if (style == null) element.style.cssText = ""
|
|
else if (typeof style === "string") element.style.cssText = style
|
|
else {
|
|
if (typeof old === "string") element.style.cssText = ""
|
|
for (var key in style) {
|
|
element.style[key] = style[key]
|
|
}
|
|
if (old != null && typeof old !== "string") {
|
|
for (var key in old) {
|
|
if (!(key in style)) element.style[key] = ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//event
|
|
function updateEvent(vnode, key, value) {
|
|
var element = vnode.dom
|
|
var callback = typeof onevent !== "function" ? value : function(e) {
|
|
var result = value.call(element, e)
|
|
onevent.call(element, e)
|
|
return result
|
|
}
|
|
if (key in element) element[key] = typeof value === "function" ? callback : null
|
|
else {
|
|
var eventName = key.slice(2)
|
|
if (vnode.events === undefined) vnode.events = {}
|
|
if (vnode.events[key] === callback) return
|
|
if (vnode.events[key] != null) element.removeEventListener(eventName, vnode.events[key], false)
|
|
if (typeof value === "function") {
|
|
vnode.events[key] = callback
|
|
element.addEventListener(eventName, vnode.events[key], false)
|
|
}
|
|
}
|
|
}
|
|
|
|
//lifecycle
|
|
function initLifecycle(source, vnode, hooks) {
|
|
if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode)
|
|
if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode))
|
|
}
|
|
function updateLifecycle(source, vnode, hooks, recycling) {
|
|
if (recycling) initLifecycle(source, vnode, hooks)
|
|
else if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode))
|
|
}
|
|
function shouldUpdate(vnode, old) {
|
|
var forceVnodeUpdate, forceComponentUpdate
|
|
if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") forceVnodeUpdate = vnode.attrs.onbeforeupdate.call(vnode.state, vnode, old)
|
|
if (typeof vnode.tag !== "string" && typeof vnode.tag.onbeforeupdate === "function") forceComponentUpdate = vnode.tag.onbeforeupdate.call(vnode.state, vnode, old)
|
|
if (!(forceVnodeUpdate === undefined && forceComponentUpdate === undefined) && !forceVnodeUpdate && !forceComponentUpdate) {
|
|
vnode.dom = old.dom
|
|
vnode.domSize = old.domSize
|
|
vnode.instance = old.instance
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
function render(dom, vnodes) {
|
|
if (!dom) throw new Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.")
|
|
var hooks = []
|
|
var active = $doc.activeElement
|
|
|
|
// First time rendering into a node clears it out
|
|
if (dom.vnodes == null) dom.textContent = ""
|
|
|
|
if (!Array.isArray(vnodes)) vnodes = [vnodes]
|
|
updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), hooks, null, undefined)
|
|
dom.vnodes = vnodes
|
|
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
|
if ($doc.activeElement !== active) active.focus()
|
|
}
|
|
|
|
return {render: render, setEventCallback: setEventCallback}
|
|
}
|