diff --git a/src/generators/nodes/EachBlock.ts b/src/generators/nodes/EachBlock.ts
index 0733793aac..882503c4b3 100644
--- a/src/generators/nodes/EachBlock.ts
+++ b/src/generators/nodes/EachBlock.ts
@@ -1,7 +1,6 @@
import deindent from '../../utils/deindent';
import Node from './shared/Node';
import ElseBlock from './ElseBlock';
-import { DomGenerator } from '../dom/index';
import Block from '../dom/Block';
import createDebuggingComment from '../../utils/createDebuggingComment';
@@ -254,6 +253,9 @@ export default class EachBlock extends Node {
const head = block.getUniqueName(`${each}_head`);
const last = block.getUniqueName(`${each}_last`);
const expected = block.getUniqueName(`${each}_expected`);
+ const keep = block.getUniqueName(`${each}_keep`);
+ const mounts = block.getUniqueName(`${each}_mounts`);
+ const next_iteration = block.getUniqueName(`${each}_next_iteration`);
block.addVariable(lookup, `@blankObject()`);
block.addVariable(head);
@@ -317,12 +319,11 @@ export default class EachBlock extends Node {
`);
const dynamic = this.block.hasUpdateMethod;
-
- let destroy;
+ let fn_destroy;
if (this.block.hasOutroMethod) {
- const fn = block.getUniqueName(`${each}_outro`);
+ fn_destroy = block.getUniqueName(`${each}_outro`);
block.builders.init.addBlock(deindent`
- function ${fn}(iteration) {
+ function ${fn_destroy}(iteration) {
iteration.o(function() {
iteration.u();
iteration.d();
@@ -330,62 +331,43 @@ export default class EachBlock extends Node {
});
}
`);
-
- destroy = deindent`
- while (${expected}) {
- ${fn}(${expected});
- ${expected} = ${expected}.next;
- }
-
- for (#i = 0; #i < discard_pile.length; #i += 1) {
- if (discard_pile[#i].discard) {
- ${fn}(discard_pile[#i]);
- }
- }
- `;
} else {
- const fn = block.getUniqueName(`${each}_destroy`);
+ fn_destroy = block.getUniqueName(`${each}_destroy`);
block.builders.init.addBlock(deindent`
- function ${fn}(iteration) {
- iteration.u();
+ function ${fn_destroy}(iteration) {
+ var first = iteration.first
+ if (first && first.parentNode) {
+ iteration.u();
+ }
iteration.d();
${lookup}[iteration.key] = null;
}
`);
-
- destroy = deindent`
- while (${expected}) {
- ${fn}(${expected});
- ${expected} = ${expected}.next;
- }
-
- for (#i = 0; #i < discard_pile.length; #i += 1) {
- var ${iteration} = discard_pile[#i];
- if (${iteration}.discard) {
- ${fn}(${iteration});
- }
- }
- `;
}
+ const destroy = deindent`
+ ${iteration} = ${head};
+ while(${iteration}) {
+ if (!${keep}[${iteration}.key]) {
+ ${fn_destroy}(${iteration});
+ }
+ ${iteration} = ${iteration}.next;
+ }
+ `;
block.builders.update.addBlock(deindent`
var ${each_block_value} = ${snippet};
-
var ${expected} = ${head};
var ${last} = null;
- var rendered = {};
- var all = {};
-
- var each_all = ${head};
- while(each_all) {
- all[each_all.key] = each_all;
- each_all = each_all.next;
- }
+ var ${keep} = {};
+ var ${mounts} = {};
+ var ${next_iteration} = null;
for (#i = 0; #i < ${each_block_value}.${length}; #i += 1) {
var ${key} = ${each_block_value}[#i].${this.key};
var ${iteration} = ${lookup}[${key}];
+ var next_data = ${each_block_value}[#i+1];
+ var next = next_data && ${lookup}[next_data.${this.key}];
var ${this.each_context} = @assign({}, state, {
${this.contextProps.join(',\n')}
@@ -393,66 +375,52 @@ export default class EachBlock extends Node {
${dynamic &&
`if (${iteration}) ${iteration}.p(changed, ${this.each_context});`}
-
- if (${expected}) {
- if (${key} === ${expected}.key) {
- ${expected} = ${expected}.next;
- } else {
- if (${iteration}) {
-
- var next_data = ${each_block_value}[#i+1];
- var next = next_data && ${lookup}[next_data.id];
- var first = ${iteration}.first;
- var first_next = next && next.first;
- ///insertNode(first, tbody, first_next);
- ${updateMountNode}.insertBefore(first, first_next);
- ${expected} = next;
- ${iteration}.next = ${expected};
- var prev_data = ${each_block_value}[#i-1];
- var prev = prev_data && ${lookup}[prev_data.id];
- if (prev) {
- prev.next = ${iteration};
- }
-
- } else {
- // key is being inserted
- ${iteration} = ${lookup}[${key}] = ${create_each_block}(#component, ${key}, ${this.each_context});
- ${iteration}.c();
- ${iteration}.${mountOrIntro}(${updateMountNode}, ${expected}.first);
-
- ${expected}.last = ${iteration};
- ${iteration}.next = ${expected};
- }
- }
+ if (${expected} && (${key} === ${expected}.key)) {
+ var first = ${iteration} && ${iteration}.first;
+ var parentNode = first && first.parentNode
+ if (!parentNode || (${iteration} && ${iteration}.next) != next) ${mounts}[${key}] = ${iteration};
+ ${expected} = ${iteration}.next;
+ } else if (${iteration}) {
+ ${mounts}[${key}] = ${iteration};
+ ${expected} = ${iteration}.next;
} else {
- // we're appending from this point forward
- if (${iteration}) {
- ${iteration}.next = null;
- ${iteration}.m(${updateMountNode}, ${anchor});
- } else {
- ${iteration} = ${lookup}[${key}] = ${create_each_block}(#component, ${key}, ${this.each_context});
- ${iteration}.c();
- ${iteration}.${mountOrIntro}(${updateMountNode}, ${anchor});
- }
+ // key is being inserted
+ ${iteration} = ${lookup}[${key}] = ${create_each_block}(#component, ${key}, ${this.each_context});
+ ${iteration}.c();
+ ${mounts}[${key}] = ${iteration};
}
- if (${iteration}) {
- rendered[${iteration}.key] = ${iteration};
- }
-
- if (${last}) ${last}.next = ${iteration};
- ${iteration}.last = ${last};
- ${this.block.hasIntroMethod && `${iteration}.i(${updateMountNode}, ${anchor});`}
+ ${lookup}[${key}] = ${iteration};
+ ${keep}[${iteration}.key] = ${iteration};
${last} = ${iteration};
}
+ ${destroy}
- if (${last}) ${last}.next = null;
-
- for (var key_all in all) {
- if (!rendered[key_all]) all[key_all].d();
- }
-
-
-
+ // Work backwards due to DOM api having insertBefore
+ for (#i = ${each_block_value}.${length} - 1; #i >= 0; #i -= 1) {
+ var data = ${each_block_value}[#i];
+ var ${key} = data.${this.key};
+ ${iteration} = ${lookup}[${key}];
+ if (${mounts}[${key}]) {
+ var anchor;
+ ${this.block.hasOutroMethod
+ ? deindent`
+ var key_next_iteration = ${next_iteration} && ${next_iteration}.key;
+ var iteration_anchor = ${iteration}.next;
+ var key_anchor;
+ do {
+ anchor = iteration_anchor && iteration_anchor.first;
+ iteration_anchor = iteration_anchor && iteration_anchor.next;
+ key_anchor = iteration_anchor && iteration_anchor.key;
+ } while(iteration_anchor && key_anchor != key_next_iteration && !${keep}[key_anchor])`
+ : deindent`
+ anchor = ${next_iteration} && ${next_iteration}.first;
+ ` }
+ ${mounts}[${key}].${mountOrIntro}(${updateMountNode}, anchor);
+ }
+ ${iteration}.next = ${next_iteration};
+ if (${next_iteration}) ${next_iteration}.last = ${iteration};
+ ${next_iteration} = ${iteration};
+ }
${head} = ${lookup}[${each_block_value}[0] && ${each_block_value}[0].${this.key}];
`);
diff --git a/src/shared/transitions.js b/src/shared/transitions.js
index bef36e741a..c2ce1c6531 100644
--- a/src/shared/transitions.js
+++ b/src/shared/transitions.js
@@ -1,4 +1,3 @@
-import { assign, noop } from './utils.js';
import { createElement } from './dom.js';
export function linear(t) {
diff --git a/test/helpers.js b/test/helpers.js
index caf78894ce..4a65b995b7 100644
--- a/test/helpers.js
+++ b/test/helpers.js
@@ -1,3 +1,4 @@
+import jsdom from 'jsdom';
import { JSDOM } from 'jsdom';
import assert from 'assert';
import glob from 'glob';
@@ -45,7 +46,8 @@ export function tryToReadFile(file) {
}
}
-const { window } = new JSDOM('');
+export const virtualConsole = new jsdom.VirtualConsole();
+const { window } = new JSDOM('', {virtualConsole});
global.document = window.document;
export function env() {
diff --git a/test/runtime/samples/each-block-keyed-random-permute/_config.js b/test/runtime/samples/each-block-keyed-random-permute/_config.js
index c14c4cd0f7..904208c6a9 100644
--- a/test/runtime/samples/each-block-keyed-random-permute/_config.js
+++ b/test/runtime/samples/each-block-keyed-random-permute/_config.js
@@ -41,6 +41,8 @@ export default {
test( 'g' );
test( '' );
test( 'abc' );
+ test( 'duqbmineapjhtlofrskcg' );
+ test( 'hdnkjougmrvftewsqpailcb' );
// then, we party
for ( let i = 0; i < 100; i += 1 ) test( permute() );