PageSourceSearch

https://mutation.ndita.org/PortalManagement/AngularScript/angular-filter.js

js ndita.org collected 2026-10-02 13:23:39 UTC 43,288 bytes, 1,993 lines download raw bytes

1/**
2 * Bunch of useful filters for angularJS
3 * @version v0.4.7 - 2014-10-03 * @link https://github.com/a8m/angular-filter
4 * @author Ariel Mashraki <[email protected]>
5 * @license MIT License, http://www.opensource.org/licenses/MIT
6 */
7(function ( window, angular, undefined ) {
8/*jshint globalstrict:true*/
9'use strict';
10
11var isDefined = angular.isDefined,
12    isUndefined = angular.isUndefined,
13    isFunction = angular.isFunction,
14    isString = angular.isString,
15    isNumber = angular.isNumber,
16    isObject = angular.isObject,
17    isArray = angular.isArray,
18    forEach = angular.forEach,
19    extend = angular.extend,
20    copy = angular.copy,
21    equals = angular.equals;
22
23
24/**
25 * get an object and return array of values
26 * @param object
27 * @returns {Array}
28 */
29function toArray(object) {
30  return isArray(object) ? object :
31    Object.keys(object).map(function(key) {
32      return object[key];
33    });
34}
35
36/**
37 *
38 * @param value
39 * @returns {boolean}
40 */
41function isNull(value) {
42    return value === null;
43}
44
45/**
46 * return if object contains partial object
47 * @param partial{object}
48 * @param object{object}
49 * @returns {boolean}
50 */
51function objectContains(partial, object) {
52  var keys = Object.keys(partial);
53
54  return keys.map(function(el) {
55    return !(!object[el] || (object[el] != partial[el]));
56  }).indexOf(false) == -1;
57
58}
59
60/**
61 * search for approximate pattern in string
62 * @param word
63 * @param pattern
64 * @returns {*}
65 */
66function hasApproxPattern(word, pattern) {
67  if(pattern === '')
68    return word;
69
70  var index = word.indexOf(pattern.charAt(0));
71
72  if(index === -1)
73    return false;
74
75  return hasApproxPattern(word.substr(index+1), pattern.substr(1))
76}
77
78/**
79 * return the first n element of an array,
80 * if expression provided, is returns as long the expression return truthy
81 * @param array
82 * @param n {number}
83 * @param expression {$parse}
84 * @return array or single object
85 */
86function getFirstMatches(array, n, expression) {
87  var count = 0;
88
89  return array.filter(function(elm) {
90    var rest = isDefined(expression) ? (count < n && expression(elm)) : count < n;
91    count = rest ? count+1 : count;
92
93    return rest;
94  });
95}
96/**
97 * Polyfill to ECMA6 String.prototype.contains
98 */
99if (!String.prototype.contains) {
100  String.prototype.contains = function() {
101    return String.prototype.indexOf.apply(this, arguments) !== -1;
102  };
103}
104
105/**
106 *
107 * @param num {Number}
108 * @param decimal {Number}
109 * @param $math
110 * @returns {Number}
111 */
112function convertToDecimal(num, decimal, $math){
113  return $math.round(num * $math.pow(10,decimal)) / ($math.pow(10,decimal));
114}
115/**
116 * @ngdoc filter
117 * @name a8m.angular
118 * @kind function
119 *
120 * @description
121 * reference to angular function
122 */
123
124angular.module('a8m.angular', [])
125
126    .filter('isUndefined', function () {
127      return function (input) {
128        return angular.isUndefined(input);
129      }
130    })
131    .filter('isDefined', function() {
132      return function (input) {
133        return angular.isDefined(input);
134      }
135    })
136    .filter('isFunction', function() {
137      return function (input) {
138        return angular.isFunction(input);
139      }
140    })
141    .filter('isString', function() {
142      return function (input) {
143        return angular.isString(input)
144      }
145    })
146    .filter('isNumber', function() {
147      return function (input) {
148        return angular.isNumber(input);
149      }
150    })
151    .filter('isArray', function() {
152      return function (input) {
153        return angular.isArray(input);
154      }
155    })
156    .filter('isObject', function() {
157      return function (input) {
158        return angular.isObject(input);
159      }
160    })
161    .filter('isEqual', function() {
162      return function (o1, o2) {
163        return angular.equals(o1, o2);
164      }
165    });
166
167/**
168 * @ngdoc filter
169 * @name a8m.conditions
170 * @kind function
171 *
172 * @description
173 * reference to math conditions
174 */
175 angular.module('a8m.conditions', [])
176
177  .filter({
178    isGreaterThan  : isGreaterThanFilter,
179    '>'            : isGreaterThanFilter,
180
181    isGreaterThanOrEqualTo  : isGreaterThanOrEqualToFilter,
182    '>='                    : isGreaterThanOrEqualToFilter,
183
184    isLessThan  : isLessThanFilter,
185    '<'         : isLessThanFilter,
186
187    isLessThanOrEqualTo  : isLessThanOrEqualToFilter,
188    '<='                 : isLessThanOrEqualToFilter,
189
190    isEqualTo  : isEqualToFilter,
191    '=='       : isEqualToFilter,
192
193    isNotEqualTo  : isNotEqualToFilter,
194    '!='          : isNotEqualToFilter,
195
196    isIdenticalTo  : isIdenticalToFilter,
197    '==='          : isIdenticalToFilter,
198
199    isNotIdenticalTo  : isNotIdenticalToFilter,
200    '!=='             : isNotIdenticalToFilter
201  });
202
203  function isGreaterThanFilter() {
204    return function (input, check) {
205      return input > check;
206    };
207  }
208
209  function isGreaterThanOrEqualToFilter() {
210    return function (input, check) {
211      return input >= check;
212    };
213  }
214
215  function isLessThanFilter() {
216    return function (input, check) {
217      return input < check;
218    };
219  }
220
221  function isLessThanOrEqualToFilter() {
222    return function (input, check) {
223      return input <= check;
224    };
225  }
226
227  function isEqualToFilter() {
228    return function (input, check) {
229      return input == check;
230    };
231  }
232
233  function isNotEqualToFilter() {
234    return function (input, check) {
235      return input != check;
236    };
237  }
238
239  function isIdenticalToFilter() {
240    return function (input, check) {
241      return input === check;
242    };
243  }
244
245  function isNotIdenticalToFilter() {
246    return function (input, check) {
247      return input !== check;
248    };
249  }
250/**
251 * @ngdoc filter
252 * @name isNull
253 * @kind function
254 *
255 * @description
256 * checks if value is null or not
257 * @return Boolean
258 */
259
260angular.module('a8m.is-null', [])
261
262    .filter('isNull', function () {
263      return function(input) {
264        return isNull(input);
265      }
266    });
267
268/**
269 * @ngdoc filter
270 * @name after-where
271 * @kind function
272 *
273 * @description
274 * get a collection and properties object, and returns all of the items
275 * in the collection after the first that found with the given properties.
276 *
277 */
278
279angular.module('a8m.after-where', [])
280    .filter('afterWhere', function() {
281      return function (collection, object) {
282
283        collection = (isObject(collection)) ?
284            toArray(collection) :
285            collection;
286
287        if(!isArray(collection) || isUndefined(object))
288          return collection;
289
290        var index = collection.map( function( elm ) {
291          return objectContains(object, elm);
292        }).indexOf( true );
293
294        return collection.slice((index === -1) ? 0 : index);
295      }
296    });
297
298/**
299 * @ngdoc filter
300 * @name after
301 * @kind function
302 *
303 * @description
304 * get a collection and specified count, and returns all of the items
305 * in the collection after the specified count.
306 *
307 */
308
309angular.module('a8m.after', [])
310    .filter('after', function() {
311      return function (collection, count) {
312
313        collection = (isObject(collection)) ?
314            toArray(collection) :
315            collection;
316
317        return (isArray(collection)) ?
318            collection.slice(count) :
319            collection;
320
321      }
322    });
323
324/**
325 * @ngdoc filter
326 * @name before-where
327 * @kind function
328 *
329 * @description
330 * get a collection and properties object, and returns all of the items
331 * in the collection before the first that found with the given properties.
332 *
333 */
334
335angular.module('a8m.before-where', [])
336  .filter('beforeWhere', function() {
337    return function (collection, object) {
338
339      collection = (isObject(collection)) ?
340        toArray(collection) :
341        collection;
342
343      if(!isArray(collection) || isUndefined(object))
344        return collection;
345
346      var index = collection.map( function( elm ) {
347        return objectContains(object, elm);
348      }).indexOf( true );
349
350      return collection.slice(0, (index === -1) ? collection.length : ++index);
351    }
352  });
353
354/**
355 * @ngdoc filter
356 * @name before
357 * @kind function
358 *
359 * @description
360 * get a collection and specified count, and returns all of the items
361 * in the collection before the specified count.
362 *
363 */
364
365angular.module('a8m.before', [])
366    .filter('before', function() {
367      return function (collection, count) {
368
369        collection = (isObject(collection)) ?
370            toArray(collection) :
371            collection;
372
373        return (isArray(collection)) ?
374            collection.slice(0, (!count) ? count : --count) :
375            collection;
376
377      }
378    });
379
380/**
381 * @ngdoc filter
382 * @name concat
383 * @kind function
384 *
385 * @description
386 * get (array/object, object/array) and return merged collection
387 *
388 */
389
390angular.module('a8m.concat', [])
391  //TODO:unique option ? or use unique filter to filter result
392  .filter('concat', [function () {
393    return function (collection, joined) {
394
395      if (isUndefined(joined)) {
396        return collection;
397      }
398      if (isArray(collection)) {
399        return (isObject(joined)) ?
400            collection.concat(toArray(joined)) :
401            collection.concat(joined);
402      }
403
404      if (isObject(collection)) {
405        var array = toArray(collection);
406        return (isObject(joined)) ?
407            array.concat(toArray(joined)) :
408            array.concat(joined);
409      }
410      return collection;
411    };
412  }
413]);
414
415/**
416 * @ngdoc filter
417 * @name contains
418 * @kind function
419 *
420 * @description
421 * Checks if given expression is present in one or more object in the collection
422 */
423
424angular.module('a8m.contains', [])
425  .filter({
426    contains: ['$parse', containsFilter],
427    some: ['$parse', containsFilter]
428  });
429
430function containsFilter( $parse ) {
431    return function (collection, expression) {
432
433      collection = (isObject(collection)) ? toArray(collection) : collection;
434
435      if(!isArray(collection) || isUndefined(expression)) {
436        return true;
437      }
438
439      return collection.some( function(elm) {
440
441        return (isObject(elm) || isFunction(expression)) ?
442          $parse(expression)(elm) :
443          elm === expression;
444
445      });
446
447    }
448 }
449
450/**
451 * @ngdoc filter
452 * @name countBy
453 * @kind function
454 *
455 * @description
456 * Sorts a list into groups and returns a count for the number of objects in each group.
457 */
458
459angular.module('a8m.count-by', [])
460
461  .filter('countBy', [ '$parse', function ( $parse ) {
462    return function (collection, property) {
463
464      var result = {},
465        get = $parse(property),
466        prop;
467
468      collection = (isObject(collection)) ? toArray(collection) : collection;
469
470      if(!isArray(collection) || isUndefined(property)) {
471        return collection;
472      }
473
474      collection.forEach( function( elm ) {
475        prop = get(elm);
476
477        if(!result[prop]) {
478          result[prop] = 0;
479        }
480
481        result[prop]++;
482      });
483
484      return result;
485    }
486  }]);
487
488/**
489 * @ngdoc filter
490 * @name every
491 * @kind function
492 *
493 * @description
494 * Checks if given expression is present in all members in the collection
495 *
496 */
497
498angular.module('a8m.every', [])
499  .filter('every', ['$parse', function($parse) {
500    return function (collection, expression) {
501
502      collection = (isObject(collection)) ? toArray(collection) : collection;
503
504      if(!isArray(collection) || isUndefined(expression)) {
505        return true;
506      }
507
508      return collection.every( function(elm) {
509
510        return (isObject(elm) || isFunction(expression)) ?
511          $parse(expression)(elm) :
512          elm === expression;
513      });
514
515    }
516  }]);
517
518/**
519 * @ngdoc filter
520 * @name filterBy
521 * @kind function
522 *
523 * @description
524 * filter by specific properties, avoid the rest
525 */
526angular.module('a8m.filter-by', [])
527
528  .filter('filterBy', ['$parse', function( $parse ) {
529    return function(collection, properties, search) {
530
531      var comparator;
532
533      search = (isString(search) || isNumber(search)) ?
534        String(search).toLowerCase() : undefined;
535
536      collection = (isObject(collection)) ? toArray(collection) : collection;
537
538      if(!isArray(collection) || isUndefined(search)) {
539        return collection;
540      }
541
542      return collection.filter(function(elm) {
543
544        return properties.some(function(prop) {
545
546          /**
547           * check if there is concatenate properties
548           * example:
549           * object: { first: 'foo', last:'bar' }
550           * filterBy: ['first + last'] => search by full name(i.e 'foo bar')
551           */
552          if(!~prop.indexOf('+')) {
553            comparator = $parse(prop)(elm)
554          } else {
555            var propList = prop.replace(new RegExp('\\s', 'g'), '').split('+');
556            comparator = propList.reduce(function(prev, cur, index) {
557              return (index === 1) ? $parse(prev)(elm) + ' ' + $parse(cur)(elm) :
558                prev + ' ' + $parse(cur)(elm);
559            });
560          }
561
562          return (isString(comparator) || isNumber(comparator)) ?
563            String(comparator).toLowerCase().contains(search) :
564            false;
565        })
566
567      });
568
569    }
570  }]);
571
572/**
573 * @ngdoc filter
574 * @name first
575 * @kind function
576 *
577 * @description
578 * Gets the first element or first n elements of an array
579 * if callback is provided, is returns as long the callback return truthy
580 */
581angular.module('a8m.first', [])
582
583  .filter('first', ['$parse', function( $parse ) {
584    return function(collection) {
585
586      var n,
587        getter,
588        args;
589
590      collection = (isObject(collection)) ? toArray(collection) :
591        collection;
592
593      if(!isArray(collection)) {
594        return collection;
595      }
596
597      args = Array.prototype.slice.call(arguments, 1);
598      n = (isNumber(args[0])) ? args[0] : 1;
599      getter = (!isNumber(args[0]))  ? args[0] : (!isNumber(args[1])) ? args[1] : undefined;
600
601      return (args.length) ? getFirstMatches(collection, n,(getter) ? $parse(getter) : getter) :
602        collection[0];
603    }
604  }]);
605
606/**
607 * @ngdoc filter
608 * @name flatten
609 * @kind function
610 *
611 * @description
612 * Flattens a nested array (the nesting can be to any depth).
613 * If you pass shallow, the array will only be flattened a single level
614 */
615
616angular.module('a8m.flatten', [])
617  .filter('flatten', function () {
618    return function(collection, shallow) {
619
620      shallow = shallow || false;
621      collection = (isObject(collection)) ? toArray(collection)
622        : collection;
623
624      if(!isArray(collection)) {
625        return collection;
626      }
627
628      return (!shallow) ? flatten(collection, 0) :
629        [].concat.apply([], collection);
630    }
631  });
632
633/**
634 * flatten nested array (the nesting can be to any depth).
635 * @param array {Array}
636 * @param i {int}
637 * @returns {Array}
638 * @private
639 */
640function flatten(array, i) {
641  i = i || 0;
642
643  if(i >= array.length)
644    return array;
645
646  if(isArray(array[i])) {
647    return flatten(array.slice(0,i)
648      .concat(array[i], array.slice(i+1)), i);
649  }
650  return flatten(array, i+1);
651}
652
653/**
654 * @ngdoc filter
655 * @name fuzzyByKey
656 * @kind function
657 *
658 * @description
659 * fuzzy string searching by key
660 */
661
662angular.module('a8m.fuzzy-by', [])
663  .filter('fuzzyBy', ['$parse', function ( $parse ) {
664    return function (collection, property, search, csensitive) {
665
666      var sensitive = csensitive || false,
667        prop, getter;
668
669      collection = (isObject(collection)) ? toArray(collection) : collection;
670
671      if(!isArray(collection) || isUndefined(property)
672        || isUndefined(search)) {
673        return collection;
674      }
675
676      getter = $parse(property);
677
678      return collection.filter(function(elm) {
679
680        prop = getter(elm);
681        if(!isString(prop)) {
682          return false;
683        }
684
685        prop = (sensitive) ? prop : prop.toLowerCase();
686        search = (sensitive) ? search : search.toLowerCase();
687
688        return hasApproxPattern(prop, search) !== false
689      })
690    }
691
692 }]);
693/**
694 * @ngdoc filter
695 * @name fuzzy
696 * @kind function
697 *
698 * @description
699 * fuzzy string searching for array of strings, objects
700 */
701
702angular.module('a8m.fuzzy', [])
703  .filter('fuzzy', function () {
704    return function (collection, search, csensitive) {
705
706      var sensitive = csensitive || false;
707
708      collection = (isObject(collection)) ? toArray(collection) : collection;
709
710      if(!isArray(collection) || isUndefined(search)) {
711        return collection;
712      }
713
714      search = (sensitive) ? search : search.toLowerCase();
715
716      return collection.filter(function(elm) {
717
718        if(isString(elm)) {
719          elm = (sensitive) ? elm : elm.toLowerCase();
720          return hasApproxPattern(elm, search) !== false
721        }
722
723        return (isObject(elm)) ? _hasApproximateKey(elm, search) : false;
724
725      });
726
727      /**
728       * checks if object has key{string} that match
729       * to fuzzy search pattern
730       * @param object
731       * @param search
732       * @returns {boolean}
733       * @private
734       */
735      function _hasApproximateKey(object, search) {
736        var properties = Object.keys(object),
737          prop, flag;
738        return 0 < properties.filter(function (elm) {
739          prop = object[elm];
740
741          //avoid iteration if we found some key that equal[performance]
742          if(flag) return true;
743
744          if (isString(prop)) {
745            prop = (sensitive) ? prop : prop.toLowerCase();
746            return flag = (hasApproxPattern(prop, search) !== false);
747          }
748
749          return false;
750
751        }).length;
752      }
753
754    }
755  });
756
757/**
758 * @ngdoc filter
759 * @name groupBy
760 * @kind function
761 *
762 * @description
763 * Create an object composed of keys generated from the result of running each element of a collection,
764 * each key is an array of the elements.
765 */
766
767angular.module('a8m.group-by', [ 'a8m.filter-watcher' ])
768
769  .filter('groupBy', [ '$parse', 'filterWatcher', function ( $parse, filterWatcher ) {
770    return function (collection, property) {
771
772      var result,
773        get = $parse(property),
774        prop;
775
776      if(!isObject(collection) || isUndefined(property)) {
777        return collection;
778      }
779
780      //Add collection instance to watch list
781      result = filterWatcher.$watch('groupBy', collection);
782
783      forEach( collection, function( elm ) {
784        prop = get(elm);
785
786        if(!result[prop]) {
787          result[prop] = [];
788        }
789
790        if(result[prop].indexOf( elm ) === -1) {
791          result[prop].push(elm);
792        }
793
794      });
795
796      //kill instance
797      filterWatcher.$destroy('groupBy', collection);
798
799      return result;
800    }
801 }]);
802
803/**
804 * @ngdoc filter
805 * @name isEmpty
806 * @kind function
807 *
808 * @description
809 * get collection or string and return if it empty
810 */
811
812angular.module('a8m.is-empty', [])
813  .filter('isEmpty', function () {
814    return function(collection) {
815      return (isObject(collection)) ?
816        !toArray(collection).length :
817        !collection.length;
818    }
819  });
820
821/**
822 * @ngdoc filter
823 * @name last
824 * @kind function
825 *
826 * @description
827 * Gets the last element or last n elements of an array
828 * if callback is provided, is returns as long the callback return truthy
829 */
830angular.module('a8m.last', [])
831
832  .filter('last', ['$parse', function( $parse ) {
833    return function(collection) {
834
835      var n,
836        getter,
837        args,
838      //cuz reverse change our src collection
839      //and we don't want side effects
840        reversed = copy(collection);
841
842      reversed = (isObject(reversed)) ? toArray(reversed) :
843        reversed;
844
845      if(!isArray(reversed)) {
846        return reversed;
847      }
848
849      args = Array.prototype.slice.call(arguments, 1);
850      n = (isNumber(args[0])) ? args[0] : 1;
851      getter = (!isNumber(args[0]))  ? args[0] : (!isNumber(args[1])) ? args[1] : undefined;
852
853      return (args.length) ?
854        //send reversed collection as arguments, and reverse it back as result
855        getFirstMatches(reversed.reverse(), n,(getter) ? $parse(getter) : getter).reverse() :
856        //get the last element
857        reversed[reversed.length-1];
858    }
859  }]);
860
861/**
862 * @ngdoc filter
863 * @name map
864 * @kind function
865 *
866 * @description
867 * Returns a new collection of the results of each expression execution.
868 */
869
870angular.module('a8m.map', [])
871
872  .filter('map', ['$parse', function($parse) {
873    return function (collection, expression) {
874
875      collection = (isObject(collection)) ?
876        toArray(collection) : collection;
877
878      if(!isArray(collection) || isUndefined(expression)) {
879        return collection;
880      }
881
882      return collection.map(function (elm) {
883
884        return $parse(expression)(elm);
885      });
886    }
887  }]);
888
889/**
890 * @ngdoc filter
891 * @name omit
892 * @kind function
893 *
894 * @description
895 * filter collection by expression
896 */
897
898angular.module('a8m.omit', [])
899
900  .filter('omit', ['$parse', function($parse) {
901    return function (collection, expression) {
902
903      collection = (isObject(collection)) ?
904        toArray(collection) : collection;
905
906      if(!isArray(collection) || isUndefined(expression)) {
907        return collection;
908      }
909
910      return collection.filter(function (elm) {
911
912        return !($parse(expression)(elm));
913      });
914    }
915  }]);
916
917/**
918 * @ngdoc filter
919 * @name omit
920 * @kind function
921 *
922 * @description
923 * filter collection by expression
924 */
925
926angular.module('a8m.pick', [])
927
928  .filter('pick', ['$parse', function($parse) {
929    return function (collection, expression) {
930
931      collection = (isObject(collection)) ?
932        toArray(collection) : collection;
933
934      if(!isArray(collection) || isUndefined(expression)) {
935        return collection;
936      }
937
938      return collection.filter(function (elm) {
939
940        return $parse(expression)(elm);
941      });
942    }
943  }]);
944
945/**
946 * @ngdoc filter
947 * @name removeWith
948 * @kind function
949 *
950 * @description
951 * get collection and properties object, and removed elements
952 * with this properties
953 */
954
955angular.module('a8m.remove-with', [])
956  .filter('removeWith', function() {
957    return function (collection, object) {
958
959      if(isUndefined(object)) {
960        return collection;
961      }
962      collection = (isObject(collection)) ?
963        toArray(collection) : collection;
964
965      return collection.filter(function (elm) {
966        return !objectContains(object, elm);
967      });
968    }
969  });
970
971
972/**
973 * @ngdoc filter
974 * @name remove
975 * @kind function
976 *
977 * @description
978 * remove specific members from collection
979 */
980
981angular.module('a8m.remove', [])
982
983  .filter('remove', function () {
984    return function (collection) {
985
986      collection = (isObject(collection)) ? toArray(collection) : collection;
987
988      var args = Array.prototype.slice.call(arguments, 1);
989
990      if(!isArray(collection)) {
991        return collection;
992      }
993
994      return collection.filter( function( member ) {
995        return !args.some(function(nest) {
996          return equals(nest, member);
997        })
998      });
999
1000    }
1001  });
1002
1003/**
1004 * @ngdoc filter
1005 * @name reverse
1006 * @kind function
1007 *
1008 * @description
1009 * Reverses a string or collection
1010 */
1011
1012angular.module('a8m.reverse', [])
1013
1014    .filter('reverse',[ function () {
1015      return function (input) {
1016
1017        input = (isObject(input)) ? toArray(input) : input;
1018
1019        if(isString(input)) {
1020          return input.split('').reverse().join('');
1021        }
1022
1023        return (isArray(input)) ? input.reverse() : input;
1024      }
1025    }]);
1026
1027
1028/**
1029 * @ngdoc filter
1030 * @name searchField
1031 * @kind function
1032 *
1033 * @description
1034 * for each member, join several strings field and add them to
1035 * new field called 'searchField' (use for search filtering)
1036 */
1037
1038angular.module('a8m.search-field', [])
1039
1040  .filter('searchField', ['$parse', function ($parse) {
1041    return function (collection) {
1042
1043      var get, field;
1044
1045      collection = (isObject(collection)) ? toArray(collection) : collection;
1046
1047      var args = Array.prototype.slice.call(arguments, 1);
1048
1049      if(!isArray(collection) || !args.length) {
1050        return collection;
1051      }
1052
1053      return collection.map(function(member) {
1054
1055        field = args.map(function(field) {
1056          get = $parse(field);
1057          return get(member);
1058        }).join(' ');
1059
1060        return extend(member, { searchField: field });
1061      });
1062    }
1063  }]);
1064
1065/**
1066 * @ngdoc filter
1067 * @name toArray
1068 * @kind function
1069 *
1070 * @description
1071 * Convert objects into stable arrays.
1072 * if addKey set to true,the filter also attaches a new property
1073 * $key to the value containing the original key that was used in
1074 * the object we are iterating over to reference the property
1075 */
1076
1077angular.module('a8m.to-array', [])
1078
1079  .filter('toArray', function() {
1080    return function (collection, addKey) {
1081
1082      if(!isObject(collection)) {
1083        return collection;
1084      }
1085
1086      return (!addKey) ? toArray(collection) :
1087
1088        Object.keys(collection).map(function (key) {
1089          return extend(collection[key], { $key: key });
1090        });
1091    }
1092  });
1093
1094/**
1095 * @ngdoc filter
1096 * @name unique/uniq
1097 * @kind function
1098 *
1099 * @description
1100 * get collection and filter duplicate members
1101 * if uniqueFilter get a property(nested to) as argument it's
1102 * filter by this property as unique identifier
1103 */
1104
1105angular.module('a8m.unique', [])
1106  .filter({
1107      unique: ['$parse', uniqFilter],
1108      uniq: ['$parse', uniqFilter]
1109    });
1110
1111function uniqFilter($parse) {
1112    return function (collection, property) {
1113
1114      collection = (isObject(collection)) ? toArray(collection) : collection;
1115
1116      if (!isArray(collection)) {
1117        return collection;
1118      }
1119
1120      //store all unique identifiers
1121      var uniqueItems = [],
1122          get = $parse(property);
1123
1124      return (isUndefined(property)) ?
1125        //if it's kind of primitive array
1126        collection.filter(function (elm, pos, self) {
1127          return self.indexOf(elm) === pos;
1128        }) :
1129        //else compare with equals
1130        collection.filter(function (elm) {
1131          var prop = get(elm);
1132          if(some(uniqueItems, prop)) {
1133            return false;
1134          }
1135          uniqueItems.push(prop);
1136          return true;
1137      });
1138
1139      //checked if the unique identifier is already exist
1140      function some(array, member) {
1141        if(isUndefined(member)) {
1142          return false;
1143        }
1144        return array.some(function(el) {
1145          return equals(el, member);
1146        });
1147      }
1148
1149    }
1150}
1151
1152/**
1153 * @ngdoc filter
1154 * @name where
1155 * @kind function
1156 *
1157 * @description
1158 * of each element in a collection to the given properties object,
1159 * returning an array of all elements that have equivalent property values.
1160 *
1161 */
1162
1163angular.module('a8m.where', [])
1164  .filter('where', function() {
1165    return function (collection, object) {
1166
1167      if(isUndefined(object)) {
1168        return collection;
1169      }
1170      collection = (isObject(collection)) ?
1171        toArray(collection) : collection;
1172
1173      return collection.filter(function (elm) {
1174        return objectContains(object, elm);
1175      });
1176    }
1177  });
1178
1179/**
1180 * @ngdoc filter
1181 * @name xor
1182 * @kind function
1183 *
1184 * @description
1185 * Exclusive or filter by expression
1186 */
1187
1188angular.module('a8m.xor', [])
1189
1190  .filter('xor', ['$parse', function($parse) {
1191    return function (col1, col2, expression) {
1192
1193      expression = expression || false;
1194
1195      col1 = (isObject(col1)) ? toArray(col1) : col1;
1196      col2 = (isObject(col2)) ? toArray(col2) : col2;
1197
1198      if(!isArray(col1) || !isArray(col2)) return col1;
1199
1200      return col1.concat(col2)
1201        .filter(function(elm) {
1202          return !(some(elm, col1) && some(elm, col2));
1203        });
1204
1205      function some(el, col) {
1206        var getter = $parse(expression);
1207        return col.some(function(dElm) {
1208          return expression ?
1209            equals(getter(dElm), getter(el)) :
1210            equals(dElm, el);
1211        })
1212      }
1213    }
1214  }]);
1215
1216/**
1217 * @ngdoc filter
1218 * @name formatBytes
1219 * @kind function
1220 *
1221 * @description
1222 * Convert bytes into appropriate display 
1223 * 1024 bytes => 1 KB
1224 */
1225
1226angular.module('a8m.math.byteFmt', ['a8m.math'])
1227
1228  .filter('byteFmt', ['$math', function ($math) {
1229    return function (bytes, decimal) {
1230
1231      if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1232        isNumber(bytes) && isFinite(bytes)) {
1233
1234        if(bytes < 1024) { // within 1 KB so B
1235            return convertToDecimal(bytes, decimal, $math) + ' B';
1236        } else if(bytes < 1048576) { // within 1 MB so KB
1237            return convertToDecimal((bytes / 1024), decimal, $math) + ' KB';
1238        } else if(bytes < 1073741824){ // within 1 GB so MB
1239            return convertToDecimal((bytes / 1048576), decimal, $math) + ' MB';
1240        } else { // GB or more
1241            return convertToDecimal((bytes / 1073741824), decimal, $math) + ' GB';
1242        }
1243
1244	  } else {
1245		return "NaN";
1246	  }
1247    }
1248  }]);
1249/**
1250 * @ngdoc filter
1251 * @name degrees
1252 * @kind function
1253 *
1254 * @description
1255 * Convert angle from radians to degrees
1256 *
1257 */
1258
1259angular.module('a8m.math.degrees', ['a8m.math'])
1260
1261  .filter('degrees', ['$math', function ($math) {
1262    return function (radians, decimal) {
1263	    // if decimal is not an integer greater than -1, we cannot do. quit with error "NaN"
1264		// if degrees is not a real number, we cannot do also. quit with error "NaN"
1265		if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1266          isNumber(radians) && isFinite(radians)) {
1267		    var degrees = (radians * 180) / $math.PI;
1268		    return $math.round(degrees * $math.pow(10,decimal)) / ($math.pow(10,decimal));
1269	    } else {
1270          return "NaN";
1271		}
1272	}
1273}]);
1274
1275 
1276 
1277/**
1278 * @ngdoc filter
1279 * @name formatBytes
1280 * @kind function
1281 *
1282 * @description
1283 * Convert bytes into appropriate display 
1284 * 1024 kilobytes => 1 MB
1285 */
1286
1287angular.module('a8m.math.kbFmt', ['a8m.math'])
1288
1289  .filter('kbFmt', ['$math', function ($math) {
1290    return function (bytes, decimal) {
1291
1292      if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1293        isNumber(bytes) && isFinite(bytes)) {
1294
1295        if(bytes < 1024) { // within 1 MB so KB
1296            return convertToDecimal(bytes, decimal, $math) + ' KB';
1297        } else if(bytes < 1048576) { // within 1 GB so MB
1298            return convertToDecimal((bytes / 1024), decimal, $math) + ' MB';
1299        } else {
1300            return convertToDecimal((bytes / 1048576), decimal, $math) + ' GB';
1301        }
1302
1303		} else {
1304			return "NaN";
1305		}
1306    }
1307}]);
1308/**
1309 * @ngdoc module
1310 * @name math
1311 * @description
1312 * reference to global Math object
1313 */
1314
1315angular.module('a8m.math', [])
1316  .factory('$math', ['$window', function ($window) {
1317
1318    return $window.Math;
1319
1320  }]);
1321
1322/**
1323 * @ngdoc filter
1324 * @name max
1325 * @kind function
1326 *
1327 * @description
1328 * Math.max
1329 *
1330 */
1331
1332angular.module('a8m.math.max', ['a8m.math'])
1333
1334  .filter('max', ['$math', function ($math) {
1335    return function (input) {
1336
1337      return (isArray(input)) ?
1338        $math.max.apply($math, input) :
1339        input;
1340    }
1341
1342  }]);
1343
1344/**
1345 * @ngdoc filter
1346 * @name min
1347 * @kind function
1348 *
1349 * @description
1350 * Math.min
1351 *
1352 */
1353
1354angular.module('a8m.math.min', ['a8m.math'])
1355
1356  .filter('min', ['$math', function ($math) {
1357    return function (input) {
1358
1359      return (isArray(input)) ?
1360        $math.min.apply($math, input) :
1361        input;
1362    }
1363
1364  }]);
1365
1366/**
1367 * @ngdoc filter
1368 * @name Percent
1369 * @kind function
1370 *
1371 * @description
1372 * percentage between two numbers
1373 *
1374 */
1375
1376angular.module('a8m.math.percent', ['a8m.math'])
1377
1378  .filter('percent', ['$math', '$window', function ($math, $window) {
1379
1380    return function (input, divided, round) {
1381
1382      var divider = (isString(input)) ? $window.Number(input) : input;
1383      divided = divided || 100;
1384      round = round || false;
1385
1386      if (!isNumber(divider) || $window.isNaN(divider)) return input;
1387
1388      return (round) ? $math.round((divider / divided) * 100) :
1389        ((divider / divided) * 100);
1390    }
1391
1392  }]);
1393
1394/**
1395 * @ngdoc filter
1396 * @name toRadians
1397 * @kind function
1398 *
1399 * @description
1400 * Convert angle from degrees to radians
1401 *
1402 */
1403
1404angular.module('a8m.math.radians', ['a8m.math'])
1405
1406  .filter('radians', ['$math', function ($math) {
1407    return function (degrees, decimal) {
1408	  // if decimal is not an integer greater than -1, we cannot do. quit with error "NaN"
1409	  // if degrees is not a real number, we cannot do also. quit with error "NaN"
1410        if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1411          isNumber(degrees) && isFinite(degrees)) {
1412          var radians = (degrees * 3.14159265359) / 180;
1413          return $math.round(radians * $math.pow(10,decimal)) / ($math.pow(10,decimal));
1414		} else {
1415		    return "NaN";
1416		}
1417	}
1418}]);
1419
1420 
1421 
1422/**
1423 * @ngdoc filter
1424 * @name Radix
1425 * @kind function
1426 *
1427 * @description
1428 * converting decimal numbers to different bases(radix)
1429 */
1430
1431angular.module('a8m.math.radix', [])
1432
1433  .filter('radix', function () {
1434
1435    return function (input, radix) {
1436
1437      var RANGE = /^[2-9]$|^[1-2]\d$|^3[0-6]$/;
1438
1439      if(!isNumber(input) || !RANGE.test(radix)) {
1440        return input;
1441      }
1442
1443      return input.toString(radix).toUpperCase();
1444
1445    }
1446
1447  });
1448
1449/**
1450 * @ngdoc filter
1451 * @name formatBytes
1452 * @kind function
1453 *
1454 * @description
1455 * Convert number into abbreviations.
1456 * i.e: K for one thousand, M for Million, B for billion
1457 * e.g: number of users:235,221, decimal:1 => 235.2 K
1458 */
1459
1460angular.module('a8m.math.shortFmt', ['a8m.math'])
1461
1462  .filter('shortFmt', ['$math', function ($math) {
1463    return function (number, decimal) {
1464      if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1465        isNumber(number) && isFinite(number)){
1466                    
1467          if(number < 1e3) {
1468              return number;
1469          } else if(number < 1e6) {
1470              return convertToDecimal((number / 1e3), decimal, $math) + ' K';
1471          } else if(number < 1e9){
1472              return convertToDecimal((number / 1e6), decimal, $math) + ' M';
1473          } else {
1474            return convertToDecimal((number / 1e9), decimal, $math) + ' B';
1475          }
1476
1477	  }else{
1478        return "NaN";
1479	  }
1480	}
1481}]);
1482/**
1483 * @ngdoc filter
1484 * @name sum
1485 * @kind function
1486 *
1487 * @description
1488 * Sum up all values within an array
1489 *
1490 */
1491
1492angular.module('a8m.math.sum', [])
1493
1494  .filter('sum', function () {
1495    return function (input, initial) {
1496
1497      return (!isArray(input)) ? input :
1498        input.reduce(function(prev, curr) {
1499          return prev + curr;
1500        }, initial || 0);
1501
1502    }
1503
1504  });
1505
1506/**
1507 * @ngdoc filter
1508 * @name endsWith
1509 * @kind function
1510 *
1511 * @description
1512 * checks whether string ends with the ends parameter.
1513 */
1514
1515angular.module('a8m.ends-with', [])
1516
1517  .filter('endsWith', function () {
1518    return function (input, ends, csensitive) {
1519
1520      var sensitive = csensitive || false,
1521        position;
1522
1523      if(!isString(input) || isUndefined(ends)) {
1524        return input;
1525      }
1526
1527      input = (sensitive) ? input : input.toLowerCase();
1528      position = input.length - ends.length;
1529
1530      return input.indexOf((sensitive) ? ends : ends.toLowerCase(), position) !== -1;
1531    }
1532  });
1533
1534/**
1535 * @ngdoc filter
1536 * @name ltrim
1537 * @kind function
1538 *
1539 * @description
1540 * Left trim. Similar to trimFilter, but only for left side.
1541 */
1542
1543angular.module('a8m.ltrim', [])
1544
1545  .filter('ltrim', function () {
1546    return function(input, chars) {
1547
1548      var trim = chars || '\\s';
1549
1550      if(!isString(input)) {
1551        return input;
1552      }
1553
1554      return input.replace(new RegExp('^' + trim + '+'), '');
1555    }
1556  });
1557
1558/**
1559 * @ngdoc filter
1560 * @name repeat
1561 * @kind function
1562 *
1563 * @description
1564 * Repeats a string n times
1565 */
1566
1567angular.module('a8m.repeat', [])
1568
1569  .filter('repeat',[ function () {
1570    return function (input, n, separator) {
1571
1572      var times = ~~n;
1573
1574      if(!isString(input)) {
1575        return input;
1576      }
1577
1578      return (!times) ? input : strRepeat(input, --n, separator || '');
1579    }
1580  }]);
1581
1582/**
1583 * Repeats a string n times with given separator
1584 * @param str string to repeat
1585 * @param n number of times
1586 * @param sep separator
1587 * @returns {*}
1588 */
1589function strRepeat(str, n, sep) {
1590  if(!n) {
1591    return str;
1592  }
1593  return str + sep + strRepeat(str, --n, sep);
1594}
1595/**
1596* @ngdoc filter
1597* @name rtrim
1598* @kind function
1599*
1600* @description
1601* Right trim. Similar to trimFilter, but only for right side.
1602*/
1603
1604angular.module('a8m.rtrim', [])
1605
1606  .filter('rtrim', function () {
1607    return function(input, chars) {
1608
1609      var trim = chars || '\\s';
1610
1611      if(!isString(input)) {
1612        return input;
1613      }
1614
1615      return input.replace(new RegExp(trim + '+$'), '');
1616    }
1617  });
1618
1619/**
1620 * @ngdoc filter
1621 * @name slugify
1622 * @kind function
1623 *
1624 * @description
1625 * remove spaces from string, replace with "-" or given argument
1626 */
1627
1628angular.module('a8m.slugify', [])
1629
1630  .filter('slugify',[ function () {
1631    return function (input, sub) {
1632
1633      var replace = sub || '-';
1634
1635      if(isString(input)) {
1636        return input.toLowerCase()
1637          .replace(/\s+/g, replace);
1638      }
1639
1640      return input;
1641    }
1642  }]);
1643
1644/**
1645 * @ngdoc filter
1646 * @name startWith
1647 * @kind function
1648 *
1649 * @description
1650 * checks whether string starts with the starts parameter.
1651 */
1652
1653angular.module('a8m.starts-with', [])
1654
1655  .filter('startsWith', function () {
1656    return function (input, start, csensitive) {
1657
1658      var sensitive = csensitive || false;
1659
1660      if(!isString(input) || isUndefined(start)) {
1661        return input;
1662      }
1663
1664      input = (sensitive) ? input : input.toLowerCase();
1665
1666      return !input.indexOf((sensitive) ? start : start.toLowerCase());
1667    }
1668  });
1669
1670/**
1671 * @ngdoc filter
1672 * @name stringular
1673 * @kind function
1674 *
1675 * @description
1676 * get string with {n} and replace match with enumeration values
1677 */
1678
1679angular.module('a8m.stringular', [])
1680  .filter('stringular', function () {
1681    return function(input) {
1682
1683      var args = Array.prototype.slice.call(arguments, 1);
1684
1685      return input.replace(/{(\d+)}/g, function (match, number) {
1686        return isUndefined(args[number]) ? match : args[number];
1687      });
1688
1689    }
1690  });
1691
1692/**
1693 * @ngdoc filter
1694 * @name stripTags
1695 * @kind function
1696 *
1697 * @description
1698 * strip html tags from string
1699 */
1700
1701angular.module('a8m.strip-tags', [])
1702  .filter('stripTags', function () {
1703    return function(input) {
1704      if(isString(input)) {
1705        return input.replace(/<\S[^><]*>/g, '');
1706      }
1707      return input;
1708    }
1709  });
1710
1711/**
1712 * @ngdoc filter
1713 * @name trim
1714 * @kind function
1715 *
1716 * @description
1717 *  Strip whitespace (or other characters) from the beginning and end of a string
1718 */
1719
1720angular.module('a8m.trim', [])
1721
1722  .filter('trim', function () {
1723    return function(input, chars) {
1724
1725      var trim = chars || '\\s';
1726
1727      if(!isString(input)) {
1728        return input;
1729      }
1730
1731      return input.replace(new RegExp('^' + trim + '+|' + trim + '+$', 'g'), '');
1732    }
1733  });
1734
1735/**
1736 * @ngdoc filter
1737 * @name truncate
1738 * @kind function
1739 *
1740 * @description
1741 * truncates a string given a specified length, providing a custom string to denote an omission.
1742 */
1743
1744angular.module('a8m.truncate', [])
1745  .filter('truncate', function () {
1746    return function(input, length, suffix, preserve) {
1747
1748      length = isUndefined(length) ? input.length : length;
1749      preserve = preserve || false;
1750      suffix = suffix || '';
1751
1752      if(!isString(input) || (input.length <= length)) return input;
1753
1754      return input.substring(0, (preserve) ?
1755          ((input.indexOf(' ', length) === -1) ? input.length : input.indexOf(' ', length)) :
1756          length) + suffix;
1757
1758    };
1759  });
1760
1761/**
1762 * @ngdoc filter
1763 * @name ucfirst
1764 * @kind function
1765 *
1766 * @description
1767 * ucfirst
1768 *
1769 */
1770
1771angular.module('a8m.ucfirst', [])
1772
1773  .filter('ucfirst', [function() {
1774    return function(input) {
1775      return angular.isString(input) ? input.split(' ')
1776        .map(function (char) {
1777          return char.charAt(0).toUpperCase() + char.substring(1);
1778        }).join(' ') : input;
1779    }
1780
1781  }]);
1782
1783/**
1784 * @ngdoc filter
1785 * @name uriEncode
1786 * @kind function
1787 *
1788 * @description
1789 * get string as parameter and return encoded string
1790 */
1791
1792angular.module('a8m.uri-encode', [])
1793
1794  .filter('uriEncode',['$window', function ($window) {
1795      return function (input) {
1796
1797        if(isString(input)) {
1798          return $window.encodeURI(input);
1799        }
1800
1801        return input;
1802      }
1803    }]);
1804
1805/**
1806 * @ngdoc filter
1807 * @name wrap
1808 * @kind function
1809 *
1810 * @description
1811 * Wrap a string with another string
1812 */
1813
1814angular.module('a8m.wrap', [])
1815
1816  .filter('wrap', function () {
1817    return function(input, wrap, ends) {
1818
1819      if(!isString(input) || isUndefined(wrap)) {
1820        return input;
1821      }
1822
1823      return [wrap, input, ends || wrap].join('');
1824
1825    }
1826  });
1827
1828/**
1829 * @ngdoc provider
1830 * @name filterWatcher
1831 * @kind function
1832 *
1833 * @description
1834 * filterWatchers is a _privateProvider
1835 * It's created to solve the problem of $rootScope:infdig(Infinite $digest loop) when using
1836 * some filters on the view.
1837 */
1838
1839angular.module('a8m.filter-watcher', [])
1840  .provider('filterWatcher', function() {
1841
1842    var filterPrefix = '_$$';
1843
1844    /**
1845     * @description
1846     * change the prefix name for filters on watch phase
1847     * @param prefix
1848     * @returns {filterWatcher}
1849     */
1850    this.setPrefix = function(prefix) {
1851      filterPrefix = prefix;
1852      return this;
1853    };
1854
1855    this.$get = ['$window', function($window) {
1856
1857      var $$timeout = $window.setTimeout;
1858
1859      /**
1860       * @description
1861       * return the filter full name
1862       * @param name
1863       * @returns {string}
1864       * @private
1865       */
1866      function _getFullName(name) {
1867        return filterPrefix + name;
1868      }
1869
1870      /**
1871       * @description
1872       * return whether or not this object is watched in current phase
1873       * @param fName
1874       * @param object
1875       * @returns {boolean}
1876       * @private
1877       */
1878      function _isWatched(fName, object) {
1879        return isDefined(object[fName]);
1880      }
1881
1882      /**
1883       * @description
1884       * return the object.$$filterName instance in current phase
1885       * @param name
1886       * @param object
1887       * @private
1888       */
1889      function _watch(name, object) {
1890        var fName = _getFullName(name);
1891
1892        if(!_isWatched(fName, object)) {
1893          //Create new instance
1894          Object.defineProperty(object, fName, {
1895            enumerable: false,
1896            configurable: true,
1897            value: {}
1898          });
1899        }
1900        return object[fName];
1901      }
1902
1903      /**
1904       * @description
1905       * destroy/delete current watch instance
1906       * @param name
1907       * @param object
1908       * @private
1909       */
1910      function _destroy(name, object) {
1911        return $$timeout(function() {
1912          delete object[_getFullName(name)];
1913        });
1914      }
1915
1916      return {
1917        $watch: _watch,
1918        $destroy: _destroy
1919      }
1920
1921    }];
1922
1923  });
1924/**
1925 * @ngdoc module
1926 * @name angular.filters
1927 * @description
1928 * Bunch of useful filters for angularJS
1929 */
1930
1931angular.module('angular.filter', [
1932
1933  'a8m.ucfirst',
1934  'a8m.uri-encode',
1935  'a8m.slugify',
1936  'a8m.strip-tags',
1937  'a8m.stringular',
1938  'a8m.truncate',
1939  'a8m.starts-with',
1940  'a8m.ends-with',
1941  'a8m.wrap',
1942  'a8m.trim',
1943  'a8m.ltrim',
1944  'a8m.rtrim',
1945  'a8m.repeat',
1946
1947  'a8m.to-array',
1948  'a8m.concat',
1949  'a8m.contains',
1950  'a8m.unique',
1951  'a8m.is-empty',
1952  'a8m.after',
1953  'a8m.after-where',
1954  'a8m.before',
1955  'a8m.before-where',
1956  'a8m.where',
1957  'a8m.reverse',
1958  'a8m.remove',
1959  'a8m.remove-with',
1960  'a8m.group-by',
1961  'a8m.count-by',
1962  'a8m.search-field',
1963  'a8m.fuzzy-by',
1964  'a8m.fuzzy',
1965  'a8m.omit',
1966  'a8m.pick',
1967  'a8m.every',
1968  'a8m.filter-by',
1969  'a8m.xor',
1970  'a8m.map',
1971  'a8m.first',
1972  'a8m.last',
1973  'a8m.flatten',
1974
1975  'a8m.math',
1976  'a8m.math.max',
1977  'a8m.math.min',
1978  'a8m.math.percent',
1979  'a8m.math.radix',
1980  'a8m.math.sum',
1981  'a8m.math.degrees',
1982  'a8m.math.radians',
1983  'a8m.math.byteFmt',
1984  'a8m.math.kbFmt',
1985  'a8m.math.shortFmt',
1986
1987  'a8m.angular',
1988  'a8m.conditions',
1989  'a8m.is-null',
1990
1991  'a8m.filter-watcher'
1992]);
1993})( window, window.angular );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.