Skip to content

Commit

Permalink
fix(Angular.js): fix isArrayLike for unusual cases
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcviers authored and petebacondarwin committed Oct 23, 2015
1 parent ad4296d commit d188bc7
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 11 deletions.
52 changes: 41 additions & 11 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,20 +198,50 @@ msie = document.documentMode;
* String ...)
*/
function isArrayLike(obj) {
if (obj == null || isWindow(obj)) {
return false;
}

// snake case is to avoid shadowing camel-cased globals
var length, objExists, isNodeList, isArguments, isSomeOtherObj, is_array, is_string, is_object;
objExists = isDefined(obj) && obj !== null;
// Support: iOS 8.2 (not reproducible in simulator)
// "length" in obj used to prevent JIT error (gh-11508)
var length = "length" in Object(obj) && obj.length;

if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
return true;
}
length = objExists ? "length" in Object(obj) && obj.length : false;
is_array = isArray(obj);
is_string = isString(obj);
is_object = isObject(obj);
isNodeList = objExists && obj.nodeType === 1 && length;
isArguments = objExists &&
(Object.prototype.toString.call(obj) === '[object Arguments]' ||
(Object.prototype.hasOwnProperty.call(obj, 'length') &&
Object.prototype.hasOwnProperty.call(obj, 'callee')));

// this only works if it doesn't return 'object' from typeof and isn't another arrayLike
isSomeOtherObj = objExists &&
!isNodeList &&
!is_array &&
!is_string &&
!isArguments &&
(
(!is_object &&
length === 0) ||
(
isNumber(length) &&
length >= 0 &&
(length - 1) in obj
)
);

return isString(obj) || isArray(obj) || length === 0 ||
typeof length === 'number' && length > 0 && (length - 1) in obj;
return (
objExists &&
!isWindow(obj) &&
(
(
isNodeList ||
is_string ||
is_array ||
isArguments
) ||
isSomeOtherObj
)
);
}

/**
Expand Down
31 changes: 31 additions & 0 deletions test/AngularSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ describe('angular', function() {
});

describe("extend", function() {

it('should not copy the private $$hashKey', function() {
var src,dst;
src = {};
Expand All @@ -471,6 +472,24 @@ describe('angular', function() {
});


it('should copy the properties of the source object onto the destination object', function() {
var destination, source;
destination = {};
source = {foo: true};
destination = extend(destination, source);
expect(isDefined(destination.foo)).toBe(true);
});


it('ISSUE #4751 - should copy the length property of an object source to the destination object', function() {
var destination, source;
destination = {};
source = {radius: 30, length: 0};
destination = extend(destination, source);
expect(isDefined(destination.length)).toBe(true);
expect(isDefined(destination.radius)).toBe(true);
});

it('should retain the previous $$hashKey', function() {
var src,dst,h;
src = {};
Expand Down Expand Up @@ -1035,6 +1054,18 @@ describe('angular', function() {
});
});

describe('isArrayLike', function() {
it('should return false if passed a number', function() {
expect(isArrayLike(10)).toBe(false);
});
it('should return true if passed an array', function() {
expect(isArrayLike([1,2,3,4])).toBe(true);
});
it('should return true if passed an object', function() {
expect(isArrayLike({0:"test", 1:"bob", 2:"tree", length:3})).toBe(true);
});
});


describe('forEach', function() {
it('should iterate over *own* object properties', function() {
Expand Down
9 changes: 9 additions & 0 deletions test/ng/directive/ngRepeatSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,15 @@ describe('ngRepeat', function() {
expect(element.text()).toEqual('misko:m:0|shyam:s:1|frodo:f:2|');
});

it('should expose iterator offset as $index when iterating over objects with length key value 0', function() {
element = $compile(
'<ul>' +
'<li ng-repeat="(key, val) in items">{{key}}:{{val}}:{{$index}}|</li>' +
'</ul>')(scope);
scope.items = {'misko':'m', 'shyam':'s', 'frodo':'f', 'length':0};
scope.$digest();
expect(element.text()).toEqual('misko:m:0|shyam:s:1|frodo:f:2|length:0:3|');
});

it('should expose iterator position as $first, $middle and $last when iterating over arrays',
function() {
Expand Down

0 comments on commit d188bc7

Please sign in to comment.