├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── config └── karma.conf.js ├── gulpfile.js ├── package.json ├── release ├── angular-br-filters.js └── angular-br-filters.min.js └── src ├── filters.js └── filters.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Dependency directory 11 | node_modules 12 | bower_components 13 | 14 | # Users Environment Variables 15 | .lock-wscript 16 | 17 | coverage 18 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // -------------------------------------------------------------------- 3 | // JSHint Configuration, Strict Edition 4 | // -------------------------------------------------------------------- 5 | // 6 | // This is a options template for [JSHint][1], using [JSHint example][2] 7 | // and [Ory Band's example][3] as basis and setting config values to 8 | // be most strict: 9 | // 10 | // * set all enforcing options to true 11 | // * set all relaxing options to false 12 | // * set all environment options to false, except the browser value 13 | // * set all JSLint legacy options to false 14 | // 15 | // [1]: http://www.jshint.com/ 16 | // [2]: https://github.com/jshint/node-jshint/blob/master/example/config.json 17 | // [3]: https://github.com/oryband/dotfiles/blob/master/jshintrc 18 | // 19 | // @author http://michael.haschke.biz/ 20 | // @license http://unlicense.org/ 21 | 22 | // == Enforcing Options =============================================== 23 | // 24 | // These options tell JSHint to be more strict towards your code. Use 25 | // them if you want to allow only a safe subset of JavaScript, very 26 | // useful when your codebase is shared with a big number of developers 27 | // with different skill levels. 28 | 29 | "bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.). 30 | "camelcase" : true, // Force all variable names to use either camelCase style or UPPER_CASE with underscores. 31 | "curly" : true, // Require {} for every new block or scope. 32 | "eqeqeq" : true, // Require triple equals i.e. `===`. 33 | "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`. 34 | "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 35 | "latedef" : true, // Prohibit variable use before definition. 36 | "maxcomplexity" : 10, // Lets you control cyclomatic complexity throughout your code. 37 | "maxdepth" : 4, // Lets you control how nested do you want your blocks to be 38 | "maxlen" : 120, // Lets you set the maximum length of a line. 39 | "maxparams" : false, // Lets you set the max number of formal parameters allowed per function 40 | "maxstatements" : 20, // Lets you set the max number of statements allowed per function 41 | "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. 42 | "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. 43 | "noempty" : true, // Prohibit use of empty blocks. 44 | "nonew" : true, // Prohibit use of constructors for side-effects. 45 | "plusplus" : false, // Prohibit use of `++` & `--`. 46 | "quotmark" : "single", // Enforces the consistency of quotation marks used throughout your code. 47 | "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions. 48 | "undef" : true, // Require all non-global variables be declared before they are used. 49 | "unused" : true, // Prohibit neved used defined variables. 50 | "strict" : false, // Require `use strict` pragma in every file. 51 | "trailing" : true, // Prohibit trailing whitespaces. 52 | 53 | // == Relaxing Options ================================================ 54 | // 55 | // These options allow you to suppress certain types of warnings. Use 56 | // them only if you are absolutely positive that you know what you are 57 | // doing. 58 | 59 | "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). 60 | "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. 61 | "debug" : false, // Allow debugger statements e.g. browser breakpoints. 62 | "eqnull" : false, // Tolerate use of `== null`. 63 | "es5" : false, // Allow EcmaScript 5 syntax. 64 | "esnext" : false, // Allow ES.next specific features such as `const` and `let`. 65 | "evil" : false, // Tolerate use of `eval`. 66 | "expr" : true, // Tolerate `ExpressionStatement` as Programs. 67 | "funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside. 68 | "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). 69 | "iterator" : false, // Allow usage of __iterator__ property. 70 | "lastsemic" : false, // Tolerat missing semicolons when the it is omitted for the last statement in a one-line block. 71 | "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. 72 | "laxcomma" : false, // Suppress warnings about comma-first coding style. 73 | "loopfunc" : false, // Allow functions to be defined within loops. 74 | "multistr" : false, // Tolerate multi-line strings. 75 | "onecase" : false, // Tolerate switches with just one case. 76 | "proto" : false, // Tolerate __proto__ property. This property is deprecated. 77 | "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. 78 | "scripturl" : false, // Tolerate script-targeted URLs. 79 | "smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignmnent only. 80 | "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. 81 | "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. 82 | "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. 83 | "validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function. 84 | 85 | // == Environments ==================================================== 86 | // 87 | // These options pre-define global variables that are exposed by 88 | // popular JavaScript libraries and runtime environments—such as 89 | // browser or node.js. 90 | 91 | "browser" : true, // Standard browser globals e.g. `window`, `document`. 92 | "couch" : false, // Enable globals exposed by CouchDB. 93 | "devel" : false, // Allow development statements e.g. `console.log();`. 94 | "dojo" : false, // Enable globals exposed by Dojo Toolkit. 95 | "jquery" : false, // Enable globals exposed by jQuery JavaScript library. 96 | "mootools" : false, // Enable globals exposed by MooTools JavaScript framework. 97 | "node" : true, // Enable globals available when code is running inside of the NodeJS runtime environment. 98 | "nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape. 99 | "prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework. 100 | "rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment. 101 | "wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host. 102 | 103 | // == Undocumented Options ============================================ 104 | // 105 | // While I've found these options in [example1][2] and [example2][3] 106 | // they are not described in the [JSHint Options documentation][4]. 107 | // 108 | // [4]: http://www.jshint.com/options/ 109 | "globals": { 110 | "after": true, 111 | "angular": true, 112 | "before": true, 113 | "describe": true, 114 | "it": true 115 | }, 116 | "predef" : [ // Extra globals. 117 | ], 118 | "indent" : 4 // Specify indentation spacing 119 | } 120 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "5" 5 | - "4" 6 | - "0.11" 7 | 8 | before_install: 9 | - npm install -g bower 10 | 11 | before_script: 12 | - export CHROME_BIN=chromium-browser 13 | - export DISPLAY=:99.0 14 | - sh -e /etc/init.d/xvfb start 15 | - sleep 1 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # [0.7.0](https://github.com/the-darc/angular-br-filters/compare/0.6.0...0.7.0) (2016-11-09) 3 | 4 | 5 | #### Bug Fixes 6 | 7 | * package.json: remove postinstall and postupdate from package.json ([cf42d12](https://github.com/the-darc/angular-br-filters/commit/cf42d12)), closes [#13](https://github.com/the-darc/angular-br-filters/pull/13) 8 | 9 | #### Others 10 | 11 | * Update dependencies ([e358d4d](https://github.com/the-darc/angular-br-filters/commit/e358d4d)) 12 | * Travis-ci changed to build in node v6, v5 and v4 ([b4baba9](https://github.com/the-darc/angular-br-filters/commit/b4baba9)) 13 | * Tests changed to use angular 1.5.x ([e358d4d](https://github.com/the-darc/angular-br-filters/commit/e358d4d)) 14 | 15 | 16 | ## 0.6.0 (2016-03-06) 17 | 18 | 19 | #### Bug Fixes 20 | 21 | * **bower.json:** fix bower main script config ([884884ee](https://github.com/the-darc/angular-br-filters/commit/884884ee)) 22 | 23 | 24 | #### Features 25 | 26 | * **age:** add filter 'age' to calculate the age based on the birthdate ([0884d523](https://github.com/the-darc/angular-br-filters/commit/0884d523)) 27 | 28 | 29 | 30 | ## 0.5.0 (2015-09-01) 31 | 32 | 33 | #### Features 34 | 35 | * **browserify:** refactor to use common-js and allow browserify ([dcac96cd](https://github.com/the-darc/angular-br-filters/commit/dcac96cd)) 36 | 37 | 38 | 39 | ## 0.4.0 (2015-05-15) 40 | 41 | 42 | #### Features 43 | 44 | * **brCpfCnpj:** new filter brCpfCnpj (resolve #2) ([7d8e4f28](https://github.com/the-darc/angular-br-filters/commit/7d8e4f28)) 45 | 46 | 47 | 48 | ### 0.3.1 (2015-05-15) 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel 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 | angular-br-filters 2 | ================== 3 | [![npm version](https://img.shields.io/npm/v/angular-br-filters.svg)](https://www.npmjs.com/package/angular-br-filters) 4 | [![Bower version](https://img.shields.io/bower/v/angular-br-filters.svg)](https://bower.io/search/?q=angular-br-filters) 5 | [![Build Status](https://travis-ci.org/the-darc/angular-br-filters.svg)](https://travis-ci.org/the-darc/angular-br-filters) 6 | [![Coverage Status](https://coveralls.io/repos/the-darc/angular-br-filters/badge.svg)](https://coveralls.io/r/the-darc/angular-br-filters) 7 | 8 | [![NPM](https://nodei.co/npm/angular-br-filters.png?downloads=true&stars=true)](https://nodei.co/npm/angular-br-filters/) 9 | 10 | An Angular library of masks applicable to several Brazilian data like I.E., CNPJ, CPF and others 11 | 12 | Installation 13 | ------------ 14 | 15 | **With Bower:** 16 | 17 | ``` 18 | bower install --save angular-br-filters 19 | ``` 20 | 21 | **With NPM:** 22 | 23 | ``` 24 | npm install --save angular-br-filters 25 | ``` 26 | 27 | How to use 28 | ---------- 29 | 30 | 1. Import the ```angular-br-filters.min.js``` script in your page. 31 | 32 | 2. Include the module ```idf.br-filters``` in your angular app. 33 | 34 | 3. Use it as an angular filter: 35 | 36 | ```html 37 | Percentage: {{'0.1' | percentage}} 38 | ``` 39 | 40 | Demo Page 41 | --------- 42 | 43 | More exaples in http://the-darc.github.io/angular-br-filters/ 44 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-br-filters", 3 | "version": "0.7.0", 4 | "homepage": "https://github.com/the-darc/angular-br-filters", 5 | "description": "An Angular library of masks applicable to several Brazilian data.", 6 | "authors": [ 7 | "Igor Rafael ", 8 | "Daniel Campos " 9 | ], 10 | "main": "release/angular-br-filters.js", 11 | "keywords": [ 12 | "angular", 13 | "filters", 14 | "cpnj", 15 | "cpf", 16 | "cep", 17 | "phone-number" 18 | ], 19 | "license": "MIT", 20 | "ignore": [ 21 | "**/.*", 22 | "node_modules", 23 | "bower_components", 24 | "test", 25 | "tests" 26 | ], 27 | "dependencies": { 28 | "br-masks": "~0.5.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /config/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | var configuration = { 3 | basePath: __dirname + '/..', 4 | frameworks: ['browserify', 'jasmine'], 5 | files: [ 6 | 'node_modules/angular/angular.js', 7 | 'node_modules/angular-mocks/angular-mocks.js', 8 | 'src/**/*.test.js', 9 | ], 10 | port: 9876, 11 | reporters: ['progress', 'coverage'], 12 | preprocessors: { 13 | 'src/**/*.test.js': [ 'browserify' ], 14 | 'src/**/!(*spec|*test).js': ['coverage'] 15 | }, 16 | browserify: { 17 | debug: true, 18 | transform: [require('browserify-istanbul')({ 19 | ignore: '**/*.test.js' 20 | })] 21 | }, 22 | coverageReporter: { 23 | dir: 'coverage', 24 | reporters: [{ 25 | type: 'lcov', 26 | subdir: 'report-lcov' 27 | }, { 28 | type: 'html', 29 | subdir: 'report-html' 30 | }, { 31 | type: 'text', 32 | }, { 33 | type: 'text-summary', 34 | }] 35 | }, 36 | colors: true, 37 | autoWatch: false, 38 | singleRun: true, 39 | browsers: ['Chrome'], 40 | customLaunchers: { 41 | Chrome_travis_ci: { 42 | base: 'Chrome', 43 | flags: ['--no-sandbox'] 44 | } 45 | }, 46 | }; 47 | 48 | if(process.env.TRAVIS){ 49 | configuration.browsers = ['Chrome_travis_ci']; 50 | 51 | configuration.reporters.push('coveralls'); 52 | } 53 | 54 | config.set(configuration); 55 | }; 56 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | path = require('path'), 3 | browserify = require('browserify'), 4 | source = require('vinyl-source-stream'), 5 | buffer = require('vinyl-buffer'), 6 | jshintReporter = require('jshint-stylish'), 7 | pkg = require(path.join(__dirname, 'package.json')), 8 | plugins = require('gulp-load-plugins')({ 9 | config: path.join(__dirname, 'package.json') 10 | }); 11 | 12 | var config = { 13 | src: { 14 | files: 'src/**/*.js' 15 | } 16 | }; 17 | 18 | var header = [ 19 | '/**', 20 | ' * <%= pkg.name %>', 21 | ' * <%= pkg.description %>', 22 | ' * @version v<%= pkg.version %>', 23 | ' * @link <%= pkg.homepage %>', 24 | ' * @license <%= pkg.license %>', 25 | ' */', 26 | ].join('\n'); 27 | 28 | gulp.task('jshint', function(done) { 29 | gulp.src(config.src.files) 30 | .pipe(plugins.jshint('.jshintrc')) 31 | .pipe(plugins.jshint.reporter(jshintReporter)); 32 | done(); 33 | }); 34 | 35 | gulp.task('build', function() { 36 | return browserify({ 37 | entries: 'filters.js', 38 | detectGlobals: false, 39 | basedir: './src/', 40 | debug: false, 41 | bundleExternal: true, 42 | }) 43 | .bundle() 44 | .pipe(source('angular-br-filters.js')) 45 | .pipe(buffer()) 46 | .pipe(plugins.header(header, {pkg: pkg})) 47 | .pipe(gulp.dest('./release/')) 48 | .pipe(plugins.uglify()) 49 | .pipe(plugins.rename({ 50 | extname: '.min.js' 51 | })) 52 | .pipe(gulp.dest('./release/')); 53 | }); 54 | 55 | gulp.task('default', ['jshint', 'build'], function() { 56 | gulp.watch(config.src.files, ['jshint', 'build']); 57 | }); 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-br-filters", 3 | "version": "0.7.0", 4 | "description": "An Angular library of masks applicable to several Brazilian data.", 5 | "main": "src/filters.js", 6 | "scripts": { 7 | "test": "karma start config/karma.conf.js", 8 | "release": "conventional-changelog -p angular -i CHANGELOG.md -s" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/the-darc/angular-br-filters.git" 13 | }, 14 | "keywords": [ 15 | "angular", 16 | "filters", 17 | "cpnj", 18 | "cpf", 19 | "cep", 20 | "phone-number" 21 | ], 22 | "authors": [ 23 | "Igor Rafael ", 24 | "Daniel Campos " 25 | ], 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/the-darc/angular-br-filters/issues" 29 | }, 30 | "homepage": "https://github.com/the-darc/angular-br-filters", 31 | "dependencies": { 32 | "br-masks": "^0.5.0" 33 | }, 34 | "devDependencies": { 35 | "angular": "~1.5.8", 36 | "angular-mocks": "~1.5.8", 37 | "browserify": "^13.1.1", 38 | "browserify-istanbul": "^2.0.0", 39 | "conventional-changelog": "1.1.0", 40 | "gulp": "^3.8.8", 41 | "gulp-concat": "^2.6.0", 42 | "gulp-footer": "^1.0.5", 43 | "gulp-header": "^1.1.1", 44 | "gulp-jshint": "^2.0.2", 45 | "gulp-load-plugins": "^1.4.0", 46 | "gulp-rename": "^1.2.2", 47 | "gulp-uglify": "^2.0.0", 48 | "istanbul": "^0.4.5", 49 | "jasmine-core": "^2.3.2", 50 | "jshint": "^2.9.4", 51 | "jshint-stylish": "^2.2.1", 52 | "karma": "^1.3.0", 53 | "karma-browserify": "^5.1.0", 54 | "karma-chrome-launcher": "^2.0.0", 55 | "karma-coverage": "^1.1.1", 56 | "karma-coveralls": "^1.1.2", 57 | "karma-jasmine": "^1.0.2", 58 | "vinyl-buffer": "^1.0.0", 59 | "vinyl-source-stream": "^1.1.0", 60 | "watchify": "^3.7.0" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /release/angular-br-filters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-br-filters 3 | * An Angular library of masks applicable to several Brazilian data. 4 | * @version v0.7.0 5 | * @link https://github.com/the-darc/angular-br-filters 6 | * @license MIT 7 | */(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0 && token && token.escape) { 39 | token = tokens[pattern.charAt(i)]; 40 | count += token && token.escape ? 1 : 0; 41 | i--; 42 | } 43 | return count > 0 && count % 2 === 1; 44 | } 45 | 46 | function calcOptionalNumbersToUse(pattern, value) { 47 | var numbersInP = pattern.replace(/[^0]/g,'').length; 48 | var numbersInV = value.replace(/[^\d]/g,'').length; 49 | return numbersInV - numbersInP; 50 | } 51 | 52 | function concatChar(text, character, options, token) { 53 | if (token && typeof token.transform === 'function') { 54 | character = token.transform(character); 55 | } 56 | if (options.reverse) { 57 | return character + text; 58 | } 59 | return text + character; 60 | } 61 | 62 | function hasMoreTokens(pattern, pos, inc) { 63 | var pc = pattern.charAt(pos); 64 | var token = tokens[pc]; 65 | if (pc === '') { 66 | return false; 67 | } 68 | return token && !token.escape ? true : hasMoreTokens(pattern, pos + inc, inc); 69 | } 70 | 71 | function hasMoreRecursiveTokens(pattern, pos, inc) { 72 | var pc = pattern.charAt(pos); 73 | var token = tokens[pc]; 74 | if (pc === '') { 75 | return false; 76 | } 77 | return token && token.recursive ? true : hasMoreRecursiveTokens(pattern, pos + inc, inc); 78 | } 79 | 80 | function insertChar(text, char, position) { 81 | var t = text.split(''); 82 | t.splice(position, 0, char); 83 | return t.join(''); 84 | } 85 | 86 | function StringMask(pattern, opt) { 87 | this.options = opt || {}; 88 | this.options = { 89 | reverse: this.options.reverse || false, 90 | usedefaults: this.options.usedefaults || this.options.reverse 91 | }; 92 | this.pattern = pattern; 93 | } 94 | 95 | StringMask.prototype.process = function proccess(value) { 96 | if (!value) { 97 | return {result: '', valid: false}; 98 | } 99 | value = value + ''; 100 | var pattern2 = this.pattern; 101 | var valid = true; 102 | var formatted = ''; 103 | var valuePos = this.options.reverse ? value.length - 1 : 0; 104 | var patternPos = 0; 105 | var optionalNumbersToUse = calcOptionalNumbersToUse(pattern2, value); 106 | var escapeNext = false; 107 | var recursive = []; 108 | var inRecursiveMode = false; 109 | 110 | var steps = { 111 | start: this.options.reverse ? pattern2.length - 1 : 0, 112 | end: this.options.reverse ? -1 : pattern2.length, 113 | inc: this.options.reverse ? -1 : 1 114 | }; 115 | 116 | function continueCondition(options) { 117 | if (!inRecursiveMode && !recursive.length && hasMoreTokens(pattern2, patternPos, steps.inc)) { 118 | // continue in the normal iteration 119 | return true; 120 | } else if (!inRecursiveMode && recursive.length && 121 | hasMoreRecursiveTokens(pattern2, patternPos, steps.inc)) { 122 | // continue looking for the recursive tokens 123 | // Note: all chars in the patterns after the recursive portion will be handled as static string 124 | return true; 125 | } else if (!inRecursiveMode) { 126 | // start to handle the recursive portion of the pattern 127 | inRecursiveMode = recursive.length > 0; 128 | } 129 | 130 | if (inRecursiveMode) { 131 | var pc = recursive.shift(); 132 | recursive.push(pc); 133 | if (options.reverse && valuePos >= 0) { 134 | patternPos++; 135 | pattern2 = insertChar(pattern2, pc, patternPos); 136 | return true; 137 | } else if (!options.reverse && valuePos < value.length) { 138 | pattern2 = insertChar(pattern2, pc, patternPos); 139 | return true; 140 | } 141 | } 142 | return patternPos < pattern2.length && patternPos >= 0; 143 | } 144 | 145 | /** 146 | * Iterate over the pattern's chars parsing/matching the input value chars 147 | * until the end of the pattern. If the pattern ends with recursive chars 148 | * the iteration will continue until the end of the input value. 149 | * 150 | * Note: The iteration must stop if an invalid char is found. 151 | */ 152 | for (patternPos = steps.start; continueCondition(this.options); patternPos = patternPos + steps.inc) { 153 | // Value char 154 | var vc = value.charAt(valuePos); 155 | // Pattern char to match with the value char 156 | var pc = pattern2.charAt(patternPos); 157 | 158 | var token = tokens[pc]; 159 | if (recursive.length && token && !token.recursive) { 160 | // In the recursive portion of the pattern: tokens not recursive must be seen as static chars 161 | token = null; 162 | } 163 | 164 | // 1. Handle escape tokens in pattern 165 | // go to next iteration: if the pattern char is a escape char or was escaped 166 | if (!inRecursiveMode || vc) { 167 | if (this.options.reverse && isEscaped(pattern2, patternPos)) { 168 | // pattern char is escaped, just add it and move on 169 | formatted = concatChar(formatted, pc, this.options, token); 170 | // skip escape token 171 | patternPos = patternPos + steps.inc; 172 | continue; 173 | } else if (!this.options.reverse && escapeNext) { 174 | // pattern char is escaped, just add it and move on 175 | formatted = concatChar(formatted, pc, this.options, token); 176 | escapeNext = false; 177 | continue; 178 | } else if (!this.options.reverse && token && token.escape) { 179 | // mark to escape the next pattern char 180 | escapeNext = true; 181 | continue; 182 | } 183 | } 184 | 185 | // 2. Handle recursive tokens in pattern 186 | // go to next iteration: if the value str is finished or 187 | // if there is a normal token in the recursive portion of the pattern 188 | if (!inRecursiveMode && token && token.recursive) { 189 | // save it to repeat in the end of the pattern and handle the value char now 190 | recursive.push(pc); 191 | } else if (inRecursiveMode && !vc) { 192 | // in recursive mode but value is finished. Add the pattern char if it is not a recursive token 193 | formatted = concatChar(formatted, pc, this.options, token); 194 | continue; 195 | } else if (!inRecursiveMode && recursive.length > 0 && !vc) { 196 | // recursiveMode not started but already in the recursive portion of the pattern 197 | continue; 198 | } 199 | 200 | // 3. Handle the value 201 | // break iterations: if value is invalid for the given pattern 202 | if (!token) { 203 | // add char of the pattern 204 | formatted = concatChar(formatted, pc, this.options, token); 205 | if (!inRecursiveMode && recursive.length) { 206 | // save it to repeat in the end of the pattern 207 | recursive.push(pc); 208 | } 209 | } else if (token.optional) { 210 | // if token is optional, only add the value char if it matchs the token pattern 211 | // if not, move on to the next pattern char 212 | if (token.pattern.test(vc) && optionalNumbersToUse) { 213 | formatted = concatChar(formatted, vc, this.options, token); 214 | valuePos = valuePos + steps.inc; 215 | optionalNumbersToUse--; 216 | } else if (recursive.length > 0 && vc) { 217 | valid = false; 218 | break; 219 | } 220 | } else if (token.pattern.test(vc)) { 221 | // if token isn't optional the value char must match the token pattern 222 | formatted = concatChar(formatted, vc, this.options, token); 223 | valuePos = valuePos + steps.inc; 224 | } else if (!vc && token._default && this.options.usedefaults) { 225 | // if the token isn't optional and has a default value, use it if the value is finished 226 | formatted = concatChar(formatted, token._default, this.options, token); 227 | } else { 228 | // the string value don't match the given pattern 229 | valid = false; 230 | break; 231 | } 232 | } 233 | 234 | return {result: formatted, valid: valid}; 235 | }; 236 | 237 | StringMask.prototype.apply = function(value) { 238 | return this.process(value).result; 239 | }; 240 | 241 | StringMask.prototype.validate = function(value) { 242 | return this.process(value).valid; 243 | }; 244 | 245 | StringMask.process = function(value, pattern, options) { 246 | return new StringMask(pattern, options).process(value); 247 | }; 248 | 249 | StringMask.apply = function(value, pattern, options) { 250 | return new StringMask(pattern, options).apply(value); 251 | }; 252 | 253 | StringMask.validate = function(value, pattern, options) { 254 | return new StringMask(pattern, options).validate(value); 255 | }; 256 | 257 | return StringMask; 258 | })); 259 | 260 | },{}],2:[function(require,module,exports){ 261 | /** 262 | * br-masks 263 | * A library of masks applicable to several Brazilian data like I.E., CNPJ, CPF and others 264 | * @version v0.5.0 265 | * @link http://github.com/the-darc/br-masks 266 | * @license MIT 267 | */ 268 | (function (root, factory) { 269 | /* istanbul ignore next */ 270 | if (typeof define === 'function' && define.amd) { 271 | // AMD. Register as an anonymous module. 272 | define(['string-mask'], factory); 273 | } else if (typeof exports === 'object') { 274 | // Node. Does not work with strict CommonJS, but 275 | // only CommonJS-like environments that support module.exports, 276 | // like Node. 277 | module.exports = factory(require('string-mask')); 278 | } else { 279 | // Browser globals (root is window) 280 | root.BrM = factory(root.StringMask); 281 | } 282 | }(this, function (StringMask) { 283 | /* istanbul ignore if */ 284 | if (!StringMask) { 285 | throw new Error('StringMask not found'); 286 | } 287 | /*exported CEP */ 288 | var CEP = function(value) { 289 | var cepMask = new StringMask('00000-000'); 290 | if(!value) { 291 | return value; 292 | } 293 | var processed = cepMask.process(value); 294 | return processed.result; 295 | }; 296 | 297 | /*exported CNPJBASE */ 298 | var CNPJBASE = function(value) { 299 | if(!value) { 300 | return value; 301 | } 302 | var cnpjBasePattern = new StringMask('00.000.000'); 303 | var formatedValue = cnpjBasePattern.apply(value); 304 | return formatedValue; 305 | }; 306 | 307 | /*exported CNPJ */ 308 | var CNPJ = function(value) { 309 | if(!value) { 310 | return value; 311 | } 312 | var cnpjPattern = new StringMask('00.000.000\/0000-00'); 313 | var formatedValue = cnpjPattern.apply(value); 314 | return formatedValue; 315 | }; 316 | 317 | /*exported CPFCNPJ */ 318 | /*globals CPF, CNPJ*/ 319 | var CPFCNPJ = function(value) { 320 | if (!value || !value.length) { 321 | return value; 322 | } else if (value.length <= 11) { 323 | return CPF(value); 324 | } else { 325 | return CNPJ(value); 326 | } 327 | }; 328 | 329 | /*exported CPF */ 330 | var CPF = function(value) { 331 | var cpfPattern = new StringMask('000.000.000-00'); 332 | if(!value) { 333 | return value; 334 | } 335 | var formatedValue = cpfPattern.apply(value); 336 | return formatedValue; 337 | }; 338 | 339 | /*exported FINANCE */ 340 | var FINANCE = function(value, precision, decimalSep, groupSep) { 341 | precision = (!precision && precision !== 0) || precision < 0? 2 : precision; 342 | decimalSep = decimalSep || '.'; 343 | groupSep = groupSep || ''; 344 | 345 | var decimalsPattern = precision > 0 ? decimalSep + new Array(precision + 1).join('0') : ''; 346 | var maskPattern = '#'+groupSep+'##0'+decimalsPattern; 347 | 348 | value = parseFloat(value); 349 | if (!value) { 350 | value = 0; 351 | } 352 | 353 | var negative = false; 354 | if (value < 0) { 355 | value = value * -1; 356 | negative = true; 357 | } 358 | var financeMask = new StringMask(maskPattern, {reverse: true}); 359 | var masked = financeMask.apply(value.toFixed(precision).replace(/[^\d]+/g,'')); 360 | return negative ? '('+masked+')' : masked; 361 | }; 362 | 363 | /*exported IE */ 364 | var IE = function(value, uf) { 365 | if (!value || typeof value !== 'string') { 366 | return value; 367 | } 368 | 369 | var ieMasks = { 370 | 'AC': [{mask: new StringMask('00.000.000/000-00')}], 371 | 'AL': [{mask: new StringMask('000000000')}], 372 | 'AM': [{mask: new StringMask('00.000.000-0')}], 373 | 'AP': [{mask: new StringMask('000000000')}], 374 | 'BA': [{chars: 8, mask: new StringMask('000000-00')}, 375 | {mask: new StringMask('0000000-00')}], 376 | 'CE': [{mask: new StringMask('00000000-0')}], 377 | 'DF': [{mask: new StringMask('00000000000-00')}], 378 | 'ES': [{mask: new StringMask('00000000-0')}], 379 | 'GO': [{mask: new StringMask('00.000.000-0')}], 380 | 'MA': [{mask: new StringMask('000000000')}], 381 | 'MG': [{mask: new StringMask('000.000.000/0000')}], 382 | 'MS': [{mask: new StringMask('000000000')}], 383 | 'MT': [{mask: new StringMask('0000000000-0')}], 384 | 'PA': [{mask: new StringMask('00-000000-0')}], 385 | 'PB': [{mask: new StringMask('00000000-0')}], 386 | 'PE': [{chars: 9, mask: new StringMask('0000000-00')}, 387 | {mask: new StringMask('00.0.000.0000000-0')}], 388 | 'PI': [{mask: new StringMask('000000000')}], 389 | 'PR': [{mask: new StringMask('000.00000-00')}], 390 | 'RJ': [{mask: new StringMask('00.000.00-0')}], 391 | 'RN': [{chars: 9, mask: new StringMask('00.000.000-0')}, 392 | {mask: new StringMask('00.0.000.000-0')}], 393 | 'RO': [{mask: new StringMask('0000000000000-0')}], 394 | 'RR': [{mask: new StringMask('00000000-0')}], 395 | 'RS': [{mask: new StringMask('000/0000000')}], 396 | 'SC': [{mask: new StringMask('000.000.000')}], 397 | 'SE': [{mask: new StringMask('00000000-0')}], 398 | 'SP': [{mask: new StringMask('000.000.000.000')}, 399 | {mask: new StringMask('-00000000.0/000')}], 400 | 'TO': [{mask: new StringMask('00000000000')}] 401 | }; 402 | 403 | function clearValue (value) { 404 | return value.replace(/[^0-9]/g, ''); 405 | } 406 | 407 | function getMask(uf, value) { 408 | if(!uf || !ieMasks[uf]) { 409 | return undefined; 410 | } 411 | var _uf = uf.toUpperCase(); 412 | if (_uf === 'SP' && /^P/i.test(value)) { 413 | return ieMasks.SP[1].mask; 414 | } 415 | var masks = ieMasks[uf]; 416 | var i = 0; 417 | while(masks[i].chars && masks[i].chars < clearValue(value).length && i < masks.length - 1) { 418 | i++; 419 | } 420 | return masks[i].mask; 421 | } 422 | 423 | var mask = getMask(uf, value); 424 | if(!mask) { 425 | return value; 426 | } 427 | var processed = mask.process(clearValue(value)); 428 | if (uf && uf.toUpperCase() === 'SP' && /^p/i.test(value)) { 429 | return 'P'+processed.result; 430 | } 431 | return processed.result; 432 | }; 433 | 434 | /*exported NFEACCESSKEY */ 435 | var NFEACCESSKEY = function(value) { 436 | if(!value) { 437 | return value; 438 | } 439 | 440 | var maskPattern = '0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000'; 441 | var nfeMask = new StringMask(maskPattern); 442 | var formatedValue = nfeMask.apply(value); 443 | return formatedValue; 444 | }; 445 | 446 | /*exported PHONE */ 447 | var PHONE = function(value) { 448 | var phoneMask8D = new StringMask('(00) 0000-0000'), 449 | phoneMask9D = new StringMask('(00) 00000-0000'), 450 | phoneMask0800 = new StringMask('0000-000-0000'); 451 | 452 | if(!value) { 453 | return value; 454 | } 455 | 456 | var formatedValue; 457 | value = value + ''; 458 | if (value.indexOf('0800') === 0) { 459 | formatedValue = phoneMask0800.apply(value); 460 | }else if(value.length < 11){ 461 | formatedValue = phoneMask8D.apply(value); 462 | }else{ 463 | formatedValue = phoneMask9D.apply(value); 464 | } 465 | 466 | return formatedValue; 467 | }; 468 | 469 | return { 470 | ie: IE, 471 | cpf: CPF, 472 | cnpj: CNPJ, 473 | cnpjBase: CNPJBASE, 474 | phone: PHONE, 475 | cep: CEP, 476 | finance: FINANCE, 477 | nfeAccessKey: NFEACCESSKEY, 478 | cpfCnpj: CPFCNPJ 479 | }; 480 | })); 481 | },{"string-mask":1}],3:[function(require,module,exports){ 482 | var BrM = require('br-masks'); 483 | 484 | var m = angular.module('idf.br-filters', []); 485 | 486 | module.exports = m.name; 487 | 488 | m.filter('percentage', ['$filter', function($filter) { 489 | return function(input, decimals) { 490 | if (angular.isUndefined(input) || input === null) { 491 | return input; 492 | } 493 | 494 | return $filter('number')(input * 100, decimals) + '%'; 495 | }; 496 | }]) 497 | .filter('brCep', [function() { 498 | return function(input) { 499 | return BrM.cep(input); 500 | }; 501 | }]) 502 | .filter('brPhoneNumber', [function() { 503 | return function(input) { 504 | return BrM.phone(input); 505 | }; 506 | }]) 507 | .filter('brCpf', [function() { 508 | return function(input) { 509 | return BrM.cpf(input); 510 | }; 511 | }]) 512 | .filter('brCnpj', [function() { 513 | return function(input) { 514 | return BrM.cnpj(input); 515 | }; 516 | }]) 517 | .filter('brCpfCnpj', [function() { 518 | return function(input) { 519 | return BrM.cpfCnpj(input); 520 | }; 521 | }]) 522 | .filter('brIe', [function() { 523 | return function(input, uf) { 524 | return BrM.ie(input,uf); 525 | }; 526 | }]) 527 | .filter('finance', ['$locale', function($locale) { 528 | return function(input, currency, decimals) { 529 | if (angular.isUndefined(input) || input === null) { 530 | return input; 531 | } 532 | 533 | var decimalDelimiter = $locale.NUMBER_FORMATS.DECIMAL_SEP, 534 | thousandsDelimiter = $locale.NUMBER_FORMATS.GROUP_SEP, 535 | currencySym = ''; 536 | 537 | if (currency === true) { 538 | currencySym = $locale.NUMBER_FORMATS.CURRENCY_SYM + ' '; 539 | } else if (currency) { 540 | currencySym = currency; 541 | } 542 | 543 | return currencySym + BrM.finance(input, decimals, decimalDelimiter, thousandsDelimiter); 544 | }; 545 | }]) 546 | .filter('nfeAccessKey', [function() { 547 | return function(input) { 548 | return BrM.nfeAccessKey(input); 549 | }; 550 | }]) 551 | .filter('age', function() { 552 | /** 553 | * @param value birthdate can be a date object or a time in milliseconds 554 | * return the age based on the birthdate or undefined if value is invalid. 555 | */ 556 | return function calculateAge(value) { 557 | if (!value) { 558 | return undefined; 559 | } 560 | var isDateInstance = (value instanceof Date); 561 | var isValidType = isDateInstance || !isNaN(parseInt(value)); 562 | if (!isValidType) { 563 | return undefined; 564 | } 565 | var birthdate = isDateInstance ? value : new Date(value); 566 | if (birthdate > new Date()) { 567 | return undefined; 568 | } 569 | var ageDifMs = Date.now() - birthdate.getTime(); 570 | var ageDate = new Date(ageDifMs); // miliseconds from epoch 571 | return Math.abs(ageDate.getUTCFullYear() - 1970); 572 | }; 573 | }); 574 | 575 | },{"br-masks":2}]},{},[3]); 576 | -------------------------------------------------------------------------------- /release/angular-br-filters.min.js: -------------------------------------------------------------------------------- 1 | !function e(n,r,t){function i(a,o){if(!r[a]){if(!n[a]){var u="function"==typeof require&&require;if(!o&&u)return u(a,!0);if(s)return s(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var c=r[a]={exports:{}};n[a][0].call(c.exports,function(e){var r=n[a][1][e];return i(r?r:e)},c,c.exports,e,n,r,t)}return r[a].exports}for(var s="function"==typeof require&&require,a=0;a=0&&i&&i.escape;)i=o[e.charAt(t)],r+=i&&i.escape?1:0,t--;return r>0&&r%2===1}function n(e,n){var r=e.replace(/[^0]/g,"").length,t=n.replace(/[^\d]/g,"").length;return t-r}function r(e,n,r,t){return t&&"function"==typeof t.transform&&(n=t.transform(n)),r.reverse?n+e:e+n}function t(e,n,r){var i=e.charAt(n),s=o[i];return""!==i&&(!(!s||s.escape)||t(e,n+r,r))}function i(e,n,r){var t=e.charAt(n),s=o[t];return""!==t&&(!(!s||!s.recursive)||i(e,n+r,r))}function s(e,n,r){var t=e.split("");return t.splice(r,0,n),t.join("")}function a(e,n){this.options=n||{},this.options={reverse:this.options.reverse||!1,usedefaults:this.options.usedefaults||this.options.reverse},this.pattern=e}var o={0:{pattern:/\d/,_default:"0"},9:{pattern:/\d/,optional:!0},"#":{pattern:/\d/,optional:!0,recursive:!0},A:{pattern:/[a-zA-Z0-9]/},S:{pattern:/[a-zA-Z]/},U:{pattern:/[a-zA-Z]/,transform:function(e){return e.toLocaleUpperCase()}},L:{pattern:/[a-zA-Z]/,transform:function(e){return e.toLocaleLowerCase()}},$:{escape:!0}};return a.prototype.process=function(a){function u(e){if(!k&&!m.length&&t(f,h,d.inc))return!0;if(!k&&m.length&&i(f,h,d.inc))return!0;if(k||(k=m.length>0),k){var n=m.shift();if(m.push(n),e.reverse&&l>=0)return h++,f=s(f,n,h),!0;if(!e.reverse&&l=0}if(!a)return{result:"",valid:!1};a+="";var f=this.pattern,c=!0,p="",l=this.options.reverse?a.length-1:0,h=0,v=n(f,a),w=!1,m=[],k=!1,d={start:this.options.reverse?f.length-1:0,end:this.options.reverse?-1:f.length,inc:this.options.reverse?-1:1};for(h=d.start;u(this.options);h+=d.inc){var g=a.charAt(l),y=f.charAt(h),A=o[y];if(m.length&&A&&!A.recursive&&(A=null),!k||g){if(this.options.reverse&&e(f,h)){p=r(p,y,this.options,A),h+=d.inc;continue}if(!this.options.reverse&&w){p=r(p,y,this.options,A),w=!1;continue}if(!this.options.reverse&&A&&A.escape){w=!0;continue}}if(!k&&A&&A.recursive)m.push(y);else{if(k&&!g){p=r(p,y,this.options,A);continue}if(!k&&m.length>0&&!g)continue}if(A)if(A.optional){if(A.pattern.test(g)&&v)p=r(p,g,this.options,A),l+=d.inc,v--;else if(m.length>0&&g){c=!1;break}}else if(A.pattern.test(g))p=r(p,g,this.options,A),l+=d.inc;else{if(g||!A._default||!this.options.usedefaults){c=!1;break}p=r(p,A._default,this.options,A)}else p=r(p,y,this.options,A),!k&&m.length&&m.push(y)}return{result:p,valid:c}},a.prototype.apply=function(e){return this.process(e).result},a.prototype.validate=function(e){return this.process(e).valid},a.process=function(e,n,r){return new a(n,r).process(e)},a.apply=function(e,n,r){return new a(n,r).apply(e)},a.validate=function(e,n,r){return new a(n,r).validate(e)},a})},{}],2:[function(e,n,r){!function(t,i){"function"==typeof define&&define.amd?define(["string-mask"],i):"object"==typeof r?n.exports=i(e("string-mask")):t.BrM=i(t.StringMask)}(this,function(e){if(!e)throw new Error("StringMask not found");var n=function(n){var r=new e("00000-000");if(!n)return n;var t=r.process(n);return t.result},r=function(n){if(!n)return n;var r=new e("00.000.000"),t=r.apply(n);return t},t=function(n){if(!n)return n;var r=new e("00.000.000/0000-00"),t=r.apply(n);return t},i=function(e){return e&&e.length?e.length<=11?s(e):t(e):e},s=function(n){var r=new e("000.000.000-00");if(!n)return n;var t=r.apply(n);return t},a=function(n,r,t,i){r=!r&&0!==r||r<0?2:r,t=t||".",i=i||"";var s=r>0?t+new Array(r+1).join("0"):"",a="#"+i+"##0"+s;n=parseFloat(n),n||(n=0);var o=!1;n<0&&(n*=-1,o=!0);var u=new e(a,{reverse:!0}),f=u.apply(n.toFixed(r).replace(/[^\d]+/g,""));return o?"("+f+")":f},o=function(n,r){function t(e){return e.replace(/[^0-9]/g,"")}function i(e,n){if(e&&s[e]){var r=e.toUpperCase();if("SP"===r&&/^P/i.test(n))return s.SP[1].mask;for(var i=s[e],a=0;i[a].chars&&i[a].charsnew Date)){var i=Date.now()-t.getTime(),s=new Date(i);return Math.abs(s.getUTCFullYear()-1970)}}}}})},{"br-masks":2}]},{},[3]); -------------------------------------------------------------------------------- /src/filters.js: -------------------------------------------------------------------------------- 1 | var BrM = require('br-masks'); 2 | 3 | var m = angular.module('idf.br-filters', []); 4 | 5 | module.exports = m.name; 6 | 7 | m.filter('percentage', ['$filter', function($filter) { 8 | return function(input, decimals) { 9 | if (angular.isUndefined(input) || input === null) { 10 | return input; 11 | } 12 | 13 | return $filter('number')(input * 100, decimals) + '%'; 14 | }; 15 | }]) 16 | .filter('brCep', [function() { 17 | return function(input) { 18 | return BrM.cep(input); 19 | }; 20 | }]) 21 | .filter('brPhoneNumber', [function() { 22 | return function(input) { 23 | return BrM.phone(input); 24 | }; 25 | }]) 26 | .filter('brCpf', [function() { 27 | return function(input) { 28 | return BrM.cpf(input); 29 | }; 30 | }]) 31 | .filter('brCnpj', [function() { 32 | return function(input) { 33 | return BrM.cnpj(input); 34 | }; 35 | }]) 36 | .filter('brCpfCnpj', [function() { 37 | return function(input) { 38 | return BrM.cpfCnpj(input); 39 | }; 40 | }]) 41 | .filter('brIe', [function() { 42 | return function(input, uf) { 43 | return BrM.ie(input,uf); 44 | }; 45 | }]) 46 | .filter('finance', ['$locale', function($locale) { 47 | return function(input, currency, decimals) { 48 | if (angular.isUndefined(input) || input === null) { 49 | return input; 50 | } 51 | 52 | var decimalDelimiter = $locale.NUMBER_FORMATS.DECIMAL_SEP, 53 | thousandsDelimiter = $locale.NUMBER_FORMATS.GROUP_SEP, 54 | currencySym = ''; 55 | 56 | if (currency === true) { 57 | currencySym = $locale.NUMBER_FORMATS.CURRENCY_SYM + ' '; 58 | } else if (currency) { 59 | currencySym = currency; 60 | } 61 | 62 | return currencySym + BrM.finance(input, decimals, decimalDelimiter, thousandsDelimiter); 63 | }; 64 | }]) 65 | .filter('nfeAccessKey', [function() { 66 | return function(input) { 67 | return BrM.nfeAccessKey(input); 68 | }; 69 | }]) 70 | .filter('age', function() { 71 | /** 72 | * @param value birthdate can be a date object or a time in milliseconds 73 | * return the age based on the birthdate or undefined if value is invalid. 74 | */ 75 | return function calculateAge(value) { 76 | if (!value) { 77 | return undefined; 78 | } 79 | var isDateInstance = (value instanceof Date); 80 | var isValidType = isDateInstance || !isNaN(parseInt(value)); 81 | if (!isValidType) { 82 | return undefined; 83 | } 84 | var birthdate = isDateInstance ? value : new Date(value); 85 | if (birthdate > new Date()) { 86 | return undefined; 87 | } 88 | var ageDifMs = Date.now() - birthdate.getTime(); 89 | var ageDate = new Date(ageDifMs); // miliseconds from epoch 90 | return Math.abs(ageDate.getUTCFullYear() - 1970); 91 | }; 92 | }); 93 | -------------------------------------------------------------------------------- /src/filters.test.js: -------------------------------------------------------------------------------- 1 | require('./filters.js'); 2 | 3 | /* global expect,inject,beforeEach,it,describe */ 4 | describe('br-filters', function() { 5 | beforeEach(angular.mock.module('idf.br-filters')); 6 | 7 | function testFilter(name) { 8 | var filter; 9 | 10 | inject(['$filter', function($filter) { 11 | filter = $filter(name); 12 | }]); 13 | 14 | return filter; 15 | } 16 | 17 | describe('percentage', function() { 18 | it('should not format if null or undefined', function(){ 19 | expect(testFilter('percentage')(null)).toBe(null); 20 | expect(testFilter('percentage')(undefined)).toBe(undefined); 21 | }); 22 | 23 | it('should format numbers', function() { 24 | expect(testFilter('percentage')('')).toBe('0%'); 25 | expect(testFilter('percentage')(0)).toBe('0%'); 26 | expect(testFilter('percentage')(0.3)).toBe('30%'); 27 | expect(testFilter('percentage')(0.189)).toBe('18.9%'); 28 | expect(testFilter('percentage')('0.045')).toBe('4.5%'); 29 | expect(testFilter('percentage')(2.345e-4)).toBe('0.023%'); 30 | }); 31 | 32 | it('should format numbers with correct number of decimals', function() { 33 | expect(testFilter('percentage')(0.3, 2)).toBe('30.00%'); 34 | expect(testFilter('percentage')(0.189, 3)).toBe('18.900%'); 35 | expect(testFilter('percentage')('0.045', 0)).toBe('5%'); 36 | expect(testFilter('percentage')(2.345e-4, 2)).toBe('0.02%'); 37 | }); 38 | }); 39 | 40 | describe('brCep', function () { 41 | it('should not format if null or undefined', function() { 42 | expect(testFilter('brCep')(null)).toBe(null); 43 | expect(testFilter('brCep')(undefined)).toBe(undefined); 44 | }); 45 | 46 | it('should format a string or a number', function() { 47 | expect(testFilter('brCep')('30480530')).toBe('30480-530'); 48 | expect(testFilter('brCep')(30480530)).toBe('30480-530'); 49 | }); 50 | }); 51 | 52 | describe('brPhoneNumber', function () { 53 | it('should not format if null or undefined', function() { 54 | expect(testFilter('brPhoneNumber')(null)).toBe(null); 55 | expect(testFilter('brPhoneNumber')(undefined)).toBe(undefined); 56 | }); 57 | 58 | it('should format a string or a number', function() { 59 | expect(testFilter('brPhoneNumber')('3133340167')).toBe('(31) 3334-0167'); 60 | expect(testFilter('brPhoneNumber')(3133340167)).toBe('(31) 3334-0167'); 61 | expect(testFilter('brPhoneNumber')('38212201255')).toBe('(38) 21220-1255'); 62 | expect(testFilter('brPhoneNumber')(38212201255)).toBe('(38) 21220-1255'); 63 | }); 64 | }); 65 | 66 | describe('brCpf', function () { 67 | it('should not format if null or undefined', function() { 68 | expect(testFilter('brCpf')(null)).toBe(null); 69 | expect(testFilter('brCpf')(undefined)).toBe(undefined); 70 | }); 71 | 72 | it('should format a string or a number', function() { 73 | expect(testFilter('brCpf')('97070868669')).toBe('970.708.686-69'); 74 | expect(testFilter('brCpf')(97070868669)).toBe('970.708.686-69'); 75 | expect(testFilter('brCpf')('1435151')).toBe('143.515.1'); 76 | expect(testFilter('brCpf')(1435151)).toBe('143.515.1'); 77 | }); 78 | }); 79 | 80 | describe('brCnpj', function () { 81 | it('should not format if null or undefined', function() { 82 | expect(testFilter('brCnpj')(null)).toBe(null); 83 | expect(testFilter('brCnpj')(undefined)).toBe(undefined); 84 | }); 85 | 86 | it('should format a string or a number', function() { 87 | expect(testFilter('brCnpj')('10157471000161')).toBe('10.157.471/0001-61'); 88 | expect(testFilter('brCnpj')(10157471000161)).toBe('10.157.471/0001-61'); 89 | }); 90 | }); 91 | 92 | describe('brCpfCnpj', function () { 93 | it('should not format if null or undefined', function() { 94 | expect(testFilter('brCpfCnpj')(null)).toBe(null); 95 | expect(testFilter('brCpfCnpj')(undefined)).toBe(undefined); 96 | }); 97 | 98 | it('should format a string or a number', function() { 99 | expect(testFilter('brCpfCnpj')('97070868669')).toBe('970.708.686-69'); 100 | expect(testFilter('brCpfCnpj')('1435151')).toBe('143.515.1'); 101 | expect(testFilter('brCpfCnpj')('10157471000161')).toBe('10.157.471/0001-61'); 102 | }); 103 | }); 104 | 105 | describe('brIe', function () { 106 | it('should not format if null or undefined', function() { 107 | expect(testFilter('brIe')(null)).toBe(null); 108 | expect(testFilter('brIe')(undefined)).toBe(undefined); 109 | }); 110 | 111 | it('should not format a number', function() { 112 | expect(testFilter('brIe')(32141840, 'PE')).toBe(32141840); 113 | }); 114 | 115 | it('should format a string', function() { 116 | expect(testFilter('brIe')('032141840', 'PE')).toBe('0321418-40'); 117 | }); 118 | }); 119 | 120 | describe('finance', function () { 121 | it('should not format if null or undefined', function() { 122 | expect(testFilter('finance')(null)).toBe(null); 123 | expect(testFilter('finance')(undefined)).toBe(undefined); 124 | }); 125 | 126 | it('should format a string or a number', function() { 127 | expect(testFilter('finance')('123.1237123', '$ ', 3)).toBe('$ 123.124'); 128 | expect(testFilter('finance')(123.1237123, 'R$ ', 3)).toBe('R$ 123.124'); 129 | expect(testFilter('finance')(123.1237123, true)).toMatch(/.+ 123.12/); 130 | expect(testFilter('finance')(0)).toMatch(/0.00/); 131 | }); 132 | }); 133 | 134 | describe('nfeAccessKey', function () { 135 | it('should not format if null or undefined', function() { 136 | expect(testFilter('nfeAccessKey')(null)).toBe(null); 137 | expect(testFilter('nfeAccessKey')(undefined)).toBe(undefined); 138 | }); 139 | 140 | it('should format a string', function() { 141 | expect(testFilter('nfeAccessKey')('35140111724258000157550010006882191630386000')) 142 | .toBe('3514 0111 7242 5800 0157 5500 1000 6882 1916 3038 6000'); 143 | }); 144 | }); 145 | describe('age', function() { 146 | it('should be undefined if null, undefined or empty', function() { 147 | expect(testFilter('age')(null)).toBe(undefined); 148 | expect(testFilter('age')(undefined)).toBe(undefined); 149 | expect(testFilter('age')('')).toBe(undefined); 150 | }); 151 | 152 | it('should be undefined if not date or time in milliseconds', function() { 153 | expect(testFilter('age')('not a date')).toBe(undefined); 154 | expect(testFilter('age')(true)).toBe(undefined); 155 | }); 156 | 157 | it('should be undefined for future birthdate', function() { 158 | var futureYear = new Date(); 159 | futureYear.setFullYear(futureYear.getFullYear() + 1); 160 | expect(testFilter('age')(futureYear)).toBe(undefined); 161 | var futureMonth = new Date(); 162 | futureMonth.setMonth(futureMonth.getMonth() + 1); 163 | expect(testFilter('age')(futureMonth)).toBe(undefined); 164 | var futureDay = new Date(); 165 | futureDay.setDate(futureDay.getDate() + 1); 166 | expect(testFilter('age')(futureDay)).toBe(undefined); 167 | var futureMinute = new Date(); 168 | futureMinute.setMinutes(futureMinute.getMinutes() + 1); 169 | expect(testFilter('age')(futureMinute)).toBe(undefined); 170 | }); 171 | 172 | it('should format date as 27', function() { 173 | var age = 27; 174 | var yearInMs = 366 * 24 * 60 * 60 * 1000; 175 | var born = new Date(new Date().getTime() - age*yearInMs); 176 | expect(testFilter('age')(born)).toBe(age); 177 | expect(testFilter('age')(born.getTime())).toBe(age); 178 | }); 179 | }); 180 | }); 181 | --------------------------------------------------------------------------------