UnitConverterSimple.sys.mjs (6258B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { UrlbarUtils } from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs"; 6 7 // NOTE: This units table need to be localized upon supporting multi locales 8 // since it supports en-US only. 9 // e.g. Should support plugada or funty as well for pound. 10 /** 11 * @type {{[key: string]: any}[]} 12 */ 13 const UNITS_GROUPS = [ 14 { 15 // Angle 16 degree: 1, 17 deg: "degree", 18 d: "degree", 19 radian: Math.PI / 180.0, 20 rad: "radian", 21 r: "radian", 22 gradian: 1 / 0.9, 23 grad: "gradian", 24 g: "gradian", 25 minute: 60, 26 min: "minute", 27 m: "minute", 28 second: 3600, 29 sec: "second", 30 s: "second", 31 sign: 1 / 30.0, 32 mil: 1 / 0.05625, 33 revolution: 1 / 360.0, 34 circle: 1 / 360.0, 35 turn: 1 / 360.0, 36 quadrant: 1 / 90.0, 37 rightangle: 1 / 90.0, 38 sextant: 1 / 60.0, 39 }, 40 { 41 // Force 42 newton: 1, 43 n: "newton", 44 kilonewton: 0.001, 45 kn: "kilonewton", 46 "gram-force": 101.9716213, 47 gf: "gram-force", 48 "kilogram-force": 0.1019716213, 49 kgf: "kilogram-force", 50 "ton-force": 0.0001019716213, 51 tf: "ton-force", 52 exanewton: 1.0e-18, 53 en: "exanewton", 54 petanewton: 1.0e-15, 55 PN: "petanewton", 56 Pn: "petanewton", 57 teranewton: 1.0e-12, 58 tn: "teranewton", 59 giganewton: 1.0e-9, 60 gn: "giganewton", 61 meganewton: 0.000001, 62 MN: "meganewton", 63 Mn: "meganewton", 64 hectonewton: 0.01, 65 hn: "hectonewton", 66 dekanewton: 0.1, 67 dan: "dekanewton", 68 decinewton: 10, 69 dn: "decinewton", 70 centinewton: 100, 71 cn: "centinewton", 72 millinewton: 1000, 73 mn: "millinewton", 74 micronewton: 1000000, 75 µn: "micronewton", 76 nanonewton: 1000000000, 77 nn: "nanonewton", 78 piconewton: 1000000000000, 79 pn: "piconewton", 80 femtonewton: 1000000000000000, 81 fn: "femtonewton", 82 attonewton: 1000000000000000000, 83 an: "attonewton", 84 dyne: 100000, 85 dyn: "dyne", 86 "joule/meter": 1, 87 "j/m": "joule/meter", 88 "joule/centimeter": 100, 89 "j/cm": "joule/centimeter", 90 "ton-force-short": 0.0001124045, 91 short: "ton-force-short", 92 "ton-force-long": 0.0001003611, 93 tonf: "ton-force-long", 94 "kip-force": 0.0002248089, 95 kipf: "kip-force", 96 "pound-force": 0.2248089431, 97 lbf: "pound-force", 98 "ounce-force": 3.5969430896, 99 ozf: "ounce-force", 100 poundal: 7.2330138512, 101 pdl: "poundal", 102 pond: 101.9716213, 103 p: "pond", 104 kilopond: 0.1019716213, 105 kp: "kilopond", 106 }, 107 { 108 // Length 109 meter: 1, 110 m: "meter", 111 nanometer: 1000000000, 112 micrometer: 1000000, 113 millimeter: 1000, 114 mm: "millimeter", 115 centimeter: 100, 116 cm: "centimeter", 117 kilometer: 0.001, 118 km: "kilometer", 119 mile: 0.0006213689, 120 mi: "mile", 121 yard: 1.0936132983, 122 yd: "yard", 123 foot: 3.280839895, 124 feet: "foot", 125 ft: "foot", 126 inch: 39.37007874, 127 inches: "inch", 128 in: "inch", 129 }, 130 { 131 // Mass 132 kilogram: 1, 133 kg: "kilogram", 134 gram: 1000, 135 g: "gram", 136 milligram: 1000000, 137 mg: "milligram", 138 ton: 0.001, 139 t: "ton", 140 longton: 0.0009842073, 141 "l.t.": "longton", 142 "l/t": "longton", 143 shortton: 0.0011023122, 144 "s.t.": "shortton", 145 "s/t": "shortton", 146 pound: 2.2046244202, 147 lbs: "pound", 148 lb: "pound", 149 ounce: 35.273990723, 150 oz: "ounce", 151 carat: 5000, 152 ffd: 5000, 153 }, 154 ]; 155 156 // There are some units that will be same in lower case in same unit group. 157 // e.g. Mn: meganewton and mn: millinewton on force group. 158 // Handle them as case-sensitive. 159 const CASE_SENSITIVE_UNITS = ["PN", "Pn", "MN", "Mn"]; 160 161 const NUMBER_REGEX = "-?\\d+(?:\\.\\d+)?\\s*"; 162 const UNIT_REGEX = "[A-Za-zµ0-9_./-]+"; 163 164 // NOTE: This regex need to be localized upon supporting multi locales 165 // since it supports en-US input format only. 166 const QUERY_REGEX = new RegExp( 167 `^(${NUMBER_REGEX})(${UNIT_REGEX})(?:\\s+in\\s+|\\s+to\\s+|\\s*=\\s*)(${UNIT_REGEX})`, 168 "i" 169 ); 170 171 /** 172 * This module converts simple unit such as angle and length. 173 */ 174 export class UnitConverterSimple { 175 /** 176 * Convert the given search string. 177 * 178 * @param {string} searchString 179 * The string to be converted 180 * @returns {string} conversion result. 181 */ 182 convert(searchString) { 183 const regexResult = QUERY_REGEX.exec(searchString); 184 if (!regexResult) { 185 return null; 186 } 187 188 const target = findUnitGroup(regexResult[2], regexResult[3]); 189 190 if (!target) { 191 return null; 192 } 193 194 const { group, inputUnit, outputUnit } = target; 195 const inputNumber = Number(regexResult[1]); 196 const outputNumber = (inputNumber / group[inputUnit]) * group[outputUnit]; 197 198 let formattedUnit; 199 try { 200 const formatter = new Intl.NumberFormat("en-US", { 201 style: "unit", 202 unit: outputUnit, 203 }); 204 const parts = formatter.formatToParts(1); 205 formattedUnit = parts.find(part => part.type == "unit").value; 206 } catch (e) { 207 formattedUnit = outputUnit; 208 } 209 210 return `${UrlbarUtils.formatUnitConversionResult(outputNumber)} ${formattedUnit}`; 211 } 212 } 213 214 /** 215 * Returns the suitable units for the given two values. 216 * If could not found suitable unit, returns null. 217 * 218 * @param {string} inputUnit 219 * A set of units to convert, mapped to the `inputUnit` value on the return 220 * @param {string} outputUnit 221 * A set of units to convert, mapped to the `outputUnit` value on the return 222 */ 223 function findUnitGroup(inputUnit, outputUnit) { 224 inputUnit = toSuitableUnit(inputUnit); 225 outputUnit = toSuitableUnit(outputUnit); 226 227 const group = UNITS_GROUPS.find(ug => ug[inputUnit] && ug[outputUnit]); 228 229 if (!group) { 230 return null; 231 } 232 233 const inputValue = group[inputUnit]; 234 const outputValue = group[outputUnit]; 235 236 return { 237 group, 238 inputUnit: typeof inputValue === "string" ? inputValue : inputUnit, 239 outputUnit: typeof outputValue === "string" ? outputValue : outputUnit, 240 }; 241 } 242 243 /** 244 * Converts the unit value to an appropriate case if necessary. 245 * 246 * @param {string} unit 247 */ 248 function toSuitableUnit(unit) { 249 return CASE_SENSITIVE_UNITS.includes(unit) ? unit : unit.toLowerCase(); 250 }