refacter(filters): convert filter/limitTo/orderBy from type augmentation to filters

This commit is contained in:
Misko Hevery 2011-11-03 20:53:33 -07:00
parent 3972d2a89b
commit dd9151e522
11 changed files with 556 additions and 528 deletions

3
angularFiles.js vendored
View file

@ -15,7 +15,10 @@ angularFiles = {
'src/service/document.js',
'src/service/exceptionHandler.js',
'src/service/filter.js',
'src/service/filter/filter.js',
'src/service/filter/filters.js',
'src/service/filter/limitTo.js',
'src/service/filter/orderBy.js',
'src/service/formFactory.js',
'src/service/location.js',
'src/service/log.js',

View file

@ -275,166 +275,6 @@ var angularArray = {
},
/**
* @ngdoc function
* @name angular.Array.filter
* @function
*
* @description
* Selects a subset of items from `array` and returns it as a new array.
*
* Note: This function is used to augment the `Array` type in Angular expressions. See
* {@link angular.Array} for more information about Angular arrays.
*
* @param {Array} array The source array.
* @param {string|Object|function()} expression The predicate to be used for selecting items from
* `array`.
*
* Can be one of:
*
* - `string`: Predicate that results in a substring match using the value of `expression`
* string. All strings or objects with string properties in `array` that contain this string
* will be returned. The predicate can be negated by prefixing the string with `!`.
*
* - `Object`: A pattern object can be used to filter specific properties on objects contained
* by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
* which have property `name` containing "M" and property `phone` containing "1". A special
* property name `$` can be used (as in `{$:"text"}`) to accept a match against any
* property of the object. That's equivalent to the simple substring match with a `string`
* as described above.
*
* - `function`: A predicate function can be used to write arbitrary filters. The function is
* called for each element of `array`. The final result is an array of those elements that
* the predicate returned true for.
*
* @example
<doc:example>
<doc:source>
<div ng:init="friends = [{name:'John', phone:'555-1276'},
{name:'Mary', phone:'800-BIG-MARY'},
{name:'Mike', phone:'555-4321'},
{name:'Adam', phone:'555-5678'},
{name:'Julie', phone:'555-8765'}]"></div>
Search: <input ng:model="searchText"/>
<table id="searchTextResults">
<tr><th>Name</th><th>Phone</th><tr>
<tr ng:repeat="friend in friends.$filter(searchText)">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<tr>
</table>
<hr>
Any: <input ng:model="search.$"/> <br>
Name only <input ng:model="search.name"/><br>
Phone only <input ng:model="search.phone"/><br>
<table id="searchObjResults">
<tr><th>Name</th><th>Phone</th><tr>
<tr ng:repeat="friend in friends.$filter(search)">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<tr>
</table>
</doc:source>
<doc:scenario>
it('should search across all fields when filtering with a string', function() {
input('searchText').enter('m');
expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
toEqual(['Mary', 'Mike', 'Adam']);
input('searchText').enter('76');
expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
toEqual(['John', 'Julie']);
});
it('should search in specific fields when filtering with a predicate object', function() {
input('search.$').enter('i');
expect(repeater('#searchObjResults tr', 'friend in friends').column('name')).
toEqual(['Mary', 'Mike', 'Julie']);
});
</doc:scenario>
</doc:example>
*/
'filter':function(array, expression) {
var predicates = [];
predicates.check = function(value) {
for (var j = 0; j < predicates.length; j++) {
if(!predicates[j](value)) {
return false;
}
}
return true;
};
var search = function(obj, text){
if (text.charAt(0) === '!') {
return !search(obj, text.substr(1));
}
switch (typeof obj) {
case "boolean":
case "number":
case "string":
return ('' + obj).toLowerCase().indexOf(text) > -1;
case "object":
for ( var objKey in obj) {
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
return true;
}
}
return false;
case "array":
for ( var i = 0; i < obj.length; i++) {
if (search(obj[i], text)) {
return true;
}
}
return false;
default:
return false;
}
};
switch (typeof expression) {
case "boolean":
case "number":
case "string":
expression = {$:expression};
case "object":
for (var key in expression) {
if (key == '$') {
(function() {
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(value, text);
});
})();
} else {
(function() {
var path = key;
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(getter(value, path), text);
});
})();
}
}
break;
case 'function':
predicates.push(expression);
break;
default:
return array;
}
var filtered = [];
for ( var j = 0; j < array.length; j++) {
var value = array[j];
if (predicates.check(value)) {
filtered.push(value);
}
}
return filtered;
},
/**
* @ngdoc function
@ -577,223 +417,6 @@ var angularArray = {
}
});
return count;
},
/**
* @ngdoc function
* @name angular.Array.orderBy
* @function
*
* @description
* Orders a specified `array` by the `expression` predicate.
*
* Note: this function is used to augment the `Array` type in Angular expressions. See
* {@link angular.Array} for more informaton about Angular arrays.
*
* @param {Array} array The array to sort.
* @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
* used by the comparator to determine the order of elements.
*
* Can be one of:
*
* - `function`: Getter function. The result of this function will be sorted using the
* `<`, `=`, `>` operator.
* - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
* to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
* ascending or descending sort order (for example, +name or -name).
* - `Array`: An array of function or string predicates. The first predicate in the array
* is used for sorting, but when two items are equivalent, the next predicate is used.
*
* @param {boolean=} reverse Reverse the order the array.
* @returns {Array} Sorted copy of the source array.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl() {
this.friends =
[{name:'John', phone:'555-1212', age:10},
{name:'Mary', phone:'555-9876', age:19},
{name:'Mike', phone:'555-4321', age:21},
{name:'Adam', phone:'555-5678', age:35},
{name:'Julie', phone:'555-8765', age:29}]
this.predicate = '-age';
}
</script>
<div ng:controller="Ctrl">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
[ <a href="" ng:click="predicate=''">unsorted</a> ]
<table class="friend">
<tr>
<th><a href="" ng:click="predicate = 'name'; reverse=false">Name</a>
(<a href ng:click="predicate = '-name'; reverse=false">^</a>)</th>
<th><a href="" ng:click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
<th><a href="" ng:click="predicate = 'age'; reverse=!reverse">Age</a></th>
<tr>
<tr ng:repeat="friend in friends.$orderBy(predicate, reverse)">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
<tr>
</table>
</div>
</doc:source>
<doc:scenario>
it('should be reverse ordered by aged', function() {
expect(binding('predicate')).toBe('Sorting predicate = -age; reverse = ');
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
toEqual(['35', '29', '21', '19', '10']);
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
});
it('should reorder the table when user selects different predicate', function() {
element('.doc-example-live a:contains("Name")').click();
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
toEqual(['35', '10', '29', '19', '21']);
element('.doc-example-live a:contains("Phone")').click();
expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
});
</doc:scenario>
</doc:example>
*/
'orderBy':function(array, sortPredicate, reverseOrder) {
if (!sortPredicate) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
sortPredicate = map(sortPredicate, function(predicate){
var descending = false, get = predicate || identity;
if (isString(predicate)) {
if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
descending = predicate.charAt(0) == '-';
predicate = predicate.substring(1);
}
get = expressionCompile(predicate);
}
return reverseComparator(function(a,b){
return compare(get(a),get(b));
}, descending);
});
var arrayCopy = [];
for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
function comparator(o1, o2){
for ( var i = 0; i < sortPredicate.length; i++) {
var comp = sortPredicate[i](o1, o2);
if (comp !== 0) return comp;
}
return 0;
}
function reverseComparator(comp, descending) {
return toBoolean(descending)
? function(a,b){return comp(b,a);}
: comp;
}
function compare(v1, v2){
var t1 = typeof v1;
var t2 = typeof v2;
if (t1 == t2) {
if (t1 == "string") v1 = v1.toLowerCase();
if (t1 == "string") v2 = v2.toLowerCase();
if (v1 === v2) return 0;
return v1 < v2 ? -1 : 1;
} else {
return t1 < t2 ? -1 : 1;
}
}
},
/**
* @ngdoc function
* @name angular.Array.limitTo
* @function
*
* @description
* Creates a new array containing only a specified number of elements in an array. The elements
* are taken from either the beginning or the end of the source array, as specified by the
* value and sign (positive or negative) of `limit`.
*
* Note: This function is used to augment the `Array` type in Angular expressions. See
* {@link angular.Array} for more information about Angular arrays.
*
* @param {Array} array Source array to be limited.
* @param {string|Number} limit The length of the returned array. If the `limit` number is
* positive, `limit` number of items from the beginning of the source array are copied.
* If the number is negative, `limit` number of items from the end of the source array are
* copied. The `limit` will be trimmed if it exceeds `array.length`
* @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
* elements.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl() {
this.numbers = [1,2,3,4,5,6,7,8,9];
this.limit = 3;
}
</script>
<div ng:controller="Ctrl">
Limit {{numbers}} to: <input type="integer" ng:model="limit"/>
<p>Output: {{ numbers.$limitTo(limit) | json }}</p>
</div>
</doc:source>
<doc:scenario>
it('should limit the numer array to first three items', function() {
expect(element('.doc-example-live input[ng\\:model=limit]').val()).toBe('3');
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3]');
});
it('should update the output when -3 is entered', function() {
input('limit').enter(-3);
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[7,8,9]');
});
it('should not exceed the maximum size of input array', function() {
input('limit').enter(100);
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3,4,5,6,7,8,9]');
});
</doc:scenario>
</doc:example>
*/
limitTo: function(array, limit) {
limit = parseInt(limit, 10);
var out = [],
i, n;
// check that array is iterable
if (!array || !(array instanceof Array))
return out;
// if abs(limit) exceeds maximum length, trim it
if (limit > array.length)
limit = array.length;
else if (limit < -array.length)
limit = -array.length;
if (limit > 0) {
i = 0;
n = limit;
} else {
i = array.length + limit;
n = array.length;
}
for (; i<n; i++) {
out.push(array[i]);
}
return out;
}
};

