summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/unitconverters
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/urlbar/unitconverters/UnitConverterSimple.sys.mjs243
-rw-r--r--browser/components/urlbar/unitconverters/UnitConverterTemperature.sys.mjs124
-rw-r--r--browser/components/urlbar/unitconverters/UnitConverterTimezone.sys.mjs148
-rw-r--r--browser/components/urlbar/unitconverters/moz.build9
4 files changed, 524 insertions, 0 deletions
diff --git a/browser/components/urlbar/unitconverters/UnitConverterSimple.sys.mjs b/browser/components/urlbar/unitconverters/UnitConverterSimple.sys.mjs
new file mode 100644
index 0000000000..47ba0cc856
--- /dev/null
+++ b/browser/components/urlbar/unitconverters/UnitConverterSimple.sys.mjs
@@ -0,0 +1,243 @@
+/* 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();
+}
diff --git a/browser/components/urlbar/unitconverters/UnitConverterTemperature.sys.mjs b/browser/components/urlbar/unitconverters/UnitConverterTemperature.sys.mjs
new file mode 100644
index 0000000000..5a78d20577
--- /dev/null
+++ b/browser/components/urlbar/unitconverters/UnitConverterTemperature.sys.mjs
@@ -0,0 +1,124 @@
+/* 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/. */
+
+const ABSOLUTE = ["celsius", "kelvin", "fahrenheit"];
+const ALIAS = ["c", "k", "f"];
+const UNITS = [...ABSOLUTE, ...ALIAS];
+
+const NUMBER_REGEX = "-?\\d+(?:\\.\\d+)?\\s*";
+const UNIT_REGEX = "\\w+";
+
+// 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 temperature unit.
+ */
+export class UnitConverterTemperature {
+ /**
+ * 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 = findUnits(regexResult[2], regexResult[3]);
+
+ if (!target) {
+ return null;
+ }
+
+ const { inputUnit, outputUnit } = target;
+ const inputNumber = Number(regexResult[1]);
+ const inputChar = inputUnit.charAt(0);
+ const outputChar = outputUnit.charAt(0);
+
+ let outputNumber;
+ if (inputChar === outputChar) {
+ outputNumber = inputNumber;
+ } else {
+ outputNumber = this[`${inputChar}2${outputChar}`](inputNumber);
+ }
+
+ outputNumber = parseFloat(outputNumber.toPrecision(DECIMAL_PRECISION));
+
+ try {
+ return new Intl.NumberFormat("en-US", {
+ style: "unit",
+ unit: outputUnit,
+ maximumFractionDigits: DECIMAL_PRECISION,
+ }).format(outputNumber);
+ } catch (e) {}
+
+ return `${outputNumber} ${outputUnit}`;
+ }
+
+ c2k(t) {
+ return t + 273.15;
+ }
+
+ c2f(t) {
+ return t * 1.8 + 32;
+ }
+
+ k2c(t) {
+ return t - 273.15;
+ }
+
+ k2f(t) {
+ return this.c2f(this.k2c(t));
+ }
+
+ f2c(t) {
+ return (t - 32) / 1.8;
+ }
+
+ f2k(t) {
+ return this.c2k(this.f2c(t));
+ }
+}
+
+/**
+ * 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 findUnits(inputUnit, outputUnit) {
+ inputUnit = inputUnit.toLowerCase();
+ outputUnit = outputUnit.toLowerCase();
+
+ if (!UNITS.includes(inputUnit) || !UNITS.includes(outputUnit)) {
+ return null;
+ }
+
+ return {
+ inputUnit: toAbsoluteUnit(inputUnit),
+ outputUnit: toAbsoluteUnit(outputUnit),
+ };
+}
+
+function toAbsoluteUnit(unit) {
+ if (unit.length !== 1) {
+ return unit;
+ }
+
+ return ABSOLUTE.find(a => a.startsWith(unit));
+}
diff --git a/browser/components/urlbar/unitconverters/UnitConverterTimezone.sys.mjs b/browser/components/urlbar/unitconverters/UnitConverterTimezone.sys.mjs
new file mode 100644
index 0000000000..50ba924bed
--- /dev/null
+++ b/browser/components/urlbar/unitconverters/UnitConverterTimezone.sys.mjs
@@ -0,0 +1,148 @@
+/* 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/. */
+
+const TIMEZONES = {
+ IDLW: -12,
+ NT: -11,
+ HST: -10,
+ AKST: -9,
+ PST: -8,
+ AKDT: -8,
+ MST: -7,
+ PDT: -7,
+ CST: -6,
+ MDT: -6,
+ EST: -5,
+ CDT: -5,
+ EDT: -4,
+ AST: -4,
+ GUY: -3,
+ ADT: -3,
+ AT: -2,
+ UTC: 0,
+ GMT: 0,
+ Z: 0,
+ WET: 0,
+ WEST: 1,
+ CET: 1,
+ BST: 1,
+ IST: 1,
+ CEST: 2,
+ EET: 2,
+ EEST: 3,
+ MSK: 3,
+ MSD: 4,
+ ZP4: 4,
+ ZP5: 5,
+ ZP6: 6,
+ WAST: 7,
+ AWST: 8,
+ WST: 8,
+ JST: 9,
+ ACST: 9.5,
+ ACDT: 10.5,
+ AEST: 10,
+ AEDT: 11,
+ NZST: 12,
+ IDLE: 12,
+ NZD: 13,
+};
+
+const TIME_REGEX = "([0-2]?[0-9])(:([0-5][0-9]))?\\s*([ap]m)?";
+const TIMEZONE_REGEX = "\\w+";
+
+// NOTE: This regex need to be localized upon supporting multi locales
+// since it supports en-US input format only.
+const QUERY_REGEX = new RegExp(
+ `^(${TIME_REGEX}|now)\\s*(${TIMEZONE_REGEX})?(?:\\s+in\\s+|\\s+to\\s+|\\s*=\\s*)(${TIMEZONE_REGEX}|here)`,
+ "i"
+);
+
+const KEYWORD_HERE = "HERE";
+const KEYWORD_NOW = "NOW";
+
+/**
+ * This module converts timezone.
+ */
+export class UnitConverterTimezone {
+ /**
+ * 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 inputTime = regexResult[1].toUpperCase();
+ const inputTimezone = regexResult[6]?.toUpperCase();
+ let outputTimezone = regexResult[7].toUpperCase();
+
+ if (
+ (inputTimezone &&
+ inputTimezone !== KEYWORD_NOW &&
+ !(inputTimezone in TIMEZONES)) ||
+ (outputTimezone !== KEYWORD_HERE && !(outputTimezone in TIMEZONES))
+ ) {
+ return null;
+ }
+
+ const inputDate = new Date();
+ let isMeridiemNeeded = false;
+ if (inputTime === KEYWORD_NOW) {
+ inputDate.setUTCHours(inputDate.getHours());
+ inputDate.setUTCMinutes(inputDate.getMinutes());
+ } else {
+ // If the input was given as AM/PM, we need to convert it to 24h.
+ // 12AM is converted to 00, and for PM times we add 12 to the hour value except for 12PM.
+ // If the input is for example 23PM, we use 23 as the hour - we don't add 12 as this would result in a date increment.
+ const inputAMPM = regexResult[5]?.toLowerCase() || "";
+ const inputHours =
+ regexResult[2] === "12" && inputAMPM === "am"
+ ? 0
+ : Number(regexResult[2]);
+ const inputMinutes = regexResult[4] ? Number(regexResult[4]) : 0;
+ const inputMeridianHourShift =
+ inputAMPM === "pm" && inputHours < 12 ? 12 : 0;
+ inputDate.setUTCHours(inputHours + inputMeridianHourShift);
+ inputDate.setUTCMinutes(inputMinutes);
+ isMeridiemNeeded = !!inputAMPM;
+ }
+
+ const inputOffset = inputTimezone
+ ? TIMEZONES[inputTimezone] * 60
+ : -inputDate.getTimezoneOffset();
+ let outputOffset;
+ if (outputTimezone === KEYWORD_HERE) {
+ outputOffset = -inputDate.getTimezoneOffset();
+ const sign = -inputDate.getTimezoneOffset() > 0 ? "+" : "-";
+ const hours = parseInt(Math.abs(outputOffset) / 60);
+ const minutes = formatMinutes((outputOffset % 60) * 60);
+ outputTimezone = `UTC${sign}${hours}${minutes}`;
+ } else {
+ outputOffset = TIMEZONES[outputTimezone] * 60;
+ }
+
+ const outputDate = new Date(inputDate.getTime());
+ outputDate.setUTCMinutes(
+ outputDate.getUTCMinutes() - inputOffset + outputOffset
+ );
+
+ const time = new Intl.DateTimeFormat("en-US", {
+ timeStyle: "short",
+ hour12: isMeridiemNeeded,
+ timeZone: "UTC",
+ }).format(outputDate);
+
+ return `${time} ${outputTimezone}`;
+ }
+}
+
+function formatMinutes(minutes) {
+ return minutes.toString().padStart(2, "0");
+}
diff --git a/browser/components/urlbar/unitconverters/moz.build b/browser/components/urlbar/unitconverters/moz.build
new file mode 100644
index 0000000000..649522658f
--- /dev/null
+++ b/browser/components/urlbar/unitconverters/moz.build
@@ -0,0 +1,9 @@
+# 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/.
+
+EXTRA_JS_MODULES += [
+ "UnitConverterSimple.sys.mjs",
+ "UnitConverterTemperature.sys.mjs",
+ "UnitConverterTimezone.sys.mjs",
+]