1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | 'use strict';
|
10 |
|
11 | var 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 |
|
23 | var round = function round(v) {
|
24 | return Math.round(v);
|
25 | };
|
26 |
|
27 | var pow = function pow(p) {
|
28 | return Math.pow(10, p);
|
29 | };
|
30 |
|
31 | var rounding = function rounding(value, increment) {
|
32 | return round(value / increment) * increment;
|
33 | };
|
34 |
|
35 | var groupRegex = /(\d)(?=(\d{3})+\b)/g;
|
36 | var vedicRegex = /(\d)(?=(\d\d)+\d\b)/g;
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 | function 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;
|
55 |
|
56 | settings.increment = settings.increment || 1 / precision;
|
57 |
|
58 |
|
59 | if (settings.useVedic) {
|
60 | settings.groups = vedicRegex;
|
61 | } else {
|
62 | settings.groups = groupRegex;
|
63 | }
|
64 |
|
65 |
|
66 | this.s = settings;
|
67 | this.p = precision;
|
68 | }
|
69 |
|
70 | function 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')
|
91 | .replace(regex, '')
|
92 | .replace(decimalString, '.');
|
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;
|
105 |
|
106 | v = v.toFixed(4);
|
107 | }
|
108 |
|
109 | return useRounding ? round(v) : v;
|
110 | }
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 | function 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 |
|
131 | currency.prototype = {
|
132 | |
133 |
|
134 |
|
135 |
|
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 |
|
146 |
|
147 |
|
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 |
|
158 |
|
159 |
|
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 |
|
169 |
|
170 |
|
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 |
|
180 |
|
181 |
|
182 |
|
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);
|
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 |
|
205 |
|
206 |
|
207 | dollars: function dollars() {
|
208 | return ~~this.value;
|
209 | },
|
210 |
|
211 | |
212 |
|
213 |
|
214 |
|
215 | cents: function cents() {
|
216 | var intValue = this.intValue,
|
217 | _precision = this.p;
|
218 | return ~~(intValue % _precision);
|
219 | },
|
220 |
|
221 | |
222 |
|
223 |
|
224 |
|
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 |
|
238 |
|
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 |
|
249 |
|
250 |
|
251 | toJSON: function toJSON() {
|
252 | return this.value;
|
253 | }
|
254 | };
|
255 |
|
256 | module.exports = currency;
|