├── .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 |
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 |
--------------------------------------------------------------------------------