mirror of https://github.com/sveltejs/svelte
parent
358c93853f
commit
c063a71729
@ -0,0 +1,646 @@
|
||||
import {
|
||||
EACH_INDEX_REACTIVE,
|
||||
EACH_IS_ANIMATED,
|
||||
EACH_IS_CONTROLLED,
|
||||
EACH_IS_PROXIED,
|
||||
EACH_ITEM_REACTIVE
|
||||
} from '../../constants.js';
|
||||
import { create_each_block } from './block.js';
|
||||
import {
|
||||
current_hydration_fragment,
|
||||
get_hydration_fragment,
|
||||
hydrate_block_anchor,
|
||||
set_current_hydration_fragment
|
||||
} from './hydration.js';
|
||||
import { clear_text_content, map_get, map_set } from './operations.js';
|
||||
import { STATE_SYMBOL } from './proxy.js';
|
||||
import { insert, remove } from './reconciler.js';
|
||||
import {
|
||||
destroy_each_item_block,
|
||||
each_item_block,
|
||||
empty,
|
||||
update_each_item_block
|
||||
} from './render.js';
|
||||
import { destroy_signal, execute_effect, push_destroy_fn, render_effect } from './runtime.js';
|
||||
import { trigger_transitions } from './transitions.js';
|
||||
import { is_array } from './utils.js';
|
||||
|
||||
const NEW_BLOCK = -1;
|
||||
const MOVED_BLOCK = 99999999;
|
||||
const LIS_BLOCK = -2;
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {Element | Comment} anchor_node
|
||||
* @param {() => V[]} collection
|
||||
* @param {number} flags
|
||||
* @param {null | ((item: V) => string)} key_fn
|
||||
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
|
||||
* @param {null | ((anchor: Node) => void)} fallback_fn
|
||||
* @param {typeof reconcile_indexed_array | reconcile_tracked_array} reconcile_fn
|
||||
* @returns {void}
|
||||
*/
|
||||
function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_fn) {
|
||||
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
|
||||
const block = create_each_block(flags, anchor_node);
|
||||
|
||||
/** @type {null | import('./types.js').Render} */
|
||||
let current_fallback = null;
|
||||
hydrate_block_anchor(anchor_node, is_controlled);
|
||||
|
||||
/** @type {V[]} */
|
||||
let array;
|
||||
|
||||
/** @type {Array<string> | null} */
|
||||
let keys = null;
|
||||
|
||||
/** @type {null | import('./types.js').EffectSignal} */
|
||||
let render = null;
|
||||
block.r =
|
||||
/** @param {import('./types.js').Transition} transition */
|
||||
(transition) => {
|
||||
const fallback = /** @type {import('./types.js').Render} */ (current_fallback);
|
||||
const transitions = fallback.s;
|
||||
transitions.add(transition);
|
||||
transition.f(() => {
|
||||
transitions.delete(transition);
|
||||
if (transitions.size === 0) {
|
||||
if (fallback.e !== null) {
|
||||
if (fallback.d !== null) {
|
||||
remove(fallback.d);
|
||||
fallback.d = null;
|
||||
}
|
||||
destroy_signal(fallback.e);
|
||||
fallback.e = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const create_fallback_effect = () => {
|
||||
/** @type {import('./types.js').Render} */
|
||||
const fallback = {
|
||||
d: null,
|
||||
e: null,
|
||||
s: new Set(),
|
||||
p: current_fallback
|
||||
};
|
||||
// Managed effect
|
||||
const effect = render_effect(
|
||||
() => {
|
||||
const dom = block.d;
|
||||
if (dom !== null) {
|
||||
remove(dom);
|
||||
block.d = null;
|
||||
}
|
||||
let anchor = block.a;
|
||||
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
|
||||
if (is_controlled) {
|
||||
anchor = empty();
|
||||
block.a.appendChild(anchor);
|
||||
}
|
||||
/** @type {(anchor: Node) => void} */ (fallback_fn)(anchor);
|
||||
fallback.d = block.d;
|
||||
block.d = null;
|
||||
},
|
||||
block,
|
||||
true
|
||||
);
|
||||
fallback.e = effect;
|
||||
current_fallback = fallback;
|
||||
};
|
||||
const each = render_effect(
|
||||
() => {
|
||||
/** @type {V[]} */
|
||||
const maybe_array = collection();
|
||||
array = is_array(maybe_array)
|
||||
? maybe_array
|
||||
: maybe_array == null
|
||||
? []
|
||||
: Array.from(maybe_array);
|
||||
if (key_fn !== null) {
|
||||
keys = array.map(key_fn);
|
||||
}
|
||||
const length = array.length;
|
||||
if (fallback_fn !== null) {
|
||||
if (length === 0) {
|
||||
if (block.v.length !== 0 || render === null) {
|
||||
create_fallback_effect();
|
||||
}
|
||||
} else if (block.v.length === 0 && current_fallback !== null) {
|
||||
const fallback = current_fallback;
|
||||
const transitions = fallback.s;
|
||||
if (transitions.size === 0) {
|
||||
if (fallback.d !== null) {
|
||||
remove(fallback.d);
|
||||
fallback.d = null;
|
||||
}
|
||||
} else {
|
||||
trigger_transitions(transitions, 'out');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (render !== null) {
|
||||
execute_effect(render);
|
||||
}
|
||||
},
|
||||
block,
|
||||
false
|
||||
);
|
||||
render = render_effect(
|
||||
/** @param {import('./types.js').EachBlock} block */
|
||||
(block) => {
|
||||
const flags = block.f;
|
||||
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
|
||||
const anchor_node = block.a;
|
||||
reconcile_fn(array, block, anchor_node, is_controlled, render_fn, flags, true, keys);
|
||||
},
|
||||
block,
|
||||
true
|
||||
);
|
||||
push_destroy_fn(each, () => {
|
||||
const flags = block.f;
|
||||
const anchor_node = block.a;
|
||||
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
|
||||
let fallback = current_fallback;
|
||||
while (fallback !== null) {
|
||||
const dom = fallback.d;
|
||||
if (dom !== null) {
|
||||
remove(dom);
|
||||
}
|
||||
const effect = fallback.e;
|
||||
if (effect !== null) {
|
||||
destroy_signal(effect);
|
||||
}
|
||||
fallback = fallback.p;
|
||||
}
|
||||
// Clear the array
|
||||
reconcile_fn([], block, anchor_node, is_controlled, render_fn, flags, false, keys);
|
||||
destroy_signal(/** @type {import('./types.js').EffectSignal} */ (render));
|
||||
});
|
||||
block.e = each;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {Element | Comment} anchor_node
|
||||
* @param {() => V[]} collection
|
||||
* @param {number} flags
|
||||
* @param {null | ((item: V) => string)} key_fn
|
||||
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
|
||||
* @param {null | ((anchor: Node) => void)} fallback_fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function each_keyed(anchor_node, collection, flags, key_fn, render_fn, fallback_fn) {
|
||||
each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_tracked_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {Element | Comment} anchor_node
|
||||
* @param {() => V[]} collection
|
||||
* @param {number} flags
|
||||
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
|
||||
* @param {null | ((anchor: Node) => void)} fallback_fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function each_indexed(anchor_node, collection, flags, render_fn, fallback_fn) {
|
||||
each(anchor_node, collection, flags, null, render_fn, fallback_fn, reconcile_indexed_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {Array<V>} array
|
||||
* @param {import('./types.js').EachBlock} each_block
|
||||
* @param {Element | Comment | Text} dom
|
||||
* @param {boolean} is_controlled
|
||||
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
|
||||
* @param {number} flags
|
||||
* @param {boolean} apply_transitions
|
||||
* @returns {void}
|
||||
*/
|
||||
export function reconcile_indexed_array(
|
||||
array,
|
||||
each_block,
|
||||
dom,
|
||||
is_controlled,
|
||||
render_fn,
|
||||
flags,
|
||||
apply_transitions
|
||||
) {
|
||||
var is_proxied_array = STATE_SYMBOL in array;
|
||||
var a_blocks = each_block.v;
|
||||
var active_transitions = each_block.s;
|
||||
|
||||
if (is_proxied_array) {
|
||||
flags &= ~EACH_ITEM_REACTIVE;
|
||||
flags |= EACH_IS_PROXIED;
|
||||
}
|
||||
|
||||
/** @type {number | void} */
|
||||
var a = a_blocks.length;
|
||||
|
||||
/** @type {number} */
|
||||
var b = array.length;
|
||||
var length = Math.max(a, b);
|
||||
var index = 0;
|
||||
|
||||
/** @type {Array<import('./types.js').EachItemBlock>} */
|
||||
var b_blocks;
|
||||
var block;
|
||||
if (active_transitions.length !== 0) {
|
||||
destroy_active_transition_blocks(active_transitions);
|
||||
}
|
||||
if (b === 0) {
|
||||
b_blocks = [];
|
||||
// Remove old blocks
|
||||
if (is_controlled && a !== 0) {
|
||||
clear_text_content(dom);
|
||||
}
|
||||
while (index < length) {
|
||||
block = a_blocks[index++];
|
||||
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
|
||||
}
|
||||
} else {
|
||||
var item;
|
||||
b_blocks = Array(b);
|
||||
if (current_hydration_fragment !== null) {
|
||||
/** @type {Node} */
|
||||
var hydrating_node = current_hydration_fragment[0];
|
||||
for (; index < length; index++) {
|
||||
// Hydrate block
|
||||
item = array[index];
|
||||
var fragment = /** @type {Array<Text | Comment | Element>} */ (
|
||||
get_hydration_fragment(hydrating_node)
|
||||
);
|
||||
set_current_hydration_fragment(fragment);
|
||||
hydrating_node = /** @type {Node} */ (
|
||||
/** @type {Node} */ (/** @type {Node} */ (fragment.at(-1)).nextSibling).nextSibling
|
||||
);
|
||||
block = each_item_block(array, item, null, index, render_fn, flags);
|
||||
b_blocks[index] = block;
|
||||
}
|
||||
} else {
|
||||
for (; index < length; index++) {
|
||||
if (index >= a) {
|
||||
// Add block
|
||||
item = array[index];
|
||||
block = each_item_block(array, item, null, index, render_fn, flags);
|
||||
b_blocks[index] = block;
|
||||
insert_each_item_block(block, dom, is_controlled, null);
|
||||
} else if (index >= b) {
|
||||
// Remove block
|
||||
block = a_blocks[index];
|
||||
destroy_each_item_block(block, active_transitions, apply_transitions);
|
||||
} else {
|
||||
// Update block
|
||||
item = array[index];
|
||||
block = a_blocks[index];
|
||||
b_blocks[index] = block;
|
||||
update_each_item_block(block, item, index, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
each_block.v = b_blocks;
|
||||
}
|
||||
// Reconcile arrays by the equality of the elements in the array. This algorithm
|
||||
// is based on Ivi's reconcilation logic:
|
||||
//
|
||||
// https://github.com/localvoid/ivi/blob/9f1bd0918f487da5b131941228604763c5d8ef56/packages/ivi/src/client/core.ts#L968
|
||||
//
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {Array<V>} array
|
||||
* @param {import('./types.js').EachBlock} each_block
|
||||
* @param {Element | Comment | Text} dom
|
||||
* @param {boolean} is_controlled
|
||||
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
|
||||
* @param {number} flags
|
||||
* @param {boolean} apply_transitions
|
||||
* @param {Array<string> | null} keys
|
||||
* @returns {void}
|
||||
*/
|
||||
export function reconcile_tracked_array(
|
||||
array,
|
||||
each_block,
|
||||
dom,
|
||||
is_controlled,
|
||||
render_fn,
|
||||
flags,
|
||||
apply_transitions,
|
||||
keys
|
||||
) {
|
||||
var a_blocks = each_block.v;
|
||||
const is_computed_key = keys !== null;
|
||||
var is_proxied_array = STATE_SYMBOL in array;
|
||||
var active_transitions = each_block.s;
|
||||
|
||||
if (is_proxied_array) {
|
||||
flags &= ~EACH_ITEM_REACTIVE;
|
||||
flags |= EACH_IS_PROXIED;
|
||||
}
|
||||
|
||||
/** @type {number | void} */
|
||||
var a = a_blocks.length;
|
||||
|
||||
/** @type {number} */
|
||||
var b = array.length;
|
||||
|
||||
/** @type {Array<import('./types.js').EachItemBlock>} */
|
||||
var b_blocks;
|
||||
var block;
|
||||
if (active_transitions.length !== 0) {
|
||||
destroy_active_transition_blocks(active_transitions);
|
||||
}
|
||||
if (b === 0) {
|
||||
b_blocks = [];
|
||||
// Remove old blocks
|
||||
if (is_controlled && a !== 0) {
|
||||
clear_text_content(dom);
|
||||
}
|
||||
while (a > 0) {
|
||||
block = a_blocks[--a];
|
||||
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
|
||||
}
|
||||
} else {
|
||||
var a_end = a - 1;
|
||||
var b_end = b - 1;
|
||||
var key;
|
||||
var item;
|
||||
var idx;
|
||||
b_blocks = Array(b);
|
||||
if (current_hydration_fragment !== null) {
|
||||
var fragment;
|
||||
|
||||
/** @type {Node} */
|
||||
var hydrating_node = current_hydration_fragment[0];
|
||||
while (b > 0) {
|
||||
// Hydrate block
|
||||
idx = b_end - --b;
|
||||
item = array[idx];
|
||||
key = is_computed_key ? keys[idx] : item;
|
||||
fragment = /** @type {Array<Text | Comment | Element>} */ (
|
||||
get_hydration_fragment(hydrating_node)
|
||||
);
|
||||
set_current_hydration_fragment(fragment);
|
||||
// Get the <!--ssr:..--> tag of the next item in the list
|
||||
// The fragment array can be empty if each block has no content
|
||||
hydrating_node = /** @type {Node} */ (
|
||||
/** @type {Node} */ ((fragment.at(-1) || hydrating_node).nextSibling).nextSibling
|
||||
);
|
||||
block = each_item_block(array, item, key, idx, render_fn, flags);
|
||||
b_blocks[idx] = block;
|
||||
}
|
||||
} else if (a === 0) {
|
||||
// Create new blocks
|
||||
while (b > 0) {
|
||||
idx = b_end - --b;
|
||||
item = array[idx];
|
||||
key = is_computed_key ? keys[idx] : item;
|
||||
block = each_item_block(array, item, key, idx, render_fn, flags);
|
||||
b_blocks[idx] = block;
|
||||
insert_each_item_block(block, dom, is_controlled, null);
|
||||
}
|
||||
} else {
|
||||
var should_update_block = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
|
||||
var start = 0;
|
||||
|
||||
/** @type {null | Text | Element | Comment} */
|
||||
var sibling = null;
|
||||
item = array[b_end];
|
||||
key = is_computed_key ? keys[b_end] : item;
|
||||
// Step 1
|
||||
outer: while (true) {
|
||||
// From the end
|
||||
while (a_blocks[a_end].k === key) {
|
||||
block = a_blocks[a_end--];
|
||||
item = array[b_end];
|
||||
if (should_update_block) {
|
||||
update_each_item_block(block, item, b_end, flags);
|
||||
}
|
||||
sibling = get_first_child(block);
|
||||
b_blocks[b_end] = block;
|
||||
if (start > --b_end || start > a_end) {
|
||||
break outer;
|
||||
}
|
||||
key = is_computed_key ? keys[b_end] : item;
|
||||
}
|
||||
item = array[start];
|
||||
key = is_computed_key ? keys[start] : item;
|
||||
// At the start
|
||||
while (start <= a_end && start <= b_end && a_blocks[start].k === key) {
|
||||
item = array[start];
|
||||
block = a_blocks[start];
|
||||
if (should_update_block) {
|
||||
update_each_item_block(block, item, start, flags);
|
||||
}
|
||||
b_blocks[start] = block;
|
||||
++start;
|
||||
key = is_computed_key ? keys[start] : array[start];
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Step 2
|
||||
if (start > a_end) {
|
||||
while (b_end >= start) {
|
||||
item = array[b_end];
|
||||
key = is_computed_key ? keys[b_end] : item;
|
||||
block = each_item_block(array, item, key, b_end, render_fn, flags);
|
||||
b_blocks[b_end--] = block;
|
||||
sibling = insert_each_item_block(block, dom, is_controlled, sibling);
|
||||
}
|
||||
} else if (start > b_end) {
|
||||
b = start;
|
||||
do {
|
||||
if ((block = a_blocks[b++]) !== null) {
|
||||
destroy_each_item_block(block, active_transitions, apply_transitions);
|
||||
}
|
||||
} while (b <= a_end);
|
||||
} else {
|
||||
// Step 3
|
||||
var pos = 0;
|
||||
var b_length = b_end - start + 1;
|
||||
var sources = new Int32Array(b_length);
|
||||
var item_index = new Map();
|
||||
for (b = 0; b < b_length; ++b) {
|
||||
a = b + start;
|
||||
sources[b] = NEW_BLOCK;
|
||||
item = array[a];
|
||||
key = is_computed_key ? keys[a] : item;
|
||||
map_set(item_index, key, a);
|
||||
}
|
||||
for (b = start; b <= a_end; ++b) {
|
||||
a = map_get(item_index, /** @type {V} */ (a_blocks[b].k));
|
||||
block = a_blocks[b];
|
||||
if (a !== undefined) {
|
||||
pos = pos < a ? a : MOVED_BLOCK;
|
||||
sources[a - start] = b;
|
||||
b_blocks[a] = block;
|
||||
} else if (block !== null) {
|
||||
destroy_each_item_block(block, active_transitions, apply_transitions);
|
||||
}
|
||||
}
|
||||
// Step 4
|
||||
if (pos === MOVED_BLOCK) {
|
||||
mark_lis(sources);
|
||||
}
|
||||
// If keys are animated, we need to do updates before actual moves
|
||||
var is_animated = (flags & EACH_IS_ANIMATED) !== 0;
|
||||
var should_create;
|
||||
if (is_animated) {
|
||||
var i = b_length;
|
||||
while (i-- > 0) {
|
||||
b_end = i + start;
|
||||
a = sources[i];
|
||||
if (pos === MOVED_BLOCK && a !== LIS_BLOCK) {
|
||||
block = b_blocks[b_end];
|
||||
update_each_item_block(block, item, b_end, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
var last_block;
|
||||
var last_sibling;
|
||||
while (b_length-- > 0) {
|
||||
b_end = b_length + start;
|
||||
a = sources[b_length];
|
||||
should_create = a === -1;
|
||||
item = array[b_end];
|
||||
if (should_create) {
|
||||
key = is_computed_key ? keys[b_end] : item;
|
||||
block = each_item_block(array, item, key, b_end, render_fn, flags);
|
||||
} else {
|
||||
block = b_blocks[b_end];
|
||||
if (!is_animated && should_update_block) {
|
||||
update_each_item_block(block, item, b_end, flags);
|
||||
}
|
||||
}
|
||||
if (should_create || (pos === MOVED_BLOCK && a !== LIS_BLOCK)) {
|
||||
last_sibling = last_block === undefined ? sibling : get_first_child(last_block);
|
||||
sibling = insert_each_item_block(block, dom, is_controlled, last_sibling);
|
||||
}
|
||||
b_blocks[b_end] = block;
|
||||
last_block = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
each_block.v = b_blocks;
|
||||
}
|
||||
// Longest Increased Subsequence algorithm.
|
||||
|
||||
/**
|
||||
* @param {Int32Array} a
|
||||
* @returns {void}
|
||||
*/
|
||||
function mark_lis(a) {
|
||||
var length = a.length;
|
||||
var parent = new Int32Array(length);
|
||||
var index = new Int32Array(length);
|
||||
var index_length = 0;
|
||||
var i = 0;
|
||||
|
||||
/** @type {number} */
|
||||
var j;
|
||||
|
||||
/** @type {number} */
|
||||
var k;
|
||||
|
||||
/** @type {number} */
|
||||
var lo;
|
||||
|
||||
/** @type {number} */
|
||||
var hi;
|
||||
// Skip -1 values at the start of the input array `a`.
|
||||
for (; a[i] === NEW_BLOCK; ++i) {
|
||||
/**/
|
||||
}
|
||||
index[0] = i++;
|
||||
for (; i < length; ++i) {
|
||||
k = a[i];
|
||||
if (k !== NEW_BLOCK) {
|
||||
// Ignore -1 values.
|
||||
j = index[index_length];
|
||||
if (a[j] < k) {
|
||||
parent[i] = j;
|
||||
index[++index_length] = i;
|
||||
} else {
|
||||
lo = 0;
|
||||
hi = index_length;
|
||||
while (lo < hi) {
|
||||
j = (lo + hi) >> 1;
|
||||
if (a[index[j]] < k) {
|
||||
lo = j + 1;
|
||||
} else {
|
||||
hi = j;
|
||||
}
|
||||
}
|
||||
if (k < a[index[lo]]) {
|
||||
if (lo > 0) {
|
||||
parent[i] = index[lo - 1];
|
||||
}
|
||||
index[lo] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mutate input array `a` and assign -2 value to all nodes that are part of LIS.
|
||||
j = index[index_length];
|
||||
while (index_length-- >= 0) {
|
||||
a[j] = LIS_BLOCK;
|
||||
j = parent[j];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').Block} block
|
||||
* @param {Element | Comment | Text} dom
|
||||
* @param {boolean} is_controlled
|
||||
* @param {null | Text | Element | Comment} sibling
|
||||
* @returns {Text | Element | Comment}
|
||||
*/
|
||||
function insert_each_item_block(block, dom, is_controlled, sibling) {
|
||||
var current = /** @type {import('./types.js').TemplateNode} */ (block.d);
|
||||
if (sibling === null) {
|
||||
if (is_controlled) {
|
||||
return insert(current, /** @type {Element} */ (dom), null);
|
||||
} else {
|
||||
return insert(current, /** @type {Element} */ (dom.parentNode), dom);
|
||||
}
|
||||
}
|
||||
return insert(current, null, sibling);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').Block} block
|
||||
* @returns {Text | Element | Comment}
|
||||
*/
|
||||
function get_first_child(block) {
|
||||
var current = block.d;
|
||||
if (is_array(current)) {
|
||||
return /** @type {Text | Element | Comment} */ (current[0]);
|
||||
}
|
||||
return /** @type {Text | Element | Comment} */ (current);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<import('./types.js').EachItemBlock>} active_transitions
|
||||
* @returns {void}
|
||||
*/
|
||||
function destroy_active_transition_blocks(active_transitions) {
|
||||
var length = active_transitions.length;
|
||||
if (length > 0) {
|
||||
var i = 0;
|
||||
var block;
|
||||
var transition;
|
||||
for (; i < length; i++) {
|
||||
block = active_transitions[i];
|
||||
transition = block.r;
|
||||
if (transition !== null) {
|
||||
block.r = null;
|
||||
destroy_each_item_block(block, null, false);
|
||||
}
|
||||
}
|
||||
active_transitions.length = 0;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue