UnitConverterTemperature.sys.mjs (3276B)
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 const ABSOLUTE = ["celsius", "kelvin", "fahrenheit"]; 8 const ALIAS = ["c", "k", "f"]; 9 const UNITS = [...ABSOLUTE, ...ALIAS]; 10 11 const NUMBER_REGEX = "-?\\d+(?:\\.\\d+)?\\s*"; 12 const UNIT_REGEX = "\\w+"; 13 14 // NOTE: This regex need to be localized upon supporting multi locales 15 // since it supports en-US input format only. 16 const QUERY_REGEX = new RegExp( 17 `^(${NUMBER_REGEX})(${UNIT_REGEX})(?:\\s+in\\s+|\\s+to\\s+|\\s*=\\s*)(${UNIT_REGEX})`, 18 "i" 19 ); 20 21 /** 22 * This module converts temperature unit. 23 */ 24 export class UnitConverterTemperature { 25 /** 26 * Convert the given search string. 27 * 28 * @param {string} searchString 29 * The string to be converted 30 * @returns {string} conversion result. 31 */ 32 convert(searchString) { 33 const regexResult = QUERY_REGEX.exec(searchString); 34 if (!regexResult) { 35 return null; 36 } 37 38 const target = findUnits(regexResult[2], regexResult[3]); 39 40 if (!target) { 41 return null; 42 } 43 44 const { inputUnit, outputUnit } = target; 45 const inputNumber = Number(regexResult[1]); 46 const inputChar = inputUnit.charAt(0); 47 const outputChar = outputUnit.charAt(0); 48 49 let outputNumber; 50 if (inputChar === outputChar) { 51 outputNumber = inputNumber; 52 } else { 53 outputNumber = this[`${inputChar}2${outputChar}`](inputNumber); 54 } 55 56 outputNumber = parseFloat(outputNumber); 57 58 let formattedUnit; 59 try { 60 const formatter = new Intl.NumberFormat("en-US", { 61 style: "unit", 62 unit: outputUnit, 63 }); 64 const parts = formatter.formatToParts(1); 65 formattedUnit = parts.find(part => part.type == "unit").value; 66 } catch (e) { 67 formattedUnit = outputUnit; 68 } 69 70 let optionalSpace = formattedUnit[0] == "°" ? "" : " "; 71 72 return `${UrlbarUtils.formatUnitConversionResult(outputNumber)}${optionalSpace}${formattedUnit}`; 73 } 74 75 c2k(t) { 76 return t + 273.15; 77 } 78 79 c2f(t) { 80 return t * 1.8 + 32; 81 } 82 83 k2c(t) { 84 return t - 273.15; 85 } 86 87 k2f(t) { 88 return this.c2f(this.k2c(t)); 89 } 90 91 f2c(t) { 92 return (t - 32) / 1.8; 93 } 94 95 f2k(t) { 96 return this.c2k(this.f2c(t)); 97 } 98 } 99 100 /** 101 * Returns the suitable units for the given two values. 102 * If could not found suitable unit, returns null. 103 * 104 * @param {string} inputUnit 105 * A set of units to convert, mapped to the `inputUnit` value on the return 106 * @param {string} outputUnit 107 * A set of units to convert, mapped to the `outputUnit` value on the return 108 * @returns {{ inputUnit: string, outputUnit: string }} The suitable units. 109 */ 110 function findUnits(inputUnit, outputUnit) { 111 inputUnit = inputUnit.toLowerCase(); 112 outputUnit = outputUnit.toLowerCase(); 113 114 if (!UNITS.includes(inputUnit) || !UNITS.includes(outputUnit)) { 115 return null; 116 } 117 118 return { 119 inputUnit: toAbsoluteUnit(inputUnit), 120 outputUnit: toAbsoluteUnit(outputUnit), 121 }; 122 } 123 124 function toAbsoluteUnit(unit) { 125 if (unit.length !== 1) { 126 return unit; 127 } 128 129 return ABSOLUTE.find(a => a.startsWith(unit)); 130 }