Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions cjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
* PERFORMANCE OF THIS SOFTWARE.
*/

const moveBefore = (parentNode, isConnected, node, before) => {
if (isConnected === node.isConnected)
parentNode.moveBefore(node, before);
else
parentNode.insertBefore(node, before);
};

/**
* @param {Node} parentNode The container where children live
* @param {Node[]} a The list of current/live children
Expand All @@ -27,6 +34,7 @@
* @returns {Node[]} The same list of future children.
*/
module.exports = (parentNode, a, b, get, before) => {
const { isConnected } = parentNode;
const bLength = b.length;
let aEnd = a.length;
let bEnd = bLength;
Expand All @@ -46,7 +54,7 @@ module.exports = (parentNode, a, b, get, before) => {
get(b[bEnd], 0)) :
before;
while (bStart < bEnd)
parentNode.insertBefore(get(b[bStart++], 1), node);
moveBefore(parentNode, isConnected, get(b[bStart++], 1), node);
}
// remove head or tail: fast path
else if (bEnd === bStart) {
Expand Down Expand Up @@ -81,11 +89,9 @@ module.exports = (parentNode, a, b, get, before) => {
// [1, 2, 3, 4, 5]
// [1, 2, 3, 5, 6, 4]
const node = get(a[--aEnd], -1).nextSibling;
parentNode.insertBefore(
get(b[bStart++], 1),
get(a[aStart++], -1).nextSibling
);
parentNode.insertBefore(get(b[--bEnd], 1), node);
moveBefore(parentNode, isConnected, get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
moveBefore(parentNode, isConnected, get(b[--bEnd], 1), node);

// mark the future index as identical (yeah, it's dirty, but cheap 👍)
// The main reason to do this, is that when a[aEnd] will be reached,
// the loop will likely be on the fast path, as identical to b[bEnd].
Expand Down Expand Up @@ -131,15 +137,19 @@ module.exports = (parentNode, a, b, get, before) => {
if (sequence > (index - bStart)) {
const node = get(a[aStart], 0);
while (bStart < index)
parentNode.insertBefore(get(b[bStart++], 1), node);
moveBefore(parentNode, isConnected, get(b[bStart++], 1), node);
}
// if the effort wasn't good enough, fallback to a replace,
// moving both source and target indexes forward, hoping that some
// similar node will be found later on, to go back to the fast path
else {
parentNode.replaceChild(
// TODO: this was a replaceChild but it's not clear if fragments
// work this way ... -1 seems also not appropriate
moveBefore(
parentNode,
isConnected,
get(b[bStart++], 1),
get(a[aStart++], -1)
get(a[aStart++], 0)
);
}
}
Expand Down
30 changes: 20 additions & 10 deletions esm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
* PERFORMANCE OF THIS SOFTWARE.
*/

const moveBefore = (parentNode, isConnected, node, before) => {
if (isConnected === node.isConnected)
parentNode.moveBefore(node, before);
else
parentNode.insertBefore(node, before);
};

/**
* @param {Node} parentNode The container where children live
* @param {Node[]} a The list of current/live children
Expand All @@ -26,6 +33,7 @@
* @returns {Node[]} The same list of future children.
*/
export default (parentNode, a, b, get, before) => {
const { isConnected } = parentNode;
const bLength = b.length;
let aEnd = a.length;
let bEnd = bLength;
Expand All @@ -45,7 +53,7 @@ export default (parentNode, a, b, get, before) => {
get(b[bEnd], 0)) :
before;
while (bStart < bEnd)
parentNode.insertBefore(get(b[bStart++], 1), node);
moveBefore(parentNode, isConnected, get(b[bStart++], 1), node);
}
// remove head or tail: fast path
else if (bEnd === bStart) {
Expand Down Expand Up @@ -79,12 +87,10 @@ export default (parentNode, a, b, get, before) => {
// or asymmetric too
// [1, 2, 3, 4, 5]
// [1, 2, 3, 5, 6, 4]
const node = get(a[--aEnd], -1).nextSibling;
parentNode.insertBefore(
get(b[bStart++], 1),
get(a[aStart++], -1).nextSibling
);
parentNode.insertBefore(get(b[--bEnd], 1), node);
const node = get(a[--aEnd], -0).nextSibling;
moveBefore(parentNode, isConnected, get(b[bStart++], 1), get(a[aStart++], -0).nextSibling);
moveBefore(parentNode, isConnected, get(b[--bEnd], 1), node);

// mark the future index as identical (yeah, it's dirty, but cheap 👍)
// The main reason to do this, is that when a[aEnd] will be reached,
// the loop will likely be on the fast path, as identical to b[bEnd].
Expand Down Expand Up @@ -130,15 +136,19 @@ export default (parentNode, a, b, get, before) => {
if (sequence > (index - bStart)) {
const node = get(a[aStart], 0);
while (bStart < index)
parentNode.insertBefore(get(b[bStart++], 1), node);
moveBefore(parentNode, isConnected, get(b[bStart++], 1), node);
}
// if the effort wasn't good enough, fallback to a replace,
// moving both source and target indexes forward, hoping that some
// similar node will be found later on, to go back to the fast path
else {
parentNode.replaceChild(
// TODO: this was a replaceChild but it's not clear if fragments
// work this way ... -1 seems also not appropriate
moveBefore(
parentNode,
isConnected,
get(b[bStart++], 1),
get(a[aStart++], -1)
get(a[aStart++], 0)
);
}
}
Expand Down
18 changes: 13 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ var udomdiff = (function (exports) {
* PERFORMANCE OF THIS SOFTWARE.
*/

var moveBefore = function moveBefore(parentNode, isConnected, node, before) {
if (isConnected === node.isConnected) parentNode.moveBefore(node, before);else parentNode.insertBefore(node, before);
};

/**
* @param {Node} parentNode The container where children live
* @param {Node[]} a The list of current/live children
Expand All @@ -29,6 +33,7 @@ var udomdiff = (function (exports) {
* @returns {Node[]} The same list of future children.
*/
var index = (function (parentNode, a, b, get, before) {
var isConnected = parentNode.isConnected;
var bLength = b.length;
var aEnd = a.length;
var bEnd = bLength;
Expand All @@ -43,7 +48,7 @@ var udomdiff = (function (exports) {
// the node to `insertBefore`, if the index is more than 0
// must be retrieved, otherwise it's gonna be the first item.
var node = bEnd < bLength ? bStart ? get(b[bStart - 1], -0).nextSibling : get(b[bEnd], 0) : before;
while (bStart < bEnd) parentNode.insertBefore(get(b[bStart++], 1), node);
while (bStart < bEnd) moveBefore(parentNode, isConnected, get(b[bStart++], 1), node);
}
// remove head or tail: fast path
else if (bEnd === bStart) {
Expand Down Expand Up @@ -74,8 +79,9 @@ var udomdiff = (function (exports) {
// [1, 2, 3, 4, 5]
// [1, 2, 3, 5, 6, 4]
var _node = get(a[--aEnd], -1).nextSibling;
parentNode.insertBefore(get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
parentNode.insertBefore(get(b[--bEnd], 1), _node);
moveBefore(parentNode, isConnected, get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
moveBefore(parentNode, isConnected, get(b[--bEnd], 1), _node);

// mark the future index as identical (yeah, it's dirty, but cheap 👍)
// The main reason to do this, is that when a[aEnd] will be reached,
// the loop will likely be on the fast path, as identical to b[bEnd].
Expand Down Expand Up @@ -118,13 +124,15 @@ var udomdiff = (function (exports) {
// will be processed at zero cost
if (sequence > index - bStart) {
var _node2 = get(a[aStart], 0);
while (bStart < index) parentNode.insertBefore(get(b[bStart++], 1), _node2);
while (bStart < index) moveBefore(parentNode, isConnected, get(b[bStart++], 1), _node2);
}
// if the effort wasn't good enough, fallback to a replace,
// moving both source and target indexes forward, hoping that some
// similar node will be found later on, to go back to the fast path
else {
parentNode.replaceChild(get(b[bStart++], 1), get(a[aStart++], -1));
// TODO: this was a replaceChild but it's not clear if fragments
// work this way ... -1 seems also not appropriate
moveBefore(parentNode, isConnected, get(b[bStart++], 1), get(a[aStart++], 0));
}
}
// otherwise move the source forward, 'cause there's nothing to do
Expand Down
2 changes: 1 addition & 1 deletion min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion new.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions test/dommy.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const remove = node => {
};

class Siblings {
get isConnected() { return true }
get nextSibling() {
const {parentNode} = this;
if (parentNode) {
Expand Down Expand Up @@ -87,6 +88,9 @@ class Dommy extends Siblings {
newNode.parentNode = this;
return newNode;
}
moveBefore(newNode, oldNode) {
this.insertBefore(newNode, oldNode);
}
insertBefore(newNode, oldNode) {
if (newNode !== oldNode) {
remove(newNode);
Expand Down
1 change: 1 addition & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<script src="../index.js"></script>
<script src="test.js"></script>
<script>
Node.prototype.moveBefore = Node.prototype.insertBefore;
window.onload = test;
</script>
</head>
Expand Down
6 changes: 5 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
global.document = {
createElement: function (tagName) {
return {tagName: tagName, value: '\n'};
return {tagName: tagName, value: '\n', isConnected: true};
},
createTextNode: function (value) {
return Object.defineProperty(
Expand All @@ -14,6 +14,7 @@ global.document = {
},
importNode: function () {},
body: {
isConnected: true,
get lastElementChild() {
return this.childNodes[this.childNodes.length - 1];
},
Expand All @@ -26,6 +27,9 @@ global.document = {
node.parentNode = this;
},
childNodes: [],
moveBefore: function (before, after) {
this.insertBefore(before, after);
},
insertBefore: function (before, after) {
if (before !== after) {
this.removeChild(before);
Expand Down