Skip to content

Commit

Permalink
safer, though maybe slightly less efficient shuffling
Browse files Browse the repository at this point in the history
  • Loading branch information
evs-chris committed Jan 31, 2019
1 parent cf352a6 commit 4719cef
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 61 deletions.
103 changes: 46 additions & 57 deletions src/view/RepeatedFragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,69 +442,64 @@ export default class RepeatedFragment {

idx = pos = 0;
while (idx < len) {
dest = newIndices[pos];
next = null;
rebound = false;

if (dest === -1) {
// drop it like it's hot
prev[pos].unbind().unrender(true);
prev[pos++] = 0;
} else if (dest > idx) {
// need to stash or pull one up
next = newIndices[pos + 1]; // TODO: maybe a shouldMove function that tracks multiple entries?
if (next <= dest) {
// if there's not an existing thing to shuffle, handle that
if (map[idx] === undefined) {
next = iters[idx] = this.createIteration(idx, idx);
if (parentNode) {
anchor = prev[pos];
anchor = (anchor && parentNode && anchor.firstNode()) || nextNode;

next.render(docFrag);
parentNode.insertBefore(docFrag, anchor);
}

idx++;
} else {
dest = newIndices[pos];

if (dest === -1) {
// if it needs to be dropped, drop it
prev[pos] && prev[pos].unbind().unrender(true);
prev[pos++] = 0;
} else if (dest > idx) {
// if it needs to move down, stash it
stash[dest] = prev[pos];
prev[pos++] = null;
} else {
next = stash[idx] || prev[map[idx]];
prev[map[idx]] = null;
anchor = prev[nextRendered(pos, newIndices, prev)];
anchor = (anchor && parentNode && anchor.firstNode()) || nextNode;
// get the fragment that goes for this idx
iters[idx] = next = iters[idx] || stash[idx] || this.createIteration(idx, idx);

if (next) {
// if it's an existing fragment, swizzle
if (stash[idx] || pos !== idx) {
rebound = this.source && next.lastValue !== value[idx];
swizzleFragment(this, next, idx, idx);
if (parentNode) parentNode.insertBefore(next.detach(), anchor);
} else {
next = iters[idx] = this.createIteration(idx, idx);
if (parentNode) {
}

// does next need to be moved?
if (parentNode && (stash[idx] || !prev[pos])) {
anchor = prev[pos + 1];
anchor = (anchor && parentNode && anchor.firstNode()) || nextNode;

if (stash[idx]) {
parentNode.insertBefore(next.detach(), anchor);
} else {
next.render(docFrag);
parentNode.insertBefore(docFrag, anchor);
}
}

prev[pos++] = 0;
idx++;
}
} else {
// all is well
next = iters[idx];
anchor = prev[nextRendered(pos, newIndices, prev)];
anchor = (anchor && parentNode && anchor.firstNode()) || nextNode;
if (!next) {
next = iters[idx] = this.createIteration(idx, idx);
if (parentNode) {
next.render(docFrag);
parentNode.insertBefore(docFrag, anchor);
}
} else if (pos !== idx || stash[idx]) {
rebound = this.source && next.lastValue !== value[idx];
swizzleFragment(this, next, idx, idx);
if (stash[idx] && parentNode) parentNode.insertBefore(next.detach(), anchor);
}

if (dest < idx) pos++; // current pos already moved, so update the pointer
idx++;
prev[pos++] = null;
}

if (next && isObjectType(next)) {
if (next.shouldRebind || rebound) {
next.rebound(rebound);
next.shouldRebind = 0;
if (next && isObjectType(next)) {
if (next.shouldRebind || rebound) {
next.rebound(rebound);
next.shouldRebind = 0;
}
next.update();
next.shuffled();
}
next.update();
next.shuffled();
}
}

Expand Down Expand Up @@ -550,13 +545,6 @@ function findDelegate(start) {
return delegate;
}

function nextRendered(start, newIndices, frags) {
const len = newIndices.length;
for (let i = start; i < len; i++) {
if (~newIndices[i] && frags[i] && frags[i].rendered) return i;
}
}

function swizzleFragment(section, fragment, key, idx) {
const model = section.context ? contextFor(section, fragment, key) : undefined;

Expand Down Expand Up @@ -589,10 +577,11 @@ function swizzleFragment(section, fragment, key, idx) {
}

function shuffleValues(section, shuffler) {
const array = section.context.get() || [];
if (shuffler === true) {
return section.context.get().slice();
return array.slice();
} else {
return section.context.get().map(v => shuffler.reduce((a, c) => a && a[c], v));
return array.map(v => shuffler.reduce((a, c) => a && a[c], v));
}
}

Expand Down
29 changes: 25 additions & 4 deletions tests/browser/autoshuffle.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,19 +527,40 @@ export default function() {
t.htmlEqual(fixture.innerHTML, '12345');

r.set('items', [3, 1, 2, 4, 5]);

t.htmlEqual(fixture.innerHTML, '31245');

r.set('items', [4, 3, 1, 2, 5]);

t.htmlEqual(fixture.innerHTML, '43125');

r.set('items', [1, 2, 3, 4, 5]);

t.htmlEqual(fixture.innerHTML, '12345');

r.set('items', [4, 5, 1, 2, 3]);

t.htmlEqual(fixture.innerHTML, '45123');
});

test(`autoshuffling multiple items at once at random`, t => {
const r = new Ractive({
target: fixture,
template: `{{#each items, true shuffle}}{{.}}{{/each}}`
});

const sets = [
[3, 2, 1, 0, 4, 6, 8, 5],
[0, 1, 4, 5, 2, 7, 6, 3],
[0, 1, 2, 3, 4, 5, 7],
[0, 1, 2, 3, 5, 6, 4],
[0, 1, 2, 3, 5, 4, 8, 6],
[4, 1, 0, 2, 3, 5, 6, 8, 7],
[0, 4, 2, 5, 1, 3, 6, 7, 9],
[6, 2, 5, 1, 0],
[1, 3, 0, 4, 5, 6],
[0, 1, 4, 2, 6, 5, 7, 8, 3]
];

for (let i = 0; i < sets.length; i++) {
r.set('items', sets[i]);
t.htmlEqual(fixture.innerHTML, sets[i].join(''));
}
});
}

0 comments on commit 4719cef

Please sign in to comment.