forked from choojs/nanomorph
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
93 lines (84 loc) · 2.52 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
var assert = require('assert')
var morph = require('./lib/morph')
var rootLabelRegex = /^data-onloadid/
var ELEMENT_NODE = 1
module.exports = nanomorph
// morph one tree into another tree
// (obj, obj) -> obj
// no parent
// -> same: diff and walk children
// -> not same: replace and return
// old node doesn't exist
// -> insert new node
// new node doesn't exist
// -> delete old node
// nodes are not the same
// -> diff nodes and apply patch to old node
// nodes are the same
// -> walk all child nodes and append to old node
function nanomorph (oldTree, newTree) {
assert.equal(typeof oldTree, 'object', 'nanomorph: oldTree should be an object')
assert.equal(typeof newTree, 'object', 'nanomorph: newTree should be an object')
persistStatefulRoot(newTree, oldTree)
var tree = walk(newTree, oldTree)
return tree
}
// walk and morph a dom tree
// (obj, obj) -> obj
function walk (newNode, oldNode) {
if (!oldNode) {
return newNode
} else if (!newNode) {
return null
} else if (newNode.isSameNode && newNode.isSameNode(oldNode)) {
return oldNode
} else if (newNode.tagName !== oldNode.tagName) {
return newNode
} else {
morph(newNode, oldNode)
updateChildren(newNode, oldNode)
return oldNode
}
}
// update the children of elements
// (obj, obj) -> null
function updateChildren (newNode, oldNode) {
if (!newNode.childNodes || !oldNode.childNodes) return
var newLength = newNode.childNodes.length
var oldLength = oldNode.childNodes.length
var length = Math.max(oldLength, newLength)
var iNew = 0
var iOld = 0
for (var i = 0; i < length; i++, iNew++, iOld++) {
var newChildNode = newNode.childNodes[iNew]
var oldChildNode = oldNode.childNodes[iOld]
var retChildNode = walk(newChildNode, oldChildNode)
if (!retChildNode) {
if (oldChildNode) {
oldNode.removeChild(oldChildNode)
iOld--
}
} else if (!oldChildNode) {
if (retChildNode) {
oldNode.appendChild(retChildNode)
iNew--
}
} else if (retChildNode !== oldChildNode) {
oldNode.replaceChild(retChildNode, oldChildNode)
iNew--
}
}
}
function persistStatefulRoot (newNode, oldNode) {
if (!newNode || !oldNode || oldNode.nodeType !== ELEMENT_NODE || newNode.nodeType !== ELEMENT_NODE) return
var oldAttrs = oldNode.attributes
var attr, name
for (var i = 0, len = oldAttrs.length; i < len; i++) {
attr = oldAttrs[i]
name = attr.name
if (rootLabelRegex.test(name)) {
newNode.setAttribute(name, attr.value)
break
}
}
}