Files
CHOMPStation2/tgui/packages/tgui_ch/format.js
2023-05-23 17:43:01 +02:00

184 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
*/
import { clamp, round, toFixed } from 'common/math';
const SI_SYMBOLS = [
'f', // femto
'p', // pico
'n', // nano
'μ', // micro
'm', // milli
// NOTE: This is a space for a reason. When we right align si numbers,
// in monospace mode, we want to units and numbers stay in their respective
// columns. If rendering in HTML mode, this space will collapse into
// a single space anyway.
' ',
'k', // kilo
'M', // mega
'G', // giga
'T', // tera
'P', // peta
'E', // exa
'Z', // zetta
'Y', // yotta
'R', // ronna
'Q', // quecca
'F',
'N',
'H',
];
const SI_BASE_INDEX = SI_SYMBOLS.indexOf(' ');
/**
* Formats a number to a human readable form, by reducing it to SI units.
* TODO: This is quite a shit code and shit math, needs optimization.
*/
export const formatSiUnit = (value, minBase1000 = -SI_BASE_INDEX, unit = '') => {
if (typeof value !== 'number' || !Number.isFinite(value)) {
return value;
}
const realBase10 = Math.floor(Math.log10(value));
const base10 = Math.floor(Math.max(minBase1000 * 3, realBase10));
const realBase1000 = Math.floor(realBase10 / 3);
const base1000 = Math.floor(base10 / 3);
const symbolIndex = clamp(SI_BASE_INDEX + base1000, 0, SI_SYMBOLS.length);
const symbol = SI_SYMBOLS[symbolIndex];
const scaledNumber = value / Math.pow(1000, base1000);
const scaledPrecision = realBase1000 > minBase1000 ? 2 + base1000 * 3 - base10 : 0;
// TODO: Make numbers bigger than precision value show
// up to 2 decimal numbers.
const finalString = toFixed(scaledNumber, scaledPrecision) + ' ' + symbol + unit;
return finalString.trim();
};
export const formatPower = (value, minBase1000 = 0) => {
return formatSiUnit(value, minBase1000, 'W');
};
export const formatMoney = (value, precision = 0) => {
if (!Number.isFinite(value)) {
return value;
}
// Round the number and make it fixed precision
let fixed = round(value, precision);
if (precision > 0) {
fixed = toFixed(value, precision);
}
fixed = String(fixed);
// Place thousand separators
const length = fixed.length;
let indexOfPoint = fixed.indexOf('.');
if (indexOfPoint === -1) {
indexOfPoint = length;
}
let result = '';
for (let i = 0; i < length; i++) {
if (i > 0 && i < indexOfPoint && (indexOfPoint - i) % 3 === 0) {
// Thin space
result += '\u2009';
}
result += fixed.charAt(i);
}
return result;
};
/**
* Formats a floating point number as a number on the decibel scale.
*/
export const formatDb = (value) => {
const db = (20 * Math.log(value)) / Math.log(10);
const sign = db >= 0 ? '+' : '';
let formatted = Math.abs(db);
if (formatted === Infinity) {
formatted = 'Inf';
} else {
formatted = toFixed(formatted, 2);
}
return sign + formatted + ' dB';
};
const SI_BASE_TEN_UNIT = [
'',
'· 10³', // kilo
'· 10⁶', // mega
'· 10⁹', // giga
'· 10¹²', // tera
'· 10¹⁵', // peta
'· 10¹⁸', // exa
'· 10²¹', // zetta
'· 10²⁴', // yotta
'· 10²⁷', // ronna
'· 10³⁰', // quecca
'· 10³³',
'· 10³⁶',
'· 10³⁹',
];
const SI_BASE_TEN_INDEX = SI_BASE_TEN_UNIT.indexOf(' ');
/**
* Formats a number to a human readable form, by reducing it to SI units.
* TODO: This is quite a shit code and shit math, needs optimization.
*/
export const formatSiBaseTenUnit = (value, minBase1000 = -SI_BASE_TEN_INDEX, unit = '') => {
if (typeof value !== 'number' || !Number.isFinite(value)) {
return value;
}
const realBase10 = Math.floor(Math.log10(value));
const base10 = Math.floor(Math.max(minBase1000 * 3, realBase10));
const realBase1000 = Math.floor(realBase10 / 3);
const base1000 = Math.floor(base10 / 3);
const symbolIndex = clamp(SI_BASE_TEN_INDEX + base1000, 0, SI_BASE_TEN_UNIT.length);
const symbol = SI_BASE_TEN_UNIT[symbolIndex];
const scaledNumber = value / Math.pow(1000, base1000);
const scaledPrecision = realBase1000 > minBase1000 ? 2 + base1000 * 3 - base10 : 0;
// TODO: Make numbers bigger than precision value show
// up to 2 decimal numbers.
const finalString = toFixed(scaledNumber, scaledPrecision) + ' ' + symbol + ' ' + unit;
return finalString.trim();
};
/**
* Formats decisecond count into HH::MM::SS display by default
* "short" format does not pad and adds hms suffixes
*/
export const formatTime = (val, formatType) => {
// THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER
// HH:MM:SS
// 00:02:13
const seconds = toFixed(Math.floor((val / 10) % 60));
const minutes = toFixed(Math.floor((val / (10 * 60)) % 60));
const hours = toFixed(Math.floor((val / (10 * 60 * 60)) % 24));
switch (formatType) {
case 'short': {
const hours_truncated = hours > 0 ? `${hours}h` : '';
const minutes_truncated = minutes > 0 ? `${minutes}m` : '';
const seconds_truncated = seconds > 0 ? `${seconds}s` : '';
return `${hours_truncated}${minutes_truncated}${seconds_truncated}`;
}
default: {
const seconds_padded = seconds.padStart(2, '0');
const minutes_padded = minutes.padStart(2, '0');
const hours_padded = hours.padStart(2, '0');
return `${hours_padded}:${minutes_padded}:${seconds_padded}`;
}
}
};
/* VOREStation Addition Start */
export const formatCommaNumber = (value) => {
if (!Number.isFinite(value)) {
return value;
}
// From http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
let parts = value.toString().split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.');
};
/* VOREStation Addition End */