UNPKG

bigmoney.js

Version:

Library for handling the money values. Based on big.js library for arbitrary-precision decimal arithmetic. Support multiple currencies.

1,272 lines (1,003 loc) 36 kB
/* big.js v2.5.0 https://github.com/MikeMcl/big.js/LICENCE */ ;(function ( global ) { 'use strict'; /* big.js v2.5.0 A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic. https://github.com/MikeMcl/big.js/ Copyright (c) 2012 Michael Mclaughlin <M8ch88l@gmail.com> MIT Expat Licence */ /****************************** EDITABLE DEFAULTS **********************************/ // The default values below must be integers within the stated ranges (inclusive). /* * The maximum number of decimal places of the results of methods involving * division, i.e. 'div' and 'sqrt', and 'pow' with negative exponents. */ Big['DP'] = 20; // 0 to MAX_DP /* * The rounding mode used when rounding to the above decimal places. * * 0 Round towards zero (i.e. truncate, no rounding). (ROUND_DOWN) * 1 Round to nearest neighbour. If equidistant, round up. (ROUND_HALF_UP) * 2 Round to nearest neighbour. If equidistant, to even neighbour. (ROUND_HALF_EVEN) * 3 Round away from zero. (ROUND_UP) */ Big['RM'] = 1; // 0, 1, 2 or 3 // The maximum value of 'Big.DP'. var MAX_DP = 1E6, // 0 to 1e+6 // The maximum magnitude of the exponent argument to the 'pow' method. MAX_POWER = 1E6, // 1 to 1e+6 /* * The exponent value at and beneath which 'toString' returns exponential notation. * Javascript's Number type: -7 * -1e+6 is the minimum recommended exponent value of a Big. */ TO_EXP_NEG = -7, // 0 to -1e+6 /* * The exponent value at and above which 'toString' returns exponential notation. * Javascript's Number type: 21 * 1e+6 is the maximum recommended exponent value of a Big, though there is no * enforcing or checking of a limit. */ TO_EXP_POS = 21, // 0 to 1e+6 /***********************************************************************************/ P = Big.prototype, isValid = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, ONE = new Big(1); // CONSTRUCTOR /* * The exported function. * Create and return a new instance of a Big object. * * n {number|string|Big} A numeric value. */ function Big( n ) { var i, j, nL, x = this; // Enable constructor usage without new. if ( !(x instanceof Big) ) { return new Big( n ) } // Duplicate. if ( n instanceof Big ) { x['s'] = n['s']; x['e'] = n['e']; x['c'] = n['c'].slice(); return } // Minus zero? if ( n === 0 && 1 / n < 0 ) { n = '-0' // Ensure 'n' is string and check validity. } else if ( !isValid.test(n += '') ) { throwErr( NaN ) } // Determine sign. x['s'] = n.charAt(0) == '-' ? ( n = n.slice(1), -1 ) : 1; // Decimal point? if ( ( i = n.indexOf('.') ) > -1 ) { n = n.replace( '.', '' ) } // Exponential form? if ( ( j = n.search(/e/i) ) > 0 ) { // Determine exponent. if ( i < 0 ) { i = j } i += +n.slice( j + 1 ); n = n.substring( 0, j ) } else if ( i < 0 ) { // Integer. i = n.length } // Determine leading zeros. for ( j = 0; n.charAt(j) == '0'; j++ ) { } if ( j == ( nL = n.length ) ) { // Zero. x['c'] = [ x['e'] = 0 ] } else { // Determine trailing zeros. for ( ; n.charAt(--nL) == '0'; ) { } x['e'] = i - j - 1; x['c'] = []; // Convert string to array of digits (without leading and trailing zeros). for ( i = 0; j <= nL; x['c'][i++] = +n.charAt(j++) ) { } } } // PRIVATE FUNCTIONS /* * Round Big 'x' to a maximum of 'dp' decimal places using rounding mode * 'rm'. (Called by 'div', 'sqrt' and 'round'.) * * x {Big} The Big to round. * dp {number} Integer, 0 to MAX_DP inclusive. * rm {number} 0, 1, 2 or 3 ( ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_UP ) * [more] {boolean} Whether the result of division was truncated. */ function rnd( x, dp, rm, more ) { var xc = x['c'], i = x['e'] + dp + 1; if ( rm === 1 ) { // 'xc[i]' is the digit after the digit that may be rounded up. more = xc[i] >= 5 } else if ( rm === 2 ) { more = xc[i] > 5 || xc[i] == 5 && ( more || i < 0 || xc[i + 1] != null || xc[i - 1] & 1 ) } else if ( rm === 3 ) { more = more || xc[i] != null || i < 0 } else if ( more = false, rm !== 0 ) { throwErr( '!Big.RM!' ) } if ( i < 1 || !xc[0] ) { x['c'] = more // 1, 0.1, 0.01, 0.001, 0.0001 etc. ? ( x['e'] = -dp, [1] ) // Zero. : [ x['e'] = 0 ]; } else { // Remove any digits after the required decimal places. xc.length = i--; // Round up? if ( more ) { // Rounding up may mean the previous digit has to be rounded up and so on. for ( ; ++xc[i] > 9; ) { xc[i] = 0; if ( !i-- ) { ++x['e']; xc.unshift(1) } } } // Remove trailing zeros. for ( i = xc.length; !xc[--i]; xc.pop() ) { } } return x } /* * Throw a BigError. * * message {string} The error message. */ function throwErr( message ) { var err = new Error( message ); err['name'] = 'BigError'; throw err } // PROTOTYPE/INSTANCE METHODS /* * Return a new Big whose value is the absolute value of this Big. */ P['abs'] = function () { var x = new Big(this); x['s'] = 1; return x }; /* * Return * 1 if the value of this 'Big' is greater than the value of 'Big' 'y', * -1 if the value of this 'Big' is less than the value of 'Big' 'y', or * 0 if they have the same value. */ P['cmp'] = function ( y ) { var xNeg, x = this, xc = x['c'], yc = ( y = new Big( y ) )['c'], i = x['s'], j = y['s'], k = x['e'], l = y['e']; // Either zero? if ( !xc[0] || !yc[0] ) { return !xc[0] ? !yc[0] ? 0 : -j : i } // Signs differ? if ( i != j ) { return i } xNeg = i < 0; // Compare exponents. if ( k != l ) { return k > l ^ xNeg ? 1 : -1 } // Compare digit by digit. for ( i = -1, j = ( k = xc.length ) < ( l = yc.length ) ? k : l; ++i < j; ) { if ( xc[i] != yc[i] ) { return xc[i] > yc[i] ^ xNeg ? 1 : -1 } } // Compare lengths. return k == l ? 0 : k > l ^ xNeg ? 1 : -1 }; /* * Return a new Big whose value is the value of this Big divided by the * value of Big 'y', rounded, if necessary, to a maximum of 'Big.DP' * decimal places using rounding mode 'Big.RM'. */ P['div'] = function ( y ) { var x = this, dvd = x['c'], dvs = ( y = new Big(y) )['c'], s = x['s'] == y['s'] ? 1 : -1, dp = Big['DP']; if ( dp !== ~~dp || dp < 0 || dp > MAX_DP ) { throwErr( '!Big.DP!' ) } // Either 0? if ( !dvd[0] || !dvs[0] ) { // Both 0? if ( dvd[0] == dvs[0] ) { throwErr( NaN ) } // 'dvs' is 0? if ( !dvs[0] ) { // Throw +-Infinity. throwErr( s / 0 ) } // 'dvd' is 0. Return +-0. return new Big( s * 0 ) } var dvsL, dvsT, next, cmp, remI, dvsZ = dvs.slice(), dvdI = dvsL = dvs.length, dvdL = dvd.length, rem = dvd.slice( 0, dvsL ), remL = rem.length, quo = new Big(ONE), qc = quo['c'] = [], qi = 0, digits = dp + ( quo['e'] = x['e'] - y['e'] ) + 1; quo['s'] = s; s = digits < 0 ? 0 : digits; // Create version of divisor with leading zero. dvsZ.unshift(0); // Add zeros to make remainder as long as divisor. for ( ; remL++ < dvsL; rem.push(0) ) { } do { // 'next' is how many times the divisor goes into the current remainder. for ( next = 0; next < 10; next++ ) { // Compare divisor and remainder. if ( dvsL != ( remL = rem.length ) ) { cmp = dvsL > remL ? 1 : -1 } else { for ( remI = -1, cmp = 0; ++remI < dvsL; ) { if ( dvs[remI] != rem[remI] ) { cmp = dvs[remI] > rem[remI] ? 1 : -1; break } } } // Subtract divisor from remainder (if divisor < remainder). if ( cmp < 0 ) { // Remainder cannot be more than one digit longer than divisor. // Equalise lengths using divisor with extra leading zero? for ( dvsT = remL == dvsL ? dvs : dvsZ; remL; ) { if ( rem[--remL] < dvsT[remL] ) { for ( remI = remL; remI && !rem[--remI]; rem[remI] = 9 ) { } --rem[remI]; rem[remL] += 10 } rem[remL] -= dvsT[remL] } for ( ; !rem[0]; rem.shift() ) { } } else { break } } // Add the 'next' digit to the result array. qc[qi++] = cmp ? next : ++next; // Update the remainder. rem[0] && cmp ? ( rem[remL] = dvd[dvdI] || 0 ) : ( rem = [ dvd[dvdI] ] ) } while ( ( dvdI++ < dvdL || rem[0] != null ) && s-- ); // Leading zero? Do not remove if result is simply zero (qi == 1). if ( !qc[0] && qi != 1) { // There can't be more than one zero. qc.shift(); quo['e']--; } // Round? if ( qi > digits ) { rnd( quo, dp, Big['RM'], rem[0] != null ) } return quo } /* * Return true if the value of this Big is equal to the value of Big 'y', * otherwise returns false. */ P['eq'] = function ( y ) { return !this.cmp( y ) }; /* * Return true if the value of this Big is greater than the value of Big 'y', * otherwise returns false. */ P['gt'] = function ( y ) { return this.cmp( y ) > 0 }; /* * Return true if the value of this Big is greater than or equal to the * value of Big 'y', otherwise returns false. */ P['gte'] = function ( y ) { return this.cmp( y ) > -1 }; /* * Return true if the value of this Big is less than the value of Big 'y', * otherwise returns false. */ P['lt'] = function ( y ) { return this.cmp( y ) < 0 }; /* * Return true if the value of this Big is less than or equal to the value * of Big 'y', otherwise returns false. */ P['lte'] = function ( y ) { return this.cmp( y ) < 1 }; /* * Return a new Big whose value is the value of this Big minus the value * of Big 'y'. */ P['minus'] = function ( y ) { var d, i, j, xLTy, x = this, a = x['s'], b = ( y = new Big( y ) )['s']; // Signs differ? if ( a != b ) { return y['s'] = -b, x['plus'](y) } var xc = x['c'].slice(), xe = x['e'], yc = y['c'], ye = y['e']; // Either zero? if ( !xc[0] || !yc[0] ) { // 'y' is non-zero? return yc[0] ? ( y['s'] = -b, y ) // 'x' is non-zero? : new Big( xc[0] ? x // Both are zero. : 0 ) } // Determine which is the bigger number. // Prepend zeros to equalise exponents. if ( a = xe - ye ) { d = ( xLTy = a < 0 ) ? ( a = -a, xc ) : ( ye = xe, yc ); for ( d.reverse(), b = a; b--; d.push(0) ) { } d.reverse() } else { // Exponents equal. Check digit by digit. j = ( ( xLTy = xc.length < yc.length ) ? xc : yc ).length; for ( a = b = 0; b < j; b++ ) { if ( xc[b] != yc[b] ) { xLTy = xc[b] < yc[b]; break } } } // 'x' < 'y'? Point 'xc' to the array of the bigger number. if ( xLTy ) { d = xc, xc = yc, yc = d; y['s'] = -y['s'] } /* * Append zeros to 'xc' if shorter. No need to add zeros to 'yc' if shorter * as subtraction only needs to start at 'yc.length'. */ if ( ( b = -( ( j = xc.length ) - yc.length ) ) > 0 ) { for ( ; b--; xc[j++] = 0 ) { } } // Subtract 'yc' from 'xc'. for ( b = yc.length; b > a; ){ if ( xc[--b] < yc[b] ) { for ( i = b; i && !xc[--i]; xc[i] = 9 ) { } --xc[i]; xc[b] += 10 } xc[b] -= yc[b] } // Remove trailing zeros. for ( ; xc[--j] == 0; xc.pop() ) { } // Remove leading zeros and adjust exponent accordingly. for ( ; xc[0] == 0; xc.shift(), --ye ) { } if ( !xc[0] ) { // n - n = +0 y['s'] = 1; // Result must be zero. xc = [ye = 0] } return y['c'] = xc, y['e'] = ye, y }; /* * Return a new Big whose value is the value of this Big modulo the * value of Big 'y'. */ P['mod'] = function ( y ) { y = new Big( y ); var c, x = this, i = x['s'], j = y['s']; if ( !y['c'][0] ) { throwErr( NaN ) } x['s'] = y['s'] = 1; c = y.cmp( x ) == 1; x['s'] = i, y['s'] = j; return c ? new Big(x) : ( i = Big['DP'], j = Big['RM'], Big['DP'] = Big['RM'] = 0, x = x['div'](y), Big['DP'] = i, Big['RM'] = j, this['minus']( x['times'](y) ) ) }; /* * Return a new Big whose value is the value of this Big plus the value * of Big 'y'. */ P['plus'] = function ( y ) { var d, x = this, a = x['s'], b = ( y = new Big( y ) )['s']; // Signs differ? if ( a != b ) { return y['s'] = -b, x['minus'](y) } var xe = x['e'], xc = x['c'], ye = y['e'], yc = y['c']; // Either zero? if ( !xc[0] || !yc[0] ) { // 'y' is non-zero? return yc[0] ? y : new Big( xc[0] // 'x' is non-zero? ? x // Both are zero. Return zero. : a * 0 ) } // Prepend zeros to equalise exponents. // Note: Faster to use reverse then do unshifts. if ( xc = xc.slice(), a = xe - ye ) { d = a > 0 ? ( ye = xe, yc ) : ( a = -a, xc ); for ( d.reverse(); a--; d.push(0) ) { } d.reverse() } // Point 'xc' to the longer array. if ( xc.length - yc.length < 0 ) { d = yc, yc = xc, xc = d } /* * Only start adding at 'yc.length - 1' as the * further digits of 'xc' can be left as they are. */ for ( a = yc.length, b = 0; a; b = ( xc[--a] = xc[a] + yc[a] + b ) / 10 ^ 0, xc[a] %= 10 ) { } // No need to check for zero, as +x + +y != 0 && -x + -y != 0 if ( b ) { xc.unshift(b); ++ye } // Remove trailing zeros. for ( a = xc.length; xc[--a] == 0; xc.pop() ) { } return y['c'] = xc, y['e'] = ye, y }; /* * Return a Big whose value is the value of this Big raised to the power * 'e'. If 'e' is negative, round, if necessary, to a maximum of 'Big.DP' * decimal places using rounding mode 'Big.RM'. * * e {number} Integer, -MAX_POWER to MAX_POWER inclusive. */ P['pow'] = function ( e ) { var isNeg = e < 0, x = new Big(this), y = ONE; if ( e !== ~~e || e < -MAX_POWER || e > MAX_POWER ) { throwErr( '!pow!' ) } for ( e = isNeg ? -e : e; ; ) { if ( e & 1 ) { y = y['times'](x) } e >>= 1; if ( !e ) { break } x = x['times'](x) } return isNeg ? ONE['div'](y) : y }; /* * Return a new Big whose value is the value of this Big rounded, if * necessary, to a maximum of 'dp' decimal places using rounding mode 'rm'. * If 'dp' is not specified, round to 0 decimal places. * If 'rm' is not specified, use 'Big.RM'. * * [dp] {number} Integer, 0 to MAX_DP inclusive. * [rm] 0, 1, 2 or 3 ( ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_UP ) */ P['round'] = function ( dp, rm ) { var x = new Big(this); if ( dp == null ) { dp = 0 } else if ( dp !== ~~dp || dp < 0 || dp > MAX_DP ) { throwErr( '!round!' ) } rnd( x, dp, rm == null ? Big['RM'] : rm ); return x }; /* * Return a new Big whose value is the square root of the value of this * Big, rounded, if necessary, to a maximum of 'Big.DP' decimal places * using rounding mode 'Big.RM'. */ P['sqrt'] = function () { var estimate, r, approx, x = this, xc = x['c'], i = x['s'], e = x['e'], half = new Big('0.5'); // Zero? if ( !xc[0] ) { return new Big(x) } // Negative? if ( i < 0 ) { throwErr( NaN ) } // Estimate. i = Math.sqrt( x.toString() ); // Math.sqrt underflow/overflow? // Pass 'x' to Math.sqrt as integer, then adjust the exponent of the result. if ( i == 0 || i == 1 / 0 ) { estimate = xc.join(''); if ( !( estimate.length + e & 1 ) ) { estimate += '0' } r = new Big( Math.sqrt(estimate).toString() ); r['e'] = ( ( ( e + 1 ) / 2 ) | 0 ) - ( e < 0 || e & 1 ) } else { r = new Big( i.toString() ) } i = r['e'] + ( Big['DP'] += 4 ); // Newton-Raphson loop. do { approx = r; r = half['times']( approx['plus']( x['div'](approx) ) ) } while ( approx['c'].slice( 0, i ).join('') !== r['c'].slice( 0, i ).join('') ); rnd( r, Big['DP'] -= 4, Big['RM'] ); return r }; /* * Return a new Big whose value is the value of this Big times the value * of Big 'y'. */ P['times'] = function ( y ) { var c, x = this, xc = x['c'], yc = ( y = new Big( y ) )['c'], a = xc.length, b = yc.length, i = x['e'], j = y['e']; y['s'] = x['s'] == y['s'] ? 1 : -1; // Either 0? if ( !xc[0] || !yc[0] ) { return new Big( y['s'] * 0 ) } y['e'] = i + j; if ( a < b ) { c = xc, xc = yc, yc = c, j = a, a = b, b = j } for ( j = a + b, c = []; j--; c.push(0) ) { } // Multiply! for ( i = b - 1; i > -1; i-- ) { for ( b = 0, j = a + i; j > i; b = c[j] + yc[i] * xc[j - i - 1] + b, c[j--] = b % 10 | 0, b = b / 10 | 0 ) { } if ( b ) { c[j] = ( c[j] + b ) % 10 } } b && ++y['e']; // Remove any leading zero. !c[0] && c.shift(); // Remove trailing zeros. for ( j = c.length; !c[--j]; c.pop() ) { } return y['c'] = c, y }; /* * Return a string representing the value of this Big. * Return exponential notation if this Big has a positive exponent equal * to or greater than 'TO_EXP_POS', or a negative exponent equal to or less * than 'TO_EXP_NEG'. */ P['toString'] = P['valueOf'] = P['toJSON'] = function () { var x = this, e = x['e'], str = x['c'].join(''), strL = str.length; // Exponential notation? if ( e <= TO_EXP_NEG || e >= TO_EXP_POS ) { str = str.charAt(0) + ( strL > 1 ? '.' + str.slice(1) : '' ) + ( e < 0 ? 'e' : 'e+' ) + e // Negative exponent? } else if ( e < 0 ) { // Prepend zeros. for ( ; ++e; str = '0' + str ) { } str = '0.' + str // Positive exponent? } else if ( e > 0 ) { if ( ++e > strL ) { // Append zeros. for ( e -= strL; e-- ; str += '0' ) { } } else if ( e < strL ) { str = str.slice( 0, e ) + '.' + str.slice(e) } // Exponent zero. } else if ( strL > 1 ) { str = str.charAt(0) + '.' + str.slice(1) } // Avoid '-0' return x['s'] < 0 && x['c'][0] ? '-' + str : str }; /* *************************************************************************** * If 'toExponential', 'toFixed', 'toPrecision' and 'format' are not * required they can safely be commented-out or deleted. No redundant code * will be left. 'format' is used only by 'toExponential', 'toFixed' and * 'toPrecision'. *************************************************************************** */ /* * PRIVATE FUNCTION * * Return a string representing the value of Big 'x' in normal or * exponential notation to a fixed number of decimal places or significant * digits 'dp'. * (Called by toString, toExponential, toFixed and toPrecision.) * * x {Big} The Big to format. * dp {number} Integer, 0 to MAX_DP inclusive. * toE {number} undefined (toFixed), 1 (toExponential) or 2 (toPrecision). */ function format( x, dp, toE ) { // The index (in normal notation) of the digit that may be rounded up. var i = dp - ( x = new Big(x) )['e'], c = x['c']; // Round? if ( c.length > ++dp ) { rnd( x, i, Big['RM'] ) } // Recalculate 'i' if toFixed as 'x.e' may have changed if value rounded up. i = !c[0] ? i + 1 : toE ? dp : ( c = x['c'], x['e'] + i + 1 ); // Append zeros? for ( ; c.length < i; c.push(0) ) { } i = x['e']; /* * 'toPrecision' returns exponential notation if the number of * significant digits specified is less than the number of digits * necessary to represent the integer part of the value in normal * notation. */ return toE == 1 || toE == 2 && ( dp <= i || i <= TO_EXP_NEG ) // Exponential notation. ? ( x['s'] < 0 && c[0] ? '-' : '' ) + ( c.length > 1 ? ( c.splice( 1, 0, '.' ), c.join('') ) : c[0] ) + ( i < 0 ? 'e' : 'e+' ) + i // Normal notation. : x.toString() } /* * Return a string representing the value of this Big in exponential * notation to 'dp' fixed decimal places and rounded, if necessary, using * 'Big.RM'. * * [dp] {number} Integer, 0 to MAX_DP inclusive. */ P['toExponential'] = function ( dp ) { if ( dp == null ) { dp = this['c'].length - 1 } else if ( dp !== ~~dp || dp < 0 || dp > MAX_DP ) { throwErr( '!toExp!' ) } return format( this, dp, 1 ) }; /* * Return a string representing the value of this Big in normal notation * to 'dp' fixed decimal places and rounded, if necessary, using 'Big.RM'. * * [dp] {number} Integer, 0 to MAX_DP inclusive. */ P['toFixed'] = function ( dp ) { var str, x = this, neg = TO_EXP_NEG, pos = TO_EXP_POS; TO_EXP_NEG = -( TO_EXP_POS = 1 / 0 ); if ( dp == null ) { str = x.toString() } else if ( dp === ~~dp && dp >= 0 && dp <= MAX_DP ) { str = format( x, x['e'] + dp ); // (-0).toFixed() is '0', but (-0.1).toFixed() is '-0'. // (-0).toFixed(1) is '0.0', but (-0.01).toFixed(1) is '-0.0'. if ( x['s'] < 0 && x['c'][0] && str.indexOf('-') < 0 ) { // As e.g. -0.5 if rounded to -0 will cause toString to omit the minus sign. str = '-' + str } } TO_EXP_NEG = neg, TO_EXP_POS = pos; if ( !str ) { throwErr( '!toFix!' ) } return str }; /* * Return a string representing the value of this Big to 'sd' significant * digits and rounded, if necessary, using 'Big.RM'. If 'sd' is less than * the number of digits necessary to represent the integer part of the value * in normal notation, then use exponential notation. * * sd {number} Integer, 1 to MAX_DP inclusive. */ P['toPrecision'] = function ( sd ) { if ( sd == null ) { return this.toString() } else if ( sd !== ~~sd || sd < 1 || sd > MAX_DP ) { throwErr( '!toPre!' ) } return format( this, sd - 1, 2 ) }; // EXPORT // Node and other CommonJS-like environments that support module.exports. if ( typeof module !== 'undefined' && module.exports ) { module.exports = Big //AMD. } else if ( typeof define == 'function' && define.amd ) { define( function () { return Big }) //Browser. } else { global['Big'] = Big } })( this ); /** * Library for handling money: * - mathematical operations * - work with different currencies * - formatted output * * @example Money(3300, 'USD').plus(99.01).convert('RUB').format() */ ;(function (root, undefined) { var Big = root.Big || require('big.js'); var settings = { base: "USD", rates: { //USD : 1, //this is base. Other rates relative to the USD //"RUB": 35.2448, //"EUR": 1/1.3485, //"JPY": 102.02 }, format: "%decimal %currency" }; function Money(val, currency, options) { if (!(this instanceof Money)) { return new Money(val, currency, options); } if (arguments.length === 2 && typeof currency === 'object') { options = currency; currency = undefined; } this.options = options || {}; this.val = Big(val); this.currency = currency || this.options.currency || Money.settings.base; } /** * wrap Big.js methods */ Money.prototype.abs = function () { return Money(this.val.abs.apply(this.val, arguments), this.currency, this.options); }; Money.prototype.cmp = function () { return this.val.cmp.apply(this.val, arguments); }; Money.prototype.div = function () { return Money(this.val.div.apply(this.val, arguments), this.currency, this.options); }; Money.prototype.eq = function () { return this.val.eq.apply(this.val, arguments); }; Money.prototype.gt = function () { return this.val.gt.apply(this.val, arguments); }; Money.prototype.gte = function () { return this.val.gte.apply(this.val, arguments); }; Money.prototype.lt = function () { return this.val.lt.apply(this.val, arguments); }; Money.prototype.lte = function () { return this.val.lte.apply(this.val, arguments); }; Money.prototype.minus = function () { return Money(this.val.minus.apply(this.val, arguments), this.currency, this.options); }; Money.prototype.mod = function () { return Money(this.val.mod.apply(this.val, arguments), this.currency, this.options); }; Money.prototype.plus = function () { return Money(this.val.plus.apply(this.val, arguments), this.currency, this.options); }; Money.prototype.pow = function () { return this.val.pow.apply(this.val, arguments); }; Money.prototype.round = function () { return Money(this.val.round.apply(this.val, arguments), this.currency, this.options); }; Money.prototype.sqrt = function () { return Money(this.val.sqrt.apply(this.val, arguments), this.currency, this.options); }; Money.prototype.times = function () { return Money(this.val.times.apply(this.val, arguments), this.currency, this.options); }; /** * Allocates amounts of money in an array so that you won't loose cents * @param ratios {Number} How many parts you want to divide the initial amount. * @returns {*} */ function sum(a, b) { return a + b; } function ones(len) { var arr = []; for (var i = 0; i < len; i++) { arr.push(1); } return arr; } Money.prototype.allocate = function(ratios) { if(typeof ratios === 'undefined') { return [this]; } else if(typeof ratios === 'number') { ratios = ones(ratios); } var amount = this, remainder = amount, total = ratios.reduce(sum), results = [], current = 0; ratios.forEach(function(ratio, index) { results.push(amount.times(ratio).div(total)); remainder = remainder.minus(results[index]); }); while(!remainder.eq(0)) { results[current] = results[current++].plus(0.01 * remainder.val.s); if(current >= results.length) { current = 0; } remainder = remainder.plus(0.01 * remainder.val.s * -1); } return results; }; /** * Convert to over currency * @param to {String} Destination currency. Default is a settings.base currency. * @returns {*} */ Money.prototype.convert = function (to) { to || (to = Money.settings.base); var rate, val, from = this.currency; if (from === to) return Money(this.val, to, this.options); if (from === Money.settings.base) { rate = Money.settings.rates[to]; if (rate === undefined) throw new Error('Unknown rate for "' + to + '" currency'); val = this.val.times(rate); return Money(val, to, this.options); } else if (to === Money.settings.base) { rate = Money.settings.rates[from]; if (rate === undefined) throw new Error('Unknown rate for "' + from + '" currency'); val = rate === 0 ? 0 : this.val.div(rate); return Money(val, to, this.options); } else { return (this.convert(Money.settings.base)).convert(to); } }; /** * Return value as number * @returns {Number} */ Money.prototype.valueOf = function () { return parseFloat(this.val.toFixed(2)); }; /** * Return value as string * @returns {string} */ Money.prototype.toString = function () { return this.val.toFixed(2); }; /** * Return formatted string * @returns {string} */ Money.prototype.format = function (formatTemplate) { return Money.formatter(this.valueOf(), this.currency, formatTemplate); }; Money.isValidCurrency = function (curr) { return typeof Money.settings.rates[curr] === 'number'; }; Money.settings = settings; Money.formatter = function(decimal, currency, formatTemplate) { formatTemplate || (formatTemplate = Money.settings.format); return formatTemplate.replace('%decimal', decimal).replace('%currency', currency); }; // EXPORT // Node and other CommonJS-like environments that support module.exports. if ( typeof module !== 'undefined' && module.exports ) { module.exports = Money; //AMD. } else if ( typeof define == 'function' && define.amd ) { define( function () { return Money }); //Browser. } else { Money.noConflict = (function(oldMoney) { return function() { // Reset the value of the root's `accounting` variable: root.Money = oldMoney; // Delete the noConflict method: Money.noConflict = undefined; // Return reference to the library to re-assign it: return Money; }; })(root.Money); root['Money'] = Money; } })(this);