mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
IE 9 compat; isParented; beginning of isRemoved
This commit is contained in:
@@ -65,7 +65,7 @@ var checkId = function (id) {
|
||||
throw new Error("id may not be empty");
|
||||
};
|
||||
|
||||
var textExpandosOk = (function () {
|
||||
var textExpandosSupported = (function () {
|
||||
var tn = document.createTextNode('');
|
||||
try {
|
||||
tn.blahblah = true;
|
||||
@@ -77,10 +77,57 @@ var textExpandosOk = (function () {
|
||||
})();
|
||||
|
||||
var createMarkerNode = (
|
||||
textExpandosOk ?
|
||||
textExpandosSupported ?
|
||||
function () { return document.createTextNode(""); } :
|
||||
function () { return document.createComment(""); });
|
||||
|
||||
var rangeParented = function (range) {
|
||||
if (! range.isParented) {
|
||||
range.isParented = true;
|
||||
|
||||
if (! range.owner) {
|
||||
// top-level (unowned) ranges in an element,
|
||||
// keep a pointer to the range on the parent
|
||||
// element. This is really just for IE 9+
|
||||
// TextNode GC issues, but we can't do reliable
|
||||
// bug detection.
|
||||
var parentNode = range.parentNode();
|
||||
var rangeDict = (
|
||||
parentNode.$_uiranges ||
|
||||
(parentNode.$_uiranges = {}));
|
||||
rangeDict[range._rangeId] = range;
|
||||
range._rangeDict = rangeDict;
|
||||
}
|
||||
|
||||
// recurse on member ranges
|
||||
var members = range.members;
|
||||
for (var k in members) {
|
||||
var mem = members[k];
|
||||
if ('dom' in mem)
|
||||
rangeParented(mem.dom);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var rangeRemoved = function (range) {
|
||||
if (! range.isRemoved) {
|
||||
range.isRemoved = true;
|
||||
|
||||
if (range._rangeDict)
|
||||
delete range._rangeDict[range._rangeId];
|
||||
|
||||
// recurse on member ranges
|
||||
var members = range.members;
|
||||
for (var k in members) {
|
||||
var mem = members[k];
|
||||
if ('dom' in mem)
|
||||
rangeRemoved(mem.dom);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var nextGuid = 1;
|
||||
|
||||
var DomRange = function (component) {
|
||||
// This code supports IE 8 if `createTextNode` is changed
|
||||
// to `createComment`. What we really should do is:
|
||||
@@ -113,6 +160,11 @@ var DomRange = function (component) {
|
||||
this.members = {};
|
||||
this.nextMemberId = 1;
|
||||
this.owner = null;
|
||||
this._rangeId = nextGuid++;
|
||||
this._rangeDict = null;
|
||||
|
||||
this.isParented = false;
|
||||
this.isRemoved = false;
|
||||
};
|
||||
|
||||
_extend(DomRange.prototype, {
|
||||
@@ -142,6 +194,13 @@ _extend(DomRange.prototype, {
|
||||
for (var i = 0, N = nodes.length; i < N; i++)
|
||||
removeNode(nodes[i]);
|
||||
|
||||
// call `rangeRemoved` on each member range
|
||||
var members = this.members;
|
||||
for (var k in members) {
|
||||
var mem = members[k];
|
||||
if ('dom' in mem)
|
||||
rangeRemoved(mem.dom);
|
||||
}
|
||||
this.members = {};
|
||||
},
|
||||
// (_nextNode is internal)
|
||||
@@ -202,6 +261,7 @@ _extend(DomRange.prototype, {
|
||||
if (oldRange.start.parentNode !== parentNode) {
|
||||
delete members[id];
|
||||
oldRange.owner = null;
|
||||
rangeRemoved(oldRange);
|
||||
} else {
|
||||
throw new Error("Member already exists: " + id.slice(1));
|
||||
}
|
||||
@@ -231,6 +291,9 @@ _extend(DomRange.prototype, {
|
||||
members[id] = newMember;
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
insertNode(nodes[i], parentNode, nextNode);
|
||||
|
||||
if (this.isParented)
|
||||
rangeParented(range);
|
||||
} else {
|
||||
// Node
|
||||
if (typeof newMember.nodeType !== 'number')
|
||||
@@ -257,6 +320,7 @@ _extend(DomRange.prototype, {
|
||||
removeNode(this.start);
|
||||
removeNode(this.end);
|
||||
this.owner = null;
|
||||
rangeRemoved(this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -349,7 +413,7 @@ _extend(DomRange.prototype, {
|
||||
var members = this.members;
|
||||
var parentNode = this.parentNode();
|
||||
for (var k in members) {
|
||||
// me, is a DomRange or node
|
||||
// mem is a component (hosting a Range) or a Node
|
||||
var mem = members[k];
|
||||
if ('dom' in mem) {
|
||||
// Range
|
||||
@@ -359,6 +423,7 @@ _extend(DomRange.prototype, {
|
||||
} else {
|
||||
range.owner = null;
|
||||
delete members[k]; // gone
|
||||
rangeRemoved(range);
|
||||
}
|
||||
} else {
|
||||
// Node
|
||||
@@ -562,6 +627,7 @@ _extend(DomRange.prototype, {
|
||||
return range.start;
|
||||
} else {
|
||||
range.owner = null;
|
||||
rangeRemoved(range);
|
||||
}
|
||||
} else {
|
||||
// Node
|
||||
@@ -629,6 +695,7 @@ DomRange.insert = function (component, parentNode, nextNode) {
|
||||
parentNode = makeOrFindTbody(parentNode, nextNode);
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
insertNode(nodes[i], parentNode, nextNode);
|
||||
rangeParented(range);
|
||||
};
|
||||
|
||||
///// TBODY FIX for compatibility with jQuery.
|
||||
|
||||
@@ -932,6 +932,77 @@ Tinytest.add("ui - DomRange - nested event order", function (test) {
|
||||
});
|
||||
});
|
||||
|
||||
Tinytest.add("ui - DomRange - isParented", function (test) {
|
||||
inDocument(new DomRange, function (r) {
|
||||
test.equal(r.isParented, true);
|
||||
var a = new DomRange;
|
||||
var b = new DomRange;
|
||||
var c = new DomRange;
|
||||
var d = new DomRange;
|
||||
var e = new DomRange;
|
||||
var abcde = function (ap, bp, cp, dp, ep) {
|
||||
test.equal(!! a.isParented, !! ap);
|
||||
test.equal(!! b.isParented, !! bp);
|
||||
test.equal(!! c.isParented, !! cp);
|
||||
test.equal(!! d.isParented, !! dp);
|
||||
test.equal(!! e.isParented, !! ep);
|
||||
};
|
||||
var div = document.createElement("DIV");
|
||||
c.add(div);
|
||||
abcde(0, 0, 0, 0, 0);
|
||||
d.add(e);
|
||||
abcde(0, 0, 0, 0, 0);
|
||||
DomRange.insert(d, div);
|
||||
abcde(0, 0, 0, 1, 1);
|
||||
a.add(b);
|
||||
abcde(0, 0, 0, 1, 1);
|
||||
r.add(a);
|
||||
abcde(1, 1, 0, 1, 1);
|
||||
b.add(c);
|
||||
abcde(1, 1, 1, 1, 1);
|
||||
|
||||
var container = r.parentNode();
|
||||
test.equal(_.keys(container.$_uiranges).length, 1);
|
||||
test.equal(_.keys(div.$_uiranges).length, 1);
|
||||
d.remove();
|
||||
test.equal(_.keys(div.$_uiranges).length, 0);
|
||||
r.remove();
|
||||
test.equal(_.keys(container.$_uiranges).length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
Tinytest.add("ui - DomRange - structural removal", function (test) {
|
||||
inDocument(new DomRange, function (r) {
|
||||
var a = new DomRange;
|
||||
test.isFalse(a.isRemoved);
|
||||
r.add('a', a);
|
||||
test.isFalse(a.isRemoved);
|
||||
r.remove('a');
|
||||
test.isTrue(a.isRemoved);
|
||||
|
||||
var b = new DomRange;
|
||||
test.isFalse(b.isRemoved);
|
||||
r.add(b);
|
||||
test.isFalse(b.isRemoved);
|
||||
r.removeAll();
|
||||
test.isTrue(b.isRemoved);
|
||||
|
||||
var c = new DomRange;
|
||||
var d = new DomRange;
|
||||
var e = new DomRange;
|
||||
c.add(d);
|
||||
d.add(e);
|
||||
r.add('c', c);
|
||||
test.isFalse(c.isRemoved);
|
||||
test.isFalse(d.isRemoved);
|
||||
test.isFalse(e.isRemoved);
|
||||
r.remove('c');
|
||||
test.isTrue(c.isRemoved);
|
||||
test.isTrue(d.isRemoved);
|
||||
test.isTrue(e.isRemoved);
|
||||
});
|
||||
});
|
||||
|
||||
// TO TEST STILL:
|
||||
// - external remove element
|
||||
// - double-add, double-remove
|
||||
|
||||
Reference in New Issue
Block a user