diff options
| -rw-r--r-- | src/Angular.js | 26 | ||||
| -rw-r--r-- | test/AngularSpec.js | 73 | 
2 files changed, 97 insertions, 2 deletions
| diff --git a/src/Angular.js b/src/Angular.js index 2affd5ef..4685dbde 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -90,6 +90,30 @@ var /** holds major version number for IE or NaN for real browsers */   * @param {Object=} context Object to become context (`this`) for the iterator function.   * @returns {Object|Array} Reference to `obj`.   */ + + +/** + * @private + * @param {*} obj + * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...) + */ +function isArrayLike(obj) { +  if (!obj || (typeof obj.length !== 'number')) return false; + +  // We have on object which has length property. Should we treat it as array? +  if (typeof obj.hasOwnProperty != 'function' && +      typeof obj.constructor != 'function') { +    // This is here for IE8: it is a bogus object treat it as array; +    return true; +  } else  { +    return obj instanceof JQLite ||                      // JQLite +           (jQuery && obj instanceof jQuery) ||          // jQuery +           toString.call(obj) !== '[object Object]' ||   // some browser native object +           typeof obj.callee === 'function';              // arguments (on IE8 looks like regular obj) +  } +} + +  function forEach(obj, iterator, context) {    var key;    if (obj) { @@ -101,7 +125,7 @@ function forEach(obj, iterator, context) {        }      } else if (obj.forEach && obj.forEach !== forEach) {        obj.forEach(iterator, context); -    } else if (isObject(obj) && isNumber(obj.length)) { +    } else if (isArrayLike(obj)) {        for (key = 0; key < obj.length; key++)          iterator.call(context, obj[key], key);      } else { diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 09bc902f..e29bb16b 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -257,7 +257,7 @@ describe('angular', function() {        function MyObj() {          this.bar = 'barVal';          this.baz = 'bazVal'; -      }; +      }        MyObj.prototype.foo = 'fooVal';        var obj = new MyObj(), @@ -267,6 +267,77 @@ describe('angular', function() {        expect(log).toEqual(['bar:barVal', 'baz:bazVal']);      }); + + +    it('should handle JQLite and jQuery objects like arrays', function() { +      var jqObject = jqLite("<p><span>s1</span><span>s2</span></p>").find("span"), +          log = []; + +      forEach(jqObject, function(value, key) { log.push(key + ':' + value.innerHTML)}); +      expect(log).toEqual(['0:s1', '1:s2']); +    }); + + +    it('should handle NodeList objects like arrays', function() { +      var nodeList = jqLite("<p><span>a</span><span>b</span><span>c</span></p>")[0].childNodes, +          log = []; + + +      forEach(nodeList, function(value, key) { log.push(key + ':' + value.innerHTML)}); +      expect(log).toEqual(['0:a', '1:b', '2:c']); +    }); + + +    it('should handle HTMLCollection objects like arrays', function() { +      document.body.innerHTML = "<p>" + +                                  "<a name='x'>a</a>" + +                                  "<a name='y'>b</a>" + +                                  "<a name='x'>c</a>" + +                                "</p>"; + +      var htmlCollection = document.getElementsByName('x'), +          log = []; + +      forEach(htmlCollection, function(value, key) { log.push(key + ':' + value.innerHTML)}); +      expect(log).toEqual(['0:a', '1:c']); +    }); + + +    it('should handle arguments objects like arrays', function() { +      var args, +          log = []; + +      (function(){ args = arguments}('a', 'b', 'c')); + +      forEach(args, function(value, key) { log.push(key + ':' + value)}); +      expect(log).toEqual(['0:a', '1:b', '2:c']); +    }); + + +    it('should handle objects with length property as objects', function() { +      var obj = { +            'foo' : 'bar', +            'length': 2 +          }, +          log = []; + +      forEach(obj, function(value, key) { log.push(key + ':' + value)}); +      expect(log).toEqual(['foo:bar', 'length:2']); +    }); + + +    it('should handle objects of custom types with length property as objects', function() { +      function CustomType() { +        this.length = 2; +        this.foo = 'bar' +      } + +      var obj = new CustomType(), +          log = []; + +      forEach(obj, function(value, key) { log.push(key + ':' + value)}); +      expect(log).toEqual(['length:2', 'foo:bar']); +    });    }); | 
