From 4d04ce034dc10f9a39d3db9024b238098e364226 Mon Sep 17 00:00:00 2001 From: Leo Horie Date: Tue, 3 Jun 2014 22:42:14 -0400 Subject: [PATCH] fix diff on nested arrays --- docs/change-log.md | 2 ++ mithril.js | 18 ++++++++----- tests/mithril-tests.js | 58 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/docs/change-log.md b/docs/change-log.md index 72ff3699..f4256410 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -5,6 +5,8 @@ ### Bug Fixes: - prevent route change when only hash changes in non-hash mode [#107](https://github.com/lhorie/mithril.js/issues/107) +- fix null reference exception with Browserify [#110](https://github.com/lhorie/mithril.js/issues/110) +- fix nested array removal edge cases [#120](https://github.com/lhorie/mithril.js/issues/120) --- diff --git a/mithril.js b/mithril.js index f5631848..b59438c2 100644 --- a/mithril.js +++ b/mithril.js @@ -32,13 +32,19 @@ Mithril = m = new function app(window) { } return cell } - function build(parentElement, parentTag, data, cached, shouldReattach, index, editable, namespace) { + function build(parentElement, parentTag, parentCache, parentIndex, data, cached, shouldReattach, index, editable, namespace) { if (data === null || data === undefined) data = "" if (data.subtree === "retain") return var cachedType = type.call(cached), dataType = type.call(data) if (cachedType != dataType) { - if (cached !== null && cached !== undefined) clear(cached.nodes) + if (cached !== null && cached !== undefined) { + if (parentCache && parentCache.nodes) { + var offset = index - parentIndex + clear(parentCache.nodes.slice(offset, offset + (dataType == "[object Array]" ? data : cached.nodes).length)) + } + else clear(cached.nodes) + } cached = new data.constructor cached.nodes = [] } @@ -46,7 +52,7 @@ Mithril = m = new function app(window) { if (dataType == "[object Array]") { var nodes = [], intact = cached.length === data.length, subArrayCount = 0 for (var i = 0, cacheCount = 0; i < data.length; i++) { - var item = build(parentElement, null, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace) + var item = build(parentElement, null, cached, index, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace) if (item === undefined) continue if (!item.nodes.intact) intact = false subArrayCount += item instanceof Array ? item.length : 1 @@ -78,7 +84,7 @@ Mithril = m = new function app(window) { cached = { tag: data.tag, attrs: setAttributes(node, data.tag, data.attrs, {}, namespace), - children: data.children !== undefined ? build(node, data.tag, data.children, cached.children, true, 0, data.attrs.contenteditable ? node : editable, namespace) : undefined, + children: data.children !== undefined ? build(node, data.tag, undefined, undefined, data.children, cached.children, true, 0, data.attrs.contenteditable ? node : editable, namespace) : undefined, nodes: [node] } parentElement.insertBefore(node, parentElement.childNodes[index] || null) @@ -86,7 +92,7 @@ Mithril = m = new function app(window) { else { node = cached.nodes[0] setAttributes(node, data.tag, data.attrs, cached.attrs, namespace) - cached.children = build(node, data.tag, data.children, cached.children, false, 0, data.attrs.contenteditable ? node : editable, namespace) + cached.children = build(node, data.tag, undefined, undefined, data.children, cached.children, false, 0, data.attrs.contenteditable ? node : editable, namespace) cached.nodes.intact = true if (shouldReattach === true) parentElement.insertBefore(node, parentElement.childNodes[index] || null) } @@ -217,7 +223,7 @@ Mithril = m = new function app(window) { var index = nodeCache.indexOf(root) var id = index < 0 ? nodeCache.push(root) - 1 : index var node = root == window.document || root == window.document.documentElement ? documentNode : root - cellCache[id] = build(node, null, cell, cellCache[id], false, 0, null, undefined) + cellCache[id] = build(node, null, undefined, undefined, cell, cellCache[id], false, 0, null, undefined) } m.trust = function(value) { diff --git a/tests/mithril-tests.js b/tests/mithril-tests.js index 288d8d80..f76b2264 100644 --- a/tests/mithril-tests.js +++ b/tests/mithril-tests.js @@ -134,7 +134,7 @@ function testMithril(mock) { var root = mock.document.createElement("div") m.render(root, m("ul", [m("li"), m("li")])) m.render(root, m("ul", [m("li"), undefined])) - return root.childNodes[0].childNodes[1].nodeValue === "" + return root.childNodes[0].childNodes.length == 2 && root.childNodes[0].childNodes[1].nodeValue === "" }) test(function() { var root = mock.document.createElement("div") @@ -398,6 +398,62 @@ function testMithril(mock) { m.render(root, m("div", [m("a")])) return root.childNodes[0].childNodes.length == 1 && root.childNodes[0].childNodes[0].nodeName == "A" }) + test(function() { + //https://github.com/lhorie/mithril.js/issues/120 + var root = mock.document.createElement("div") + m.render(root, m("div", ["a", "b", "c", "d"])) + m.render(root, m("div", [["d", "e"]])) + var children = root.childNodes[0].childNodes + return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" + }) + test(function() { + //https://github.com/lhorie/mithril.js/issues/120 + var root = mock.document.createElement("div") + m.render(root, m("div", [["a", "b", "c", "d"]])) + m.render(root, m("div", ["d", "e"])) + var children = root.childNodes[0].childNodes + return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" + }) + test(function() { + //https://github.com/lhorie/mithril.js/issues/120 + var root = mock.document.createElement("div") + m.render(root, m("div", ["x", [["a"], "b", "c", "d"]])) + m.render(root, m("div", ["d", ["e"]])) + var children = root.childNodes[0].childNodes + return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" + }) + test(function() { + //https://github.com/lhorie/mithril.js/issues/120 + var root = mock.document.createElement("div") + m.render(root, m("div", ["b"])) + m.render(root, m("div", [["e"]])) + var children = root.childNodes[0].childNodes + return children.length == 1 && children[0].nodeValue == "e" + }) + test(function() { + //https://github.com/lhorie/mithril.js/issues/120 + var root = mock.document.createElement("div") + m.render(root, m("div", ["a", ["b"]])) + m.render(root, m("div", ["d", [["e"]]])) + var children = root.childNodes[0].childNodes + return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" + }) + test(function() { + //https://github.com/lhorie/mithril.js/issues/120 + var root = mock.document.createElement("div") + m.render(root, m("div", ["a", [["b"]]])) + m.render(root, m("div", ["d", ["e"]])) + var children = root.childNodes[0].childNodes + return children.length == 2 && children[0].nodeValue == "d" && children[1].nodeValue == "e" + }) + test(function() { + //https://github.com/lhorie/mithril.js/issues/120 + var root = mock.document.createElement("div") + m.render(root, m("div", ["a", [["b"], "c"]])) + m.render(root, m("div", ["d", [[["e"]], "x"]])) + var children = root.childNodes[0].childNodes + return children.length == 3 && children[0].nodeValue == "d" && children[1].nodeValue == "e" + }) //end m.render //m.redraw