recursive liverange destroy

This commit is contained in:
David Greenspan
2012-04-23 16:50:40 -07:00
parent 4af0a8b9f7
commit 365cd81f55
2 changed files with 65 additions and 61 deletions

View File

@@ -215,10 +215,41 @@ Meteor.ui = Meteor.ui || {};
// object. However, on old versions of IE, you probably do need to
// manually remove all ranges because IE can't GC reference cycles
// through the DOM.
Meteor.ui._LiveRange.prototype.destroy = function () {
this._remove_entries(this._start, 0, this._start_idx, this._start_idx + 1);
this._remove_entries(this._end, 1, this._end_idx, this._end_idx + 1);
this._start = this._end = null;
Meteor.ui._LiveRange.prototype.destroy = function (recursive) {
if (recursive) {
this.visit(function(is_start, range) {
if (is_start) {
range.finalize();
range._start = null;
range._end = null;
}
}, function(is_start, node) {
if (! is_start) {
// when leaving a node, clean its children
for(var n = node.firstChild; n; n = n.nextSibling) {
Meteor.ui._LiveRange._clean_node(this.tag, n, true);
}
}
});
this._remove_entries(this._start, 0, this._start_idx);
this._remove_entries(this._end, 1, 0, this._end_idx + 1);
// force-clean the top-level nodes in this, besides _start and _end
if (this._start !== this._end) {
for(var n = this._start.nextSibling;
n !== this._end;
n = n.nextSibling) {
Meteor.ui._LiveRange._clean_node(this.tag, n, true);
}
}
this._start = this._end = null;
} else {
this._remove_entries(this._start, 0, this._start_idx, this._start_idx + 1);
this._remove_entries(this._end, 1, this._end_idx, this._end_idx + 1);
this._start = this._end = null;
}
};
// Return the first node in the range (in preorder traversal)
@@ -263,9 +294,10 @@ Meteor.ui = Meteor.ui || {};
Meteor.ui._LiveRange.visit_children = function(rangeOrParent, tag,
visit_range, visit_node)
{
if (rangeOrParent instanceof Meteor.ui._LiveRange)
rangeOrParent.visit(visit_range, visit_node);
else {
if (rangeOrParent.firstNode) {
_visit_nodes(rangeOrParent.tag, rangeOrParent.firstNode(),
rangeOrParent.lastNode(), visit_range, visit_node);
} else {
if (rangeOrParent.firstChild)
_visit_nodes(tag, rangeOrParent.firstChild, rangeOrParent.lastChild,
visit_range, visit_node);
@@ -275,7 +307,7 @@ Meteor.ui = Meteor.ui || {};
// If ranges starting between first_node and last_node extend past
// last_node, we will visit them and keep going. We don't detect
// when we follow a range end past last_node. Tag is optional
// if we won't be visiting ranges.
// if we won't be visiting ranges.
var _visit_nodes =
function(tag, first_node, last_node, visit_range, visit_node,
start_range_skip)
@@ -291,10 +323,15 @@ Meteor.ui = Meteor.ui || {};
if (startData && startIndex < startData.length) {
// immediate child range that starts with n
var range = startData[startIndex];
// copy _start and _end in case they change inside
// the callback (hard to support in the general case,
// but useful in destroy(true) for example)
var range_start = range._start;
var range_end = range._end;
if (visit_range(true, range) !== false)
recurse(range._start, range._end, startIndex+1);
recurse(range_start, range_end, startIndex+1);
visit_range(false, range);
n = range._end;
n = range_end;
}
else {
// bare node
@@ -392,7 +429,7 @@ Meteor.ui = Meteor.ui || {};
if (typeof new_frag_or_func === "function") {
ret = new_frag_or_func();
ret = new_frag_or_func(old_start, old_end);
} else {
@@ -538,41 +575,4 @@ Meteor.ui = Meteor.ui || {};
return result;
};
// Strip all livedata information from nodes in rangeOrParent, calling
// finalize() on each range found. Most liverange operations are not
// safe to call on a liverange from the finalizer, as the liverange tree
// is being torn down. If rangeOrParent is a range, its tag is used
// instead of the tag argument.
Meteor.ui._LiveRange.cleanup = function(rangeOrParent, tag)
{
var first_node, last_node;
if (rangeOrParent instanceof Meteor.ui._LiveRange) {
first_node = rangeOrParent.firstNode();
last_node = rangeOrParent.lastNode();
tag = rangeOrParent.tag;
} else {
first_node = rangeOrParent.firstChild;
last_node = rangeOrParent.lastChild;
}
var ranges = [];
_visit_nodes(tag, first_node, last_node,
function(is_start, range) {
is_start && ranges.push(range);
});
for(var i=0; i<ranges.length; i++) {
var r = ranges[i];
r.finalize && r.finalize();
r._start = null;
r._end = null;
}
_visit_nodes(
null, first_node, last_node, null,
function(is_start, node) {
is_start && Meteor.ui._LiveRange._clean_node(tag, node, true);
});
};
})();

View File

@@ -249,7 +249,7 @@ Meteor.ui = Meteor.ui || {};
(Meteor.ui._onscreen(node) || Meteor.ui._is_held(node)))
return false;
Meteor.ui._LiveRange.cleanup(range);
range.destroy(true);
return true;
};
@@ -275,7 +275,7 @@ Meteor.ui = Meteor.ui || {};
cx.on_invalidate(function() {
--frag._liveui_refs;
if (! frag._liveui_refs)
Meteor.ui._LiveRange.cleanup(frag, Meteor.ui._LiveUIRange.tag);
cleanup(frag);
});
cx.invalidate();
};
@@ -458,8 +458,12 @@ Meteor.ui = Meteor.ui || {};
//old_range.replace_contents(new_parent);
old_range.replace_contents(function() {
Meteor.ui._LiveRange.cleanup(old_range);
old_range.replace_contents(function(start, end) {
var r;
r = new Meteor.ui._LiveUIRange(start, end);
r.destroy(true);
r = { firstNode: function() { return start; },
lastNode: function() { return end; } };
// remove event handlers on old nodes (which we will be patching)
// at top level, where they are attached by $(...).delegate().
@@ -468,7 +472,7 @@ Meteor.ui = Meteor.ui || {};
n = n.nextSibling)
$(n).unbind();
patch(old_range, new_parent);
patch(r, new_parent);
});
};
@@ -537,8 +541,7 @@ Meteor.ui = Meteor.ui || {};
var frag = makeItem(doc);
var range = new Meteor.ui._LiveUIRange(frag);
if (range_list.length === 0)
Meteor.ui._LiveRange.cleanup(outer_range.replace_contents(frag),
Meteor.ui._LiveUIRange.tag);
cleanup(outer_range.replace_contents(frag));
else if (before_idx === range_list.length)
range_list[range_list.length-1].insert_after(frag);
else
@@ -548,14 +551,11 @@ Meteor.ui = Meteor.ui || {};
},
removed: function(doc, at_idx) {
if (range_list.length === 1)
Meteor.ui._LiveRange.cleanup(
cleanup(
outer_range.replace_contents(Meteor.ui.render(
else_func, react_data)),
Meteor.ui._LiveUIRange.tag);
else_func, react_data)));
else
Meteor.ui._LiveRange.cleanup(
range_list[at_idx].extract(),
Meteor.ui._LiveUIRange.tag);
cleanup(range_list[at_idx].extract());
range_list.splice(at_idx, 1);
},
@@ -600,4 +600,8 @@ Meteor.ui = Meteor.ui || {};
"<!-- ENDRANGE_"+commentId+" -->";
};
var cleanup = function(frag) {
(new Meteor.ui._LiveUIRange(frag)).destroy(true);
};
})();