DomRange handles tbody quirk

This isn't really needed for IE 8+ except for
jQuery compatibility.
This commit is contained in:
David Greenspan
2013-08-28 15:12:55 -07:00
parent fee94a706d
commit 9062fa1024
2 changed files with 166 additions and 1 deletions

View File

@@ -201,6 +201,10 @@ _extend(DomRange.prototype, {
var range = newMember.dom;
range.owner = this.component;
var nodes = range.getNodes();
if (tbodyFixNeeded(nodes, parentNode))
parentNode = moveWithOwnersIntoTbody(this);
for (var i = 0; i < nodes.length; i++)
insertNode(nodes[i], parentNode, nextNode);
} else {
@@ -210,6 +214,10 @@ _extend(DomRange.prototype, {
var node = newMember;
if (node.nodeType !== 3)
node.$ui = this.component;
if (tbodyFixNeeded(node, parentNode))
parentNode = moveWithOwnersIntoTbody(this);
insertNode(node, parentNode, nextNode);
}
},
@@ -580,8 +588,66 @@ DomRange.insert = function (component, parentNode, nextNode) {
if (! range)
throw new Error("Expected a component with a DomRange");
var nodes = range.getNodes();
if (tbodyFixNeeded(nodes, parentNode))
parentNode = makeOrFindTbody(parentNode, nextNode);
for (var i = 0; i < nodes.length; i++)
insertNode(nodes[i], parentNode, nextNode);
};
UI.DomRange = DomRange;
///// TBODY FIX for compatibility with jQuery.
//
// Because people might use jQuery from UI hooks, and
// jQuery is unable to do $(myTable).append(myTR) without
// adding a TBODY (for historical reasons), we move any DomRange
// that gains a TR, and its immediately enclosing DomRanges,
// into a TBODY.
//
// See http://www.quora.com/David-Greenspan/Posts/The-Great-TBODY-Debacle
var tbodyFixNeeded = function (childOrChildren, parent) {
if (parent.nodeName !== 'TABLE')
return false;
if (isArray(childOrChildren)) {
var foundTR = false;
for (var i = 0, N = childOrChildren.length; i < N; i++) {
var n = childOrChildren[i];
if (n.nodeType === 1 && n.nodeName === 'TR') {
foundTR = true;
break;
}
}
if (! foundTR)
return false;
} else {
var n = childOrChildren;
if (! (n.nodeType === 1 && n.nodeName === 'TR'))
return false;
}
return true;
};
var makeOrFindTbody = function (parent, next) {
// we have a TABLE > TR situation
var tbody = parent.getElementsByTagName('tbody')[0];
if (! tbody) {
tbody = parent.ownerDocument.createElement("tbody");
parent.insertBefore(tbody, next || null);
}
return tbody;
};
var moveWithOwnersIntoTbody = function (range) {
while (range.owner)
range = range.owner.dom;
var nodes = range.getNodes(); // causes refresh
var tbody = makeOrFindTbody(range.parentNode(),
range.end.nextSibling);
for (var i = 0; i < nodes.length; i++)
tbody.appendChild(nodes[i]);
return tbody;
};
UI.DomRange = DomRange;

View File

@@ -468,6 +468,105 @@ Tinytest.add("ui - DomRange - external moves", function (test) {
strip('-------------- (aXb ghiWjkl)'));
});
Tinytest.add("ui - DomRange - tables", function (test) {
var range = function (x) {
new DomRange(x);
if (x.el) {
x.dom.add(x.el);
if (x.content)
DomRange.insert(x.content, x.el);
}
return x;
};
var tr, td;
var table = range({
el: document.createElement('table'),
content: tr = range({
el: document.createElement('tr'),
content: td = range({
el: document.createElement('td')
})
})
});
// TBODY got inserted automatically.
// This tests DomRange.insert.
test.equal(table.el.childNodes.length, 1);
test.equal(table.el.firstChild.nodeName, 'TBODY');
// TBODY contains [start, TR, end]
test.equal(table.el.firstChild.childNodes.length, 3);
test.equal(table.el.firstChild.childNodes[1], tr.el);
test.equal(tr.el.childNodes.length, 3);
test.equal(tr.el.childNodes[1], td.el);
// start over
$(table.el).empty();
test.equal(table.el.childNodes.length, 0);
table.content = range({});
DomRange.insert(table.content, 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);
test.notEqual(table.el.lastChild.nodeType, 1);
// shazam, adding a TR should move the whole range
// into a TBODY. This tests range.add(node).
table.content.dom.add(document.createElement('tr'));
test.equal(table.el.childNodes.length, 1);
test.equal(table.el.firstChild.nodeName, 'TBODY');
test.equal(table.el.firstChild.childNodes.length, 3);
test.equal(table.el.firstChild.childNodes[1].nodeName, 'TR');
// start over.
$(table.el).empty();
test.equal(table.el.childNodes.length, 0);
table.content = range({});
DomRange.insert(table.content, table.el);
var a1 = range({});
var a2 = range({});
a1.dom.add(a2);
table.content.dom.add(a1);
// 6 marker nodes in table, no elements
test.equal(table.el.childNodes.length, 6);
test.equal($(table.el).find("*").length, 0);
// shazam, adding a TR to the innermost range
// should move all the ranges into a TBODY.
a2.dom.add(document.createElement('tr'));
test.equal(table.el.childNodes.length, 1);
test.equal(table.el.firstChild.nodeName, 'TBODY');
test.equal(table.el.firstChild.childNodes.length, 7);
test.equal(table.el.firstChild.childNodes[3].nodeName, 'TR');
// start over. this time test adding a range containing
// a TR.
$(table.el).empty();
test.equal(table.el.childNodes.length, 0);
table.content = range({});
DomRange.insert(table.content, table.el);
var a1 = range({});
var a2 = range({});
table.content.dom.add(a1);
a2.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 a2, which contains a TR,
// should move all the ranges into a TBODY.
a1.dom.add(a2);
test.equal(table.el.childNodes.length, 1);
test.equal(table.el.firstChild.nodeName, 'TBODY');
test.equal(table.el.firstChild.childNodes.length, 7);
test.equal(table.el.firstChild.childNodes[3].nodeName, 'TR');
test.equal(a2.dom.parentNode().nodeName, 'TBODY');
test.equal(a1.dom.parentNode().nodeName, 'TBODY');
test.equal(table.content.dom.parentNode().nodeName, 'TBODY');
});
// TO TEST STILL:
// - external remove element