mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-12-27 10:14:17 +08:00
679 lines
23 KiB
JavaScript
679 lines
23 KiB
JavaScript
/*!
|
|
* 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);
|