From 361c91e6100046eaf346e0960187c8e94191aee3 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 12 Aug 2012 21:40:46 -0700 Subject: [PATCH] Add support in `_.isEmpty` and `_.size` for jQuery/MooTools DOM query collections. Former-commit-id: dc834256d09d7a3f2797ba65690961aad00717bf --- build/pre-compile.js | 2 ++ lodash.js | 36 ++++++++++++++++++++++++------------ test/test.js | 14 ++++++++++++++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/build/pre-compile.js b/build/pre-compile.js index 96ce8d38c..8fb380e75 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -43,6 +43,7 @@ 'ArrayProto', 'bind', 'callee', + 'className', 'compareAscending', 'destValue', 'forIn', @@ -57,6 +58,7 @@ 'isPlainObject', 'methodName', 'noaccum', + 'objectClass', 'objectTypes', 'pass', 'properties', diff --git a/lodash.js b/lodash.js index 3b2636f9d..09cd80bc0 100644 --- a/lodash.js +++ b/lodash.js @@ -663,8 +663,8 @@ var factory = Function( 'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, ' + 'funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray, ' + - 'isPlainObject, iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' + - 'slice, stringClass, toString', + 'isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, ' + + 'propertyIsEnumerable, slice, stringClass, toString', 'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + 'return callee' ); @@ -672,8 +672,8 @@ return factory( arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray, - isPlainObject, iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, - slice, stringClass, toString + isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, + propertyIsEnumerable, slice, stringClass, toString ); } @@ -1259,9 +1259,12 @@ 'args': 'value', 'init': 'true', 'top': - 'if (arrayLikeClasses[toString.call(value)]' + - (noArgsClass ? ' || isArguments(value)' : '') + - ') return !value.length', + 'var className = toString.call(value), length = value.length;\n' + + 'if (arrayLikeClasses[className]' + + (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + + ' (className == objectClass && length > -1 && length === length >>> 0 &&\n' + + ' toString.call(value.splice) == funcClass)' + + ') return !length', 'inLoop': { 'object': 'return false' } @@ -1726,9 +1729,19 @@ if (!value) { return 0; } - return arrayLikeClasses[toString.call(value)] || (noArgsClass && isArguments(value)) - ? value.length - : keys(value).length; + var className = toString.call(value), + length = value.length; + + // return `value.length` for `arguments` objects, arrays, strings, and DOM + // query collections of libraries like jQuery and MooTools + // http://code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/chrome/reps.js?r=12614#653 + // http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/InjectedScriptSource.js?rev=125186#L609 + if (arrayLikeClasses[className] || (noArgsClass && isArguments(value)) || + (className == objectClass && length > -1 && length === length >>> 0 && + toString.call(value.splice) == funcClass)) { + return length; + } + return keys(value).length; } /** @@ -1961,8 +1974,7 @@ ' isFunc = typeof methodName == \'function\'', 'inLoop': { 'array': - 'result[index] = (isFunc ? methodName : value[methodName])' + - '.apply(value, args)', + 'result[index] = (isFunc ? methodName : value[methodName]).apply(value, args)', 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '((isFunc ? methodName : value[methodName]).apply(value, args))' diff --git a/test/test.js b/test/test.js index 1f564925e..4013c777a 100644 --- a/test/test.js +++ b/test/test.js @@ -713,6 +713,13 @@ equal(_.isEmpty({ 'length': 0 }), false); }); + test('should work with array-like collections', function() { + function Foo(elements) { Array.prototype.push.apply(this, elements); } + Foo.prototype = { 'splice': Array.prototype.splice }; + + equal(_.isEmpty(new Foo([])), true); + }); + test('should work with `arguments` objects (test in IE < 9)', function() { equal(_.isEmpty(args), false); }); @@ -1084,6 +1091,13 @@ equal(_.size({ 'length': 3 }), 1); }); + test('should work with array-like collections', function() { + function Foo(elements) { Array.prototype.push.apply(this, elements); } + Foo.prototype = { 'splice': Array.prototype.splice }; + + equal(_.size(new Foo([1, 2, 3])), 3); + }); + test('should work with `arguments` objects (test in IE < 9)', function() { equal(_.size(args), 3); });