Source code

Revision control

Copy as Markdown

Other Tools

/* 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();
}