angular_master 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cd1ab6cf2f8616b30ee45a8255b4d6aabf4e82ad
4
+ data.tar.gz: 9841762c1aad4885b9371edaa5a653dd183975d6
5
+ SHA512:
6
+ metadata.gz: f3d7638ee748edaed62a2d50ba6ca8441d01a0f7923f753d566cc44c81c3c48f13094542d7cac0f3092504e23c9a7b5ce6f059ea490d9447b8a6aee6b93140e1
7
+ data.tar.gz: 0f0a6951ef8397be60723db9cb466a32c34440728897d0f9a240bd646b8ba37421351a0e63d2f384cc498646bbfce78eabf63587fc370821dc19c1f105e44c16
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in angular_master.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Angular Master
2
+
3
+
4
+
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'angular_master'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install angular_master
21
+
22
+ ## Usage
23
+
24
+ ### a. CSS
25
+
26
+ require the following files in application.css to use [Angular Material](https://material.angularjs.org/latest/).
27
+
28
+ ```css
29
+ *= require angular-material
30
+ ```
31
+ ### b. Javascript
32
+ require the following files in application.js
33
+ 1. for AngularJS
34
+ ```js
35
+ //= require angular
36
+ ```
37
+ 2. for Angular UI Router
38
+ ```js
39
+ //= require angular-ui-router
40
+ ```
41
+ 3. for Angular Moment
42
+ ```js
43
+ //= require angular-moment
44
+ ```
45
+ 4. for Angular Filter
46
+ ```js
47
+ //= require angular-filter
48
+ ```
49
+ 5. for Angular Material (remember to use this while including css file for angular material)
50
+ ```js
51
+ //= require angular-material
52
+ ```
53
+ ## Contributing
54
+
55
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/angular_master.
56
+
57
+ ## License
58
+
59
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'angular_master/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "angular_master"
8
+ spec.version = AngularMaster::VERSION
9
+ spec.authors = ["hitman"]
10
+ spec.email = ["agrimmittal97@gmail.com"]
11
+
12
+ spec.summary = %q{This gem includes latest AngularJS and various libraries associated with it.}
13
+ spec.description = %q{AngularJS is open source project by Google. This gem provides various libraries like angularjs, angular-ui-router, angular-material etc.}
14
+ spec.homepage = "https://github.com/agrim123/angular_master"
15
+ spec.license = "MIT"
16
+
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.12"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "angular_master"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require "angular_master/version"
2
+
3
+ module AngularMaster
4
+ class Engine < ::Rails::Engine;
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module AngularMaster
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,2293 @@
1
+ /**
2
+ * Bunch of useful filters for angularJS(with no external dependencies!)
3
+ * @version v0.5.9 - 2016-07-15 * @link https://github.com/a8m/angular-filter
4
+ * @author Ariel Mashraki <ariel@mashraki.co.il>
5
+ * @license MIT License, http://www.opensource.org/licenses/MIT
6
+ */
7
+ (function ( window, angular, undefined ) {
8
+ /*jshint globalstrict:true*/
9
+ 'use strict';
10
+
11
+ var 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
+ * @description
26
+ * get an object and return array of values
27
+ * @param object
28
+ * @returns {Array}
29
+ */
30
+ function toArray(object) {
31
+ return isArray(object)
32
+ ? object
33
+ : Object.keys(object).map(function(key) {
34
+ return object[key];
35
+ });
36
+ }
37
+
38
+ /**
39
+ * @param value
40
+ * @returns {boolean}
41
+ */
42
+ function isNull(value) {
43
+ return value === null;
44
+ }
45
+
46
+ /**
47
+ * @description
48
+ * return if object contains partial object
49
+ * @param partial{object}
50
+ * @param object{object}
51
+ * @returns {boolean}
52
+ */
53
+ function objectContains(partial, object) {
54
+ var keys = Object.keys(partial);
55
+
56
+ return keys.map(function(el) {
57
+ return (object[el] !== undefined) && (object[el] == partial[el]);
58
+ }).indexOf(false) == -1;
59
+
60
+ }
61
+
62
+ /**
63
+ * @description
64
+ * search for approximate pattern in string
65
+ * @param word
66
+ * @param pattern
67
+ * @returns {*}
68
+ */
69
+ function hasApproxPattern(word, pattern) {
70
+ if(pattern === '')
71
+ return word;
72
+
73
+ var index = word.indexOf(pattern.charAt(0));
74
+
75
+ if(index === -1)
76
+ return false;
77
+
78
+ return hasApproxPattern(word.substr(index+1), pattern.substr(1))
79
+ }
80
+
81
+ /**
82
+ * @description
83
+ * return the first n element of an array,
84
+ * if expression provided, is returns as long the expression return truthy
85
+ * @param array
86
+ * @param n {number}
87
+ * @param expression {$parse}
88
+ * @return array or single object
89
+ */
90
+ function getFirstMatches(array, n, expression) {
91
+ var count = 0;
92
+
93
+ return array.filter(function(elm) {
94
+ var rest = isDefined(expression) ? (count < n && expression(elm)) : count < n;
95
+ count = rest ? count+1 : count;
96
+
97
+ return rest;
98
+ });
99
+ }
100
+ /**
101
+ * Polyfill to ECMA6 String.prototype.contains
102
+ */
103
+ if (!String.prototype.contains) {
104
+ String.prototype.contains = function() {
105
+ return String.prototype.indexOf.apply(this, arguments) !== -1;
106
+ };
107
+ }
108
+
109
+ /**
110
+ * @param num {Number}
111
+ * @param decimal {Number}
112
+ * @param $math
113
+ * @returns {Number}
114
+ */
115
+ function convertToDecimal(num, decimal, $math){
116
+ return $math.round(num * $math.pow(10,decimal)) / ($math.pow(10,decimal));
117
+ }
118
+
119
+ /**
120
+ * @description
121
+ * Get an object, and return an array composed of it's properties names(nested too).
122
+ * @param obj {Object}
123
+ * @param stack {Array}
124
+ * @param parent {String}
125
+ * @returns {Array}
126
+ * @example
127
+ * parseKeys({ a:1, b: { c:2, d: { e: 3 } } }) ==> ["a", "b.c", "b.d.e"]
128
+ */
129
+ function deepKeys(obj, stack, parent) {
130
+ stack = stack || [];
131
+ var keys = Object.keys(obj);
132
+
133
+ keys.forEach(function(el) {
134
+ //if it's a nested object
135
+ if(isObject(obj[el]) && !isArray(obj[el])) {
136
+ //concatenate the new parent if exist
137
+ var p = parent ? parent + '.' + el : parent;
138
+ deepKeys(obj[el], stack, p || el);
139
+ } else {
140
+ //create and save the key
141
+ var key = parent ? parent + '.' + el : el;
142
+ stack.push(key)
143
+ }
144
+ });
145
+ return stack
146
+ }
147
+
148
+ /**
149
+ * @description
150
+ * Test if given object is a Scope instance
151
+ * @param obj
152
+ * @returns {Boolean}
153
+ */
154
+ function isScope(obj) {
155
+ return obj && obj.$evalAsync && obj.$watch;
156
+ }
157
+
158
+ /**
159
+ * @ngdoc filter
160
+ * @name a8m.angular
161
+ * @kind function
162
+ *
163
+ * @description
164
+ * reference to angular function
165
+ */
166
+
167
+ angular.module('a8m.angular', [])
168
+
169
+ .filter('isUndefined', function () {
170
+ return function (input) {
171
+ return angular.isUndefined(input);
172
+ }
173
+ })
174
+ .filter('isDefined', function() {
175
+ return function (input) {
176
+ return angular.isDefined(input);
177
+ }
178
+ })
179
+ .filter('isFunction', function() {
180
+ return function (input) {
181
+ return angular.isFunction(input);
182
+ }
183
+ })
184
+ .filter('isString', function() {
185
+ return function (input) {
186
+ return angular.isString(input)
187
+ }
188
+ })
189
+ .filter('isNumber', function() {
190
+ return function (input) {
191
+ return angular.isNumber(input);
192
+ }
193
+ })
194
+ .filter('isArray', function() {
195
+ return function (input) {
196
+ return angular.isArray(input);
197
+ }
198
+ })
199
+ .filter('isObject', function() {
200
+ return function (input) {
201
+ return angular.isObject(input);
202
+ }
203
+ })
204
+ .filter('isEqual', function() {
205
+ return function (o1, o2) {
206
+ return angular.equals(o1, o2);
207
+ }
208
+ });
209
+
210
+ /**
211
+ * @ngdoc filter
212
+ * @name a8m.conditions
213
+ * @kind function
214
+ *
215
+ * @description
216
+ * reference to math conditions
217
+ */
218
+ angular.module('a8m.conditions', [])
219
+
220
+ .filter({
221
+ isGreaterThan : isGreaterThanFilter,
222
+ '>' : isGreaterThanFilter,
223
+
224
+ isGreaterThanOrEqualTo : isGreaterThanOrEqualToFilter,
225
+ '>=' : isGreaterThanOrEqualToFilter,
226
+
227
+ isLessThan : isLessThanFilter,
228
+ '<' : isLessThanFilter,
229
+
230
+ isLessThanOrEqualTo : isLessThanOrEqualToFilter,
231
+ '<=' : isLessThanOrEqualToFilter,
232
+
233
+ isEqualTo : isEqualToFilter,
234
+ '==' : isEqualToFilter,
235
+
236
+ isNotEqualTo : isNotEqualToFilter,
237
+ '!=' : isNotEqualToFilter,
238
+
239
+ isIdenticalTo : isIdenticalToFilter,
240
+ '===' : isIdenticalToFilter,
241
+
242
+ isNotIdenticalTo : isNotIdenticalToFilter,
243
+ '!==' : isNotIdenticalToFilter
244
+ });
245
+
246
+ function isGreaterThanFilter() {
247
+ return function (input, check) {
248
+ return input > check;
249
+ };
250
+ }
251
+
252
+ function isGreaterThanOrEqualToFilter() {
253
+ return function (input, check) {
254
+ return input >= check;
255
+ };
256
+ }
257
+
258
+ function isLessThanFilter() {
259
+ return function (input, check) {
260
+ return input < check;
261
+ };
262
+ }
263
+
264
+ function isLessThanOrEqualToFilter() {
265
+ return function (input, check) {
266
+ return input <= check;
267
+ };
268
+ }
269
+
270
+ function isEqualToFilter() {
271
+ return function (input, check) {
272
+ return input == check;
273
+ };
274
+ }
275
+
276
+ function isNotEqualToFilter() {
277
+ return function (input, check) {
278
+ return input != check;
279
+ };
280
+ }
281
+
282
+ function isIdenticalToFilter() {
283
+ return function (input, check) {
284
+ return input === check;
285
+ };
286
+ }
287
+
288
+ function isNotIdenticalToFilter() {
289
+ return function (input, check) {
290
+ return input !== check;
291
+ };
292
+ }
293
+ /**
294
+ * @ngdoc filter
295
+ * @name isNull
296
+ * @kind function
297
+ *
298
+ * @description
299
+ * checks if value is null or not
300
+ * @return Boolean
301
+ */
302
+ angular.module('a8m.is-null', [])
303
+ .filter('isNull', function () {
304
+ return function(input) {
305
+ return isNull(input);
306
+ }
307
+ });
308
+
309
+ /**
310
+ * @ngdoc filter
311
+ * @name after-where
312
+ * @kind function
313
+ *
314
+ * @description
315
+ * get a collection and properties object, and returns all of the items
316
+ * in the collection after the first that found with the given properties.
317
+ *
318
+ */
319
+ angular.module('a8m.after-where', [])
320
+ .filter('afterWhere', function() {
321
+ return function (collection, object) {
322
+
323
+ collection = isObject(collection)
324
+ ? toArray(collection)
325
+ : collection;
326
+
327
+ if(!isArray(collection) || isUndefined(object)) return collection;
328
+
329
+ var index = collection.map( function( elm ) {
330
+ return objectContains(object, elm);
331
+ }).indexOf( true );
332
+
333
+ return collection.slice((index === -1) ? 0 : index);
334
+ }
335
+ });
336
+
337
+ /**
338
+ * @ngdoc filter
339
+ * @name after
340
+ * @kind function
341
+ *
342
+ * @description
343
+ * get a collection and specified count, and returns all of the items
344
+ * in the collection after the specified count.
345
+ *
346
+ */
347
+
348
+ angular.module('a8m.after', [])
349
+ .filter('after', function() {
350
+ return function (collection, count) {
351
+ collection = isObject(collection)
352
+ ? toArray(collection)
353
+ : collection;
354
+
355
+ return (isArray(collection))
356
+ ? collection.slice(count)
357
+ : collection;
358
+ }
359
+ });
360
+
361
+ /**
362
+ * @ngdoc filter
363
+ * @name before-where
364
+ * @kind function
365
+ *
366
+ * @description
367
+ * get a collection and properties object, and returns all of the items
368
+ * in the collection before the first that found with the given properties.
369
+ */
370
+ angular.module('a8m.before-where', [])
371
+ .filter('beforeWhere', function() {
372
+ return function (collection, object) {
373
+
374
+ collection = isObject(collection)
375
+ ? toArray(collection)
376
+ : collection;
377
+
378
+ if(!isArray(collection) || isUndefined(object)) return collection;
379
+
380
+ var index = collection.map( function( elm ) {
381
+ return objectContains(object, elm);
382
+ }).indexOf( true );
383
+
384
+ return collection.slice(0, (index === -1) ? collection.length : ++index);
385
+ }
386
+ });
387
+
388
+ /**
389
+ * @ngdoc filter
390
+ * @name before
391
+ * @kind function
392
+ *
393
+ * @description
394
+ * get a collection and specified count, and returns all of the items
395
+ * in the collection before the specified count.
396
+ */
397
+ angular.module('a8m.before', [])
398
+ .filter('before', function() {
399
+ return function (collection, count) {
400
+ collection = isObject(collection)
401
+ ? toArray(collection)
402
+ : collection;
403
+
404
+ return (isArray(collection))
405
+ ? collection.slice(0, (!count) ? count : --count)
406
+ : collection;
407
+ }
408
+ });
409
+
410
+ /**
411
+ * @ngdoc filter
412
+ * @name chunkBy
413
+ * @kind function
414
+ *
415
+ * @description
416
+ * Collect data into fixed-length chunks or blocks
417
+ */
418
+
419
+ angular.module('a8m.chunk-by', ['a8m.filter-watcher'])
420
+ .filter('chunkBy', ['filterWatcher', function (filterWatcher) {
421
+ return function (array, n, fillVal) {
422
+
423
+ return filterWatcher.isMemoized('chunkBy', arguments) ||
424
+ filterWatcher.memoize('chunkBy', arguments, this,
425
+ _chunkBy(array, n, fillVal));
426
+ /**
427
+ * @description
428
+ * Get array with size `n` in `val` inside it.
429
+ * @param n
430
+ * @param val
431
+ * @returns {Array}
432
+ */
433
+ function fill(n, val) {
434
+ var ret = [];
435
+ while (n--) ret[n] = val;
436
+ return ret;
437
+ }
438
+
439
+ function _chunkBy(array, n, fillVal) {
440
+ if (!isArray(array)) return array;
441
+ return array.map(function (el, i, self) {
442
+ i = i * n;
443
+ el = self.slice(i, i + n);
444
+ return !isUndefined(fillVal) && el.length < n
445
+ ? el.concat(fill(n - el.length, fillVal))
446
+ : el;
447
+ }).slice(0, Math.ceil(array.length / n));
448
+ }
449
+ }
450
+ }]);
451
+
452
+ /**
453
+ * @ngdoc filter
454
+ * @name concat
455
+ * @kind function
456
+ *
457
+ * @description
458
+ * get (array/object, object/array) and return merged collection
459
+ */
460
+ angular.module('a8m.concat', [])
461
+ .filter('concat', [function () {
462
+ return function (collection, joined) {
463
+
464
+ if (isUndefined(joined)) return collection;
465
+
466
+ if (isArray(collection)) {
467
+ return isObject(joined)
468
+ ? collection.concat(toArray(joined))
469
+ : collection.concat(joined);
470
+ }
471
+
472
+ if (isObject(collection)) {
473
+ var array = toArray(collection);
474
+ return (isObject(joined))
475
+ ? array.concat(toArray(joined))
476
+ : array.concat(joined);
477
+ }
478
+ return collection;
479
+ };
480
+ }
481
+ ]);
482
+
483
+ /**
484
+ * @ngdoc filter
485
+ * @name contains
486
+ * @kind function
487
+ *
488
+ * @description
489
+ * Checks if given expression is present in one or more object in the collection
490
+ */
491
+ angular.module('a8m.contains', [])
492
+ .filter({
493
+ contains: ['$parse', containsFilter],
494
+ some: ['$parse', containsFilter]
495
+ });
496
+
497
+ function containsFilter($parse) {
498
+ return function (collection, expression) {
499
+
500
+ collection = isObject(collection) ? toArray(collection) : collection;
501
+
502
+ if(!isArray(collection) || isUndefined(expression)) {
503
+ return false;
504
+ }
505
+
506
+ return collection.some(function(elm) {
507
+ return ((isString(expression) && isObject(elm)) || isFunction(expression))
508
+ ? $parse(expression)(elm)
509
+ : elm === expression;
510
+ });
511
+
512
+ }
513
+ }
514
+
515
+ /**
516
+ * @ngdoc filter
517
+ * @name countBy
518
+ * @kind function
519
+ *
520
+ * @description
521
+ * Sorts a list into groups and returns a count for the number of objects in each group.
522
+ */
523
+
524
+ angular.module('a8m.count-by', [])
525
+
526
+ .filter('countBy', [ '$parse', function ( $parse ) {
527
+ return function (collection, property) {
528
+
529
+ var result = {},
530
+ get = $parse(property),
531
+ prop;
532
+
533
+ collection = (isObject(collection)) ? toArray(collection) : collection;
534
+
535
+ if(!isArray(collection) || isUndefined(property)) {
536
+ return collection;
537
+ }
538
+
539
+ collection.forEach( function( elm ) {
540
+ prop = get(elm);
541
+
542
+ if(!result[prop]) {
543
+ result[prop] = 0;
544
+ }
545
+
546
+ result[prop]++;
547
+ });
548
+
549
+ return result;
550
+ }
551
+ }]);
552
+
553
+ /**
554
+ * @ngdoc filter
555
+ * @name defaults
556
+ * @kind function
557
+ *
558
+ * @description
559
+ * defaultsFilter allows to specify a default fallback value for properties that resolve to undefined.
560
+ */
561
+ angular.module('a8m.defaults', [])
562
+ .filter('defaults', ['$parse', function( $parse ) {
563
+ return function(collection, defaults) {
564
+
565
+ collection = isObject(collection) ? toArray(collection) : collection;
566
+
567
+ if(!isArray(collection) || !isObject(defaults)) {
568
+ return collection;
569
+ }
570
+
571
+ var keys = deepKeys(defaults);
572
+
573
+ collection.forEach(function(elm) {
574
+ //loop through all the keys
575
+ keys.forEach(function(key) {
576
+ var getter = $parse(key);
577
+ var setter = getter.assign;
578
+ //if it's not exist
579
+ if(isUndefined(getter(elm))) {
580
+ //get from defaults, and set to the returned object
581
+ setter(elm, getter(defaults))
582
+ }
583
+ });
584
+ });
585
+
586
+ return collection;
587
+ }
588
+ }]);
589
+ /**
590
+ * @ngdoc filter
591
+ * @name every
592
+ * @kind function
593
+ *
594
+ * @description
595
+ * Checks if given expression is present in all members in the collection
596
+ *
597
+ */
598
+ angular.module('a8m.every', [])
599
+ .filter('every', ['$parse', function($parse) {
600
+ return function (collection, expression) {
601
+ collection = isObject(collection) ? toArray(collection) : collection;
602
+
603
+ if(!isArray(collection) || isUndefined(expression)) {
604
+ return true;
605
+ }
606
+
607
+ return collection.every( function(elm) {
608
+ return (isObject(elm) || isFunction(expression))
609
+ ? $parse(expression)(elm)
610
+ : elm === expression;
611
+ });
612
+ }
613
+ }]);
614
+
615
+ /**
616
+ * @ngdoc filter
617
+ * @name filterBy
618
+ * @kind function
619
+ *
620
+ * @description
621
+ * filter by specific properties, avoid the rest
622
+ */
623
+ angular.module('a8m.filter-by', [])
624
+ .filter('filterBy', ['$parse', function( $parse ) {
625
+ return function(collection, properties, search, strict) {
626
+ var comparator;
627
+
628
+ search = (isString(search) || isNumber(search)) ?
629
+ String(search).toLowerCase() : undefined;
630
+
631
+ collection = isObject(collection) ? toArray(collection) : collection;
632
+
633
+ if(!isArray(collection) || isUndefined(search)) {
634
+ return collection;
635
+ }
636
+
637
+ return collection.filter(function(elm) {
638
+ return properties.some(function(prop) {
639
+
640
+ /**
641
+ * check if there is concatenate properties
642
+ * example:
643
+ * object: { first: 'foo', last:'bar' }
644
+ * filterBy: ['first + last'] => search by full name(i.e 'foo bar')
645
+ */
646
+ if(!~prop.indexOf('+')) {
647
+ comparator = $parse(prop)(elm)
648
+ } else {
649
+ var propList = prop.replace(/\s+/g, '').split('+');
650
+ comparator = propList
651
+ .map(function(prop) { return $parse(prop)(elm); })
652
+ .join(' ');
653
+ }
654
+
655
+ if (!isString(comparator) && !isNumber(comparator)) {
656
+ return false;
657
+ }
658
+
659
+ comparator = String(comparator).toLowerCase();
660
+
661
+ return strict ? comparator === search : comparator.contains(search);
662
+ });
663
+ });
664
+ }
665
+ }]);
666
+
667
+ /**
668
+ * @ngdoc filter
669
+ * @name first
670
+ * @kind function
671
+ *
672
+ * @description
673
+ * Gets the first element or first n elements of an array
674
+ * if callback is provided, is returns as long the callback return truthy
675
+ */
676
+ angular.module('a8m.first', [])
677
+ .filter('first', ['$parse', function( $parse ) {
678
+ return function(collection) {
679
+ var n
680
+ , getter
681
+ , args;
682
+
683
+ collection = isObject(collection)
684
+ ? toArray(collection)
685
+ : collection;
686
+
687
+ if(!isArray(collection)) {
688
+ return collection;
689
+ }
690
+
691
+ args = Array.prototype.slice.call(arguments, 1);
692
+ n = (isNumber(args[0])) ? args[0] : 1;
693
+ getter = (!isNumber(args[0])) ? args[0] : (!isNumber(args[1])) ? args[1] : undefined;
694
+
695
+ return (args.length) ? getFirstMatches(collection, n,(getter) ? $parse(getter) : getter) :
696
+ collection[0];
697
+ }
698
+ }]);
699
+
700
+ /**
701
+ * @ngdoc filter
702
+ * @name flatten
703
+ * @kind function
704
+ *
705
+ * @description
706
+ * Flattens a nested array (the nesting can be to any depth).
707
+ * If you pass shallow, the array will only be flattened a single level
708
+ */
709
+ angular.module('a8m.flatten', [])
710
+ .filter('flatten', function () {
711
+ return function(collection, shallow) {
712
+
713
+ shallow = shallow || false;
714
+ collection = isObject(collection)
715
+ ? toArray(collection)
716
+ : collection;
717
+
718
+ if(!isArray(collection)) {
719
+ return collection;
720
+ }
721
+
722
+ return !shallow
723
+ ? flatten(collection, 0)
724
+ : [].concat.apply([], collection);
725
+ }
726
+ });
727
+
728
+ /**
729
+ * flatten nested array (the nesting can be to any depth).
730
+ * @param array {Array}
731
+ * @param i {int}
732
+ * @returns {Array}
733
+ * @private
734
+ */
735
+ function flatten(array, i) {
736
+ i = i || 0;
737
+
738
+ if(i >= array.length)
739
+ return array;
740
+
741
+ if(isArray(array[i])) {
742
+ return flatten(array.slice(0,i)
743
+ .concat(array[i], array.slice(i+1)), i);
744
+ }
745
+ return flatten(array, i+1);
746
+ }
747
+
748
+ /**
749
+ * @ngdoc filter
750
+ * @name fuzzyByKey
751
+ * @kind function
752
+ *
753
+ * @description
754
+ * fuzzy string searching by key
755
+ */
756
+ angular.module('a8m.fuzzy-by', [])
757
+ .filter('fuzzyBy', ['$parse', function ( $parse ) {
758
+ return function (collection, property, search, csensitive) {
759
+
760
+ var sensitive = csensitive || false,
761
+ prop, getter;
762
+
763
+ collection = isObject(collection) ? toArray(collection) : collection;
764
+
765
+ if(!isArray(collection) || isUndefined(property)
766
+ || isUndefined(search)) {
767
+ return collection;
768
+ }
769
+
770
+ getter = $parse(property);
771
+
772
+ return collection.filter(function(elm) {
773
+
774
+ prop = getter(elm);
775
+ if(!isString(prop)) {
776
+ return false;
777
+ }
778
+
779
+ prop = (sensitive) ? prop : prop.toLowerCase();
780
+ search = (sensitive) ? search : search.toLowerCase();
781
+
782
+ return hasApproxPattern(prop, search) !== false
783
+ })
784
+ }
785
+
786
+ }]);
787
+ /**
788
+ * @ngdoc filter
789
+ * @name fuzzy
790
+ * @kind function
791
+ *
792
+ * @description
793
+ * fuzzy string searching for array of strings, objects
794
+ */
795
+ angular.module('a8m.fuzzy', [])
796
+ .filter('fuzzy', function () {
797
+ return function (collection, search, csensitive) {
798
+ var sensitive = csensitive || false;
799
+ collection = isObject(collection) ? toArray(collection) : collection;
800
+
801
+ if(!isArray(collection) || isUndefined(search)) {
802
+ return collection;
803
+ }
804
+
805
+ search = (sensitive) ? search : search.toLowerCase();
806
+
807
+ return collection.filter(function(elm) {
808
+ if(isString(elm)) {
809
+ elm = (sensitive) ? elm : elm.toLowerCase();
810
+ return hasApproxPattern(elm, search) !== false
811
+ }
812
+ return (isObject(elm)) ? _hasApproximateKey(elm, search) : false;
813
+ });
814
+
815
+ /**
816
+ * checks if object has key{string} that match
817
+ * to fuzzy search pattern
818
+ * @param object
819
+ * @param search
820
+ * @returns {boolean}
821
+ * @private
822
+ */
823
+ function _hasApproximateKey(object, search) {
824
+ var properties = Object.keys(object),
825
+ prop, flag;
826
+ return 0 < properties.filter(function (elm) {
827
+ prop = object[elm];
828
+
829
+ //avoid iteration if we found some key that equal[performance]
830
+ if(flag) return true;
831
+
832
+ if (isString(prop)) {
833
+ prop = (sensitive) ? prop : prop.toLowerCase();
834
+ return flag = (hasApproxPattern(prop, search) !== false);
835
+ }
836
+
837
+ return false;
838
+
839
+ }).length;
840
+ }
841
+ }
842
+ });
843
+
844
+ /**
845
+ * @ngdoc filter
846
+ * @name groupBy
847
+ * @kind function
848
+ *
849
+ * @description
850
+ * Create an object composed of keys generated from the result of running each element of a collection,
851
+ * each key is an array of the elements.
852
+ */
853
+
854
+ angular.module('a8m.group-by', [ 'a8m.filter-watcher' ])
855
+ .filter('groupBy', [ '$parse', 'filterWatcher', function ( $parse, filterWatcher ) {
856
+ return function (collection, property) {
857
+
858
+ if(!isObject(collection) || isUndefined(property)) {
859
+ return collection;
860
+ }
861
+
862
+ return filterWatcher.isMemoized('groupBy', arguments) ||
863
+ filterWatcher.memoize('groupBy', arguments, this,
864
+ _groupBy(collection, $parse(property)));
865
+
866
+ /**
867
+ * groupBy function
868
+ * @param collection
869
+ * @param getter
870
+ * @returns {{}}
871
+ */
872
+ function _groupBy(collection, getter) {
873
+ var result = {};
874
+ var prop;
875
+
876
+ forEach( collection, function( elm ) {
877
+ prop = getter(elm);
878
+
879
+ if(!result[prop]) {
880
+ result[prop] = [];
881
+ }
882
+ result[prop].push(elm);
883
+ });
884
+ return result;
885
+ }
886
+ }
887
+ }]);
888
+
889
+ /**
890
+ * @ngdoc filter
891
+ * @name isEmpty
892
+ * @kind function
893
+ *
894
+ * @description
895
+ * get collection or string and return if it empty
896
+ */
897
+ angular.module('a8m.is-empty', [])
898
+ .filter('isEmpty', function () {
899
+ return function(collection) {
900
+ return isObject(collection)
901
+ ? !toArray(collection).length
902
+ : !collection.length;
903
+ }
904
+ });
905
+
906
+ /**
907
+ * @ngdoc filter
908
+ * @name join
909
+ * @kind function
910
+ *
911
+ * @description
912
+ * join a collection by a provided delimiter (space by default)
913
+ */
914
+ angular.module('a8m.join', [])
915
+ .filter('join', function () {
916
+ return function (input, delimiter) {
917
+ if (isUndefined(input) || !isArray(input)) {
918
+ return input;
919
+ }
920
+ if (isUndefined(delimiter)) delimiter = ' ';
921
+
922
+ return input.join(delimiter);
923
+ };
924
+ })
925
+ ;
926
+
927
+ /**
928
+ * @ngdoc filter
929
+ * @name last
930
+ * @kind function
931
+ *
932
+ * @description
933
+ * Gets the last element or last n elements of an array
934
+ * if callback is provided, is returns as long the callback return truthy
935
+ */
936
+ angular.module('a8m.last', [])
937
+ .filter('last', ['$parse', function( $parse ) {
938
+ return function(collection) {
939
+ var n
940
+ , getter
941
+ , args
942
+ //cuz reverse change our src collection
943
+ //and we don't want side effects
944
+ , reversed = copy(collection);
945
+
946
+ reversed = isObject(reversed)
947
+ ? toArray(reversed)
948
+ : reversed;
949
+
950
+ if(!isArray(reversed)) {
951
+ return reversed;
952
+ }
953
+
954
+ args = Array.prototype.slice.call(arguments, 1);
955
+ n = (isNumber(args[0])) ? args[0] : 1;
956
+ getter = (!isNumber(args[0])) ? args[0] : (!isNumber(args[1])) ? args[1] : undefined;
957
+
958
+ return (args.length)
959
+ //send reversed collection as arguments, and reverse it back as result
960
+ ? getFirstMatches(reversed.reverse(), n,(getter) ? $parse(getter) : getter).reverse()
961
+ //get the last element
962
+ : reversed[reversed.length-1];
963
+ }
964
+ }]);
965
+
966
+ /**
967
+ * @ngdoc filter
968
+ * @name map
969
+ * @kind function
970
+ *
971
+ * @description
972
+ * Returns a new collection of the results of each expression execution.
973
+ */
974
+ angular.module('a8m.map', [])
975
+ .filter('map', ['$parse', function($parse) {
976
+ return function (collection, expression) {
977
+
978
+ collection = isObject(collection)
979
+ ? toArray(collection)
980
+ : collection;
981
+
982
+ if(!isArray(collection) || isUndefined(expression)) {
983
+ return collection;
984
+ }
985
+
986
+ return collection.map(function (elm) {
987
+ return $parse(expression)(elm);
988
+ });
989
+ }
990
+ }]);
991
+
992
+ /**
993
+ * @ngdoc filter
994
+ * @name omit
995
+ * @kind function
996
+ *
997
+ * @description
998
+ * filter collection by expression
999
+ */
1000
+
1001
+ angular.module('a8m.omit', [])
1002
+
1003
+ .filter('omit', ['$parse', function($parse) {
1004
+ return function (collection, expression) {
1005
+
1006
+ collection = isObject(collection)
1007
+ ? toArray(collection)
1008
+ : collection;
1009
+
1010
+ if(!isArray(collection) || isUndefined(expression)) {
1011
+ return collection;
1012
+ }
1013
+
1014
+ return collection.filter(function (elm) {
1015
+ return !($parse(expression)(elm));
1016
+ });
1017
+ }
1018
+ }]);
1019
+
1020
+ /**
1021
+ * @ngdoc filter
1022
+ * @name pick
1023
+ * @kind function
1024
+ *
1025
+ * @description
1026
+ * filter collection by expression
1027
+ */
1028
+
1029
+ angular.module('a8m.pick', [])
1030
+
1031
+ .filter('pick', ['$parse', function($parse) {
1032
+ return function (collection, expression) {
1033
+
1034
+ collection = isObject(collection)
1035
+ ? toArray(collection)
1036
+ : collection;
1037
+
1038
+ if(!isArray(collection) || isUndefined(expression)) {
1039
+ return collection;
1040
+ }
1041
+
1042
+ return collection.filter(function (elm) {
1043
+ return $parse(expression)(elm);
1044
+ });
1045
+ }
1046
+ }]);
1047
+
1048
+ /**
1049
+ * @ngdoc filter
1050
+ * @name range
1051
+ * @kind function
1052
+ *
1053
+ * @description
1054
+ * rangeFilter provides some support for a for loop using numbers
1055
+ */
1056
+ angular.module('a8m.range', [])
1057
+ .filter('range', function () {
1058
+ return function (input, total, start, increment, cb) {
1059
+ start = start || 0;
1060
+ increment = increment || 1;
1061
+ for (var i = 0; i < parseInt(total); i++) {
1062
+ var j = start + i * increment;
1063
+ input.push(isFunction(cb) ? cb(j) : j);
1064
+ }
1065
+ return input;
1066
+ };
1067
+ });
1068
+ /**
1069
+ * @ngdoc filter
1070
+ * @name removeWith
1071
+ * @kind function
1072
+ *
1073
+ * @description
1074
+ * get collection and properties object, and removed elements
1075
+ * with this properties
1076
+ */
1077
+
1078
+ angular.module('a8m.remove-with', [])
1079
+ .filter('removeWith', function() {
1080
+ return function (collection, object) {
1081
+
1082
+ if(isUndefined(object)) {
1083
+ return collection;
1084
+ }
1085
+ collection = isObject(collection)
1086
+ ? toArray(collection)
1087
+ : collection;
1088
+
1089
+ return collection.filter(function (elm) {
1090
+ return !objectContains(object, elm);
1091
+ });
1092
+ }
1093
+ });
1094
+
1095
+
1096
+ /**
1097
+ * @ngdoc filter
1098
+ * @name remove
1099
+ * @kind function
1100
+ *
1101
+ * @description
1102
+ * remove specific members from collection
1103
+ */
1104
+
1105
+ angular.module('a8m.remove', [])
1106
+
1107
+ .filter('remove', function () {
1108
+ return function (collection) {
1109
+ collection = isObject(collection) ? toArray(collection) : collection;
1110
+ var args = Array.prototype.slice.call(arguments, 1);
1111
+
1112
+ if(!isArray(collection)) {
1113
+ return collection;
1114
+ }
1115
+
1116
+ return collection.filter( function( member ) {
1117
+ return !args.some(function(nest) {
1118
+ return equals(nest, member);
1119
+ })
1120
+ });
1121
+ }
1122
+ });
1123
+
1124
+ /**
1125
+ * @ngdoc filter
1126
+ * @name reverse
1127
+ * @kind function
1128
+ *
1129
+ * @description
1130
+ * Reverses a string or collection
1131
+ */
1132
+ angular.module('a8m.reverse', [])
1133
+ .filter('reverse',[ function () {
1134
+ return function (input) {
1135
+ input = isObject(input) ? toArray(input) : input;
1136
+
1137
+ if(isString(input)) {
1138
+ return input.split('').reverse().join('');
1139
+ }
1140
+
1141
+ return isArray(input)
1142
+ ? input.slice().reverse()
1143
+ : input;
1144
+ }
1145
+ }]);
1146
+
1147
+ /**
1148
+ * @ngdoc filter
1149
+ * @name searchField
1150
+ * @kind function
1151
+ *
1152
+ * @description
1153
+ * for each member, join several strings field and add them to
1154
+ * new field called 'searchField' (use for search filtering)
1155
+ */
1156
+ angular.module('a8m.search-field', [])
1157
+ .filter('searchField', ['$parse', function ($parse) {
1158
+ return function (collection) {
1159
+
1160
+ var get, field;
1161
+
1162
+ collection = isObject(collection) ? toArray(collection) : collection;
1163
+
1164
+ var args = Array.prototype.slice.call(arguments, 1);
1165
+
1166
+ if(!isArray(collection) || !args.length) {
1167
+ return collection;
1168
+ }
1169
+
1170
+ return collection.map(function(member) {
1171
+
1172
+ field = args.map(function(field) {
1173
+ get = $parse(field);
1174
+ return get(member);
1175
+ }).join(' ');
1176
+
1177
+ return extend(member, { searchField: field });
1178
+ });
1179
+ }
1180
+ }]);
1181
+
1182
+ /**
1183
+ * @ngdoc filter
1184
+ * @name toArray
1185
+ * @kind function
1186
+ *
1187
+ * @description
1188
+ * Convert objects into stable arrays.
1189
+ * if addKey set to true,the filter also attaches a new property
1190
+ * $key to the value containing the original key that was used in
1191
+ * the object we are iterating over to reference the property
1192
+ */
1193
+ angular.module('a8m.to-array', [])
1194
+ .filter('toArray', function() {
1195
+ return function (collection, addKey) {
1196
+
1197
+ if(!isObject(collection)) {
1198
+ return collection;
1199
+ }
1200
+
1201
+ return !addKey
1202
+ ? toArray(collection)
1203
+ : Object.keys(collection).map(function (key) {
1204
+ return extend(collection[key], { $key: key });
1205
+ });
1206
+ }
1207
+ });
1208
+
1209
+ /**
1210
+ * @ngdoc filter
1211
+ * @name unique/uniq
1212
+ * @kind function
1213
+ *
1214
+ * @description
1215
+ * get collection and filter duplicate members
1216
+ * if uniqueFilter get a property(nested to) as argument it's
1217
+ * filter by this property as unique identifier
1218
+ */
1219
+
1220
+ angular.module('a8m.unique', [])
1221
+ .filter({
1222
+ unique: ['$parse', uniqFilter],
1223
+ uniq: ['$parse', uniqFilter]
1224
+ });
1225
+
1226
+ function uniqFilter($parse) {
1227
+ return function (collection, property) {
1228
+
1229
+ collection = isObject(collection) ? toArray(collection) : collection;
1230
+
1231
+ if (!isArray(collection)) {
1232
+ return collection;
1233
+ }
1234
+
1235
+ //store all unique identifiers
1236
+ var uniqueItems = [],
1237
+ get = $parse(property);
1238
+
1239
+ return (isUndefined(property))
1240
+ //if it's kind of primitive array
1241
+ ? collection.filter(function (elm, pos, self) {
1242
+ return self.indexOf(elm) === pos;
1243
+ })
1244
+ //else compare with equals
1245
+ : collection.filter(function (elm) {
1246
+ var prop = get(elm);
1247
+ if(some(uniqueItems, prop)) {
1248
+ return false;
1249
+ }
1250
+ uniqueItems.push(prop);
1251
+ return true;
1252
+ });
1253
+
1254
+ //checked if the unique identifier is already exist
1255
+ function some(array, member) {
1256
+ if(isUndefined(member)) {
1257
+ return false;
1258
+ }
1259
+ return array.some(function(el) {
1260
+ return equals(el, member);
1261
+ });
1262
+ }
1263
+ }
1264
+ }
1265
+
1266
+ /**
1267
+ * @ngdoc filter
1268
+ * @name where
1269
+ * @kind function
1270
+ *
1271
+ * @description
1272
+ * of each element in a collection to the given properties object,
1273
+ * returning an array of all elements that have equivalent property values.
1274
+ *
1275
+ */
1276
+ angular.module('a8m.where', [])
1277
+ .filter('where', function() {
1278
+ return function (collection, object) {
1279
+ if(isUndefined(object)) return collection;
1280
+ collection = isObject(collection)
1281
+ ? toArray(collection)
1282
+ : collection;
1283
+
1284
+ return collection.filter(function (elm) {
1285
+ return objectContains(object, elm);
1286
+ });
1287
+ }
1288
+ });
1289
+
1290
+ /**
1291
+ * @ngdoc filter
1292
+ * @name xor
1293
+ * @kind function
1294
+ *
1295
+ * @description
1296
+ * Exclusive or filter by expression
1297
+ */
1298
+
1299
+ angular.module('a8m.xor', [])
1300
+
1301
+ .filter('xor', ['$parse', function($parse) {
1302
+ return function (col1, col2, expression) {
1303
+
1304
+ expression = expression || false;
1305
+
1306
+ col1 = isObject(col1) ? toArray(col1) : col1;
1307
+ col2 = isObject(col2) ? toArray(col2) : col2;
1308
+
1309
+ if(!isArray(col1) || !isArray(col2)) return col1;
1310
+
1311
+ return col1.concat(col2)
1312
+ .filter(function(elm) {
1313
+ return !(some(elm, col1) && some(elm, col2));
1314
+ });
1315
+
1316
+ function some(el, col) {
1317
+ var getter = $parse(expression);
1318
+ return col.some(function(dElm) {
1319
+ return expression
1320
+ ? equals(getter(dElm), getter(el))
1321
+ : equals(dElm, el);
1322
+ });
1323
+ }
1324
+ }
1325
+ }]);
1326
+
1327
+ /**
1328
+ * @ngdoc filter
1329
+ * @name formatBytes
1330
+ * @kind function
1331
+ *
1332
+ * @description
1333
+ * Convert bytes into appropriate display
1334
+ * 1024 bytes => 1 KB
1335
+ */
1336
+ angular.module('a8m.math.byteFmt', ['a8m.math'])
1337
+ .filter('byteFmt', ['$math', function ($math) {
1338
+ return function (bytes, decimal) {
1339
+
1340
+ if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1341
+ isNumber(bytes) && isFinite(bytes)) {
1342
+ if(bytes < 1024) { // within 1 KB so B
1343
+ return convertToDecimal(bytes, decimal, $math) + ' B';
1344
+ } else if(bytes < 1048576) { // within 1 MB so KB
1345
+ return convertToDecimal((bytes / 1024), decimal, $math) + ' KB';
1346
+ } else if(bytes < 1073741824){ // within 1 GB so MB
1347
+ return convertToDecimal((bytes / 1048576), decimal, $math) + ' MB';
1348
+ } else { // GB or more
1349
+ return convertToDecimal((bytes / 1073741824), decimal, $math) + ' GB';
1350
+ }
1351
+
1352
+ }
1353
+ return "NaN";
1354
+ }
1355
+ }]);
1356
+ /**
1357
+ * @ngdoc filter
1358
+ * @name degrees
1359
+ * @kind function
1360
+ *
1361
+ * @description
1362
+ * Convert angle from radians to degrees
1363
+ */
1364
+ angular.module('a8m.math.degrees', ['a8m.math'])
1365
+ .filter('degrees', ['$math', function ($math) {
1366
+ return function (radians, decimal) {
1367
+ // if decimal is not an integer greater than -1, we cannot do. quit with error "NaN"
1368
+ // if degrees is not a real number, we cannot do also. quit with error "NaN"
1369
+ if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1370
+ isNumber(radians) && isFinite(radians)) {
1371
+ var degrees = (radians * 180) / $math.PI;
1372
+ return $math.round(degrees * $math.pow(10,decimal)) / ($math.pow(10,decimal));
1373
+ } else {
1374
+ return "NaN";
1375
+ }
1376
+ }
1377
+ }]);
1378
+
1379
+
1380
+
1381
+ /**
1382
+ * @ngdoc filter
1383
+ * @name formatBytes
1384
+ * @kind function
1385
+ *
1386
+ * @description
1387
+ * Convert bytes into appropriate display
1388
+ * 1024 kilobytes => 1 MB
1389
+ */
1390
+ angular.module('a8m.math.kbFmt', ['a8m.math'])
1391
+ .filter('kbFmt', ['$math', function ($math) {
1392
+ return function (bytes, decimal) {
1393
+
1394
+ if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1395
+ isNumber(bytes) && isFinite(bytes)) {
1396
+ if(bytes < 1024) { // within 1 MB so KB
1397
+ return convertToDecimal(bytes, decimal, $math) + ' KB';
1398
+ } else if(bytes < 1048576) { // within 1 GB so MB
1399
+ return convertToDecimal((bytes / 1024), decimal, $math) + ' MB';
1400
+ } else {
1401
+ return convertToDecimal((bytes / 1048576), decimal, $math) + ' GB';
1402
+ }
1403
+ }
1404
+ return "NaN";
1405
+ }
1406
+ }]);
1407
+ /**
1408
+ * @ngdoc module
1409
+ * @name math
1410
+ * @description
1411
+ * reference to global Math object
1412
+ */
1413
+ angular.module('a8m.math', [])
1414
+ .factory('$math', ['$window', function ($window) {
1415
+ return $window.Math;
1416
+ }]);
1417
+
1418
+ /**
1419
+ * @ngdoc filter
1420
+ * @name max
1421
+ * @kind function
1422
+ *
1423
+ * @description
1424
+ * Math.max will get an array and return the max value. if an expression
1425
+ * is provided, will return max value by expression.
1426
+ */
1427
+ angular.module('a8m.math.max', ['a8m.math'])
1428
+ .filter('max', ['$math', '$parse', function ($math, $parse) {
1429
+ return function (input, expression) {
1430
+
1431
+ if(!isArray(input)) {
1432
+ return input;
1433
+ }
1434
+ return isUndefined(expression)
1435
+ ? $math.max.apply($math, input)
1436
+ : input[indexByMax(input, expression)];
1437
+ };
1438
+
1439
+ /**
1440
+ * @private
1441
+ * @param array
1442
+ * @param exp
1443
+ * @returns {number|*|Number}
1444
+ */
1445
+ function indexByMax(array, exp) {
1446
+ var mappedArray = array.map(function(elm){
1447
+ return $parse(exp)(elm);
1448
+ });
1449
+ return mappedArray.indexOf($math.max.apply($math, mappedArray));
1450
+ }
1451
+ }]);
1452
+ /**
1453
+ * @ngdoc filter
1454
+ * @name min
1455
+ * @kind function
1456
+ *
1457
+ * @description
1458
+ * Math.min will get an array and return the min value. if an expression
1459
+ * is provided, will return min value by expression.
1460
+ */
1461
+ angular.module('a8m.math.min', ['a8m.math'])
1462
+ .filter('min', ['$math', '$parse', function ($math, $parse) {
1463
+ return function (input, expression) {
1464
+
1465
+ if(!isArray(input)) {
1466
+ return input;
1467
+ }
1468
+ return isUndefined(expression)
1469
+ ? $math.min.apply($math, input)
1470
+ : input[indexByMin(input, expression)];
1471
+ };
1472
+
1473
+ /**
1474
+ * @private
1475
+ * @param array
1476
+ * @param exp
1477
+ * @returns {number|*|Number}
1478
+ */
1479
+ function indexByMin(array, exp) {
1480
+ var mappedArray = array.map(function(elm){
1481
+ return $parse(exp)(elm);
1482
+ });
1483
+ return mappedArray.indexOf($math.min.apply($math, mappedArray));
1484
+ }
1485
+ }]);
1486
+ /**
1487
+ * @ngdoc filter
1488
+ * @name Percent
1489
+ * @kind function
1490
+ *
1491
+ * @description
1492
+ * percentage between two numbers
1493
+ */
1494
+ angular.module('a8m.math.percent', ['a8m.math'])
1495
+ .filter('percent', ['$math', '$window', function ($math, $window) {
1496
+ return function (input, divided, round) {
1497
+
1498
+ var divider = isString(input) ? $window.Number(input) : input;
1499
+ divided = divided || 100;
1500
+ round = round || false;
1501
+
1502
+ if (!isNumber(divider) || $window.isNaN(divider)) return input;
1503
+
1504
+ return round
1505
+ ? $math.round((divider / divided) * 100)
1506
+ : (divider / divided) * 100;
1507
+ }
1508
+ }]);
1509
+
1510
+ /**
1511
+ * @ngdoc filter
1512
+ * @name toRadians
1513
+ * @kind function
1514
+ *
1515
+ * @description
1516
+ * Convert angle from degrees to radians
1517
+ */
1518
+ angular.module('a8m.math.radians', ['a8m.math'])
1519
+ .filter('radians', ['$math', function ($math) {
1520
+ return function (degrees, decimal) {
1521
+ // if decimal is not an integer greater than -1, we cannot do. quit with error "NaN"
1522
+ // if degrees is not a real number, we cannot do also. quit with error "NaN"
1523
+ if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1524
+ isNumber(degrees) && isFinite(degrees)) {
1525
+ var radians = (degrees * 3.14159265359) / 180;
1526
+ return $math.round(radians * $math.pow(10,decimal)) / ($math.pow(10,decimal));
1527
+ }
1528
+ return "NaN";
1529
+ }
1530
+ }]);
1531
+
1532
+
1533
+
1534
+ /**
1535
+ * @ngdoc filter
1536
+ * @name Radix
1537
+ * @kind function
1538
+ *
1539
+ * @description
1540
+ * converting decimal numbers to different bases(radix)
1541
+ */
1542
+ angular.module('a8m.math.radix', [])
1543
+ .filter('radix', function () {
1544
+ return function (input, radix) {
1545
+ var RANGE = /^[2-9]$|^[1-2]\d$|^3[0-6]$/;
1546
+
1547
+ if(!isNumber(input) || !RANGE.test(radix)) {
1548
+ return input;
1549
+ }
1550
+
1551
+ return input.toString(radix).toUpperCase();
1552
+ }
1553
+ });
1554
+
1555
+ /**
1556
+ * @ngdoc filter
1557
+ * @name formatBytes
1558
+ * @kind function
1559
+ *
1560
+ * @description
1561
+ * Convert number into abbreviations.
1562
+ * i.e: K for one thousand, M for Million, B for billion
1563
+ * e.g: number of users:235,221, decimal:1 => 235.2 K
1564
+ */
1565
+ angular.module('a8m.math.shortFmt', ['a8m.math'])
1566
+ .filter('shortFmt', ['$math', function ($math) {
1567
+ return function (number, decimal) {
1568
+ if(isNumber(decimal) && isFinite(decimal) && decimal%1===0 && decimal >= 0 &&
1569
+ isNumber(number) && isFinite(number)){
1570
+ if(number < 1e3) {
1571
+ return number;
1572
+ } else if(number < 1e6) {
1573
+ return convertToDecimal((number / 1e3), decimal, $math) + ' K';
1574
+ } else if(number < 1e9){
1575
+ return convertToDecimal((number / 1e6), decimal, $math) + ' M';
1576
+ } else {
1577
+ return convertToDecimal((number / 1e9), decimal, $math) + ' B';
1578
+ }
1579
+
1580
+ }
1581
+ return "NaN";
1582
+ }
1583
+ }]);
1584
+ /**
1585
+ * @ngdoc filter
1586
+ * @name sum
1587
+ * @kind function
1588
+ *
1589
+ * @description
1590
+ * Sum up all values within an array
1591
+ */
1592
+ angular.module('a8m.math.sum', [])
1593
+ .filter('sum', function () {
1594
+ return function (input, initial) {
1595
+ return !isArray(input)
1596
+ ? input
1597
+ : input.reduce(function(prev, curr) {
1598
+ return prev + curr;
1599
+ }, initial || 0);
1600
+ }
1601
+ });
1602
+
1603
+ /**
1604
+ * @ngdoc filter
1605
+ * @name endsWith
1606
+ * @kind function
1607
+ *
1608
+ * @description
1609
+ * checks whether string ends with the ends parameter.
1610
+ */
1611
+ angular.module('a8m.ends-with', [])
1612
+
1613
+ .filter('endsWith', function () {
1614
+ return function (input, ends, csensitive) {
1615
+
1616
+ var sensitive = csensitive || false,
1617
+ position;
1618
+
1619
+ if(!isString(input) || isUndefined(ends)) {
1620
+ return input;
1621
+ }
1622
+
1623
+ input = (sensitive) ? input : input.toLowerCase();
1624
+ position = input.length - ends.length;
1625
+
1626
+ return input.indexOf((sensitive) ? ends : ends.toLowerCase(), position) !== -1;
1627
+ }
1628
+ });
1629
+
1630
+ /**
1631
+ * @ngdoc filter
1632
+ * @name latinize
1633
+ * @kind function
1634
+ *
1635
+ * @description
1636
+ * remove accents/diacritics from a string
1637
+ */
1638
+ angular.module('a8m.latinize', [])
1639
+ .filter('latinize',[ function () {
1640
+ var defaultDiacriticsRemovalap = [
1641
+ {'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'},
1642
+ {'base':'AA','letters':'\uA732'},
1643
+ {'base':'AE','letters':'\u00C6\u01FC\u01E2'},
1644
+ {'base':'AO','letters':'\uA734'},
1645
+ {'base':'AU','letters':'\uA736'},
1646
+ {'base':'AV','letters':'\uA738\uA73A'},
1647
+ {'base':'AY','letters':'\uA73C'},
1648
+ {'base':'B', 'letters':'\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181'},
1649
+ {'base':'C', 'letters':'\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E'},
1650
+ {'base':'D', 'letters':'\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779'},
1651
+ {'base':'DZ','letters':'\u01F1\u01C4'},
1652
+ {'base':'Dz','letters':'\u01F2\u01C5'},
1653
+ {'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'},
1654
+ {'base':'F', 'letters':'\u0046\u24BB\uFF26\u1E1E\u0191\uA77B'},
1655
+ {'base':'G', 'letters':'\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E'},
1656
+ {'base':'H', 'letters':'\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D'},
1657
+ {'base':'I', 'letters':'\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197'},
1658
+ {'base':'J', 'letters':'\u004A\u24BF\uFF2A\u0134\u0248'},
1659
+ {'base':'K', 'letters':'\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2'},
1660
+ {'base':'L', 'letters':'\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780'},
1661
+ {'base':'LJ','letters':'\u01C7'},
1662
+ {'base':'Lj','letters':'\u01C8'},
1663
+ {'base':'M', 'letters':'\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C'},
1664
+ {'base':'N', 'letters':'\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4'},
1665
+ {'base':'NJ','letters':'\u01CA'},
1666
+ {'base':'Nj','letters':'\u01CB'},
1667
+ {'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'},
1668
+ {'base':'OI','letters':'\u01A2'},
1669
+ {'base':'OO','letters':'\uA74E'},
1670
+ {'base':'OU','letters':'\u0222'},
1671
+ {'base':'OE','letters':'\u008C\u0152'},
1672
+ {'base':'oe','letters':'\u009C\u0153'},
1673
+ {'base':'P', 'letters':'\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754'},
1674
+ {'base':'Q', 'letters':'\u0051\u24C6\uFF31\uA756\uA758\u024A'},
1675
+ {'base':'R', 'letters':'\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782'},
1676
+ {'base':'S', 'letters':'\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784'},
1677
+ {'base':'T', 'letters':'\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786'},
1678
+ {'base':'TZ','letters':'\uA728'},
1679
+ {'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'},
1680
+ {'base':'V', 'letters':'\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245'},
1681
+ {'base':'VY','letters':'\uA760'},
1682
+ {'base':'W', 'letters':'\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72'},
1683
+ {'base':'X', 'letters':'\u0058\u24CD\uFF38\u1E8A\u1E8C'},
1684
+ {'base':'Y', 'letters':'\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE'},
1685
+ {'base':'Z', 'letters':'\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762'},
1686
+ {'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'},
1687
+ {'base':'aa','letters':'\uA733'},
1688
+ {'base':'ae','letters':'\u00E6\u01FD\u01E3'},
1689
+ {'base':'ao','letters':'\uA735'},
1690
+ {'base':'au','letters':'\uA737'},
1691
+ {'base':'av','letters':'\uA739\uA73B'},
1692
+ {'base':'ay','letters':'\uA73D'},
1693
+ {'base':'b', 'letters':'\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253'},
1694
+ {'base':'c', 'letters':'\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184'},
1695
+ {'base':'d', 'letters':'\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A'},
1696
+ {'base':'dz','letters':'\u01F3\u01C6'},
1697
+ {'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'},
1698
+ {'base':'f', 'letters':'\u0066\u24D5\uFF46\u1E1F\u0192\uA77C'},
1699
+ {'base':'g', 'letters':'\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F'},
1700
+ {'base':'h', 'letters':'\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265'},
1701
+ {'base':'hv','letters':'\u0195'},
1702
+ {'base':'i', 'letters':'\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131'},
1703
+ {'base':'j', 'letters':'\u006A\u24D9\uFF4A\u0135\u01F0\u0249'},
1704
+ {'base':'k', 'letters':'\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3'},
1705
+ {'base':'l', 'letters':'\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747'},
1706
+ {'base':'lj','letters':'\u01C9'},
1707
+ {'base':'m', 'letters':'\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F'},
1708
+ {'base':'n', 'letters':'\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5'},
1709
+ {'base':'nj','letters':'\u01CC'},
1710
+ {'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'},
1711
+ {'base':'oi','letters':'\u01A3'},
1712
+ {'base':'ou','letters':'\u0223'},
1713
+ {'base':'oo','letters':'\uA74F'},
1714
+ {'base':'p','letters':'\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755'},
1715
+ {'base':'q','letters':'\u0071\u24E0\uFF51\u024B\uA757\uA759'},
1716
+ {'base':'r','letters':'\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783'},
1717
+ {'base':'s','letters':'\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B'},
1718
+ {'base':'t','letters':'\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787'},
1719
+ {'base':'tz','letters':'\uA729'},
1720
+ {'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'},
1721
+ {'base':'v','letters':'\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C'},
1722
+ {'base':'vy','letters':'\uA761'},
1723
+ {'base':'w','letters':'\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73'},
1724
+ {'base':'x','letters':'\u0078\u24E7\uFF58\u1E8B\u1E8D'},
1725
+ {'base':'y','letters':'\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF'},
1726
+ {'base':'z','letters':'\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763'}
1727
+ ];
1728
+
1729
+ var diacriticsMap = {};
1730
+ for (var i = 0; i < defaultDiacriticsRemovalap.length; i++) {
1731
+ var letters = defaultDiacriticsRemovalap[i].letters.split("");
1732
+ for (var j = 0; j < letters.length ; j++){
1733
+ diacriticsMap[letters[j]] = defaultDiacriticsRemovalap[i].base;
1734
+ }
1735
+ }
1736
+
1737
+ // "what?" version ... http://jsperf.com/diacritics/12
1738
+ function removeDiacritics (str) {
1739
+ return str.replace(/[^\u0000-\u007E]/g, function(a){
1740
+ return diacriticsMap[a] || a;
1741
+ });
1742
+ }
1743
+
1744
+ return function (input) {
1745
+
1746
+ return isString(input)
1747
+ ? removeDiacritics(input)
1748
+ : input;
1749
+ }
1750
+ }]);
1751
+
1752
+ /**
1753
+ * @ngdoc filter
1754
+ * @name ltrim
1755
+ * @kind function
1756
+ *
1757
+ * @description
1758
+ * Left trim. Similar to trimFilter, but only for left side.
1759
+ */
1760
+ angular.module('a8m.ltrim', [])
1761
+ .filter('ltrim', function () {
1762
+ return function(input, chars) {
1763
+
1764
+ var trim = chars || '\\s';
1765
+
1766
+ return isString(input)
1767
+ ? input.replace(new RegExp('^' + trim + '+'), '')
1768
+ : input;
1769
+ }
1770
+ });
1771
+
1772
+ /**
1773
+ * @ngdoc filter
1774
+ * @name match
1775
+ * @kind function
1776
+ *
1777
+ * @description
1778
+ * Return the matched pattern in a string.
1779
+ */
1780
+ angular.module('a8m.match', [])
1781
+ .filter('match', function () {
1782
+ return function (input, pattern, flag) {
1783
+
1784
+ var reg = new RegExp(pattern, flag);
1785
+
1786
+ return isString(input)
1787
+ ? input.match(reg)
1788
+ : null;
1789
+ }
1790
+ });
1791
+
1792
+ /**
1793
+ * @ngdoc filter
1794
+ * @name repeat
1795
+ * @kind function
1796
+ *
1797
+ * @description
1798
+ * Repeats a string n times
1799
+ */
1800
+ angular.module('a8m.repeat', [])
1801
+ .filter('repeat',[ function () {
1802
+ return function (input, n, separator) {
1803
+
1804
+ var times = ~~n;
1805
+
1806
+ if(!isString(input)) {
1807
+ return input;
1808
+ }
1809
+
1810
+ return !times
1811
+ ? input
1812
+ : strRepeat(input, --n, separator || '');
1813
+ }
1814
+ }]);
1815
+
1816
+ /**
1817
+ * Repeats a string n times with given separator
1818
+ * @param str string to repeat
1819
+ * @param n number of times
1820
+ * @param sep separator
1821
+ * @returns {*}
1822
+ */
1823
+ function strRepeat(str, n, sep) {
1824
+ if(!n) {
1825
+ return str;
1826
+ }
1827
+ return str + sep + strRepeat(str, --n, sep);
1828
+ }
1829
+ /**
1830
+ * @ngdoc filter
1831
+ * @name rtrim
1832
+ * @kind function
1833
+ *
1834
+ * @description
1835
+ * Right trim. Similar to trimFilter, but only for right side.
1836
+ */
1837
+ angular.module('a8m.rtrim', [])
1838
+ .filter('rtrim', function () {
1839
+ return function(input, chars) {
1840
+
1841
+ var trim = chars || '\\s';
1842
+
1843
+ return isString(input)
1844
+ ? input.replace(new RegExp(trim + '+$'), '')
1845
+ : input;
1846
+ }
1847
+ });
1848
+
1849
+ /**
1850
+ * @ngdoc filter
1851
+ * @name slugify
1852
+ * @kind function
1853
+ *
1854
+ * @description
1855
+ * remove spaces from string, replace with "-" or given argument
1856
+ */
1857
+ angular.module('a8m.slugify', [])
1858
+ .filter('slugify',[ function () {
1859
+ return function (input, sub) {
1860
+
1861
+ var replace = (isUndefined(sub)) ? '-' : sub;
1862
+
1863
+ return isString(input)
1864
+ ? input.toLowerCase().replace(/\s+/g, replace)
1865
+ : input;
1866
+ }
1867
+ }]);
1868
+
1869
+ /**
1870
+ * @ngdoc filter
1871
+ * @name startWith
1872
+ * @kind function
1873
+ *
1874
+ * @description
1875
+ * checks whether string starts with the starts parameter.
1876
+ */
1877
+ angular.module('a8m.starts-with', [])
1878
+ .filter('startsWith', function () {
1879
+ return function (input, start, csensitive) {
1880
+
1881
+ var sensitive = csensitive || false;
1882
+
1883
+ if(!isString(input) || isUndefined(start)) {
1884
+ return input;
1885
+ }
1886
+
1887
+ input = (sensitive) ? input : input.toLowerCase();
1888
+
1889
+ return !input.indexOf((sensitive) ? start : start.toLowerCase());
1890
+ }
1891
+ });
1892
+
1893
+ /**
1894
+ * @ngdoc filter
1895
+ * @name stringular
1896
+ * @kind function
1897
+ *
1898
+ * @description
1899
+ * get string with {n} and replace match with enumeration values
1900
+ */
1901
+ angular.module('a8m.stringular', [])
1902
+ .filter('stringular', function () {
1903
+ return function(input) {
1904
+
1905
+ var args = Array.prototype.slice.call(arguments, 1);
1906
+
1907
+ return input.replace(/{(\d+)}/g, function (match, number) {
1908
+ return isUndefined(args[number]) ? match : args[number];
1909
+ });
1910
+ }
1911
+ });
1912
+
1913
+ /**
1914
+ * @ngdoc filter
1915
+ * @name stripTags
1916
+ * @kind function
1917
+ *
1918
+ * @description
1919
+ * strip html tags from string
1920
+ */
1921
+ angular.module('a8m.strip-tags', [])
1922
+ .filter('stripTags', function () {
1923
+ return function(input) {
1924
+ return isString(input)
1925
+ ? input.replace(/<\S[^><]*>/g, '')
1926
+ : input;
1927
+ }
1928
+ });
1929
+
1930
+ /**
1931
+ * @ngdoc filter
1932
+ * @name test
1933
+ * @kind function
1934
+ *
1935
+ * @description
1936
+ * test if a string match a pattern.
1937
+ */
1938
+ angular.module('a8m.test', [])
1939
+ .filter('test', function () {
1940
+ return function (input, pattern, flag) {
1941
+
1942
+ var reg = new RegExp(pattern, flag);
1943
+
1944
+ return isString(input)
1945
+ ? reg.test(input)
1946
+ : input;
1947
+ }
1948
+ });
1949
+
1950
+ /**
1951
+ * @ngdoc filter
1952
+ * @name trim
1953
+ * @kind function
1954
+ *
1955
+ * @description
1956
+ * Strip whitespace (or other characters) from the beginning and end of a string
1957
+ */
1958
+ angular.module('a8m.trim', [])
1959
+ .filter('trim', function () {
1960
+ return function(input, chars) {
1961
+
1962
+ var trim = chars || '\\s';
1963
+
1964
+ return isString(input)
1965
+ ? input.replace(new RegExp('^' + trim + '+|' + trim + '+$', 'g'), '')
1966
+ : input;
1967
+ }
1968
+ });
1969
+
1970
+ /**
1971
+ * @ngdoc filter
1972
+ * @name truncate
1973
+ * @kind function
1974
+ *
1975
+ * @description
1976
+ * truncates a string given a specified length, providing a custom string to denote an omission.
1977
+ */
1978
+ angular.module('a8m.truncate', [])
1979
+ .filter('truncate', function () {
1980
+ return function(input, length, suffix, preserve) {
1981
+
1982
+ length = isUndefined(length) ? input.length : length;
1983
+ preserve = preserve || false;
1984
+ suffix = suffix || '';
1985
+
1986
+ if(!isString(input) || (input.length <= length)) return input;
1987
+
1988
+ return input.substring(0, (preserve)
1989
+ ? ((input.indexOf(' ', length) === -1) ? input.length : input.indexOf(' ', length))
1990
+ : length) + suffix;
1991
+ };
1992
+ });
1993
+
1994
+ /**
1995
+ * @ngdoc filter
1996
+ * @name ucfirst
1997
+ * @kind function
1998
+ *
1999
+ * @description
2000
+ * ucfirst
2001
+ */
2002
+ angular.module('a8m.ucfirst', [])
2003
+ .filter('ucfirst', [function() {
2004
+ return function(input) {
2005
+ return isString(input)
2006
+ ? input
2007
+ .split(' ')
2008
+ .map(function (ch) {
2009
+ return ch.charAt(0).toUpperCase() + ch.substring(1);
2010
+ })
2011
+ .join(' ')
2012
+ : input;
2013
+ }
2014
+ }]);
2015
+
2016
+ /**
2017
+ * @ngdoc filter
2018
+ * @name uriComponentEncode
2019
+ * @kind function
2020
+ *
2021
+ * @description
2022
+ * get string as parameter and return encoded string
2023
+ */
2024
+ angular.module('a8m.uri-component-encode', [])
2025
+ .filter('uriComponentEncode',['$window', function ($window) {
2026
+ return function (input) {
2027
+ return isString(input)
2028
+ ? $window.encodeURIComponent(input)
2029
+ : input;
2030
+ }
2031
+ }]);
2032
+
2033
+ /**
2034
+ * @ngdoc filter
2035
+ * @name uriEncode
2036
+ * @kind function
2037
+ *
2038
+ * @description
2039
+ * get string as parameter and return encoded string
2040
+ */
2041
+ angular.module('a8m.uri-encode', [])
2042
+ .filter('uriEncode',['$window', function ($window) {
2043
+ return function (input) {
2044
+ return isString(input)
2045
+ ? $window.encodeURI(input)
2046
+ : input;
2047
+ }
2048
+ }]);
2049
+
2050
+ /**
2051
+ * @ngdoc filter
2052
+ * @name wrap
2053
+ * @kind function
2054
+ *
2055
+ * @description
2056
+ * Wrap a string with another string
2057
+ */
2058
+ angular.module('a8m.wrap', [])
2059
+ .filter('wrap', function () {
2060
+ return function(input, wrap, ends) {
2061
+ return isString(input) && isDefined(wrap)
2062
+ ? [wrap, input, ends || wrap].join('')
2063
+ : input;
2064
+ }
2065
+ });
2066
+
2067
+ /**
2068
+ * @ngdoc provider
2069
+ * @name filterWatcher
2070
+ * @kind function
2071
+ *
2072
+ * @description
2073
+ * store specific filters result in $$cache, based on scope life time(avoid memory leak).
2074
+ * on scope.$destroy remove it's cache from $$cache container
2075
+ */
2076
+
2077
+ angular.module('a8m.filter-watcher', [])
2078
+ .provider('filterWatcher', function() {
2079
+
2080
+ this.$get = ['$window', '$rootScope', function($window, $rootScope) {
2081
+
2082
+ /**
2083
+ * Cache storing
2084
+ * @type {Object}
2085
+ */
2086
+ var $$cache = {};
2087
+
2088
+ /**
2089
+ * Scope listeners container
2090
+ * scope.$destroy => remove all cache keys
2091
+ * bind to current scope.
2092
+ * @type {Object}
2093
+ */
2094
+ var $$listeners = {};
2095
+
2096
+ /**
2097
+ * $timeout without triggering the digest cycle
2098
+ * @type {function}
2099
+ */
2100
+ var $$timeout = $window.setTimeout;
2101
+
2102
+ /**
2103
+ * @description
2104
+ * get `HashKey` string based on the given arguments.
2105
+ * @param fName
2106
+ * @param args
2107
+ * @returns {string}
2108
+ */
2109
+ function getHashKey(fName, args) {
2110
+ function replacerFactory() {
2111
+ var cache = [];
2112
+ return function(key, val) {
2113
+ if(isObject(val) && !isNull(val)) {
2114
+ if (~cache.indexOf(val)) return '[Circular]';
2115
+ cache.push(val)
2116
+ }
2117
+ if($window == val) return '$WINDOW';
2118
+ if($window.document == val) return '$DOCUMENT';
2119
+ if(isScope(val)) return '$SCOPE';
2120
+ return val;
2121
+ }
2122
+ }
2123
+ return [fName, JSON.stringify(args, replacerFactory())]
2124
+ .join('#')
2125
+ .replace(/"/g,'');
2126
+ }
2127
+
2128
+ /**
2129
+ * @description
2130
+ * fir on $scope.$destroy,
2131
+ * remove cache based scope from `$$cache`,
2132
+ * and remove itself from `$$listeners`
2133
+ * @param event
2134
+ */
2135
+ function removeCache(event) {
2136
+ var id = event.targetScope.$id;
2137
+ forEach($$listeners[id], function(key) {
2138
+ delete $$cache[key];
2139
+ });
2140
+ delete $$listeners[id];
2141
+ }
2142
+
2143
+ /**
2144
+ * @description
2145
+ * for angular version that greater than v.1.3.0
2146
+ * it clear cache when the digest cycle is end.
2147
+ */
2148
+ function cleanStateless() {
2149
+ $$timeout(function() {
2150
+ if(!$rootScope.$$phase)
2151
+ $$cache = {};
2152
+ }, 2000);
2153
+ }
2154
+
2155
+ /**
2156
+ * @description
2157
+ * Store hashKeys in $$listeners container
2158
+ * on scope.$destroy, remove them all(bind an event).
2159
+ * @param scope
2160
+ * @param hashKey
2161
+ * @returns {*}
2162
+ */
2163
+ function addListener(scope, hashKey) {
2164
+ var id = scope.$id;
2165
+ if(isUndefined($$listeners[id])) {
2166
+ scope.$on('$destroy', removeCache);
2167
+ $$listeners[id] = [];
2168
+ }
2169
+ return $$listeners[id].push(hashKey);
2170
+ }
2171
+
2172
+ /**
2173
+ * @description
2174
+ * return the `cacheKey` or undefined.
2175
+ * @param filterName
2176
+ * @param args
2177
+ * @returns {*}
2178
+ */
2179
+ function $$isMemoized(filterName, args) {
2180
+ var hashKey = getHashKey(filterName, args);
2181
+ return $$cache[hashKey];
2182
+ }
2183
+
2184
+ /**
2185
+ * @description
2186
+ * store `result` in `$$cache` container, based on the hashKey.
2187
+ * add $destroy listener and return result
2188
+ * @param filterName
2189
+ * @param args
2190
+ * @param scope
2191
+ * @param result
2192
+ * @returns {*}
2193
+ */
2194
+ function $$memoize(filterName, args, scope, result) {
2195
+ var hashKey = getHashKey(filterName, args);
2196
+ //store result in `$$cache` container
2197
+ $$cache[hashKey] = result;
2198
+ // for angular versions that less than 1.3
2199
+ // add to `$destroy` listener, a cleaner callback
2200
+ if(isScope(scope)) {
2201
+ addListener(scope, hashKey);
2202
+ } else {
2203
+ cleanStateless();
2204
+ }
2205
+ return result;
2206
+ }
2207
+
2208
+ return {
2209
+ isMemoized: $$isMemoized,
2210
+ memoize: $$memoize
2211
+ }
2212
+ }];
2213
+ });
2214
+
2215
+
2216
+ /**
2217
+ * @ngdoc module
2218
+ * @name angular.filters
2219
+ * @description
2220
+ * Bunch of useful filters for angularJS
2221
+ */
2222
+
2223
+ angular.module('angular.filter', [
2224
+
2225
+ 'a8m.ucfirst',
2226
+ 'a8m.uri-encode',
2227
+ 'a8m.uri-component-encode',
2228
+ 'a8m.slugify',
2229
+ 'a8m.latinize',
2230
+ 'a8m.strip-tags',
2231
+ 'a8m.stringular',
2232
+ 'a8m.truncate',
2233
+ 'a8m.starts-with',
2234
+ 'a8m.ends-with',
2235
+ 'a8m.wrap',
2236
+ 'a8m.trim',
2237
+ 'a8m.ltrim',
2238
+ 'a8m.rtrim',
2239
+ 'a8m.repeat',
2240
+ 'a8m.test',
2241
+ 'a8m.match',
2242
+
2243
+ 'a8m.to-array',
2244
+ 'a8m.concat',
2245
+ 'a8m.contains',
2246
+ 'a8m.unique',
2247
+ 'a8m.is-empty',
2248
+ 'a8m.after',
2249
+ 'a8m.after-where',
2250
+ 'a8m.before',
2251
+ 'a8m.before-where',
2252
+ 'a8m.defaults',
2253
+ 'a8m.where',
2254
+ 'a8m.reverse',
2255
+ 'a8m.remove',
2256
+ 'a8m.remove-with',
2257
+ 'a8m.group-by',
2258
+ 'a8m.count-by',
2259
+ 'a8m.chunk-by',
2260
+ 'a8m.search-field',
2261
+ 'a8m.fuzzy-by',
2262
+ 'a8m.fuzzy',
2263
+ 'a8m.omit',
2264
+ 'a8m.pick',
2265
+ 'a8m.every',
2266
+ 'a8m.filter-by',
2267
+ 'a8m.xor',
2268
+ 'a8m.map',
2269
+ 'a8m.first',
2270
+ 'a8m.last',
2271
+ 'a8m.flatten',
2272
+ 'a8m.join',
2273
+ 'a8m.range',
2274
+
2275
+ 'a8m.math',
2276
+ 'a8m.math.max',
2277
+ 'a8m.math.min',
2278
+ 'a8m.math.percent',
2279
+ 'a8m.math.radix',
2280
+ 'a8m.math.sum',
2281
+ 'a8m.math.degrees',
2282
+ 'a8m.math.radians',
2283
+ 'a8m.math.byteFmt',
2284
+ 'a8m.math.kbFmt',
2285
+ 'a8m.math.shortFmt',
2286
+
2287
+ 'a8m.angular',
2288
+ 'a8m.conditions',
2289
+ 'a8m.is-null',
2290
+
2291
+ 'a8m.filter-watcher'
2292
+ ]);
2293
+ })( window, window.angular );