├── .editorconfig ├── .gitignore ├── .jshintrc ├── .node-version ├── .travis.yml ├── Gruntfile.js ├── README.md ├── bower.json ├── dist ├── angular-rut.js └── angular-rut.min.js ├── karma-sauce.conf.js ├── karma.conf.js ├── package.json ├── src └── rut.js └── test ├── .jshintrc └── rut_spec.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | 5 | # Change these settings to your own preference 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.js] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | lib 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "esnext": true, 4 | "bitwise": false, 5 | "camelcase": true, 6 | "curly": false, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": false, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "globals": { 21 | "angular": false 22 | } 23 | } -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 0.10 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - 0.10 5 | before_script: 6 | - npm install -g bower 7 | - bower install 8 | addons: 9 | sauce_connect: true 10 | env: 11 | global: 12 | - secure: T6QHZz6rXedDMXgp7TdGRRL+fL4JtTgofRw1WJEtusKU0x4P2+y8dPPXSmxx63BaM0/l1Gl8SmffCXy+CaxKqcVqUq3qQFv5kZZVbtpW1cOce/Fw2vSfO4WLf6yra7FyIIxwe1nc+JvEEBHVTKXuGAs0kHb2dTKdrS3tOc/tP4Q= 13 | - secure: WAANH0DdkRMjx/ZDw+xX4XAV12q869roNCaw7wcvVtTrDm95eya39QcgIXEzmIPQyecoyFto8yvjauw8uKQGQpnQ4b90tjx1oI06s8SW+IR6RQWRdQep1FM81GFaFd93sGEmhqoPyNUtRCUGRoFWDT93uE1P3zMZppIGeBmE4Q4= 14 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | pkg: grunt.file.readJSON('bower.json'), 8 | meta: { 9 | banner: '/**\n' + 10 | ' * <%= pkg.description %>\n' + 11 | ' * @version v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + 12 | ' * @link <%= pkg.homepage %>\n' + 13 | ' * @author <%= pkg.authors.join(", ") %>\n' + 14 | ' * @license MIT License, http://www.opensource.org/licenses/MIT\n' + 15 | ' */\n' 16 | }, 17 | bower: { 18 | install: {} 19 | }, 20 | concat: { 21 | options: { 22 | banner: '<%= meta.banner %>\n(function(angular, undefined) {\n\'use strict\';\n', 23 | footer: '})(angular);', 24 | process: function(src, filepath) { 25 | return src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1'); 26 | } 27 | }, 28 | dist: { 29 | files: { 30 | 'dist/<%= pkg.name %>.js': 'src/rut.js' 31 | } 32 | } 33 | }, 34 | uglify: { 35 | options: { 36 | banner: '<%= meta.banner %>' 37 | }, 38 | dist: { 39 | files: { 40 | 'dist/<%= pkg.name %>.min.js': 'dist/<%= pkg.name %>.js' 41 | } 42 | } 43 | }, 44 | jshint: { 45 | files: ['Gruntfile.js', 'src/*.js'], 46 | options: { 47 | curly: false, 48 | browser: true, 49 | eqeqeq: true, 50 | immed: true, 51 | latedef: true, 52 | newcap: true, 53 | noarg: true, 54 | sub: true, 55 | undef: true, 56 | boss: true, 57 | eqnull: true, 58 | expr: true, 59 | node: true, 60 | globals: { 61 | exports: true, 62 | angular: false, 63 | $: false 64 | } 65 | } 66 | }, 67 | karma: { 68 | options: { 69 | configFile: 'karma.conf.js' 70 | }, 71 | build: { 72 | singleRun: true, 73 | autoWatch: false 74 | }, 75 | dev: { 76 | autoWatch: true 77 | } 78 | }, 79 | gitcommit: { 80 | bump: { 81 | options: { 82 | message: "<%= pkg.version %>", 83 | noStatus: true 84 | }, 85 | files: { 86 | src: [ 87 | 'bower.json', 88 | 'dist/**/*' 89 | ] 90 | } 91 | } 92 | }, 93 | gittag: { 94 | bump: { 95 | options: { 96 | tag: "v<%= pkg.version %>", 97 | noStatus: true 98 | } 99 | } 100 | }, 101 | gitpush: { 102 | bump: { 103 | options: { 104 | branch: 'master', 105 | tags: true 106 | } 107 | } 108 | } 109 | }); 110 | 111 | grunt.loadNpmTasks('grunt-contrib-jshint'); 112 | grunt.loadNpmTasks('grunt-contrib-concat'); 113 | grunt.loadNpmTasks('grunt-contrib-uglify'); 114 | grunt.loadNpmTasks('grunt-bower-task'); 115 | grunt.loadNpmTasks('grunt-karma'); 116 | 117 | // Default task 118 | grunt.registerTask('default', ['build']); 119 | 120 | // Build task 121 | grunt.registerTask('build', ['bower', 'karma:build', 'concat', 'uglify']); 122 | 123 | // Test task 124 | grunt.registerTask('test', ['karma:build']); 125 | 126 | // Release Task 127 | grunt.registerTask('release', ['bump', 'build']); 128 | 129 | // Publish Task 130 | grunt.registerTask('publish', ['gitcommit:bump', 'gittag:bump', 'gitpush:bump']); 131 | 132 | // Provides the "bump" task. 133 | grunt.registerTask('bump', 'Increment version number', function() { 134 | var versionType = grunt.option('type'); 135 | function bumpVersion(version, versionType) { 136 | var type = {patch: 2, minor: 1, major: 0}, 137 | parts = version.split('.'), 138 | idx = type[versionType || 'patch']; 139 | parts[idx] = parseInt(parts[idx], 10) + 1; 140 | while(++idx < parts.length) { parts[idx] = 0; } 141 | return parts.join('.'); 142 | } 143 | 144 | var version = grunt.config.data.pkg.version; 145 | version = bumpVersion(version, versionType || 'patch'); 146 | 147 | grunt.config.data.pkg.version = version; 148 | grunt.file.write('bower.json', JSON.stringify(grunt.config.data.pkg, null, ' ')); 149 | 150 | grunt.log.ok('Version bumped to ' + version); 151 | }); 152 | 153 | }; 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | angular-rut [![Bower version][bower-badge]][bower] [![Build Status][travis-badge]][travis] 2 | =============== 3 | 4 | [travis]: https://travis-ci.org/platanus/angular-rut 5 | [travis-badge]: https://travis-ci.org/platanus/angular-rut.svg?branch=master 6 | [bower]: http://badge.fury.io/bo/angular-rut 7 | [bower-badge]: https://badge.fury.io/bo/angular-rut.svg 8 | 9 | An Angular module with several components to handle Chilean RUT validation, cleaning and formatting. It includes: 10 | 11 | - **ngRut**: a directive to easily add validation, cleaning and formatting to any element with an associated ```ng-model``` value 12 | - **rut**: a filter to format valid RUTs with dots and dashes (11.111.111-1) in the view 13 | - **RutHelper**: a constant you can inject in your modules and exposes individual methods for validation, cleaning and formatting. 14 | 15 | ## angular-rut is no longer maintained. 16 | 17 | - We will leave the Issues open as a discussion forum only. 18 | - We do not guarantee a response from us in the Issues. 19 | - We are no longer accepting pull requests. 20 | 21 | ## Installation 22 | 23 | *Just use [Bower](http://bower.io/)*. 24 | 25 | ``` 26 | bower install angular-rut --save 27 | ``` 28 | 29 | Then, inject it into your application: 30 | 31 | ```javascript 32 | angular.module('MyApp', ['platanus.rut']); 33 | ``` 34 | 35 | ## Directive 36 | 37 | Add the ```ng-rut``` directive to any element with an associated ```ng-model``` to automatically perform validation, cleaning and formatting. 38 | 39 | ```html 40 | 41 | ``` 42 | The directive does the following: 43 | 44 | - It **validates** whether the input is a valid RUT (by using ```$setValidity```), 45 | - passes a **clean** RUT (numbers and K only) to the model, 46 | - and **formats** the view by adding dots and dashes (11.111.111-1). 47 | 48 | #### Options 49 | 50 | You can use the ```rut-format``` attribute to define when should the view be formatted: 51 | 52 | - ```live```: Format as the RUT changes (e.g. as a user types into the input): 53 | ```html 54 | 55 | ``` 56 | 57 | - ```blur```: Format when the input is blurred: 58 | ```html 59 | 60 | ``` 61 | 62 | 63 | ## Filter 64 | 65 | Use it just like any Angular filter. It takes a (presumably valid) RUT, cleans and formats it. 66 | 67 | ```html 68 | {{ model.rut | rut }} 69 | ``` 70 | 71 | ## RutHelper 72 | 73 | Inject it as a dependency anywhere you like: 74 | 75 | ```javascript 76 | app.controller('RutCtrl', ['RutHelper', function(RutHelper){ ... 77 | ``` 78 | 79 | Then use it like so: 80 | 81 | ```javascript 82 | RutHelper.format('111111111'); // returns 11.111.111-1 83 | ``` 84 | 85 | There are three methods available: 86 | 87 | - ```RutHelper.clean(rut)``` strips every character except numbers and the letter K. 88 | - ```RutHelper.format(rut)``` cleans and formats the RUT number with dots and dashes (e.g 11.111.111-1). 89 | - ```RutHelper.validate(rut)``` returns a boolean indicating whether the given RUT is valid. 90 | 91 | ## Contributing 92 | 93 | 1. Fork it 94 | 2. Create your feature branch (`git checkout -b my-new-feature`) 95 | 3. Commit your changes (`git commit -am 'Add some feature'`) 96 | 4. Push to the branch (`git push origin my-new-feature`) 97 | 5. Create new Pull Request 98 | 99 | ## Credits 100 | 101 | Thank you [contributors](https://github.com/platanus/angular-rut/graphs/contributors)! 102 | 103 | Platanus 104 | 105 | angular-rut is maintained by [platanus](http://platan.us). 106 | 107 | ## License 108 | 109 | It is free software and may be redistributed under the terms specified in the LICENSE file. 110 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-rut", 3 | "version": "1.0.2", 4 | "authors": [ 5 | "Jaime Bunzli ", 6 | "Ignacio Baixas ", 7 | "René Morales " 8 | ], 9 | "description": "Chilean RUT module for angular", 10 | "license": "MIT", 11 | "homepage": "https://github.com/angular-platanus/rut", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/angular-platanus/rut.git" 15 | }, 16 | "main": "./dist/angular-rut.min.js", 17 | "ignore": [ 18 | "**/.*", 19 | "Gruntfile.js", 20 | "karma.conf.js", 21 | "package.json", 22 | "test", 23 | "src", 24 | ".travis.yml" 25 | ], 26 | "devDependencies": { 27 | "angular": "~1.2.27", 28 | "angular-mocks": "~1.2.27" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dist/angular-rut.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Chilean RUT module for angular 3 | * @version v1.0.2 - 2016-12-16 4 | * @link https://github.com/angular-platanus/rut 5 | * @author Jaime Bunzli , Ignacio Baixas , René Morales 6 | * @license MIT License, http://www.opensource.org/licenses/MIT 7 | */ 8 | 9 | (function(angular, undefined) { 10 | 'use strict'; 11 | function cleanRut(_value) { 12 | return typeof _value === 'string' ? _value.replace(/[^0-9kK]+/g,'').toUpperCase() : ''; 13 | } 14 | 15 | function formatRut(_value, _default) { 16 | _value = cleanRut(_value); 17 | 18 | if(!_value) return _default; 19 | if(_value.length <= 1) return _value; 20 | 21 | var result = _value.slice(-4,-1) + '-' + _value.substr(_value.length-1); 22 | for(var i = 4; i < _value.length; i+=3) result = _value.slice(-3-i,-i) + '.' + result; 23 | return result; 24 | } 25 | 26 | function validateRut(_value) { 27 | if(typeof _value !== 'string') return false; 28 | var t = parseInt(_value.slice(0,-1), 10), m = 0, s = 1; 29 | while(t > 0) { 30 | s = (s + t%10 * (9 - m++%6)) % 11; 31 | t = Math.floor(t / 10); 32 | } 33 | var v = (s > 0) ? (s-1)+'' : 'K'; 34 | return (v === _value.slice(-1)); 35 | } 36 | 37 | function addValidatorToNgModel(ngModel){ 38 | var validate = function(value) { 39 | var valid = (value.length > 0) ? validateRut(value) : true; 40 | ngModel.$setValidity('rut', valid); 41 | return valid; 42 | }; 43 | 44 | var validateAndFilter = function(_value) { 45 | _value = cleanRut(_value); 46 | return validate(_value) ? _value : null; 47 | }; 48 | 49 | var validateAndFormat = function(_value) { 50 | _value = cleanRut(_value); 51 | validate(_value); 52 | return formatRut(_value); 53 | }; 54 | 55 | ngModel.$parsers.unshift(validateAndFilter); 56 | ngModel.$formatters.unshift(validateAndFormat); 57 | } 58 | 59 | function formatRutOnWatch($scope, ngModel) { 60 | $scope.$watch(function() { 61 | return ngModel.$viewValue; 62 | }, function() { 63 | ngModel.$setViewValue(formatRut(ngModel.$viewValue)); 64 | ngModel.$render(); 65 | }); 66 | } 67 | 68 | function formatRutOnBlur($element, ngModel) { 69 | $element.on('blur', function(){ 70 | ngModel.$setViewValue(formatRut(ngModel.$viewValue)); 71 | ngModel.$render(); 72 | }); 73 | } 74 | 75 | angular.module('platanus.rut', []) 76 | 77 | .directive('ngRut', function() { 78 | return { 79 | restrict: 'A', 80 | require: 'ngModel', 81 | link: function($scope, $element, $attrs, ngModel) { 82 | if ( typeof $attrs.rutFormat === 'undefined' ) { 83 | $attrs.rutFormat = 'live'; 84 | } 85 | 86 | addValidatorToNgModel(ngModel); 87 | 88 | switch($attrs.rutFormat) { 89 | case 'live': 90 | formatRutOnWatch($scope, ngModel); 91 | break; 92 | case 'blur': 93 | formatRutOnBlur($element, ngModel); 94 | break; 95 | } 96 | } 97 | }; 98 | }) 99 | 100 | .filter('rut', function() { 101 | return formatRut; 102 | }) 103 | 104 | .constant('RutHelper', { 105 | format: formatRut, 106 | clean: cleanRut, 107 | validate: function(value) { 108 | return validateRut(cleanRut(value)); 109 | } 110 | }); 111 | })(angular); 112 | -------------------------------------------------------------------------------- /dist/angular-rut.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Chilean RUT module for angular 3 | * @version v1.0.2 - 2016-12-16 4 | * @link https://github.com/angular-platanus/rut 5 | * @author Jaime Bunzli , Ignacio Baixas , René Morales 6 | * @license MIT License, http://www.opensource.org/licenses/MIT 7 | */ 8 | !function(a,b){"use strict";function c(a){return"string"==typeof a?a.replace(/[^0-9kK]+/g,"").toUpperCase():""}function d(a,b){if(a=c(a),!a)return b;if(a.length<=1)return a;for(var d=a.slice(-4,-1)+"-"+a.substr(a.length-1),e=4;e0;)d=(d+b%10*(9-c++%6))%11,b=Math.floor(b/10);var e=d>0?d-1+"":"K";return e===a.slice(-1)}function f(a){var b=function(b){var c=!(b.length>0)||e(b);return a.$setValidity("rut",c),c},f=function(a){return a=c(a),b(a)?a:null},g=function(a){return a=c(a),b(a),d(a)};a.$parsers.unshift(f),a.$formatters.unshift(g)}function g(a,b){a.$watch(function(){return b.$viewValue},function(){b.$setViewValue(d(b.$viewValue)),b.$render()})}function h(a,b){a.on("blur",function(){b.$setViewValue(d(b.$viewValue)),b.$render()})}a.module("platanus.rut",[]).directive("ngRut",function(){return{restrict:"A",require:"ngModel",link:function(a,b,c,d){switch("undefined"==typeof c.rutFormat&&(c.rutFormat="live"),f(d),c.rutFormat){case"live":g(a,d);break;case"blur":h(b,d)}}}}).filter("rut",function(){return d}).constant("RutHelper",{format:d,clean:c,validate:function(a){return e(c(a))}})}(angular); 9 | -------------------------------------------------------------------------------- /karma-sauce.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 | 7 | // Base path, that will be used to resolve files and exclude 8 | basePath: '', 9 | 10 | // Load jasmine and requirejs (require is used by the readme spec) 11 | frameworks: ['jasmine'], 12 | 13 | // List of files / patterns to load in the browser 14 | files: [ 15 | // libraries 16 | 'bower_components/angular/angular.js', 17 | 'bower_components/angular-mocks/angular-mocks.js', 18 | 19 | // our app 20 | 'src/*.js', 21 | 22 | // the specs 23 | // Do not run the README test since it requires requirejs and I could make it work on sauce 24 | 'test/**/*spec.js' 25 | ], 26 | 27 | // Karma plugins 28 | plugins: [ 29 | 'karma-jasmine', 30 | 'karma-sauce-launcher' 31 | ], 32 | 33 | // List of files to exclude 34 | exclude: [ 35 | 'test/readme-spec.js' 36 | ], 37 | 38 | // Test results reporter to use 39 | // possible values: 'dots', 'progress', 'junit' 40 | reporters: ['progress', 'saucelabs'], 41 | 42 | // Enable / disable colors in the output (reporters and logs) 43 | colors: true, 44 | 45 | // Level of logging 46 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 47 | logLevel: config.LOG_INFO, 48 | 49 | // Enable / disable watching file and executing tests whenever any file changes 50 | autoWatch: false, 51 | 52 | // Broser connection tolerances (partially based on angularjs configuration) 53 | captureTimeout: 0, 54 | browserDisconnectTolerance: 2, 55 | browserDisconnectTimeout: 10000, 56 | browserNoActivityTimeout: 60000, 57 | 58 | // Disable websocket for full browser tests 59 | transports: ['xhr-polling'], 60 | 61 | // Continuous Integration mode by default 62 | singleRun: true, 63 | 64 | // Sauce config, requires username and accessKey to be loaded in ENV 65 | sauceLabs: { 66 | testName: 'Angular Rut Tests', 67 | startConnect: false 68 | }, 69 | 70 | // Custom sauce launchers 71 | customLaunchers: 72 | { 73 | 'SL_Chrome': { 74 | base: 'SauceLabs', 75 | browserName: 'chrome', 76 | version: '34' 77 | }, 78 | 'SL_Firefox': { 79 | base: 'SauceLabs', 80 | browserName: 'firefox', 81 | version: '26' 82 | }, 83 | 'SL_Safari': { 84 | base: 'SauceLabs', 85 | browserName: 'safari', 86 | platform: 'OS X 10.9', 87 | version: '7' 88 | }, 89 | 'SL_IE_8': { // TODO: fix IE8 tests, for some reason tests do not work as expected (but library does), maybe is a jasmine/angularjs issue? 90 | base: 'SauceLabs', 91 | browserName: 'internet explorer', 92 | platform: 'Windows XP', 93 | version: '8' 94 | }, 95 | 'SL_IE_9': { 96 | base: 'SauceLabs', 97 | browserName: 'internet explorer', 98 | platform: 'Windows 2008', 99 | version: '9' 100 | }, 101 | 'SL_IE_10': { 102 | base: 'SauceLabs', 103 | browserName: 'internet explorer', 104 | platform: 'Windows 2012', 105 | version: '10' 106 | }, 107 | 'SL_IE_11': { 108 | base: 'SauceLabs', 109 | browserName: 'internet explorer', 110 | platform: 'Windows 8.1', 111 | version: '11' 112 | } 113 | }, 114 | 115 | browsers: ['SL_Chrome', 'SL_Firefox', 'SL_Safari', 'SL_IE_9', 'SL_IE_10', 'SL_IE_11'] 116 | }); 117 | 118 | // set tunnel identifier for travis builds, by default it uses the job number. 119 | if (process.env.TRAVIS) { 120 | config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER; 121 | } 122 | }; 123 | -------------------------------------------------------------------------------- /karma.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 | 7 | // base path, that will be used to resolve files and exclude 8 | basePath: '', 9 | 10 | frameworks: ["jasmine"], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | // libraries 15 | 'bower_components/angular/angular.js', 16 | 'bower_components/angular-mocks/angular-mocks.js', 17 | 18 | // our app 19 | 'src/*.js', 20 | 21 | // tests 22 | 'test/**/*.js' 23 | ], 24 | 25 | 26 | // list of files to exclude 27 | exclude: [ 28 | 29 | ], 30 | 31 | 32 | // test results reporter to use 33 | // possible values: 'dots', 'progress', 'junit' 34 | reporters: ['progress'], 35 | 36 | 37 | // web server port 38 | port: 9876, 39 | 40 | 41 | // cli runner port 42 | runnerPort: 9100, 43 | 44 | 45 | // enable / disable colors in the output (reporters and logs) 46 | colors: true, 47 | 48 | 49 | // level of logging 50 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 51 | logLevel: config.LOG_INFO, 52 | 53 | 54 | // enable / disable watching file and executing tests whenever any file changes 55 | autoWatch: true, 56 | 57 | 58 | // Start these browsers, currently available: 59 | // - Chrome 60 | // - ChromeCanary 61 | // - Firefox 62 | // - Opera 63 | // - Safari (only Mac) 64 | // - PhantomJS 65 | // - IE (only Windows) 66 | browsers: ['PhantomJS'], 67 | 68 | 69 | // If browser does not capture in given timeout [ms], kill it 70 | captureTimeout: 60000, 71 | 72 | 73 | // Continuous Integration mode 74 | // if true, it capture browsers, run tests and exit 75 | singleRun: false 76 | 77 | }); 78 | }; 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-rut", 3 | "version": "1.0.2", 4 | "engines": { 5 | "node": ">=0.10" 6 | }, 7 | "dependencies": {}, 8 | "devDependencies": { 9 | "grunt-bower": "~0.16.0", 10 | "grunt-bower-task": "0.4.0", 11 | "grunt-cli": "~0.1.13", 12 | "grunt-contrib-concat": "~0.5.0", 13 | "grunt-contrib-jshint": "~0.10.0", 14 | "grunt-contrib-uglify": "~0.6.0", 15 | "grunt-git": "^0.2.14", 16 | "grunt-karma": "~0.9.0", 17 | "karma-cli": "0.0.4", 18 | "karma-jasmine": "~0.3.2", 19 | "karma-phantomjs-launcher": "~0.1.4", 20 | "karma-sauce-launcher": "^0.2.10" 21 | }, 22 | "scripts": { 23 | "test": "./node_modules/.bin/karma start karma-sauce.conf.js --single-run" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/rut.js: -------------------------------------------------------------------------------- 1 | function cleanRut(_value) { 2 | return typeof _value === 'string' ? _value.replace(/[^0-9kK]+/g,'').toUpperCase() : ''; 3 | } 4 | 5 | function formatRut(_value, _default) { 6 | _value = cleanRut(_value); 7 | 8 | if(!_value) return _default; 9 | if(_value.length <= 1) return _value; 10 | 11 | var result = _value.slice(-4,-1) + '-' + _value.substr(_value.length-1); 12 | for(var i = 4; i < _value.length; i+=3) result = _value.slice(-3-i,-i) + '.' + result; 13 | return result; 14 | } 15 | 16 | function validateRut(_value) { 17 | if(typeof _value !== 'string') return false; 18 | var t = parseInt(_value.slice(0,-1), 10), m = 0, s = 1; 19 | while(t > 0) { 20 | s = (s + t%10 * (9 - m++%6)) % 11; 21 | t = Math.floor(t / 10); 22 | } 23 | var v = (s > 0) ? (s-1)+'' : 'K'; 24 | return (v === _value.slice(-1)); 25 | } 26 | 27 | function addValidatorToNgModel(ngModel){ 28 | var validate = function(value) { 29 | var valid = (value.length > 0) ? validateRut(value) : true; 30 | ngModel.$setValidity('rut', valid); 31 | return valid; 32 | }; 33 | 34 | var validateAndFilter = function(_value) { 35 | _value = cleanRut(_value); 36 | return validate(_value) ? _value : null; 37 | }; 38 | 39 | var validateAndFormat = function(_value) { 40 | _value = cleanRut(_value); 41 | validate(_value); 42 | return formatRut(_value); 43 | }; 44 | 45 | ngModel.$parsers.unshift(validateAndFilter); 46 | ngModel.$formatters.unshift(validateAndFormat); 47 | } 48 | 49 | function formatRutOnWatch($scope, ngModel) { 50 | $scope.$watch(function() { 51 | return ngModel.$viewValue; 52 | }, function() { 53 | ngModel.$setViewValue(formatRut(ngModel.$viewValue)); 54 | ngModel.$render(); 55 | }); 56 | } 57 | 58 | function formatRutOnBlur($element, ngModel) { 59 | $element.on('blur', function(){ 60 | ngModel.$setViewValue(formatRut(ngModel.$viewValue)); 61 | ngModel.$render(); 62 | }); 63 | } 64 | 65 | angular.module('platanus.rut', []) 66 | 67 | .directive('ngRut', function() { 68 | return { 69 | restrict: 'A', 70 | require: 'ngModel', 71 | link: function($scope, $element, $attrs, ngModel) { 72 | if ( typeof $attrs.rutFormat === 'undefined' ) { 73 | $attrs.rutFormat = 'live'; 74 | } 75 | 76 | addValidatorToNgModel(ngModel); 77 | 78 | switch($attrs.rutFormat) { 79 | case 'live': 80 | formatRutOnWatch($scope, ngModel); 81 | break; 82 | case 'blur': 83 | formatRutOnBlur($element, ngModel); 84 | break; 85 | } 86 | } 87 | }; 88 | }) 89 | 90 | .filter('rut', function() { 91 | return formatRut; 92 | }) 93 | 94 | .constant('RutHelper', { 95 | format: formatRut, 96 | clean: cleanRut, 97 | validate: function(value) { 98 | return validateRut(cleanRut(value)); 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": false, 6 | "camelcase": true, 7 | "curly": false, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "module": false, 23 | "after": false, 24 | "afterEach": false, 25 | "angular": false, 26 | "before": false, 27 | "beforeEach": false, 28 | "browser": false, 29 | "describe": false, 30 | "expect": false, 31 | "inject": false, 32 | "it": false, 33 | "spyOn": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/rut_spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('', function() { 4 | 5 | var setInputValue = function(element, value) { 6 | element.val(value); 7 | element.triggerHandler('change'); 8 | }; 9 | 10 | beforeEach(module('platanus.rut')); 11 | 12 | describe('ngRut directive', function() { 13 | var element, scope; 14 | 15 | beforeEach(inject(function($rootScope, $compile) { 16 | element = angular.element('
'); 17 | 18 | scope = $rootScope.$new(); 19 | $compile(element)(scope); 20 | scope.$digest(); 21 | 22 | element = element.find('input'); 23 | scope.form.rut.$setViewValue(''); 24 | })); 25 | 26 | it('should make input display an empty string if model value is empty', function() { 27 | scope.inputs = { rut: '' }; 28 | scope.$digest(); 29 | expect(element.val()).toEqual(''); 30 | }); 31 | 32 | it('should make input display a formated rut if model value changes', function() { 33 | scope.inputs = { rut: '999999999' }; 34 | scope.$digest(); 35 | expect(element.val()).toEqual('99.999.999-9'); 36 | }); 37 | 38 | it('should set model value to null if view value is invalid', function() { 39 | setInputValue(element, '1.018.177-6'); 40 | expect(scope.inputs.rut).toEqual(null); 41 | }); 42 | 43 | it('should pass with valid rut', function() { 44 | setInputValue(element, '99.999.999-9'); 45 | expect(scope.form.rut.$valid).toEqual(true); 46 | }); 47 | 48 | it('should not pass with invalid rut', function() { 49 | setInputValue(element, '1.018.177-6'); 50 | expect(scope.form.rut.$valid).toEqual(false); 51 | }); 52 | 53 | it('should pass with an empty value', function() { 54 | setInputValue(element, ''); 55 | expect(scope.form.rut.$valid).toEqual(true); 56 | }); 57 | 58 | it('should format the rut shown in the input', function() { 59 | setInputValue(element, '999999999'); 60 | expect(element.val()).toEqual('99.999.999-9'); 61 | }); 62 | 63 | it('should format an invalid rut shown in the input', function() { 64 | setInputValue(element, '153363081'); 65 | expect(element.val()).toEqual('15.336.308-1'); 66 | }); 67 | 68 | it('should pass a clean rut to the model', function() { 69 | setInputValue(element, '11.111.111-1'); 70 | expect(scope.inputs.rut).toEqual('111111111'); 71 | }); 72 | 73 | }); 74 | 75 | describe('ngRut directive with rutFormat blur', function() { 76 | var element, scope; 77 | 78 | beforeEach(inject(function($rootScope, $compile) { 79 | element = angular.element('
'); 80 | 81 | scope = $rootScope.$new(); 82 | scope.inputs = { rut: '' }; 83 | $compile(element)(scope); 84 | scope.$digest(); 85 | 86 | element = element.find('input'); 87 | })); 88 | 89 | it('should not format the rut in real time', function() { 90 | setInputValue(element, '999999999'); 91 | expect(element.val()).toEqual('999999999'); 92 | }); 93 | 94 | it('should not format the rut in real time', function() { 95 | setInputValue(element, '999999999'); 96 | 97 | element.triggerHandler('blur'); 98 | 99 | expect(element.val()).toEqual('99.999.999-9'); 100 | }); 101 | 102 | }); 103 | 104 | describe('rut filter', function(){ 105 | var rutFilter; 106 | 107 | beforeEach(inject(function($filter) { 108 | rutFilter = $filter('rut'); 109 | })); 110 | 111 | it('should properly format a rut', function() { 112 | expect(rutFilter('1')).toEqual('1'); 113 | expect(rutFilter('101818k')).toEqual('101.818-K'); 114 | expect(rutFilter('101818l')).toEqual('10.181-8'); 115 | expect(rutFilter('1018189982')).toEqual('101.818.998-2'); 116 | }); 117 | 118 | it('should return default value if no rut is given', function() { 119 | expect(rutFilter('', '-')).toEqual('-'); 120 | expect(rutFilter(null, 'NA')).toEqual('NA'); 121 | expect(rutFilter(200, 'NA')).toEqual('NA'); 122 | }); 123 | }); 124 | 125 | describe('rutHelper', function(){ 126 | var rutHelper; 127 | 128 | beforeEach(inject(function(RutHelper) { 129 | rutHelper = RutHelper; 130 | })); 131 | 132 | it('should properly format a rut', function() { 133 | expect(rutHelper.format('1')).toEqual('1'); 134 | expect(rutHelper.format('101818k')).toEqual('101.818-K'); 135 | expect(rutHelper.format('101818l')).toEqual('10.181-8'); 136 | expect(rutHelper.format('1018189982')).toEqual('101.818.998-2'); 137 | }); 138 | 139 | it('should properly validate a rut', function() { 140 | expect(rutHelper.validate('189783205')).toBeTruthy(); 141 | expect(rutHelper.validate('18978320K')).toBeFalsy(); 142 | expect(rutHelper.validate('11.111.111-1')).toBeTruthy(); 143 | expect(rutHelper.validate('')).toBeFalsy(); 144 | }); 145 | 146 | it('should properly clean a rut', function() { 147 | expect(rutHelper.clean('18A978L3205')).toEqual('189783205'); 148 | expect(rutHelper.clean('18.978.320-5')).toEqual('189783205'); 149 | expect(rutHelper.clean('18978320-5')).toEqual('189783205'); 150 | expect(rutHelper.clean('18-978-320.5')).toEqual('189783205'); 151 | }); 152 | }); 153 | 154 | }); 155 | 156 | --------------------------------------------------------------------------------