View file

@ -17,11 +17,14 @@ function $FilterProvider($provide) {
////////////////////////////////////////
$provide.filter('currency', currencyFilter);
$provide.filter('number', numberFilter);
$provide.filter('date', dateFilter);
$provide.filter('json', jsonFilter);
$provide.filter('lowercase', lowercaseFilter);
$provide.filter('uppercase', uppercaseFilter);
$provide.filter('filter', filterFilter);
$provide.filter('html', htmlFilter);
$provide.filter('json', jsonFilter);
$provide.filter('limitTo', limitToFilter);
$provide.filter('linky', linkyFilter);
$provide.filter('lowercase', lowercaseFilter);
$provide.filter('number', numberFilter);
$provide.filter('orderBy', orderByFilter);
$provide.filter('uppercase', uppercaseFilter);
}

View file

@ -0,0 +1,164 @@
'use strict';
/**
* @ngdoc function
* @name angular.Array.filter
* @function
*
* @description
* Selects a subset of items from `array` and returns it as a new array.
*
* Note: This function is used to augment the `Array` type in Angular expressions. See
* {@link angular.Array} for more information about Angular arrays.
*
* @param {Array} array The source array.
* @param {string|Object|function()} expression The predicate to be used for selecting items from
* `array`.
*
* Can be one of:
*
* - `string`: Predicate that results in a substring match using the value of `expression`
* string. All strings or objects with string properties in `array` that contain this string
* will be returned. The predicate can be negated by prefixing the string with `!`.
*
* - `Object`: A pattern object can be used to filter specific properties on objects contained
* by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
* which have property `name` containing "M" and property `phone` containing "1". A special
* property name `$` can be used (as in `{$:"text"}`) to accept a match against any
* property of the object. That's equivalent to the simple substring match with a `string`
* as described above.
*
* - `function`: A predicate function can be used to write arbitrary filters. The function is
* called for each element of `array`. The final result is an array of those elements that
* the predicate returned true for.
*
* @example
<doc:example>
<doc:source>
<div ng:init="friends = [{name:'John', phone:'555-1276'},
{name:'Mary', phone:'800-BIG-MARY'},
{name:'Mike', phone:'555-4321'},
{name:'Adam', phone:'555-5678'},
{name:'Julie', phone:'555-8765'}]"></div>
Search: <input ng:model="searchText"/>
<table id="searchTextResults">
<tr><th>Name</th><th>Phone</th><tr>
<tr ng:repeat="friend in friends.$filter(searchText)">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<tr>
</table>
<hr>
Any: <input ng:model="search.$"/> <br>
Name only <input ng:model="search.name"/><br>
Phone only <input ng:model="search.phone"/><br>
<table id="searchObjResults">
<tr><th>Name</th><th>Phone</th><tr>
<tr ng:repeat="friend in friends.$filter(search)">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<tr>
</table>
</doc:source>
<doc:scenario>
it('should search across all fields when filtering with a string', function() {
input('searchText').enter('m');
expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
toEqual(['Mary', 'Mike', 'Adam']);
input('searchText').enter('76');
expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
toEqual(['John', 'Julie']);
});
it('should search in specific fields when filtering with a predicate object', function() {
input('search.$').enter('i');
expect(repeater('#searchObjResults tr', 'friend in friends').column('name')).
toEqual(['Mary', 'Mike', 'Julie']);
});
</doc:scenario>
</doc:example>
*/
function filterFilter() {
return function(array, expression) {
if (!(array instanceof Array)) return array;
var predicates = [];
predicates.check = function(value) {
for (var j = 0; j < predicates.length; j++) {
if(!predicates[j](value)) {
return false;
}
}
return true;
};
var search = function(obj, text){
if (text.charAt(0) === '!') {
return !search(obj, text.substr(1));
}
switch (typeof obj) {
case "boolean":
case "number":
case "string":
return ('' + obj).toLowerCase().indexOf(text) > -1;
case "object":
for ( var objKey in obj) {
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
return true;
}
}
return false;
case "array":
for ( var i = 0; i < obj.length; i++) {
if (search(obj[i], text)) {
return true;
}
}
return false;
default:
return false;
}
};
switch (typeof expression) {
case "boolean":
case "number":
case "string":
expression = {$:expression};
case "object":
for (var key in expression) {
if (key == '$') {
(function() {
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(value, text);
});
})();
} else {
(function() {
var path = key;
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(getter(value, path), text);
});
})();
}
}
break;
case 'function':
predicates.push(expression);
break;
default:
return array;
}
var filtered = [];
for ( var j = 0; j < array.length; j++) {
var value = array[j];
if (predicates.check(value)) {
filtered.push(value);
}
}
return filtered;
}
}

