diff --git a/src/ng/filter/limitTo.js b/src/ng/filter/limitTo.js index 5f77d58d8f3d..2a1c2f436d97 100644 --- a/src/ng/filter/limitTo.js +++ b/src/ng/filter/limitTo.js @@ -9,9 +9,10 @@ * Creates a new array or string containing only a specified number of elements. The elements * are taken from either the beginning or the end of the source array, string or number, as specified by * the value and sign (positive or negative) of `limit`. If a number is used as input, it is - * converted to a string. + * converted to a string. Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) + * are also supported. * - * @param {Array|string|number} input Source array, string or number to be limited. + * @param {Array} input Array or array-like to be limited. * @param {string|number} limit The length of the returned array or string. If the `limit` number * is positive, `limit` number of items from the beginning of the source array/string are copied. * If the number is negative, `limit` number of items from the end of the source array/string @@ -108,19 +109,25 @@ function limitToFilter() { if (isNaN(limit)) return input; if (isNumber(input)) input = input.toString(); - if (!isArray(input) && !isString(input)) return input; + if (!isArrayLike(input)) return input; begin = (!begin || isNaN(begin)) ? 0 : toInt(begin); begin = (begin < 0) ? Math.max(0, input.length + begin) : begin; if (limit >= 0) { - return input.slice(begin, begin + limit); + return sliceFn(input, begin, begin + limit); } else { if (begin === 0) { - return input.slice(limit, input.length); + return sliceFn(input, limit, input.length); } else { - return input.slice(Math.max(0, begin + limit), begin); + return sliceFn(input, Math.max(0, begin + limit), begin); } } }; } + +function sliceFn(input, begin, end) { + if (isString(input)) return input.slice(begin, end); + + return slice.call(input, begin, end); +} diff --git a/test/ng/filter/limitToSpec.js b/test/ng/filter/limitToSpec.js index a403c43b94ba..ea79e3db3cf8 100644 --- a/test/ng/filter/limitToSpec.js +++ b/test/ng/filter/limitToSpec.js @@ -4,12 +4,26 @@ describe('Filter: limitTo', function() { var items; var str; var number; + var arrayLike; var limitTo; beforeEach(inject(function($filter) { items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; str = "tuvwxyz"; number = 100.045; + arrayLike = { + 0: 'a', + 1: 'b', + 2: 'c', + 3: 'd', + 4: 'e', + 5: 'f', + 6: 'g', + 7: 'h', + get length() { + return Object.keys(this).length - 1; + } + }; limitTo = $filter('limitTo'); })); @@ -21,6 +35,8 @@ describe('Filter: limitTo', function() { expect(limitTo(str, '3')).toEqual("tuv"); expect(limitTo(number, 3)).toEqual("100"); expect(limitTo(number, '3')).toEqual("100"); + expect(limitTo(arrayLike, 3)).toEqual(['a', 'b', 'c']); + expect(limitTo(arrayLike, '3')).toEqual(['a', 'b', 'c']); }); it('should return the first X items beginning from index Y when X and Y are positive', function() { @@ -28,6 +44,8 @@ describe('Filter: limitTo', function() { expect(limitTo(items, '3', 3)).toEqual(['d', 'e', 'f']); expect(limitTo(str, 3, 3)).toEqual("wxy"); expect(limitTo(str, '3', '3')).toEqual("wxy"); + expect(limitTo(arrayLike, 3, 3)).toEqual(['d', 'e', 'f']); + expect(limitTo(arrayLike, '3', '3')).toEqual(['d', 'e', 'f']); }); it('should return the first X items beginning from index Y when X is positive and Y is negative', function() { @@ -35,6 +53,8 @@ describe('Filter: limitTo', function() { expect(limitTo(items, '3', -3)).toEqual(['f', 'g', 'h']); expect(limitTo(str, 3, -3)).toEqual("xyz"); expect(limitTo(str, '3', '-3')).toEqual("xyz"); + expect(limitTo(arrayLike, 3, '-3')).toEqual(['f', 'g', 'h']); + expect(limitTo(arrayLike, '3', -3)).toEqual(['f', 'g', 'h']); }); it('should return the last X items when X is negative', function() { @@ -44,6 +64,8 @@ describe('Filter: limitTo', function() { expect(limitTo(str, '-3')).toEqual("xyz"); expect(limitTo(number, -3)).toEqual("045"); expect(limitTo(number, '-3')).toEqual("045"); + expect(limitTo(arrayLike, -3)).toEqual(['f', 'g', 'h']); + expect(limitTo(arrayLike, '-3')).toEqual(['f', 'g', 'h']); }); it('should return the last X items until index Y when X and Y are negative', function() { @@ -51,6 +73,8 @@ describe('Filter: limitTo', function() { expect(limitTo(items, '-3', -3)).toEqual(['c', 'd', 'e']); expect(limitTo(str, -3, -3)).toEqual("uvw"); expect(limitTo(str, '-3', '-3')).toEqual("uvw"); + expect(limitTo(arrayLike, -3, '-3')).toEqual(['c', 'd', 'e']); + expect(limitTo(arrayLike, '-3', -3)).toEqual(['c', 'd', 'e']); }); it('should return the last X items until index Y when X is negative and Y is positive', function() { @@ -58,19 +82,28 @@ describe('Filter: limitTo', function() { expect(limitTo(items, '-3', 4)).toEqual(['b', 'c', 'd']); expect(limitTo(str, -3, 4)).toEqual("uvw"); expect(limitTo(str, '-3', '4')).toEqual("uvw"); + expect(limitTo(arrayLike, -3, '4')).toEqual(['b', 'c', 'd']); + expect(limitTo(arrayLike, '-3', 4)).toEqual(['b', 'c', 'd']); }); it('should return an empty array when X = 0', function() { expect(limitTo(items, 0)).toEqual([]); expect(limitTo(items, '0')).toEqual([]); + expect(limitTo(arrayLike, 0)).toEqual([]); + expect(limitTo(arrayLike, '0')).toEqual([]); }); it('should return entire array when X cannot be parsed', function() { - expect(limitTo(items, 'bogus')).toEqual(items); - expect(limitTo(items, 'null')).toEqual(items); - expect(limitTo(items, 'undefined')).toEqual(items); - expect(limitTo(items, null)).toEqual(items); - expect(limitTo(items, undefined)).toEqual(items); + expect(limitTo(items, 'bogus')).toBe(items); + expect(limitTo(items, 'null')).toBe(items); + expect(limitTo(items, 'undefined')).toBe(items); + expect(limitTo(items, null)).toBe(items); + expect(limitTo(items, undefined)).toBe(items); + expect(limitTo(arrayLike, 'bogus')).toBe(arrayLike); + expect(limitTo(arrayLike, 'null')).toBe(arrayLike); + expect(limitTo(arrayLike, 'undefined')).toBe(arrayLike); + expect(limitTo(arrayLike, null)).toBe(arrayLike); + expect(limitTo(arrayLike, undefined)).toBe(arrayLike); }); it('should return an empty string when X = 0', function() { @@ -97,9 +130,14 @@ describe('Filter: limitTo', function() { expect(limitTo(str, '3', 'undefined')).toEqual(limitTo(str, '3')); expect(limitTo(str, '-3', null)).toEqual(limitTo(str, '-3', 0)); expect(limitTo(str, 3, undefined)).toEqual(limitTo(str, 3)); + expect(limitTo(arrayLike, 3, 'bogus')).toEqual(limitTo(arrayLike, 3, 0)); + expect(limitTo(arrayLike, -3, 'null')).toEqual(limitTo(arrayLike, -3)); + expect(limitTo(arrayLike, '3', 'undefined')).toEqual(limitTo(arrayLike, '3', 0)); + expect(limitTo(arrayLike, '-3', null)).toEqual(limitTo(arrayLike, '-3')); + expect(limitTo(arrayLike, 3, undefined)).toEqual(limitTo(arrayLike, 3, 0)); }); - it('should return input if not String or Array or Number', function() { + it('should return input if not array-like or Number', function() { expect(limitTo(null, 1)).toEqual(null); expect(limitTo(undefined, 1)).toEqual(undefined); expect(limitTo({}, 1)).toEqual({}); @@ -111,8 +149,13 @@ describe('Filter: limitTo', function() { expect(limitTo(items, '9')).toEqual(items); expect(limitTo(items, -9)).toEqual(items); expect(limitTo(items, '-9')).toEqual(items); + expect(limitTo(arrayLike, 9)).toEqual(items); + expect(limitTo(arrayLike, '9')).toEqual(items); + expect(limitTo(arrayLike, -9)).toEqual(items); + expect(limitTo(arrayLike, '-9')).toEqual(items); expect(limitTo(items, 9)).not.toBe(items); + expect(limitTo(arrayLike, 9)).not.toBe(arrayLike); }); it('should return the entire string if X exceeds input length', function() { @@ -129,6 +172,10 @@ describe('Filter: limitTo', function() { expect(limitTo(items, 'Infinity')).toEqual(items); expect(limitTo(items, -Infinity)).toEqual(items); expect(limitTo(items, '-Infinity')).toEqual(items); + expect(limitTo(arrayLike, Infinity)).toEqual(items); + expect(limitTo(arrayLike, 'Infinity')).toEqual(items); + expect(limitTo(arrayLike, -Infinity)).toEqual(items); + expect(limitTo(arrayLike, '-Infinity')).toEqual(items); }); it('should return the entire string when limited by Infinity', function() { @@ -141,6 +188,8 @@ describe('Filter: limitTo', function() { it('should return an empty array if Y exceeds input length', function() { expect(limitTo(items, '3', 12)).toEqual([]); expect(limitTo(items, -3, '12')).toEqual([]); + expect(limitTo(arrayLike, '3', 12)).toEqual([]); + expect(limitTo(arrayLike, -3, '12')).toEqual([]); }); it('should return an empty string if Y exceeds input length', function() { @@ -153,6 +202,8 @@ describe('Filter: limitTo', function() { expect(limitTo(items, '-4', -12)).toEqual(['e', 'f', 'g', 'h']); expect(limitTo(str, 4, '-12')).toEqual("tuvw"); expect(limitTo(str, '-4', -12)).toEqual("wxyz"); + expect(limitTo(arrayLike, 4, '-12')).toEqual(['a', 'b', 'c', 'd']); + expect(limitTo(arrayLike, '-4', -12)).toEqual(['e', 'f', 'g', 'h']); }); it('should return the entire string beginning from Y if X is positive and X+Y exceeds input length', function() { @@ -160,6 +211,8 @@ describe('Filter: limitTo', function() { expect(limitTo(items, 7, -3)).toEqual(['f', 'g', 'h']); expect(limitTo(str, 6, 3)).toEqual("wxyz"); expect(limitTo(str, 6, -3)).toEqual("xyz"); + expect(limitTo(arrayLike, 7, 3)).toEqual(['d', 'e', 'f', 'g', 'h']); + expect(limitTo(arrayLike, 7, -3)).toEqual(['f', 'g', 'h']); }); it('should return the entire string until index Y if X is negative and X+Y exceeds input length', function() { @@ -167,5 +220,20 @@ describe('Filter: limitTo', function() { expect(limitTo(items, -7, -3)).toEqual(['a', 'b', 'c', 'd', 'e']); expect(limitTo(str, -6, 3)).toEqual("tuv"); expect(limitTo(str, -6, -3)).toEqual("tuvw"); + expect(limitTo(arrayLike, -7, 3)).toEqual(['a', 'b', 'c']); + expect(limitTo(arrayLike, -7, -3)).toEqual(['a', 'b', 'c', 'd', 'e']); + }); + + it('should not throw an error if used with an array like object', function() { + function getArguments() { + return arguments; + } + var argsObj = getArguments({name: 'Misko'}, {name: 'Igor'}, {name: 'Brad'}); + + var nodeList = jqLite("
MiskoIgorBrad
")[0].childNodes; + + expect(limitTo(argsObj, 2).length).toBe(2); + expect(limitTo('abc', 1).length).toBe(1); + expect(limitTo(nodeList, 2).length).toBe(2); }); });