243 lines
5.9 KiB
JavaScript
243 lines
5.9 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// NOTE: This units table need to be localized upon supporting multi locales
|
|
// since it supports en-US only.
|
|
// e.g. Should support plugada or funty as well for pound.
|
|
const UNITS_GROUPS = [
|
|
{
|
|
// Angle
|
|
degree: 1,
|
|
deg: "degree",
|
|
d: "degree",
|
|
radian: Math.PI / 180.0,
|
|
rad: "radian",
|
|
r: "radian",
|
|
gradian: 1 / 0.9,
|
|
grad: "gradian",
|
|
g: "gradian",
|
|
minute: 60,
|
|
min: "minute",
|
|
m: "minute",
|
|
second: 3600,
|
|
sec: "second",
|
|
s: "second",
|
|
sign: 1 / 30.0,
|
|
mil: 1 / 0.05625,
|
|
revolution: 1 / 360.0,
|
|
circle: 1 / 360.0,
|
|
turn: 1 / 360.0,
|
|
quadrant: 1 / 90.0,
|
|
rightangle: 1 / 90.0,
|
|
sextant: 1 / 60.0,
|
|
},
|
|
{
|
|
// Force
|
|
newton: 1,
|
|
n: "newton",
|
|
kilonewton: 0.001,
|
|
kn: "kilonewton",
|
|
"gram-force": 101.9716213,
|
|
gf: "gram-force",
|
|
"kilogram-force": 0.1019716213,
|
|
kgf: "kilogram-force",
|
|
"ton-force": 0.0001019716213,
|
|
tf: "ton-force",
|
|
exanewton: 1.0e-18,
|
|
en: "exanewton",
|
|
petanewton: 1.0e-15,
|
|
PN: "petanewton",
|
|
Pn: "petanewton",
|
|
teranewton: 1.0e-12,
|
|
tn: "teranewton",
|
|
giganewton: 1.0e-9,
|
|
gn: "giganewton",
|
|
meganewton: 0.000001,
|
|
MN: "meganewton",
|
|
Mn: "meganewton",
|
|
hectonewton: 0.01,
|
|
hn: "hectonewton",
|
|
dekanewton: 0.1,
|
|
dan: "dekanewton",
|
|
decinewton: 10,
|
|
dn: "decinewton",
|
|
centinewton: 100,
|
|
cn: "centinewton",
|
|
millinewton: 1000,
|
|
mn: "millinewton",
|
|
micronewton: 1000000,
|
|
µn: "micronewton",
|
|
nanonewton: 1000000000,
|
|
nn: "nanonewton",
|
|
piconewton: 1000000000000,
|
|
pn: "piconewton",
|
|
femtonewton: 1000000000000000,
|
|
fn: "femtonewton",
|
|
attonewton: 1000000000000000000,
|
|
an: "attonewton",
|
|
dyne: 100000,
|
|
dyn: "dyne",
|
|
"joule/meter": 1,
|
|
"j/m": "joule/meter",
|
|
"joule/centimeter": 100,
|
|
"j/cm": "joule/centimeter",
|
|
"ton-force-short": 0.0001124045,
|
|
short: "ton-force-short",
|
|
"ton-force-long": 0.0001003611,
|
|
tonf: "ton-force-long",
|
|
"kip-force": 0.0002248089,
|
|
kipf: "kip-force",
|
|
"pound-force": 0.2248089431,
|
|
lbf: "pound-force",
|
|
"ounce-force": 3.5969430896,
|
|
ozf: "ounce-force",
|
|
poundal: 7.2330138512,
|
|
pdl: "poundal",
|
|
pond: 101.9716213,
|
|
p: "pond",
|
|
kilopond: 0.1019716213,
|
|
kp: "kilopond",
|
|
},
|
|
{
|
|
// Length
|
|
meter: 1,
|
|
m: "meter",
|
|
nanometer: 1000000000,
|
|
micrometer: 1000000,
|
|
millimeter: 1000,
|
|
mm: "millimeter",
|
|
centimeter: 100,
|
|
cm: "centimeter",
|
|
kilometer: 0.001,
|
|
km: "kilometer",
|
|
mile: 0.0006213689,
|
|
mi: "mile",
|
|
yard: 1.0936132983,
|
|
yd: "yard",
|
|
foot: 3.280839895,
|
|
feet: "foot",
|
|
ft: "foot",
|
|
inch: 39.37007874,
|
|
inches: "inch",
|
|
in: "inch",
|
|
},
|
|
{
|
|
// Mass
|
|
kilogram: 1,
|
|
kg: "kilogram",
|
|
gram: 1000,
|
|
g: "gram",
|
|
milligram: 1000000,
|
|
mg: "milligram",
|
|
ton: 0.001,
|
|
t: "ton",
|
|
longton: 0.0009842073,
|
|
"l.t.": "longton",
|
|
"l/t": "longton",
|
|
shortton: 0.0011023122,
|
|
"s.t.": "shortton",
|
|
"s/t": "shortton",
|
|
pound: 2.2046244202,
|
|
lbs: "pound",
|
|
lb: "pound",
|
|
ounce: 35.273990723,
|
|
oz: "ounce",
|
|
carat: 5000,
|
|
ffd: 5000,
|
|
},
|
|
];
|
|
|
|
// There are some units that will be same in lower case in same unit group.
|
|
// e.g. Mn: meganewton and mn: millinewton on force group.
|
|
// Handle them as case-sensitive.
|
|
const CASE_SENSITIVE_UNITS = ["PN", "Pn", "MN", "Mn"];
|
|
|
|
const NUMBER_REGEX = "-?\\d+(?:\\.\\d+)?\\s*";
|
|
const UNIT_REGEX = "[A-Za-zµ0-9_./-]+";
|
|
|
|
// NOTE: This regex need to be localized upon supporting multi locales
|
|
// since it supports en-US input format only.
|
|
const QUERY_REGEX = new RegExp(
|
|
`^(${NUMBER_REGEX})(${UNIT_REGEX})(?:\\s+in\\s+|\\s+to\\s+|\\s*=\\s*)(${UNIT_REGEX})`,
|
|
"i"
|
|
);
|
|
|
|
const DECIMAL_PRECISION = 10;
|
|
|
|
/**
|
|
* This module converts simple unit such as angle and length.
|
|
*/
|
|
export class UnitConverterSimple {
|
|
/**
|
|
* Convert the given search string.
|
|
*
|
|
* @param {string} searchString
|
|
* The string to be converted
|
|
* @returns {string} conversion result.
|
|
*/
|
|
convert(searchString) {
|
|
const regexResult = QUERY_REGEX.exec(searchString);
|
|
if (!regexResult) {
|
|
return null;
|
|
}
|
|
|
|
const target = findUnitGroup(regexResult[2], regexResult[3]);
|
|
|
|
if (!target) {
|
|
return null;
|
|
}
|
|
|
|
const { group, inputUnit, outputUnit } = target;
|
|
const inputNumber = Number(regexResult[1]);
|
|
const outputNumber = parseFloat(
|
|
((inputNumber / group[inputUnit]) * group[outputUnit]).toPrecision(
|
|
DECIMAL_PRECISION
|
|
)
|
|
);
|
|
|
|
try {
|
|
return new Intl.NumberFormat("en-US", {
|
|
style: "unit",
|
|
unit: outputUnit,
|
|
maximumFractionDigits: DECIMAL_PRECISION,
|
|
}).format(outputNumber);
|
|
} catch (e) {}
|
|
|
|
return `${outputNumber} ${outputUnit}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the suitable units for the given two values.
|
|
* If could not found suitable unit, returns null.
|
|
*
|
|
* @param {string} inputUnit
|
|
* A set of units to convert, mapped to the `inputUnit` value on the return
|
|
* @param {string} outputUnit
|
|
* A set of units to convert, mapped to the `outputUnit` value on the return
|
|
* @returns {{ inputUnit: string, outputUnit: string }} The suitable units.
|
|
*/
|
|
function findUnitGroup(inputUnit, outputUnit) {
|
|
inputUnit = toSuitableUnit(inputUnit);
|
|
outputUnit = toSuitableUnit(outputUnit);
|
|
|
|
const group = UNITS_GROUPS.find(ug => ug[inputUnit] && ug[outputUnit]);
|
|
|
|
if (!group) {
|
|
return null;
|
|
}
|
|
|
|
const inputValue = group[inputUnit];
|
|
const outputValue = group[outputUnit];
|
|
|
|
return {
|
|
group,
|
|
inputUnit: typeof inputValue === "string" ? inputValue : inputUnit,
|
|
outputUnit: typeof outputValue === "string" ? outputValue : outputUnit,
|
|
};
|
|
}
|
|
|
|
function toSuitableUnit(unit) {
|
|
return CASE_SENSITIVE_UNITS.includes(unit) ? unit : unit.toLowerCase();
|
|
}
|