View file

@ -0,0 +1,87 @@
'use strict';
/**
* @ngdoc function
* @name angular.Array.limitTo
* @function
*
* @description
* Creates a new array containing only a specified number of elements in an array. The elements
* are taken from either the beginning or the end of the source array, as specified by the
* value and sign (positive or negative) of `limit`.
*
* Note: This function is used to augment the `Array` type in Angular expressions. See
* {@link angular.Array} for more information about Angular arrays.
*
* @param {Array} array Source array to be limited.
* @param {string|Number} limit The length of the returned array. If the `limit` number is
* positive, `limit` number of items from the beginning of the source array are copied.
* If the number is negative, `limit` number of items from the end of the source array are
* copied. The `limit` will be trimmed if it exceeds `array.length`
* @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
* elements.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl() {
this.numbers = [1,2,3,4,5,6,7,8,9];
this.limit = 3;
}
</script>
<div ng:controller="Ctrl">
Limit {{numbers}} to: <input type="integer" ng:model="limit"/>
<p>Output: {{ numbers.$limitTo(limit) | json }}</p>
</div>
</doc:source>
<doc:scenario>
it('should limit the numer array to first three items', function() {
expect(element('.doc-example-live input[ng\\:model=limit]').val()).toBe('3');
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3]');
});
it('should update the output when -3 is entered', function() {
input('limit').enter(-3);
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[7,8,9]');
});
it('should not exceed the maximum size of input array', function() {
input('limit').enter(100);
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3,4,5,6,7,8,9]');
});
</doc:scenario>
</doc:example>
*/
function limitToFilter(){
return function(array, limit) {
if (!(array instanceof Array)) return array;
limit = parseInt(limit, 10);
var out = [],
i, n;
// check that array is iterable
if (!array || !(array instanceof Array))
return out;
// if abs(limit) exceeds maximum length, trim it
if (limit > array.length)
limit = array.length;
else if (limit < -array.length)
limit = -array.length;
if (limit > 0) {
i = 0;
n = limit;
} else {
i = array.length + limit;
n = array.length;
}
for (; i<n; i++) {
out.push(array[i]);
}
return out;
}
}

