Skip to content

Commit a992fa6

Browse files
fix(Angular.js): fix isArrayLike for unusual cases
Closes angular#10186 Closes angular#8000 Closes angular#4855 Closes angular#4751 Closes angular#10272
1 parent a040f02 commit a992fa6

File tree

3 files changed

+40
-45
lines changed

3 files changed

+40
-45
lines changed

src/Angular.js

+16-42
Original file line numberDiff line numberDiff line change
@@ -191,57 +191,31 @@ var
191191
msie = document.documentMode;
192192

193193

194+
function isNodeList(obj) {
195+
return typeof obj.length == 'number' &&
196+
typeof obj.item == 'function';
197+
}
198+
194199
/**
195200
* @private
196201
* @param {*} obj
197202
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
198203
* String ...)
199204
*/
200205
function isArrayLike(obj) {
201-
// snake case is to avoid shadowing camel-cased globals
202-
var length, objExists, isNodeList, isArguments, isSomeOtherObj, is_array, is_string, is_object;
203-
objExists = isDefined(obj) && obj !== null;
206+
207+
// `null`, `undefined` and `window` are not array-like
208+
if (obj == null || isWindow(obj)) return false;
209+
210+
// arrays and strings are array like
211+
if (isArray(obj) || isString(obj)) return true;
212+
204213
// Support: iOS 8.2 (not reproducible in simulator)
205214
// "length" in obj used to prevent JIT error (gh-11508)
206-
length = objExists ? "length" in Object(obj) && obj.length : false;
207-
is_array = isArray(obj);
208-
is_string = isString(obj);
209-
is_object = isObject(obj);
210-
isNodeList = objExists && obj.nodeType === 1 && length;
211-
isArguments = objExists &&
212-
(Object.prototype.toString.call(obj) === '[object Arguments]' ||
213-
(Object.prototype.hasOwnProperty.call(obj, 'length') &&
214-
Object.prototype.hasOwnProperty.call(obj, 'callee')));
215-
216-
// this only works if it doesn't return 'object' from typeof and isn't another arrayLike
217-
isSomeOtherObj = objExists &&
218-
!isNodeList &&
219-
!is_array &&
220-
!is_string &&
221-
!isArguments &&
222-
(
223-
(!is_object &&
224-
length === 0) ||
225-
(
226-
isNumber(length) &&
227-
length >= 0 &&
228-
(length - 1) in obj
229-
)
230-
);
215+
var length = "length" in Object(obj) && obj.length;
231216

232-
return (
233-
objExists &&
234-
!isWindow(obj) &&
235-
(
236-
(
237-
isNodeList ||
238-
is_string ||
239-
is_array ||
240-
isArguments
241-
) ||
242-
isSomeOtherObj
243-
)
244-
);
217+
// node lists and objects with suitable length characteristics are array-like
218+
return (isNumber(length) && length >= 0 && (length - 1) in obj) || isNodeList(obj);
245219
}
246220

247221
/**
@@ -501,7 +475,7 @@ identity.$inject = [];
501475
function valueFn(value) {return function() {return value;};}
502476

503477
function hasCustomToString(obj) {
504-
return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
478+
return isFunction(obj.toString) && obj.toString !== toString;
505479
}
506480

507481

src/ngAnimate/animateQueue.js

-3
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
502502
var node = getDomNode(element);
503503
var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
504504
forEach(children, function(child) {
505-
if(!isFunction(child.getAttribute)) {
506-
console.log(child, children);
507-
}
508505
var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
509506
var animationDetails = activeAnimationsLookup.get(child);
510507
switch (state) {

test/AngularSpec.js

+24
Original file line numberDiff line numberDiff line change
@@ -1055,15 +1055,39 @@ describe('angular', function() {
10551055
});
10561056

10571057
describe('isArrayLike', function() {
1058+
10581059
it('should return false if passed a number', function() {
10591060
expect(isArrayLike(10)).toBe(false);
10601061
});
1062+
10611063
it('should return true if passed an array', function() {
10621064
expect(isArrayLike([1,2,3,4])).toBe(true);
10631065
});
1066+
10641067
it('should return true if passed an object', function() {
10651068
expect(isArrayLike({0:"test", 1:"bob", 2:"tree", length:3})).toBe(true);
10661069
});
1070+
1071+
it('should return true if passed arguments object', function() {
1072+
function test(a,b,c) {
1073+
expect(isArrayLike(arguments)).toBe(true);
1074+
}
1075+
test(1,2,3);
1076+
});
1077+
1078+
it('should return true if passed a nodelist', function() {
1079+
var nodes = document.body.childNodes;
1080+
expect(isArrayLike(nodes)).toBe(true);
1081+
});
1082+
1083+
it('should return false for objects with `length` but no matching indexable items', function() {
1084+
var obj = {
1085+
a: 'a',
1086+
b:'b',
1087+
length: 10
1088+
};
1089+
expect(isArrayLike(obj)).toBe(false);
1090+
});
10671091
});
10681092

10691093

0 commit comments

Comments
 (0)