UNPKG

6.65 kBJavaScriptView Raw
1/*!
2 * currency.js - v2.0.4
3 * http://scurker.github.io/currency.js
4 *
5 * Copyright (c) 2021 Jason Wilson
6 * Released under MIT license
7 */
8
9'use strict';
10
11var defaults = {
12 symbol: '$',
13 separator: ',',
14 decimal: '.',
15 errorOnInvalid: false,
16 precision: 2,
17 pattern: '!#',
18 negativePattern: '-!#',
19 format: format,
20 fromCents: false
21};
22
23var round = function round(v) {
24 return Math.round(v);
25};
26
27var pow = function pow(p) {
28 return Math.pow(10, p);
29};
30
31var rounding = function rounding(value, increment) {
32 return round(value / increment) * increment;
33};
34
35var groupRegex = /(\d)(?=(\d{3})+\b)/g;
36var vedicRegex = /(\d)(?=(\d\d)+\d\b)/g;
37/**
38 * Create a new instance of currency.js
39 * @param {number|string|currency} value
40 * @param {object} [opts]
41 */
42
43function currency(value, opts) {
44 var that = this;
45
46 if (!(that instanceof currency)) {
47 return new currency(value, opts);
48 }
49
50 var settings = Object.assign({}, defaults, opts),
51 precision = pow(settings.precision),
52 v = parse(value, settings);
53 that.intValue = v;
54 that.value = v / precision; // Set default incremental value
55
56 settings.increment = settings.increment || 1 / precision; // Support vedic numbering systems
57 // see: https://en.wikipedia.org/wiki/Indian_numbering_system
58
59 if (settings.useVedic) {
60 settings.groups = vedicRegex;
61 } else {
62 settings.groups = groupRegex;
63 } // Intended for internal usage only - subject to change
64
65
66 this.s = settings;
67 this.p = precision;
68}
69
70function parse(value, opts) {
71 var useRounding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
72 var v = 0,
73 decimal = opts.decimal,
74 errorOnInvalid = opts.errorOnInvalid,
75 decimals = opts.precision,
76 fromCents = opts.fromCents,
77 precision = pow(decimals),
78 isNumber = typeof value === 'number',
79 isCurrency = value instanceof currency;
80
81 if (isCurrency && fromCents) {
82 return value.intValue;
83 }
84
85 if (isNumber || isCurrency) {
86 v = isCurrency ? value.value : value;
87 } else if (typeof value === 'string') {
88 var regex = new RegExp('[^-\\d' + decimal + ']', 'g'),
89 decimalString = new RegExp('\\' + decimal, 'g');
90 v = value.replace(/\((.*)\)/, '-$1') // allow negative e.g. (1.99)
91 .replace(regex, '') // replace any non numeric values
92 .replace(decimalString, '.'); // convert any decimal values
93
94 v = v || 0;
95 } else {
96 if (errorOnInvalid) {
97 throw Error('Invalid Input');
98 }
99
100 v = 0;
101 }
102
103 if (!fromCents) {
104 v *= precision; // scale number to integer value
105
106 v = v.toFixed(4); // Handle additional decimal for proper rounding.
107 }
108
109 return useRounding ? round(v) : v;
110}
111/**
112 * Formats a currency object
113 * @param currency
114 * @param {object} [opts]
115 */
116
117
118function format(currency, settings) {
119 var pattern = settings.pattern,
120 negativePattern = settings.negativePattern,
121 symbol = settings.symbol,
122 separator = settings.separator,
123 decimal = settings.decimal,
124 groups = settings.groups,
125 split = ('' + currency).replace(/^-/, '').split('.'),
126 dollars = split[0],
127 cents = split[1];
128 return (currency.value >= 0 ? pattern : negativePattern).replace('!', symbol).replace('#', dollars.replace(groups, '$1' + separator) + (cents ? decimal + cents : ''));
129}
130
131currency.prototype = {
132 /**
133 * Adds values together.
134 * @param {number} number
135 * @returns {currency}
136 */
137 add: function add(number) {
138 var intValue = this.intValue,
139 _settings = this.s,
140 _precision = this.p;
141 return currency((intValue += parse(number, _settings)) / (_settings.fromCents ? 1 : _precision), _settings);
142 },
143
144 /**
145 * Subtracts value.
146 * @param {number} number
147 * @returns {currency}
148 */
149 subtract: function subtract(number) {
150 var intValue = this.intValue,
151 _settings = this.s,
152 _precision = this.p;
153 return currency((intValue -= parse(number, _settings)) / (_settings.fromCents ? 1 : _precision), _settings);
154 },
155
156 /**
157 * Multiplies values.
158 * @param {number} number
159 * @returns {currency}
160 */
161 multiply: function multiply(number) {
162 var intValue = this.intValue,
163 _settings = this.s;
164 return currency((intValue *= number) / (_settings.fromCents ? 1 : pow(_settings.precision)), _settings);
165 },
166
167 /**
168 * Divides value.
169 * @param {number} number
170 * @returns {currency}
171 */
172 divide: function divide(number) {
173 var intValue = this.intValue,
174 _settings = this.s;
175 return currency(intValue /= parse(number, _settings, false), _settings);
176 },
177
178 /**
179 * Takes the currency amount and distributes the values evenly. Any extra pennies
180 * left over from the distribution will be stacked onto the first set of entries.
181 * @param {number} count
182 * @returns {array}
183 */
184 distribute: function distribute(count) {
185 var intValue = this.intValue,
186 _precision = this.p,
187 _settings = this.s,
188 distribution = [],
189 split = Math[intValue >= 0 ? 'floor' : 'ceil'](intValue / count),
190 pennies = Math.abs(intValue - split * count),
191 precision = _settings.fromCents ? 1 : _precision;
192
193 for (; count !== 0; count--) {
194 var item = currency(split / precision, _settings); // Add any left over pennies
195
196 pennies-- > 0 && (item = item[intValue >= 0 ? 'add' : 'subtract'](1 / precision));
197 distribution.push(item);
198 }
199
200 return distribution;
201 },
202
203 /**
204 * Returns the dollar value.
205 * @returns {number}
206 */
207 dollars: function dollars() {
208 return ~~this.value;
209 },
210
211 /**
212 * Returns the cent value.
213 * @returns {number}
214 */
215 cents: function cents() {
216 var intValue = this.intValue,
217 _precision = this.p;
218 return ~~(intValue % _precision);
219 },
220
221 /**
222 * Formats the value as a string according to the formatting settings.
223 * @param {boolean} useSymbol - format with currency symbol
224 * @returns {string}
225 */
226 format: function format(options) {
227 var _settings = this.s;
228
229 if (typeof options === 'function') {
230 return options(this, _settings);
231 }
232
233 return _settings.format(this, Object.assign({}, _settings, options));
234 },
235
236 /**
237 * Formats the value as a string according to the formatting settings.
238 * @returns {string}
239 */
240 toString: function toString() {
241 var intValue = this.intValue,
242 _precision = this.p,
243 _settings = this.s;
244 return rounding(intValue / _precision, _settings.increment).toFixed(_settings.precision);
245 },
246
247 /**
248 * Value for JSON serialization.
249 * @returns {float}
250 */
251 toJSON: function toJSON() {
252 return this.value;
253 }
254};
255
256module.exports = currency;