diff --git a/mithril.js b/mithril.js index ee63e707..da004c57 100644 --- a/mithril.js +++ b/mithril.js @@ -328,10 +328,13 @@ var renderService = function($window) { if (vnode.instance != null) { var element = createNode(vnode.instance, hooks, ns) vnode.dom = vnode.instance.dom - vnode.domSize = vnode.instance.domSize + vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 return element } - else return $emptyFragment + else { + vnode.domSize = 0 + return $emptyFragment + } } //update function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) { @@ -341,12 +344,12 @@ var renderService = function($window) { else { 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) oldStart++, start++ - else if (o != null && v != null && o.key === v.key) { + else if (o != null && v != null && o.key === v.key && o.tag === v.tag) { oldStart++, start++ updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) @@ -354,7 +357,7 @@ var renderService = function($window) { else { var o = old[oldEnd] if (o === v) oldEnd--, start++ - else if (o != null && v != null && o.key === v.key) { + else if (o != null && v != null && o.key === v.key && o.tag === v.tag) { updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling)) oldEnd--, start++ @@ -365,10 +368,10 @@ var renderService = function($window) { while (oldEnd >= oldStart && end >= start) { var o = old[oldEnd], v = vnodes[end] if (o === v) oldEnd--, end-- - else if (o != null && v != null && o.key === v.key) { + else if (o != null && v != null && o.key === v.key && o.tag === v.tag) { updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) - nextSibling = o.dom + if (o.dom != null) nextSibling = o.dom oldEnd--, end-- } else { @@ -380,7 +383,7 @@ var renderService = function($window) { updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) insertNode(parent, toFragment(movable), nextSibling) old[oldIndex].skip = true - nextSibling = movable.dom + if (movable.dom != null) nextSibling = movable.dom } else { var dom = createNode(v, hooks, undefined) @@ -440,7 +443,7 @@ var renderService = function($window) { if (children != null) { for (var i = 0; i < children.length; i++) { var child = children[i] - if (child != null) { + if (child != null && child.dom != null) { if (vnode.dom == null) vnode.dom = child.dom domSize += child.domSize || 1 } @@ -479,7 +482,12 @@ var renderService = function($window) { } else if (old.instance != null) { removeNode(parent, old.instance, null, false) - vnode.dom = vnode.domSize = undefined + vnode.dom = undefined + vnode.domSize = 0 + } + else { + vnode.dom = old.dom + vnode.domSize = old.domSize } } function isRecyclable(old, vnodes) { @@ -506,7 +514,7 @@ var renderService = function($window) { } function toFragment(vnode) { var count = vnode.domSize - if (count != null) { + if (count != null || vnode.dom == null) { var fragment = $doc.createDocumentFragment() if (count > 0) { var dom = vnode.dom @@ -519,7 +527,7 @@ var renderService = function($window) { } function getNextSibling(vnodes, i, nextSibling) { for (; i < vnodes.length; i++) { - if (vnodes[i] != null) return vnodes[i].dom + if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom } return nextSibling } @@ -699,12 +707,12 @@ var renderService = function($window) { function copy(data) { if (data instanceof Array) { var output = [] - for (var i = 0; i < data.length; i++) output[i] = copy(data[i]) + for (var i = 0; i < data.length; i++) output[i] = data[i] return output } else if (typeof data === "object") { var output = {} - for (var i in data) output[i] = copy(data[i]) + for (var i in data) output[i] = data[i] return output } return data diff --git a/render/render.js b/render/render.js index 52abd954..62227b17 100644 --- a/render/render.js +++ b/render/render.js @@ -98,10 +98,13 @@ module.exports = function($window) { if (vnode.instance != null) { var element = createNode(vnode.instance, hooks, ns) vnode.dom = vnode.instance.dom - vnode.domSize = vnode.instance.domSize + vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0 return element } - else return $emptyFragment + else { + vnode.domSize = 0 + return $emptyFragment + } } //update @@ -112,12 +115,12 @@ module.exports = function($window) { else { 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) oldStart++, start++ - else if (o != null && v != null && o.key === v.key) { + else if (o != null && v != null && o.key === v.key && o.tag === v.tag) { oldStart++, start++ updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) @@ -125,7 +128,7 @@ module.exports = function($window) { else { var o = old[oldEnd] if (o === v) oldEnd--, start++ - else if (o != null && v != null && o.key === v.key) { + else if (o != null && v != null && o.key === v.key && o.tag === v.tag) { updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling)) oldEnd--, start++ @@ -136,10 +139,10 @@ module.exports = function($window) { while (oldEnd >= oldStart && end >= start) { var o = old[oldEnd], v = vnodes[end] if (o === v) oldEnd--, end-- - else if (o != null && v != null && o.key === v.key) { + else if (o != null && v != null && o.key === v.key && o.tag === v.tag) { updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling) - nextSibling = o.dom + if (o.dom != null) nextSibling = o.dom oldEnd--, end-- } else { @@ -151,7 +154,7 @@ module.exports = function($window) { updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns) insertNode(parent, toFragment(movable), nextSibling) old[oldIndex].skip = true - nextSibling = movable.dom + if (movable.dom != null) nextSibling = movable.dom } else { var dom = createNode(v, hooks, undefined) @@ -211,7 +214,7 @@ module.exports = function($window) { if (children != null) { for (var i = 0; i < children.length; i++) { var child = children[i] - if (child != null) { + if (child != null && child.dom != null) { if (vnode.dom == null) vnode.dom = child.dom domSize += child.domSize || 1 } @@ -250,7 +253,12 @@ module.exports = function($window) { } else if (old.instance != null) { removeNode(parent, old.instance, null, false) - vnode.dom = vnode.domSize = undefined + vnode.dom = undefined + vnode.domSize = 0 + } + else { + vnode.dom = old.dom + vnode.domSize = old.domSize } } function isRecyclable(old, vnodes) { @@ -277,7 +285,7 @@ module.exports = function($window) { } function toFragment(vnode) { var count = vnode.domSize - if (count != null) { + if (count != null || vnode.dom == null) { var fragment = $doc.createDocumentFragment() if (count > 0) { var dom = vnode.dom @@ -290,7 +298,7 @@ module.exports = function($window) { } function getNextSibling(vnodes, i, nextSibling) { for (; i < vnodes.length; i++) { - if (vnodes[i] != null) return vnodes[i].dom + if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom } return nextSibling } @@ -479,12 +487,12 @@ module.exports = function($window) { function copy(data) { if (data instanceof Array) { var output = [] - for (var i = 0; i < data.length; i++) output[i] = copy(data[i]) + for (var i = 0; i < data.length; i++) output[i] = data[i] return output } else if (typeof data === "object") { var output = {} - for (var i in data) output[i] = copy(data[i]) + for (var i in data) output[i] = data[i] return output } return data diff --git a/render/tests/test-component.js b/render/tests/test-component.js index 828585c7..c6078469 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -640,10 +640,11 @@ o.spec("component", function() { }) }) o.spec("state", function() { - o("deep copies state", function() { + o("copies state", function() { var called = 0 + var data = {a: 1} var component = { - data: [{a: 1}], + data: data, oninit: init, view: function() { return "" @@ -653,7 +654,27 @@ o.spec("component", function() { render(root, [{tag: component}]) function init(vnode) { - o(vnode.state.data).deepEquals([{a: 1}]) + o(vnode.state.data).deepEquals(data) + o(vnode.state.data).equals(data) + } + }) + o("state copy is shallow", function() { + var called = 0 + var body = {a: 1} + var data = [body] + var component = { + data: data, + oninit: init, + view: function() { + return "" + } + } + + render(root, [{tag: component}]) + + function init(vnode) { + o(vnode.state.data).equals(data) + o(vnode.state.data[0]).equals(body) } }) }) diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index 29ea9fe9..598f0910 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -813,4 +813,36 @@ o.spec("updateNodes", function() { o(root.childNodes[0].nodeName).equals("A") o(root.childNodes[1].nodeName).equals("B") }) + o("fragment child toggles from null when followed by null component then tag", function() { + var component = {view: function() {return null}} + var vnodes = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}] + var temp = [{tag: "[", children: [null, {tag: component}, {tag: "b"}]}] + var updated = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}] + + render(root, vnodes) + render(root, temp) + render(root, updated) + + o(root.childNodes.length).equals(2) + o(root.childNodes[0].nodeName).equals("A") + o(root.childNodes[1].nodeName).equals("B") + }) + o("fragment child toggles from null in component when followed by null component then tag", function() { + var flag = true + var a = {view: function() {return flag ? {tag: "a"} : null}} + var b = {view: function() {return null}} + var vnodes = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}] + var temp = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}] + var updated = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}] + + render(root, vnodes) + flag = false + render(root, temp) + flag = true + render(root, updated) + + o(root.childNodes.length).equals(2) + o(root.childNodes[0].nodeName).equals("A") + o(root.childNodes[1].nodeName).equals("S") + }) })