diff --git a/js/core/utils.js b/js/core/utils.js index 0b4563d352..59c793aec4 100644 --- a/js/core/utils.js +++ b/js/core/utils.js @@ -14,6 +14,35 @@ define( msg.pub("end", "core/utils"); cb(); } + , + /** + * Allows a node to be swapped into a different document at + * some insertion point(Element). This function is useful for + * opportunistic insertion of DOM nodes into a document, without + * first knowing if that is the final document where the node will + * reside. + * + * @param {Node} node The node to be swapped. + * @return {Function} A function that takes a document and a new + * insertion point (element). When called, + * node gets inserted into doc at before a given + * insertion point (node) - or just appended, if + * the element has no children. + */ + makeOwnerSwapper: function(node) { + if(!(node instanceof Node)){ + throw TypeError(); + } + return function(doc, insertionPoint) { + node.remove(); + doc.adoptNode(node); + if (insertionPoint.firstElementChild) { + insertionPoint.insertBefore(node, insertionPoint.firstElementChild); + return; + } + insertionPoint.appendChild(node); + }; + } , calculateLeftPad: function(text) { var spaceOrTab = /^[\ |\t]*/; // Find smallest padding value @@ -45,7 +74,7 @@ define( return { value: nextLikeFunction(), get done() { - return this.value === null + return this.value === null; } }; }; diff --git a/tests/spec/core/utils-spec.js b/tests/spec/core/utils-spec.js index 1bca553616..f2d0c72df1 100644 --- a/tests/spec/core/utils-spec.js +++ b/tests/spec/core/utils-spec.js @@ -8,6 +8,66 @@ describe("Core - Utils", function() { }); }); + describe("makeOwnerSwapper()", function() { + it("throws if passed something that is not a node", function(done) { + expect(function() { + utils.toESIterable(function() {}); + }).not.toThrow(); + expect(function() { + utils.toESIterable(""); + }).toThrow(); + expect(function() { + utils.toESIterable(null); + }).toThrow(); + expect(function() { + utils.toESIterable([]); + }).toThrow(); + expect(function() { + utils.toESIterable(undefined); + }).toThrow(); + done(); + }); + + it("returns a function", function() { + var testNode = document.createTextNode("test"); + var testFunction = utils.makeOwnerSwapper(testNode); + expect(testFunction instanceof Function).toBe(true); + }); + + it("removes the original node from the its owner document", function() { + var testNode = document.createTextNode("test"); + var swapTestNode = utils.makeOwnerSwapper(testNode); + var newDoc = document.implementation.createHTMLDocument("test"); + document.body.appendChild(testNode); + expect(document.body.contains(testNode)).toBe(true); + swapTestNode(newDoc, newDoc.body); + expect(document.body.contains(testNode)).toBe(false); + expect(testNode.ownerDocument).toEqual(newDoc); + }); + + it("appends the node into a new document", function() { + var testNode = document.createElement("link"); + var swapTestNode = utils.makeOwnerSwapper(testNode); + var newDoc = document.implementation.createHTMLDocument("test"); + expect(document.head.contains(testNode)).toBe(false); + swapTestNode(newDoc, newDoc.head); + expect(newDoc.head.contains(testNode)).toBe(true); + expect(testNode.ownerDocument).toEqual(newDoc); + }); + + it("prepends the node into a new document at the right place", function() { + var testNode = document.createElement("link"); + var swapTestNode = utils.makeOwnerSwapper(testNode); + var newDoc = document.implementation.createHTMLDocument("test"); + var metaElem = newDoc.createElement("meta"); + newDoc.head.appendChild(metaElem); + swapTestNode(newDoc, newDoc.head); + expect(newDoc.head.firstChild).toEqual(testNode); + expect(newDoc.head.lastChild).toEqual(metaElem); + }); + }); + + describe("toESIterable() method", function() { it("throws if passed something that is not a function", function(done) {