mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
DomRanges have owners; find ranges under element
This commit is contained in:
@@ -56,8 +56,6 @@ var isSignificantNode = function (n) {
|
||||
/^\s+$/.test(n.nodeValue)));
|
||||
};
|
||||
|
||||
var nextColor = 1;
|
||||
|
||||
var DomRange = function (component) {
|
||||
var start = document.createTextNode("");
|
||||
var end = document.createTextNode("");
|
||||
@@ -79,6 +77,7 @@ var DomRange = function (component) {
|
||||
|
||||
this.members = {};
|
||||
this.nextMemberId = 1;
|
||||
this.owner = null;
|
||||
};
|
||||
|
||||
_extend(DomRange.prototype, {
|
||||
@@ -154,6 +153,7 @@ _extend(DomRange.prototype, {
|
||||
throw new Error("Component not built");
|
||||
// Range
|
||||
var range = newMember.dom;
|
||||
range.owner = this.component;
|
||||
var nodes = range.getNodes();
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
insertNode(nodes[i], parentNode, nextNode);
|
||||
@@ -172,6 +172,7 @@ _extend(DomRange.prototype, {
|
||||
this.removeAll();
|
||||
removeNode(this.start);
|
||||
removeNode(this.end);
|
||||
this.owner = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,6 +195,7 @@ _extend(DomRange.prototype, {
|
||||
if ('dom' in member) {
|
||||
// Range
|
||||
var range = member.dom;
|
||||
range.owner = null;
|
||||
// Don't mind if range (specifically its start
|
||||
// marker) has been removed already.
|
||||
if (range.start.parentNode === parentNode)
|
||||
@@ -265,10 +267,12 @@ _extend(DomRange.prototype, {
|
||||
if ('dom' in mem) {
|
||||
// Range
|
||||
var range = mem.dom;
|
||||
if (range.start.parentNode === parentNode)
|
||||
if (range.start.parentNode === parentNode) {
|
||||
rangeFunc && rangeFunc(range); // still there
|
||||
else
|
||||
} else {
|
||||
range.owner = null;
|
||||
delete members[k]; // gone
|
||||
}
|
||||
} else {
|
||||
// Node
|
||||
var node = mem;
|
||||
@@ -316,8 +320,6 @@ _extend(DomRange.prototype, {
|
||||
// see `getInsertionPoint`. Adding multiple members
|
||||
// at once using `add(array)` is faster.
|
||||
refresh: function () {
|
||||
var color = nextColor++;
|
||||
this.color = color;
|
||||
|
||||
var someNode = null;
|
||||
var someRange = null;
|
||||
@@ -329,7 +331,6 @@ _extend(DomRange.prototype, {
|
||||
numMembers++;
|
||||
}, function (range) {
|
||||
range.refresh();
|
||||
range.color = color;
|
||||
someRange = range;
|
||||
numMembers++;
|
||||
});
|
||||
@@ -366,14 +367,17 @@ _extend(DomRange.prototype, {
|
||||
for (var node = parentNode.firstChild;
|
||||
node; node = node.nextSibling) {
|
||||
|
||||
var nodeOwner;
|
||||
if (node.$ui &&
|
||||
((node.$ui.dom === this &&
|
||||
(nodeOwner = node.$ui.dom) &&
|
||||
((nodeOwner === this &&
|
||||
node !== this.start &&
|
||||
node !== this.end &&
|
||||
isSignificantNode(node)) ||
|
||||
(node.$ui.dom !== this &&
|
||||
node.$ui.dom.color === color &&
|
||||
node.$ui.dom.start === node))) {
|
||||
(nodeOwner !== this &&
|
||||
nodeOwner.owner &&
|
||||
nodeOwner.owner.dom === this &&
|
||||
nodeOwner.start === node))) {
|
||||
// found a member range or node
|
||||
// (excluding "insignificant" empty text nodes,
|
||||
// which won't be moved by, say, jQuery)
|
||||
@@ -409,11 +413,11 @@ _extend(DomRange.prototype, {
|
||||
// nodes as well.
|
||||
for (var n;
|
||||
(n = firstNode.previousSibling) &&
|
||||
n.$ui && n.$ui.dom.color === color;)
|
||||
n.$ui && n.$ui.dom === this;)
|
||||
firstNode = n;
|
||||
for (var n;
|
||||
(n = lastNode.nextSibling) &&
|
||||
n.$ui && n.$ui.dom.color === color;)
|
||||
n.$ui && n.$ui.dom === this;)
|
||||
lastNode = n;
|
||||
// adjust our start/end pointers
|
||||
if (firstNode !== this.start)
|
||||
@@ -452,6 +456,8 @@ _extend(DomRange.prototype, {
|
||||
// still there
|
||||
range.refresh();
|
||||
return range.start;
|
||||
} else {
|
||||
range.owner = null;
|
||||
}
|
||||
} else {
|
||||
// Node
|
||||
@@ -467,4 +473,32 @@ _extend(DomRange.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
// In a real-life case where you need a refresh,
|
||||
// you probably don't have easy
|
||||
// access to the appropriate DomRange or component,
|
||||
// just the enclosing element:
|
||||
//
|
||||
// ```
|
||||
// {{#Sortable}}
|
||||
// <div>
|
||||
// {{#each}}
|
||||
// ...
|
||||
// ```
|
||||
//
|
||||
// In this case, Sortable wants to call `refresh`
|
||||
// on the div, not the each, so it would use this function.
|
||||
DomRange.refresh = function (element) {
|
||||
var topLevelRanges = [];
|
||||
for (var n = element.firstChild;
|
||||
n; n = n.nextSibling) {
|
||||
if (n.$ui && n === n.$ui.dom.start &&
|
||||
! n.$ui.dom.owner)
|
||||
topLevelRanges.push(n.$ui.dom);
|
||||
}
|
||||
|
||||
for (var i = 0, N = topLevelRanges.length;
|
||||
i < N; i++)
|
||||
topLevelRanges[i].refresh();
|
||||
};
|
||||
|
||||
UI.DomRange = DomRange;
|
||||
@@ -52,6 +52,8 @@ Tinytest.add("ui - DomRange - basic", function (test) {
|
||||
s.add(span);
|
||||
r.add(s);
|
||||
test.equal(_.keys(r.members).length, 2);
|
||||
test.isFalse(r.owner);
|
||||
test.equal(s.owner, r);
|
||||
|
||||
// DOM should go: rStart, DIV, sStart, SPAN, sEnd, rEnd.
|
||||
test.equal(span.previousSibling, s.startNode());
|
||||
@@ -73,6 +75,7 @@ Tinytest.add("ui - DomRange - basic", function (test) {
|
||||
|
||||
// removal
|
||||
s.remove();
|
||||
test.isFalse(s.owner);
|
||||
// sStart, SPAN, sEnd are gone from the DOM.
|
||||
test.equal(rStart.nextSibling, div);
|
||||
test.equal(rEnd.previousSibling, div);
|
||||
@@ -168,6 +171,11 @@ Tinytest.add("ui - DomRange - shuffling", function (test) {
|
||||
test.equal(r.get('I'), I);
|
||||
test.equal(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);
|
||||
|
||||
r.remove('Y');
|
||||
test.equal(spellDom(), '((Z)(X)BAAIU)');
|
||||
test.equal(r.get('Y'), null);
|
||||
@@ -203,7 +211,7 @@ Tinytest.add("ui - DomRange - nested", function (test) {
|
||||
|
||||
// nest empty ranges; should work even though
|
||||
// there are no element nodes
|
||||
var A,B,C,D,E,F,G;
|
||||
var A,B,C,D,E,F;
|
||||
|
||||
test.equal(spellDom(), '()');
|
||||
r.add(A = new Comp('A'));
|
||||
@@ -228,9 +236,13 @@ Tinytest.add("ui - DomRange - nested", function (test) {
|
||||
test.equal(spellDom(), '(AaCcBFfDdb)');
|
||||
r.remove('B');
|
||||
test.equal(spellDom(), '(AaCc)');
|
||||
|
||||
test.isFalse(r.owner);
|
||||
test.equal(A.dom.owner, r);
|
||||
test.equal(C.dom.owner, r);
|
||||
});
|
||||
|
||||
Tinytest.add("ui - DomRange - outside moves", function (test) {
|
||||
Tinytest.add("ui - DomRange - external moves", function (test) {
|
||||
// In this one, uppercase letters are div elements,
|
||||
// lowercase letters are marker text nodes, as follows:
|
||||
//
|
||||
@@ -323,6 +335,13 @@ Tinytest.add("ui - DomRange - outside 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);
|
||||
|
||||
// all right, now let's mess around with these elements!
|
||||
|
||||
$([Y,Z]).insertBefore(X);
|
||||
@@ -436,9 +455,19 @@ Tinytest.add("ui - DomRange - outside moves", function (test) {
|
||||
//
|
||||
// See `range.getInsertionPoint`.
|
||||
|
||||
r.refresh();
|
||||
// Same as `r.refresh()` but tests
|
||||
// the convenience function `DomRange.refresh(element)`:
|
||||
DomRange.refresh(r.parentNode());
|
||||
|
||||
r.moveBefore('gl', null);
|
||||
|
||||
test.equal(spellDom(),
|
||||
strip('------------- (- aXb ghiWjkl)'));
|
||||
});
|
||||
strip('-------------- (aXb ghiWjkl)'));
|
||||
});
|
||||
|
||||
|
||||
// TO TEST STILL:
|
||||
// - external remove element
|
||||
// - double-add, double-remove
|
||||
// - external entire remove
|
||||
// - element adoption during move/remove/refresh
|
||||
Reference in New Issue
Block a user