From 10f49e48f8663db848a455600830f7dfecb8f214 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 14:48:33 +0200 Subject: [PATCH 01/12] makeArray: consider array-like only: array, jquery, nodelist, arguments, form element. Fixes #8104. --- src/core.js | 58 +++++++++++++++++++++++++++++++++++++---------- test/unit/core.js | 10 ++++---- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/core.js b/src/core.js index 056fb88fbb..d1c8dbebb0 100644 --- a/src/core.js +++ b/src/core.js @@ -646,18 +646,27 @@ jQuery.extend({ makeArray: function( array, results ) { var ret = results || []; - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // The extra typeof function check is to prevent crashes - // in Safari 2 (See: #3039) - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - var type = jQuery.type( array ); - - if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { - push.call( ret, array ); - } else { - jQuery.merge( ret, array ); - } + if ( array == null ) { + return ret; + } + + var type = jQuery.type( array ); + + if ( array.length != null && ( array instanceof jQuery || type === "object" && !jQuery.isWindow( array ) && ( + // form - is it really needed? + array.nodeType && /form/i.test( array.nodeName ) || + // NodeList + array.item && ( array.namedItem || jQuery.isFunction( array.item ) ) || + // iframe jQuery + array.jquery && !jQuery.isPlainObject( array ) ) ) + ) { + jQuery.merge( ret, array ); + + } else if ( type === "array" || isArguments( array ) ) { + push.apply( ret, array ); + + } else { + push.call( ret, array ); } return ret; @@ -909,6 +918,31 @@ function doScrollCheck() { jQuery.ready(); } +var isArguments = (function( undefined ) { + + var ostr = ({}).toString, + ARGS = ostr.call( arguments ); + + return ARGS === "[object Arguments]" ? + function( obj ) { + return obj != null && ostr.call( obj ) === ARGS; + } : + function( obj ) { + if ( obj == null || obj.length === undefined || ostr.call( obj ) !== ARGS ) { + return false; + } + + try { + var arr = []; + arr.push.apply( arr, obj ); + return true; + + } catch (e) { + return false; + } + }; +})(); + // Expose jQuery to the global object return jQuery; diff --git a/test/unit/core.js b/test/unit/core.js index 75d3e0e2c1..5ce442f88d 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -867,7 +867,7 @@ test("jQuery.each(Object,Function)", function() { f[i] = "baz"; }); equals( "baz", f.foo, "Loop over a function" ); - + var stylesheet_count = 0; jQuery.each(document.styleSheets, function(i){ stylesheet_count++; @@ -877,7 +877,7 @@ test("jQuery.each(Object,Function)", function() { }); test("jQuery.makeArray", function(){ - expect(17); + expect(14); equals( jQuery.makeArray(jQuery("html>*"))[0].nodeName.toUpperCase(), "HEAD", "Pass makeArray a jQuery object" ); @@ -897,7 +897,7 @@ test("jQuery.makeArray", function(){ equals( jQuery.makeArray( document.createElement("div") )[0].nodeName.toUpperCase(), "DIV", "Pass makeArray a single node" ); - equals( jQuery.makeArray( {length:2, 0:"a", 1:"b"} ).join(""), "ab", "Pass makeArray an array like map (with length)" ); + //equals( jQuery.makeArray( {length:2, 0:"a", 1:"b"} ).join(""), "ab", "Pass makeArray an array like map (with length)" ); ok( !!jQuery.makeArray( document.documentElement.childNodes ).slice(0,1)[0].nodeName, "Pass makeArray a childNodes array" ); @@ -912,8 +912,8 @@ test("jQuery.makeArray", function(){ ok( jQuery.makeArray(document.getElementById("form")).length >= 13, "Pass makeArray a form (treat as elements)" ); // For #5610 - same( jQuery.makeArray({length: "0"}), [], "Make sure object is coerced properly."); - same( jQuery.makeArray({length: "5"}), [], "Make sure object is coerced properly."); + //same( jQuery.makeArray({length: "0"}), [], "Make sure object is coerced properly."); + //same( jQuery.makeArray({length: "5"}), [], "Make sure object is coerced properly."); }); test("jQuery.isEmptyObject", function(){ From dc23f08669fd0f01d0738bf7dd97ce1e24f3ebf0 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 15:28:32 +0200 Subject: [PATCH 02/12] Minor optimization in isArguments. --- src/core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core.js b/src/core.js index d1c8dbebb0..01509983a4 100644 --- a/src/core.js +++ b/src/core.js @@ -921,6 +921,7 @@ function doScrollCheck() { var isArguments = (function( undefined ) { var ostr = ({}).toString, + noop = function() { }, ARGS = ostr.call( arguments ); return ARGS === "[object Arguments]" ? @@ -933,8 +934,7 @@ var isArguments = (function( undefined ) { } try { - var arr = []; - arr.push.apply( arr, obj ); + noop.apply( this, obj ); return true; } catch (e) { From 247682ed5b05ad438f3f572d5829adb9b99c3d51 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 15:32:34 +0200 Subject: [PATCH 03/12] Reuse existing toString. --- src/core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.js b/src/core.js index 01509983a4..68da056ec6 100644 --- a/src/core.js +++ b/src/core.js @@ -920,7 +920,7 @@ function doScrollCheck() { var isArguments = (function( undefined ) { - var ostr = ({}).toString, + var ostr = toString, noop = function() { }, ARGS = ostr.call( arguments ); From 26a2c65ef8232e4b2cf693186452e50aa1050e88 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 15:39:01 +0200 Subject: [PATCH 04/12] Added some test for #8104. --- test/unit/core.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/unit/core.js b/test/unit/core.js index 5ce442f88d..f5ef1b4dc1 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -877,7 +877,7 @@ test("jQuery.each(Object,Function)", function() { }); test("jQuery.makeArray", function(){ - expect(14); + expect(16); equals( jQuery.makeArray(jQuery("html>*"))[0].nodeName.toUpperCase(), "HEAD", "Pass makeArray a jQuery object" ); @@ -914,6 +914,14 @@ test("jQuery.makeArray", function(){ // For #5610 //same( jQuery.makeArray({length: "0"}), [], "Make sure object is coerced properly."); //same( jQuery.makeArray({length: "5"}), [], "Make sure object is coerced properly."); + + // For #8104 + function Klass( n ) { + this.length = n; + } + equals( jQuery.makeArray( new Klass(0) ).length, 1, "Make sure class instances are not considered arrays" ); + equals( jQuery.makeArray( new Klass(3) ).length, 1, "Make sure class instances are not considered arrays" ); + }); test("jQuery.isEmptyObject", function(){ From 314ed3727e9bd610f5ae5bd9c5a8b0546662cc7b Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 15:54:28 +0200 Subject: [PATCH 05/12] Reduced size of isArguments. --- src/core.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/core.js b/src/core.js index 68da056ec6..c4ae418ebb 100644 --- a/src/core.js +++ b/src/core.js @@ -929,17 +929,13 @@ var isArguments = (function( undefined ) { return obj != null && ostr.call( obj ) === ARGS; } : function( obj ) { - if ( obj == null || obj.length === undefined || ostr.call( obj ) !== ARGS ) { - return false; - } - - try { - noop.apply( this, obj ); - return true; - - } catch (e) { - return false; + if ( obj != null && obj.length !== undefined && ostr.call( obj ) === ARGS ) { + try { + noop.apply( this, obj ); + return true; + } catch (e) {} } + return false; }; })(); From fed86b2296eaae8866c16790becea4e0b910b617 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 21:06:24 +0200 Subject: [PATCH 06/12] Make isArguments more future-proof. --- src/core.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core.js b/src/core.js index c4ae418ebb..ac0366f4e3 100644 --- a/src/core.js +++ b/src/core.js @@ -921,7 +921,8 @@ function doScrollCheck() { var isArguments = (function( undefined ) { var ostr = toString, - noop = function() { }, + // To be sure it will be colled (future engines). + test = function() { return true }, ARGS = ostr.call( arguments ); return ARGS === "[object Arguments]" ? @@ -931,8 +932,7 @@ var isArguments = (function( undefined ) { function( obj ) { if ( obj != null && obj.length !== undefined && ostr.call( obj ) === ARGS ) { try { - noop.apply( this, obj ); - return true; + return test.apply( this, obj ); } catch (e) {} } return false; From 26568bf164a182f6fa689005036231a0c0533c1f Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 21:11:48 +0200 Subject: [PATCH 07/12] isArguments: Typo fix in comment, and renamed a var for clarity- --- src/core.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core.js b/src/core.js index ac0366f4e3..349af1c940 100644 --- a/src/core.js +++ b/src/core.js @@ -921,8 +921,8 @@ function doScrollCheck() { var isArguments = (function( undefined ) { var ostr = toString, - // To be sure it will be colled (future engines). - test = function() { return true }, + // To be sure it will be called (future engines). + returnTrue = function() { return true }, ARGS = ostr.call( arguments ); return ARGS === "[object Arguments]" ? @@ -932,7 +932,7 @@ var isArguments = (function( undefined ) { function( obj ) { if ( obj != null && obj.length !== undefined && ostr.call( obj ) === ARGS ) { try { - return test.apply( this, obj ); + return returnTrue.apply( this, obj ); } catch (e) {} } return false; From 5a0160d937f03da0abe1357dd10f651be4c28328 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sat, 30 Apr 2011 21:20:33 +0200 Subject: [PATCH 08/12] Made isArguments even more future-proof. --- src/core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.js b/src/core.js index 349af1c940..656734eee3 100644 --- a/src/core.js +++ b/src/core.js @@ -922,7 +922,7 @@ var isArguments = (function( undefined ) { var ostr = toString, // To be sure it will be called (future engines). - returnTrue = function() { return true }, + returnTrue = function() { return arguments !== undefined; }, ARGS = ostr.call( arguments ); return ARGS === "[object Arguments]" ? From d97ca60273b33db5482491c62a2e20f49b999327 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sun, 1 May 2011 00:15:30 +0200 Subject: [PATCH 09/12] Made comment in isArgument more clear. --- src/core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.js b/src/core.js index 656734eee3..7b4d09c898 100644 --- a/src/core.js +++ b/src/core.js @@ -921,7 +921,7 @@ function doScrollCheck() { var isArguments = (function( undefined ) { var ostr = toString, - // To be sure it will be called (future engines). + // To be sure it will not be inlined (future engines). returnTrue = function() { return arguments !== undefined; }, ARGS = ostr.call( arguments ); From d9aee03890a1d6bcf4f8753b61292cae9f225754 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sun, 1 May 2011 18:53:55 +0200 Subject: [PATCH 10/12] makeArray: minimized toString.call calls. Replaced isArguments with argsRudeCheck. --- src/core.js | 67 +++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/core.js b/src/core.js index 7b4d09c898..2d67748ceb 100644 --- a/src/core.js +++ b/src/core.js @@ -65,7 +65,23 @@ var jQuery = function( selector, context ) { indexOf = Array.prototype.indexOf, // [[Class]] -> type pairs - class2type = {}; + class2type = {}, + + // FREE IF PULL #366 WILL BE MERGED + argsRudeCheck = (function(){ + var ARGS = toString.call( arguments ), + // To be sure it will not be inlined (future engines). + returnTrue = function() { return arguments !== 0; }; + + return function( cls, obj ) { + if ( cls === ARGS ) { + try { + return returnTrue.apply( this, obj ); + } catch (e) {} + } + return false; + }; + })(); jQuery.fn = jQuery.prototype = { constructor: jQuery, @@ -648,23 +664,29 @@ jQuery.extend({ if ( array == null ) { return ret; + + } else if ( array.length == null ) { + push.call( ret, array ); + return ret; } - var type = jQuery.type( array ); + var cls = toString.call( array ); + + if ( cls === "[object Array]" || cls === "[object Arguments]" ) { + push.apply( ret, array ); - if ( array.length != null && ( array instanceof jQuery || type === "object" && !jQuery.isWindow( array ) && ( - // form - is it really needed? - array.nodeType && /form/i.test( array.nodeName ) || + } else if ( array instanceof jQuery || !( cls in class2type ) && !jQuery.isWindow( array ) && ( + // form,.. + array.nodeType || // NodeList array.item && ( array.namedItem || jQuery.isFunction( array.item ) ) || - // iframe jQuery - array.jquery && !jQuery.isPlainObject( array ) ) ) + // jQuery-like + array.jquery && !jQuery.isPlainObject( array ) || + // arguments + argsRudeCheck( cls, array ) ) ) { jQuery.merge( ret, array ); - } else if ( type === "array" || isArguments( array ) ) { - push.apply( ret, array ); - } else { push.call( ret, array ); } @@ -857,8 +879,8 @@ jQuery.extend({ browser: {} }); -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { +// Populate the class2type map. Don't add Object! +jQuery.each("Boolean Number String Function Array Date RegExp".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); @@ -918,27 +940,6 @@ function doScrollCheck() { jQuery.ready(); } -var isArguments = (function( undefined ) { - - var ostr = toString, - // To be sure it will not be inlined (future engines). - returnTrue = function() { return arguments !== undefined; }, - ARGS = ostr.call( arguments ); - - return ARGS === "[object Arguments]" ? - function( obj ) { - return obj != null && ostr.call( obj ) === ARGS; - } : - function( obj ) { - if ( obj != null && obj.length !== undefined && ostr.call( obj ) === ARGS ) { - try { - return returnTrue.apply( this, obj ); - } catch (e) {} - } - return false; - }; -})(); - // Expose jQuery to the global object return jQuery; From 2d7b4cf2d7556cddab64ed5ff61ea40d07e0fefe Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Sun, 1 May 2011 22:12:34 +0200 Subject: [PATCH 11/12] argsRudeCheck: Testing for the callee presence. --- src/core.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core.js b/src/core.js index 2d67748ceb..6e1c35a6f4 100644 --- a/src/core.js +++ b/src/core.js @@ -67,14 +67,16 @@ var jQuery = function( selector, context ) { // [[Class]] -> type pairs class2type = {}, - // FREE IF PULL #366 WILL BE MERGED + // **** FREE IF PULL #366 WILL BE MERGED **** argsRudeCheck = (function(){ - var ARGS = toString.call( arguments ), + var returnTrue = function() { // To be sure it will not be inlined (future engines). - returnTrue = function() { return arguments !== 0; }; + return arguments !== 0; + }; - return function( cls, obj ) { - if ( cls === ARGS ) { + return function( obj ) { + // Using "in" works in strict mode too. + if ( "callee" in obj ) { try { return returnTrue.apply( this, obj ); } catch (e) {} @@ -82,6 +84,7 @@ var jQuery = function( selector, context ) { return false; }; })(); + ////////////////////////////////////////////// jQuery.fn = jQuery.prototype = { constructor: jQuery, @@ -683,7 +686,7 @@ jQuery.extend({ // jQuery-like array.jquery && !jQuery.isPlainObject( array ) || // arguments - argsRudeCheck( cls, array ) ) + argsRudeCheck( array ) ) ) { jQuery.merge( ret, array ); From 9efade095f7d424e4c39c2d697c5e5c065fc8b80 Mon Sep 17 00:00:00 2001 From: Robert Katic Date: Wed, 4 May 2011 01:50:20 +0200 Subject: [PATCH 12/12] Remuved argsRudeCheck in favor of the simply callee check. --- src/core.js | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/core.js b/src/core.js index 6e1c35a6f4..f527341bae 100644 --- a/src/core.js +++ b/src/core.js @@ -65,26 +65,7 @@ var jQuery = function( selector, context ) { indexOf = Array.prototype.indexOf, // [[Class]] -> type pairs - class2type = {}, - - // **** FREE IF PULL #366 WILL BE MERGED **** - argsRudeCheck = (function(){ - var returnTrue = function() { - // To be sure it will not be inlined (future engines). - return arguments !== 0; - }; - - return function( obj ) { - // Using "in" works in strict mode too. - if ( "callee" in obj ) { - try { - return returnTrue.apply( this, obj ); - } catch (e) {} - } - return false; - }; - })(); - ////////////////////////////////////////////// + class2type = {}; jQuery.fn = jQuery.prototype = { constructor: jQuery, @@ -681,12 +662,12 @@ jQuery.extend({ } else if ( array instanceof jQuery || !( cls in class2type ) && !jQuery.isWindow( array ) && ( // form,.. array.nodeType || + // arguments (using "in" to be sure to not throw exceptions) + ( "callee" in array ) || // NodeList array.item && ( array.namedItem || jQuery.isFunction( array.item ) ) || // jQuery-like - array.jquery && !jQuery.isPlainObject( array ) || - // arguments - argsRudeCheck( array ) ) + array.jquery && !jQuery.isPlainObject( array ) ) ) { jQuery.merge( ret, array );