View file

@ -0,0 +1,137 @@
'use strict';
/**
* @ngdoc function
* @name angular.Array.orderBy
* @function
*
* @description
* Orders a specified `array` by the `expression` predicate.
*
* Note: this function is used to augment the `Array` type in Angular expressions. See
* {@link angular.Array} for more informaton about Angular arrays.
*
* @param {Array} array The array to sort.
* @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
* used by the comparator to determine the order of elements.
*
* Can be one of:
*
* - `function`: Getter function. The result of this function will be sorted using the
* `<`, `=`, `>` operator.
* - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
* to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
* ascending or descending sort order (for example, +name or -name).
* - `Array`: An array of function or string predicates. The first predicate in the array
* is used for sorting, but when two items are equivalent, the next predicate is used.
*
* @param {boolean=} reverse Reverse the order the array.
* @returns {Array} Sorted copy of the source array.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl() {
this.friends =
[{name:'John', phone:'555-1212', age:10},
{name:'Mary', phone:'555-9876', age:19},
{name:'Mike', phone:'555-4321', age:21},
{name:'Adam', phone:'555-5678', age:35},
{name:'Julie', phone:'555-8765', age:29}]
this.predicate = '-age';
}
</script>
<div ng:controller="Ctrl">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
[ <a href="" ng:click="predicate=''">unsorted</a> ]
<table class="friend">
<tr>
<th><a href="" ng:click="predicate = 'name'; reverse=false">Name</a>
(<a href ng:click="predicate = '-name'; reverse=false">^</a>)</th>
<th><a href="" ng:click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
<th><a href="" ng:click="predicate = 'age'; reverse=!reverse">Age</a></th>
<tr>
<tr ng:repeat="friend in friends.$orderBy(predicate, reverse)">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
<tr>
</table>
</div>
</doc:source>
<doc:scenario>
it('should be reverse ordered by aged', function() {
expect(binding('predicate')).toBe('Sorting predicate = -age; reverse = ');
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
toEqual(['35', '29', '21', '19', '10']);
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
});
it('should reorder the table when user selects different predicate', function() {
element('.doc-example-live a:contains("Name")').click();
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
toEqual(['35', '10', '29', '19', '21']);
element('.doc-example-live a:contains("Phone")').click();
expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
});
</doc:scenario>
</doc:example>
*/
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){
return function(array, sortPredicate, reverseOrder) {
if (!(array instanceof Array)) return array;
if (!sortPredicate) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
sortPredicate = map(sortPredicate, function(predicate){
var descending = false, get = predicate || identity;
if (isString(predicate)) {
if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
descending = predicate.charAt(0) == '-';
predicate = predicate.substring(1);
}
get = $parse(predicate);
}
return reverseComparator(function(a,b){
return compare(get(a),get(b));
}, descending);
});
var arrayCopy = [];
for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
function comparator(o1, o2){
for ( var i = 0; i < sortPredicate.length; i++) {
var comp = sortPredicate[i](o1, o2);
if (comp !== 0) return comp;
}
return 0;
}
function reverseComparator(comp, descending) {
return toBoolean(descending)
? function(a,b){return comp(b,a);}
: comp;
}
function compare(v1, v2){
var t1 = typeof v1;
var t2 = typeof v2;
if (t1 == t2) {
if (t1 == "string") v1 = v1.toLowerCase();
if (t1 == "string") v2 = v2.toLowerCase();
if (v1 === v2) return 0;
return v1 < v2 ? -1 : 1;
} else {
return t1 < t2 ? -1 : 1;
}
}
}
}

