Clean up DomRange, phase 1

- Get rid of DomRange "host objects" (too confusing)
- start to use `r instanceof DomRange` instead of `'dom' in r`

Ported domrange tests; all tests pass
This commit is contained in:
David Greenspan
2014-01-09 20:23:35 -08:00
parent 7e7f29c550
commit cd6387cc02
6 changed files with 123 additions and 175 deletions

View File

@@ -172,7 +172,7 @@ html_scanner = {
});
// We may be one of many `<body>` tags.
results.js += "\nUI.body.contentParts.push(UI.Component.extend({render: " + renderFuncCode + "}));\nMeteor.startup(function () { if (! UI.body.INSTANTIATED) { UI.body.INSTANTIATED = true; UI.materialize(UI.body, document.body); } });\n";
results.js += "\nUI.body.contentParts.push(UI.Component.extend({render: " + renderFuncCode + "}));\nMeteor.startup(function () { if (! UI.body.INSTANTIATED) { UI.body.INSTANTIATED = true; UI.DomRange.insert(UI.render(UI.body).dom, document.body); } });\n";
}
} catch (e) {
if (e.scanner) {

View File

@@ -355,18 +355,5 @@ UI.Component.notifyParented = function () {
}
};
// XXX we don't really want this to be a user-visible callback,
// it's just a particular signal we need from DomRange.
UI.Component.removed = function () {
var self = this;
self.isDestroyed = true;
if (self.destroyed) {
Deps.nonreactive(function () {
updateTemplateInstance(self);
self.destroyed.call(self.templateInstance);
});
}
};
// past compat
UI.Component.preserve = function () {};

View File

@@ -114,15 +114,15 @@ var rangeParented = function (range) {
});
}
if (range.component.notifyParented)
if (range.component && range.component.notifyParented)
range.component.notifyParented();
// recurse on member ranges
var members = range.members;
for (var k in members) {
var mem = members[k];
if ('dom' in mem)
rangeParented(mem.dom);
if (mem instanceof DomRange)
rangeParented(mem);
}
}
};
@@ -137,8 +137,8 @@ var rangeRemoved = function (range) {
// XXX clean up events in $_uievents
// notify component of removal
if (range.component.removed)
range.component.removed();
if (range.removed)
range.removed();
membersRemoved(range);
}
@@ -148,7 +148,7 @@ var nodeRemoved = function (node, viaBackend) {
if (node.nodeType === 1) { // ELEMENT
var comps = DomRange.getComponents(node);
for (var i = 0, N = comps.length; i < N; i++)
rangeRemoved(comps[i].dom);
rangeRemoved(comps[i]);
if (! viaBackend)
DomBackend.removeElement(node);
@@ -159,8 +159,8 @@ var membersRemoved = function (range) {
var members = range.members;
for (var k in members) {
var mem = members[k];
if ('dom' in mem)
rangeRemoved(mem.dom);
if (mem instanceof DomRange)
rangeRemoved(mem);
else
nodeRemoved(mem);
}
@@ -168,34 +168,16 @@ var membersRemoved = function (range) {
var nextGuid = 1;
var DomRange = function (component) {
// This code supports IE 8 if `createTextNode` is changed
// to `createComment`. What we really should do is:
// - use comments in IE 8
// - use TextNodes in all other browsers
// - keep a list of all DomRanges to avoid IE 9+ GC of
// TextNodes; this will probably help DomRange removal
// detection too.
var DomRange = function () {
var start = createMarkerNode();
var end = createMarkerNode();
var fragment = DomBackend.newFragment([start, end]);
fragment.$_uiIsOffscreen = true;
if (component) {
this.component = component;
component.dom = this;
// must NOT set `this.dom` to anything (even `null`)
// in this case.
} else {
// self-host
this.component = this;
this.dom = this;
}
this.start = start;
this.end = end;
start.$ui = this.component;
end.$ui = this.component;
start.$ui = this;
end.$ui = this;
this.members = {};
this.nextMemberId = 1;
@@ -296,9 +278,9 @@ _extend(DomRange.prototype, {
var members = this.members;
if (members.hasOwnProperty(id)) {
var oldMember = members[id];
if ('dom' in oldMember) {
if (oldMember instanceof DomRange) {
// range, does it still exist?
var oldRange = oldMember.dom;
var oldRange = oldMember;
if (oldRange.start.parentNode !== parentNode) {
delete members[id];
oldRange.owner = null;
@@ -318,12 +300,10 @@ _extend(DomRange.prototype, {
}
}
if ('dom' in newMember) {
if (! newMember.dom)
throw new Error("Component not built");
if (newMember instanceof DomRange) {
// Range
var range = newMember.dom;
range.owner = this.component;
var range = newMember;
range.owner = this;
var nodes = range.getNodes();
if (tbodyFixNeeded(nodes, parentNode))
@@ -345,7 +325,7 @@ _extend(DomRange.prototype, {
// can't attach `$ui` to a TextNode in IE 8, so
// don't bother on any browser.
if (node.nodeType !== 3)
node.$ui = this.component;
node.$ui = this;
if (tbodyFixNeeded(node, parentNode))
// may cause a refresh(); important that the
@@ -384,14 +364,14 @@ _extend(DomRange.prototype, {
if (! parentNode)
return;
if ('dom' in member) {
if (member instanceof DomRange) {
// Range
var range = member.dom;
var range = member;
range.owner = null;
// Don't mind if range (specifically its start
// marker) has been removed already.
if (range.start.parentNode === parentNode)
member.dom.remove();
member.remove();
} else {
// Node
var node = member;
@@ -418,9 +398,9 @@ _extend(DomRange.prototype, {
if (! parentNode)
return;
if ('dom' in member) {
if (member instanceof DomRange) {
// Range
var range = member.dom;
var range = member;
// Don't mind if range (specifically its start marker)
// has been removed already.
if (range.start.parentNode === parentNode) {
@@ -458,9 +438,9 @@ _extend(DomRange.prototype, {
for (var k in members) {
// mem is a component (hosting a Range) or a Node
var mem = members[k];
if ('dom' in mem) {
if (mem instanceof DomRange) {
// Range
var range = mem.dom;
var range = mem;
if (range.start.parentNode === parentNode) {
rangeFunc && rangeFunc(range); // still there
} else {
@@ -517,6 +497,7 @@ _extend(DomRange.prototype, {
// see `getInsertionPoint`. Adding multiple members
// at once using `add(array)` is faster.
refresh: function () {
var parentNode = this.parentNode();
if (! parentNode)
return;
@@ -579,14 +560,13 @@ _extend(DomRange.prototype, {
var nodeOwner;
if (node.$ui &&
(nodeOwner = node.$ui.dom) &&
(nodeOwner = node.$ui) &&
((nodeOwner === this &&
node !== this.start &&
node !== this.end &&
isSignificantNode(node)) ||
(nodeOwner !== this &&
nodeOwner.owner &&
nodeOwner.owner.dom === this &&
nodeOwner.owner === this &&
nodeOwner.start === node))) {
// found a member range or node
// (excluding "insignificant" empty text nodes,
@@ -602,10 +582,10 @@ _extend(DomRange.prototype, {
// can't attach `$ui` to a TextNode in IE 8, so
// don't bother on any browser.
if (n.nodeType !== 3)
n.$ui = this.component;
n.$ui = this;
}
}
if (node.$ui.dom === this) {
if (node.$ui === this) {
// Node
firstNode = (firstNode || node);
lastNode = node;
@@ -614,7 +594,7 @@ _extend(DomRange.prototype, {
// skip it and include its nodes in
// firstNode/lastNode.
firstNode = (firstNode || node);
node = node.$ui.dom.end;
node = node.$ui.end;
lastNode = node;
}
}
@@ -626,12 +606,12 @@ _extend(DomRange.prototype, {
// nodes as well.
for (var n;
(n = firstNode.previousSibling) &&
(n.$ui && n.$ui.dom === this ||
(n.$ui && n.$ui === this ||
_contains(textNodes, n));)
firstNode = n;
for (var n;
(n = lastNode.nextSibling) &&
(n.$ui && n.$ui.dom === this ||
(n.$ui && n.$ui === this ||
_contains(textNodes, n));)
lastNode = n;
// adjust our start/end pointers
@@ -665,9 +645,9 @@ _extend(DomRange.prototype, {
beforeId = ' ' + beforeId;
var mem = members[beforeId];
if ('dom' in mem) {
if (mem instanceof DomRange) {
// Range
var range = mem.dom;
var range = mem;
if (range.start.parentNode === parentNode) {
// still there
range.refresh();
@@ -723,25 +703,22 @@ DomRange.refresh = function (element) {
var comps = DomRange.getComponents(element);
for (var i = 0, N = comps.length; i < N; i++)
comps[i].dom.refresh();
comps[i].refresh();
};
DomRange.getComponents = function (element) {
var topLevelComps = [];
for (var n = element.firstChild;
n; n = n.nextSibling) {
if (n.$ui && n === n.$ui.dom.start &&
! n.$ui.dom.owner)
if (n.$ui && n === n.$ui.start &&
! n.$ui.owner)
topLevelComps.push(n.$ui);
}
return topLevelComps;
};
// `parentNode` must be an ELEMENT, not a fragment
DomRange.insert = function (component, parentNode, nextNode) {
var range = component.dom;
if (! range)
throw new Error("Expected a component with a DomRange");
DomRange.insert = function (range, parentNode, nextNode) {
var nodes = range.getNodes();
if (tbodyFixNeeded(nodes, parentNode))
parentNode = makeOrFindTbody(parentNode, nextNode);
@@ -801,7 +778,7 @@ var makeOrFindTbody = function (parent, next) {
var moveWithOwnersIntoTbody = function (range) {
while (range.owner)
range = range.owner.dom;
range = range.owner;
var nodes = range.getNodes(); // causes refresh
var tbody = makeOrFindTbody(range.parentNode(),
@@ -826,9 +803,9 @@ DomRange.prototype.contains = function (compOrNode) {
return false;
var range;
if ('dom' in compOrNode) {
if (compOrNode instanceof DomRange) {
// Component
range = compOrNode.dom;
range = compOrNode;
var pn = range.parentNode();
if (! pn)
return false;
@@ -849,14 +826,14 @@ DomRange.prototype.contains = function (compOrNode) {
while (node.parentNode !== parentNode)
node = node.parentNode;
range = node.$ui && node.$ui.dom;
range = node.$ui;
}
// Now see if `range` is truthy and either `this`
// or an immediate subrange
while (range && range !== this)
range = range.owner && range.owner.dom;
range = range.owner;
return range === this;
};
@@ -960,7 +937,7 @@ var HandlerRec = function (elem, type, selector, handler, $ui) {
if ((! h.selector) && evt.currentTarget !== evt.target)
// no selector means only fire on target
return;
if (! h.$ui.dom.contains(evt.currentTarget))
if (! h.$ui.contains(evt.currentTarget))
return;
return h.handler.call(h.$ui, evt);
};
@@ -1075,17 +1052,16 @@ DomRange.prototype.on = function (events, selector, handler) {
}
var handlerList = info.handlers;
var handlerRec = new HandlerRec(
parentNode, type, selector, handler, this.component);
parentNode, type, selector, handler, this);
handlerRec.bind();
handlerList.push(handlerRec);
// move handlers of enclosing ranges to end
for (var r = (this.owner && this.owner.dom);
r; r = (r.owner && r.owner.dom)) {
for (var r = this.owner; r; r = r.owner) {
// r is an enclosing DomRange
for (var j = 0, Nj = handlerList.length;
j < Nj; j++) {
var h = handlerList[j];
if (h.$ui && h.$ui.dom === r) {
if (h.$ui === r) {
h.unbind();
h.bind();
handlerList.splice(j, 1); // remove handlerList[j]

View File

@@ -5,23 +5,23 @@ var parseHTML = UI.DomBackend.parseHTML;
// fake component; DomRange host
var Comp = function (which) {
this.which = which;
new DomRange(this);
this.dom = new DomRange;
this.dom.component = this;
};
var isStartMarker = function (n) {
return (n.$ui && n === n.$ui.dom.start);
return (n.$ui && n === n.$ui.start);
};
var isEndMarker = function (n) {
return (n.$ui && n === n.$ui.dom.end);
return (n.$ui && n === n.$ui.end);
};
var inDocument = function (range, func) {
var onscreen = document.createElement("DIV");
onscreen.style.display = 'none';
document.body.appendChild(onscreen);
DomRange.insert(range.component, onscreen);
DomRange.insert(range, onscreen);
try {
func(range);
} finally {
@@ -72,7 +72,6 @@ Tinytest.add("ui - DomRange - basic", function (test) {
test.equal(div.previousSibling, rStart);
test.equal(div.nextSibling, rEnd);
test.equal(div.$ui, r);
test.equal(div.$ui.dom, r);
// add a subrange
var s = new DomRange;
@@ -143,8 +142,8 @@ Tinytest.add("ui - DomRange - shuffling", function (test) {
else
str += '-';
} else {
if (n.$ui.which)
str += n.$ui.which;
if (n.$ui.component && n.$ui.component.which)
str += n.$ui.component.which;
else
str += (n.nodeName || '?');
}
@@ -170,12 +169,12 @@ Tinytest.add("ui - DomRange - shuffling", function (test) {
var X = new Comp('X');
var Y = new Comp('Y');
var Z = new Comp('Z');
r.add('X', X, 'I');
r.add('X', X.dom, 'I');
X.dom.add(document.createElement("SPAN"));
Y.dom.add(document.createElement("SPAN"));
Z.dom.add(document.createElement("SPAN"));
r.add('Y', Y, 'U');
r.add('Z', Z);
r.add('Y', Y.dom, 'U');
r.add('Z', Z.dom);
test.equal(spellDom(), '(B(X)I(Y)U(Z))');
@@ -190,21 +189,22 @@ Tinytest.add("ui - DomRange - shuffling", function (test) {
r.moveBefore('U', 'Y');
test.equal(spellDom(), '((X)BAAIU(Y)(Z))');
r.moveBefore('Z', 'X');
r.moveBefore('Y', 'X');
test.equal(spellDom(), '((Z)(Y)(X)BAAIU)');
test.equal(r.get('X'), X);
test.equal(r.get('Y'), Y);
test.equal(r.get('Z'), Z);
test.equal(r.get('B'), B);
test.equal(r.get('I'), I);
test.equal(r.get('U'), U);
test.isTrue(r.get('X') === X.dom);
test.isTrue(r.get('Y') === Y.dom);
test.isTrue(r.get('Z') === Z.dom);
test.isTrue(r.get('B') === B);
test.isTrue(r.get('I') === I);
test.isTrue(r.get('U') === U);
test.isFalse(r.owner);
test.equal(X.dom.owner, r);
test.equal(Y.dom.owner, r);
test.equal(Z.dom.owner, r);
test.isTrue(X.dom.owner === r);
test.isTrue(Y.dom.owner === r);
test.isTrue(Z.dom.owner === r);
r.remove('Y');
test.equal(spellDom(), '((Z)(X)BAAIU)');
@@ -226,9 +226,9 @@ Tinytest.add("ui - DomRange - nested", function (test) {
_.each(frag.childNodes, function (n) {
var ui = n.$ui;
if (isStartMarker(n))
str += (ui.which ? ui.which : '(');
str += (ui.component ? ui.component.which : '(');
else if (isEndMarker(n))
str += (ui.which ? ui.which.toLowerCase() : ')');
str += (ui.component ? ui.component.which.toLowerCase() : ')');
else
str += '?';
});
@@ -240,16 +240,16 @@ Tinytest.add("ui - DomRange - nested", function (test) {
var A,B,C,D,E,F;
test.equal(spellDom(), '()');
r.add(A = new Comp('A'));
r.add((A = new Comp('A')).dom);
test.equal(spellDom(), '(Aa)');
r.add('B', B = new Comp('B'));
r.add('C', C = new Comp('C'), 'B');
r.add('B', (B = new Comp('B')).dom);
r.add('C', (C = new Comp('C')).dom, 'B');
test.equal(spellDom(), '(AaCcBb)');
r.get('B').dom.add('D', D = new Comp('D'));
D.dom.add('E', new Comp('E'));
r.get('B').add('D', (D = new Comp('D')).dom);
D.dom.add('E', (E = new Comp('E')).dom);
test.equal(spellDom(), '(AaCcBDEedb)');
B.dom.add('F', F = new Comp('F'));
B.dom.add('F', (F = new Comp('F')).dom);
test.equal(spellDom(), '(AaCcBDEedFfb)');
r.moveBefore('B', 'C');
@@ -305,7 +305,7 @@ Tinytest.add("ui - DomRange - external moves", function (test) {
de.dom.add('Z', Z);
de.dom.add(wsp());
cf.dom.add(wsp());
cf.dom.add('de', de);
cf.dom.add('de', de.dom);
cf.dom.add(wsp());
var gl = new Comp('gl');
var hk = new Comp('hk');
@@ -316,12 +316,12 @@ Tinytest.add("ui - DomRange - external moves", function (test) {
// i-W-j
test.equal(ij.dom.getNodes().length, 5);
gl.dom.add(wsp());
gl.dom.add('hk', hk);
gl.dom.add('hk', hk.dom);
gl.dom.add(wsp());
// g-hk-l
test.equal(gl.dom.getNodes().length, 6);
hk.dom.add(wsp());
hk.dom.add('ij', ij);
hk.dom.add('ij', ij.dom);
hk.dom.add(wsp());
// h-i-W-j-k
test.equal(hk.dom.getNodes().length, 9);
@@ -329,12 +329,12 @@ Tinytest.add("ui - DomRange - external moves", function (test) {
test.equal(gl.dom.getNodes().length, 13);
var r = new DomRange;
r.dom.add('ab', ab);
r.dom.add(wsp());
r.dom.add('cf', cf);
r.dom.add(wsp());
r.dom.add('gl', gl);
r.dom.add('V', V);
r.add('ab', ab.dom);
r.add(wsp());
r.add('cf', cf.dom);
r.add(wsp());
r.add('gl', gl.dom);
r.add('V', V);
var spellDom = function () {
var frag = r.parentNode();
@@ -342,9 +342,9 @@ Tinytest.add("ui - DomRange - external moves", function (test) {
_.each(frag.childNodes, function (n) {
var ui = n.$ui;
if (isStartMarker(n))
str += (ui.which ? ui.which.charAt(0) : '(');
str += (ui.component ? ui.component.which.charAt(0) : '(');
else if (isEndMarker(n))
str += (ui.which ? ui.which.charAt(1) : ')');
str += (ui.component ? ui.component.which.charAt(1) : ')');
else if (n.nodeType === 3)
str += '-';
else
@@ -359,12 +359,12 @@ Tinytest.add("ui - DomRange - external moves", function (test) {
test.equal(spellDom(),
strip('(a-X-b - c-d-Y-Z-e-f - g-h-i-W-j-k-l V)'));
test.equal(ab.dom.owner, r);
test.equal(cf.dom.owner, r);
test.equal(de.dom.owner, cf);
test.equal(gl.dom.owner, r);
test.equal(hk.dom.owner, gl);
test.equal(ij.dom.owner, hk);
test.isTrue(ab.dom.owner === r);
test.isTrue(cf.dom.owner === r);
test.isTrue(de.dom.owner === cf.dom);
test.isTrue(gl.dom.owner === r);
test.isTrue(hk.dom.owner === gl.dom);
test.isTrue(ij.dom.owner === hk.dom);
// all right, now let's mess around with these elements!
@@ -491,11 +491,13 @@ Tinytest.add("ui - DomRange - external moves", function (test) {
Tinytest.add("ui - DomRange - tables", function (test) {
var range = function (x) {
new DomRange(x);
// create a range x.dom containing an element x.el,
// inside that element, the range x.content.dom
x.dom = new DomRange;
if (x.el) {
x.dom.add(x.el);
if (x.content)
DomRange.insert(x.content, x.el);
DomRange.insert(x.content.dom, x.el);
}
return x;
};
@@ -525,7 +527,7 @@ Tinytest.add("ui - DomRange - tables", function (test) {
test.equal(table.el.childNodes.length, 0);
table.content = range({});
DomRange.insert(table.content, table.el);
DomRange.insert(table.content.dom, table.el);
// table has two children (start/end markers), no elements
test.equal(table.el.childNodes.length, 2);
test.notEqual(table.el.firstChild.nodeType, 1);
@@ -545,11 +547,11 @@ Tinytest.add("ui - DomRange - tables", function (test) {
test.equal(table.el.childNodes.length, 0);
table.content = range({});
DomRange.insert(table.content, table.el);
DomRange.insert(table.content.dom, table.el);
var a1 = range({});
var a2 = range({});
a1.dom.add(a2);
table.content.dom.add(a1);
a1.dom.add(a2.dom);
table.content.dom.add(a1.dom);
// 6 marker nodes in table, no elements
test.equal(table.el.childNodes.length, 6);
test.equal($(table.el).find("*").length, 0);
@@ -567,17 +569,17 @@ Tinytest.add("ui - DomRange - tables", function (test) {
test.equal(table.el.childNodes.length, 0);
table.content = range({});
DomRange.insert(table.content, table.el);
DomRange.insert(table.content.dom, table.el);
var b1 = range({});
var b2 = range({});
table.content.dom.add(b1);
table.content.dom.add(b1.dom);
b2.dom.add(document.createElement('tr'));
// 4 marker nodes in table, no elements
test.equal(table.el.childNodes.length, 4);
test.equal($(table.el).find("*").length, 0);
// shazam, adding b2, which contains a TR,
// should move all the ranges into a TBODY.
b1.dom.add(b2);
b1.dom.add(b2.dom);
test.equal(table.el.childNodes.length, 1);
test.equal(table.el.firstChild.nodeName, 'TBODY');
test.equal(table.el.firstChild.childNodes.length, 7);
@@ -594,8 +596,8 @@ Tinytest.add("ui - DomRange - tables", function (test) {
var c1 = range({});
var c2 = range({});
DomRange.insert(c1, table.el);
DomRange.insert(c2, table.el);
DomRange.insert(c1.dom, table.el);
DomRange.insert(c2.dom, table.el);
test.equal(table.el.childNodes.length, 4);
test.equal($(table.el).find("*").length, 0);
c2.dom.add(document.createElement('tr'));
@@ -745,22 +747,15 @@ Tinytest.add("ui - DomRange - contains", function (test) {
Tinytest.add("ui - DomRange - constructor", function (test) {
var r = new DomRange;
test.isTrue(r.dom === r);
test.isTrue(r.component === r);
var x = {};
var s = new DomRange(x);
test.isTrue(x.dom === s);
test.isTrue(s.component === x);
test.isTrue(r.parentNode());
test.isTrue(s.parentNode());
test.isTrue(s.start.$ui === x);
test.isTrue(s.end.$ui === x);
test.isTrue(r.start.$ui === r);
test.isTrue(r.end.$ui === r);
var div = document.createElement('div');
s.add(div);
test.isTrue(div.$ui === x);
r.add(div);
test.isTrue(div.$ui === r);
});
Tinytest.add("ui - DomRange - get", function (test) {
@@ -803,16 +798,6 @@ Tinytest.add("ui - DomRange - get", function (test) {
test.isTrue(r.get('c') === c);
test.isTrue(r.get('toString') === d);
var x = {};
var s = new DomRange(x);
test.throws(function () {
r.add('s', s);
});
r.add('x', x);
test.isTrue(r.get('x') === x);
});
// This test targets IE 9 and 10, which allow properties

View File

@@ -100,7 +100,7 @@ UI.Each = Component.extend({
beforeId = LocalCollection._idStringify(beforeId);
var renderedItem = UI.render(content.withData(dataFunc), self);
range.add(id, renderedItem, beforeId);
range.add(id, renderedItem.dom, beforeId);
},
removed: function (id, item) {
addToCount(-1);
@@ -112,7 +112,7 @@ UI.Each = Component.extend({
beforeId && LocalCollection._idStringify(beforeId));
},
changed: function (id, newItem) {
range.get(LocalCollection._idStringify(id)).data.$set(newItem);
range.get(LocalCollection._idStringify(id)).component.data.$set(newItem);
}
});

View File

@@ -159,13 +159,11 @@ UI.emboxValue = function (funcOrValue, equals) {
////////////////////////////////////////
UI.insert = UI.DomRange && UI.DomRange.insert;
// Insert a DOM node or DomRange into a DOM element or DomRange.
//
// One of three things happens depending on what needs to be inserted into what:
// - `range.add` (anything into DomRange)
// - `UI.insert` (DomRange into element)
// - `UI.DomRange.insert` (DomRange into element)
// - `elem.insertBefore` (node into element)
//
// The optional `before` argument is an existing node or id to insert before in
@@ -174,12 +172,11 @@ var insert = function (nodeOrRange, parent, before) {
if (! parent)
throw new Error("Materialization parent required");
if (parent.component && parent.component.dom) {
// parent is DomRange; add node or range
parent.add(nodeOrRange.component || nodeOrRange, before);
} else if (nodeOrRange.component && nodeOrRange.component.dom) {
if (parent instanceof UI.DomRange) {
parent.add(nodeOrRange, before);
} else if (nodeOrRange instanceof UI.DomRange) {
// parent is an element; inserting a range
UI.insert(nodeOrRange.component, parent, before);
UI.DomRange.insert(nodeOrRange, parent, before);
} else {
// parent is an element; inserting an element
parent.insertBefore(nodeOrRange, before || null); // `null` for IE
@@ -243,10 +240,13 @@ UI.render = function (kind, parentComponent) {
var content = content = (inst.render && inst.render());
var range = new UI.DomRange(inst);
var range = new UI.DomRange;
inst.dom = range;
range.component = inst;
materialize(content, range, null, inst);
inst.removed = function () {
range.removed = function () {
inst.isDestroyed = true;
if (inst.destroyed) {
updateTemplateInstance(inst);