Prevent DOM node expandos _countedByPrototype and _prototypeEventID from being serialized into (inner|outer)HTML in IE. Closes #10909. [dcpedit, Tobie Langel, Andrew Dupont]

This commit is contained in:
Andrew Dupont 2008-01-25 18:49:40 +00:00
parent 794457da75
commit a1ec25cddb
5 changed files with 71 additions and 27 deletions

View File

@ -1,5 +1,7 @@
*SVN*
* Prevent DOM node expandos _countedByPrototype and _prototypeEventID from being serialized into (inner|outer)HTML in IE. Closes #10909. [dcpedit, Tobie Langel, Andrew Dupont]
* Make Ajax.Request#isSameOrigin JavaScript Lint compliant. [Andrew Dupont]
* Properly serialize form fields with names that collide with built-in JS properties (like "length" or "toString"). Closes #9609. [gryn, kangax]

View File

@ -128,11 +128,11 @@ Event.extend = (function() {
Object.extend(Event, (function() {
var cache = Event.cache;
function getEventID(element) {
if (element._eventID) return element._eventID;
if (element._prototypeEventID) return element._prototypeEventID[0];
arguments.callee.id = arguments.callee.id || 1;
return element._eventID = ++arguments.callee.id;
return element._prototypeEventID = [++arguments.callee.id];
}
function getDOMEventName(eventName) {

View File

@ -312,14 +312,15 @@ Object.extend(Selector, {
// marks an array of nodes for counting
mark: function(nodes) {
var _true = Prototype.emptyFunction;
for (var i = 0, node; node = nodes[i]; i++)
node._counted = true;
node._countedByPrototype = _true;
return nodes;
},
unmark: function(nodes) {
for (var i = 0, node; node = nodes[i]; i++)
node._counted = undefined;
node._countedByPrototype = undefined;
return nodes;
},
@ -327,15 +328,15 @@ Object.extend(Selector, {
// "ofType" flag indicates whether we're indexing for nth-of-type
// rather than nth-child
index: function(parentNode, reverse, ofType) {
parentNode._counted = true;
parentNode._countedByPrototype = Prototype.emptyFunction;
if (reverse) {
for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
var node = nodes[i];
if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
}
} else {
for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
}
},
@ -344,8 +345,8 @@ Object.extend(Selector, {
if (nodes.length == 0) return nodes;
var results = [], n;
for (var i = 0, l = nodes.length; i < l; i++)
if (!(n = nodes[i])._counted) {
n._counted = true;
if (!(n = nodes[i])._countedByPrototype) {
n._countedByPrototype = Prototype.emptyFunction;
results.push(Element.extend(n));
}
return Selector.handlers.unmark(results);
@ -547,7 +548,7 @@ Object.extend(Selector, {
var h = Selector.handlers, results = [], indexed = [], m;
h.mark(nodes);
for (var i = 0, node; node = nodes[i]; i++) {
if (!node.parentNode._counted) {
if (!node.parentNode._countedByPrototype) {
h.index(node.parentNode, reverse, ofType);
indexed.push(node.parentNode);
}
@ -585,7 +586,7 @@ Object.extend(Selector, {
var exclusions = new Selector(selector).findElements(root);
h.mark(exclusions);
for (var i = 0, results = [], node; node = nodes[i]; i++)
if (!node._counted) results.push(node);
if (!node._countedByPrototype) results.push(node);
h.unmark(exclusions);
return results;
},
@ -618,12 +619,20 @@ Object.extend(Selector, {
'~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
'|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
},
split: function(expression) {
var expressions = [];
expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
expressions.push(m[1].strip());
});
return expressions;
},
matchElements: function(elements, expression) {
var matches = $$(expression), h = Selector.handlers;
h.mark(matches);
for (var i = 0, results = [], element; element = elements[i]; i++)
if (element._counted) results.push(element);
if (element._countedByPrototype) results.push(element);
h.unmark(matches);
return results;
},
@ -636,11 +645,7 @@ Object.extend(Selector, {
},
findChildElements: function(element, expressions) {
var exprs = expressions.join(',');
expressions = [];
exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
expressions.push(m[1].strip());
});
expressions = Selector.split(expressions.join(','));
var results = [], h = Selector.handlers;
for (var i = 0, l = expressions.length, selector; i < l; i++) {
selector = new Selector(expressions[i].strip());
@ -651,13 +656,22 @@ Object.extend(Selector, {
});
if (Prototype.Browser.IE) {
// IE returns comment nodes on getElementsByTagName("*").
// Filter them out.
Selector.handlers.concat = function(a, b) {
for (var i = 0, node; node = b[i]; i++)
if (node.tagName !== "!") a.push(node);
return a;
};
Object.extend(Selector.handlers, {
// IE returns comment nodes on getElementsByTagName("*").
// Filter them out.
concat: function(a, b) {
for (var i = 0, node; node = b[i]; i++)
if (node.tagName !== "!") a.push(node);
return a;
},
// IE improperly serializes _countedByPrototype in (inner|outer)HTML.
unmark: function(nodes) {
for (var i = 0, node; node = nodes[i]; i++)
node.removeAttribute('_countedByPrototype');
return nodes;
}
});
}
function $$() {

View File

@ -26,6 +26,7 @@
<div id="outer" style="display: none">
<p id="inner">One two three <span id="span">four</span></p>
</div>
<div id="container"><div></div></div>
<!-- Tests follow -->
<script type="text/javascript" language="javascript" charset="utf-8">
@ -174,7 +175,7 @@
var span = $("span"), observer = function() { }, eventID;
span.observe("test:somethingHappened", observer);
eventID = span._eventID;
eventID = span._prototypeEventID;
assert(Event.cache[eventID]);
assert(Object.isArray(Event.cache[eventID]["test:somethingHappened"]));
@ -240,6 +241,12 @@
assertElementMatches(event.findElement('p'), 'p#inner');
assertEqual(null, event.findElement('div.does_not_exist'));
assertElementMatches(event.findElement('.does_not_exist, span'), 'span#span');
}},
testEventIDDuplication: function() { with(this) {
$('container').down().observe("test:somethingHappened", Prototype.emptyFunction);
$('container').innerHTML += $('container').innerHTML;
assertUndefined($('container').down(1)._prototypeEventID);
}}
});

View File

@ -88,7 +88,7 @@
<input type="hidden" id="commaChild" name="foo" value="#commaOne,#commaTwo" />
<input type="hidden" id="commaTwo" name="foo2" value="oops" />
</form>
<div id="counted_container"><div class="is_counted"></div></div>
</div> <!-- #fixtures -->
<!-- Log output -->
@ -442,8 +442,29 @@
assert(typeof results[0].show == 'function');
assert(typeof results[1].show == 'function');
assert(typeof results[2].show == 'function');
}},
testCountedIsNotAnAttribute: function() { with(this) {
var el = $('list');
Selector.handlers.mark([el]);
assert(!el.innerHTML.include("_counted"));
Selector.handlers.unmark([el]);
assert(!el.innerHTML.include("_counted"));
}},
testCopiedNodesGetIncluded: function(){ with(this) {
assertElementsMatch(
Selector.matchElements($('counted_container').descendants(), 'div'),
'div.is_counted'
);
$('counted_container').innerHTML += $('counted_container').innerHTML;
assertElementsMatch(
Selector.matchElements($('counted_container').descendants(), 'div'), 'div.is_counted',
'div.is_counted'
);
}}
});
// ]]>
</script>
</body>