angular_master 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 );