diff --git a/CHANGELOG b/CHANGELOG index dfe1afe..d0325dc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +* Fix another failure in IE8, `for`/`htmlFor` {get/set}Attribute translation. (kangax) + +* Fix `Element#writeAttribute` and `Element#readAttribute` failures in IE8 due to lack of proper feature testing. (kangax) + +* Remove sniffing from one of the DOM tests, which produced failures in IE8. (kangax) + +* Fix `Form.reset` test where `respondsTo` wouldn't detect a method due to typeof returning "object" (rather than "function") in IE (kangax) + +* Remove Array#reduce which currently overrides native `reduce` in clients implementing JS1.8, e.g. Firefox 3+ (Tobie Langel, Andrew Dupont, kangax) + * Make sure try/catch/finally is used instead of try/finally for clients without support for the latter one (e.g. Blackberry, IE) (Ville Koskinen, kangax) * Use `in` operator when accessing property of a nodelist to prevent Safari <=2.0.4 from crashing (kangax) diff --git a/src/dom/dom.js b/src/dom/dom.js index b293299..1ea6001 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -1169,7 +1169,7 @@ Element._attributeTranslations = { write: { names: { className: 'class', - htmlFor: 'for' + htmlFor: 'for' }, values: { } } @@ -1306,42 +1306,95 @@ else if (Prototype.Browser.IE) { return element; }; - Element._attributeTranslations = { - read: { - names: { - 'class': 'className', - 'for': 'htmlFor' - }, - values: { - _getAttr: function(element, attribute) { - return element.getAttribute(attribute, 2); + Element._attributeTranslations = (function(){ + + var classProp = 'className'; + var forProp = 'for'; + + var el = document.createElement('div'); + + // try "className" first (IE <8) + el.setAttribute(classProp, 'x'); + + if (el.className !== 'x') { + // try "class" (IE 8) + el.setAttribute('class', 'x'); + if (el.className === 'x') { + classProp = 'class'; + } + } + el = null; + + el = document.createElement('label'); + el.setAttribute(forProp, 'x'); + if (el.htmlFor !== 'x') { + el.setAttribute('htmlFor', 'x'); + if (el.htmlFor === 'x') { + forProp = 'htmlFor'; + } + } + el = null; + + return { + read: { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp }, - _getAttrNode: function(element, attribute) { - var node = element.getAttributeNode(attribute); - return node ? node.value : ""; - }, - _getEv: function(element, attribute) { - attribute = element.getAttribute(attribute); - - // TODO: Need something less ugly here. - if (!attribute) return null; - attribute = attribute.toString(); - attribute = attribute.split('{')[1]; - attribute = attribute.split('}')[0]; - return attribute.strip(); - }, - _flag: function(element, attribute) { - return $(element).hasAttribute(attribute) ? attribute : null; - }, - style: function(element) { - return element.style.cssText.toLowerCase(); - }, - title: function(element) { - return element.title; + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: (function(){ + + var el = document.createElement('div'); + el.onclick = Prototype.emptyFunction; + var value = el.getAttribute('onclick'); + var f; + + // IE<8 + if (String(value).indexOf('{') > -1) { + // intrinsic event attributes are serialized as `function { ... }` + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + attribute = attribute.toString(); + attribute = attribute.split('{')[1]; + attribute = attribute.split('}')[0]; + return attribute.strip(); + } + } + // IE8 + else if (value === '') { + // only function body is serialized + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + return attribute.strip(); + } + } + el = null; + return f; + })(), + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } } } } - }; + })(); Element._attributeTranslations.write = { names: Object.extend({ @@ -1887,4 +1940,4 @@ Element.addMethods({ } return Element.extend(clone); } -}); +}); \ No newline at end of file diff --git a/src/lang/array.js b/src/lang/array.js index 9fc4e05..8d6e1d4 100644 --- a/src/lang/array.js +++ b/src/lang/array.js @@ -206,15 +206,6 @@ Array.from = $A; return (inline !== false ? this : this.toArray())._reverse(); } - /** - * Array#reduce() -> Array - * Reduces arrays: one-element arrays are turned into their unique item, - * while multiple-element arrays are returned untouched. - **/ - function reduce() { - return this.length > 1 ? this : this[0]; - } - /** * Array#uniq([sorted = false]) -> Array * - sorted (Boolean): Whether the array has already been sorted. If `true`, @@ -353,7 +344,6 @@ Array.from = $A; flatten: flatten, without: without, reverse: reverse, - reduce: reduce, uniq: uniq, intersect: intersect, clone: clone, diff --git a/test/unit/array_test.js b/test/unit/array_test.js index 88d8fad..d090504 100644 --- a/test/unit/array_test.js +++ b/test/unit/array_test.js @@ -137,14 +137,6 @@ new Test.Unit.Runner({ this.assertEqual('[\"a\", 1]', ['a', 1].toJSON()); this.assertEqual('[\"a\", {\"b\": null}]', ['a', {'b': null}].toJSON()); }, - - testReduce: function(){ - this.assertUndefined([].reduce()); - this.assertNull([null].reduce()); - this.assertEqual(1, [1].reduce()); - this.assertEnumEqual([1,2,3], [1,2,3].reduce()); - this.assertEnumEqual([1,null,3], [1,null,3].reduce()); - }, testReverse: function(){ this.assertEnumEqual([], [].reverse()); diff --git a/test/unit/dom_test.js b/test/unit/dom_test.js index 7fbe072..b351340 100644 --- a/test/unit/dom_test.js +++ b/test/unit/dom_test.js @@ -73,7 +73,15 @@ new Test.Unit.Runner({ this.assertElementsMatch(document.getElementsByClassName('A'), 'p.A', 'ul#class_names_ul.A', 'li.A.C'); - if (Prototype.Browser.IE) + + var isElementPrototypeSupported = (function(){ + var el = document.createElement('div'); + var result = typeof el.show != 'undefined'; + el = null; + return result; + })(); + + if (!isElementPrototypeSupported) this.assertUndefined(document.getElementById('unextended').show); this.assertElementsMatch(div.getElementsByClassName('B'), 'ul#class_names_ul.A.B', 'div.B.C.D'); @@ -95,7 +103,8 @@ new Test.Unit.Runner({ this.assertElementsMatch(list.getElementsByClassName({})); // those lookups shouldn't have extended all nodes in document - if (Prototype.Browser.IE) this.assertUndefined(document.getElementById('unextended')['show']); + if (!isElementPrototypeSupported) + this.assertUndefined(document.getElementById('unextended')['show']); }, testElementInsertWithHTML: function() { diff --git a/test/unit/form_test.js b/test/unit/form_test.js index 38a4666..f3ae538 100644 --- a/test/unit/form_test.js +++ b/test/unit/form_test.js @@ -27,7 +27,7 @@ new Test.Unit.Runner({ }, testFormReset: function() { - this.assertRespondsTo('reset', Form.reset('form')); + this.assert(!Object.isUndefined(Form.reset('form').reset)); }, testFormElementEventObserver: function(){ diff --git a/test/unit/selector_test.js b/test/unit/selector_test.js index 5501eed..9ec3c34 100644 --- a/test/unit/selector_test.js +++ b/test/unit/selector_test.js @@ -1,5 +1,9 @@ var $RunBenchmarks = false; +function reduce(arr) { + return arr.length > 1 ? arr : arr[0]; +} + new Test.Unit.Runner({ testSelectorWithTagName: function() { @@ -193,12 +197,12 @@ new Test.Unit.Runner({ testSelectorWithAdjacence: function() { this.assertEnumEqual([$('uncle')], $$('div.brothers + div.brothers')); this.assertEnumEqual([$('uncle')], $$('div.brothers + div')); - this.assertEqual($('level2_2'), $$('#level2_1+span').reduce()); - this.assertEqual($('level2_2'), $$('#level2_1 + span').reduce()); - this.assertEqual($('level2_2'), $$('#level2_1 + *').reduce()); + this.assertEqual($('level2_2'), reduce($$('#level2_1+span'))); + this.assertEqual($('level2_2'), reduce($$('#level2_1 + span'))); + this.assertEqual($('level2_2'), reduce($$('#level2_1 + *'))); this.assertEnumEqual([], $$('#level2_2 + span')); - this.assertEqual($('level3_2'), $$('#level3_1 + span').reduce()); - this.assertEqual($('level3_2'), $$('#level3_1 + *').reduce()); + this.assertEqual($('level3_2'), reduce($$('#level3_1 + span'))); + this.assertEqual($('level3_2'), reduce($$('#level3_1 + *'))); this.assertEnumEqual([], $$('#level3_2 + *')); this.assertEnumEqual([], $$('#level3_1 + em')); $RunBenchmarks && this.wait(500, function() { @@ -208,8 +212,8 @@ new Test.Unit.Runner({ testSelectorWithLaterSibling: function() { this.assertEnumEqual([$('list')], $$('h1 ~ ul')); - this.assertEqual($('level2_2'), $$('#level2_1 ~ span').reduce()); - this.assertEnumEqual($('level2_2', 'level2_3'), $$('#level2_1 ~ *').reduce()); + this.assertEqual($('level2_2'), reduce($$('#level2_1 ~ span'))); + this.assertEnumEqual($('level2_2', 'level2_3'), reduce($$('#level2_1 ~ *'))); this.assertEnumEqual([], $$('#level2_2 ~ span')); this.assertEnumEqual([], $$('#level3_2 ~ *')); this.assertEnumEqual([], $$('#level3_1 ~ em'));