├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── bower.json ├── examples └── index.html ├── gulpfile.js ├── karma.conf.js ├── package.json ├── release ├── dynamic-number.js └── dynamic-number.min.js ├── src └── numberDirective.js └── test ├── directiveSpec.js └── mobileDirectiveSpec.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.sublime-project 3 | *.sublime-workspace 4 | .DS_Store 5 | bower_components 6 | npm-debug.log 7 | .idea -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.sublime-project 3 | *.sublime-workspace 4 | .DS_Store 5 | bower_components 6 | test 7 | src 8 | examples 9 | gulpfile.js 10 | karma.conf.js 11 | README.md 12 | .travis.yml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | branches: 7 | only: 8 | - master 9 | notifications: 10 | email: false 11 | node_js: 12 | - '4.0' 13 | before_install: 14 | - npm i -g npm@^2.0.0 15 | - npm install -g bower 16 | - npm install -g gulp 17 | - bower install 18 | - npm install 19 | - gulp script 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Dynamic Number 2 | [![Build Status](https://travis-ci.org/uhlryk/angular-dynamic-number.svg)](https://travis-ci.org/uhlryk/angular-dynamic-number) 3 | [![Downloads](https://img.shields.io/npm/dt/angular-dynamic-number.svg)](https://www.npmjs.com/package/angular-dynamic-number) 4 | [![Downloads](https://img.shields.io/npm/dm/angular-dynamic-number.svg)](https://www.npmjs.com/package/angular-dynamic-number) 5 | [![NPM version](https://img.shields.io/npm/v/angular-dynamic-number.svg)](https://www.npmjs.com/package/angular-dynamic-number) 6 | [![Bower](https://img.shields.io/bower/v/angular-dynamic-number.svg)](http://bower.io/search/?q=angular-dynamic-number) 7 | 8 | Highly customizable AngularJS directive for numbers. 9 | It validates inputs in realtime (if user press not acceptable character 10 | it wont appear in input field). This directive may be configured for each input (you can set number of digits in integer part and number of digits in decimal part, you can set decimal separator, accept only positive or negative values) 11 | 12 | The Big advantage of this directive, is separation of view value and model value. You can set comma as decimal separator (default is dot) for numbers. And then in input field there will be comma as separator, but your model value will be correct float number with dot separator. 13 | 14 | It works at realtime, therefore this model value may be use in computation for other elements and they will change real time too. 15 | 16 | It works also when you set in controller your model value (with dot) and if you set separator as comma, then in input field there will be comma. 17 | 18 | There is also filter for one directional binding (ngBind). It round fraction part to fixed number of digits. Round method can be select from 19 | Math.round, Math.ceil and Math.floor. It can show comma or dot as decimal separator. 20 | 21 | Filters can reuse strategies. 22 | 23 | It even allow to dynamically change directive properties. Just set them as models. 24 | 25 | It is also React version available [React Dynamic Number](https://github.com/uhlryk/react-dynamic-number) 26 | 27 | ## Change Log: 28 | 29 | ### v2.0.0: 30 | 31 | model type is number instead of string. 32 | 33 | ## Demo: 34 | [link](http://htmlpreview.github.io/?https://github.com/uhlryk/angular-dynamic-number/blob/master/examples/index.html) 35 | 36 | ## Features: 37 | - config max numbers for integer part and decimal part. 38 | - config decimal separator (dot or comma) 39 | - config to accept positive, negative and both numbers. 40 | - model value is correct javascript number, but view value may be correct number for localities 41 | - dynamic thousand separator (by default if decimal separator is comma then thousand separator is dot) 42 | - thousand separarator: space, dot or comma 43 | - filter with comma/dot separator and congurable number of fraction digits 44 | - filter with thousand separator 45 | - keeps cursor position 46 | - custom strategies for directive and filer 47 | - allow add currency (single character) 48 | - allow to dynamically change directive properties 49 | 50 | ## Limitations: 51 | Directive is designed for input text/tel field ( **type="text"** and **type="tel"** ). Tel field type triggers numeric keyboard on mobile devices. The input field must have ngModel. 52 | 53 | ## Installation: 54 | ### npm 55 | npm install angular-dynamic-number 56 | ### bower: 57 | bower install angular-dynamic-number 58 | then reference: 59 | 60 | bower_components/angular-dynamic-number/release/dynamic-number.js 61 | 62 | or 63 | 64 | bower_components/angular-dynamic-number/release/dynamic-number.min.js 65 | ### manualy 66 | Clone repository ```https://github.com/uhlryk/angular-dynamic-number.git``` and use in your code ```release/dynamic-number.js``` 67 | ## Quick start: How to use it 68 | Add the js file to your html: 69 | 70 | 71 | Or if you use browserify and install by npm: 72 | 73 | require('angular-dynamic-number'); 74 | Add the module to your dependencies: 75 | 76 | angular.module('myApp', ['dynamicNumber', ...]) 77 | 78 | Add the ```awnum``` attribute to input fields: 79 | 80 | 81 | 82 | ## Options 83 | Options are also input field attributes 84 | 85 | **num-int**: 86 | 87 | Set maximum numbers of digits integer part (digits before decimal separator) (default 6). 88 | 89 | **num-fract**: 90 | 91 | Set maximum numbers of digits fraction part (digits after decimal separator) (default 2). 92 | 93 | **num-sep**: 94 | 95 | Set decimal separator (dot or comma) (default '.'). 96 | 97 | **num-pos**: 98 | 99 | If true then number can be positive (default 'true'). 100 | 101 | **num-neg**: 102 | 103 | If true then number can be negative (default 'true'). 104 | 105 | **num-round**: 106 | 107 | Define round method for fraction part when convert from model to view and for filter 108 | 109 | **num-fixed 110 | 111 | If true then there is fixed number of fraction digets - (useful when fraction part is 00 and we need to show this zeros e.g. 12,00 ) 112 | 113 | **num-thousand**: 114 | 115 | If true then number has thousand separator. 116 | 117 | **num-thousand-sep**: 118 | 119 | Set thousand separator (dot or comma or space or apostrophe) (enable if num-thousand = true, by default if num-sep equal dot then thousand separator is comma). 120 | If you want to set separator as space remember that angular by default trim spaces. You can as value set "{{' '}}" 121 | 122 | **num-prepend** 123 | 124 | Allow to set single character prepend currency e.g. $1234.12. Html could have problem with show some characters. In those situations you should set currency as html entit. 125 | € = `€` 126 | 127 | **num-append** 128 | 129 | Allow to set single character append currency e.g. 1234.12€. Html could have problem with show some characters. In those situations you should set currency as html entit. 130 | € = `€` 131 | 132 | ## Dynamic properties 133 | 134 | Some of properties of directive can be a models. And change in models change properties in directive. For example you can change currency, decimal separator etc. 135 | Demo page has example of usage dynamic properties. 136 | 137 | In short separator, integer, fraction, thousand, append are models 138 | 139 | 148 | 149 | Changes of some properties reset value in input (num-int, num-fract). Others properties after changes recreate value in input. 150 | 151 | ## Custom strategies 152 | 153 | There are multiple options for configuration each input. If you don't like write each time same options, there is for you custom strategies. 154 | You can in config file create multiple sets of configs, and use them in input file 155 | 156 | To create custom strategy in config use ```dynamicNumberStrategyProvider```. It has methods: 157 | 158 | addStrategy(strategyName, { 159 | numInt: number, 160 | numFract: number, 161 | numSep: char [\.|,], 162 | numPos: boolean [true|false], 163 | numNeg: boolean [true|false], 164 | numRound: string ['round' | 'ceil' | 'floor'], 165 | numThousand: boolean [true|false] 166 | }) 167 | 168 | for example, create price strategy: 169 | 170 | app.config(['dynamicNumberStrategyProvider', function(dynamicNumberStrategyProvider){ 171 | dynamicNumberStrategyProvider.addStrategy('price', { 172 | numInt: 6, 173 | numFract: 2, 174 | numSep: '.', 175 | numPos: true, 176 | numNeg: true, 177 | numRound: 'round', 178 | numThousand: true 179 | }); 180 | }]); 181 | 182 | and use it in input: 183 | 184 | 185 | 186 | ## Filter options 187 | 188 | {{ expression | awnum:numFrac:numSep:numRound:numFixed:numThousand:numThousandSep:numPrepend:numAppend}} 189 | 190 | **numFrac** 191 | 192 | Set maximum numbers of digits fraction part (digits after decimal separator) (default 2). 193 | 194 | **numSep** 195 | 196 | Set decimal separator (dot or comma) (default '.'). 197 | 198 | **numRound** 199 | 200 | Define round method for fraction part when convert from model to view and for filter 201 | 202 | **numFixed** 203 | 204 | If true then there is fixed number of fraction digets - (useful when fraction part is 00 and we need to show this zeros e.g. 12,00 ) 205 | 206 | **numThousand** 207 | 208 | If true then number has thousand separator. 209 | 210 | **numThousandSep** 211 | 212 | Set thousand separator (dot or comma or space or apostrophe). 213 | 214 | **numPrepend** 215 | 216 | Allow to set single character prepend currency e.g. $1234.12. Html could have problem with show some characters. In those situations you should set currency as html entit. 217 | € = `€` 218 | 219 | **numAppend** 220 | 221 | Allow to set single character append currency e.g. 1234.12€. Html could have problem with show some characters. In those situations you should set currency as html entit. 222 | € = `€` 223 | 224 | ## Filter with strategies 225 | 226 | {{ expression | awnum:strategy}} 227 | 228 | **strategy** 229 | 230 | This is name of strategy 231 | 232 | ## ngTrim and spaces 233 | 234 | This is angular input directive parameter. By default it has value true, which means that it automatically trim spaces and Angular Dynamic Number don't get spaces. 235 | Therefore the best result is when you set for input ng-trim=false 236 | 237 | ## Example: 238 | Negative number with max value 9999.99 and comma as separator 239 | 240 | 241 | 242 | Negative or positive number with max value 9999.99 and comma as separator and thousand separator space 243 | 244 | 245 | 246 | Filter for number with max 3 fraction number and comma separator 247 | 248 |
{{somemodel|awnum:3:',':'round'}}
249 | 250 | ## License 251 | MIT 252 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-dynamic-number", 3 | "version": "2.5.1", 4 | "homepage": "https://github.com/uhlryk/angular-dynamic-number", 5 | "authors": [ 6 | "Krzysztof Sztompka " 7 | ], 8 | "description": "Highly customizable angular directive for numbers", 9 | "main": "release/dynamic-number.js", 10 | "keywords": [ 11 | "angular", 12 | "directive", 13 | "number", 14 | "format", 15 | "comma", 16 | "dot", 17 | "number separator", 18 | "thousand separator", 19 | "decimal separator" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | "**/.*", 24 | "node_modules", 25 | "bower_components", 26 | "test", 27 | "src", 28 | "examples", 29 | ".travis.yml", 30 | ".npmignore", 31 | "karma.conf.js", 32 | "package.json", 33 | "gulpfile.js" 34 | ], 35 | "dependencies": { 36 | "angular": "~1.4.7" 37 | }, 38 | "devDependencies": { 39 | "angular-mocks": "~1.4.7" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My Angular App 5 | 6 | 7 | 8 | 9 | 10 | 41 | 42 | 43 |
44 |
45 |
46 |

Example of angular dynamic number directive

47 |
48 |
49 | 50 | 51 |
How looks model: {{value1}}
52 |
53 |
54 | 55 | 56 |
How looks model: {{value14}}
57 |
58 |
59 | 60 | 61 |
How looks model: {{value6}}
62 |
63 |
64 | 65 | 66 |
How looks model: {{value7}}
67 |
68 |
69 | 70 | 71 |
How looks model: {{value9}}
72 |
73 |
74 | 75 | 76 |
How looks model: {{value12}}
77 |
78 |
79 | 80 | 81 |
How looks model: {{value2}}
82 |
83 |
84 | 85 | 86 |
How looks model: {{value3}}
87 |
88 |
89 | 90 | 91 |
How looks model: {{value13}}
92 |
93 |
94 | 95 | 96 |
How looks model: {{value4}}
97 |
98 |
99 | 100 | 101 |
How looks model: {{value5}}
102 |
103 |
104 | 105 | 106 |
How looks model: {{value10}}
107 |
108 |
109 | 110 | 111 |
How looks model: {{value11}}
112 |
113 |
114 | 115 | 116 |
How looks model: {{value8}}
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |

Example of angular dynamic number directive with dynamic properties

126 |
127 |
128 | 129 | 133 |
Active separator: {{separator}}
134 |
135 |
136 | 140 |
Is thousand separator: {{thousand}}
141 |
142 |
143 | 144 | 152 |
Active append: {{append}}
153 |
154 |
155 | 156 | 167 |
How looks model: {{value}}
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |

Example of angular dynamic number filter

177 |

basic

178 |
179 |
Model value without filter:{{filter1}}
180 |
Filter: {{filter1|awnum:2:',':'round'}}
181 |
182 |

Example of angular dynamic number filter with comma separator, thousand dot separator

183 |
184 |
Model value without filter:{{filter2}}
185 |
Filter: {{filter2|awnum:2:',':'round':'false':'true'}}
186 |
187 |

Example of angular dynamic number filter with comma separator, thousand space separator

188 |
189 |
Model value without filter:{{filter3}}
190 |
Filter: {{filter3|awnum:2:',':'round':'false':'true':' '}}
191 |
192 |

basic number with $ currency

193 |
194 |
Model value without filter:{{filter4}}
195 |
Filter: {{filter4|awnum:2:',':'round':'false':'false':' ':'$'}}
196 |
197 |

basic number with € currency

198 |
199 |
Model value without filter:{{filter5}}
200 |
Filter: {{filter5|awnum:2:',':'round':'false':'false':' ':null:'€'}}
201 |
202 |

Example of angular dynamic number filter with strategy

203 |
204 |
Model value without filter:{{filter5}}
205 |
Filter: {{filter5|awnum:'price'}}
206 |
207 |
208 |
209 |
210 | 211 | 212 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var uglify = require('gulp-uglify'); 3 | var rename = require("gulp-rename"); 4 | var karma = require('karma').Server; 5 | 6 | gulp.task("script",function(){ 7 | gulp.src(["src/*.js"]) 8 | .pipe(rename("dynamic-number.js")) 9 | .pipe(gulp.dest("release")) 10 | .pipe(uglify()) 11 | .pipe(rename("dynamic-number.min.js")) 12 | .pipe(gulp.dest("release")) 13 | ; 14 | }); 15 | gulp.task('test', ['script'],function (done) { 16 | karma.start({ 17 | configFile: __dirname + '/karma.conf.js', 18 | singleRun: true 19 | }, function() { 20 | done(); 21 | }); 22 | }); -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Oct 08 2015 22:04:02 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | 'bower_components/angular/angular.js', 19 | 'bower_components/angular-mocks/angular-mocks.js', 20 | 'release/dynamic-number.js', 21 | 'test/directiveSpec.js', 22 | 'test/mobileDirectiveSpec.js' 23 | ], 24 | 25 | 26 | // list of files to exclude 27 | exclude: [ 28 | ], 29 | 30 | 31 | // preprocess matching files before serving them to the browser 32 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 33 | preprocessors: { 34 | }, 35 | 36 | 37 | // test results reporter to use 38 | // possible values: 'dots', 'progress' 39 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 40 | reporters: ['progress'], 41 | 42 | 43 | // web server port 44 | port: 9876, 45 | 46 | 47 | // enable / disable colors in the output (reporters and logs) 48 | colors: true, 49 | 50 | 51 | // level of logging 52 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 53 | logLevel: config.LOG_INFO, 54 | 55 | 56 | // enable / disable watching file and executing tests whenever any file changes 57 | autoWatch: false, 58 | 59 | 60 | // start these browsers 61 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 62 | browsers: ['Chrome'], 63 | 64 | 65 | // Continuous Integration mode 66 | // if true, Karma captures browsers, runs the tests and exits 67 | singleRun: false 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-dynamic-number", 3 | "version": "2.5.1", 4 | "description": "Highly customizable angular directive for numbers", 5 | "keywords": [ 6 | "angular", 7 | "directive", 8 | "number", 9 | "format", 10 | "comma", 11 | "dot", 12 | "number separator", 13 | "thousand separator", 14 | "decimal separator" 15 | ], 16 | "main": "release/dynamic-number.js", 17 | "homepage": "https://github.com/uhlryk/angular-dynamic-number", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/uhlryk/angular-dynamic-number.git" 21 | }, 22 | "author": "Krzysztof Sztompka ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/uhlryk/angular-dynamic-number/issues" 26 | }, 27 | "scripts": { 28 | "test": "gulp test", 29 | "build": "gulp script" 30 | }, 31 | "devDependencies": { 32 | "gulp": "^3.9.0", 33 | "gulp-rename": "^1.2.2", 34 | "gulp-uglify": "^1.4.1", 35 | "jasmine-core": "^2.4.1", 36 | "karma": "^0.13.10", 37 | "karma-chrome-launcher": "^0.2.1", 38 | "karma-firefox-launcher": "^0.1.6", 39 | "karma-jasmine": "^0.3.6" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /release/dynamic-number.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | (function(root, factory) { 3 | "use strict"; 4 | 5 | if (typeof define === 'function' && define.amd) { 6 | define(['angular'], factory); 7 | 8 | } else if (typeof module !== 'undefined' && typeof module.exports === 9 | 'object') { 10 | module.exports = factory(require('angular')); 11 | 12 | } else { 13 | return factory(root.angular); 14 | } 15 | }(window, function(angular) { 16 | 'use strict'; 17 | 18 | var wasPasted = false; 19 | 20 | function convModelToView(modelValue, viewSeparator, prepend, append) { 21 | if (modelValue === undefined || modelValue === null || modelValue === 22 | "") { 23 | return ""; 24 | } 25 | var newViewValue = ''; 26 | if (viewSeparator === ',') { 27 | newViewValue = String(modelValue).replace(".", ","); 28 | } else { 29 | newViewValue = String(modelValue); 30 | } 31 | return newViewValue; 32 | } 33 | 34 | function convViewToModel(viewValue, viewSeparator, thousandSeparator) { 35 | 36 | if (viewSeparator === ',') { 37 | return String(viewValue).replace(/['\.\s]/g, "").replace(",", "."); 38 | } else if (viewSeparator === '.') { 39 | return String(viewValue).replace(/[',\s]/g, ""); 40 | } 41 | } 42 | 43 | function addPrependAppend(value, prepend, append) { 44 | var newViewValue = value; 45 | if (append) { 46 | newViewValue = newViewValue + append; 47 | } 48 | if (prepend) { 49 | if (/^\-.+/.test(newViewValue)) { 50 | newViewValue = newViewValue.replace('-', '-' + prepend); 51 | } else if (/^\-/.test(newViewValue)) { 52 | newViewValue = newViewValue; 53 | } else { 54 | newViewValue = prepend + newViewValue; 55 | } 56 | } 57 | return newViewValue; 58 | } 59 | 60 | function initIntegerPart(attrs_num_int, def_num_int) { 61 | if (attrs_num_int >= 0) { 62 | var _num_int = parseInt(attrs_num_int, 10); 63 | if (isNaN(_num_int) === false && isFinite(_num_int) && _num_int >= 0) { 64 | return _num_int; 65 | } 66 | } 67 | return def_num_int; 68 | } 69 | 70 | function initFractionPart(attrs_num_fract, def_num_fract) { 71 | if (attrs_num_fract >= 0) { 72 | var _num_fract = parseInt(attrs_num_fract, 10); 73 | if (isNaN(_num_fract) === false && isFinite(_num_fract) && _num_fract >= 74 | 0) { 75 | return _num_fract; 76 | } 77 | } 78 | return def_num_fract; 79 | } 80 | 81 | function initSeparator(attrs_num_sep, def_num_sep) { 82 | if (attrs_num_sep === ',') { 83 | return ','; 84 | } else if (attrs_num_sep === '.') { 85 | return '.'; 86 | } 87 | return def_num_sep; 88 | } 89 | 90 | function initIsPositive(attrs_num_pos, def_num_pos) { 91 | if (attrs_num_pos === 'false' || attrs_num_pos === false) { 92 | return false; 93 | } else if (attrs_num_pos === 'true' || attrs_num_pos === true) { 94 | return true; 95 | } 96 | return def_num_pos; 97 | } 98 | 99 | function initIsNegative(attrs_num_neg, def_num_neg) { 100 | if (attrs_num_neg === 'false' || attrs_num_neg === false) { 101 | return false; 102 | } else if (attrs_num_neg === 'true' || attrs_num_neg === true) { 103 | return true; 104 | } 105 | return def_num_neg; 106 | } 107 | 108 | function initRound(attrs_round, def_round) { 109 | if (attrs_round === 'floor') { 110 | return Math.floor; 111 | } else if (attrs_round === 'ceil') { 112 | return Math.ceil; 113 | } else if (attrs_round === 'round') { 114 | return Math.round; 115 | } 116 | return def_round; 117 | } 118 | 119 | function initIsFixed(attrs_fixed, def_fixed) { 120 | if (attrs_fixed === 'false' || attrs_fixed === false) { 121 | return false; 122 | } else if (attrs_fixed === 'true' || attrs_fixed === true) { 123 | return true; 124 | } 125 | return def_fixed; 126 | } 127 | 128 | function initIsThousand(attrs_thousand, def_thousand) { 129 | if (attrs_thousand === 'false' || attrs_thousand === false) { 130 | return false; 131 | } else if (attrs_thousand === 'true' || attrs_thousand === true) { 132 | return true; 133 | } 134 | return def_thousand; 135 | } 136 | 137 | function initThousandSeparator(attrs_thousand, fractionSeparator, 138 | def_thousand) { 139 | if (!attrs_thousand) { 140 | return def_thousand; 141 | } 142 | var regexp; 143 | if (fractionSeparator === '.') { 144 | regexp = new RegExp('^[\',\\s]$'); 145 | } else { 146 | regexp = new RegExp('^[\'\\.\\s]$'); 147 | } 148 | if (regexp.test(attrs_thousand)) { 149 | return attrs_thousand; 150 | } else { 151 | return def_thousand; 152 | } 153 | } 154 | 155 | function initNumAppendPrepend(attrs_num_char) { 156 | var regexp = new RegExp('[^\\d,\\.\\s\\-]{1}'); 157 | if (regexp.test(attrs_num_char)) { 158 | return attrs_num_char; 159 | } 160 | return null; 161 | } 162 | 163 | 164 | 165 | function buildRegexp(integerPart, fractionPart, fractionSeparator, 166 | isPositiveNumber, isNegativeNumber) { 167 | var negativeRegex = '-?'; 168 | if (isPositiveNumber === false && isNegativeNumber === true) { 169 | negativeRegex = '-'; 170 | } else if (isPositiveNumber === true && isNegativeNumber === false) { 171 | negativeRegex = ''; 172 | } 173 | var intRegex = '[0-9]{0,' + (integerPart) + '}'; 174 | if (integerPart === 0) { 175 | intRegex = '0'; 176 | } 177 | var fractRegex = '(\\' + fractionSeparator + '([0-9]){0,' + 178 | fractionPart + '})'; 179 | if (fractionPart === 0) { 180 | fractRegex = ''; 181 | } 182 | return new RegExp('^' + negativeRegex + intRegex + fractRegex + '?$'); 183 | } 184 | 185 | function removeLeadingZero(value) { 186 | return String(value) 187 | .replace(/^(-?)(0+)$/g, "$10") //change 00000 to '0' and -00000 to '-0' 188 | .replace(/^0(\d+)/g, "$1") //change 000001 to '1' 189 | .replace(/^-0(\d+)/g, "-$1") //change -013212 to -0 190 | .replace(new RegExp('^-([\\.,\\s])', 'g'), "-0$1") //change -. to -0. 191 | .replace(new RegExp('^[\\.,\\s]', 'g'), "0$&"); //change . to 0. 192 | } 193 | 194 | function removePrependAppendChars(value, prepend, append) { 195 | var newValue = value; 196 | if (prepend) { 197 | newValue = newValue.replace(new RegExp('[\\' + prepend + ']', 'g'), 198 | ''); 199 | } 200 | if (append) { 201 | newValue = newValue.replace(new RegExp('[\\' + append + ']', 'g'), ''); 202 | } 203 | return newValue; 204 | } 205 | 206 | function removeThousandSeparators(value, thousandSeparator) { 207 | if (thousandSeparator === '.') { 208 | return String(value).replace(/\./g, ""); 209 | } else if (thousandSeparator === ',') { 210 | return String(value).replace(/,/g, ""); 211 | } else { 212 | return String(value).replace(new RegExp('[\'\\s]', 'g'), ""); 213 | } 214 | } 215 | 216 | function addThousandSeparator(value, fractionSeparator, thousandSeparator) { 217 | value = String(value).split(fractionSeparator); 218 | value[0] = value[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator); 219 | return value.join(fractionSeparator); 220 | } 221 | 222 | function addFixedZeros(value, parameters) { 223 | var newValue = value; 224 | if (parameters.isFixed) { 225 | var fractionPart = newValue.split(parameters.fractionSeparator)[1]; 226 | var fractionLength = (fractionPart && fractionPart.length) ? 227 | fractionPart.length : 0; 228 | if (fractionLength === 0) { 229 | newValue += parameters.fractionSeparator; 230 | } 231 | for (var i = fractionLength; i < parameters.fractionPart; i++) { 232 | newValue += "0"; 233 | } 234 | } 235 | return newValue; 236 | } 237 | 238 | function changeViewValue(ngModelController, value, parameters, state, 239 | disable) { 240 | 241 | if (disable) { 242 | state.enable = false; 243 | } 244 | var valueString = String(value); 245 | var valueWithFixedZeros = addFixedZeros(valueString, parameters); 246 | var valueWithPrependAppend = addPrependAppend(valueWithFixedZeros, 247 | parameters.prepend, parameters.append); 248 | // https://github.com/angular/angular.js/issues/13068 249 | // ngModelController.$viewValue = value; 250 | var version = angular.version; 251 | if (version.major === 1 && version.minor === 2) { 252 | ngModelController.$viewValue = valueWithPrependAppend; 253 | } else { 254 | ngModelController.$setViewValue(valueWithPrependAppend); 255 | } 256 | ngModelController.$render(); 257 | } 258 | 259 | function filterModelValue( 260 | value, 261 | fractionPart, 262 | fractionSeparator, 263 | roundFunction, 264 | numFixed, 265 | isThousandSeparator, 266 | thousandSeparator, 267 | prepend, 268 | append 269 | ) { 270 | if (value === '' || value === undefined || value === null) { 271 | return ''; 272 | } 273 | value = Number(value); 274 | if (!isNaN(value) && isFinite(value)) { 275 | var powerOfTen = Math.pow(10, fractionPart); 276 | if (numFixed) { 277 | value = convModelToView((roundFunction(value * powerOfTen) / 278 | powerOfTen).toFixed(fractionPart), fractionSeparator, prepend, 279 | append); 280 | } else { 281 | value = convModelToView(String(roundFunction(value * powerOfTen) / 282 | powerOfTen), fractionSeparator, prepend, append); 283 | } 284 | value = addPrependAppend(value, prepend, append); 285 | if (isThousandSeparator) { 286 | value = addThousandSeparator(value, fractionSeparator, 287 | thousandSeparator); 288 | } 289 | return value; 290 | } 291 | if (numFixed) { 292 | return (0).toFixed(fractionPart); 293 | } else { 294 | return "0"; 295 | } 296 | } 297 | /** 298 | * from this source: 299 | * http://stackoverflow.com/a/2897229/4138339 300 | */ 301 | function getCaretPosition(oField) { 302 | var iCaretPos = 0; 303 | if (document.selection) { 304 | oField.focus(); 305 | var oSel = document.selection.createRange(); 306 | oSel.moveStart('character', -oField.value.length); 307 | iCaretPos = oSel.text.length; 308 | } else if (oField.selectionStart || oField.selectionStart == '0') 309 | iCaretPos = oField.selectionDirection == 'backward' ? oField.selectionStart : 310 | oField.selectionEnd; 311 | return (iCaretPos); 312 | } 313 | /** 314 | * from this source 315 | * http://stackoverflow.com/a/22574572/4138339 316 | */ 317 | function setCaretPosition(elem, caretPos) { 318 | if (elem !== null) { 319 | if (elem.createTextRange) { 320 | var range = elem.createTextRange(); 321 | range.move('character', caretPos); 322 | range.select(); 323 | } else { 324 | if (elem.selectionStart) { 325 | elem.focus(); 326 | elem.setSelectionRange(caretPos, caretPos); 327 | } else 328 | elem.focus(); 329 | } 330 | } 331 | } 332 | 333 | function countThousandSeparatorToPosition(value, separator, position) { 334 | var countPosition = 0; 335 | var countDots = 0; 336 | for (var i = 0; i < value.length; i++) { 337 | if (value[i] !== separator) { 338 | countPosition++; 339 | if (countPosition >= position) break; 340 | } else { 341 | countDots++; 342 | } 343 | } 344 | return countDots; 345 | } 346 | 347 | function cutSurplusFractionPart(value, parameters) { 348 | 349 | var newValue = value; 350 | var splitedValue = newValue.split(parameters.fractionSeparator); 351 | var integerPart = splitedValue[0]; 352 | var fractionPart = splitedValue[1]; 353 | if (fractionPart && fractionPart.length > parameters.fractionPart) { 354 | fractionPart = fractionPart.slice(0, parameters.fractionPart); 355 | newValue = [integerPart, fractionPart].join(parameters.fractionSeparator); 356 | } 357 | return newValue; 358 | } 359 | 360 | function prepareResponse(value) { 361 | if (value === null) { 362 | return null; 363 | } else { 364 | return Number(value); 365 | } 366 | } 367 | 368 | function createPropertyObject(scope, key, value) { 369 | var properties = { 370 | awnum: scope.awnum, 371 | numInt: scope.numInt, 372 | numFract: scope.numFract, 373 | numSep: scope.numSep, 374 | numPos: scope.numPos, 375 | numNeg: scope.numNeg, 376 | numRound: scope.numRound, 377 | numThousand: scope.numThousand, 378 | numThousandSep: scope.numThousandSep, 379 | numPrepend: scope.numPrepend, 380 | numAppend: scope.numAppend, 381 | numFixed: scope.numFixed 382 | }; 383 | if (key) { 384 | properties[key] = value; 385 | } 386 | return properties; 387 | } 388 | 389 | function removeDoubledDecimalSeparators(value, parameters) { 390 | return value.replace(new RegExp("[\\" + parameters.fractionSeparator + 391 | "]+", "g"), parameters.fractionSeparator); 392 | } 393 | 394 | function initAllProperties(properties, element, attrs, ngModelController, 395 | dynamicNumberStrategy) { 396 | var strategy = {}; 397 | if (properties.awnum) { 398 | strategy = dynamicNumberStrategy.getStrategy(properties.awnum); 399 | } 400 | var integerPart = initIntegerPart(properties.numInt !== undefined ? 401 | properties.numInt : strategy.numInt, 6); 402 | var fractionPart = initFractionPart(properties.numFract !== undefined ? 403 | properties.numFract : strategy.numFract, 2); 404 | var fractionSeparator = initSeparator(properties.numSep !== undefined ? 405 | properties.numSep : strategy.numSep, '.'); 406 | var isPositiveNumber = initIsPositive(properties.numPos !== undefined ? 407 | properties.numPos : strategy.numPos, true); 408 | var isNegativeNumber = initIsNegative(properties.numNeg !== undefined ? 409 | properties.numNeg : strategy.numNeg, true); 410 | var roundFunction = initRound(properties.numRound !== undefined ? 411 | properties.numRound : strategy.numRound, Math.round); 412 | var isThousandSeparator = initIsThousand(properties.numThousand !== 413 | undefined ? properties.numThousand : strategy.numThousand, false); 414 | var thousandSeparator = initThousandSeparator(properties.numThousandSep !== 415 | undefined ? properties.numThousandSep : strategy.numThousandSep, 416 | fractionSeparator, fractionSeparator === '.' ? ',' : '.'); 417 | var prepend = initNumAppendPrepend(properties.numPrepend !== undefined ? 418 | properties.numPrepend : strategy.numPrepend); 419 | var append = initNumAppendPrepend(properties.numAppend !== undefined ? 420 | properties.numAppend : strategy.numAppend); 421 | var isFixed = initIsFixed(properties.numFixed !== undefined ? 422 | properties.numFixed : strategy.numFixed, false); 423 | if (isPositiveNumber === false && isNegativeNumber === false) { 424 | throw new Error( 425 | 'Number is set to not be positive and not be negative. Change num_pos attr or/and num_neg attr to true' 426 | ); 427 | } 428 | var viewRegexTest = buildRegexp(integerPart, fractionPart, 429 | fractionSeparator, isPositiveNumber, isNegativeNumber); 430 | return { 431 | element: element, 432 | attrs: attrs, 433 | ngModelController: ngModelController, 434 | viewRegexTest: viewRegexTest, 435 | integerPart: integerPart, 436 | fractionPart: fractionPart, 437 | fractionSeparator: fractionSeparator, 438 | isPositiveNumber: isPositiveNumber, 439 | isNegativeNumber: isNegativeNumber, 440 | roundFunction: roundFunction, 441 | isThousandSeparator: isThousandSeparator, 442 | thousandSeparator: thousandSeparator, 443 | prepend: prepend, 444 | append: append, 445 | isFixed: isFixed 446 | } 447 | } 448 | 449 | function directiveParser(value, parameters, state) { 450 | var element = parameters.element; 451 | var attrs = parameters.attrs; 452 | var ngModelController = parameters.ngModelController; 453 | var viewRegexTest = parameters.viewRegexTest; 454 | var integerPart = parameters.integerPart; 455 | var fractionPart = parameters.fractionPart; 456 | var fractionSeparator = parameters.fractionSeparator; 457 | var isPositiveNumber = parameters.isPositiveNumber; 458 | var isNegativeNumber = parameters.isNegativeNumber; 459 | var roundFunction = parameters.roundFunction; 460 | var isThousandSeparator = parameters.isThousandSeparator; 461 | var thousandSeparator = parameters.thousandSeparator; 462 | var prepend = parameters.prepend; 463 | var append = parameters.append; 464 | var isFixed = parameters.isFixed; 465 | 466 | var parsedValue = String(value); 467 | 468 | if (wasPasted) { 469 | wasPasted = false; 470 | 471 | // Remove all characters which are not number-relevant 472 | var regex = new RegExp('[^' + ((isNegativeNumber) ? '-' : '') + 473 | fractionSeparator + thousandSeparator + '0-9]+', 'g'); 474 | parsedValue = parsedValue.replace(regex, ''); 475 | 476 | // Remove trailing separators 477 | regex = new RegExp('^[' + fractionSeparator + thousandSeparator + ']'); 478 | parsedValue = parsedValue.replace(regex, ''); 479 | 480 | // Replace separator if at fraction position 481 | regex = new RegExp('[' + fractionSeparator + thousandSeparator + 482 | ']([0-9]{' + fractionPart + '})$'); 483 | parsedValue = parsedValue.replace(regex, fractionSeparator + '$1'); 484 | } 485 | parsedValue = removePrependAppendChars(parsedValue, prepend, append); 486 | parsedValue = removeDoubledDecimalSeparators(parsedValue, parameters); 487 | if (new RegExp('^[\.,' + thousandSeparator + ']{2,}').test(parsedValue)) { 488 | changeViewValue(ngModelController, '', parameters, state); 489 | return null; 490 | } 491 | var cursorPosition = getCaretPosition(element[0]); 492 | if (prepend) { 493 | cursorPosition--; 494 | } 495 | var valBeforeCursor = parsedValue.slice(0, cursorPosition); 496 | valBeforeCursor = removeThousandSeparators(valBeforeCursor, 497 | thousandSeparator); 498 | parsedValue = removeThousandSeparators(parsedValue, thousandSeparator); 499 | valBeforeCursor = removeLeadingZero(valBeforeCursor); 500 | var beforeRemovingLeadingZero = parsedValue; 501 | parsedValue = removeLeadingZero(parsedValue); 502 | if (parsedValue === "0" + fractionSeparator && 503 | beforeRemovingLeadingZero === fractionSeparator && isPositiveNumber) { 504 | if (fractionPart) { 505 | changeViewValue(ngModelController, '' + fractionSeparator, 506 | parameters, state, true); 507 | setCaretPosition(element[0], 2); 508 | return null; 509 | } else { 510 | changeViewValue(ngModelController, '', parameters, state); 511 | return null; 512 | } 513 | } 514 | if (parsedValue === '' && String(value).charAt(0) === '0') { 515 | changeViewValue(ngModelController, '', parameters); 516 | return null; 517 | } 518 | if (parsedValue === undefined || parsedValue === '') { 519 | changeViewValue(ngModelController, '', parameters); 520 | return null; 521 | } 522 | if (parsedValue === '-') { 523 | if (isPositiveNumber && !isNegativeNumber) { 524 | changeViewValue(ngModelController, '', parameters, state); 525 | } else { 526 | changeViewValue(ngModelController, '-', parameters, state); 527 | } 528 | return null; 529 | } 530 | 531 | parsedValue = cutSurplusFractionPart(parsedValue, parameters); 532 | /** 533 | * view value failed 'correct view format' test 534 | * therefore view value is set from last correct model value (it must be formatted - change dot to comma) 535 | */ 536 | if (viewRegexTest.test(parsedValue) === false) { 537 | var modelValue = convModelToView(ngModelController.$modelValue, 538 | fractionSeparator, parameters); 539 | 540 | if (isThousandSeparator) { 541 | modelValue = addThousandSeparator(modelValue, fractionSeparator, 542 | thousandSeparator); 543 | } 544 | changeViewValue(ngModelController, modelValue, parameters, state); 545 | setCaretPosition(element[0], cursorPosition - 1); 546 | return ngModelController.$modelValue; 547 | } 548 | /** 549 | * view value success 'correct view format' test 550 | * therefore model value is set from correct view value (it must be formatter - change comma to dot) 551 | */ 552 | else { 553 | var dots = 0; 554 | var currentPosition = valBeforeCursor.length; 555 | if (isThousandSeparator) { 556 | parsedValue = addThousandSeparator(parsedValue, fractionSeparator, 557 | thousandSeparator); 558 | dots = countThousandSeparatorToPosition(parsedValue, 559 | thousandSeparator, currentPosition); 560 | } 561 | if (prepend) { 562 | dots++; 563 | if (new RegExp('^(\\-\\d)$').test(parsedValue)) { 564 | dots += 2; 565 | } 566 | if (new RegExp('^(\\d)$').test(parsedValue)) { 567 | dots++; 568 | } 569 | } 570 | changeViewValue(ngModelController, parsedValue, parameters, state); 571 | setCaretPosition(element[0], currentPosition + dots); 572 | return convViewToModel(parsedValue, fractionSeparator, 573 | thousandSeparator); 574 | } 575 | } 576 | 577 | function triggerParsers(ngModelController, value) { 578 | ngModelController.$setViewValue(''); 579 | ngModelController.$render(); 580 | ngModelController.$setViewValue(value); 581 | ngModelController.$render(); 582 | } 583 | 584 | function onPropertyWatch(ngModelController, initObject) { 585 | var value = filterModelValue( 586 | ngModelController.$modelValue, 587 | initObject.fractionPart, 588 | initObject.fractionSeparator, 589 | initObject.roundFunction, 590 | initObject.isFixed, 591 | initObject.isThousandSeparator, 592 | initObject.thousandSeparator, 593 | initObject.prepend, 594 | initObject.append 595 | ); 596 | triggerParsers(ngModelController, value); 597 | } 598 | 599 | function dynamicNumberDirective(dynamicNumberStrategy) { 600 | return { 601 | restrict: 'A', 602 | require: '?ngModel', 603 | scope: { 604 | awnum: "@", 605 | numInt: "@", 606 | numFract: "@", 607 | numSep: "@", 608 | numPos: "@", 609 | numNeg: "@", 610 | numRound: "@", 611 | numThousand: "@", 612 | numThousandSep: "@", 613 | numPrepend: "@", 614 | numAppend: "@", 615 | numFixed: "@" 616 | }, 617 | link: function(scope, element, attrs, ngModelController) { 618 | if (!element[0] || element[0].tagName !== 'INPUT' || (element[0].type !== 619 | 'text' && element[0].type !== 'tel')) { 620 | console.warn( 621 | 'Directive angular-dynamic-number works only for \'input\' tag with type = \'text\' or type = \'tel\'' 622 | ); 623 | return; 624 | } 625 | if (!ngModelController) { 626 | console.warn( 627 | 'Directive angular-dynamic-number need ngModel attribute'); 628 | return; 629 | } 630 | var initObject = initAllProperties( 631 | createPropertyObject(scope), 632 | element, 633 | attrs, 634 | ngModelController, 635 | dynamicNumberStrategy 636 | ); 637 | 638 | element.on('paste', function() { 639 | wasPasted = true; 640 | }); 641 | 642 | scope.$watch('numInt', function(newProperty, oldProperty) { 643 | if (oldProperty === newProperty) { 644 | return; 645 | } 646 | initObject = initAllProperties(createPropertyObject(scope, 647 | 'numInt', newProperty), element, attrs, 648 | ngModelController, dynamicNumberStrategy); 649 | onPropertyWatch(ngModelController, initObject); 650 | }); 651 | 652 | scope.$watch('numFract', function(newProperty, oldProperty) { 653 | if (oldProperty === newProperty) { 654 | return; 655 | } 656 | initObject = initAllProperties(createPropertyObject(scope, 657 | 'numFract', newProperty), element, attrs, 658 | ngModelController, dynamicNumberStrategy); 659 | onPropertyWatch(ngModelController, initObject); 660 | }); 661 | 662 | scope.$watch('numSep', function(newProperty, oldProperty) { 663 | if (oldProperty === newProperty) { 664 | return; 665 | } 666 | initObject = initAllProperties(createPropertyObject(scope, 667 | 'numSep', newProperty), element, attrs, 668 | ngModelController, dynamicNumberStrategy); 669 | onPropertyWatch(ngModelController, initObject); 670 | }); 671 | 672 | scope.$watch('numPos', function(newProperty, oldProperty) { 673 | if (oldProperty === newProperty) { 674 | return; 675 | } 676 | initObject = initAllProperties(createPropertyObject(scope, 677 | 'numPos', newProperty), element, attrs, 678 | ngModelController, dynamicNumberStrategy); 679 | onPropertyWatch(ngModelController, initObject); 680 | }); 681 | 682 | scope.$watch('numNeg', function(newProperty, oldProperty) { 683 | if (oldProperty === newProperty) { 684 | return; 685 | } 686 | initObject = initAllProperties(createPropertyObject(scope, 687 | 'numNeg', newProperty), element, attrs, 688 | ngModelController, dynamicNumberStrategy); 689 | onPropertyWatch(ngModelController, initObject); 690 | }); 691 | 692 | scope.$watch('numThousand', function(newProperty, oldProperty) { 693 | if (oldProperty === newProperty) { 694 | return; 695 | } 696 | initObject = initAllProperties(createPropertyObject(scope, 697 | 'numThousand', newProperty), element, attrs, 698 | ngModelController, dynamicNumberStrategy); 699 | onPropertyWatch(ngModelController, initObject); 700 | }); 701 | 702 | scope.$watch('numThousandSep', function(newProperty, oldProperty) { 703 | if (oldProperty === newProperty) { 704 | return; 705 | } 706 | initObject = initAllProperties(createPropertyObject(scope, 707 | 'numThousandSep', newProperty), element, attrs, 708 | ngModelController, dynamicNumberStrategy); 709 | onPropertyWatch(ngModelController, initObject); 710 | }); 711 | 712 | scope.$watch('numAppend', function(newProperty, oldProperty) { 713 | if (oldProperty === newProperty) { 714 | return; 715 | } 716 | initObject = initAllProperties(createPropertyObject(scope, 717 | 'numAppend', newProperty), element, attrs, 718 | ngModelController, dynamicNumberStrategy); 719 | onPropertyWatch(ngModelController, initObject); 720 | }); 721 | 722 | scope.$watch('numPrepend', function(newProperty, oldProperty) { 723 | if (oldProperty === newProperty) { 724 | return; 725 | } 726 | initObject = initAllProperties(createPropertyObject(scope, 727 | 'numPrepend', newProperty), element, attrs, 728 | ngModelController, dynamicNumberStrategy); 729 | onPropertyWatch(ngModelController, initObject); 730 | }); 731 | scope.$watch('numFixed', function(newProperty, oldProperty) { 732 | if (oldProperty === newProperty) { 733 | return; 734 | } 735 | initObject = initAllProperties(createPropertyObject(scope, 736 | 'numFixed', newProperty), element, attrs, 737 | ngModelController, dynamicNumberStrategy); 738 | onPropertyWatch(ngModelController, initObject); 739 | }); 740 | var state = { 741 | enable: true, 742 | count: 0 743 | }; 744 | ngModelController.$parsers.unshift(function(value) { 745 | if (state.enable) { 746 | state.count++; 747 | return prepareResponse(directiveParser(value, initObject, 748 | state)); 749 | } else { 750 | state.enable = true; 751 | return value; 752 | } 753 | }); 754 | /** 755 | * it is like filter, 756 | */ 757 | ngModelController.$formatters.push(function(value) { 758 | return filterModelValue( 759 | value, 760 | initObject.fractionPart, 761 | initObject.fractionSeparator, 762 | initObject.roundFunction, 763 | initObject.isFixed, 764 | initObject.isThousandSeparator, 765 | initObject.thousandSeparator, 766 | initObject.prepend, 767 | initObject.append 768 | ); 769 | }); 770 | } 771 | }; 772 | } 773 | 774 | var moduleName = 'dynamicNumber'; 775 | 776 | angular.module(moduleName, []) 777 | .provider('dynamicNumberStrategy', function() { 778 | var strategies = {}; 779 | 780 | this.addStrategy = addStrategy; 781 | this.getStrategy = getStrategy; 782 | this.getStrategies = getStrategies; 783 | 784 | var strategyProvider = { 785 | addStrategy: addStrategy, 786 | getStrategy: getStrategy, 787 | getStrategies: getStrategies 788 | }; 789 | 790 | this.$get = function() { 791 | return strategyProvider; 792 | }; 793 | 794 | function addStrategy(name, strategy) { 795 | strategies[name] = strategy; 796 | } 797 | 798 | function getStrategy(name) { 799 | return strategies[name]; 800 | } 801 | 802 | function getStrategies() { 803 | return strategies; 804 | } 805 | }) 806 | .filter('awnum', ['dynamicNumberStrategy', function( 807 | dynamicNumberStrategy) { 808 | return function(value, numFract, numSep, numRound, numFixed, 809 | numThousand, numThousandSep, numPrepend, numAppend) { 810 | var strategy = {}; 811 | var fractionPart; 812 | if (angular.isString(numFract)) { 813 | strategy = dynamicNumberStrategy.getStrategy(numFract); 814 | numFract = strategy.numFract; 815 | } 816 | var fractionPart = initFractionPart(numFract, 2); 817 | var fractionSeparator = initSeparator(numSep !== undefined ? 818 | numSep : strategy.numSep, '.'); 819 | var roundFunction = initRound(numRound !== undefined ? 820 | numRound : strategy.numRound, Math.round); 821 | var isFixed = initIsFixed(numFixed !== undefined ? numFixed : 822 | strategy.numFixed, false); 823 | var isThousandSeparator = initIsThousand(numThousand !== 824 | undefined ? numThousand : strategy.numThousand, false); 825 | var thousandSeparator = initThousandSeparator(numThousandSep !== 826 | undefined ? numThousandSep : strategy.numThousandSep, 827 | fractionSeparator, fractionSeparator === '.' ? ',' : '.'); 828 | var prepend = initNumAppendPrepend(numPrepend !== undefined ? 829 | numPrepend : strategy.numPrepend); 830 | var append = initNumAppendPrepend(numAppend !== undefined ? 831 | numAppend : strategy.numAppend); 832 | var filteredValue = filterModelValue(value, fractionPart, 833 | fractionSeparator, roundFunction, isFixed, 834 | isThousandSeparator, thousandSeparator, prepend, append); 835 | if (filteredValue === '') { 836 | return '0'; 837 | } 838 | return filteredValue; 839 | }; 840 | }]) 841 | .directive('awnum', ['dynamicNumberStrategy', dynamicNumberDirective]); 842 | 843 | return moduleName; 844 | })); 845 | -------------------------------------------------------------------------------- /release/dynamic-number.min.js: -------------------------------------------------------------------------------- 1 | !function(n,e){"use strict";if("function"==typeof define&&define.amd)define(["angular"],e);else{if("undefined"==typeof module||"object"!=typeof module.exports)return e(n.angular);module.exports=e(require("angular"))}}(window,function(n){"use strict";function e(n,e,t,r){if(void 0===n||null===n||""===n)return"";var u="";return u=","===e?String(n).replace(".",","):String(n)}function t(n,e,t){return","===e?String(n).replace(/['\.\s]/g,"").replace(",","."):"."===e?String(n).replace(/[',\s]/g,""):void 0}function r(n,e,t){var r=n;return t&&(r+=t),e&&(r=/^\-.+/.test(r)?r.replace("-","-"+e):/^\-/.test(r)?r:e+r),r}function u(n,e){if(n>=0){var t=parseInt(n,10);if(isNaN(t)===!1&&isFinite(t)&&t>=0)return t}return e}function a(n,e){if(n>=0){var t=parseInt(n,10);if(isNaN(t)===!1&&isFinite(t)&&t>=0)return t}return e}function i(n,e){return","===n?",":"."===n?".":e}function o(n,e){return"false"!==n&&n!==!1&&("true"===n||n===!0||e)}function c(n,e){return"false"!==n&&n!==!1&&("true"===n||n===!0||e)}function d(n,e){return"floor"===n?Math.floor:"ceil"===n?Math.ceil:"round"===n?Math.round:e}function p(n,e){return"false"!==n&&n!==!1&&("true"===n||n===!0||e)}function m(n,e){return"false"!==n&&n!==!1&&("true"===n||n===!0||e)}function s(n,e,t){if(!n)return t;var r;return r="."===e?new RegExp("^[',\\s]$"):new RegExp("^['\\.\\s]$"),r.test(n)?n:t}function f(n){var e=new RegExp("[^\\d,\\.\\s\\-]{1}");return e.test(n)?n:null}function l(n,e,t,r,u){var a="-?";r===!1&&u===!0?a="-":r===!0&&u===!1&&(a="");var i="[0-9]{0,"+n+"}";0===n&&(i="0");var o="(\\"+t+"([0-9]){0,"+e+"})";return 0===e&&(o=""),new RegExp("^"+a+i+o+"?$")}function g(n){return String(n).replace(/^(-?)(0+)$/g,"$10").replace(/^0(\d+)/g,"$1").replace(/^-0(\d+)/g,"-$1").replace(new RegExp("^-([\\.,\\s])","g"),"-0$1").replace(new RegExp("^[\\.,\\s]","g"),"0$&")}function v(n,e,t){var r=n;return e&&(r=r.replace(new RegExp("[\\"+e+"]","g"),"")),t&&(r=r.replace(new RegExp("[\\"+t+"]","g"),"")),r}function S(n,e){return"."===e?String(n).replace(/\./g,""):","===e?String(n).replace(/,/g,""):String(n).replace(new RegExp("['\\s]","g"),"")}function h(n,e,t){return n=String(n).split(e),n[0]=n[0].replace(/\B(?=(\d{3})+(?!\d))/g,t),n.join(e)}function w(n,e){var t=n;if(e.isFixed){var r=t.split(e.fractionSeparator)[1],u=r&&r.length?r.length:0;0===u&&(t+=e.fractionSeparator);for(var a=u;a=t)break}else u++;return u}function R(n,e){var t=n,r=t.split(e.fractionSeparator),u=r[0],a=r[1];return a&&a.length>e.fractionPart&&(a=a.slice(0,e.fractionPart),t=[u,a].join(e.fractionSeparator)),t}function T(n){return null===n?null:Number(n)}function b(n,e,t){var r={awnum:n.awnum,numInt:n.numInt,numFract:n.numFract,numSep:n.numSep,numPos:n.numPos,numNeg:n.numNeg,numRound:n.numRound,numThousand:n.numThousand,numThousandSep:n.numThousandSep,numPrepend:n.numPrepend,numAppend:n.numAppend,numFixed:n.numFixed};return e&&(r[e]=t),r}function y(n,e){return n.replace(new RegExp("[\\"+e.fractionSeparator+"]+","g"),e.fractionSeparator)}function E(n,e,t,r,g){var v={};n.awnum&&(v=g.getStrategy(n.awnum));var S=u(void 0!==n.numInt?n.numInt:v.numInt,6),h=a(void 0!==n.numFract?n.numFract:v.numFract,2),w=i(void 0!==n.numSep?n.numSep:v.numSep,"."),x=o(void 0!==n.numPos?n.numPos:v.numPos,!0),$=c(void 0!==n.numNeg?n.numNeg:v.numNeg,!0),F=d(void 0!==n.numRound?n.numRound:v.numRound,Math.round),N=m(void 0!==n.numThousand?n.numThousand:v.numThousand,!1),P=s(void 0!==n.numThousandSep?n.numThousandSep:v.numThousandSep,w,"."===w?",":"."),R=f(void 0!==n.numPrepend?n.numPrepend:v.numPrepend),T=f(void 0!==n.numAppend?n.numAppend:v.numAppend),b=p(void 0!==n.numFixed?n.numFixed:v.numFixed,!1);if(x===!1&&$===!1)throw new Error("Number is set to not be positive and not be negative. Change num_pos attr or/and num_neg attr to true");var y=l(S,h,w,x,$);return{element:e,attrs:t,ngModelController:r,viewRegexTest:y,integerPart:S,fractionPart:h,fractionSeparator:w,isPositiveNumber:x,isNegativeNumber:$,roundFunction:F,isThousandSeparator:N,thousandSeparator:P,prepend:R,append:T,isFixed:b}}function A(n,r,u){var a=r.element,i=(r.attrs,r.ngModelController),o=r.viewRegexTest,c=(r.integerPart,r.fractionPart),d=r.fractionSeparator,p=r.isPositiveNumber,m=r.isNegativeNumber,s=(r.roundFunction,r.isThousandSeparator),f=r.thousandSeparator,l=r.prepend,w=r.append,$=(r.isFixed,String(n));if(j){j=!1;var T=new RegExp("[^"+(m?"-":"")+d+f+"0-9]+","g");$=$.replace(T,""),T=new RegExp("^["+d+f+"]"),$=$.replace(T,""),T=new RegExp("["+d+f+"]([0-9]{"+c+"})$"),$=$.replace(T,d+"$1")}if($=v($,l,w),$=y($,r),new RegExp("^[.,"+f+"]{2,}").test($))return x(i,"",r,u),null;var b=F(a[0]);l&&b--;var E=$.slice(0,b);E=S(E,f),$=S($,f),E=g(E);var A=$;if($=g($),$==="0"+d&&A===d&&p)return c?(x(i,""+d,r,u,!0),N(a[0],2),null):(x(i,"",r,u),null);if(""===$&&"0"===String(n).charAt(0))return x(i,"",r),null;if(void 0===$||""===$)return x(i,"",r),null;if("-"===$)return p&&!m?x(i,"",r,u):x(i,"-",r,u),null;if($=R($,r),o.test($)===!1){var I=e(i.$modelValue,d,r);return s&&(I=h(I,d,f)),x(i,I,r,u),N(a[0],b-1),i.$modelValue}var M=0,V=E.length;return s&&($=h($,d,f),M=P($,f,V)),l&&(M++,new RegExp("^(\\-\\d)$").test($)&&(M+=2),new RegExp("^(\\d)$").test($)&&M++),x(i,$,r,u),N(a[0],V+M),t($,d,f)}function I(n,e){n.$setViewValue(""),n.$render(),n.$setViewValue(e),n.$render()}function M(n,e){var t=$(n.$modelValue,e.fractionPart,e.fractionSeparator,e.roundFunction,e.isFixed,e.isThousandSeparator,e.thousandSeparator,e.prepend,e.append);I(n,t)}function V(n){return{restrict:"A",require:"?ngModel",scope:{awnum:"@",numInt:"@",numFract:"@",numSep:"@",numPos:"@",numNeg:"@",numRound:"@",numThousand:"@",numThousandSep:"@",numPrepend:"@",numAppend:"@",numFixed:"@"},link:function(e,t,r,u){if(!t[0]||"INPUT"!==t[0].tagName||"text"!==t[0].type&&"tel"!==t[0].type)return void console.warn("Directive angular-dynamic-number works only for 'input' tag with type = 'text' or type = 'tel'");if(!u)return void console.warn("Directive angular-dynamic-number need ngModel attribute");var a=E(b(e),t,r,u,n);t.on("paste",function(){j=!0}),e.$watch("numInt",function(i,o){o!==i&&(a=E(b(e,"numInt",i),t,r,u,n),M(u,a))}),e.$watch("numFract",function(i,o){o!==i&&(a=E(b(e,"numFract",i),t,r,u,n),M(u,a))}),e.$watch("numSep",function(i,o){o!==i&&(a=E(b(e,"numSep",i),t,r,u,n),M(u,a))}),e.$watch("numPos",function(i,o){o!==i&&(a=E(b(e,"numPos",i),t,r,u,n),M(u,a))}),e.$watch("numNeg",function(i,o){o!==i&&(a=E(b(e,"numNeg",i),t,r,u,n),M(u,a))}),e.$watch("numThousand",function(i,o){o!==i&&(a=E(b(e,"numThousand",i),t,r,u,n),M(u,a))}),e.$watch("numThousandSep",function(i,o){o!==i&&(a=E(b(e,"numThousandSep",i),t,r,u,n),M(u,a))}),e.$watch("numAppend",function(i,o){o!==i&&(a=E(b(e,"numAppend",i),t,r,u,n),M(u,a))}),e.$watch("numPrepend",function(i,o){o!==i&&(a=E(b(e,"numPrepend",i),t,r,u,n),M(u,a))}),e.$watch("numFixed",function(i,o){o!==i&&(a=E(b(e,"numFixed",i),t,r,u,n),M(u,a))});var i={enable:!0,count:0};u.$parsers.unshift(function(n){return i.enable?(i.count++,T(A(n,a,i))):(i.enable=!0,n)}),u.$formatters.push(function(n){return $(n,a.fractionPart,a.fractionSeparator,a.roundFunction,a.isFixed,a.isThousandSeparator,a.thousandSeparator,a.prepend,a.append)})}}}var j=!1,k="dynamicNumber";return n.module(k,[]).provider("dynamicNumberStrategy",function(){function n(n,e){r[n]=e}function e(n){return r[n]}function t(){return r}var r={};this.addStrategy=n,this.getStrategy=e,this.getStrategies=t;var u={addStrategy:n,getStrategy:e,getStrategies:t};this.$get=function(){return u}}).filter("awnum",["dynamicNumberStrategy",function(e){return function(t,r,u,o,c,l,g,v,S){var h,w={};n.isString(r)&&(w=e.getStrategy(r),r=w.numFract);var h=a(r,2),x=i(void 0!==u?u:w.numSep,"."),F=d(void 0!==o?o:w.numRound,Math.round),N=p(void 0!==c?c:w.numFixed,!1),P=m(void 0!==l?l:w.numThousand,!1),R=s(void 0!==g?g:w.numThousandSep,x,"."===x?",":"."),T=f(void 0!==v?v:w.numPrepend),b=f(void 0!==S?S:w.numAppend),y=$(t,h,x,F,N,P,R,T,b);return""===y?"0":y}}]).directive("awnum",["dynamicNumberStrategy",V]),k}); -------------------------------------------------------------------------------- /src/numberDirective.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | (function(root, factory) { 3 | "use strict"; 4 | 5 | if (typeof define === 'function' && define.amd) { 6 | define(['angular'], factory); 7 | 8 | } else if (typeof module !== 'undefined' && typeof module.exports === 9 | 'object') { 10 | module.exports = factory(require('angular')); 11 | 12 | } else { 13 | return factory(root.angular); 14 | } 15 | }(window, function(angular) { 16 | 'use strict'; 17 | 18 | var wasPasted = false; 19 | 20 | function convModelToView(modelValue, viewSeparator, prepend, append) { 21 | if (modelValue === undefined || modelValue === null || modelValue === 22 | "") { 23 | return ""; 24 | } 25 | var newViewValue = ''; 26 | if (viewSeparator === ',') { 27 | newViewValue = String(modelValue).replace(".", ","); 28 | } else { 29 | newViewValue = String(modelValue); 30 | } 31 | return newViewValue; 32 | } 33 | 34 | function convViewToModel(viewValue, viewSeparator, thousandSeparator) { 35 | 36 | if (viewSeparator === ',') { 37 | return String(viewValue).replace(/['\.\s]/g, "").replace(",", "."); 38 | } else if (viewSeparator === '.') { 39 | return String(viewValue).replace(/[',\s]/g, ""); 40 | } 41 | } 42 | 43 | function addPrependAppend(value, prepend, append) { 44 | var newViewValue = value; 45 | if (append) { 46 | newViewValue = newViewValue + append; 47 | } 48 | if (prepend) { 49 | if (/^\-.+/.test(newViewValue)) { 50 | newViewValue = newViewValue.replace('-', '-' + prepend); 51 | } else if (/^\-/.test(newViewValue)) { 52 | newViewValue = newViewValue; 53 | } else { 54 | newViewValue = prepend + newViewValue; 55 | } 56 | } 57 | return newViewValue; 58 | } 59 | 60 | function initIntegerPart(attrs_num_int, def_num_int) { 61 | if (attrs_num_int >= 0) { 62 | var _num_int = parseInt(attrs_num_int, 10); 63 | if (isNaN(_num_int) === false && isFinite(_num_int) && _num_int >= 0) { 64 | return _num_int; 65 | } 66 | } 67 | return def_num_int; 68 | } 69 | 70 | function initFractionPart(attrs_num_fract, def_num_fract) { 71 | if (attrs_num_fract >= 0) { 72 | var _num_fract = parseInt(attrs_num_fract, 10); 73 | if (isNaN(_num_fract) === false && isFinite(_num_fract) && _num_fract >= 74 | 0) { 75 | return _num_fract; 76 | } 77 | } 78 | return def_num_fract; 79 | } 80 | 81 | function initSeparator(attrs_num_sep, def_num_sep) { 82 | if (attrs_num_sep === ',') { 83 | return ','; 84 | } else if (attrs_num_sep === '.') { 85 | return '.'; 86 | } 87 | return def_num_sep; 88 | } 89 | 90 | function initIsPositive(attrs_num_pos, def_num_pos) { 91 | if (attrs_num_pos === 'false' || attrs_num_pos === false) { 92 | return false; 93 | } else if (attrs_num_pos === 'true' || attrs_num_pos === true) { 94 | return true; 95 | } 96 | return def_num_pos; 97 | } 98 | 99 | function initIsNegative(attrs_num_neg, def_num_neg) { 100 | if (attrs_num_neg === 'false' || attrs_num_neg === false) { 101 | return false; 102 | } else if (attrs_num_neg === 'true' || attrs_num_neg === true) { 103 | return true; 104 | } 105 | return def_num_neg; 106 | } 107 | 108 | function initRound(attrs_round, def_round) { 109 | if (attrs_round === 'floor') { 110 | return Math.floor; 111 | } else if (attrs_round === 'ceil') { 112 | return Math.ceil; 113 | } else if (attrs_round === 'round') { 114 | return Math.round; 115 | } 116 | return def_round; 117 | } 118 | 119 | function initIsFixed(attrs_fixed, def_fixed) { 120 | if (attrs_fixed === 'false' || attrs_fixed === false) { 121 | return false; 122 | } else if (attrs_fixed === 'true' || attrs_fixed === true) { 123 | return true; 124 | } 125 | return def_fixed; 126 | } 127 | 128 | function initIsThousand(attrs_thousand, def_thousand) { 129 | if (attrs_thousand === 'false' || attrs_thousand === false) { 130 | return false; 131 | } else if (attrs_thousand === 'true' || attrs_thousand === true) { 132 | return true; 133 | } 134 | return def_thousand; 135 | } 136 | 137 | function initThousandSeparator(attrs_thousand, fractionSeparator, 138 | def_thousand) { 139 | if (!attrs_thousand) { 140 | return def_thousand; 141 | } 142 | var regexp; 143 | if (fractionSeparator === '.') { 144 | regexp = new RegExp('^[\',\\s]$'); 145 | } else { 146 | regexp = new RegExp('^[\'\\.\\s]$'); 147 | } 148 | if (regexp.test(attrs_thousand)) { 149 | return attrs_thousand; 150 | } else { 151 | return def_thousand; 152 | } 153 | } 154 | 155 | function initNumAppendPrepend(attrs_num_char) { 156 | var regexp = new RegExp('[^\\d,\\.\\s\\-]{1}'); 157 | if (regexp.test(attrs_num_char)) { 158 | return attrs_num_char; 159 | } 160 | return null; 161 | } 162 | 163 | 164 | 165 | function buildRegexp(integerPart, fractionPart, fractionSeparator, 166 | isPositiveNumber, isNegativeNumber) { 167 | var negativeRegex = '-?'; 168 | if (isPositiveNumber === false && isNegativeNumber === true) { 169 | negativeRegex = '-'; 170 | } else if (isPositiveNumber === true && isNegativeNumber === false) { 171 | negativeRegex = ''; 172 | } 173 | var intRegex = '[0-9]{0,' + (integerPart) + '}'; 174 | if (integerPart === 0) { 175 | intRegex = '0'; 176 | } 177 | var fractRegex = '(\\' + fractionSeparator + '([0-9]){0,' + 178 | fractionPart + '})'; 179 | if (fractionPart === 0) { 180 | fractRegex = ''; 181 | } 182 | return new RegExp('^' + negativeRegex + intRegex + fractRegex + '?$'); 183 | } 184 | 185 | function removeLeadingZero(value) { 186 | return String(value) 187 | .replace(/^(-?)(0+)$/g, "$10") //change 00000 to '0' and -00000 to '-0' 188 | .replace(/^0(\d+)/g, "$1") //change 000001 to '1' 189 | .replace(/^-0(\d+)/g, "-$1") //change -013212 to -0 190 | .replace(new RegExp('^-([\\.,\\s])', 'g'), "-0$1") //change -. to -0. 191 | .replace(new RegExp('^[\\.,\\s]', 'g'), "0$&"); //change . to 0. 192 | } 193 | 194 | function removePrependAppendChars(value, prepend, append) { 195 | var newValue = value; 196 | if (prepend) { 197 | newValue = newValue.replace(new RegExp('[\\' + prepend + ']', 'g'), 198 | ''); 199 | } 200 | if (append) { 201 | newValue = newValue.replace(new RegExp('[\\' + append + ']', 'g'), ''); 202 | } 203 | return newValue; 204 | } 205 | 206 | function removeThousandSeparators(value, thousandSeparator) { 207 | if (thousandSeparator === '.') { 208 | return String(value).replace(/\./g, ""); 209 | } else if (thousandSeparator === ',') { 210 | return String(value).replace(/,/g, ""); 211 | } else { 212 | return String(value).replace(new RegExp('[\'\\s]', 'g'), ""); 213 | } 214 | } 215 | 216 | function addThousandSeparator(value, fractionSeparator, thousandSeparator) { 217 | value = String(value).split(fractionSeparator); 218 | value[0] = value[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator); 219 | return value.join(fractionSeparator); 220 | } 221 | 222 | function addFixedZeros(value, parameters) { 223 | var newValue = value; 224 | if (parameters.isFixed) { 225 | var fractionPart = newValue.split(parameters.fractionSeparator)[1]; 226 | var fractionLength = (fractionPart && fractionPart.length) ? 227 | fractionPart.length : 0; 228 | if (fractionLength === 0) { 229 | newValue += parameters.fractionSeparator; 230 | } 231 | for (var i = fractionLength; i < parameters.fractionPart; i++) { 232 | newValue += "0"; 233 | } 234 | } 235 | return newValue; 236 | } 237 | 238 | function changeViewValue(ngModelController, value, parameters, state, 239 | disable) { 240 | 241 | if (disable) { 242 | state.enable = false; 243 | } 244 | var valueString = String(value); 245 | var valueWithFixedZeros = addFixedZeros(valueString, parameters); 246 | var valueWithPrependAppend = addPrependAppend(valueWithFixedZeros, 247 | parameters.prepend, parameters.append); 248 | // https://github.com/angular/angular.js/issues/13068 249 | // ngModelController.$viewValue = value; 250 | var version = angular.version; 251 | if (version.major === 1 && version.minor === 2) { 252 | ngModelController.$viewValue = valueWithPrependAppend; 253 | } else { 254 | ngModelController.$setViewValue(valueWithPrependAppend); 255 | } 256 | ngModelController.$render(); 257 | } 258 | 259 | function filterModelValue( 260 | value, 261 | fractionPart, 262 | fractionSeparator, 263 | roundFunction, 264 | numFixed, 265 | isThousandSeparator, 266 | thousandSeparator, 267 | prepend, 268 | append 269 | ) { 270 | if (value === '' || value === undefined || value === null) { 271 | return ''; 272 | } 273 | value = Number(value); 274 | if (!isNaN(value) && isFinite(value)) { 275 | var powerOfTen = Math.pow(10, fractionPart); 276 | if (numFixed) { 277 | value = convModelToView((roundFunction(value * powerOfTen) / 278 | powerOfTen).toFixed(fractionPart), fractionSeparator, prepend, 279 | append); 280 | } else { 281 | value = convModelToView(String(roundFunction(value * powerOfTen) / 282 | powerOfTen), fractionSeparator, prepend, append); 283 | } 284 | value = addPrependAppend(value, prepend, append); 285 | if (isThousandSeparator) { 286 | value = addThousandSeparator(value, fractionSeparator, 287 | thousandSeparator); 288 | } 289 | return value; 290 | } 291 | if (numFixed) { 292 | return (0).toFixed(fractionPart); 293 | } else { 294 | return "0"; 295 | } 296 | } 297 | /** 298 | * from this source: 299 | * http://stackoverflow.com/a/2897229/4138339 300 | */ 301 | function getCaretPosition(oField) { 302 | var iCaretPos = 0; 303 | if (document.selection) { 304 | oField.focus(); 305 | var oSel = document.selection.createRange(); 306 | oSel.moveStart('character', -oField.value.length); 307 | iCaretPos = oSel.text.length; 308 | } else if (oField.selectionStart || oField.selectionStart == '0') 309 | iCaretPos = oField.selectionDirection == 'backward' ? oField.selectionStart : 310 | oField.selectionEnd; 311 | return (iCaretPos); 312 | } 313 | /** 314 | * from this source 315 | * http://stackoverflow.com/a/22574572/4138339 316 | */ 317 | function setCaretPosition(elem, caretPos) { 318 | if (elem !== null) { 319 | if (elem.createTextRange) { 320 | var range = elem.createTextRange(); 321 | range.move('character', caretPos); 322 | range.select(); 323 | } else { 324 | if (elem.selectionStart) { 325 | elem.focus(); 326 | elem.setSelectionRange(caretPos, caretPos); 327 | } else 328 | elem.focus(); 329 | } 330 | } 331 | } 332 | 333 | function countThousandSeparatorToPosition(value, separator, position) { 334 | var countPosition = 0; 335 | var countDots = 0; 336 | for (var i = 0; i < value.length; i++) { 337 | if (value[i] !== separator) { 338 | countPosition++; 339 | if (countPosition >= position) break; 340 | } else { 341 | countDots++; 342 | } 343 | } 344 | return countDots; 345 | } 346 | 347 | function cutSurplusFractionPart(value, parameters) { 348 | 349 | var newValue = value; 350 | var splitedValue = newValue.split(parameters.fractionSeparator); 351 | var integerPart = splitedValue[0]; 352 | var fractionPart = splitedValue[1]; 353 | if (fractionPart && fractionPart.length > parameters.fractionPart) { 354 | fractionPart = fractionPart.slice(0, parameters.fractionPart); 355 | newValue = [integerPart, fractionPart].join(parameters.fractionSeparator); 356 | } 357 | return newValue; 358 | } 359 | 360 | function prepareResponse(value) { 361 | if (value === null) { 362 | return null; 363 | } else { 364 | return Number(value); 365 | } 366 | } 367 | 368 | function createPropertyObject(scope, key, value) { 369 | var properties = { 370 | awnum: scope.awnum, 371 | numInt: scope.numInt, 372 | numFract: scope.numFract, 373 | numSep: scope.numSep, 374 | numPos: scope.numPos, 375 | numNeg: scope.numNeg, 376 | numRound: scope.numRound, 377 | numThousand: scope.numThousand, 378 | numThousandSep: scope.numThousandSep, 379 | numPrepend: scope.numPrepend, 380 | numAppend: scope.numAppend, 381 | numFixed: scope.numFixed 382 | }; 383 | if (key) { 384 | properties[key] = value; 385 | } 386 | return properties; 387 | } 388 | 389 | function removeDoubledDecimalSeparators(value, parameters) { 390 | return value.replace(new RegExp("[\\" + parameters.fractionSeparator + 391 | "]+", "g"), parameters.fractionSeparator); 392 | } 393 | 394 | function initAllProperties(properties, element, attrs, ngModelController, 395 | dynamicNumberStrategy) { 396 | var strategy = {}; 397 | if (properties.awnum) { 398 | strategy = dynamicNumberStrategy.getStrategy(properties.awnum); 399 | } 400 | var integerPart = initIntegerPart(properties.numInt !== undefined ? 401 | properties.numInt : strategy.numInt, 6); 402 | var fractionPart = initFractionPart(properties.numFract !== undefined ? 403 | properties.numFract : strategy.numFract, 2); 404 | var fractionSeparator = initSeparator(properties.numSep !== undefined ? 405 | properties.numSep : strategy.numSep, '.'); 406 | var isPositiveNumber = initIsPositive(properties.numPos !== undefined ? 407 | properties.numPos : strategy.numPos, true); 408 | var isNegativeNumber = initIsNegative(properties.numNeg !== undefined ? 409 | properties.numNeg : strategy.numNeg, true); 410 | var roundFunction = initRound(properties.numRound !== undefined ? 411 | properties.numRound : strategy.numRound, Math.round); 412 | var isThousandSeparator = initIsThousand(properties.numThousand !== 413 | undefined ? properties.numThousand : strategy.numThousand, false); 414 | var thousandSeparator = initThousandSeparator(properties.numThousandSep !== 415 | undefined ? properties.numThousandSep : strategy.numThousandSep, 416 | fractionSeparator, fractionSeparator === '.' ? ',' : '.'); 417 | var prepend = initNumAppendPrepend(properties.numPrepend !== undefined ? 418 | properties.numPrepend : strategy.numPrepend); 419 | var append = initNumAppendPrepend(properties.numAppend !== undefined ? 420 | properties.numAppend : strategy.numAppend); 421 | var isFixed = initIsFixed(properties.numFixed !== undefined ? 422 | properties.numFixed : strategy.numFixed, false); 423 | if (isPositiveNumber === false && isNegativeNumber === false) { 424 | throw new Error( 425 | 'Number is set to not be positive and not be negative. Change num_pos attr or/and num_neg attr to true' 426 | ); 427 | } 428 | var viewRegexTest = buildRegexp(integerPart, fractionPart, 429 | fractionSeparator, isPositiveNumber, isNegativeNumber); 430 | return { 431 | element: element, 432 | attrs: attrs, 433 | ngModelController: ngModelController, 434 | viewRegexTest: viewRegexTest, 435 | integerPart: integerPart, 436 | fractionPart: fractionPart, 437 | fractionSeparator: fractionSeparator, 438 | isPositiveNumber: isPositiveNumber, 439 | isNegativeNumber: isNegativeNumber, 440 | roundFunction: roundFunction, 441 | isThousandSeparator: isThousandSeparator, 442 | thousandSeparator: thousandSeparator, 443 | prepend: prepend, 444 | append: append, 445 | isFixed: isFixed 446 | } 447 | } 448 | 449 | function directiveParser(value, parameters, state) { 450 | var element = parameters.element; 451 | var attrs = parameters.attrs; 452 | var ngModelController = parameters.ngModelController; 453 | var viewRegexTest = parameters.viewRegexTest; 454 | var integerPart = parameters.integerPart; 455 | var fractionPart = parameters.fractionPart; 456 | var fractionSeparator = parameters.fractionSeparator; 457 | var isPositiveNumber = parameters.isPositiveNumber; 458 | var isNegativeNumber = parameters.isNegativeNumber; 459 | var roundFunction = parameters.roundFunction; 460 | var isThousandSeparator = parameters.isThousandSeparator; 461 | var thousandSeparator = parameters.thousandSeparator; 462 | var prepend = parameters.prepend; 463 | var append = parameters.append; 464 | var isFixed = parameters.isFixed; 465 | 466 | var parsedValue = String(value); 467 | 468 | if (wasPasted) { 469 | wasPasted = false; 470 | 471 | // Remove all characters which are not number-relevant 472 | var regex = new RegExp('[^' + ((isNegativeNumber) ? '-' : '') + 473 | fractionSeparator + thousandSeparator + '0-9]+', 'g'); 474 | parsedValue = parsedValue.replace(regex, ''); 475 | 476 | // Remove trailing separators 477 | regex = new RegExp('^[' + fractionSeparator + thousandSeparator + ']'); 478 | parsedValue = parsedValue.replace(regex, ''); 479 | 480 | // Replace separator if at fraction position 481 | regex = new RegExp('[' + fractionSeparator + thousandSeparator + 482 | ']([0-9]{' + fractionPart + '})$'); 483 | parsedValue = parsedValue.replace(regex, fractionSeparator + '$1'); 484 | } 485 | parsedValue = removePrependAppendChars(parsedValue, prepend, append); 486 | parsedValue = removeDoubledDecimalSeparators(parsedValue, parameters); 487 | if (new RegExp('^[\.,' + thousandSeparator + ']{2,}').test(parsedValue)) { 488 | changeViewValue(ngModelController, '', parameters, state); 489 | return null; 490 | } 491 | var cursorPosition = getCaretPosition(element[0]); 492 | if (prepend) { 493 | cursorPosition--; 494 | } 495 | var valBeforeCursor = parsedValue.slice(0, cursorPosition); 496 | valBeforeCursor = removeThousandSeparators(valBeforeCursor, 497 | thousandSeparator); 498 | parsedValue = removeThousandSeparators(parsedValue, thousandSeparator); 499 | valBeforeCursor = removeLeadingZero(valBeforeCursor); 500 | var beforeRemovingLeadingZero = parsedValue; 501 | parsedValue = removeLeadingZero(parsedValue); 502 | if (parsedValue === "0" + fractionSeparator && 503 | beforeRemovingLeadingZero === fractionSeparator && isPositiveNumber) { 504 | if (fractionPart) { 505 | changeViewValue(ngModelController, '' + fractionSeparator, 506 | parameters, state, true); 507 | setCaretPosition(element[0], 2); 508 | return null; 509 | } else { 510 | changeViewValue(ngModelController, '', parameters, state); 511 | return null; 512 | } 513 | } 514 | if (parsedValue === '' && String(value).charAt(0) === '0') { 515 | changeViewValue(ngModelController, '', parameters); 516 | return null; 517 | } 518 | if (parsedValue === undefined || parsedValue === '') { 519 | changeViewValue(ngModelController, '', parameters); 520 | return null; 521 | } 522 | if (parsedValue === '-') { 523 | if (isPositiveNumber && !isNegativeNumber) { 524 | changeViewValue(ngModelController, '', parameters, state); 525 | } else { 526 | changeViewValue(ngModelController, '-', parameters, state); 527 | } 528 | return null; 529 | } 530 | 531 | parsedValue = cutSurplusFractionPart(parsedValue, parameters); 532 | /** 533 | * view value failed 'correct view format' test 534 | * therefore view value is set from last correct model value (it must be formatted - change dot to comma) 535 | */ 536 | if (viewRegexTest.test(parsedValue) === false) { 537 | var modelValue = convModelToView(ngModelController.$modelValue, 538 | fractionSeparator, parameters); 539 | 540 | if (isThousandSeparator) { 541 | modelValue = addThousandSeparator(modelValue, fractionSeparator, 542 | thousandSeparator); 543 | } 544 | changeViewValue(ngModelController, modelValue, parameters, state); 545 | setCaretPosition(element[0], cursorPosition - 1); 546 | return ngModelController.$modelValue; 547 | } 548 | /** 549 | * view value success 'correct view format' test 550 | * therefore model value is set from correct view value (it must be formatter - change comma to dot) 551 | */ 552 | else { 553 | var dots = 0; 554 | var currentPosition = valBeforeCursor.length; 555 | if (isThousandSeparator) { 556 | parsedValue = addThousandSeparator(parsedValue, fractionSeparator, 557 | thousandSeparator); 558 | dots = countThousandSeparatorToPosition(parsedValue, 559 | thousandSeparator, currentPosition); 560 | } 561 | if (prepend) { 562 | dots++; 563 | if (new RegExp('^(\\-\\d)$').test(parsedValue)) { 564 | dots += 2; 565 | } 566 | if (new RegExp('^(\\d)$').test(parsedValue)) { 567 | dots++; 568 | } 569 | } 570 | changeViewValue(ngModelController, parsedValue, parameters, state); 571 | setCaretPosition(element[0], currentPosition + dots); 572 | return convViewToModel(parsedValue, fractionSeparator, 573 | thousandSeparator); 574 | } 575 | } 576 | 577 | function triggerParsers(ngModelController, value) { 578 | ngModelController.$setViewValue(''); 579 | ngModelController.$render(); 580 | ngModelController.$setViewValue(value); 581 | ngModelController.$render(); 582 | } 583 | 584 | function onPropertyWatch(ngModelController, initObject) { 585 | var value = filterModelValue( 586 | ngModelController.$modelValue, 587 | initObject.fractionPart, 588 | initObject.fractionSeparator, 589 | initObject.roundFunction, 590 | initObject.isFixed, 591 | initObject.isThousandSeparator, 592 | initObject.thousandSeparator, 593 | initObject.prepend, 594 | initObject.append 595 | ); 596 | triggerParsers(ngModelController, value); 597 | } 598 | 599 | function dynamicNumberDirective(dynamicNumberStrategy) { 600 | return { 601 | restrict: 'A', 602 | require: '?ngModel', 603 | scope: { 604 | awnum: "@", 605 | numInt: "@", 606 | numFract: "@", 607 | numSep: "@", 608 | numPos: "@", 609 | numNeg: "@", 610 | numRound: "@", 611 | numThousand: "@", 612 | numThousandSep: "@", 613 | numPrepend: "@", 614 | numAppend: "@", 615 | numFixed: "@" 616 | }, 617 | link: function(scope, element, attrs, ngModelController) { 618 | if (!element[0] || element[0].tagName !== 'INPUT' || (element[0].type !== 619 | 'text' && element[0].type !== 'tel')) { 620 | console.warn( 621 | 'Directive angular-dynamic-number works only for \'input\' tag with type = \'text\' or type = \'tel\'' 622 | ); 623 | return; 624 | } 625 | if (!ngModelController) { 626 | console.warn( 627 | 'Directive angular-dynamic-number need ngModel attribute'); 628 | return; 629 | } 630 | var initObject = initAllProperties( 631 | createPropertyObject(scope), 632 | element, 633 | attrs, 634 | ngModelController, 635 | dynamicNumberStrategy 636 | ); 637 | 638 | element.on('paste', function() { 639 | wasPasted = true; 640 | }); 641 | 642 | scope.$watch('numInt', function(newProperty, oldProperty) { 643 | if (oldProperty === newProperty) { 644 | return; 645 | } 646 | initObject = initAllProperties(createPropertyObject(scope, 647 | 'numInt', newProperty), element, attrs, 648 | ngModelController, dynamicNumberStrategy); 649 | onPropertyWatch(ngModelController, initObject); 650 | }); 651 | 652 | scope.$watch('numFract', function(newProperty, oldProperty) { 653 | if (oldProperty === newProperty) { 654 | return; 655 | } 656 | initObject = initAllProperties(createPropertyObject(scope, 657 | 'numFract', newProperty), element, attrs, 658 | ngModelController, dynamicNumberStrategy); 659 | onPropertyWatch(ngModelController, initObject); 660 | }); 661 | 662 | scope.$watch('numSep', function(newProperty, oldProperty) { 663 | if (oldProperty === newProperty) { 664 | return; 665 | } 666 | initObject = initAllProperties(createPropertyObject(scope, 667 | 'numSep', newProperty), element, attrs, 668 | ngModelController, dynamicNumberStrategy); 669 | onPropertyWatch(ngModelController, initObject); 670 | }); 671 | 672 | scope.$watch('numPos', function(newProperty, oldProperty) { 673 | if (oldProperty === newProperty) { 674 | return; 675 | } 676 | initObject = initAllProperties(createPropertyObject(scope, 677 | 'numPos', newProperty), element, attrs, 678 | ngModelController, dynamicNumberStrategy); 679 | onPropertyWatch(ngModelController, initObject); 680 | }); 681 | 682 | scope.$watch('numNeg', function(newProperty, oldProperty) { 683 | if (oldProperty === newProperty) { 684 | return; 685 | } 686 | initObject = initAllProperties(createPropertyObject(scope, 687 | 'numNeg', newProperty), element, attrs, 688 | ngModelController, dynamicNumberStrategy); 689 | onPropertyWatch(ngModelController, initObject); 690 | }); 691 | 692 | scope.$watch('numThousand', function(newProperty, oldProperty) { 693 | if (oldProperty === newProperty) { 694 | return; 695 | } 696 | initObject = initAllProperties(createPropertyObject(scope, 697 | 'numThousand', newProperty), element, attrs, 698 | ngModelController, dynamicNumberStrategy); 699 | onPropertyWatch(ngModelController, initObject); 700 | }); 701 | 702 | scope.$watch('numThousandSep', function(newProperty, oldProperty) { 703 | if (oldProperty === newProperty) { 704 | return; 705 | } 706 | initObject = initAllProperties(createPropertyObject(scope, 707 | 'numThousandSep', newProperty), element, attrs, 708 | ngModelController, dynamicNumberStrategy); 709 | onPropertyWatch(ngModelController, initObject); 710 | }); 711 | 712 | scope.$watch('numAppend', function(newProperty, oldProperty) { 713 | if (oldProperty === newProperty) { 714 | return; 715 | } 716 | initObject = initAllProperties(createPropertyObject(scope, 717 | 'numAppend', newProperty), element, attrs, 718 | ngModelController, dynamicNumberStrategy); 719 | onPropertyWatch(ngModelController, initObject); 720 | }); 721 | 722 | scope.$watch('numPrepend', function(newProperty, oldProperty) { 723 | if (oldProperty === newProperty) { 724 | return; 725 | } 726 | initObject = initAllProperties(createPropertyObject(scope, 727 | 'numPrepend', newProperty), element, attrs, 728 | ngModelController, dynamicNumberStrategy); 729 | onPropertyWatch(ngModelController, initObject); 730 | }); 731 | scope.$watch('numFixed', function(newProperty, oldProperty) { 732 | if (oldProperty === newProperty) { 733 | return; 734 | } 735 | initObject = initAllProperties(createPropertyObject(scope, 736 | 'numFixed', newProperty), element, attrs, 737 | ngModelController, dynamicNumberStrategy); 738 | onPropertyWatch(ngModelController, initObject); 739 | }); 740 | var state = { 741 | enable: true, 742 | count: 0 743 | }; 744 | ngModelController.$parsers.unshift(function(value) { 745 | if (state.enable) { 746 | state.count++; 747 | return prepareResponse(directiveParser(value, initObject, 748 | state)); 749 | } else { 750 | state.enable = true; 751 | return value; 752 | } 753 | }); 754 | /** 755 | * it is like filter, 756 | */ 757 | ngModelController.$formatters.push(function(value) { 758 | return filterModelValue( 759 | value, 760 | initObject.fractionPart, 761 | initObject.fractionSeparator, 762 | initObject.roundFunction, 763 | initObject.isFixed, 764 | initObject.isThousandSeparator, 765 | initObject.thousandSeparator, 766 | initObject.prepend, 767 | initObject.append 768 | ); 769 | }); 770 | } 771 | }; 772 | } 773 | 774 | var moduleName = 'dynamicNumber'; 775 | 776 | angular.module(moduleName, []) 777 | .provider('dynamicNumberStrategy', function() { 778 | var strategies = {}; 779 | 780 | this.addStrategy = addStrategy; 781 | this.getStrategy = getStrategy; 782 | this.getStrategies = getStrategies; 783 | 784 | var strategyProvider = { 785 | addStrategy: addStrategy, 786 | getStrategy: getStrategy, 787 | getStrategies: getStrategies 788 | }; 789 | 790 | this.$get = function() { 791 | return strategyProvider; 792 | }; 793 | 794 | function addStrategy(name, strategy) { 795 | strategies[name] = strategy; 796 | } 797 | 798 | function getStrategy(name) { 799 | return strategies[name]; 800 | } 801 | 802 | function getStrategies() { 803 | return strategies; 804 | } 805 | }) 806 | .filter('awnum', ['dynamicNumberStrategy', function( 807 | dynamicNumberStrategy) { 808 | return function(value, numFract, numSep, numRound, numFixed, 809 | numThousand, numThousandSep, numPrepend, numAppend) { 810 | var strategy = {}; 811 | var fractionPart; 812 | if (angular.isString(numFract)) { 813 | strategy = dynamicNumberStrategy.getStrategy(numFract); 814 | numFract = strategy.numFract; 815 | } 816 | var fractionPart = initFractionPart(numFract, 2); 817 | var fractionSeparator = initSeparator(numSep !== undefined ? 818 | numSep : strategy.numSep, '.'); 819 | var roundFunction = initRound(numRound !== undefined ? 820 | numRound : strategy.numRound, Math.round); 821 | var isFixed = initIsFixed(numFixed !== undefined ? numFixed : 822 | strategy.numFixed, false); 823 | var isThousandSeparator = initIsThousand(numThousand !== 824 | undefined ? numThousand : strategy.numThousand, false); 825 | var thousandSeparator = initThousandSeparator(numThousandSep !== 826 | undefined ? numThousandSep : strategy.numThousandSep, 827 | fractionSeparator, fractionSeparator === '.' ? ',' : '.'); 828 | var prepend = initNumAppendPrepend(numPrepend !== undefined ? 829 | numPrepend : strategy.numPrepend); 830 | var append = initNumAppendPrepend(numAppend !== undefined ? 831 | numAppend : strategy.numAppend); 832 | var filteredValue = filterModelValue(value, fractionPart, 833 | fractionSeparator, roundFunction, isFixed, 834 | isThousandSeparator, thousandSeparator, prepend, append); 835 | if (filteredValue === '') { 836 | return '0'; 837 | } 838 | return filteredValue; 839 | }; 840 | }]) 841 | .directive('awnum', ['dynamicNumberStrategy', dynamicNumberDirective]); 842 | 843 | return moduleName; 844 | })); 845 | -------------------------------------------------------------------------------- /test/directiveSpec.js: -------------------------------------------------------------------------------- 1 | describe('Angular-dynamic-number basic', function() { 2 | beforeEach(function(){ 3 | module('dynamicNumber'); 4 | }); 5 | describe('directive', function(){ 6 | var $compile, $scope, dynamicNumberStrategyProvider; 7 | beforeEach(function(){ 8 | module(function(_dynamicNumberStrategyProvider_){ 9 | dynamicNumberStrategyProvider = _dynamicNumberStrategyProvider_; 10 | }); 11 | }); 12 | beforeEach(function () { 13 | inject(function(_$compile_, _$rootScope_){ 14 | $compile = _$compile_; 15 | $scope = _$rootScope_.$new(); 16 | $scope.testInput = ""; 17 | }); 18 | }); 19 | describe('test init values', function() { 20 | it('should have view value "" and model value "" when init value is ""', function () { 21 | $scope.testInput = ""; 22 | $compile('
')($scope); 23 | $scope.$digest(); 24 | 25 | expect($scope.testInput).toEqual(''); 26 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 27 | }); 28 | it('should have view value "12" and model value 12 when init value is 12', function () { 29 | $scope.testInput = 0; 30 | $compile('
')($scope); 31 | $scope.$digest(); 32 | 33 | expect($scope.testInput).toEqual(0); 34 | expect($scope.testForm.testInput.$viewValue).toEqual('0'); 35 | }); 36 | it('should have view value "12" and model value 12 when init value is 12', function () { 37 | $scope.testInput = 12; 38 | $compile('
')($scope); 39 | $scope.$digest(); 40 | 41 | expect($scope.testInput).toEqual(12); 42 | expect($scope.testForm.testInput.$viewValue).toEqual('12'); 43 | }); 44 | it('should have view value "" and model value null when init value is null', function () { 45 | $scope.testInput = null; 46 | $compile('
')($scope); 47 | $scope.$digest(); 48 | 49 | expect($scope.testInput).toEqual(null); 50 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 51 | }); 52 | it('should have view value "" and model value undefined when init value is undefined', function () { 53 | $scope.testInput = undefined; 54 | $compile('
')($scope); 55 | $scope.$digest(); 56 | 57 | expect($scope.testInput).toEqual(undefined); 58 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 59 | }); 60 | //directive modify model only when user types to input. Id doesn't change it only because init model value is wrong -this is responsibility of dev to have correct init value. 61 | it('should have view value "" and model value string when init value is string', function () { 62 | $scope.testInput = 'dsads'; 63 | $compile('
')($scope); 64 | $scope.$digest(); 65 | 66 | expect($scope.testInput).toEqual('dsads'); 67 | expect($scope.testForm.testInput.$viewValue).toEqual('0'); 68 | }); 69 | }); 70 | describe('number format: 2 integers, decimals dot separator, positive and negative', function() { 71 | beforeEach(function(){ 72 | $compile('
')($scope); 73 | $scope.$digest(); 74 | }); 75 | it('should have view value 11.11 and model value 11.11 when set 11.11', function () { 76 | $scope.testForm.testInput.$setViewValue('11.11'); 77 | expect($scope.testInput).toEqual(11.11); 78 | expect($scope.testForm.testInput.$viewValue).toEqual('11.11'); 79 | }); 80 | it('should have view value 0 and model value 0 when set 111.11', function () { 81 | $scope.testForm.testInput.$setViewValue('111.11'); 82 | expect($scope.testInput).toEqual(null); 83 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 84 | }); 85 | it('should have view value 0 and model value 0 when set 11.111', function () { 86 | $scope.testForm.testInput.$setViewValue('111.11'); 87 | expect($scope.testInput).toEqual(null); 88 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 89 | }); 90 | it('should have view value 0.11 and model value 0.11 when set 0.11', function () { 91 | $scope.testForm.testInput.$setViewValue('0.11'); 92 | expect($scope.testInput).toEqual(0.11); 93 | expect($scope.testForm.testInput.$viewValue).toEqual('0.11'); 94 | }); 95 | it('should have view value 0 and model value 0 when set 00.11', function () { 96 | $scope.testForm.testInput.$setViewValue('00.11'); 97 | expect($scope.testInput).toEqual(0.11); 98 | expect($scope.testForm.testInput.$viewValue).toEqual('0.11'); 99 | }); 100 | it('should have view value -11.11 and model value -11.11 when set -11.11', function () { 101 | $scope.testForm.testInput.$setViewValue('-11.11'); 102 | expect($scope.testInput).toEqual(-11.11); 103 | expect($scope.testForm.testInput.$viewValue).toEqual('-11.11'); 104 | }); 105 | it('should have view value 0 and model value 0 when set -111.11', function () { 106 | $scope.testForm.testInput.$setViewValue('-111.11'); 107 | expect($scope.testInput).toEqual(null); 108 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 109 | }); 110 | it('should have view value 0 and model value 0 when set -11.111', function () { 111 | $scope.testForm.testInput.$setViewValue('-111.11'); 112 | expect($scope.testInput).toEqual(null); 113 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 114 | }); 115 | it('should have view value -0.11 and model value -0.11 when set -0.11', function () { 116 | $scope.testForm.testInput.$setViewValue('-0.11'); 117 | expect($scope.testInput).toEqual(-0.11); 118 | expect($scope.testForm.testInput.$viewValue).toEqual('-0.11'); 119 | }); 120 | it('should have view value -0.11 and model value -0.11 when set -00.11', function () { 121 | $scope.testForm.testInput.$setViewValue('-00.11'); 122 | expect($scope.testInput).toEqual(-0.11); 123 | expect($scope.testForm.testInput.$viewValue).toEqual('-0.11'); 124 | }); 125 | it('should have view value 0 and model value 0 when set 11,11', function () { 126 | $scope.testForm.testInput.$setViewValue('11,11'); 127 | expect($scope.testInput).toEqual(null); 128 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 129 | }); 130 | }); 131 | describe('number format: 2 integers, decimals comma separator, positive and negative', function() { 132 | beforeEach(function(){ 133 | var el = $compile('
')($scope); 134 | $scope.$digest(); 135 | }); 136 | it('should have view value 11,11 and model value 11.11 when set 11,11', function () { 137 | $scope.testForm.testInput.$setViewValue('11,11'); 138 | expect($scope.testInput).toEqual(11.11); 139 | expect($scope.testForm.testInput.$viewValue).toEqual('11,11'); 140 | }); 141 | it('should have view value 0,11 and model value 0.11 when set 0,11', function () { 142 | $scope.testForm.testInput.$setViewValue('0,11'); 143 | expect($scope.testInput).toEqual(0.11); 144 | expect($scope.testForm.testInput.$viewValue).toEqual('0,11'); 145 | }); 146 | it('should have view value -11,11 and model value -11.11 when set -11,11', function () { 147 | $scope.testForm.testInput.$setViewValue('-11,11'); 148 | expect($scope.testInput).toEqual(-11.11); 149 | expect($scope.testForm.testInput.$viewValue).toEqual('-11,11'); 150 | }); 151 | it('should have view value -0,11 and model value -0.11 when set -0,11', function () { 152 | $scope.testForm.testInput.$setViewValue('-0,11'); 153 | expect($scope.testInput).toEqual(-0.11); 154 | expect($scope.testForm.testInput.$viewValue).toEqual('-0,11'); 155 | }); 156 | it('should have view value 11.11 and model value 11.11 when set 11.11', function () { 157 | $scope.testForm.testInput.$setViewValue('11.11'); 158 | expect($scope.testInput).toEqual(null); 159 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 160 | }); 161 | }); 162 | describe('number format: 2 integers, decimals comma separator, positive and negative, dot thousand separator', function() { 163 | beforeEach(function(){ 164 | var el = $compile('
')($scope); 165 | $scope.$digest(); 166 | }); 167 | it('should have view value 111.111,11 and model value 111111.11 when set 111.111,11', function () { 168 | $scope.testForm.testInput.$setViewValue('111.111,11'); 169 | expect($scope.testInput).toEqual(111111.11); 170 | expect($scope.testForm.testInput.$viewValue).toEqual('111.111,11'); 171 | }); 172 | it('should have view value -111.111,11 and model value -111111.11 when set -111.111,11', function () { 173 | $scope.testForm.testInput.$setViewValue('-111.111,11'); 174 | expect($scope.testInput).toEqual(-111111.11); 175 | expect($scope.testForm.testInput.$viewValue).toEqual('-111.111,11'); 176 | }); 177 | }); 178 | describe('number format: 2 integers, decimals dot separator, positive and negative, comma thousand separator', function() { 179 | beforeEach(function(){ 180 | var el = $compile('
')($scope); 181 | $scope.$digest(); 182 | }); 183 | it('should have view value 111,111.11 and model value 111111.11 when set 111,111.11', function () { 184 | $scope.testForm.testInput.$setViewValue('111,111.11'); 185 | expect($scope.testInput).toEqual(111111.11); 186 | expect($scope.testForm.testInput.$viewValue).toEqual('111,111.11'); 187 | }); 188 | it('should have view value -111,111.11 and model value -111111.11 when set -111,111.11', function () { 189 | $scope.testForm.testInput.$setViewValue('-111,111.11'); 190 | expect($scope.testInput).toEqual(-111111.11); 191 | expect($scope.testForm.testInput.$viewValue).toEqual('-111,111.11'); 192 | }); 193 | }); 194 | describe('number format: integers, positive', function() { 195 | beforeEach(function(){ 196 | var el = $compile('
')($scope); 197 | $scope.$digest(); 198 | }); 199 | it('should have view value empty and model value 0 when set -', function () { 200 | $scope.testForm.testInput.$setViewValue('-'); 201 | expect($scope.testInput).toEqual(null); 202 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 203 | }); 204 | it('should have view value 0, and model value 0, when set ,', function () { 205 | $scope.testForm.testInput.$setViewValue(','); 206 | expect($scope.testInput).toEqual(null); 207 | expect($scope.testForm.testInput.$viewValue).toEqual(','); 208 | }); 209 | }); 210 | describe('number format: 2 integers, decimals comma separator, negative', function() { 211 | beforeEach(function(){ 212 | var el = $compile('
')($scope); 213 | $scope.$digest(); 214 | }); 215 | it('should have view value 0 and model value 0 when set 11,11', function () { 216 | $scope.testForm.testInput.$setViewValue('11,11'); 217 | expect($scope.testInput).toEqual(null); 218 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 219 | }); 220 | it('should have view value -11,11 and model value -11.11 when set -11,11', function () { 221 | $scope.testForm.testInput.$setViewValue('-11,11'); 222 | expect($scope.testInput).toEqual(-11.11); 223 | expect($scope.testForm.testInput.$viewValue).toEqual('-11,11'); 224 | }); 225 | it('should have view value -0,11 and model value -0.11 when set -0,11', function () { 226 | $scope.testForm.testInput.$setViewValue('-0,11'); 227 | expect($scope.testInput).toEqual(-0.11); 228 | expect($scope.testForm.testInput.$viewValue).toEqual('-0,11'); 229 | }); 230 | it('should have view value 0 and model value 0 when set 11.11', function () { 231 | $scope.testForm.testInput.$setViewValue('11.11'); 232 | expect($scope.testInput).toEqual(null); 233 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 234 | }); 235 | }); 236 | describe('number format: 6 integers, decimals dot separator, positive and negative, space thousand separator', function() { 237 | beforeEach(function(){ 238 | var el = $compile('
')($scope); 239 | $scope.$digest(); 240 | }); 241 | it('should have view value 111 111.11 and model value 111111.11 when set 111 111.11', function () { 242 | $scope.testForm.testInput.$setViewValue('111 111.11'); 243 | expect($scope.testInput).toEqual(111111.11); 244 | expect($scope.testForm.testInput.$viewValue).toEqual('111 111.11'); 245 | }); 246 | it('should have view value -111 111.11 and model value -111111.11 when set -111 111.11', function () { 247 | $scope.testForm.testInput.$setViewValue('-111 111.11'); 248 | expect($scope.testInput).toEqual(-111111.11); 249 | expect($scope.testForm.testInput.$viewValue).toEqual('-111 111.11'); 250 | }); 251 | }); 252 | describe('number format: 6 integers, decimals dot separator, positive and negative, apostrophe thousand separator', function() { 253 | beforeEach(function(){ 254 | var el = $compile('
')($scope); 255 | $scope.$digest(); 256 | }); 257 | it('should have view value 111\'111.11 and model value 111111.11 when set 111\'111.11', function () { 258 | $scope.testForm.testInput.$setViewValue('111\'111.11'); 259 | expect($scope.testInput).toEqual(111111.11); 260 | expect($scope.testForm.testInput.$viewValue).toEqual('111\'111.11'); 261 | }); 262 | it('should have view value -111\'111.11 and model value -111111.11 when set -111\'111.11', function () { 263 | $scope.testForm.testInput.$setViewValue('-111\'111.11'); 264 | expect($scope.testInput).toEqual(-111111.11); 265 | expect($scope.testForm.testInput.$viewValue).toEqual('-111\'111.11'); 266 | }); 267 | }); 268 | describe('custom strategy provider', function(){ 269 | beforeEach(function(){ 270 | dynamicNumberStrategyProvider.addStrategy('price', { 271 | numInt: 6, 272 | numFract: 2, 273 | numSep: '.', 274 | numPos: true, 275 | numNeg: true, 276 | numRound: 'round', 277 | numThousand: true 278 | }); 279 | var el = $compile('
')($scope); 280 | $scope.$digest(); 281 | }); 282 | it('should have view value 11.11 and model value 11.11 when set 11.11', function () { 283 | $scope.testForm.testInput.$setViewValue('11.11'); 284 | expect($scope.testInput).toEqual(11.11); 285 | expect($scope.testForm.testInput.$viewValue).toEqual('11.11'); 286 | }); 287 | it('should have view value 0.11 and model value 0.11 when set 0.11', function () { 288 | $scope.testForm.testInput.$setViewValue('0.11'); 289 | expect($scope.testInput).toEqual(0.11); 290 | expect($scope.testForm.testInput.$viewValue).toEqual('0.11'); 291 | }); 292 | it('should have view value -11.11 and model value -11.11 when set -11.11', function () { 293 | $scope.testForm.testInput.$setViewValue('-11.11'); 294 | expect($scope.testInput).toEqual(-11.11); 295 | expect($scope.testForm.testInput.$viewValue).toEqual('-11.11'); 296 | }); 297 | it('should have view value -0.11 and model value -0.11 when set -0.11', function () { 298 | $scope.testForm.testInput.$setViewValue('-0.11'); 299 | expect($scope.testInput).toEqual(-0.11); 300 | expect($scope.testForm.testInput.$viewValue).toEqual('-0.11'); 301 | }); 302 | it('should have view value 1,111 and model value 1111 when set 11,11', function () { 303 | $scope.testForm.testInput.$setViewValue('11,11'); 304 | expect($scope.testInput).toEqual(1111); 305 | expect($scope.testForm.testInput.$viewValue).toEqual('1,111'); 306 | }); 307 | }); 308 | }); 309 | describe('filter', function(){ 310 | var $filter, dynamicNumberStrategyProvider; 311 | beforeEach(function() { 312 | module(function(_dynamicNumberStrategyProvider_) { 313 | dynamicNumberStrategyProvider = _dynamicNumberStrategyProvider_; 314 | }); 315 | }); 316 | beforeEach(inject(function(_$filter_){ 317 | $filter = _$filter_; 318 | })); 319 | describe('number format: 2 integers, decimals comma separator, negative', function() { 320 | it('should return 0 when value null', function () { 321 | expect($filter('awnum')(null)).toEqual('0'); 322 | }); 323 | it('should return 0 when value \'abc\' ', function () { 324 | expect($filter('awnum')('abc')).toEqual('0'); 325 | }); 326 | it('should return 11,29 when value \'11.287\' ', function () { 327 | expect($filter('awnum')('11.287',2, ',','round')).toEqual('11,29'); 328 | }); 329 | it('should return 11,00 when value \'11\' and fixed fraction digit number is 2', function () { 330 | expect($filter('awnum')('11.00',2, ',','round','true')).toEqual('11,00'); 331 | }); 332 | }); 333 | describe('number format: 6 integers, decimals comma separator, thousand dot separator', function() { 334 | it('should return 1.111,29 when value \'1111.287\' ', function () { 335 | expect($filter('awnum')('1111.287',2, ',','round','false','true')).toEqual('1.111,29'); 336 | }); 337 | }); 338 | describe('filter with custom strategy', function() { 339 | beforeEach(function() { 340 | dynamicNumberStrategyProvider.addStrategy('price', { 341 | numFract: 1, 342 | numSep: '.', 343 | numPos: true, 344 | numNeg: true, 345 | numRound: 'round', 346 | numThousand: true 347 | }); 348 | }); 349 | it('should return 1,111.2 when value \'1111.16\' ', function () { 350 | expect($filter('awnum')('1111.16', 'price')).toEqual('1,111.2'); 351 | }); 352 | it('should return 1.111,2 when value \'1111.16\' ', function () { 353 | expect($filter('awnum')('1111.16', 'price', ',')).toEqual('1.111,2'); 354 | }); 355 | }); 356 | }); 357 | }); 358 | -------------------------------------------------------------------------------- /test/mobileDirectiveSpec.js: -------------------------------------------------------------------------------- 1 | describe('Angular-dynamic-number basic', function() { 2 | beforeEach(function(){ 3 | module('dynamicNumber'); 4 | }); 5 | describe('directive', function(){ 6 | var $compile, $scope, dynamicNumberStrategyProvider; 7 | beforeEach(function(){ 8 | module(function(_dynamicNumberStrategyProvider_){ 9 | dynamicNumberStrategyProvider = _dynamicNumberStrategyProvider_; 10 | }); 11 | }); 12 | beforeEach(function () { 13 | inject(function(_$compile_, _$rootScope_){ 14 | $compile = _$compile_; 15 | $scope = _$rootScope_.$new(); 16 | $scope.testInput = ""; 17 | }); 18 | }); 19 | describe('test init values where type="tel"', function() { 20 | it('should have view value "" and model value "" when init value is ""', function () { 21 | $scope.testInput = ""; 22 | $compile('
')($scope); 23 | $scope.$digest(); 24 | 25 | expect($scope.testInput).toEqual(''); 26 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 27 | }); 28 | it('should have view value "12" and model value 12 when init value is 12', function () { 29 | $scope.testInput = 0; 30 | $compile('
')($scope); 31 | $scope.$digest(); 32 | 33 | expect($scope.testInput).toEqual(0); 34 | expect($scope.testForm.testInput.$viewValue).toEqual('0'); 35 | }); 36 | it('should have view value "12" and model value 12 when init value is 12', function () { 37 | $scope.testInput = 12; 38 | $compile('
')($scope); 39 | $scope.$digest(); 40 | 41 | expect($scope.testInput).toEqual(12); 42 | expect($scope.testForm.testInput.$viewValue).toEqual('12'); 43 | }); 44 | it('should have view value "" and model value null when init value is null', function () { 45 | $scope.testInput = null; 46 | $compile('
')($scope); 47 | $scope.$digest(); 48 | 49 | expect($scope.testInput).toEqual(null); 50 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 51 | }); 52 | it('should have view value "" and model value undefined when init value is undefined', function () { 53 | $scope.testInput = undefined; 54 | $compile('
')($scope); 55 | $scope.$digest(); 56 | 57 | expect($scope.testInput).toEqual(undefined); 58 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 59 | }); 60 | //directive modify model only when user types to input. Id doesn't change it only because init model value is wrong -this is responsibility of dev to have correct init value. 61 | it('should have view value "" and model value string when init value is string', function () { 62 | $scope.testInput = 'dsads'; 63 | $compile('
')($scope); 64 | $scope.$digest(); 65 | 66 | expect($scope.testInput).toEqual('dsads'); 67 | expect($scope.testForm.testInput.$viewValue).toEqual('0'); 68 | }); 69 | }); 70 | describe('number format: 2 integeres, decimals dot separator, positive and negative', function() { 71 | beforeEach(function(){ 72 | $compile('
')($scope); 73 | $scope.$digest(); 74 | }); 75 | it('should have view value 11.11 and model value 11.11 when set 11.11', function () { 76 | $scope.testForm.testInput.$setViewValue('11.11'); 77 | expect($scope.testInput).toEqual(11.11); 78 | expect($scope.testForm.testInput.$viewValue).toEqual('11.11'); 79 | }); 80 | it('should have view value 0 and model value 0 when set 111.11', function () { 81 | $scope.testForm.testInput.$setViewValue('111.11'); 82 | expect($scope.testInput).toEqual(null); 83 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 84 | }); 85 | it('should have view value 0 and model value 0 when set 11.111', function () { 86 | $scope.testForm.testInput.$setViewValue('111.11'); 87 | expect($scope.testInput).toEqual(null); 88 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 89 | }); 90 | it('should have view value 0.11 and model value 0.11 when set 0.11', function () { 91 | $scope.testForm.testInput.$setViewValue('0.11'); 92 | expect($scope.testInput).toEqual(0.11); 93 | expect($scope.testForm.testInput.$viewValue).toEqual('0.11'); 94 | }); 95 | it('should have view value 0.11 and model value 0.11 when set 00.11', function () { 96 | $scope.testForm.testInput.$setViewValue('00.11'); 97 | expect($scope.testInput).toEqual(0.11); 98 | expect($scope.testForm.testInput.$viewValue).toEqual('0.11'); 99 | }); 100 | it('should have view value -11.11 and model value -11.11 when set -11.11', function () { 101 | $scope.testForm.testInput.$setViewValue('-11.11'); 102 | expect($scope.testInput).toEqual(-11.11); 103 | expect($scope.testForm.testInput.$viewValue).toEqual('-11.11'); 104 | }); 105 | it('should have view value 0 and model value 0 when set -111.11', function () { 106 | $scope.testForm.testInput.$setViewValue('-111.11'); 107 | expect($scope.testInput).toEqual(null); 108 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 109 | }); 110 | it('should have view value 0 and model value 0 when set -11.111', function () { 111 | $scope.testForm.testInput.$setViewValue('-111.11'); 112 | expect($scope.testInput).toEqual(null); 113 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 114 | }); 115 | it('should have view value -0.11 and model value -0.11 when set -0.11', function () { 116 | $scope.testForm.testInput.$setViewValue('-0.11'); 117 | expect($scope.testInput).toEqual(-0.11); 118 | expect($scope.testForm.testInput.$viewValue).toEqual('-0.11'); 119 | }); 120 | it('should have view value -0.11 and model value -0.11 when set -00.11', function () { 121 | $scope.testForm.testInput.$setViewValue('-00.11'); 122 | expect($scope.testInput).toEqual(-0.11); 123 | expect($scope.testForm.testInput.$viewValue).toEqual('-0.11'); 124 | }); 125 | it('should have view value 0 and model value 0 when set 11,11', function () { 126 | $scope.testForm.testInput.$setViewValue('11,11'); 127 | expect($scope.testInput).toEqual(null); 128 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 129 | }); 130 | }); 131 | describe('number format: 2 integeres, decimals comma separator, positive and negative', function() { 132 | beforeEach(function(){ 133 | var el = $compile('
')($scope); 134 | $scope.$digest(); 135 | }); 136 | it('should have view value 11,11 and model value 11.11 when set 11,11', function () { 137 | $scope.testForm.testInput.$setViewValue('11,11'); 138 | expect($scope.testInput).toEqual(11.11); 139 | expect($scope.testForm.testInput.$viewValue).toEqual('11,11'); 140 | }); 141 | it('should have view value 0,11 and model value 0.11 when set 0,11', function () { 142 | $scope.testForm.testInput.$setViewValue('0,11'); 143 | expect($scope.testInput).toEqual(0.11); 144 | expect($scope.testForm.testInput.$viewValue).toEqual('0,11'); 145 | }); 146 | it('should have view value -11,11 and model value -11.11 when set -11,11', function () { 147 | $scope.testForm.testInput.$setViewValue('-11,11'); 148 | expect($scope.testInput).toEqual(-11.11); 149 | expect($scope.testForm.testInput.$viewValue).toEqual('-11,11'); 150 | }); 151 | it('should have view value -0,11 and model value -0.11 when set -0,11', function () { 152 | $scope.testForm.testInput.$setViewValue('-0,11'); 153 | expect($scope.testInput).toEqual(-0.11); 154 | expect($scope.testForm.testInput.$viewValue).toEqual('-0,11'); 155 | }); 156 | it('should not have view value 0 and model value 0 when set 11.11', function () { 157 | $scope.testForm.testInput.$setViewValue('11.11'); 158 | expect($scope.testInput).toEqual(null); 159 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 160 | }); 161 | }); 162 | describe('number format: 2 integeres, decimals comma separator, positive and negative, dot thousand separator', function() { 163 | beforeEach(function(){ 164 | var el = $compile('
')($scope); 165 | $scope.$digest(); 166 | }); 167 | it('should have view value 111.111,11 and model value 111111.11 when set 111.111,11', function () { 168 | $scope.testForm.testInput.$setViewValue('111.111,11'); 169 | expect($scope.testInput).toEqual(111111.11); 170 | expect($scope.testForm.testInput.$viewValue).toEqual('111.111,11'); 171 | }); 172 | it('should have view value -111.111,11 and model value -111111.11 when set -111.111,11', function () { 173 | $scope.testForm.testInput.$setViewValue('-111.111,11'); 174 | expect($scope.testInput).toEqual(-111111.11); 175 | expect($scope.testForm.testInput.$viewValue).toEqual('-111.111,11'); 176 | }); 177 | }); 178 | describe('number format: 2 integeres, decimals dot separator, positive and negative, comma thousand separator', function() { 179 | beforeEach(function(){ 180 | var el = $compile('
')($scope); 181 | $scope.$digest(); 182 | }); 183 | it('should have view value 111,111.11 and model value 111111.11 when set 111,111.11', function () { 184 | $scope.testForm.testInput.$setViewValue('111,111.11'); 185 | expect($scope.testInput).toEqual(111111.11); 186 | expect($scope.testForm.testInput.$viewValue).toEqual('111,111.11'); 187 | }); 188 | it('should have view value -111,111.11 and model value -111111.11 when set -111,111.11', function () { 189 | $scope.testForm.testInput.$setViewValue('-111,111.11'); 190 | expect($scope.testInput).toEqual(-111111.11); 191 | expect($scope.testForm.testInput.$viewValue).toEqual('-111,111.11'); 192 | }); 193 | }); 194 | describe('number format: 2 integeres, decimals comma separator, negative', function() { 195 | beforeEach(function(){ 196 | var el = $compile('
')($scope); 197 | $scope.$digest(); 198 | }); 199 | it('should have view value 0 and model value 0 when set 11,11', function () { 200 | $scope.testForm.testInput.$setViewValue('11,11'); 201 | expect($scope.testInput).toEqual(null); 202 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 203 | }); 204 | it('should have view value -11,11 and model value -11.11 when set -11,11', function () { 205 | $scope.testForm.testInput.$setViewValue('-11,11'); 206 | expect($scope.testInput).toEqual(-11.11); 207 | expect($scope.testForm.testInput.$viewValue).toEqual('-11,11'); 208 | }); 209 | it('should have view value -0,11 and model value -0.11 when set -0,11', function () { 210 | $scope.testForm.testInput.$setViewValue('-0,11'); 211 | expect($scope.testInput).toEqual(-0.11); 212 | expect($scope.testForm.testInput.$viewValue).toEqual('-0,11'); 213 | }); 214 | it('should not have view value 11.11 and model value 11.11 when set 11.11', function () { 215 | $scope.testForm.testInput.$setViewValue('11.11'); 216 | expect($scope.testInput).toEqual(null); 217 | expect($scope.testForm.testInput.$viewValue).toEqual(''); 218 | }); 219 | }); 220 | describe('number format: 6 integeres, decimals dot separator, positive and negative, space thousand separator', function() { 221 | beforeEach(function(){ 222 | var el = $compile('
')($scope); 223 | $scope.$digest(); 224 | }); 225 | it('should have view value 111 111.11 and model value 111111.11 when set 111 111.11', function () { 226 | $scope.testForm.testInput.$setViewValue('111 111.11'); 227 | expect($scope.testInput).toEqual(111111.11); 228 | expect($scope.testForm.testInput.$viewValue).toEqual('111 111.11'); 229 | }); 230 | it('should have view value -111 111.11 and model value -111111.11 when set -111 111.11', function () { 231 | $scope.testForm.testInput.$setViewValue('-111 111.11'); 232 | expect($scope.testInput).toEqual(-111111.11); 233 | expect($scope.testForm.testInput.$viewValue).toEqual('-111 111.11'); 234 | }); 235 | }); 236 | describe('custom strategy provider', function(){ 237 | beforeEach(function(){ 238 | dynamicNumberStrategyProvider.addStrategy('price', { 239 | numInt: 6, 240 | numFract: 2, 241 | numSep: '.', 242 | numPos: true, 243 | numNeg: true, 244 | numRound: 'round', 245 | numThousand: true 246 | }); 247 | var el = $compile('
')($scope); 248 | $scope.$digest(); 249 | }); 250 | it('should have view value 11.11 and model value 11.11 when set 11.11', function () { 251 | $scope.testForm.testInput.$setViewValue('11.11'); 252 | expect($scope.testInput).toEqual(11.11); 253 | expect($scope.testForm.testInput.$viewValue).toEqual('11.11'); 254 | }); 255 | it('should have view value 0.11 and model value 0.11 when set 0.11', function () { 256 | $scope.testForm.testInput.$setViewValue('0.11'); 257 | expect($scope.testInput).toEqual(0.11); 258 | expect($scope.testForm.testInput.$viewValue).toEqual('0.11'); 259 | }); 260 | it('should have view value -11.11 and model value -11.11 when set -11.11', function () { 261 | $scope.testForm.testInput.$setViewValue('-11.11'); 262 | expect($scope.testInput).toEqual(-11.11); 263 | expect($scope.testForm.testInput.$viewValue).toEqual('-11.11'); 264 | }); 265 | it('should have view value -0.11 and model value -0.11 when set -0.11', function () { 266 | $scope.testForm.testInput.$setViewValue('-0.11'); 267 | expect($scope.testInput).toEqual(-0.11); 268 | expect($scope.testForm.testInput.$viewValue).toEqual('-0.11'); 269 | }); 270 | it('should not have view value 11.11 and model value 11.11 when set 11,11', function () { 271 | $scope.testForm.testInput.$setViewValue('11,11'); 272 | expect($scope.testInput).not.toEqual(11.11); 273 | expect($scope.testForm.testInput.$viewValue).not.toEqual('11.11'); 274 | }); 275 | }); 276 | }); 277 | describe('filter', function(){ 278 | var $filter; 279 | beforeEach(inject(function(_$filter_){ 280 | $filter = _$filter_; 281 | })); 282 | describe('number format: 2 integeres, decimals comma separator, negative', function() { 283 | it('should return 0 when value null', function () { 284 | expect($filter('awnum')(null)).toEqual('0'); 285 | }); 286 | it('should return 0 when value \'abc\' ', function () { 287 | expect($filter('awnum')('abc')).toEqual('0'); 288 | }); 289 | it('should return 11,29 when value \'11.287\' ', function () { 290 | expect($filter('awnum')('11.287',2, ',','round')).toEqual('11,29'); 291 | }); 292 | it('should return 11,00 when value \'11\' and fixed fraction digit number is 2', function () { 293 | expect($filter('awnum')('11.00',2, ',','round','true')).toEqual('11,00'); 294 | }); 295 | }); 296 | describe('number format: 6 integeres, decimals comma separator, thousand dot separator', function() { 297 | it('should return 1.111,29 when value \'1111.287\' ', function () { 298 | expect($filter('awnum')('1111.287',2, ',','round','false','true')).toEqual('1.111,29'); 299 | }); 300 | }); 301 | }); 302 | }); --------------------------------------------------------------------------------