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