PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / assets / js / libraries / accounting.js

accounting.js in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.13, at assets/js/libraries/accounting.js

414 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*!
2 * accounting.js v0.4.2
3 * Copyright 2014 Open Exchange Rates
4 *
5 * Freely distributable under the MIT license.
6 * Portions of accounting.js are inspired or borrowed from underscore.js
7 *
8 * Full details and documentation:
9 * http://openexchangerates.github.io/accounting.js/
10 */
11
12 (function(root, undefined) {
13
14 /* --- Setup --- */
15
16 // Create the local library object, to be exported or referenced globally later
17 var lib = {};
18
19 // Current version
20 lib.version = '0.4.1';
21
22
23 /* --- Exposed settings --- */
24
25 // The library's settings configuration object. Contains default parameters for
26 // currency and number formatting
27 lib.settings = {
28 currency: {
29 symbol : "$", // default currency symbol is '$'
30 format : "%s%v", // controls output: %s = symbol, %v = value (can be object, see docs)
31 decimal : ".", // decimal point separator
32 thousand : ",", // thousands separator
33 precision : 2, // decimal places
34 grouping : 3 // digit grouping (not implemented yet)
35 },
36 number: {
37 precision : 0, // default precision on numbers is 0
38 grouping : 3, // digit grouping (not implemented yet)
39 thousand : ",",
40 decimal : "."
41 }
42 };
43
44
45 /* --- Internal Helper Methods --- */
46
47 // Store reference to possibly-available ECMAScript 5 methods for later
48 var nativeMap = Array.prototype.map,
49 nativeIsArray = Array.isArray,
50 toString = Object.prototype.toString;
51
52 /**
53 * Tests whether supplied parameter is a string
54 * from underscore.js
55 */
56 function isString(obj) {
57 return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
58 }
59
60 /**
61 * Tests whether supplied parameter is a string
62 * from underscore.js, delegates to ECMA5's native Array.isArray
63 */
64 function isArray(obj) {
65 return nativeIsArray ? nativeIsArray(obj) : toString.call(obj) === '[object Array]';
66 }
67
68 /**
69 * Tests whether supplied parameter is a true object
70 */
71 function isObject(obj) {
72 return obj && toString.call(obj) === '[object Object]';
73 }
74
75 /**
76 * Extends an object with a defaults object, similar to underscore's _.defaults
77 *
78 * Used for abstracting parameter handling from API methods
79 */
80 function defaults(object, defs) {
81 var key;
82 object = object || {};
83 defs = defs || {};
84 // Iterate over object non-prototype properties:
85 for (key in defs) {
86 if (defs.hasOwnProperty(key)) {
87 // Replace values with defaults only if undefined (allow empty/zero values):
88 if (object[key] == null) object[key] = defs[key];
89 }
90 }
91 return object;
92 }
93
94 /**
95 * Implementation of `Array.map()` for iteration loops
96 *
97 * Returns a new Array as a result of calling `iterator` on each array value.
98 * Defers to native Array.map if available
99 */
100 function map(obj, iterator, context) {
101 var results = [], i, j;
102
103 if (!obj) return results;
104
105 // Use native .map method if it exists:
106 if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
107
108 // Fallback for native .map:
109 for (i = 0, j = obj.length; i < j; i++ ) {
110 results[i] = iterator.call(context, obj[i], i, obj);
111 }
112 return results;
113 }
114
115 /**
116 * Check and normalise the value of precision (must be positive integer)
117 */
118 function checkPrecision(val, base) {
119 val = Math.round(Math.abs(val));
120 return isNaN(val)? base : val;
121 }
122
123
124 /**
125 * Parses a format string or object and returns format obj for use in rendering
126 *
127 * `format` is either a string with the default (positive) format, or object
128 * containing `pos` (required), `neg` and `zero` values (or a function returning
129 * either a string or object)
130 *
131 * Either string or format.pos must contain "%v" (value) to be valid
132 */
133 function checkCurrencyFormat(format) {
134 var defaults = lib.settings.currency.format;
135
136 // Allow function as format parameter (should return string or object):
137 if ( typeof format === "function" ) format = format();
138
139 // Format can be a string, in which case `value` ("%v") must be present:
140 if ( isString( format ) && format.match("%v") ) {
141
142 // Create and return positive, negative and zero formats:
143 return {
144 pos : format,
145 neg : format.replace("-", "").replace("%v", "-%v"),
146 zero : format
147 };
148
149 // If no format, or object is missing valid positive value, use defaults:
150 } else if ( !format || !format.pos || !format.pos.match("%v") ) {
151
152 // If defaults is a string, casts it to an object for faster checking next time:
153 return ( !isString( defaults ) ) ? defaults : lib.settings.currency.format = {
154 pos : defaults,
155 neg : defaults.replace("%v", "-%v"),
156 zero : defaults
157 };
158
159 }
160 // Otherwise, assume format was fine:
161 return format;
162 }
163
164
165 /* --- API Methods --- */
166
167 /**
168 * Takes a string/array of strings, removes all formatting/cruft and returns the raw float value
169 * Alias: `accounting.parse(string)`
170 *
171 * Decimal must be included in the regular expression to match floats (defaults to
172 * accounting.settings.number.decimal), so if the number uses a non-standard decimal
173 * separator, provide it as the second argument.
174 *
175 * Also matches bracketed negatives (eg. "$ (1.99)" => -1.99)
176 *
177 * Doesn't throw any errors (`NaN`s become 0) but this may change in future
178 */
179 var unformat = lib.unformat = lib.parse = function(value, decimal) {
180 // Recursively unformat arrays:
181 if (isArray(value)) {
182 return map(value, function(val) {
183 return unformat(val, decimal);
184 });
185 }
186
187 // Fails silently (need decent errors):
188 value = value || 0;
189
190 // Return the value as-is if it's already a number:
191 if (typeof value === "number") return value;
192
193 // Default decimal point comes from settings, but could be set to eg. "," in opts:
194 decimal = decimal || lib.settings.number.decimal;
195
196 // Build regex to strip out everything except digits, decimal point and minus sign:
197 var regex = new RegExp("[^0-9-" + decimal + "]", ["g"]),
198 unformatted = parseFloat(
199 ("" + value)
200 .replace(/\((.*)\)/, "-$1") // replace bracketed values with negatives
201 .replace(regex, '') // strip out any cruft
202 .replace(decimal, '.') // make sure decimal point is standard
203 );
204
205 // This will fail silently which may cause trouble, let's wait and see:
206 return !isNaN(unformatted) ? unformatted : 0;
207 };
208
209
210 /**
211 * Implementation of toFixed() that treats floats more like decimals
212 *
213 * Fixes binary rounding issues (eg. (0.615).toFixed(2) === "0.61") that present
214 * problems for accounting- and finance-related software.
215 */
216 var toFixed = lib.toFixed = function(value, precision) {
217 precision = checkPrecision(precision, lib.settings.number.precision);
218 var power = Math.pow(10, precision);
219
220 // Multiply up by precision, round accurately, then divide and use native toFixed():
221 return (Math.round(lib.unformat(value) * power) / power).toFixed(precision);
222 };
223
224
225 /**
226 * Format a number, with comma-separated thousands and custom precision/decimal places
227 * Alias: `accounting.format()`
228 *
229 * Localise by overriding the precision and thousand / decimal separators
230 * 2nd parameter `precision` can be an object matching `settings.number`
231 */
232 var formatNumber = lib.formatNumber = lib.format = function(number, precision, thousand, decimal) {
233 // Resursively format arrays:
234 if (isArray(number)) {
235 return map(number, function(val) {
236 return formatNumber(val, precision, thousand, decimal);
237 });
238 }
239
240 // Clean up number:
241 number = unformat(number);
242
243 // Build options object from second param (if object) or all params, extending defaults:
244 var opts = defaults(
245 (isObject(precision) ? precision : {
246 precision : precision,
247 thousand : thousand,
248 decimal : decimal
249 }),
250 lib.settings.number
251 ),
252
253 // Clean up precision
254 usePrecision = checkPrecision(opts.precision),
255
256 // Do some calc:
257 negative = number < 0 ? "-" : "",
258 base = parseInt(toFixed(Math.abs(number || 0), usePrecision), 10) + "",
259 mod = base.length > 3 ? base.length % 3 : 0;
260
261 // Format the number:
262 return negative + (mod ? base.substr(0, mod) + opts.thousand : "") + base.substr(mod).replace(/(\d{3})(?=\d)/g, "$1" + opts.thousand) + (usePrecision ? opts.decimal + toFixed(Math.abs(number), usePrecision).split('.')[1] : "");
263 };
264
265
266 /**
267 * Format a number into currency
268 *
269 * Usage: accounting.formatMoney(number, symbol, precision, thousandsSep, decimalSep, format)
270 * defaults: (0, "$", 2, ",", ".", "%s%v")
271 *
272 * Localise by overriding the symbol, precision, thousand / decimal separators and format
273 * Second param can be an object matching `settings.currency` which is the easiest way.
274 *
275 * To do: tidy up the parameters
276 */
277 var formatMoney = lib.formatMoney = function(number, symbol, precision, thousand, decimal, format) {
278 // Resursively format arrays:
279 if (isArray(number)) {
280 return map(number, function(val){
281 return formatMoney(val, symbol, precision, thousand, decimal, format);
282 });
283 }
284
285 // Clean up number:
286 number = unformat(number);
287
288 // Build options object from second param (if object) or all params, extending defaults:
289 var opts = defaults(
290 (isObject(symbol) ? symbol : {
291 symbol : symbol,
292 precision : precision,
293 thousand : thousand,
294 decimal : decimal,
295 format : format
296 }),
297 lib.settings.currency
298 ),
299
300 // Check format (returns object with pos, neg and zero):
301 formats = checkCurrencyFormat(opts.format),
302
303 // Choose which format to use for this value:
304 useFormat = number > 0 ? formats.pos : number < 0 ? formats.neg : formats.zero;
305
306 // Return with currency symbol added:
307 return useFormat.replace('%s', opts.symbol).replace('%v', formatNumber(Math.abs(number), checkPrecision(opts.precision), opts.thousand, opts.decimal));
308 };
309
310
311 /**
312 * Format a list of numbers into an accounting column, padding with whitespace
313 * to line up currency symbols, thousand separators and decimals places
314 *
315 * List should be an array of numbers
316 * Second parameter can be an object containing keys that match the params
317 *
318 * Returns array of accouting-formatted number strings of same length
319 *
320 * NB: `white-space:pre` CSS rule is required on the list container to prevent
321 * browsers from collapsing the whitespace in the output strings.
322 */
323 lib.formatColumn = function(list, symbol, precision, thousand, decimal, format) {
324 if (!list) return [];
325
326 // Build options object from second param (if object) or all params, extending defaults:
327 var opts = defaults(
328 (isObject(symbol) ? symbol : {
329 symbol : symbol,
330 precision : precision,
331 thousand : thousand,
332 decimal : decimal,
333 format : format
334 }),
335 lib.settings.currency
336 ),
337
338 // Check format (returns object with pos, neg and zero), only need pos for now:
339 formats = checkCurrencyFormat(opts.format),
340
341 // Whether to pad at start of string or after currency symbol:
342 padAfterSymbol = formats.pos.indexOf("%s") < formats.pos.indexOf("%v") ? true : false,
343
344 // Store value for the length of the longest string in the column:
345 maxLength = 0,
346
347 // Format the list according to options, store the length of the longest string:
348 formatted = map(list, function(val, i) {
349 if (isArray(val)) {
350 // Recursively format columns if list is a multi-dimensional array:
351 return lib.formatColumn(val, opts);
352 } else {
353 // Clean up the value
354 val = unformat(val);
355
356 // Choose which format to use for this value (pos, neg or zero):
357 var useFormat = val > 0 ? formats.pos : val < 0 ? formats.neg : formats.zero,
358
359 // Format this value, push into formatted list and save the length:
360 fVal = useFormat.replace('%s', opts.symbol).replace('%v', formatNumber(Math.abs(val), checkPrecision(opts.precision), opts.thousand, opts.decimal));
361
362 if (fVal.length > maxLength) maxLength = fVal.length;
363 return fVal;
364 }
365 });
366
367 // Pad each number in the list and send back the column of numbers:
368 return map(formatted, function(val, i) {
369 // Only if this is a string (not a nested array, which would have already been padded):
370 if (isString(val) && val.length < maxLength) {
371 // Depending on symbol position, pad after symbol or at index 0:
372 return padAfterSymbol ? val.replace(opts.symbol, opts.symbol+(new Array(maxLength - val.length + 1).join(" "))) : (new Array(maxLength - val.length + 1).join(" ")) + val;
373 }
374 return val;
375 });
376 };
377
378
379 /* --- Module Definition --- */
380
381 // Export accounting for CommonJS. If being loaded as an AMD module, define it as such.
382 // Otherwise, just add `accounting` to the global object
383 if (typeof exports !== 'undefined') {
384 if (typeof module !== 'undefined' && module.exports) {
385 exports = module.exports = lib;
386 }
387 exports.accounting = lib;
388 } else if (typeof define === 'function' && define.amd) {
389 // Return the library as an AMD module:
390 define([], function() {
391 return lib;
392 });
393 } else {
394 // Use accounting.noConflict to restore `accounting` back to its original value.
395 // Returns a reference to the library's `accounting` object;
396 // e.g. `var numbers = accounting.noConflict();`
397 lib.noConflict = (function(oldAccounting) {
398 return function() {
399 // Reset the value of the root's `accounting` variable:
400 root.accounting = oldAccounting;
401 // Delete the noConflict method:
402 lib.noConflict = undefined;
403 // Return reference to the library to re-assign it:
404 return lib;
405 };
406 })(root.accounting);
407
408 // Declare `fx` on the root (global/window) object:
409 root['accounting'] = lib;
410 }
411
412 // Root will be `window` in browser or `global` on the server:
413 }(this));
414