diff --git a/src/shared/keyed-each.js b/src/shared/keyed-each.js index fd9ad18c59..d1999d4672 100644 --- a/src/shared/keyed-each.js +++ b/src/shared/keyed-each.js @@ -1,92 +1,78 @@ import { assign } from './utils.js'; -export function updateKeyedEach(component, key, changed, block_key, dynamic, each_block_value, head, lookup, updateMountNode, hasOutroMethod, create_each_block, get_context) { - var last = null; - var expected = head; +export function destroyIteration(iteration, lookup) { + var first = iteration.first + if (first && first.parentNode) { + iteration.u(); + } + iteration.d(); + lookup[iteration.key] = null; +} + +export function outroAndDestroyIteration(iteration, lookup) { + iteration.o(function() { + iteration.u(); + iteration.d(); + lookup[iteration.key] = null; + }); +} +export function updateKeyedEach(component, key, changed, key_prop, dynamic, list, head, lookup, updateMountNode, hasOutroMethod, create_each_block, get_context) { var keep = {}; - var mounts = {}; - var next_iteration = null; - for (i = 0; i < each_block_value.length; i += 1) { - var key = each_block_value[i][block_key]; + var i = list.length; + while (i--) { + var key = list[i][key_prop]; var iteration = lookup[key]; - var next_data = each_block_value[i+1]; - var next = next_data && lookup[next_data[block_key]]; - if (dynamic && iteration) iteration.p(changed, get_context(i)); // TODO should this be deferred? could it be redundant? - - 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; + if (iteration) { + if (dynamic) iteration.p(changed, get_context(i)); } else { - // key is being inserted iteration = lookup[key] = create_each_block(component, key, get_context(i)); iteration.c(); - mounts[key] = iteration; } + lookup[key] = iteration; - keep[iteration.key] = iteration; - last = iteration; + keep[key] = 1; } var destroy = hasOutroMethod - ? function(iteration) { - iteration.o(function() { - iteration.u(); - iteration.d(); - lookup[iteration.key] = null; - }); - } - : function(iteration) { - var first = iteration.first - if (first && first.parentNode) { - iteration.u(); - } - iteration.d(); - lookup[iteration.key] = null; - } + ? outroAndDestroyIteration + : destroyIteration; iteration = head; - while(iteration) { - if (!keep[iteration.key]) { - destroy(iteration); - } + while (iteration) { + if (!keep[iteration.key]) destroy(iteration, lookup); iteration = iteration.next; } - // 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[block_key]; + var next = null; + + i = list.length; + while (i--) { + key = list[i][key_prop]; iteration = lookup[key]; - var block = mounts[key]; - if (block) { - var anchor; + var anchor; - if (hasOutroMethod) { - 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]) - } else { - anchor = next_iteration && next_iteration.first; - } + if (hasOutroMethod) { + var next_key = next && next.key; + var neighbour = iteration.next; + var anchor_key; - block[block.i ? 'i' : 'm'](updateMountNode, anchor); + while (neighbour && anchor_key != next_key && !keep[anchor_key]) { + anchor = neighbour && neighbour.first; + neighbour = neighbour.next; + anchor_key = neighbour && neighbour.key; + } + } else { + anchor = next && next.first; } - iteration.next = next_iteration; - if (next_iteration) next_iteration.last = iteration; - next_iteration = iteration; + + iteration[iteration.i ? 'i' : 'm'](updateMountNode, anchor); + + iteration.next = next; + if (next) next.last = iteration; + next = iteration; } } \ No newline at end of file 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 904208c6a9..164257056f 100644 --- a/test/runtime/samples/each-block-keyed-random-permute/_config.js +++ b/test/runtime/samples/each-block-keyed-random-permute/_config.js @@ -1,50 +1,60 @@ -const VALUES = Array.from( 'abcdefghijklmnopqrstuvwxyz' ); +const VALUES = Array.from('abcdefghijklmnopqrstuvwxyz'); -function toObjects ( array ) { - return array.split( '' ).map( x => ({ id: x }) ); +function toObjects(array) { + return array.split('').map(x => ({ id: x })); } -function permute () { +function permute() { const values = VALUES.slice(); const number = Math.floor(Math.random() * VALUES.length); const permuted = []; for (let i = 0; i < number; i++) { - permuted.push( ...values.splice( Math.floor( Math.random() * ( number - i ) ), 1 ) ); + permuted.push( + ...values.splice(Math.floor(Math.random() * (number - i)), 1) + ); } - return permuted.join( '' ); + return permuted.join(''); } export default { data: { - values: toObjects( 'abc' ) + values: toObjects('abc'), }, html: `(a)(b)(c)`, - test ( assert, component, target ) { - function test ( sequence ) { - component.set({ values: toObjects( sequence ) }); - assert.htmlEqual( target.innerHTML, sequence.split( '' ).map( x => `(${x})` ).join( '' ) ); + test(assert, component, target) { + function test(sequence) { + const previous = target.textContent; + const expected = sequence.split('').map(x => `(${x})`).join(''); + component.set({ values: toObjects(sequence) }); + assert.htmlEqual( + target.innerHTML, + expected, + `\n${previous} -> ${expected}\n${target.textContent}` + ); } // first, some fixed tests so that we can debug them - test( 'abc' ); - test( 'abcd' ); - test( 'abecd' ); - test( 'fabecd' ); - test( 'fabed' ); - test( 'beadf' ); - test( 'ghbeadf' ); - test( 'gf' ); - test( 'gc' ); - test( 'g' ); - test( '' ); - test( 'abc' ); - test( 'duqbmineapjhtlofrskcg' ); - test( 'hdnkjougmrvftewsqpailcb' ); + test('abc'); + test('abcd'); + test('abecd'); + test('fabecd'); + test('fabed'); + test('beadf'); + test('ghbeadf'); + test('gf'); + test('gc'); + test('g'); + test(''); + test('abc'); + test('duqbmineapjhtlofrskcg'); + test('hdnkjougmrvftewsqpailcb'); + test('bidhfacge'); + test('kgjnempcboaflidh'); // then, we party - for ( let i = 0; i < 100; i += 1 ) test( permute() ); + for (let i = 0; i < 100; i += 1) test(permute()); } };