angular-filter
Version:
Bunch of useful filters for angularJS(with no external dependencies!)
1,826 lines (1,592 loc) • 62 kB
JavaScript
/**
* Bunch of useful filters for angularJS(with no external dependencies!)
* @version v0.5.17 - 2017-09-22 * @link https://github.com/a8m/angular-filter
* @author Ariel Mashraki <ariel@mashraki.co.il>
* @license MIT License, http://www.opensource.org/licenses/MIT
*/
(function ( window, angular, undefined ) {
/*jshint globalstrict:true*/
'use strict';
var isDefined = angular.isDefined,
isUndefined = angular.isUndefined,
isFunction = angular.isFunction,
isString = angular.isString,
isNumber = angular.isNumber,
isObject = angular.isObject,
isArray = angular.isArray,
forEach = angular.forEach,
extend = angular.extend,
copy = angular.copy,
equals = angular.equals;
/**
* @description
* get an object and return array of values
* @param object
* @returns {Array}
*/
function toArray(object) {
return isArray(object)
? object
: Object.keys(object).map(function(key) {
return object[key];
});
}
/**
* @param value
* @returns {boolean}
*/
function isNull(value) {
return value === null;
}
/**
* @description
* return if object contains partial object
* @param partial{object}
* @param object{object}
* @returns {boolean}
*/
function objectContains(partial, object) {
var keys = Object.keys(partial);
return keys.map(function(el) {
return (object[el] !== undefined) && (object[el] == partial[el]);
}).indexOf(false) == -1;
}
/**
* @description
* search for approximate pattern in string
* @param word
* @param pattern
* @returns {*}
*/
function hasApproxPattern(word, pattern) {
// cheaper version of indexOf; instead of creating each
// iteration new str.
function indexOf(word, p, c) {
var j = 0;
while ((p + j) <= word.length) {
if (word.charAt(p + j) == c) return j;
j++;
}
return -1;
}
var p = 0;
for (var i = 0; i <= pattern.length; i++) {
var index = indexOf(word, p, pattern.charAt(i));
if (index == -1) return false;
p += index + 1;
}
return true
}
/**
* @description
* return the first n element of an array,
* if expression provided, is returns as long the expression return truthy
* @param array
* @param n {number}
* @param expression {$parse}
* @return array or single object
*/
function getFirstMatches(array, n, expression) {
var count = 0;
return array.filter(function(elm) {
var rest = isDefined(expression) ? (count < n && expression(elm)) : count < n;
count = rest ? count+1 : count;
return rest;
});
}
/**
* Polyfill to ECMA6 String.prototype.contains
*/
if (!String.prototype.contains) {
String.prototype.contains = function() {
return String.prototype.indexOf.apply(this, arguments) !== -1;
};
}
/**
* @param num {Number}
* @param decimal {Number}
* @returns {Number}
*/
function convertToDecimal(num, decimal){
return Math.round(num * Math.pow(10,decimal)) / (Math.pow(10, decimal));
}
/**
* @description
* Get an object, and return an array composed of it's properties names(nested too).
* @param obj {Object}
* @param stack {Array}
* @param parent {String}
* @returns {Array}
* @example
* parseKeys({ a:1, b: { c:2, d: { e: 3 } } }) ==> ["a", "b.c", "b.d.e"]
*/
function deepKeys(obj, stack, parent) {
stack = stack || [];
var keys = Object.keys(obj);
keys.forEach(function(el) {
//if it's a nested object
if(isObject(obj[el]) && !isArray(obj[el])) {
//concatenate the new parent if exist
var p = parent ? parent + '.' + el : parent;
deepKeys(obj[el], stack, p || el);
} else {
//create and save the key
var key = parent ? parent + '.' + el : el;
stack.push(key)
}
});
return stack
}
/**
* @description
* Test if given object is a Scope instance
* @param obj
* @returns {Boolean}
*/
function isScope(obj) {
return obj && obj.$evalAsync && obj.$watch;
}
/**
* @ngdoc filter
* @name a8m.angular
* @kind function
*
* @description
* reference to angular function
*/
angular.module('a8m.angular', [])
.filter('isUndefined', function () {
return function (input) {
return angular.isUndefined(input);
}
})
.filter('isDefined', function() {
return function (input) {
return angular.isDefined(input);
}
})
.filter('isFunction', function() {
return function (input) {
return angular.isFunction(input);
}
})
.filter('isString', function() {
return function (input) {
return angular.isString(input)
}
})
.filter('isNumber', function() {
return function (input) {
return angular.isNumber(input);
}
})
.filter('isArray', function() {
return function (input) {
return angular.isArray(input);
}
})
.filter('isObject', function() {
return function (input) {
return angular.isObject(input);
}
})
.filter('isEqual', function() {
return function (o1, o2) {
return angular.equals(o1, o2);
}
});
/**
* @ngdoc filter
* @name a8m.conditions
* @kind function
*
* @description
* reference to math conditions
*/
angular.module('a8m.conditions', [])
.filter({
isGreaterThan : isGreaterThanFilter,
'>' : isGreaterThanFilter,
isGreaterThanOrEqualTo : isGreaterThanOrEqualToFilter,
'>=' : isGreaterThanOrEqualToFilter,
isLessThan : isLessThanFilter,
'<' : isLessThanFilter,
isLessThanOrEqualTo : isLessThanOrEqualToFilter,
'<=' : isLessThanOrEqualToFilter,
isEqualTo : isEqualToFilter,
'==' : isEqualToFilter,
isNotEqualTo : isNotEqualToFilter,
'!=' : isNotEqualToFilter,
isIdenticalTo : isIdenticalToFilter,
'===' : isIdenticalToFilter,
isNotIdenticalTo : isNotIdenticalToFilter,
'!==' : isNotIdenticalToFilter
});
function isGreaterThanFilter() {
return function (input, check) {
return input > check;
};
}
function isGreaterThanOrEqualToFilter() {
return function (input, check) {
return input >= check;
};
}
function isLessThanFilter() {
return function (input, check) {
return input < check;
};
}
function isLessThanOrEqualToFilter() {
return function (input, check) {
return input <= check;
};
}
function isEqualToFilter() {
return function (input, check) {
return input == check;
};
}
function isNotEqualToFilter() {
return function (input, check) {
return input != check;
};
}
function isIdenticalToFilter() {
return function (input, check) {
return input === check;
};
}
function isNotIdenticalToFilter() {
return function (input, check) {
return input !== check;
};
}
/**
* @ngdoc filter
* @name isNull
* @kind function
*
* @description
* checks if value is null or not
* @return Boolean
*/
angular.module('a8m.is-null', [])
.filter('isNull', function () {
return function(input) {
return isNull(input);
}
});
/**
* @ngdoc filter
* @name after-where
* @kind function
*
* @description
* get a collection and properties object, and returns all of the items
* in the collection after the first that found with the given properties.
*
*/
angular.module('a8m.after-where', [])
.filter('afterWhere', function() {
return function (collection, object) {
collection = isObject(collection)
? toArray(collection)
: collection;
if(!isArray(collection) || isUndefined(object)) return collection;
var index = collection.map( function( elm ) {
return objectContains(object, elm);
}).indexOf( true );
return collection.slice((index === -1) ? 0 : index);
}
});
/**
* @ngdoc filter
* @name after
* @kind function
*
* @description
* get a collection and specified count, and returns all of the items
* in the collection after the specified count.
*
*/
angular.module('a8m.after', [])
.filter('after', function() {
return function (collection, count) {
collection = isObject(collection)
? toArray(collection)
: collection;
return (isArray(collection))
? collection.slice(count)
: collection;
}
});
/**
* @ngdoc filter
* @name before-where
* @kind function
*
* @description
* get a collection and properties object, and returns all of the items
* in the collection before the first that found with the given properties.
*/
angular.module('a8m.before-where', [])
.filter('beforeWhere', function() {
return function (collection, object) {
collection = isObject(collection)
? toArray(collection)
: collection;
if(!isArray(collection) || isUndefined(object)) return collection;
var index = collection.map( function( elm ) {
return objectContains(object, elm);
}).indexOf( true );
return collection.slice(0, (index === -1) ? collection.length : ++index);
}
});
/**
* @ngdoc filter
* @name before
* @kind function
*
* @description
* get a collection and specified count, and returns all of the items
* in the collection before the specified count.
*/
angular.module('a8m.before', [])
.filter('before', function() {
return function (collection, count) {
collection = isObject(collection)
? toArray(collection)
: collection;
return (isArray(collection))
? collection.slice(0, (!count) ? count : --count)
: collection;
}
});
/**
* @ngdoc filter
* @name chunkBy
* @kind function
*
* @description
* Collect data into fixed-length chunks or blocks
*/
angular.module('a8m.chunk-by', ['a8m.filter-watcher'])
.filter('chunkBy', ['filterWatcher', function (filterWatcher) {
return function (array, n, fillVal) {
return filterWatcher.isMemoized('chunkBy', arguments) ||
filterWatcher.memoize('chunkBy', arguments, this,
_chunkBy(array, n, fillVal));
/**
* @description
* Get array with size `n` in `val` inside it.
* @param n
* @param val
* @returns {Array}
*/
function fill(n, val) {
var ret = [];
while (n--) ret[n] = val;
return ret;
}
function _chunkBy(array, n, fillVal) {
if (!isArray(array)) return array;
return array.map(function (el, i, self) {
i = i * n;
el = self.slice(i, i + n);
return !isUndefined(fillVal) && el.length < n
? el.concat(fill(n - el.length, fillVal))
: el;
}).slice(0, Math.ceil(array.length / n));
}
}
}]);
/**
* @ngdoc filter
* @name concat
* @kind function
*
* @description
* get (array/object, object/array) and return merged collection
*/
angular.module('a8m.concat', [])
.filter('concat', [function () {
return function (collection, joined) {
if (isUndefined(joined)) return collection;
if (isArray(collection)) {
return isObject(joined)
? collection.concat(toArray(joined))
: collection.concat(joined);
}
if (isObject(collection)) {
var array = toArray(collection);
return (isObject(joined))
? array.concat(toArray(joined))
: array.concat(joined);
}
return collection;
};
}
]);
/**
* @ngdoc filter
* @name contains
* @kind function
*
* @description
* Checks if given expression is present in one or more object in the collection
*/
angular.module('a8m.contains', [])
.filter({
contains: ['$parse', containsFilter],
some: ['$parse', containsFilter]
});
function containsFilter($parse) {
return function (collection, expression) {
collection = isObject(collection) ? toArray(collection) : collection;
if(!isArray(collection) || isUndefined(expression)) {
return false;
}
return collection.some(function(elm) {
return ((isString(expression) && isObject(elm)) || isFunction(expression))
? $parse(expression)(elm)
: elm === expression;
});
}
}
/**
* @ngdoc filter
* @name countBy
* @kind function
*
* @description
* Sorts a list into groups and returns a count for the number of objects in each group.
*/
angular.module('a8m.count-by', [])
.filter('countBy', [ '$parse', function ( $parse ) {
return function (collection, property) {
var result = {},
get = $parse(property),
prop;
collection = (isObject(collection)) ? toArray(collection) : collection;
if(!isArray(collection) || isUndefined(property)) {
return collection;
}
collection.forEach( function( elm ) {
prop = get(elm);
if(!result[prop]) {
result[prop] = 0;
}
result[prop]++;
});
return result;
}
}]);
/**
* @ngdoc filter
* @name defaults
* @kind function
*
* @description
* defaultsFilter allows to specify a default fallback value for properties that resolve to undefined.
*/
angular.module('a8m.defaults', [])
.filter('defaults', ['$parse', function( $parse ) {
return function(collection, defaults) {
collection = isObject(collection) ? toArray(collection) : collection;
if(!isArray(collection) || !isObject(defaults)) {
return collection;
}
var keys = deepKeys(defaults);
collection.forEach(function(elm) {
//loop through all the keys
keys.forEach(function(key) {
var getter = $parse(key);
var setter = getter.assign;
//if it's not exist
if(isUndefined(getter(elm))) {
//get from defaults, and set to the returned object
setter(elm, getter(defaults))
}
});
});
return collection;
}
}]);
/**
* @ngdoc filter
* @name every
* @kind function
*
* @description
* Checks if given expression is present in all members in the collection
*
*/
angular.module('a8m.every', [])
.filter('every', ['$parse', function($parse) {
return function (collection, expression) {
collection = isObject(collection) ? toArray(collection) : collection;
if(!isArray(collection) || isUndefined(expression)) {
return true;
}
return collection.every( function(elm) {
return (isObject(elm) || isFunction(expression))
? $parse(expression)(elm)
: elm === expression;
});
}
}]);
/**
* @ngdoc filter
* @name filterBy
* @kind function
*
* @description
* filter by specific properties, avoid the rest
*/
angular.module('a8m.filter-by', [])
.filter('filterBy', ['$parse', function( $parse ) {
return function(collection, properties, search, strict) {
var comparator;
search = (isString(search) || isNumber(search)) ?
String(search).toLowerCase() : undefined;
collection = isObject(collection) ? toArray(collection) : collection;
if(!isArray(collection) || isUndefined(search)) {
return collection;
}
return collection.filter(function(elm) {
return properties.some(function(prop) {
/**
* check if there is concatenate properties
* example:
* object: { first: 'foo', last:'bar' }
* filterBy: ['first + last'] => search by full name(i.e 'foo bar')
*/
if(!~prop.indexOf('+')) {
comparator = $parse(prop)(elm)
} else {
var propList = prop.replace(/\s+/g, '').split('+');
comparator = propList
.map(function(prop) { return $parse(prop)(elm); })
.join(' ');
}
if (!isString(comparator) && !isNumber(comparator)) {
return false;
}
comparator = String(comparator).toLowerCase();
return strict ? comparator === search : comparator.contains(search);
});
});
}
}]);
/**
* @ngdoc filter
* @name first
* @kind function
*
* @description
* Gets the first element or first n elements of an array
* if callback is provided, is returns as long the callback return truthy
*/
angular.module('a8m.first', [])
.filter('first', ['$parse', function( $parse ) {
return function(collection) {
var n
, getter
, args;
collection = isObject(collection)
? toArray(collection)
: collection;
if(!isArray(collection)) {
return collection;
}
args = Array.prototype.slice.call(arguments, 1);
n = (isNumber(args[0])) ? args[0] : 1;
getter = (!isNumber(args[0])) ? args[0] : (!isNumber(args[1])) ? args[1] : undefined;
return (args.length) ? getFirstMatches(collection, n,(getter) ? $parse(getter) : getter) :
collection[0];
}
}]);
/**
* @ngdoc filter
* @name flatten
* @kind function
*
* @description
* Flattens a nested array (the nesting can be to any depth).
* If you pass shallow, the array will only be flattened a single level
*/
angular.module('a8m.flatten', [])
.filter('flatten', function () {
return function(collection, shallow) {
shallow = shallow || false;
collection = isObject(collection)
? toArray(collection)
: collection;
if(!isArray(collection)) {
return collection;
}
return !shallow
? flatten(collection, 0)
: [].concat.apply([], collection);
}
});
/**
* flatten nested array (the nesting can be to any depth).
* @param array {Array}
* @param i {int}
* @returns {Array}
* @private
*/
function flatten(array, i) {
i = i || 0;
if(i >= array.length)
return array;
if(isArray(array[i])) {
return flatten(array.slice(0,i)
.concat(array[i], array.slice(i+1)), i);
}
return flatten(array, i+1);
}
/**
* @ngdoc filter
* @name fuzzyByKey
* @kind function
*
* @description
* fuzzy string searching by key
*/
angular.module('a8m.fuzzy-by', [])
.filter('fuzzyBy', ['$parse', function ( $parse ) {
return function (collection, property, search, csensitive) {
var sensitive = csensitive || false,
prop, getter;
collection = isObject(collection) ? toArray(collection) : collection;
if(!isArray(collection) || isUndefined(property)
|| isUndefined(search)) {
return collection;
}
getter = $parse(property);
return collection.filter(function(elm) {
prop = getter(elm);
if(!isString(prop)) {
return false;
}
prop = (sensitive) ? prop : prop.toLowerCase();
search = (sensitive) ? search : search.toLowerCase();
return hasApproxPattern(prop, search) !== false
})
}
}]);
/**
* @ngdoc filter
* @name fuzzy
* @kind function
*
* @description
* fuzzy string searching for array of strings, objects
*/
angular.module('a8m.fuzzy', [])
.filter('fuzzy', function () {
return function (collection, search, csensitive) {
var sensitive = csensitive || false;
collection = isObject(collection) ? toArray(collection) : collection;
if(!isArray(collection) || isUndefined(search)) {
return collection;
}
search = (sensitive) ? search : search.toLowerCase();
return collection.filter(function(elm) {
if(isString(elm)) {
elm = (sensitive) ? elm : elm.toLowerCase();
return hasApproxPattern(elm, search) !== false
}
return (isObject(elm)) ? _hasApproximateKey(elm, search) : false;
});
/**
* checks if object has key{string} that match
* to fuzzy search pattern
* @param object
* @param search
* @returns {boolean}
* @private
*/
function _hasApproximateKey(object, search) {
var properties = Object.keys(object),
prop, flag;
return 0 < properties.filter(function (elm) {
prop = object[elm];
//avoid iteration if we found some key that equal[performance]
if(flag) return true;
if (isString(prop)) {
prop = (sensitive) ? prop : prop.toLowerCase();
return flag = (hasApproxPattern(prop, search) !== false);
}
return false;
}).length;
}
}
});
/**
* @ngdoc filter
* @name groupBy
* @kind function
*
* @description
* Create an object composed of keys generated from the result of running each element of a collection,
* each key is an array of the elements.
*/
angular.module('a8m.group-by', [ 'a8m.filter-watcher' ])
.filter('groupBy', [ '$parse', 'filterWatcher', function ( $parse, filterWatcher ) {
return function (collection, property) {
if(!isObject(collection) || isUndefined(property)) {
return collection;
}
return filterWatcher.isMemoized('groupBy', arguments) ||
filterWatcher.memoize('groupBy', arguments, this,
_groupBy(collection, $parse(property)));
/**
* groupBy function
* @param collection
* @param getter
* @returns {{}}
*/
function _groupBy(collection, getter) {
var result = {};
var prop;
forEach( collection, function( elm ) {
prop = getter(elm);
if(!result[prop]) {
result[prop] = [];
}
result[prop].push(elm);
});
return result;
}
}
}]);
/**
* @ngdoc filter
* @name isEmpty
* @kind function
*
* @description
* get collection or string and return if it empty
*/
angular.module('a8m.is-empty', [])
.filter('isEmpty', function () {
return function(collection) {
return isObject(collection)
? !toArray(collection).length
: !collection.length;
}
});
/**
* @ngdoc filter
* @name join
* @kind function
*
* @description
* join a collection by a provided delimiter (space by default)
*/
angular.module('a8m.join', [])
.filter('join', function () {
return function (input, delimiter) {
if (isUndefined(input) || !isArray(input)) {
return input;
}
if (isUndefined(delimiter)) delimiter = ' ';
return input.join(delimiter);
};
})
;
/**
* @ngdoc filter
* @name last
* @kind function
*
* @description
* Gets the last element or last n elements of an array
* if callback is provided, is returns as long the callback return truthy
*/
angular.module('a8m.last', [])
.filter('last', ['$parse', function( $parse ) {
return function(collection) {
var n
, getter
, args
//cuz reverse change our src collection
//and we don't want side effects
, reversed = copy(collection);
reversed = isObject(reversed)
? toArray(reversed)
: reversed;
if(!isArray(reversed)) {
return reversed;
}
args = Array.prototype.slice.call(arguments, 1);
n = (isNumber(args[0])) ? args[0] : 1;
getter = (!isNumber(args[0])) ? args[0] : (!isNumber(args[1])) ? args[1] : undefined;
return (args.length)
//send reversed collection as arguments, and reverse it back as result
? getFirstMatches(reversed.reverse(), n,(getter) ? $parse(getter) : getter).reverse()
//get the last element
: reversed[reversed.length-1];
}
}]);
/**
* @ngdoc filter
* @name map
* @kind function
*
* @description
* Returns a new collection of the results of each expression execution.
*/
angular.module('a8m.map', [])
.filter('map', ['$parse', function($parse) {
return function (collection, expression) {
collection = isObject(collection)
? toArray(collection)
: collection;
if(!isArray(collection) || isUndefined(expression)) {
return collection;
}
return collection.map(function (elm) {
return $parse(expression)(elm);
});
}
}]);
/**
* @ngdoc filter
* @name omit
* @kind function
*
* @description
* filter collection by expression
*/
angular.module('a8m.omit', [])
.filter('omit', ['$parse', function($parse) {
return function (collection, expression) {
collection = isObject(collection)
? toArray(collection)
: collection;
if(!isArray(collection) || isUndefined(expression)) {
return collection;
}
return collection.filter(function (elm) {
return !($parse(expression)(elm));
});
}
}]);
/**
* @ngdoc filter
* @name pick
* @kind function
*
* @description
* filter collection by expression
*/
angular.module('a8m.pick', [])
.filter('pick', ['$parse', function($parse) {
return function (collection, expression) {
collection = isObject(collection)
? toArray(collection)
: collection;
if(!isArray(collection) || isUndefined(expression)) {
return collection;
}
return collection.filter(function (elm) {
return $parse(expression)(elm);
});
}
}]);
/**
* @ngdoc filter
* @name range
* @kind function
*
* @description
* rangeFilter provides some support for a for loop using numbers
*/
angular.module('a8m.range', [])
.filter('range', function () {
return function (input, total, start, increment, cb) {
start = start || 0;
increment = increment || 1;
for (var i = 0; i < parseInt(total); i++) {
var j = start + i * increment;
input.push(isFunction(cb) ? cb(j) : j);
}
return input;
};
});
/**
* @ngdoc filter
* @name removeWith
* @kind function
*
* @description
* get collection and properties object, and removed elements
* with this properties
*/
angular.module('a8m.remove-with', [])
.filter('removeWith', function() {
return function (collection, object) {
if(isUndefined(object)) {
return collection;
}
collection = isObject(collection)
? toArray(collection)
: collection;
return collection.filter(function (elm) {
return !objectContains(object, elm);
});
}
});
/**
* @ngdoc filter
* @name remove
* @kind function
*
* @description
* remove specific members from collection
*/
angular.module('a8m.remove', [])
.filter('remove', function () {
return function (collection) {
collection = isObject(collection) ? toArray(collection) : collection;
var args = Array.prototype.slice.call(arguments, 1);
if(!isArray(collection)) {
return collection;
}
return collection.filter( function( member ) {
return !args.some(function(nest) {
return equals(nest, member);
})
});
}
});
/**
* @ngdoc filter
* @name reverse
* @kind function
*
* @description
* Reverses a string or collection
*/
angular.module('a8m.reverse', [])
.filter('reverse',[ function () {
return function (input) {
input = isObject(input) ? toArray(input) : input;
if(isString(input)) {
return input.split('').reverse().join('');
}
return isArray(input)
? input.slice().reverse()
: input;
}
}]);
/**
* @ngdoc filter
* @name searchField
* @kind function
*
* @description
* for each member, join several strings field and add them to
* new field called 'searchField' (use for search filtering)
*/
angular.module('a8m.search-field', [])
.filter('searchField', ['$parse', function ($parse) {
return function (collection) {
var get, field;
collection = isObject(collection) ? toArray(collection) : collection;
var args = Array.prototype.slice.call(arguments, 1);
if(!isArray(collection) || !args.length) {
return collection;
}
return collection.map(function(member) {
field = args.map(function(field) {
get = $parse(field);
return get(member);
}).join(' ');
return extend(member, { searchField: field });
});
}
}]);
/**
* @ngdoc filter
* @name toArray
* @kind function
*
* @description
* Convert objects into stable arrays.
* if addKey set to true,the filter also attaches a new property
* $key to the value containing the original key that was used in
* the object we are iterating over to reference the property
*/
angular.module('a8m.to-array', [])
.filter('toArray', function() {
return function (collection, addKey) {
if(!isObject(collection)) {
return collection;
}
return !addKey
? toArray(collection)
: Object.keys(collection).map(function (key) {
return extend(collection[key], { $key: key });
});
}
});
/**
* @ngdoc filter
* @name unique/uniq
* @kind function
*
* @description
* get collection and filter duplicate members
* if uniqueFilter get a property(nested to) as argument it's
* filter by this property as unique identifier
*/
angular.module('a8m.unique', [])
.filter({
unique: ['$parse', uniqFilter],
uniq: ['$parse', uniqFilter]
});
function uniqFilter($parse) {
return function (collection, property) {
collection = isObject(collection) ? toArray(collection) : collection;
if (!isArray(collection)) {
return collection;
}
//store all unique identifiers
var uniqueItems = [],
get = $parse(property);
return (isUndefined(property))
//if it's kind of primitive array
? collection.filter(function (elm, pos, self) {
return self.indexOf(elm) === pos;
})
//else compare with equals
: collection.filter(function (elm) {
var prop = get(elm);
if(some(uniqueItems, prop)) {
return false;
}
uniqueItems.push(prop);
return true;
});
//checked if the unique identifier is already exist
function some(array, member) {
if(isUndefined(member)) {
return false;
}
return array.some(function(el) {
return equals(el, member);
});
}
}
}
/**
* @ngdoc filter
* @name where
* @kind function
*
* @description
* of each element in a collection to the given properties object,
* returning an array of all elements that have equivalent property values.
*
*/
angular.module('a8m.where', [])
.filter('where', function() {
return function (collection, object) {
if(isUndefined(object)) return collection;
collection = isObject(collection)
? toArray(collection)
: collection;
return collection.filter(function (elm) {
return objectContains(object, elm);
});
}
});
/**
* @ngdoc filter
* @name xor
* @kind function
*
* @description
* Exclusive or filter by expression
*/
angular.module('a8m.xor', [])
.filter('xor', ['$parse', function($parse) {
return function (col1, col2, expression) {
expression = expression || false;
col1 = isObject(col1) ? toArray(col1) : col1;
col2 = isObject(col2) ? toArray(col2) : col2;
if(!isArray(col1) || !isArray(col2)) return col1;
return col1.concat(col2)
.filter(function(elm) {
return !(some(elm, col1) && some(elm, col2));
});
function some(el, col) {
var getter = $parse(expression);
return col.some(function(dElm) {
return expression
? equals(getter(dElm), getter(el))
: equals(dElm, el);
});
}
}
}]);
/**
* @ngdoc filter
* @name abs
* @kind function
*
* @description
* Will return the absolute value of a number
*/
angular.module('a8m.math.abs', [])
.filter('abs', function () {
return function (input) {
return Math.abs(input);
}
});
/**
* @ngdoc filter
* @name formatBytes
* @kind function
*
* @description
* Convert bytes into appropriate display
* 1024 bytes => 1 KB
*/
angular.module('a8m.math.byteFmt', [])
.filter('byteFmt', function () {
var compared = [{str: 'B', val: 1024}];
['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'].forEach(function(el, i) {
compared.push({str: el, val: compared[i].val * 1024 });
});
return function (bytes, decimal) {
if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
isNumber(bytes) && isFinite(bytes)) {
var i = 0;
while (i < compared.length-1 && bytes >= compared[i].val) i++;
bytes /= i > 0 ? compared[i-1].val : 1;
return convertToDecimal(bytes, decimal) + ' ' + compared[i].str;
}
return 'NaN';
}
});
/**
* @ngdoc filter
* @name degrees
* @kind function
*
* @description
* Convert angle from radians to degrees
*/
angular.module('a8m.math.degrees', [])
.filter('degrees', function () {
return function (radians, decimal) {
// if decimal is not an integer greater than -1, we cannot do. quit with error "NaN"
// if degrees is not a real number, we cannot do also. quit with error "NaN"
if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
isNumber(radians) && isFinite(radians)) {
var degrees = (radians * 180) / Math.PI;
return Math.round(degrees * Math.pow(10,decimal)) / (Math.pow(10,decimal));
} else {
return 'NaN';
}
}
});
/**
* @ngdoc filter
* @name formatBytes
* @kind function
*
* @description
* Convert bytes into appropriate display
* 1024 kilobytes => 1 MB
*/
angular.module('a8m.math.kbFmt', [])
.filter('kbFmt', function () {
var compared = [{str: 'KB', val: 1024}];
['MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'].forEach(function(el, i) {
compared.push({str: el, val: compared[i].val * 1024 });
});
return function (bytes, decimal) {
if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
isNumber(bytes) && isFinite(bytes)) {
var i = 0;
while (i < compared.length-1 && bytes >= compared[i].val) i++;
bytes /= i > 0 ? compared[i-1].val : 1;
return convertToDecimal(bytes, decimal) + ' ' + compared[i].str;
}
return 'NaN';
}
});
/**
* @ngdoc filter
* @name max
* @kind function
*
* @description
* Math.max will get an array and return the max value. if an expression
* is provided, will return max value by expression.
*/
angular.module('a8m.math.max', [])
.filter('max', ['$parse', function ($parse) {
return function (input, expression) {
if(!isArray(input)) {
return input;
}
return isUndefined(expression)
? Math.max.apply(Math, input)
: input[indexByMax(input, expression)];
};
/**
* @private
* @param array
* @param exp
* @returns {number|*|Number}
*/
function indexByMax(array, exp) {
var mappedArray = array.map(function(elm){
return $parse(exp)(elm);
});
return mappedArray.indexOf(Math.max.apply(Math, mappedArray));
}
}]);
/**
* @ngdoc filter
* @name min
* @kind function
*
* @description
* Math.min will get an array and return the min value. if an expression
* is provided, will return min value by expression.
*/
angular.module('a8m.math.min', [])
.filter('min', ['$parse', function ($parse) {
return function (input, expression) {
if(!isArray(input)) {
return input;
}
return isUndefined(expression)
? Math.min.apply(Math, input)
: input[indexByMin(input, expression)];
};
/**
* @private
* @param array
* @param exp
* @returns {number|*|Number}
*/
function indexByMin(array, exp) {
var mappedArray = array.map(function(elm){
return $parse(exp)(elm);
});
return mappedArray.indexOf(Math.min.apply(Math, mappedArray));
}
}]);
/**
* @ngdoc filter
* @name Percent
* @kind function
*
* @description
* percentage between two numbers
*/
angular.module('a8m.math.percent', [])
.filter('percent', function () {
return function (input, divided, round) {
var divider = isString(input) ? Number(input) : input;
divided = divided || 100;
round = round || false;
if (!isNumber(divider) || isNaN(divider)) return input;
return round
? Math.round((divider / divided) * 100)
: (divider / divided) * 100;
}
});
/**
* @ngdoc filter
* @name toRadians
* @kind function
*
* @description
* Convert angle from degrees to radians
*/
angular.module('a8m.math.radians', [])
.filter('radians', function() {
return function (degrees, decimal) {
// if decimal is not an integer greater than -1, we cannot do. quit with error "NaN"
// if degrees is not a real number, we cannot do also. quit with error "NaN"
if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
isNumber(degrees) && isFinite(degrees)) {
var radians = (degrees * 3.14159265359) / 180;
return Math.round(radians * Math.pow(10,decimal)) / (Math.pow(10,decimal));
}
return 'NaN';
}
});
/**
* @ngdoc filter
* @name Radix
* @kind function
*
* @description
* converting decimal numbers to different bases(radix)
*/
angular.module('a8m.math.radix', [])
.filter('radix', function () {
return function (input, radix) {
var RANGE = /^[2-9]$|^[1-2]\d$|^3[0-6]$/;
if(!isNumber(input) || !RANGE.test(radix)) {
return input;
}
return input.toString(radix).toUpperCase();
}
});
/**
* @ngdoc filter
* @name formatBytes
* @kind function
*
* @description
* Convert number into abbreviations.
* i.e: K for one thousand, M for Million, B for billion
* e.g: number of users:235,221, decimal:1 => 235.2 K
*/
angular.module('a8m.math.shortFmt', [])
.filter('shortFmt', function () {
return function (number, decimal) {
if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
isNumber(number) && isFinite(number)){
if(number < 1e3) {
return '' + number; // Coerce to string
} else if(number < 1e6) {
return convertToDecimal((number / 1e3), decimal) + ' K';
} else if(number < 1e9){
return convertToDecimal((number / 1e6), decimal) + ' M';
} else {
return convertToDecimal((number / 1e9), decimal) + ' B';
}
}
return 'NaN';
}
});
/**
* @ngdoc filter
* @name sum
* @kind function
*
* @description
* Sum up all values within an array
*/
angular.module('a8m.math.sum', [])
.filter('sum', function () {
return function (input, initial) {
return !isArray(input)
? input
: input.reduce(function(prev, curr) {
return prev + curr;
}, initial || 0);
}
});
/**
* @ngdoc filter
* @name endsWith
* @kind function
*
* @description
* checks whether string ends with the ends parameter.
*/
angular.module('a8m.ends-with', [])
.filter('endsWith', function () {
return function (input, ends, csensitive) {
var sensitive = csensitive || false,
position;
if(!isString(input) || isUndefined(ends)) {
return input;
}
input = (sensitive) ? input : input.toLowerCase();
position = input.length - ends.length;
return input.indexOf((sensitive) ? ends : ends.toLowerCase(), position) !== -1;
}
});
/**
* @ngdoc filter
* @name latinize
* @kind function
*
* @description
* remove accents/diacritics from a string
*/
angular.module('a8m.latinize', [])
.filter('latinize',[ function () {
var defaultDiacriticsRemovalap = [
{'base':'A', 'letters':'\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F'},
{'base':'AA','letters':'\uA732'},
{'base':'AE','letters':'\u00C6\u01FC\u01E2'},
{'base':'AO','letters':'\uA734'},
{'base':'AU','letters':'\uA736'},
{'base':'AV','letters':'\uA738\uA73A'},
{'base':'AY','letters':'\uA73C'},
{'base':'B', 'letters':'\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181'},
{'base':'C', 'letters':'\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E'},
{'base':'D', 'letters':'\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779'},
{'base':'DZ','letters':'\u01F1\u01C4'},
{'base':'Dz','letters':'\u01F2\u01C5'},
{'base':'E', 'letters':'\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E'},
{'base':'F', 'letters':'\u0046\u24BB\uFF26\u1E1E\u0191\uA77B'},
{'base':'G', 'letters':'\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E'},
{'base':'H', 'letters':'\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D'},
{'base':'I', 'letters':'\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197'},
{'base':'J', 'letters':'\u004A\u24BF\uFF2A\u0134\u0248'},
{'base':'K', 'letters':'\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2'},
{'base':'L', 'letters':'\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780'},
{'base':'LJ','letters':'\u01C7'},
{'base':'Lj','letters':'\u01C8'},
{'base':'M', 'letters':'\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C'},
{'base':'N', 'letters':'\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4'},
{'base':'NJ','letters':'\u01CA'},
{'base':'Nj','letters':'\u01CB'},
{'base':'O', 'letters':'\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C'},
{'base':'OI','letters':'\u01A2'},
{'base':'OO','letters':'\uA74E'},
{'base':'OU','letters':'\u0222'},
{'base':'OE','letters':'\u008C\u0152'},
{'base':'oe','letters':'\u009C\u0153'},
{'base':'P', 'letters':'\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754'},
{'base':'Q', 'letters':'\u0051\u24C6\uFF31\uA756\uA758\u024A'},
{'base':'R', 'letters':'\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782'},
{'base':'S', 'letters':'\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784'},
{'base':'T', 'letters':'\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786'},
{'base':'TZ','letters':'\uA728'},
{'base':'U', 'letters':'\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244'},
{'base':'V', 'letters':'\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245'},
{'base':'VY','letters':'\uA760'},
{'base':'W', 'letters':'\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72'},
{'base':'X', 'letters':'\u0058\u24CD\uFF38\u1E8A\u1E8C'},
{'base':'Y', 'letters':'\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE'},
{'base':'Z', 'letters':'\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762'},
{'base':'a', 'letters':'\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250'},
{'base':'aa','letters':'\uA733'},
{'base':'ae','letters':'\u00E6\u01FD\u01E3'},
{'base':'ao','letters':'\uA735'},
{'base':'au','letters':'\uA737'},
{'base':'av','letters':'\uA739\uA73B'},
{'base':'ay','letters':'\uA73D'},
{'base':'b', 'letters':'\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253'},
{'base':'c', 'letters':'\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184'},
{'base':'d', 'letters':'\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A'},
{'base':'dz','letters':'\u01F3\u01C6'},
{'base':'e', 'letters':'\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD'},
{'base':'f', 'letters':'\u0066\u24D5\uFF46\u1E1F\u0192\uA77C'},
{'base':'g', 'letters':'\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F'},
{'base':'h', 'letters':'\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265'},
{'base':'hv','letters':'\u0195'},
{'base':'i', 'letters':'\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131'},
{'base':'j', 'letters':'\u006A\u24D9\uFF4A\u0135\u01F0\u0249'},
{'base':'k', 'letters':'\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3'},
{'base':'l', 'letters':'\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747'},
{'base':'lj','letters':'\u01C9'},
{'base':'m', 'letters':'\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F'},
{'base':'n', 'letters':'\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5'},
{'base':'nj','letters':'\u01CC'},
{'base':'o', 'letters':'\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275'},
{'base':'oi','letters':'\u01A3'},
{'base':'ou','letters':'\u0223'},
{'base':'oo','letters':'\uA74F'},
{'base':'p','letters':'\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755'},
{'base':'q','letters':'\u0071\u24E0\uFF51\u024B\uA757\uA759'},
{'base':'r','letters':'\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783'},
{'base':'s','letters':'\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B'},
{'base':'t','letters':'\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787'},
{'base':'tz','letters':'\uA729'},
{'base':'u','letters': '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289'},
{'base':'v','letters':'\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C'},
{'base':'vy','letters':'\uA761'},
{'base':'w','letters':'\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73'},
{'base':'x','letters':'\u0078\u24E7\uFF58\u1E8B\u1E8D'},
{'base':'y','letters':'\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF'},
{'base':'z','letters':'\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763'}
];
var diacriticsMap = {};
for (var i = 0; i < defaultDiacriticsRemovalap.length; i++) {
var letters = defaultDiacriticsRemovalap[i].letters.split("");
for (var j = 0; j < letters.length ; j++){
diacriticsMap[letters[j]] = defaultDiacriticsRemovalap[i].base;
}
}
// "what?" version ... http://jsperf.com/diacritics/12
function removeDiacritics (str) {
return str.replace(/[^\u0000-\u007E]/g, function(a){
return diacriticsMap[a] || a;
});
}
return function (input) {
return isString(input)
? removeDiacritics(input)
: input;
}
}]);
/**
* @ngdoc filter
* @name ltrim
* @kind function
*
* @description
* Left trim. Similar to trimFilter, but only for left side.
*/
angular.module('a8m.ltrim', [])
.filter('ltrim', function () {
return function(input, chars) {
var trim = chars || '\\s';
return isString(input)
? input.replace(new RegExp('^' + trim + '+'), '')
: input;
}
});
/**
* @ngdoc filter
* @name match
* @kind function
*
* @description
* Return the matched pattern in a string.
*/
angular.module('a8m.match', [])
.filter('match', function () {
return function (input, pattern, flag) {
var reg = new RegExp(pattern, flag);
return isString(input)
? input.match(reg)
: null;
}
});
/**
* @ngdoc filter
* @name phone-us
* @kind function
*
* @description
* format a string or a number into a us-style
* phone number in the form (***) ***-****
*/
angular.module('a8m.phoneUS', [])
.filter('phoneUS', function () {
return function(num) {
num += '';
return '(' + num.slice(0, 3) + ') ' + num.slice(3, 6) + '-' + num.slice(6);
}
});
/**
* @ngdoc filter
* @name repeat
* @kind function
*
* @description
* Repeats a string n times
*/
angular.module('a8m.re