View file

@ -105,117 +105,6 @@ describe('api', function() {
});
describe('filter', function() {
it('should filter by string', function() {
var items = ["MIsKO", {name:"shyam"}, ["adam"], 1234];
assertEquals(4, angular.Array.filter(items, "").length);
assertEquals(4, angular.Array.filter(items, undefined).length);
assertEquals(1, angular.Array.filter(items, 'iSk').length);
assertEquals("MIsKO", angular.Array.filter(items, 'isk')[0]);
assertEquals(1, angular.Array.filter(items, 'yam').length);
assertEquals(items[1], angular.Array.filter(items, 'yam')[0]);
assertEquals(1, angular.Array.filter(items, 'da').length);
assertEquals(items[2], angular.Array.filter(items, 'da')[0]);
assertEquals(1, angular.Array.filter(items, '34').length);
assertEquals(1234, angular.Array.filter(items, '34')[0]);
assertEquals(0, angular.Array.filter(items, "I don't exist").length);
});
it('should not read $ properties', function() {
assertEquals("", "".charAt(0)); // assumption
var items = [{$name:"misko"}];
assertEquals(0, angular.Array.filter(items, "misko").length);
});
it('should filter on specific property', function() {
var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}];
assertEquals(2, angular.Array.filter(items, {}).length);
assertEquals(2, angular.Array.filter(items, {name:'a'}).length);
assertEquals(1, angular.Array.filter(items, {name:'b'}).length);
assertEquals("abc", angular.Array.filter(items, {name:'b'})[0].name);
});
it('should take function as predicate', function() {
var items = [{name:"a"}, {name:"abc", done:true}];
assertEquals(1, angular.Array.filter(items, function(i) {return i.done;}).length);
});
it('should take object as perdicate', function() {
var items = [{first:"misko", last:"hevery"},
{first:"adam", last:"abrons"}];
assertEquals(2, angular.Array.filter(items, {first:'', last:''}).length);
assertEquals(1, angular.Array.filter(items, {first:'', last:'hevery'}).length);
assertEquals(0, angular.Array.filter(items, {first:'adam', last:'hevery'}).length);
assertEquals(1, angular.Array.filter(items, {first:'misko', last:'hevery'}).length);
assertEquals(items[0], angular.Array.filter(items, {first:'misko', last:'hevery'})[0]);
});
it('should support negation operator', function() {
var items = ["misko", "adam"];
assertEquals(1, angular.Array.filter(items, '!isk').length);
assertEquals(items[1], angular.Array.filter(items, '!isk')[0]);
});
});
describe('limit', function() {
var items;
beforeEach(function() {
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
});
it('should return the first X items when X is positive', function() {
expect(angular.Array.limitTo(items, 3)).toEqual(['a', 'b', 'c']);
expect(angular.Array.limitTo(items, '3')).toEqual(['a', 'b', 'c']);
});
it('should return the last X items when X is negative', function() {
expect(angular.Array.limitTo(items, -3)).toEqual(['f', 'g', 'h']);
expect(angular.Array.limitTo(items, '-3')).toEqual(['f', 'g', 'h']);
});
it('should return an empty array when X cannot be parsed', function() {
expect(angular.Array.limitTo(items, 'bogus')).toEqual([]);
expect(angular.Array.limitTo(items, 'null')).toEqual([]);
expect(angular.Array.limitTo(items, 'undefined')).toEqual([]);
expect(angular.Array.limitTo(items, null)).toEqual([]);
expect(angular.Array.limitTo(items, undefined)).toEqual([]);
});
it('should return an empty array when input is not Array type', function() {
expect(angular.Array.limitTo('bogus', 1)).toEqual([]);
expect(angular.Array.limitTo(null, 1)).toEqual([]);
expect(angular.Array.limitTo(undefined, 1)).toEqual([]);
expect(angular.Array.limitTo(null, 1)).toEqual([]);
expect(angular.Array.limitTo(undefined, 1)).toEqual([]);
expect(angular.Array.limitTo({}, 1)).toEqual([]);
});
it('should return a copy of input array if X is exceeds array length', function () {
expect(angular.Array.limitTo(items, 19)).toEqual(items);
expect(angular.Array.limitTo(items, '9')).toEqual(items);
expect(angular.Array.limitTo(items, -9)).toEqual(items);
expect(angular.Array.limitTo(items, '-9')).toEqual(items);
expect(angular.Array.limitTo(items, 9)).not.toBe(items);
});
});
it('add', function() {
var add = angular.Array.add;
@ -232,41 +121,6 @@ describe('api', function() {
});
describe('orderBy', function() {
var orderBy;
beforeEach(function() {
orderBy = angular.Array.orderBy;
});
it('should return same array if predicate is falsy', function() {
var array = [1, 2, 3];
expect(orderBy(array)).toBe(array);
});
it('shouldSortArrayInReverse', function() {
assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', true));
assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "T"));
assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "reverse"));
});
it('should sort array by predicate', function() {
assertJsonEquals([{a:2, b:1},{a:15, b:1}],
angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['a', 'b']));
assertJsonEquals([{a:2, b:1},{a:15, b:1}],
angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['b', 'a']));
assertJsonEquals([{a:15, b:1},{a:2, b:1}],
angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['+b', '-a']));
});
it('should use function', function() {
expect(
orderBy(
[{a:15, b:1},{a:2, b:1}],
function(value) { return value.a; })).
toEqual([{a:2, b:1},{a:15, b:1}]);
});
});
});

