├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── dist └── ng-filters.js ├── karma.conf.js ├── karma.underscore.conf.js ├── package.json └── src ├── angular-filters.js ├── bytes ├── bytes-filter-spec.js └── bytes-filter.js ├── join ├── join-filter-spec.js └── join-filter.js ├── percentage ├── percentage-filter-spec.js └── percentage-filter.js ├── replace ├── replace-filter-spec.js └── replace-filter.js ├── reverse ├── reverse-filter-spec.js └── reverse-filter.js └── toSpacedWords ├── to-spaced-words-filter-spec.js └── to-spaced-words-filter.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | bower_components 3 | node_modules 4 | /.idea 5 | 6 | *.log 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": false, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "browser": true, 13 | "smarttabs": true, 14 | "globals": { 15 | "jQuery": true, 16 | "angular": true, 17 | "console": true, 18 | "$": true, 19 | "_": true, 20 | "moment": true, 21 | "describe": true, 22 | "beforeEach": true, 23 | "module": true, 24 | "inject": true, 25 | "it": true, 26 | "expect": true, 27 | "xdescribe": true, 28 | "xit": true, 29 | "spyOn": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | 5 | before_script: 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | - 'npm install -g bower grunt-cli' 9 | - 'bower install --config.interactive=false' 10 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | meta: { 8 | banner: [ 9 | '/**', 10 | ' * <%= pkg.description %>', 11 | ' * @version v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>' + 12 | ' * @link <%= pkg.homepage %>', 13 | ' * @author <%= pkg.author %>', 14 | ' * @license MIT License, http://www.opensource.org/licenses/MIT', 15 | ' */', 16 | '' 17 | ].join('\n') 18 | }, 19 | dirs: { 20 | dest: 'dist' 21 | }, 22 | concat: { 23 | options: { 24 | banner: '<%= meta.banner %>' 25 | }, 26 | dist: { 27 | src: ['src/angular-filters.js', 'src/**/*-filter.js'], 28 | dest: '<%= dirs.dest %>/<%= pkg.name %>.js' 29 | } 30 | }, 31 | bowerInstall: { 32 | install: { 33 | } 34 | }, 35 | jshint: { 36 | files: ['Gruntfile.js', 'src/angular-filters.js', 'src/**/*.js'], 37 | options: { 38 | jshintrc: true 39 | } 40 | }, 41 | karma: { 42 | options: { 43 | configFile: 'karma.conf.js' 44 | }, 45 | build: { 46 | singleRun: true, 47 | autoWatch: false 48 | }, 49 | debug: { 50 | singleRun: false, 51 | autoWatch: true, 52 | browsers: ['Chrome'] 53 | }, 54 | travis: { 55 | singleRun: true, 56 | autoWatch: false, 57 | browsers: ['Firefox'] 58 | }, 59 | travisUnderscore: { 60 | singleRun: true, 61 | autoWatch: false, 62 | browsers: ['Firefox'], 63 | configFile: 'karma.underscore.conf.js' 64 | }, 65 | buildUnderscore: { 66 | configFile: 'karma.underscore.conf.js', 67 | singleRun: true, 68 | autoWatch: false 69 | }, 70 | dev: { 71 | autoWatch: true 72 | } 73 | }, 74 | changelog: { 75 | options: { 76 | dest: 'CHANGELOG.md' 77 | } 78 | } 79 | }); 80 | 81 | // Load the plugin that provides the "jshint" task. 82 | grunt.loadNpmTasks('grunt-contrib-jshint'); 83 | 84 | // Load the plugin that provides the "concat" task. 85 | grunt.loadNpmTasks('grunt-contrib-concat'); 86 | 87 | grunt.loadNpmTasks('grunt-bower-task'); 88 | 89 | grunt.renameTask('bower', 'bowerInstall'); 90 | 91 | grunt.loadNpmTasks('grunt-karma'); 92 | grunt.loadNpmTasks('grunt-conventional-changelog'); 93 | 94 | // Default task. 95 | grunt.registerTask('default', ['build']); 96 | 97 | // Build task. 98 | grunt.registerTask('build', ['bowerInstall', /* 'test', */ 'concat']); 99 | 100 | grunt.registerTask('test', ['karma:build', 'karma:buildUnderscore']); 101 | 102 | grunt.registerTask('test-debug', ['karma:debug']); 103 | 104 | grunt.registerTask('travis', ['karma:travis', 'karma:travisUnderscore']); 105 | 106 | // Provides the "bump" task. 107 | grunt.registerTask('bump', 'Increment version number', function() { 108 | var versionType = grunt.option('type'); 109 | function bumpVersion(version, versionType) { 110 | var type = {patch: 2, minor: 1, major: 0}, 111 | parts = version.split('.'), 112 | idx = type[versionType || 'patch']; 113 | parts[idx] = parseInt(parts[idx], 10) + 1; 114 | while(++idx < parts.length) { parts[idx] = 0; } 115 | return parts.join('.'); 116 | } 117 | var version; 118 | function updateFile(file) { 119 | var json = grunt.file.readJSON(file); 120 | version = json.version = bumpVersion(json.version, versionType || 'patch'); 121 | grunt.file.write(file, JSON.stringify(json, null, ' ')); 122 | } 123 | updateFile('package.json'); 124 | updateFile('bower.json'); 125 | grunt.log.ok('Version bumped to ' + version); 126 | }); 127 | }; 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Blake Niemyjski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ng-filters 2 | [![Bower version](https://img.shields.io/bower/v/ng-filters.svg)](http://bower.io/search/?q=ng-filters) 3 | [![Slack Status](https://slack.exceptionless.com/badge.svg)](https://slack.exceptionless.com) 4 | [![Donate](https://img.shields.io/badge/donorbox-donate-blue.svg)](https://donorbox.org/exceptionless) 5 | 6 | Useful filters for AngularJS 7 | 8 | Install 9 | ------- 10 | ```html 11 | bower install ng-filters --save-dev 12 | ``` 13 | 14 | Usage 15 | ------- 16 | 17 | ###bytes 18 | Prints a number in a friendly byte representation
19 | 20 | ```html 21 | {{1536 | bytes}} 22 | {{'1536' | bytes}} 23 | {{1536 | bytes:2}} 24 | 25 | Result: 26 | 1.5 kB 27 | 1.5 kB 28 | 1.50 kB 29 | ``` 30 | 31 | ###join 32 | Joins an array into a string
33 | 34 | ```html 35 | {{[1, 2] | join}} 36 | {{[1, null, 2, undefined] | join}} 37 | {{[1, 2] | join:', '}} 38 | 39 | Result: 40 | 1,2 41 | 1,2 42 | 1, 2 43 | ``` 44 | 45 | ###percentage 46 | Returns a number formatted as a percentage. Numbers between 0 and 1 will be rounded up to 10th decimal place.
47 | 48 | ```html 49 | {{123 | percentage}} 50 | {{null | percentage}} 51 | {{60.0 | percentage}} 52 | {{0 | percentage}} 53 | {{0.000001 | percentage}} 54 | {{100.000001 | percentage:100}} 55 | 56 | Result: 57 | 123% 58 | 0% 59 | 60% 60 | 0% 61 | 0.1% 62 | 100% 63 | ``` 64 | 65 | ###replace 66 | Replaces string content
67 | 68 | ```html 69 | {{'blake' | replace:'b':'B'}} 70 | 71 | Result: 72 | Blake 73 | ``` 74 | 75 | ###reverse 76 | Reverses as string or array
77 | 78 | ```html 79 | {{'blake' | reverse}} 80 | {{[1, 2] | reverse}} 81 | 82 | Result: 83 | ekalb 84 | [2, 1] 85 | ``` 86 | 87 | ###toSpacedWords 88 | Splits a single word into multiple words
89 | 90 | ```html 91 | {{'blake' | toSpacedWords}} 92 | {{'blakeIsAwesome' | toSpacedWords}} 93 | 94 | Result: 95 | Blake 96 | Blake Is Awesome 97 | ``` 98 | 99 | Acknowledgements 100 | ------- 101 | I used [Restangular](https://github.com/mgonto/restangular) as a template for this project as no one has yet to create a project template for new projects. 102 | The bytes filter originated from [Thom Seddon](https://gist.github.com/thomseddon/3511330) before some improvements were made. 103 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-filters", 3 | "version": "1.3.1", 4 | "main": "./dist/ng-filters.js", 5 | "description": "Useful filters for AngularJS", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/exceptionless/ng-filters.git" 9 | }, 10 | "dependencies": { 11 | "angular": "^1.5.8", 12 | "angular-mocks": "^1.5.8" 13 | }, 14 | "ignore": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /dist/ng-filters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Useful filters for AngularJS 3 | * @version v1.3.1 - 2016-09-26 * @link https://github.com/exceptionless/ng-filters 4 | * @author Blake Niemyjski 5 | * @license MIT License, http://www.opensource.org/licenses/MIT 6 | */ 7 | (function () { 8 | 'use strict'; 9 | 10 | angular.module('angular-filters', []); 11 | }()); 12 | 13 | (function () { 14 | 'use strict'; 15 | 16 | angular.module('angular-filters') 17 | .filter('bytes', [function () { 18 | return function(bytes, precision) { 19 | if (typeof bytes !== 'number') { 20 | bytes = parseFloat(bytes); 21 | } 22 | 23 | if (bytes === 0) { 24 | return '0 B'; 25 | } else if (isNaN(bytes) || !isFinite(bytes)) { 26 | return '-'; 27 | } 28 | 29 | var isNegative = bytes < 0; 30 | if (isNegative) { 31 | bytes = -bytes; 32 | } 33 | 34 | if (typeof precision !== 'number') { 35 | precision = parseFloat(precision); 36 | } 37 | 38 | if (isNaN(precision) || !isFinite(precision)) { 39 | precision = 1; 40 | } 41 | 42 | var units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 43 | var exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1); 44 | var number = (bytes / Math.pow(1024, Math.floor(exponent))).toFixed(precision); 45 | 46 | return (isNegative ? '-' : '') + number + ' ' + units[exponent]; 47 | }; 48 | }]); 49 | }()); 50 | 51 | (function () { 52 | 'use strict'; 53 | 54 | angular.module('angular-filters') 55 | .filter('join', [function () { 56 | return function(input, separator) { 57 | if (!Array.isArray(input)){ 58 | return input; 59 | } 60 | 61 | var filtered = []; 62 | input.forEach(function (item) { 63 | if (item){ 64 | filtered.push(item); 65 | } 66 | }); 67 | 68 | return filtered.join(separator || ','); 69 | }; 70 | }]); 71 | }()); 72 | 73 | (function () { 74 | 'use strict'; 75 | 76 | angular.module('angular-filters') 77 | .filter('percentage', ['$filter', function ($filter) { 78 | function roundUpToNextTenth(input) { 79 | input = parseFloat(input.toFixed(5)); 80 | 81 | // Shift 82 | input = input.toString().split('e'); 83 | input = Math.ceil(+(input[0] + 'e' + (input[1] ? (+input[1] + 1) : 1))); 84 | // Shift back 85 | input = input.toString().split('e'); 86 | 87 | return +(input[0] + 'e' + (input[1] ? (+input[1] - 1) : -1)); 88 | } 89 | 90 | return function(input, max) { 91 | input = parseFloat(input); 92 | if (!isFinite(input)) { 93 | return '0%'; 94 | } 95 | 96 | input = Math.floor(input) + roundUpToNextTenth(input - Math.floor(input)); 97 | max = parseFloat(max); 98 | if (isFinite(max) && input > max) { 99 | return max + '%'; 100 | } 101 | 102 | return parseFloat($filter('number')(input, (input % 1 === 0) ? 0 : 1)) + '%'; 103 | }; 104 | }]); 105 | }()); 106 | 107 | (function () { 108 | 'use strict'; 109 | 110 | angular.module('angular-filters') 111 | .filter('replace', [function () { 112 | function isString(input) { 113 | return typeof input === 'string' || input instanceof String; 114 | } 115 | 116 | return function(input, searchValue, newValue) { 117 | if (!isString(input) || !isString(searchValue) || !isString(newValue)) 118 | return input; 119 | 120 | return input.split(searchValue).join(newValue); 121 | }; 122 | }]); 123 | }()); 124 | 125 | (function () { 126 | 'use strict'; 127 | 128 | angular.module('angular-filters') 129 | .filter('reverse', [function () { 130 | function reverseArray(items) { 131 | return items.slice().reverse(); 132 | } 133 | 134 | function reverseString(input) { 135 | var result = ''; 136 | for (var i = 0; i < input.length; i++) { 137 | result = input.charAt(i) + result; 138 | } 139 | 140 | return result; 141 | } 142 | 143 | return function(input) { 144 | if (Array.isArray(input)){ 145 | return reverseArray(input); 146 | } 147 | 148 | if (typeof input === 'string' || input instanceof String) { 149 | return reverseString(input); 150 | } 151 | 152 | return input; 153 | }; 154 | }]); 155 | }()); 156 | 157 | (function () { 158 | 'use strict'; 159 | 160 | angular.module('angular-filters') 161 | .filter('toSpacedWords', [function () { 162 | function toSpacedWords(input) { 163 | if (!input.match(/\d+|__/g)) { 164 | input = input.replace(/([a-z])([A-Z])/g, '$1 $2'); 165 | input = input.length > 1 ? input.charAt(0).toUpperCase() + input.slice(1) : input; 166 | } 167 | 168 | return input; 169 | } 170 | 171 | return function(input) { 172 | if (!input) { 173 | return input; 174 | } 175 | 176 | if (typeof input === 'string' || input instanceof String) { 177 | return toSpacedWords(input); 178 | } 179 | 180 | return input; 181 | }; 182 | }]); 183 | }()); 184 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '', 4 | frameworks: ["jasmine"], 5 | 6 | files: [ 7 | 'node_modules/angular/angular.js', 8 | 'node_modules/angular-mocks/angular-mocks.js', 9 | 'src/angular-filters.js', 10 | 'src/**/*.js' 11 | ], 12 | 13 | exclude: [], 14 | reporters: ['mocha'], 15 | port: 9876, 16 | runnerPort: 9100, 17 | colors: true, 18 | 19 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 20 | logLevel: config.LOG_WARN, 21 | autoWatch: true, 22 | 23 | browsers: ['PhantomJS'], 24 | 25 | captureTimeout: 60000, 26 | singleRun: true 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /karma.underscore.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri Aug 09 2013 14:14:35 GMT-0500 (CDT) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '', 8 | frameworks: ["jasmine"], 9 | 10 | // list of files / patterns to load in the browser 11 | files: [ 12 | 'bower_components/angular/angular.js', 13 | 'bower_components/angular-mocks/angular-mocks.js', 14 | 'src/angular-filters.js', 15 | 'src/**/*.js' 16 | ], 17 | 18 | // list of files to exclude 19 | exclude: [], 20 | 21 | // test results reporter to use 22 | // possible values: 'dots', 'progress', 'junit' 23 | reporters: ['progress'], 24 | 25 | // web server port 26 | port: 9877, 27 | 28 | // cli runner port 29 | runnerPort: 9101, 30 | 31 | // enable / disable colors in the output (reporters and logs) 32 | colors: true, 33 | 34 | // level of logging 35 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 36 | logLevel: config.LOG_INFO, 37 | 38 | // enable / disable watching file and executing tests whenever any file changes 39 | autoWatch: true, 40 | 41 | // Start these browsers, currently available: 42 | // - Chrome 43 | // - ChromeCanary 44 | // - Firefox 45 | // - Opera 46 | // - Safari (only Mac) 47 | // - PhantomJS 48 | // - IE (only Windows) 49 | browsers: ['PhantomJS'], 50 | 51 | // If browser does not capture in given timeout [ms], kill it 52 | captureTimeout: 60000, 53 | 54 | // Continuous Integration mode 55 | // if true, it capture browsers, run tests and exit 56 | singleRun: true 57 | }); 58 | }; 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-filters", 3 | "description": "Useful filters for AngularJS", 4 | "version": "1.3.1", 5 | "filename": "ng-filters.js", 6 | "main": "./dist/ng-filters.js", 7 | "homepage": "https://github.com/exceptionless/ng-filters", 8 | "author": "Blake Niemyjski ", 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/exceptionless/ng-filters.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "client", 16 | "browser", 17 | "filter", 18 | "bytes", 19 | "join", 20 | "replace", 21 | "reverse" 22 | ], 23 | "maintainers": [ 24 | { 25 | "name": "Blake Niemyjski", 26 | "website": "http://niemyjski.com" 27 | } 28 | ], 29 | "dependencies": { 30 | "angular": "^1.5.8", 31 | "angular-mocks": "^1.5.8" 32 | }, 33 | "devDependencies": { 34 | "grunt": "1.0.1", 35 | "grunt-bower": "0.21", 36 | "grunt-bower-task": "0.4", 37 | "grunt-cli": "1.2.0", 38 | "grunt-contrib-concat": "1.0", 39 | "grunt-contrib-jshint": "1.0", 40 | "grunt-conventional-changelog": "6.1.0", 41 | "grunt-karma": "2.0", 42 | "jasmine-core": "2.5.2", 43 | "karma": "1.3.0", 44 | "karma-chrome-launcher": "^2.0.0", 45 | "karma-jasmine": "1.0.2", 46 | "karma-mocha-reporter": "2.2.0", 47 | "karma-phantomjs-launcher": "1.0.2", 48 | "phantomjs-prebuilt": "2.1.12" 49 | }, 50 | "scripts": { 51 | "test": "grunt test --verbose" 52 | }, 53 | "license": "MIT" 54 | } 55 | -------------------------------------------------------------------------------- /src/angular-filters.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('angular-filters', []); 5 | }()); 6 | -------------------------------------------------------------------------------- /src/bytes/bytes-filter-spec.js: -------------------------------------------------------------------------------- 1 | describe('Filter: bytes', function() { 2 | beforeEach(module('angular-filters')); 3 | 4 | // initialize a new instance of the filter before each test 5 | var byte; 6 | beforeEach(inject(function ($filter) { 7 | byte = $filter('bytes'); 8 | })); 9 | 10 | it('should return nothing when there is no filesize', function () { 11 | expect(byte('text')).toBe('-'); 12 | }); 13 | 14 | it('should round the filesize based on the configured precision', function () { 15 | var size = 1024 + 512; 16 | expect(byte(size)).toBe('1.5 kB'); 17 | expect(byte(size, 2)).toBe('1.50 kB'); 18 | }); 19 | 20 | it('should parse string numbers into byte', function () { 21 | expect(byte('0', 0)).toBe('0 B'); 22 | expect(byte('10', 0)).toBe('10 B'); 23 | 24 | var size = 1024 + 512; 25 | expect(byte(size, '2')).toBe('1.50 kB'); 26 | }); 27 | 28 | it('should recognize zero byte', function () { 29 | expect(byte(0, 0)).toBe('0 B'); 30 | }); 31 | 32 | it('should recognize negative byte', function () { 33 | expect(byte(-1, 0)).toBe('-1 B'); 34 | }); 35 | 36 | it('should recognize byte', function () { 37 | expect(byte(1, 0)).toBe('1 B'); 38 | }); 39 | 40 | it('should recognize kilobyte', function () { 41 | expect(byte(Math.pow(1024, 1), 0)).toBe('1 kB'); 42 | }); 43 | 44 | it('should recognize megabyte', function () { 45 | expect(byte(Math.pow(1024, 2), 0)).toBe('1 MB'); 46 | }); 47 | 48 | it('should recognize gigabyte', function () { 49 | expect(byte(Math.pow(1024, 3), 0)).toBe('1 GB'); 50 | }); 51 | 52 | it('should recognize terabyte', function () { 53 | expect(byte(Math.pow(1024, 4), 0)).toBe('1 TB'); 54 | }); 55 | 56 | it('should recognize petabyte', function () { 57 | expect(byte(Math.pow(1024, 5), 0)).toBe('1 PB'); 58 | }); 59 | 60 | it('should recognize exabyte', function () { 61 | expect(byte(Math.pow(1024, 6), 0)).toBe('1 EB'); 62 | }); 63 | 64 | it('should recognize zettabyte', function () { 65 | expect(byte(Math.pow(1024, 7), 0)).toBe('1 ZB'); 66 | }); 67 | 68 | it('should recognize yottabyte', function () { 69 | expect(byte(Math.pow(1024, 8), 0)).toBe('1 YB'); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /src/bytes/bytes-filter.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('angular-filters') 5 | .filter('bytes', [function () { 6 | return function(bytes, precision) { 7 | if (typeof bytes !== 'number') { 8 | bytes = parseFloat(bytes); 9 | } 10 | 11 | if (bytes === 0) { 12 | return '0 B'; 13 | } else if (isNaN(bytes) || !isFinite(bytes)) { 14 | return '-'; 15 | } 16 | 17 | var isNegative = bytes < 0; 18 | if (isNegative) { 19 | bytes = -bytes; 20 | } 21 | 22 | if (typeof precision !== 'number') { 23 | precision = parseFloat(precision); 24 | } 25 | 26 | if (isNaN(precision) || !isFinite(precision)) { 27 | precision = 1; 28 | } 29 | 30 | var units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 31 | var exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1); 32 | var number = (bytes / Math.pow(1024, Math.floor(exponent))).toFixed(precision); 33 | 34 | return (isNegative ? '-' : '') + number + ' ' + units[exponent]; 35 | }; 36 | }]); 37 | }()); 38 | -------------------------------------------------------------------------------- /src/join/join-filter-spec.js: -------------------------------------------------------------------------------- 1 | describe('Filter: join', function() { 2 | beforeEach(module('angular-filters')); 3 | 4 | // initialize a new instance of the filter before each test 5 | var join; 6 | beforeEach(inject(function ($filter) { 7 | join = $filter('join'); 8 | })); 9 | 10 | it('should return undefined when nothing is passed in', function () { 11 | expect(join()).toBe(undefined); 12 | }); 13 | 14 | it('should return null when null is passed in', function () { 15 | expect(join(null)).toBe(null); 16 | }); 17 | 18 | it('should join array and filter null and undefined values', function () { 19 | expect(join([1, null, 2, undefined])).toBe('1,2'); 20 | }); 21 | 22 | it('should join array', function () { 23 | expect(join([1, 2])).toBe('1,2'); 24 | expect(join([1, 2], ', ')).toBe('1, 2'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/join/join-filter.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('angular-filters') 5 | .filter('join', [function () { 6 | return function(input, separator) { 7 | if (!Array.isArray(input)){ 8 | return input; 9 | } 10 | 11 | var filtered = []; 12 | input.forEach(function (item) { 13 | if (item){ 14 | filtered.push(item); 15 | } 16 | }); 17 | 18 | return filtered.join(separator || ','); 19 | }; 20 | }]); 21 | }()); 22 | -------------------------------------------------------------------------------- /src/percentage/percentage-filter-spec.js: -------------------------------------------------------------------------------- 1 | describe('Filter: percentage', function() { 2 | beforeEach(module('angular-filters')); 3 | 4 | // initialize a new instance of the filter before each test 5 | var percentage; 6 | beforeEach(inject(function ($filter) { 7 | percentage = $filter('percentage'); 8 | })); 9 | 10 | it('should return 0% when invalid value is passed in', function () { 11 | expect(percentage()).toBe('0%'); 12 | expect(percentage(null)).toBe('0%'); 13 | expect(percentage(true)).toBe('0%'); 14 | expect(percentage(false)).toBe('0%'); 15 | expect(percentage('')).toBe('0%'); 16 | }); 17 | 18 | it('should return number % when number is passed in', function () { 19 | expect(percentage(123)).toBe('123%'); 20 | }); 21 | 22 | it('should return rounded %', function () { 23 | expect(percentage(0)).toBe('0%'); 24 | expect(percentage(0.00000)).toBe('0%'); 25 | expect(percentage(1)).toBe('1%'); 26 | expect(percentage(2)).toBe('2%'); 27 | expect(percentage(2.11)).toBe('2.2%'); 28 | expect(percentage(0.99)).toBe('1%'); 29 | expect(percentage(0.000001)).toBe('0%'); 30 | expect(percentage(0.00001)).toBe('0.1%'); 31 | expect(percentage(0.0001)).toBe('0.1%'); 32 | expect(percentage(0.001)).toBe('0.1%'); 33 | expect(percentage(0.01)).toBe('0.1%'); 34 | expect(percentage(0.1)).toBe('0.1%'); 35 | expect(percentage(0.6)).toBe('0.6%'); 36 | expect(percentage(0.61)).toBe('0.7%'); 37 | expect(percentage(100.1)).toBe('100.1%'); 38 | expect(percentage(100.01)).toBe('100.1%'); 39 | }); 40 | 41 | it('should return rounded % with max', function () { 42 | expect(percentage(0.1, 0.01)).toBe('0.01%'); 43 | expect(percentage(0.6, 0.5)).toBe('0.5%'); 44 | expect(percentage(0.6, 0.7)).toBe('0.6%'); 45 | expect(percentage(99.9, 100)).toBe('99.9%'); 46 | expect(percentage(99.99, 99.99)).toBe('99.99%'); 47 | expect(percentage(99.99, 100)).toBe('100%'); 48 | expect(percentage(100, 100)).toBe('100%'); 49 | expect(percentage(100.00001, 100)).toBe('100%'); 50 | expect(percentage(100.00001, 100.0)).toBe('100%'); 51 | expect(percentage(101, 100)).toBe('100%'); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/percentage/percentage-filter.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('angular-filters') 5 | .filter('percentage', ['$filter', function ($filter) { 6 | function roundUpToNextTenth(input) { 7 | input = parseFloat(input.toFixed(5)); 8 | 9 | // Shift 10 | input = input.toString().split('e'); 11 | input = Math.ceil(+(input[0] + 'e' + (input[1] ? (+input[1] + 1) : 1))); 12 | // Shift back 13 | input = input.toString().split('e'); 14 | 15 | return +(input[0] + 'e' + (input[1] ? (+input[1] - 1) : -1)); 16 | } 17 | 18 | return function(input, max) { 19 | input = parseFloat(input); 20 | if (!isFinite(input)) { 21 | return '0%'; 22 | } 23 | 24 | input = Math.floor(input) + roundUpToNextTenth(input - Math.floor(input)); 25 | max = parseFloat(max); 26 | if (isFinite(max) && input > max) { 27 | return max + '%'; 28 | } 29 | 30 | return parseFloat($filter('number')(input, (input % 1 === 0) ? 0 : 1)) + '%'; 31 | }; 32 | }]); 33 | }()); 34 | -------------------------------------------------------------------------------- /src/replace/replace-filter-spec.js: -------------------------------------------------------------------------------- 1 | describe('Filter: replace', function() { 2 | beforeEach(module('angular-filters')); 3 | 4 | // initialize a new instance of the filter before each test 5 | var replace; 6 | beforeEach(inject(function ($filter) { 7 | replace = $filter('replace'); 8 | })); 9 | 10 | it('should return undefined when nothing is passed in', function () { 11 | expect(replace()).toBe(undefined); 12 | }); 13 | 14 | it('should return null when null is passed in', function () { 15 | expect(replace(null)).toBe(null); 16 | }); 17 | 18 | it('should return number when number is passed in', function () { 19 | expect(replace(123)).toBe(123); 20 | }); 21 | 22 | it('should return object when object is passed in', function () { 23 | expect(replace({ name: 'blake' })).toEqual({ name: 'blake' }); 24 | }); 25 | 26 | it('should return empty string', function () { 27 | expect(replace('')).toBe(''); 28 | }); 29 | 30 | it('should return initial value when there are invalid arguments', function () { 31 | expect(replace('blake')).toBe('blake'); 32 | expect(replace('blake', null)).toBe('blake'); 33 | expect(replace('blake', 1)).toBe('blake'); 34 | expect(replace('blake', null, null)).toBe('blake'); 35 | expect(replace('blake', 1, 1)).toBe('blake'); 36 | }); 37 | 38 | it('should replace b with B', function () { 39 | expect(replace('blake blake', 'b', 'B')).toBe('Blake Blake'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/replace/replace-filter.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('angular-filters') 5 | .filter('replace', [function () { 6 | function isString(input) { 7 | return typeof input === 'string' || input instanceof String; 8 | } 9 | 10 | return function(input, searchValue, newValue) { 11 | if (!isString(input) || !isString(searchValue) || !isString(newValue)) 12 | return input; 13 | 14 | return input.split(searchValue).join(newValue); 15 | }; 16 | }]); 17 | }()); 18 | -------------------------------------------------------------------------------- /src/reverse/reverse-filter-spec.js: -------------------------------------------------------------------------------- 1 | describe('Filter: reverse', function() { 2 | beforeEach(module('angular-filters')); 3 | 4 | // initialize a new instance of the filter before each test 5 | var reverse; 6 | beforeEach(inject(function ($filter) { 7 | reverse = $filter('reverse'); 8 | })); 9 | 10 | it('should return undefined when nothing is passed in', function () { 11 | expect(reverse()).toBe(undefined); 12 | }); 13 | 14 | it('should return null when null is passed in', function () { 15 | expect(reverse(null)).toBe(null); 16 | }); 17 | 18 | it('should return number when number is passed in', function () { 19 | expect(reverse(123)).toBe(123); 20 | }); 21 | 22 | it('should return object when object is passed in', function () { 23 | expect(reverse({ name: 'blake' })).toEqual({ name: 'blake' }); 24 | }); 25 | 26 | it('should return empty array', function () { 27 | expect(reverse([])).toEqual([]); 28 | }); 29 | 30 | it('should reverse array', function () { 31 | expect(reverse([1, 2])).toEqual([2, 1]); 32 | }); 33 | 34 | it('should return empty string', function () { 35 | expect(reverse('')).toBe(''); 36 | }); 37 | 38 | it('should reverse string', function () { 39 | expect(reverse('blake')).toBe('ekalb'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/reverse/reverse-filter.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('angular-filters') 5 | .filter('reverse', [function () { 6 | function reverseArray(items) { 7 | return items.slice().reverse(); 8 | } 9 | 10 | function reverseString(input) { 11 | var result = ''; 12 | for (var i = 0; i < input.length; i++) { 13 | result = input.charAt(i) + result; 14 | } 15 | 16 | return result; 17 | } 18 | 19 | return function(input) { 20 | if (Array.isArray(input)){ 21 | return reverseArray(input); 22 | } 23 | 24 | if (typeof input === 'string' || input instanceof String) { 25 | return reverseString(input); 26 | } 27 | 28 | return input; 29 | }; 30 | }]); 31 | }()); 32 | -------------------------------------------------------------------------------- /src/toSpacedWords/to-spaced-words-filter-spec.js: -------------------------------------------------------------------------------- 1 | describe('Filter: toSpacedWords', function() { 2 | beforeEach(module('angular-filters')); 3 | 4 | // initialize a new instance of the filter before each test 5 | var toSpacedWords; 6 | beforeEach(inject(function ($filter) { 7 | toSpacedWords = $filter('toSpacedWords'); 8 | })); 9 | 10 | it('should return undefined when nothing is passed in', function () { 11 | expect(toSpacedWords()).toBe(undefined); 12 | }); 13 | 14 | it('should return null when null is passed in', function () { 15 | expect(toSpacedWords(null)).toBe(null); 16 | }); 17 | 18 | it('should return number when number is passed in', function () { 19 | expect(toSpacedWords(123)).toBe(123); 20 | }); 21 | 22 | it('should return object when object is passed in', function () { 23 | expect(toSpacedWords({ name: 'blake' })).toEqual({ name: 'blake' }); 24 | }); 25 | 26 | it('should return empty array', function () { 27 | expect(toSpacedWords([])).toEqual([]); 28 | }); 29 | 30 | it('should toSpacedWords array', function () { 31 | expect(toSpacedWords([1, 2])).toEqual([1, 2]); 32 | }); 33 | 34 | it('should return empty string', function () { 35 | expect(toSpacedWords('')).toBe(''); 36 | }); 37 | 38 | it('should upper case single word', function () { 39 | expect(toSpacedWords('blake')).toBe('Blake'); 40 | }); 41 | 42 | it('should split upper case multiple words', function () { 43 | expect(toSpacedWords('blakeNiemyjskiIsAwesome')).toBe('Blake Niemyjski Is Awesome'); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/toSpacedWords/to-spaced-words-filter.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('angular-filters') 5 | .filter('toSpacedWords', [function () { 6 | function toSpacedWords(input) { 7 | if (!input.match(/\d+|__/g)) { 8 | input = input.replace(/([a-z])([A-Z])/g, '$1 $2'); 9 | input = input.length > 1 ? input.charAt(0).toUpperCase() + input.slice(1) : input; 10 | } 11 | 12 | return input; 13 | } 14 | 15 | return function(input) { 16 | if (!input) { 17 | return input; 18 | } 19 | 20 | if (typeof input === 'string' || input instanceof String) { 21 | return toSpacedWords(input); 22 | } 23 | 24 | return input; 25 | }; 26 | }]); 27 | }()); 28 | --------------------------------------------------------------------------------