Skip to content

Commit

Permalink
prototype: Make Element#insert standard-compliant. Fixes an issue in …
Browse files Browse the repository at this point in the history
…FF3b2 when inserting HTML or text inside DOM nodes which aren't (yet) appended to the document.
  • Loading branch information
tobie committed Jan 3, 2008
1 parent f553d22 commit b4735bc
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 79 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
*SVN*

* Make Element#insert standard-compliant. Fixes an issue in FF3b2 when inserting HTML or text inside DOM nodes which aren't (yet) appended to the document. [Tobie Langel]

* Add some missing semicolons to the source tree. Closes #10659. [Richard Quadling]

* Ensure Ajax.Response#getHeader returns null for missing headers in Opera. [Tobie Langel]
Expand Down
101 changes: 22 additions & 79 deletions src/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,24 +119,28 @@ Element.Methods = {
Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
insertions = {bottom:insertions};

var content, t, range;
var content, insert, tagName, childNodes;

for (position in insertions) {
content = insertions[position];
position = position.toLowerCase();
t = Element._insertionTranslations[position];
insert = Element._insertionTranslations[position];

if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) {
t.insert(element, content);
insert(element, content);
continue;
}

content = Object.toHTML(content);

range = element.ownerDocument.createRange();
t.initializeRange(element, range);
t.insert(element, range.createContextualFragment(content.stripScripts()));
tagName = ((position == 'before' || position == 'after')
? element.parentNode : element).tagName.toUpperCase();

childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());

if (position == 'top' || position == 'after') childNodes.reverse();
childNodes.each(insert.curry(element));

content.evalScripts.bind(content).defer();
}
Expand Down Expand Up @@ -663,46 +667,6 @@ Element._attributeTranslations = {
}
};


if (!document.createRange || Prototype.Browser.Opera) {
Element.Methods.insert = function(element, insertions) {
element = $(element);

if (Object.isString(insertions) || Object.isNumber(insertions) ||
Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
insertions = { bottom: insertions };

var t = Element._insertionTranslations, content, position, pos, tagName;

for (position in insertions) {
content = insertions[position];
position = position.toLowerCase();
pos = t[position];

if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) {
pos.insert(element, content);
continue;
}

content = Object.toHTML(content);
tagName = ((position == 'before' || position == 'after')
? element.parentNode : element).tagName.toUpperCase();

if (t.tags[tagName]) {
var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
if (position == 'top' || position == 'after') fragments.reverse();
fragments.each(pos.insert.curry(element));
}
else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());

content.evalScripts.bind(content).defer();
}

return element;
};
}

if (Prototype.Browser.Opera) {
Element.Methods.getStyle = Element.Methods.getStyle.wrap(
function(proceed, element, style) {
Expand Down Expand Up @@ -992,45 +956,25 @@ Element._returnOffset = function(l, t) {

Element._getContentFromAnonymousElement = function(tagName, html) {
var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
div.innerHTML = t[0] + html + t[1];
t[2].times(function() { div = div.firstChild });
if (t) {
div.innerHTML = t[0] + html + t[1];
t[2].times(function() { div = div.firstChild });
} else div.innerHTML = html;
return $A(div.childNodes);
};

Element._insertionTranslations = {
before: {
adjacency: 'beforeBegin',
insert: function(element, node) {
element.parentNode.insertBefore(node, element);
},
initializeRange: function(element, range) {
range.setStartBefore(element);
}
before: function(element, node) {
element.parentNode.insertBefore(node, element);
},
top: {
adjacency: 'afterBegin',
insert: function(element, node) {
element.insertBefore(node, element.firstChild);
},
initializeRange: function(element, range) {
range.selectNodeContents(element);
range.collapse(true);
}
top: function(element, node) {
element.insertBefore(node, element.firstChild);
},
bottom: {
adjacency: 'beforeEnd',
insert: function(element, node) {
element.appendChild(node);
}
bottom: function(element, node) {
element.appendChild(node);
},
after: {
adjacency: 'afterEnd',
insert: function(element, node) {
element.parentNode.insertBefore(node, element.nextSibling);
},
initializeRange: function(element, range) {
range.setStartAfter(element);
}
after: function(element, node) {
element.parentNode.insertBefore(node, element.nextSibling);
},
tags: {
TABLE: ['<table>', '</table>', 1],
Expand All @@ -1042,7 +986,6 @@ Element._insertionTranslations = {
};

(function() {
this.bottom.initializeRange = this.top.initializeRange;
Object.extend(this.tags, {
THEAD: this.tags.TBODY,
TFOOT: this.tags.TBODY,
Expand Down
16 changes: 16 additions & 0 deletions test/unit/dom.html
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,22 @@ <h1>Prototype Unit test file</h1>
assertEqual('test1337', getInnerHTML('element-insertions-main'));
}},

testNewElementInsert: function() {with(this) {
var container = new Element('div');
element = new Element('div');
container.insert(element);

element.insert({ before: '<p>a paragraph</p>' });
assertEqual('<p>a paragraph</p><div></div>', getInnerHTML(container));
element.insert({ after: 'some text' });
assertEqual('<p>a paragraph</p><div></div>some text', getInnerHTML(container));

element.insert({ top: '<p>a paragraph</p>' });
assertEqual('<p>a paragraph</p>', getInnerHTML(element));
element.insert('some text');
assertEqual('<p>a paragraph</p>some text', getInnerHTML(element));
}},

testInsertionBackwardsCompatibility: function() {with(this) {
new Insertion.Before('element-insertions-main', 'some backward-compatibility testing before');
assert(getInnerHTML('element-insertions-container').include('some backward-compatibility testing before'));
Expand Down

0 comments on commit b4735bc

Please sign in to comment.