View file

@ -0,0 +1,68 @@
'use strict';
describe('Filter: filter', function() {
var filter;
beforeEach(inject(function($filter){
filter = $filter('filter');
}));
it('should filter by string', function() {
var items = ["MIsKO", {name:"shyam"}, ["adam"], 1234];
assertEquals(4, filter(items, "").length);
assertEquals(4, filter(items, undefined).length);
assertEquals(1, filter(items, 'iSk').length);
assertEquals("MIsKO", filter(items, 'isk')[0]);
assertEquals(1, filter(items, 'yam').length);
assertEquals(items[1], filter(items, 'yam')[0]);
assertEquals(1, filter(items, 'da').length);
assertEquals(items[2], filter(items, 'da')[0]);
assertEquals(1, filter(items, '34').length);
assertEquals(1234, filter(items, '34')[0]);
assertEquals(0, filter(items, "I don't exist").length);
});
it('should not read $ properties', function() {
assertEquals("", "".charAt(0)); // assumption
var items = [{$name:"misko"}];
assertEquals(0, filter(items, "misko").length);
});
it('should filter on specific property', function() {
var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}];
assertEquals(2, filter(items, {}).length);
assertEquals(2, filter(items, {name:'a'}).length);
assertEquals(1, filter(items, {name:'b'}).length);
assertEquals("abc", filter(items, {name:'b'})[0].name);
});
it('should take function as predicate', function() {
var items = [{name:"a"}, {name:"abc", done:true}];
assertEquals(1, filter(items, function(i) {return i.done;}).length);
});
it('should take object as perdicate', function() {
var items = [{first:"misko", last:"hevery"},
{first:"adam", last:"abrons"}];
assertEquals(2, filter(items, {first:'', last:''}).length);
assertEquals(1, filter(items, {first:'', last:'hevery'}).length);
assertEquals(0, filter(items, {first:'adam', last:'hevery'}).length);
assertEquals(1, filter(items, {first:'misko', last:'hevery'}).length);
assertEquals(items[0], filter(items, {first:'misko', last:'hevery'})[0]);
});
it('should support negation operator', function() {
var items = ["misko", "adam"];
assertEquals(1, filter(items, '!isk').length);
assertEquals(items[1], filter(items, '!isk')[0]);
});
});

