scinote-web/vendor/assets/javascripts/numeral.js

680 lines
23 KiB
JavaScript
Raw Normal View History

2016-09-05 19:36:45 +08:00
/*!
* numeral.js
* version : 1.5.3
* author : Adam Draper
* license : MIT
* http://adamwdraper.github.com/Numeral-js/
*/
(function () {
/************************************
Constants
************************************/
var numeral,
VERSION = '1.5.3',
// internal storage for language config files
languages = {},
currentLanguage = 'en',
zeroFormat = null,
defaultFormat = '0,0',
// check for nodeJS
hasModule = (typeof module !== 'undefined' && module.exports);
/************************************
Constructors
************************************/
// Numeral prototype object
function Numeral (number) {
this._value = number;
}
/**
* Implementation of toFixed() that treats floats more like decimals
*
* Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present
* problems for accounting- and finance-related software.
*/
function toFixed (value, precision, roundingFunction, optionals) {
var power = Math.pow(10, precision),
optionalsRegExp,
output;
//roundingFunction = (roundingFunction !== undefined ? roundingFunction : Math.round);
// Multiply up by precision, round accurately, then divide and use native toFixed():
output = (roundingFunction(value * power) / power).toFixed(precision);
if (optionals) {
optionalsRegExp = new RegExp('0{1,' + optionals + '}$');
output = output.replace(optionalsRegExp, '');
}
return output;
}
/************************************
Formatting
************************************/
// determine what type of formatting we need to do
function formatNumeral (n, format, roundingFunction) {
var output;
// figure out what kind of format we are dealing with
if (format.indexOf('$') > -1) { // currency!!!!!
output = formatCurrency(n, format, roundingFunction);
} else if (format.indexOf('%') > -1) { // percentage
output = formatPercentage(n, format, roundingFunction);
} else if (format.indexOf(':') > -1) { // time
output = formatTime(n, format);
} else { // plain ol' numbers or bytes
output = formatNumber(n._value, format, roundingFunction);
}
// return string
return output;
}
// revert to number
function unformatNumeral (n, string) {
var stringOriginal = string,
thousandRegExp,
millionRegExp,
billionRegExp,
trillionRegExp,
suffixes = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
bytesMultiplier = false,
power;
if (string.indexOf(':') > -1) {
n._value = unformatTime(string);
} else {
if (string === zeroFormat) {
n._value = 0;
} else {
if (languages[currentLanguage].delimiters.decimal !== '.') {
string = string.replace(/\./g,'').replace(languages[currentLanguage].delimiters.decimal, '.');
}
// see if abbreviations are there so that we can multiply to the correct number
thousandRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.thousand + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$');
millionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.million + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$');
billionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.billion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$');
trillionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.trillion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$');
// see if bytes are there so that we can multiply to the correct number
for (power = 0; power <= suffixes.length; power++) {
bytesMultiplier = (string.indexOf(suffixes[power]) > -1) ? Math.pow(1024, power + 1) : false;
if (bytesMultiplier) {
break;
}
}
// do some math to create our number
n._value = ((bytesMultiplier) ? bytesMultiplier : 1) * ((stringOriginal.match(thousandRegExp)) ? Math.pow(10, 3) : 1) * ((stringOriginal.match(millionRegExp)) ? Math.pow(10, 6) : 1) * ((stringOriginal.match(billionRegExp)) ? Math.pow(10, 9) : 1) * ((stringOriginal.match(trillionRegExp)) ? Math.pow(10, 12) : 1) * ((string.indexOf('%') > -1) ? 0.01 : 1) * (((string.split('-').length + Math.min(string.split('(').length-1, string.split(')').length-1)) % 2)? 1: -1) * Number(string.replace(/[^0-9\.]+/g, ''));
// round if we are talking about bytes
n._value = (bytesMultiplier) ? Math.ceil(n._value) : n._value;
}
}
return n._value;
}
function formatCurrency (n, format, roundingFunction) {
var symbolIndex = format.indexOf('$'),
openParenIndex = format.indexOf('('),
minusSignIndex = format.indexOf('-'),
space = '',
spliceIndex,
output;
// check for space before or after currency
if (format.indexOf(' $') > -1) {
space = ' ';
format = format.replace(' $', '');
} else if (format.indexOf('$ ') > -1) {
space = ' ';
format = format.replace('$ ', '');
} else {
format = format.replace('$', '');
}
// format the number
output = formatNumber(n._value, format, roundingFunction);
// position the symbol
if (symbolIndex <= 1) {
if (output.indexOf('(') > -1 || output.indexOf('-') > -1) {
output = output.split('');
spliceIndex = 1;
if (symbolIndex < openParenIndex || symbolIndex < minusSignIndex){
// the symbol appears before the "(" or "-"
spliceIndex = 0;
}
output.splice(spliceIndex, 0, languages[currentLanguage].currency.symbol + space);
output = output.join('');
} else {
output = languages[currentLanguage].currency.symbol + space + output;
}
} else {
if (output.indexOf(')') > -1) {
output = output.split('');
output.splice(-1, 0, space + languages[currentLanguage].currency.symbol);
output = output.join('');
} else {
output = output + space + languages[currentLanguage].currency.symbol;
}
}
return output;
}
function formatPercentage (n, format, roundingFunction) {
var space = '',
output,
value = n._value * 100;
// check for space before %
if (format.indexOf(' %') > -1) {
space = ' ';
format = format.replace(' %', '');
} else {
format = format.replace('%', '');
}
output = formatNumber(value, format, roundingFunction);
if (output.indexOf(')') > -1 ) {
output = output.split('');
output.splice(-1, 0, space + '%');
output = output.join('');
} else {
output = output + space + '%';
}
return output;
}
function formatTime (n) {
var hours = Math.floor(n._value/60/60),
minutes = Math.floor((n._value - (hours * 60 * 60))/60),
seconds = Math.round(n._value - (hours * 60 * 60) - (minutes * 60));
return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds);
}
function unformatTime (string) {
var timeArray = string.split(':'),
seconds = 0;
// turn hours and minutes into seconds and add them all up
if (timeArray.length === 3) {
// hours
seconds = seconds + (Number(timeArray[0]) * 60 * 60);
// minutes
seconds = seconds + (Number(timeArray[1]) * 60);
// seconds
seconds = seconds + Number(timeArray[2]);
} else if (timeArray.length === 2) {
// minutes
seconds = seconds + (Number(timeArray[0]) * 60);
// seconds
seconds = seconds + Number(timeArray[1]);
}
return Number(seconds);
}
function formatNumber (value, format, roundingFunction) {
var negP = false,
signed = false,
optDec = false,
abbr = '',
abbrK = false, // force abbreviation to thousands
abbrM = false, // force abbreviation to millions
abbrB = false, // force abbreviation to billions
abbrT = false, // force abbreviation to trillions
abbrForce = false, // force abbreviation
bytes = '',
ord = '',
abs = Math.abs(value),
suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
min,
max,
power,
w,
precision,
thousands,
d = '',
neg = false;
// check if number is zero and a custom zero format has been set
if (value === 0 && zeroFormat !== null) {
return zeroFormat;
} else {
// see if we should use parentheses for negative number or if we should prefix with a sign
// if both are present we default to parentheses
if (format.indexOf('(') > -1) {
negP = true;
format = format.slice(1, -1);
} else if (format.indexOf('+') > -1) {
signed = true;
format = format.replace(/\+/g, '');
}
// see if abbreviation is wanted
if (format.indexOf('a') > -1) {
// check if abbreviation is specified
abbrK = format.indexOf('aK') >= 0;
abbrM = format.indexOf('aM') >= 0;
abbrB = format.indexOf('aB') >= 0;
abbrT = format.indexOf('aT') >= 0;
abbrForce = abbrK || abbrM || abbrB || abbrT;
// check for space before abbreviation
if (format.indexOf(' a') > -1) {
abbr = ' ';
format = format.replace(' a', '');
} else {
format = format.replace('a', '');
}
if (abs >= Math.pow(10, 12) && !abbrForce || abbrT) {
// trillion
abbr = abbr + languages[currentLanguage].abbreviations.trillion;
value = value / Math.pow(10, 12);
} else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9) && !abbrForce || abbrB) {
// billion
abbr = abbr + languages[currentLanguage].abbreviations.billion;
value = value / Math.pow(10, 9);
} else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6) && !abbrForce || abbrM) {
// million
abbr = abbr + languages[currentLanguage].abbreviations.million;
value = value / Math.pow(10, 6);
} else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3) && !abbrForce || abbrK) {
// thousand
abbr = abbr + languages[currentLanguage].abbreviations.thousand;
value = value / Math.pow(10, 3);
}
}
// see if we are formatting bytes
if (format.indexOf('b') > -1) {
// check for space before
if (format.indexOf(' b') > -1) {
bytes = ' ';
format = format.replace(' b', '');
} else {
format = format.replace('b', '');
}
for (power = 0; power <= suffixes.length; power++) {
min = Math.pow(1024, power);
max = Math.pow(1024, power+1);
if (value >= min && value < max) {
bytes = bytes + suffixes[power];
if (min > 0) {
value = value / min;
}
break;
}
}
}
// see if ordinal is wanted
if (format.indexOf('o') > -1) {
// check for space before
if (format.indexOf(' o') > -1) {
ord = ' ';
format = format.replace(' o', '');
} else {
format = format.replace('o', '');
}
ord = ord + languages[currentLanguage].ordinal(value);
}
if (format.indexOf('[.]') > -1) {
optDec = true;
format = format.replace('[.]', '.');
}
w = value.toString().split('.')[0];
precision = format.split('.')[1];
thousands = format.indexOf(',');
if (precision) {
if (precision.indexOf('[') > -1) {
precision = precision.replace(']', '');
precision = precision.split('[');
d = toFixed(value, (precision[0].length + precision[1].length), roundingFunction, precision[1].length);
} else {
d = toFixed(value, precision.length, roundingFunction);
}
w = d.split('.')[0];
if (d.split('.')[1].length) {
d = languages[currentLanguage].delimiters.decimal + d.split('.')[1];
} else {
d = '';
}
if (optDec && Number(d.slice(1)) === 0) {
d = '';
}
} else {
w = toFixed(value, null, roundingFunction);
}
// format number
if (w.indexOf('-') > -1) {
w = w.slice(1);
neg = true;
}
if (thousands > -1) {
w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands);
}
if (format.indexOf('.') === 0) {
w = '';
}
return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + ((!neg && signed) ? '+' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '');
}
}
/************************************
Top Level Functions
************************************/
numeral = function (input) {
if (numeral.isNumeral(input)) {
input = input.value();
} else if (input === 0 || typeof input === 'undefined') {
input = 0;
} else if (!Number(input)) {
input = numeral.fn.unformat(input);
}
return new Numeral(Number(input));
};
// version number
numeral.version = VERSION;
// compare numeral object
numeral.isNumeral = function (obj) {
return obj instanceof Numeral;
};
// This function will load languages and then set the global language. If
// no arguments are passed in, it will simply return the current global
// language key.
numeral.language = function (key, values) {
if (!key) {
return currentLanguage;
}
if (key && !values) {
if(!languages[key]) {
throw new Error('Unknown language : ' + key);
}
currentLanguage = key;
}
if (values || !languages[key]) {
loadLanguage(key, values);
}
return numeral;
};
// This function provides access to the loaded language data. If
// no arguments are passed in, it will simply return the current
// global language object.
numeral.languageData = function (key) {
if (!key) {
return languages[currentLanguage];
}
if (!languages[key]) {
throw new Error('Unknown language : ' + key);
}
return languages[key];
};
numeral.language('en', {
delimiters: {
thousands: ',',
decimal: '.'
},
abbreviations: {
thousand: 'k',
million: 'm',
billion: 'b',
trillion: 't'
},
ordinal: function (number) {
var b = number % 10;
return (~~ (number % 100 / 10) === 1) ? 'th' :
(b === 1) ? 'st' :
(b === 2) ? 'nd' :
(b === 3) ? 'rd' : 'th';
},
currency: {
symbol: '$'
}
});
numeral.zeroFormat = function (format) {
zeroFormat = typeof(format) === 'string' ? format : null;
};
numeral.defaultFormat = function (format) {
defaultFormat = typeof(format) === 'string' ? format : '0.0';
};
/************************************
Helpers
************************************/
function loadLanguage(key, values) {
languages[key] = values;
}
/************************************
Floating-point helpers
************************************/
// The floating-point helper functions and implementation
// borrows heavily from sinful.js: http://guipn.github.io/sinful.js/
/**
* Array.prototype.reduce for browsers that don't support it
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Compatibility
*/
if ('function' !== typeof Array.prototype.reduce) {
Array.prototype.reduce = function (callback, opt_initialValue) {
'use strict';
if (null === this || 'undefined' === typeof this) {
// At the moment all modern browsers, that support strict mode, have
// native implementation of Array.prototype.reduce. For instance, IE8
// does not support strict mode, so this check is actually useless.
throw new TypeError('Array.prototype.reduce called on null or undefined');
}
if ('function' !== typeof callback) {
throw new TypeError(callback + ' is not a function');
}
var index,
value,
length = this.length >>> 0,
isValueSet = false;
if (1 < arguments.length) {
value = opt_initialValue;
isValueSet = true;
}
for (index = 0; length > index; ++index) {
if (this.hasOwnProperty(index)) {
if (isValueSet) {
value = callback(value, this[index], index, this);
} else {
value = this[index];
isValueSet = true;
}
}
}
if (!isValueSet) {
throw new TypeError('Reduce of empty array with no initial value');
}
return value;
};
}
/**
* Computes the multiplier necessary to make x >= 1,
* effectively eliminating miscalculations caused by
* finite precision.
*/
function multiplier(x) {
var parts = x.toString().split('.');
if (parts.length < 2) {
return 1;
}
return Math.pow(10, parts[1].length);
}
/**
* Given a variable number of arguments, returns the maximum
* multiplier that must be used to normalize an operation involving
* all of them.
*/
function correctionFactor() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function (prev, next) {
var mp = multiplier(prev),
mn = multiplier(next);
return mp > mn ? mp : mn;
}, -Infinity);
}
/************************************
Numeral Prototype
************************************/
numeral.fn = Numeral.prototype = {
clone : function () {
return numeral(this);
},
format : function (inputString, roundingFunction) {
return formatNumeral(this,
inputString ? inputString : defaultFormat,
(roundingFunction !== undefined) ? roundingFunction : Math.round
);
},
unformat : function (inputString) {
if (Object.prototype.toString.call(inputString) === '[object Number]') {
return inputString;
}
return unformatNumeral(this, inputString ? inputString : defaultFormat);
},
value : function () {
return this._value;
},
valueOf : function () {
return this._value;
},
set : function (value) {
this._value = Number(value);
return this;
},
add : function (value) {
var corrFactor = correctionFactor.call(null, this._value, value);
function cback(accum, curr, currI, O) {
return accum + corrFactor * curr;
}
this._value = [this._value, value].reduce(cback, 0) / corrFactor;
return this;
},
subtract : function (value) {
var corrFactor = correctionFactor.call(null, this._value, value);
function cback(accum, curr, currI, O) {
return accum - corrFactor * curr;
}
this._value = [value].reduce(cback, this._value * corrFactor) / corrFactor;
return this;
},
multiply : function (value) {
function cback(accum, curr, currI, O) {
var corrFactor = correctionFactor(accum, curr);
return (accum * corrFactor) * (curr * corrFactor) /
(corrFactor * corrFactor);
}
this._value = [this._value, value].reduce(cback, 1);
return this;
},
divide : function (value) {
function cback(accum, curr, currI, O) {
var corrFactor = correctionFactor(accum, curr);
return (accum * corrFactor) / (curr * corrFactor);
}
this._value = [this._value, value].reduce(cback);
return this;
},
difference : function (value) {
return Math.abs(numeral(this._value).subtract(value).value());
}
};
/************************************
Exposing Numeral
************************************/
// CommonJS module is defined
if (hasModule) {
module.exports = numeral;
}
/*global ender:false */
if (typeof ender === 'undefined') {
// here, `this` means `window` in the browser, or `global` on the server
// add `numeral` as a global object via a string identifier,
// for Closure Compiler 'advanced' mode
this['numeral'] = numeral;
}
/*global define:false */
if (typeof define === 'function' && define.amd) {
define([], function () {
return numeral;
});
}
}).call(this);