exchange-rounding
Version:
Exchange Number Formatting
374 lines (373 loc) • 13.2 kB
JavaScript
class ExchNumberFormat {
version = '1.1.4';
replacer = 'XYZ';
formatter;
intlOptions;
customCurrencyData;
totalCurrencyData = {};
originalCurrency;
totalAliases = {};
internalAliases = {};
constructor(locales, options = {}) {
this.customCurrencyData = {
'ADA': {
'symbol': '₳',
'narrowSymbol': '₳',
'code': 'ADA',
'name': 'Cardano',
'defaultDecimals': 2,
},
'BCH': {
'symbol': 'Ƀ',
'narrowSymbol': 'Ƀ',
'code': 'BCH',
'name': 'BitcoinCash',
'defaultDecimals': 4,
},
'BTC': {
'symbol': '₿',
'narrowSymbol': '₿',
'code': 'BTC',
'name': 'Bitcoin',
'defaultDecimals': 8,
},
'CTN': {
'symbol': 'Ƈ',
'narrowSymbol': 'Ƈ',
'code': 'CTN',
'name': 'CoreToken',
'defaultDecimals': 2,
},
'CHFX': {
'symbol': '₣X',
'narrowSymbol': '₣X',
'code': 'CHFX',
'name': 'Swiss Franc X',
'defaultDecimals': 2,
},
'DOT': {
'symbol': '•',
'narrowSymbol': '•',
'code': 'DOT',
'name': 'Polkadot',
'defaultDecimals': 2,
},
'ETC': {
'symbol': 'ξ',
'narrowSymbol': 'ξ',
'code': 'ETC',
'name': 'EthereumClassic',
'defaultDecimals': 3,
},
'ETH': {
'symbol': 'Ξ',
'narrowSymbol': 'Ξ',
'code': 'ETH',
'name': 'Ethereum',
'defaultDecimals': 4,
},
'EURC': {
'symbol': '€C',
'narrowSymbol': '€C',
'code': 'EURC',
'name': 'Euro Coin',
'defaultDecimals': 2,
},
'EURX': {
'symbol': '€X',
'narrowSymbol': '€X',
'code': 'EURX',
'name': 'Euro X',
'defaultDecimals': 2,
},
'LTC': {
'symbol': 'Ł',
'narrowSymbol': 'Ł',
'code': 'LTC',
'name': 'Litecoin',
'defaultDecimals': 3,
},
'SOL': {
'symbol': '◎',
'narrowSymbol': '◎',
'code': 'SOL',
'name': 'Solana',
'defaultDecimals': 2,
},
'TRX': {
'symbol': 'TRX',
'narrowSymbol': 'TRX',
'code': 'TRX',
'name': 'Tron',
'defaultDecimals': 2,
},
'USDC': {
'symbol': '$C',
'narrowSymbol': '$C',
'code': 'USDC',
'name': 'US Dollar Coin',
'defaultDecimals': 2,
},
'USDT': {
'symbol': '₮',
'narrowSymbol': '₮',
'code': 'USDT',
'name': 'US Tether Dollar',
'defaultDecimals': 2,
},
'USDX': {
'symbol': '$X',
'narrowSymbol': '$X',
'code': 'USDX',
'name': 'US X Dollar',
'defaultDecimals': 2,
},
'XAB': {
'symbol': '₡t',
'narrowSymbol': '₡t',
'code': 'XAB',
'name': 'Core Testnet',
'defaultDecimals': 3,
},
'XCB': {
'symbol': '₡',
'narrowSymbol': '₡',
'code': 'XCB',
'name': 'Core',
'defaultDecimals': 3,
},
'XCE': {
'symbol': '₡e',
'narrowSymbol': '₡e',
'code': 'XCE',
'name': 'Core Enterprise',
'defaultDecimals': 3,
},
'XMR': {
'symbol': 'ɱ',
'narrowSymbol': 'ɱ',
'code': 'XMR',
'name': 'Monero',
'defaultDecimals': 3,
},
'XRP': {
'symbol': 'XRP',
'narrowSymbol': 'XRP',
'code': 'XRP',
'name': 'Ripple',
'defaultDecimals': 2,
},
'XXX': {
'symbol': '¤',
'narrowSymbol': '¤',
'code': 'XXX',
'name': 'Unknown',
'defaultDecimals': 2,
},
'XAU': {
'symbol': 'Au',
'narrowSymbol': 'Au',
'code': 'XAU',
'name': 'Gold',
'defaultDecimals': 6,
},
'XAG': {
'symbol': 'Ag',
'narrowSymbol': 'Ag',
'code': 'XAG',
'name': 'Silver',
'defaultDecimals': 6,
},
'XCU': {
'symbol': 'Cu',
'narrowSymbol': 'Cu',
'code': 'XCU',
'name': 'Copper',
'defaultDecimals': 2,
},
};
this.internalAliases = {};
const defaultOptions = {
localeMatcher: 'best fit',
style: 'currency',
currency: undefined,
currencyDisplay: 'symbol',
roundFunction: 'floor',
useGrouping: true,
notation: 'standard',
compactDisplay: 'short',
wrapped: false,
wrappedSymbol: 'w',
digitized: false,
digitizedSymbol: 'd',
useAliases: true,
aliases: {},
useCustomCurrency: true,
customCurrency: {},
cropZeros: false,
};
this.intlOptions = { ...defaultOptions, ...options };
this.totalAliases = { ...this.intlOptions.aliases, ...this.internalAliases };
if (this.intlOptions.useCustomCurrency) {
this.totalCurrencyData = { ...this.customCurrencyData, ...this.intlOptions.customCurrency };
}
else {
this.totalCurrencyData = {};
}
this.originalCurrency = this.intlOptions.currency;
if (this.intlOptions.useAliases && this.originalCurrency && this.totalAliases[this.originalCurrency]) {
this.originalCurrency = this.totalAliases[this.originalCurrency];
}
let setLocale = locales === 'auto' ? this.determineLocale() : locales;
const currencyData = this.totalCurrencyData[this.originalCurrency?.toUpperCase() || ''];
if (this.originalCurrency && currencyData) {
this.intlOptions.currency = this.replacer;
this.intlOptions.minimumFractionDigits = currencyData.defaultDecimals;
}
try {
if (this.intlOptions.currency) {
this.formatter = new Intl.NumberFormat(setLocale, this.intlOptions);
}
else {
this.useDecimalStyle();
this.formatter = new Intl.NumberFormat(setLocale, this.intlOptions);
}
}
catch (error) {
if (error instanceof RangeError) {
this.useDecimalStyle();
this.formatter = new Intl.NumberFormat(setLocale, this.intlOptions);
}
else {
console.error("Error creating Intl.NumberFormat instance");
return;
}
}
}
format(number) {
let formatted = this.formatter.format(number);
formatted = this.replaceCurrency(formatted);
if (this.intlOptions.cropZeros !== false) {
const parts = this.formatter.formatToParts(number);
const decimalPart = parts.find(part => part.type === 'fraction');
const decimalSeparator = parts.find(part => part.type === 'decimal');
if (decimalPart && decimalSeparator) {
let fractionValue = decimalPart.value;
const minZeros = typeof this.intlOptions.cropZeros === 'number' ? this.intlOptions.cropZeros : 0;
const trimmedFraction = fractionValue.replace(/0+$/, '');
if (trimmedFraction.length < minZeros) {
fractionValue = trimmedFraction + '0'.repeat(minZeros - trimmedFraction.length);
}
else {
fractionValue = trimmedFraction;
}
if (fractionValue.length === 0) {
formatted = formatted.replace(`${decimalSeparator.value}${decimalPart.value}`, '');
}
else {
formatted = formatted.replace(decimalPart.value, fractionValue);
}
}
}
return formatted;
}
formatToParts(number) {
const parts = this.formatter.formatToParts(number);
if (this.originalCurrency && this.totalCurrencyData[this.originalCurrency.toUpperCase()]) {
const currencyData = this.totalCurrencyData[this.originalCurrency.toUpperCase()];
parts.forEach(part => {
if (part.type === 'currency') {
part.value = part.value.replace(this.replacer, this.addType(currencyData.symbol));
}
});
}
else if (this.originalCurrency) {
parts.forEach(part => {
if (part.type === 'currency') {
part.value = this.addType(part.value);
}
});
}
return parts;
}
isCurrencySupported(currency) {
try {
new Intl.NumberFormat(undefined, { style: 'currency', currency: currency.toUpperCase() });
return true;
}
catch (error) {
if (error instanceof RangeError) {
return false;
}
else {
console.warn("Error creating Intl.NumberFormat instance");
return false;
}
}
}
replaceCurrency(input) {
if (this.originalCurrency && this.totalCurrencyData[this.originalCurrency.toUpperCase()]) {
const currencyData = this.totalCurrencyData[this.originalCurrency.toUpperCase()];
let replacementValue;
switch (this.intlOptions.currencyDisplay) {
case 'narrowSymbol':
replacementValue = this.addType(currencyData.narrowSymbol);
break;
case 'code':
replacementValue = this.addType(currencyData.code);
break;
case 'name':
replacementValue = this.addType(currencyData.name);
break;
default:
replacementValue = this.addType(currencyData.symbol);
break;
}
return input.replace(this.replacer, replacementValue);
}
else if (this.originalCurrency) {
switch (this.intlOptions.currencyDisplay) {
case 'narrowSymbol':
input = this.addType(input);
break;
case 'code':
input = input.replace(this.originalCurrency, this.addType(this.originalCurrency));
break;
case 'name':
input = this.addType(input);
break;
default:
input = this.addType(input);
break;
}
return input;
}
return input;
}
useDecimalStyle() {
this.intlOptions.style = 'decimal';
this.intlOptions.minimumFractionDigits = this.intlOptions.minimumFractionDigits !== undefined ? this.intlOptions.minimumFractionDigits : 2;
this.intlOptions.maximumFractionDigits = this.intlOptions.maximumFractionDigits !== undefined ? this.intlOptions.maximumFractionDigits : 2;
delete this.intlOptions.currency;
}
determineLocale() {
if (typeof window !== 'undefined' && navigator.languages && navigator.languages.length) {
return navigator.languages[0];
}
else if (typeof window !== 'undefined' && navigator.language) {
return navigator.language;
}
return 'en';
}
addType(value) {
let output = value;
if (this.intlOptions.digitized) {
output = this.intlOptions.digitizedSymbol + output;
}
if (this.intlOptions.wrapped) {
output = this.intlOptions.wrappedSymbol + output;
}
return output;
}
}
export default ExchNumberFormat;