View file

@ -1,6 +1,6 @@
'use strict';
describe('filter', function() {
describe('filters', function() {
var filter;

View file

@ -0,0 +1,52 @@
'use strict';
describe('Filter: limitTo', function() {
var items;
var limitTo;
beforeEach(inject(function($filter) {
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
limitTo = $filter('limitTo');
}));
it('should return the first X items when X is positive', function() {
expect(limitTo(items, 3)).toEqual(['a', 'b', 'c']);
expect(limitTo(items, '3')).toEqual(['a', 'b', 'c']);
});
it('should return the last X items when X is negative', function() {
expect(limitTo(items, -3)).toEqual(['f', 'g', 'h']);
expect(limitTo(items, '-3')).toEqual(['f', 'g', 'h']);
});
it('should return an empty array when X cannot be parsed', function() {
expect(limitTo(items, 'bogus')).toEqual([]);
expect(limitTo(items, 'null')).toEqual([]);
expect(limitTo(items, 'undefined')).toEqual([]);
expect(limitTo(items, null)).toEqual([]);
expect(limitTo(items, undefined)).toEqual([]);
});
it('should return an empty array when input is not Array type', function() {
expect(limitTo('bogus', 1)).toEqual('bogus');
expect(limitTo(null, 1)).toEqual(null);
expect(limitTo(undefined, 1)).toEqual(undefined);
expect(limitTo(null, 1)).toEqual(null);
expect(limitTo(undefined, 1)).toEqual(undefined);
expect(limitTo({}, 1)).toEqual({});
});
it('should return a copy of input array if X is exceeds array length', function () {
expect(limitTo(items, 19)).toEqual(items);
expect(limitTo(items, '9')).toEqual(items);
expect(limitTo(items, -9)).toEqual(items);
expect(limitTo(items, '-9')).toEqual(items);
expect(limitTo(items, 9)).not.toBe(items);
});
});

View file

@ -0,0 +1,37 @@
'use strict';
describe('Filter: orderBy', function() {
var orderBy;
beforeEach(inject(function($filter) {
orderBy = $filter('orderBy');
}));
it('should return same array if predicate is falsy', function() {
var array = [1, 2, 3];
expect(orderBy(array)).toBe(array);
});
it('shouldSortArrayInReverse', function() {
assertJsonEquals([{a:15},{a:2}], orderBy([{a:15},{a:2}], 'a', true));
assertJsonEquals([{a:15},{a:2}], orderBy([{a:15},{a:2}], 'a', "T"));
assertJsonEquals([{a:15},{a:2}], orderBy([{a:15},{a:2}], 'a', "reverse"));
});
it('should sort array by predicate', function() {
assertJsonEquals([{a:2, b:1},{a:15, b:1}],
orderBy([{a:15, b:1},{a:2, b:1}], ['a', 'b']));
assertJsonEquals([{a:2, b:1},{a:15, b:1}],
orderBy([{a:15, b:1},{a:2, b:1}], ['b', 'a']));
assertJsonEquals([{a:15, b:1},{a:2, b:1}],
orderBy([{a:15, b:1},{a:2, b:1}], ['+b', '-a']));
});
it('should use function', function() {
expect(
orderBy(
[{a:15, b:1},{a:2, b:1}],
function(value) { return value.a; })).
toEqual([{a:2, b:1},{a:15, b:1}]);
});
});