├── .cjsescache
├── .eslintrc
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .npmrc
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── index.d.ts
├── index.html
├── lib
├── format.es5.js
├── format.esm.js
├── format.js
└── format.min.js
├── package.json
├── rollup.config.js
├── src
└── format.js
└── test
└── test.js
/.cjsescache:
--------------------------------------------------------------------------------
1 | {
2 | "src/format.js": null
3 | }
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "parserOptions": {
7 | "ecmaVersion": 2017,
8 | "sourceType": "module"
9 | },
10 | "rules": {
11 | "curly": 1,
12 | "dot-location": [2,"property"],
13 | "eqeqeq": 1,
14 | "linebreak-style": [2, "unix"],
15 | "no-else-return": 1,
16 | "no-eval": 2,
17 | "no-octal": 2,
18 | "no-with": 2,
19 | "radix": 2,
20 | "brace-style": 1,
21 | "camelcase": 2,
22 | "indent": [2, "tab"],
23 | "no-array-constructor": 2,
24 | "quotes": [2, "double", {
25 | "allowTemplateLiterals": true,
26 | "avoidEscape": true
27 | }],
28 | "quote-props": 0,
29 | "spaced-comment": 2,
30 | "arrow-spacing": 2,
31 | "no-var": 2,
32 | "no-unused-vars": 1
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # lockfiles
2 | package-lock.json
3 | yarn.lock
4 |
5 | # temp stuff
6 | tmp/
7 | *.tmp
8 | *.bak
9 |
10 | # logs
11 | *.stackdump
12 | *.log
13 |
14 | # Build
15 | node_modules/
16 |
17 | # Windows crap
18 | Thumbs.db
19 | Desktop.ini
20 |
21 | # Mac crap
22 | .DS_Store
23 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise" : true,
3 | "camelcase" : true,
4 | "curly" : true,
5 | "eqeqeq" : true,
6 | "es3" : true,
7 | "eqnull" : true,
8 | "forin" : true,
9 | "freeze" : true,
10 | "immed" : true,
11 | "indent" : 2,
12 | "jquery" : true,
13 | "latedef" : true,
14 | "newcap" : true,
15 | "noarg" : true,
16 | "noempty" : true,
17 | "nonbsp" : true,
18 | "nonew" : true,
19 | "quotmark" : true,
20 | "sub" : true,
21 | "trailing" : true,
22 | "undef" : true,
23 | "unused" : true
24 | }
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "12"
4 | - "10"
5 | - "8"
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Please do not submit pull requests to the main branch.
4 |
5 | * Fork the library.
6 | * Install [grunt](http://gruntjs.com/getting-started#installing-the-cli).
7 | * Run `npm install` to install dependencies.
8 | * Make your changes to the `src/format.js` file.
9 | * Add unit tests to the `/test/test.js` file; your contribution may not be merged without unit tests.
10 | * To test your changes, run `grunt`.
11 | * When all your tests are passing, commit your changes to your fork.
12 | * Submit a pull request to a branch of the repository.
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2016 ecava (author KPL)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to use,
6 | copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
7 | Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Javascript Number Formatter
2 |
3 | Lightweight & Fast JavaScript Number Formatter
4 |
5 | [![Build Status][build-image]][build-url] [![NPM Version][npm-image]][npm-url] [![devDependency Status][david-dev-image]][david-dev-url] [![MIT][license-image]][license-url]
6 |
7 | ## Introduction
8 |
9 | This standalone number formatter† is intended to be short and fast. As they are the main factors for a high performance JavaScript app. Development release is around 150 lines including license info, blank lines and comments. And production release is less than 2,000 bytes.
10 |
11 | ```js
12 | format( "#,##0.####", 1234567.890 ); // output: "1,234,567.89"
13 | format( "$ #,###.00", -1234567.890 ); // output: "$ -1,234,567.89"
14 |
15 | // Added in v2.0.0
16 | format( "$ #,###.00", -1234567.890, {enforceMaskSign: true}); // output: "$ 1,234,567.89"
17 | format( "$ -#,###.00", -1234567.890, {enforceMaskSign: true}); // output: "$ -1,234,567.89"
18 | format( "$ +#,###.00", -1234567.890, {enforceMaskSign: true}); // output: "$ -1,234,567.89"
19 | format( "$ +#,###.00", 1234567.890, {enforceMaskSign: true}); // output: "$ +1,234,567.89"
20 | ```
21 |
22 | † Initial development release of this code was written by KPL and hosted at [Google Code](https://code.google.com/p/javascript-number-formatter/).
23 |
24 | ## Features
25 |
26 | * Short, fast, flexible yet standalone.
27 | * Accept standard number formatting like `#,##0.00` or with negation `-000.####`.
28 | * Accept any country format like `# ##0,00`, `#,###.##`, `#'###.##` or any type of non-numbering symbol.
29 | * Accept any numbers of digit grouping. `#,##,#0.000` or `#,###0.##` are all valid.
30 | * Accept any redundant/fool-proof formatting. `##,###,##.#` or `0#,#00#.###0#` are all OK.
31 | * Auto number rounding.
32 | * Simple interface, just supply mask & value like this: `format( "0.0000", 3.141592)`.
33 | * Include a prefix & suffix with the mask.
34 |
35 | ## Limitations
36 |
37 | * No scientific/engineering formatting.
38 | * Not for date or phone formation.
39 | * No color control.
40 | * No prefix or suffix is allowed except leading negation symbol. So `$#,##0.00` or `#,###.##USD` will not yield expected outcome. Use `'$'+format('#,##0.00', 123.45)` or `format('#,##0.00', 456.789) + 'USD'`
41 | * The prefix or suffix *can not* include any numbers (`0-9`), dashes (`-`), or plus signs (`+`).
42 |
43 | ## Format Symbols
44 |
45 | | Description | Symbol | Summary |
46 | |---------------|--------|---------|
47 | | Mask symbols | #0123456789+- | Mask symbols used for formatting the value. |
48 | | Placeholders | #123456789 | Un-forced digit*; this optional digit will only show if it is required as a placeholder. |
49 | | Zero | 0 | Forced digit; the digit will be shown whether or not the digit is relevant to the value. |
50 | | Signs | +- | Indicates a positive or negative value; visible depending on the value sign and the `enforceMaskSign` setting. |
51 | | Leftmost | | _Any_ non-mask symbol† inside the mask will be set to represent a thousands separator. |
52 | | Rightmost | | _Any_ non-mask symbol† inside the mask‡ will be set to represent the decimal separator. |
53 | | Prefix/Suffix | | _Any_ non-mask symbol† outside the mask. |
54 |
55 | \* Non-zero mask digits (`1` through `9`) behave the same as the `#`.
56 | † Anything not a digit, and not a `+`, `-` or `#`.
57 | ‡ In the case where there is a trailing decimal or comma, it will be included in the mask, e.g. `#.` or `0,`.
58 |
59 | ## Note
60 |
61 | When only one symbol is supplied, the library will always treat that symbol as a decimal. For example, `format( '#,###', 1234567.890)` will output `1234567,890`.
62 |
63 | To force a single symbol to be used as a separator, add a trailing symbol. In this example, a period is added to the end of the mask - `format( '#,###.', 1234567.890)` - resulting in it being used as a decimal and forcing the first symbol to be the separator and return this output: `1,234,567`.
64 |
65 | ## Installation
66 |
67 | ### npm package
68 |
69 | npm install --save number-format.js
70 |
71 | ## Demos
72 |
73 | A demo/sample page with few examples is provided ([demo](http://mottie.github.io/javascript-number-formatter/)).
74 |
75 | And a jsFiddle was created to aid in testing: https://jsfiddle.net/Mottie/t2etyodx/
76 |
77 | [build-url]: https://travis-ci.org/Mottie/javascript-number-formatter
78 | [build-image]: https://travis-ci.org/Mottie/javascript-number-formatter.png?branch=master
79 | [npm-url]: https://www.npmjs.com/package/number-format.js
80 | [npm-image]: https://img.shields.io/npm/v/number-format.js.svg
81 | [david-dev-url]: https://david-dm.org/Mottie/javascript-number-formatter?type=dev
82 | [david-dev-image]: https://david-dm.org/Mottie/javascript-number-formatter/dev-status.svg
83 | [license-url]: https://github.com/Mottie/javascript-number-formatter/blob/master/LICENSE
84 | [license-image]: https://img.shields.io/badge/license-MIT-blue.svg
85 |
86 | ## Recent Changes
87 |
88 | View the [complete change log here](https://github.com/Mottie/javascript-number-formatter/wiki).
89 |
90 | ### v2.0.7 (2018-11-13)
91 |
92 | * Update typescript binding. See [issue #20](https://github.com/Mottie/javascript-number-formatter/issues/20).
93 | * Fix improper placeholder behavior. Updated Readme with format symbols table. Closes [issue #19](https://github.com/Mottie/javascript-number-formatter/issues/19).
94 | * Add more tests.
95 | * Meta:
96 | * Update dependencies.
97 | * Improve code readability.
98 | * Include version in min.js.
99 |
100 | ### v2.0.6 (2018-11-06)
101 |
102 | * Trim trailing zeros in mask. Fixes [issue #18](https://github.com/Mottie/javascript-number-formatter/issues/18).
103 |
104 | ### v2.0.0 – 2.0.5 (2018-10-26)
105 |
106 | * Add `ignoreSign` option (modified to `enforeceMaskSign`!).
107 | * Switch to XO, AVA & rollup.
108 | * Meta: Update dot files & remove bower support.
109 | * Code cleanup & convert to ES2015.
110 | * Rename `ignoreSign` to `enforceMaskSign` (default `false`).
111 | * Reduce code complexity.
112 | * Export as node module.
113 | * Update TS with options.
114 | * Switch demo to use lib file & highlight valid results.
115 | * Switch from Grunt to rollup.
116 | * Switch from IIFE to UMD output.
117 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | declare const format : (mask: string, value: number, options?: object) => string;
2 | export = format;
3 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JS Number Formatter • Test/Sample Page
6 |
19 |
20 |
21 |
22 |
23 | Usage
24 |
25 |
// format( mask, value );
26 | // result "1,234,567.89"
27 | format( "#,##0.####", 1234567.890 );
28 |
29 |
30 | Examples
31 |
32 |
33 | # Description Input Format Output
34 |
35 |
36 | Most common world wide
37 |
38 |
39 |
40 | Simple
41 | 123456.789
42 | #,##0.00
43 |
44 |
45 |
46 |
47 | Random
48 | 20110628.15001234
49 | #,##0.###0
50 |
51 |
52 |
53 |
54 | Random
55 | 0
56 | #,###.##0
57 |
58 |
59 |
60 |
61 | Long number
62 | 1234567890.1234567890
63 | #,###.##0
64 |
65 |
66 |
67 |
68 | Negative value
69 | -0.1
70 | #
71 |
72 |
73 |
74 |
75 | Negative value
76 | -0.1
77 | 0
78 |
79 |
80 |
81 |
82 | Negative value
83 | -0.13
84 | 0.#
85 |
86 |
87 |
88 |
89 | Negative value
90 | -5000.123456789
91 | #,##0.######
92 |
93 |
94 |
95 | Localization format
96 |
97 |
98 |
99 | US, UK and many more
100 | 1234567.890
101 | #,##0.00
102 |
103 |
104 |
105 |
106 | Estonia, France
107 | -128983833.4560022
108 | ### ###,##
109 |
110 |
111 |
112 |
113 | Germany, Italy
114 | -1234560.10002920
115 | ##.000,00
116 |
117 |
118 |
119 |
120 | Japan
121 | 963852741.001
122 | ###,####.00
123 |
124 |
125 |
126 |
127 | Switzerland
128 | 33445566.778899
129 | #'###'#00.00
130 |
131 |
132 |
133 | Any format
134 |
135 |
136 |
137 |
138 | -1234.5678
139 | ##.000,00
140 |
141 |
142 |
143 |
144 |
145 | 4651321.841
146 | ##^000*00
147 |
148 |
149 |
150 |
151 |
152 | 3411.498
153 | -##¿000$00
154 |
155 |
156 |
157 |
158 |
159 | 465456456.87987212
160 | 00!00@00
161 |
162 |
163 |
164 |
165 | Force comma as separator
166 | 1112341.4348712
167 | ###,###.
168 |
169 |
170 |
171 |
172 | Force space as separator
173 | 2344441.4348712
174 | ### ###.
175 |
176 |
177 |
178 |
179 | Force dot as separator
180 | 2345341.4348712
181 | ###.###
182 |
183 |
184 |
185 |
186 | Prefix & Suffix
187 |
188 |
189 |
190 |
191 | No spaces
192 | 123456789.9876
193 | $#,##0.00USD
194 |
195 |
196 |
197 |
198 | Extra spaces (set white-space: pre;
in cells)
199 | 123456789.9876
200 | $ #,##0.00 USD
201 |
202 |
203 |
204 |
205 |
206 | 123456789.9876
207 | ##.000,00 €
208 |
209 |
210 |
211 |
212 |
213 | 123456789.9876
214 | ###,####.00 ¥
215 |
216 |
217 |
218 |
219 |
220 | 123456789.9876
221 | ### ###,### ¢ and stuff
222 |
223 |
224 |
225 |
226 |
227 | 123456789.9876
228 | #,##0.00 a b c
229 |
230 |
231 |
232 |
233 | Spaces & parenthesis (indicates a negative value, but the input is positive)
234 | 123456789.9876
235 | $ (#,###.00) Money
236 |
237 |
238 |
239 |
240 | Spaces & parenthesis (negative; not converted!)
241 | -123456789.9876
242 | $ (#,###.00) Money
243 |
244 |
245 |
246 |
247 | Prefix with comma
248 | 123456789.9876
249 | a, b c? #.00 yep!
250 |
251 |
252 |
253 |
254 | Prefix with a periods
255 | 123456789.9876
256 | cost... #,##0.00 yep!
257 |
258 |
259 |
260 |
261 | Suffix with comma & period
262 | 123456789.9876
263 | $# ###,00 USD, or euros.
264 |
265 |
266 |
267 |
268 | Suffix with period
269 | 123456789.9876
270 | It costs $# ###,00 euros.
271 |
272 |
273 |
274 |
275 | Hanging decimal
276 | 123456789.9876
277 | test:### ###. ing
278 |
279 |
280 |
281 |
282 | Masks that don't work
283 |
284 |
285 |
286 |
287 | No "#" outside of mask
288 | 123456789.9876
289 | item #abc $#,###.00
290 |
291 |
292 |
293 |
294 | No numbers outside of mask
295 | 123456789.9876
296 | 99 items = $#,###.00
297 |
298 |
299 |
300 |
301 | No dashes outside of mask
302 | 123456789.9876
303 | cost -- $#,###.00 -- value
304 |
305 |
306 |
307 |
308 | No plus sign in prefix
309 | 123456789.9876
310 | ++ value! $#,###.00
311 |
312 |
313 |
314 |
315 | No plus sign in suffix
316 | 123456789.9876
317 | $#,###.00 ++ value!
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 | See also...
326 |
327 |
328 | Original:
329 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
355 |
356 |
357 |
--------------------------------------------------------------------------------
/lib/format.es5.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Javascript-number-formatter
3 | * Lightweight & Fast JavaScript Number Formatter
4 | *
5 | * @preserve IntegraXor Web SCADA - JavaScript Number Formatter (http://www.integraxor.com/)
6 | * @author KPL
7 | * @maintainer Rob Garrison
8 | * @copyright 2019 ecava
9 | * @license MIT
10 | * @link http://mottie.github.com/javascript-number-formatter/
11 | * @version 2.0.9
12 | */
13 | (function (global, factory) {
14 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
15 | typeof define === 'function' && define.amd ? define(factory) :
16 | (global = global || self, global.format = factory());
17 | }(this, function () { 'use strict';
18 |
19 | function _slicedToArray(arr, i) {
20 | return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
21 | }
22 |
23 | function _arrayWithHoles(arr) {
24 | if (Array.isArray(arr)) return arr;
25 | }
26 |
27 | function _iterableToArrayLimit(arr, i) {
28 | var _arr = [];
29 | var _n = true;
30 | var _d = false;
31 | var _e = undefined;
32 |
33 | try {
34 | for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
35 | _arr.push(_s.value);
36 |
37 | if (i && _arr.length === i) break;
38 | }
39 | } catch (err) {
40 | _d = true;
41 | _e = err;
42 | } finally {
43 | try {
44 | if (!_n && _i["return"] != null) _i["return"]();
45 | } finally {
46 | if (_d) throw _e;
47 | }
48 | }
49 |
50 | return _arr;
51 | }
52 |
53 | function _nonIterableRest() {
54 | throw new TypeError("Invalid attempt to destructure non-iterable instance");
55 | }
56 |
57 | var maskRegex = /[0-9\-+#]/;
58 | var notMaskRegex = /[^\d\-+#]/g;
59 |
60 | function getIndex(mask) {
61 | return mask.search(maskRegex);
62 | }
63 |
64 | function processMask() {
65 | var mask = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "#.##";
66 | var maskObj = {};
67 | var len = mask.length;
68 | var start = getIndex(mask);
69 | maskObj.prefix = start > 0 ? mask.substring(0, start) : ""; // Reverse string: not an ideal method if there are surrogate pairs
70 |
71 | var end = getIndex(mask.split("").reverse().join(""));
72 | var offset = len - end;
73 | var substr = mask.substring(offset, offset + 1); // Add 1 to offset if mask has a trailing decimal/comma
74 |
75 | var indx = offset + (substr === "." || substr === "," ? 1 : 0);
76 | maskObj.suffix = end > 0 ? mask.substring(indx, len) : "";
77 | maskObj.mask = mask.substring(start, indx);
78 | maskObj.maskHasNegativeSign = maskObj.mask.charAt(0) === "-";
79 | maskObj.maskHasPositiveSign = maskObj.mask.charAt(0) === "+"; // Search for group separator & decimal; anything not digit,
80 | // not +/- sign, and not #
81 |
82 | var result = maskObj.mask.match(notMaskRegex); // Treat the right most symbol as decimal
83 |
84 | maskObj.decimal = result && result[result.length - 1] || "."; // Treat the left most symbol as group separator
85 |
86 | maskObj.separator = result && result[1] && result[0] || ","; // Split the decimal for the format string if any
87 |
88 | result = maskObj.mask.split(maskObj.decimal);
89 | maskObj.integer = result[0];
90 | maskObj.fraction = result[1];
91 | return maskObj;
92 | }
93 |
94 | function processValue(value, maskObj, options) {
95 | var isNegative = false;
96 | var valObj = {
97 | value: value
98 | };
99 |
100 | if (value < 0) {
101 | isNegative = true; // Process only abs(), and turn on flag.
102 |
103 | valObj.value = -valObj.value;
104 | }
105 |
106 | valObj.sign = isNegative ? "-" : ""; // Fix the decimal first, toFixed will auto fill trailing zero.
107 |
108 | valObj.value = Number(valObj.value).toFixed(maskObj.fraction && maskObj.fraction.length); // Convert number to string to trim off *all* trailing decimal zero(es)
109 |
110 | valObj.value = Number(valObj.value).toString(); // Fill back any trailing zero according to format
111 | // look for last zero in format
112 |
113 | var posTrailZero = maskObj.fraction && maskObj.fraction.lastIndexOf("0");
114 |
115 | var _valObj$value$split = valObj.value.split("."),
116 | _valObj$value$split2 = _slicedToArray(_valObj$value$split, 2),
117 | _valObj$value$split2$ = _valObj$value$split2[0],
118 | valInteger = _valObj$value$split2$ === void 0 ? "0" : _valObj$value$split2$,
119 | _valObj$value$split2$2 = _valObj$value$split2[1],
120 | valFraction = _valObj$value$split2$2 === void 0 ? "" : _valObj$value$split2$2;
121 |
122 | if (!valFraction || valFraction && valFraction.length <= posTrailZero) {
123 | valFraction = posTrailZero < 0 ? "" : Number("0." + valFraction).toFixed(posTrailZero + 1).replace("0.", "");
124 | }
125 |
126 | valObj.integer = valInteger;
127 | valObj.fraction = valFraction;
128 | addSeparators(valObj, maskObj); // Remove negative sign if result is zero
129 |
130 | if (valObj.result === "0" || valObj.result === "") {
131 | // Remove negative sign if result is zero
132 | isNegative = false;
133 | valObj.sign = "";
134 | }
135 |
136 | if (!isNegative && maskObj.maskHasPositiveSign) {
137 | valObj.sign = "+";
138 | } else if (isNegative && maskObj.maskHasPositiveSign) {
139 | valObj.sign = "-";
140 | } else if (isNegative) {
141 | valObj.sign = options && options.enforceMaskSign && !maskObj.maskHasNegativeSign ? "" : "-";
142 | }
143 |
144 | return valObj;
145 | }
146 |
147 | function addSeparators(valObj, maskObj) {
148 | valObj.result = ""; // Look for separator
149 |
150 | var szSep = maskObj.integer.split(maskObj.separator); // Join back without separator for counting the pos of any leading 0
151 |
152 | var maskInteger = szSep.join("");
153 | var posLeadZero = maskInteger && maskInteger.indexOf("0");
154 |
155 | if (posLeadZero > -1) {
156 | while (valObj.integer.length < maskInteger.length - posLeadZero) {
157 | valObj.integer = "0" + valObj.integer;
158 | }
159 | } else if (Number(valObj.integer) === 0) {
160 | valObj.integer = "";
161 | } // Process the first group separator from decimal (.) only, the rest ignore.
162 | // get the length of the last slice of split result.
163 |
164 |
165 | var posSeparator = szSep[1] && szSep[szSep.length - 1].length;
166 |
167 | if (posSeparator) {
168 | var len = valObj.integer.length;
169 | var offset = len % posSeparator;
170 |
171 | for (var indx = 0; indx < len; indx++) {
172 | valObj.result += valObj.integer.charAt(indx); // -posSeparator so that won't trail separator on full length
173 |
174 | if (!((indx - offset + 1) % posSeparator) && indx < len - posSeparator) {
175 | valObj.result += maskObj.separator;
176 | }
177 | }
178 | } else {
179 | valObj.result = valObj.integer;
180 | }
181 |
182 | valObj.result += maskObj.fraction && valObj.fraction ? maskObj.decimal + valObj.fraction : "";
183 | return valObj;
184 | }
185 |
186 | var format = (function (mask, value) {
187 | var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
188 |
189 | if (!mask || isNaN(Number(value))) {
190 | // Invalid inputs
191 | return value;
192 | }
193 |
194 | var maskObj = processMask(mask);
195 | var valObj = processValue(value, maskObj, options);
196 | return maskObj.prefix + valObj.sign + valObj.result + maskObj.suffix;
197 | });
198 |
199 | return format;
200 |
201 | }));
202 |
--------------------------------------------------------------------------------
/lib/format.esm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Javascript-number-formatter
3 | * Lightweight & Fast JavaScript Number Formatter
4 | *
5 | * @preserve IntegraXor Web SCADA - JavaScript Number Formatter (http://www.integraxor.com/)
6 | * @author KPL
7 | * @maintainer Rob Garrison
8 | * @copyright 2019 ecava
9 | * @license MIT
10 | * @link http://mottie.github.com/javascript-number-formatter/
11 | * @version 2.0.9
12 | */
13 | const maskRegex = /[0-9\-+#]/;
14 | const notMaskRegex = /[^\d\-+#]/g;
15 |
16 | function getIndex(mask) {
17 | return mask.search(maskRegex);
18 | }
19 |
20 | function processMask(mask = "#.##") {
21 | const maskObj = {};
22 | const len = mask.length;
23 | const start = getIndex(mask);
24 | maskObj.prefix = start > 0 ? mask.substring(0, start) : "";
25 |
26 | // Reverse string: not an ideal method if there are surrogate pairs
27 | const end = getIndex(mask.split("").reverse().join(""));
28 | const offset = len - end;
29 | const substr = mask.substring(offset, offset + 1);
30 | // Add 1 to offset if mask has a trailing decimal/comma
31 | const indx = offset + ((substr === "." || (substr === ",")) ? 1 : 0);
32 | maskObj.suffix = end > 0 ? mask.substring(indx, len) : "";
33 |
34 | maskObj.mask = mask.substring(start, indx);
35 | maskObj.maskHasNegativeSign = maskObj.mask.charAt(0) === "-";
36 | maskObj.maskHasPositiveSign = maskObj.mask.charAt(0) === "+";
37 |
38 | // Search for group separator & decimal; anything not digit,
39 | // not +/- sign, and not #
40 | let result = maskObj.mask.match(notMaskRegex);
41 | // Treat the right most symbol as decimal
42 | maskObj.decimal = (result && result[result.length - 1]) || ".";
43 | // Treat the left most symbol as group separator
44 | maskObj.separator = (result && result[1] && result[0]) || ",";
45 |
46 | // Split the decimal for the format string if any
47 | result = maskObj.mask.split(maskObj.decimal);
48 | maskObj.integer = result[0];
49 | maskObj.fraction = result[1];
50 | return maskObj;
51 | }
52 |
53 | function processValue(value, maskObj, options) {
54 | let isNegative = false;
55 | const valObj = {
56 | value
57 | };
58 | if (value < 0) {
59 | isNegative = true;
60 | // Process only abs(), and turn on flag.
61 | valObj.value = -valObj.value;
62 | }
63 |
64 | valObj.sign = isNegative ? "-" : "";
65 |
66 | // Fix the decimal first, toFixed will auto fill trailing zero.
67 | valObj.value = Number(valObj.value).toFixed(maskObj.fraction && maskObj.fraction.length);
68 | // Convert number to string to trim off *all* trailing decimal zero(es)
69 | valObj.value = Number(valObj.value).toString();
70 |
71 | // Fill back any trailing zero according to format
72 | // look for last zero in format
73 | const posTrailZero = maskObj.fraction && maskObj.fraction.lastIndexOf("0");
74 | let [valInteger = "0", valFraction = ""] = valObj.value.split(".");
75 | if (!valFraction || (valFraction && valFraction.length <= posTrailZero)) {
76 | valFraction = posTrailZero < 0
77 | ? ""
78 | : (Number("0." + valFraction).toFixed(posTrailZero + 1)).replace("0.", "");
79 | }
80 |
81 | valObj.integer = valInteger;
82 | valObj.fraction = valFraction;
83 | addSeparators(valObj, maskObj);
84 |
85 | // Remove negative sign if result is zero
86 | if (valObj.result === "0" || valObj.result === "") {
87 | // Remove negative sign if result is zero
88 | isNegative = false;
89 | valObj.sign = "";
90 | }
91 |
92 | if (!isNegative && maskObj.maskHasPositiveSign) {
93 | valObj.sign = "+";
94 | } else if (isNegative && maskObj.maskHasPositiveSign) {
95 | valObj.sign = "-";
96 | } else if (isNegative) {
97 | valObj.sign = options && options.enforceMaskSign && !maskObj.maskHasNegativeSign
98 | ? ""
99 | : "-";
100 | }
101 |
102 | return valObj;
103 | }
104 |
105 | function addSeparators(valObj, maskObj) {
106 | valObj.result = "";
107 | // Look for separator
108 | const szSep = maskObj.integer.split(maskObj.separator);
109 | // Join back without separator for counting the pos of any leading 0
110 | const maskInteger = szSep.join("");
111 |
112 | const posLeadZero = maskInteger && maskInteger.indexOf("0");
113 | if (posLeadZero > -1) {
114 | while (valObj.integer.length < (maskInteger.length - posLeadZero)) {
115 | valObj.integer = "0" + valObj.integer;
116 | }
117 | } else if (Number(valObj.integer) === 0) {
118 | valObj.integer = "";
119 | }
120 |
121 | // Process the first group separator from decimal (.) only, the rest ignore.
122 | // get the length of the last slice of split result.
123 | const posSeparator = (szSep[1] && szSep[szSep.length - 1].length);
124 | if (posSeparator) {
125 | const len = valObj.integer.length;
126 | const offset = len % posSeparator;
127 | for (let indx = 0; indx < len; indx++) {
128 | valObj.result += valObj.integer.charAt(indx);
129 | // -posSeparator so that won't trail separator on full length
130 | if (!((indx - offset + 1) % posSeparator) && indx < len - posSeparator) {
131 | valObj.result += maskObj.separator;
132 | }
133 | }
134 | } else {
135 | valObj.result = valObj.integer;
136 | }
137 |
138 | valObj.result += (maskObj.fraction && valObj.fraction)
139 | ? maskObj.decimal + valObj.fraction
140 | : "";
141 | return valObj;
142 | }
143 |
144 | var format = (mask, value, options = {}) => {
145 | if (!mask || isNaN(Number(value))) {
146 | // Invalid inputs
147 | return value;
148 | }
149 |
150 | const maskObj = processMask(mask);
151 | const valObj = processValue(value, maskObj, options);
152 | return maskObj.prefix + valObj.sign + valObj.result + maskObj.suffix;
153 | };
154 |
155 | export default format;
156 |
--------------------------------------------------------------------------------
/lib/format.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Javascript-number-formatter
3 | * Lightweight & Fast JavaScript Number Formatter
4 | *
5 | * @preserve IntegraXor Web SCADA - JavaScript Number Formatter (http://www.integraxor.com/)
6 | * @author KPL
7 | * @maintainer Rob Garrison
8 | * @copyright 2019 ecava
9 | * @license MIT
10 | * @link http://mottie.github.com/javascript-number-formatter/
11 | * @version 2.0.9
12 | */
13 | (function (global, factory) {
14 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
15 | typeof define === 'function' && define.amd ? define(factory) :
16 | (global = global || self, global.format = factory());
17 | }(this, function () { 'use strict';
18 |
19 | const maskRegex = /[0-9\-+#]/;
20 | const notMaskRegex = /[^\d\-+#]/g;
21 |
22 | function getIndex(mask) {
23 | return mask.search(maskRegex);
24 | }
25 |
26 | function processMask(mask = "#.##") {
27 | const maskObj = {};
28 | const len = mask.length;
29 | const start = getIndex(mask);
30 | maskObj.prefix = start > 0 ? mask.substring(0, start) : "";
31 |
32 | // Reverse string: not an ideal method if there are surrogate pairs
33 | const end = getIndex(mask.split("").reverse().join(""));
34 | const offset = len - end;
35 | const substr = mask.substring(offset, offset + 1);
36 | // Add 1 to offset if mask has a trailing decimal/comma
37 | const indx = offset + ((substr === "." || (substr === ",")) ? 1 : 0);
38 | maskObj.suffix = end > 0 ? mask.substring(indx, len) : "";
39 |
40 | maskObj.mask = mask.substring(start, indx);
41 | maskObj.maskHasNegativeSign = maskObj.mask.charAt(0) === "-";
42 | maskObj.maskHasPositiveSign = maskObj.mask.charAt(0) === "+";
43 |
44 | // Search for group separator & decimal; anything not digit,
45 | // not +/- sign, and not #
46 | let result = maskObj.mask.match(notMaskRegex);
47 | // Treat the right most symbol as decimal
48 | maskObj.decimal = (result && result[result.length - 1]) || ".";
49 | // Treat the left most symbol as group separator
50 | maskObj.separator = (result && result[1] && result[0]) || ",";
51 |
52 | // Split the decimal for the format string if any
53 | result = maskObj.mask.split(maskObj.decimal);
54 | maskObj.integer = result[0];
55 | maskObj.fraction = result[1];
56 | return maskObj;
57 | }
58 |
59 | function processValue(value, maskObj, options) {
60 | let isNegative = false;
61 | const valObj = {
62 | value
63 | };
64 | if (value < 0) {
65 | isNegative = true;
66 | // Process only abs(), and turn on flag.
67 | valObj.value = -valObj.value;
68 | }
69 |
70 | valObj.sign = isNegative ? "-" : "";
71 |
72 | // Fix the decimal first, toFixed will auto fill trailing zero.
73 | valObj.value = Number(valObj.value).toFixed(maskObj.fraction && maskObj.fraction.length);
74 | // Convert number to string to trim off *all* trailing decimal zero(es)
75 | valObj.value = Number(valObj.value).toString();
76 |
77 | // Fill back any trailing zero according to format
78 | // look for last zero in format
79 | const posTrailZero = maskObj.fraction && maskObj.fraction.lastIndexOf("0");
80 | let [valInteger = "0", valFraction = ""] = valObj.value.split(".");
81 | if (!valFraction || (valFraction && valFraction.length <= posTrailZero)) {
82 | valFraction = posTrailZero < 0
83 | ? ""
84 | : (Number("0." + valFraction).toFixed(posTrailZero + 1)).replace("0.", "");
85 | }
86 |
87 | valObj.integer = valInteger;
88 | valObj.fraction = valFraction;
89 | addSeparators(valObj, maskObj);
90 |
91 | // Remove negative sign if result is zero
92 | if (valObj.result === "0" || valObj.result === "") {
93 | // Remove negative sign if result is zero
94 | isNegative = false;
95 | valObj.sign = "";
96 | }
97 |
98 | if (!isNegative && maskObj.maskHasPositiveSign) {
99 | valObj.sign = "+";
100 | } else if (isNegative && maskObj.maskHasPositiveSign) {
101 | valObj.sign = "-";
102 | } else if (isNegative) {
103 | valObj.sign = options && options.enforceMaskSign && !maskObj.maskHasNegativeSign
104 | ? ""
105 | : "-";
106 | }
107 |
108 | return valObj;
109 | }
110 |
111 | function addSeparators(valObj, maskObj) {
112 | valObj.result = "";
113 | // Look for separator
114 | const szSep = maskObj.integer.split(maskObj.separator);
115 | // Join back without separator for counting the pos of any leading 0
116 | const maskInteger = szSep.join("");
117 |
118 | const posLeadZero = maskInteger && maskInteger.indexOf("0");
119 | if (posLeadZero > -1) {
120 | while (valObj.integer.length < (maskInteger.length - posLeadZero)) {
121 | valObj.integer = "0" + valObj.integer;
122 | }
123 | } else if (Number(valObj.integer) === 0) {
124 | valObj.integer = "";
125 | }
126 |
127 | // Process the first group separator from decimal (.) only, the rest ignore.
128 | // get the length of the last slice of split result.
129 | const posSeparator = (szSep[1] && szSep[szSep.length - 1].length);
130 | if (posSeparator) {
131 | const len = valObj.integer.length;
132 | const offset = len % posSeparator;
133 | for (let indx = 0; indx < len; indx++) {
134 | valObj.result += valObj.integer.charAt(indx);
135 | // -posSeparator so that won't trail separator on full length
136 | if (!((indx - offset + 1) % posSeparator) && indx < len - posSeparator) {
137 | valObj.result += maskObj.separator;
138 | }
139 | }
140 | } else {
141 | valObj.result = valObj.integer;
142 | }
143 |
144 | valObj.result += (maskObj.fraction && valObj.fraction)
145 | ? maskObj.decimal + valObj.fraction
146 | : "";
147 | return valObj;
148 | }
149 |
150 | var format = (mask, value, options = {}) => {
151 | if (!mask || isNaN(Number(value))) {
152 | // Invalid inputs
153 | return value;
154 | }
155 |
156 | const maskObj = processMask(mask);
157 | const valObj = processValue(value, maskObj, options);
158 | return maskObj.prefix + valObj.sign + valObj.result + maskObj.suffix;
159 | };
160 |
161 | return format;
162 |
163 | }));
164 |
--------------------------------------------------------------------------------
/lib/format.min.js:
--------------------------------------------------------------------------------
1 | /*! Javascript-number-formatter v2.0.9 */
2 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).format=t()}(this,function(){"use strict";const e=/[0-9\-+#]/,t=/[^\d\-+#]/g;function n(t){return t.search(e)}return(e,i,r={})=>{if(!e||isNaN(Number(i)))return i;const s=function(e="#.##"){const i={},r=e.length,s=n(e);i.prefix=s>0?e.substring(0,s):"";const a=n(e.split("").reverse().join("")),o=r-a,u=e.substring(o,o+1),l=o+("."===u||","===u?1:0);i.suffix=a>0?e.substring(l,r):"",i.mask=e.substring(s,l),i.maskHasNegativeSign="-"===i.mask.charAt(0),i.maskHasPositiveSign="+"===i.mask.charAt(0);let g=i.mask.match(t);return i.decimal=g&&g[g.length-1]||".",i.separator=g&&g[1]&&g[0]||",",g=i.mask.split(i.decimal),i.integer=g[0],i.fraction=g[1],i}(e),a=function(e,t,n){let i=!1;const r={value:e};e<0&&(i=!0,r.value=-r.value),r.sign=i?"-":"",r.value=Number(r.value).toFixed(t.fraction&&t.fraction.length),r.value=Number(r.value).toString();const s=t.fraction&&t.fraction.lastIndexOf("0");let[a="0",o=""]=r.value.split(".");return(!o||o&&o.length<=s)&&(o=s<0?"":Number("0."+o).toFixed(s+1).replace("0.","")),r.integer=a,r.fraction=o,function(e,t){e.result="";const n=t.integer.split(t.separator),i=n.join(""),r=i&&i.indexOf("0");if(r>-1)for(;e.integer.length=8"
57 | },
58 | "scripts": {
59 | "build": "rollup -c",
60 | "test": "xo && ava",
61 | "updater": "npx updates -cu && npm install"
62 | },
63 | "typings": "./index.d.ts",
64 | "devDependencies": {
65 | "@babel/core": "^7.2.2",
66 | "@babel/plugin-transform-object-assign": "^7.2.0",
67 | "@babel/preset-env": "^7.3.1",
68 | "ava": "*",
69 | "rollup": "^1.1.2",
70 | "rollup-plugin-babel": "^4.3.2",
71 | "rollup-plugin-cjs-es": "^0.7.0",
72 | "rollup-plugin-node-resolve": "^4.0.0",
73 | "rollup-plugin-terser": "^4.0.3",
74 | "updates": "^6.2.1",
75 | "xo": "*"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import cjs from "rollup-plugin-cjs-es";
2 | import resolve from "rollup-plugin-node-resolve";
3 | import babel from "rollup-plugin-babel";
4 | import {terser} from "rollup-plugin-terser";
5 |
6 | import pkg from "./package.json";
7 |
8 | const banner = `/**
9 | * Javascript-number-formatter
10 | * Lightweight & Fast JavaScript Number Formatter
11 | *
12 | * @preserve IntegraXor Web SCADA - JavaScript Number Formatter (http://www.integraxor.com/)
13 | * @author KPL
14 | * @maintainer Rob Garrison
15 | * @copyright ${new Date().getFullYear()} ecava
16 | * @license MIT
17 | * @link http://mottie.github.com/javascript-number-formatter/
18 | * @version ${pkg.version}
19 | */`;
20 |
21 | export default [{
22 | input: "src/format.js",
23 | output: [{
24 | file: "lib/format.js",
25 | name: "format",
26 | format: "umd",
27 | sourceMap: false,
28 | banner,
29 | },{
30 | file: "lib/format.esm.js",
31 | name: "format",
32 | format: "esm",
33 | sourceMap: false,
34 | banner,
35 | }],
36 | plugins: [
37 | resolve(),
38 | cjs({
39 | nested: true
40 | })
41 | ]
42 | }, {
43 | input: "src/format.js",
44 | output: [{
45 | file: "lib/format.es5.js",
46 | name: "format",
47 | format: "umd",
48 | sourceMap: false,
49 | banner,
50 | }],
51 | plugins: [
52 | resolve(),
53 | cjs({
54 | nested: true
55 | }),
56 | babel({
57 | exclude: "node_modules/**",
58 | presets: [
59 | ["@babel/preset-env", {
60 | modules: false
61 | }]
62 | ],
63 | plugins: [
64 | "@babel/plugin-transform-object-assign"
65 | ]
66 | }),
67 | ]
68 | },
69 |
70 | {
71 | input: "src/format.js",
72 | output: {
73 | file: "lib/format.min.js",
74 | name: "format",
75 | format: "umd",
76 | sourceMap: false,
77 | banner: `/*! Javascript-number-formatter v${pkg.version} */`,
78 | },
79 | plugins: [
80 | resolve(),
81 | cjs({
82 | nested: true
83 | }),
84 | terser({
85 | compress: {
86 | passes: 3
87 | },
88 | output: {
89 | comments: /^!/
90 | }
91 | })
92 | ]
93 | }];
94 |
--------------------------------------------------------------------------------
/src/format.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const maskRegex = /[0-9\-+#]/;
4 | const notMaskRegex = /[^\d\-+#]/g;
5 |
6 | function getIndex(mask) {
7 | return mask.search(maskRegex);
8 | }
9 |
10 | function processMask(mask = "#.##") {
11 | const maskObj = {};
12 | const len = mask.length;
13 | const start = getIndex(mask);
14 | maskObj.prefix = start > 0 ? mask.substring(0, start) : "";
15 |
16 | // Reverse string: not an ideal method if there are surrogate pairs
17 | const end = getIndex(mask.split("").reverse().join(""));
18 | const offset = len - end;
19 | const substr = mask.substring(offset, offset + 1);
20 | // Add 1 to offset if mask has a trailing decimal/comma
21 | const indx = offset + ((substr === "." || (substr === ",")) ? 1 : 0);
22 | maskObj.suffix = end > 0 ? mask.substring(indx, len) : "";
23 |
24 | maskObj.mask = mask.substring(start, indx);
25 | maskObj.maskHasNegativeSign = maskObj.mask.charAt(0) === "-";
26 | maskObj.maskHasPositiveSign = maskObj.mask.charAt(0) === "+";
27 |
28 | // Search for group separator & decimal; anything not digit,
29 | // not +/- sign, and not #
30 | let result = maskObj.mask.match(notMaskRegex);
31 | // Treat the right most symbol as decimal
32 | maskObj.decimal = (result && result[result.length - 1]) || ".";
33 | // Treat the left most symbol as group separator
34 | maskObj.separator = (result && result[1] && result[0]) || ",";
35 |
36 | // Split the decimal for the format string if any
37 | result = maskObj.mask.split(maskObj.decimal);
38 | maskObj.integer = result[0];
39 | maskObj.fraction = result[1];
40 | return maskObj;
41 | }
42 |
43 | function processValue(value, maskObj, options) {
44 | let isNegative = false;
45 | const valObj = {
46 | value
47 | };
48 | if (value < 0) {
49 | isNegative = true;
50 | // Process only abs(), and turn on flag.
51 | valObj.value = -valObj.value;
52 | }
53 |
54 | valObj.sign = isNegative ? "-" : "";
55 |
56 | // Fix the decimal first, toFixed will auto fill trailing zero.
57 | valObj.value = Number(valObj.value).toFixed(maskObj.fraction && maskObj.fraction.length);
58 | // Convert number to string to trim off *all* trailing decimal zero(es)
59 | valObj.value = Number(valObj.value).toString();
60 |
61 | // Fill back any trailing zero according to format
62 | // look for last zero in format
63 | const posTrailZero = maskObj.fraction && maskObj.fraction.lastIndexOf("0");
64 | let [valInteger = "0", valFraction = ""] = valObj.value.split(".");
65 | if (!valFraction || (valFraction && valFraction.length <= posTrailZero)) {
66 | valFraction = posTrailZero < 0
67 | ? ""
68 | : (Number("0." + valFraction).toFixed(posTrailZero + 1)).replace("0.", "");
69 | }
70 |
71 | valObj.integer = valInteger;
72 | valObj.fraction = valFraction;
73 | addSeparators(valObj, maskObj);
74 |
75 | // Remove negative sign if result is zero
76 | if (valObj.result === "0" || valObj.result === "") {
77 | // Remove negative sign if result is zero
78 | isNegative = false;
79 | valObj.sign = "";
80 | }
81 |
82 | if (!isNegative && maskObj.maskHasPositiveSign) {
83 | valObj.sign = "+";
84 | } else if (isNegative && maskObj.maskHasPositiveSign) {
85 | valObj.sign = "-";
86 | } else if (isNegative) {
87 | valObj.sign = options && options.enforceMaskSign && !maskObj.maskHasNegativeSign
88 | ? ""
89 | : "-";
90 | }
91 |
92 | return valObj;
93 | }
94 |
95 | function addSeparators(valObj, maskObj) {
96 | valObj.result = "";
97 | // Look for separator
98 | const szSep = maskObj.integer.split(maskObj.separator);
99 | // Join back without separator for counting the pos of any leading 0
100 | const maskInteger = szSep.join("");
101 |
102 | const posLeadZero = maskInteger && maskInteger.indexOf("0");
103 | if (posLeadZero > -1) {
104 | while (valObj.integer.length < (maskInteger.length - posLeadZero)) {
105 | valObj.integer = "0" + valObj.integer;
106 | }
107 | } else if (Number(valObj.integer) === 0) {
108 | valObj.integer = "";
109 | }
110 |
111 | // Process the first group separator from decimal (.) only, the rest ignore.
112 | // get the length of the last slice of split result.
113 | const posSeparator = (szSep[1] && szSep[szSep.length - 1].length);
114 | if (posSeparator) {
115 | const len = valObj.integer.length;
116 | const offset = len % posSeparator;
117 | for (let indx = 0; indx < len; indx++) {
118 | valObj.result += valObj.integer.charAt(indx);
119 | // -posSeparator so that won't trail separator on full length
120 | if (!((indx - offset + 1) % posSeparator) && indx < len - posSeparator) {
121 | valObj.result += maskObj.separator;
122 | }
123 | }
124 | } else {
125 | valObj.result = valObj.integer;
126 | }
127 |
128 | valObj.result += (maskObj.fraction && valObj.fraction)
129 | ? maskObj.decimal + valObj.fraction
130 | : "";
131 | return valObj;
132 | }
133 |
134 | module.exports = (mask, value, options = {}) => {
135 | if (!mask || isNaN(Number(value))) {
136 | // Invalid inputs
137 | return value;
138 | }
139 |
140 | const maskObj = processMask(mask);
141 | const valObj = processValue(value, maskObj, options);
142 | return maskObj.prefix + valObj.sign + valObj.result + maskObj.suffix;
143 | };
144 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | import test from "ava";
2 | import format from "../src/format";
3 |
4 | /* Simple mask */
5 | test("basic masks", t => {
6 | t.is(format("#,##0.00", 123456789.123), "123,456,789.12", "Mask: \"#,##0.00\"");
7 | t.is(format("#,##0.00", "123456.789"), "123,456.79");
8 | t.is(format("#,##0.00", 123456.789), "123,456.79");
9 | t.is(format("#,##0.00", 123456.7), "123,456.70");
10 | t.is(format("#,##0.00", 123456), "123,456.00");
11 | t.is(format("#,##0.00", 0), "0.00");
12 | t.is(format("#", -0.1), "");
13 | t.is(format("0", -0.1), "0");
14 | t.is(format("0.#", -0.13), "-0.1");
15 | t.is(format("#,##0.00", -123), "-123.00");
16 | t.is(format("#,##0.00", -123456.789), "-123,456.79");
17 |
18 | t.is(format("#,##0.0", 123456789.123), "123,456,789.1", "Mask: \"#,##0.0\"");
19 | t.is(format("#,##0.0", 123456.789), "123,456.8");
20 | t.is(format("#,##0.0", 123456.7), "123,456.7");
21 | t.is(format("#,##0.0", 123456), "123,456.0");
22 | t.is(format("#,##0.0", 0), "0.0");
23 | t.is(format("#,##0.0", -123), "-123.0");
24 | t.is(format("#,##0.0", -123456.789), "-123,456.8");
25 |
26 | t.is(format("#,##0.", 123456789.123), "123,456,789", "Mask: \"#,##0.\"");
27 | t.is(format("#,##0.", 123456.789), "123,457");
28 | t.is(format("#,##0.", 123456.7), "123,457");
29 | t.is(format("#,##0.", 123456), "123,456");
30 | t.is(format("#,##0.", 0), "0");
31 | t.is(format("#,##0.", -123), "-123");
32 | t.is(format("#,##0.", -123456.789), "-123,457");
33 |
34 | t.is(format("#.##0,", 123456789.123), "123.456.789", "Mask: \"#.##0,\"");
35 | t.is(format("#.##0,", 123456.789), "123.457");
36 | t.is(format("#.##0,", 123456.7), "123.457");
37 | t.is(format("#.##0,", 123456), "123.456");
38 | t.is(format("#.##0,", 0), "0");
39 | t.is(format("#.##0,", -123), "-123");
40 | t.is(format("#.##0,", -123456.789), "-123.457");
41 |
42 | t.is(format("#,##0.###0", 12345678.98765432), "12,345,678.9877", "Mask: \"#,##0.###0\"");
43 | });
44 |
45 | /* Localizations */
46 | test("Localizations", t => {
47 | t.is(format("### ###,##", 123456789.987654321), "123 456 789,99", "Estonia, France: ### ###,##");
48 | t.is(format("##.000,00", 123456789.987654321), "123.456.789,99", "Germany, Italy: ##.000,00");
49 | t.is(format("###,####.00", 123456789.987654321), "1,2345,6789.99", "Japan: ###,####.00");
50 | t.is(format("#'###'#00.00", 123456789.987654321), "123'456'789.99", "Switzerland: #'###'#00.00");
51 | });
52 |
53 | /* Made-up-izations */
54 | test("Any format", t => {
55 | t.is(format("#,##0 00", 123456789.987654321), "123,456,789 99");
56 | t.is(format("#x##0 00", 123456789.987654321), "123x456x789 99");
57 | t.is(format("##^000*00", 123456789.987654321), "123^456^789*99");
58 | t.is(format("##¿000$00", 123456789.987654321), "123¿456¿789$99");
59 | t.is(format("00!00@00", 123456789.987654321), "1!23!45!67!89@99");
60 | });
61 |
62 | /* Any non-zero digit in mask behaves like # */
63 | test("Non-zero digits", t => {
64 | t.is(format("999.999", 123.0), "123");
65 | t.is(format("123,456789", 123.0), "123");
66 | });
67 |
68 | /* Precision */
69 | test("Precision", t => {
70 | t.is(format("### ###,", 123456789.987654321), "123 456 790");
71 | t.is(format("###.###,", 123456789.987654321), "123.456.790");
72 | t.is(format("##,000.", 123456789.987654321), "123,456,790");
73 | t.is(format("###,####.", 123456789.187654321), "1,2345,6789");
74 | t.is(format("#'###'#00,", 123456789.087654321), "123'456'789");
75 | t.is(format("#,##0.####", 1234567.890), "1,234,567.89");
76 | t.is(format("#,##0.###0", 1234567.890), "1,234,567.8900");
77 | t.is(format("#,##0.##0#", 1234567.890), "1,234,567.890");
78 | t.is(format("#,##0.#0##", 1234567.890), "1,234,567.89");
79 | t.is(format("#,##0.#", 1234567.890), "1,234,567.9");
80 | t.is(format("#,###.", 1234567.890), "1,234,568");
81 |
82 | t.is(format("#,###.", 1234567), "1,234,567");
83 | t.is(format("#,###.#", 1234567), "1,234,567");
84 | t.is(format("#,###.##", 1234567), "1,234,567");
85 | t.is(format("#,###.0", 1234567), "1,234,567.0");
86 | t.is(format("#,###.00", 1234567), "1,234,567.00");
87 |
88 | t.is(format("#.00", 0.78), ".78");
89 | t.is(format("#.000", 0.78), ".780");
90 | t.is(format("#.0000", 0.78), ".7800");
91 | t.is(format("0.00", 0.78), "0.78");
92 | t.is(format("0.000", 0.78), "0.780");
93 | t.is(format("0.0000", 0.78), "0.7800");
94 | t.is(format("00.00", 0.78), "00.78");
95 |
96 | t.is(format("#.##", 0), "");
97 | t.is(format("0.00", 0), "0.00");
98 | t.is(format("0", 0), "0");
99 | });
100 |
101 | /* Mask with prefix and/or suffix */
102 | test("Prefix & Suffix", t => {
103 | // Usage
104 | t.is(format("$#,##0.00USD", 123456789.123), "$123,456,789.12USD", "$#,##0.00USD");
105 | t.is(format("$ #,##0.00 USD", 123456789.123), "$ 123,456,789.12 USD", "$ #,##0.00 USD");
106 | t.is(format("##.000,00 €", 123456789.123), "123.456.789,12 €", "##.000,00 €");
107 | t.is(format("###,####.00 ¥", 123456789.123), "1,2345,6789.12 ¥", "###,####.00 ¥");
108 |
109 | t.is(format("### ###,### ¢ and stuff", 123456789.123), "123 456 789,123 ¢ and stuff", "### ###,### ¢ and stuff");
110 | t.is(format(" #,##0.00 a b c ", 123456789.123), " 123,456,789.12 a b c ", "leading & trailing spaces");
111 |
112 | t.is(format("$ (#,###.00) Money", 123456789.123), "$ (123,456,789.12) Money", "spaces & mask wrapped in parenthesis");
113 | t.is(format("prefix with a comma, includes everything? #.00 yep!", 123456789.123), "prefix with a comma, includes everything? 123456789.12 yep!", "prefix with a comma");
114 | t.is(format("$# ###,00 USD, or euros.", 123456789.123), "$123 456 789,12 USD, or euros.", "suffix with comma & period");
115 | t.is(format("prefix with a periods?... #.00 yep!", 123456789.123), "prefix with a periods?... 123456789.12 yep!", "prefix with a periods");
116 | t.is(format("It costs $# ###,00 euros.", 123456789.123), "It costs $123 456 789,12 euros.", "suffix with period");
117 | t.is(format("test:### ###. ing", 123456789.123), "test:123 456 789 ing", "Hanging decimals");
118 | });
119 |
120 | test("Masks that don't work", t => {
121 | // Not allowed
122 | t.is(format("No # outside of the mask $#,###.00", 123456789.123) !== "No # outside of the mask $123,456,789.12", true, "BROKEN: # outside of the mask");
123 | t.is(format("99 items = $#,###.00", 123456789.123) !== "99 items = $123,456,789.12", true, "BROKEN: numbers outside of mask");
124 | t.is(format("cost -- $#,###.00 -- value", 123456789.123) !== "cost -- $123,456,789.12 -- value", true, "BROKEN: dashes outside of mask");
125 | t.is(format("++ value! $#,###.00 ++ value!", 123456789.123) !== "++ value! $123,456,789.12 ++ value!", true, "BROKEN: plus signs outside of mask");
126 | });
127 |
128 | test("Numbers with negative sign", t => {
129 | t.is(format("-#,##0.######", -5000.123456789), "-5,000.123457");
130 | t.is(format("-#,##0.######", 5000.123456789), "5,000.123457");
131 | t.is(format("#,##0.######", -5000.123456789), "-5,000.123457");
132 | t.is(format("$ #,###.00", -1234567.890), "$ -1,234,567.89");
133 | t.is(format("$ -#,###.00", -1234567.890), "$ -1,234,567.89");
134 |
135 | t.is(format("-#,##0.######", -5000.123456789, {enforceMaskSign: true}), "-5,000.123457");
136 | t.is(format("-#,##0.######", 5000.123456789, {enforceMaskSign: true}), "5,000.123457");
137 | t.is(format("#,##0.######", -5000.123456789, {enforceMaskSign: true}), "5,000.123457");
138 | t.is(format("$ #,###.00", -1234567.890, {enforceMaskSign: true}), "$ 1,234,567.89");
139 | t.is(format("$ -#,###.00", -1234567.890, {enforceMaskSign: true}), "$ -1,234,567.89");
140 | });
141 |
142 | test("Numbers with positive sign", t => {
143 | t.is(format("+#,##0.######", +5000.123456789), "+5,000.123457");
144 | t.is(format("+#,##0.######", 5000.123456789), "+5,000.123457");
145 | t.is(format("#,##0.######", +5000.123456789), "5,000.123457");
146 | t.is(format("+#,##0.######", -5000.123456789), "-5,000.123457");
147 |
148 | t.is(format("+#,##0.######", +5000.123456789, {enforceMaskSign: true}), "+5,000.123457");
149 | t.is(format("+#,##0.######", 5000.123456789, {enforceMaskSign: true}), "+5,000.123457");
150 | t.is(format("#,##0.######", +5000.123456789, {enforceMaskSign: true}), "5,000.123457");
151 | t.is(format("+#,##0.######", -5000.123456789, {enforceMaskSign: true}), "-5,000.123457");
152 | t.is(format("$ +#,###.00", -1234567.890, {enforceMaskSign: true}), "$ -1,234,567.89");
153 | t.is(format("$ +#,###.00", 1234567.890, {enforceMaskSign: true}), "$ +1,234,567.89");
154 | });
155 |
--------------------------------------------------------------------------------