--- a/browser/extensions/formautofill/phonenumberutils/PhoneNumber.jsm
+++ b/browser/extensions/formautofill/phonenumberutils/PhoneNumber.jsm
@@ -6,191 +6,206 @@
// be further maintained by our own in Form Autofill codebase.
"use strict";
this.EXPORTED_SYMBOLS = ["PhoneNumber"];
const Cu = Components.utils;
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PHONE_NUMBER_META_DATA",
"resource://formautofill/phonenumberutils/PhoneNumberMetaData.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PhoneNumberNormalizer",
"resource://formautofill/phonenumberutils/PhoneNumberNormalizer.jsm");
-this.PhoneNumber = (function (dataBase) {
- // Use strict in our context only - users might not want it
- 'use strict';
-
+this.PhoneNumber = (function(dataBase) {
const MAX_PHONE_NUMBER_LENGTH = 50;
const NON_ALPHA_CHARS = /[^a-zA-Z]/g;
const NON_DIALABLE_CHARS = /[^,#+\*\d]/g;
const NON_DIALABLE_CHARS_ONCE = new RegExp(NON_DIALABLE_CHARS.source);
const BACKSLASH = /\\/g;
const SPLIT_FIRST_GROUP = /^(\d+)(.*)$/;
const LEADING_PLUS_CHARS_PATTERN = /^[+\uFF0B]+/g;
// Format of the string encoded meta data. If the name contains "^" or "$"
// we will generate a regular expression from the value, with those special
// characters as prefix/suffix.
- const META_DATA_ENCODING = ["region",
- "^(?:internationalPrefix)",
- "nationalPrefix",
- "^(?:nationalPrefixForParsing)",
- "nationalPrefixTransformRule",
- "nationalPrefixFormattingRule",
- "^possiblePattern$",
- "^nationalPattern$",
- "formats"];
+ const META_DATA_ENCODING = [
+ "region",
+ "^(?:internationalPrefix)",
+ "nationalPrefix",
+ "^(?:nationalPrefixForParsing)",
+ "nationalPrefixTransformRule",
+ "nationalPrefixFormattingRule",
+ "^possiblePattern$",
+ "^nationalPattern$",
+ "formats",
+ ];
- const FORMAT_ENCODING = ["^pattern$",
- "nationalFormat",
- "^leadingDigits",
- "nationalPrefixFormattingRule",
- "internationalFormat"];
+ const FORMAT_ENCODING = [
+ "^pattern$",
+ "nationalFormat",
+ "^leadingDigits",
+ "nationalPrefixFormattingRule",
+ "internationalFormat",
+ ];
- var regionCache = Object.create(null);
+ let regionCache = Object.create(null);
// Parse an array of strings into a convenient object. We store meta
// data as arrays since thats much more compact than JSON.
function ParseArray(array, encoding, obj) {
- for (var n = 0; n < encoding.length; ++n) {
- var value = array[n];
- if (!value)
+ for (let n = 0; n < encoding.length; ++n) {
+ let value = array[n];
+ if (!value) {
continue;
- var field = encoding[n];
- var fieldAlpha = field.replace(NON_ALPHA_CHARS, "");
- if (field != fieldAlpha)
+ }
+ let field = encoding[n];
+ let fieldAlpha = field.replace(NON_ALPHA_CHARS, "");
+ if (field != fieldAlpha) {
value = new RegExp(field.replace(fieldAlpha, value));
+ }
obj[fieldAlpha] = value;
}
return obj;
}
// Parse string encoded meta data into a convenient object
// representation.
function ParseMetaData(countryCode, md) {
- var array = eval(md.replace(BACKSLASH, "\\\\"));
+ /* eslint-disable no-eval */
+ let array = eval(md.replace(BACKSLASH, "\\\\"));
md = ParseArray(array,
META_DATA_ENCODING,
- { countryCode: countryCode });
+ {countryCode});
regionCache[md.region] = md;
return md;
}
// Parse string encoded format data into a convenient object
// representation.
function ParseFormat(md) {
- var formats = md.formats;
+ let formats = md.formats;
if (!formats) {
- return null;
+ return;
}
// Bail if we already parsed the format definitions.
- if (!(Array.isArray(formats[0])))
+ if (!(Array.isArray(formats[0]))) {
return;
- for (var n = 0; n < formats.length; ++n) {
+ }
+ for (let n = 0; n < formats.length; ++n) {
formats[n] = ParseArray(formats[n],
FORMAT_ENCODING,
{});
}
}
// Search for the meta data associated with a region identifier ("US") in
// our database, which is indexed by country code ("1"). Since we have
// to walk the entire database for this, we cache the result of the lookup
// for future reference.
function FindMetaDataForRegion(region) {
// Check in the region cache first. This will find all entries we have
// already resolved (parsed from a string encoding).
- var md = regionCache[region];
- if (md)
+ let md = regionCache[region];
+ if (md) {
return md;
- for (var countryCode in dataBase) {
- var entry = dataBase[countryCode];
+ }
+ for (let countryCode in dataBase) {
+ let entry = dataBase[countryCode];
// Each entry is a string encoded object of the form '["US..', or
// an array of strings. We don't want to parse the string here
// to save memory, so we just substring the region identifier
// and compare it. For arrays, we compare against all region
// identifiers with that country code. We skip entries that are
// of type object, because they were already resolved (parsed into
// an object), and their country code should have been in the cache.
if (Array.isArray(entry)) {
- for (var n = 0; n < entry.length; n++) {
- if (typeof entry[n] == "string" && entry[n].substr(2,2) == region) {
+ for (let n = 0; n < entry.length; n++) {
+ if (typeof entry[n] == "string" && entry[n].substr(2, 2) == region) {
if (n > 0) {
// Only the first entry has the formats field set.
// Parse the main country if we haven't already and use
// the formats field from the main country.
- if (typeof entry[0] == "string")
+ if (typeof entry[0] == "string") {
entry[0] = ParseMetaData(countryCode, entry[0]);
+ }
let formats = entry[0].formats;
let current = ParseMetaData(countryCode, entry[n]);
current.formats = formats;
- return entry[n] = current;
+ entry[n] = current;
+ return entry[n];
}
entry[n] = ParseMetaData(countryCode, entry[n]);
return entry[n];
}
}
continue;
}
- if (typeof entry == "string" && entry.substr(2,2) == region)
- return dataBase[countryCode] = ParseMetaData(countryCode, entry);
+ if (typeof entry == "string" && entry.substr(2, 2) == region) {
+ dataBase[countryCode] = ParseMetaData(countryCode, entry);
+ return dataBase[countryCode];
+ }
}
}
// Format a national number for a given region. The boolean flag "intl"
// indicates whether we want the national or international format.
function FormatNumber(regionMetaData, number, intl) {
// We lazily parse the format description in the meta data for the region,
// so make sure to parse it now if we haven't already done so.
ParseFormat(regionMetaData);
- var formats = regionMetaData.formats;
+ let formats = regionMetaData.formats;
if (!formats) {
return null;
}
- for (var n = 0; n < formats.length; ++n) {
- var format = formats[n];
+ for (let n = 0; n < formats.length; ++n) {
+ let format = formats[n];
// The leading digits field is optional. If we don't have it, just
// use the matching pattern to qualify numbers.
- if (format.leadingDigits && !format.leadingDigits.test(number))
+ if (format.leadingDigits && !format.leadingDigits.test(number)) {
continue;
- if (!format.pattern.test(number))
+ }
+ if (!format.pattern.test(number)) {
continue;
+ }
if (intl) {
// If there is no international format, just fall back to the national
// format.
- var internationalFormat = format.internationalFormat;
- if (!internationalFormat)
+ let internationalFormat = format.internationalFormat;
+ if (!internationalFormat) {
internationalFormat = format.nationalFormat;
+ }
// Some regions have numbers that can't be dialed from outside the
// country, indicated by "NA" for the international format of that
// number format pattern.
- if (internationalFormat == "NA")
+ if (internationalFormat == "NA") {
return null;
+ }
// Prepend "+" and the country code.
number = "+" + regionMetaData.countryCode + " " +
number.replace(format.pattern, internationalFormat);
} else {
number = number.replace(format.pattern, format.nationalFormat);
// The region has a national prefix formatting rule, and it can be overwritten
// by each actual number format rule.
- var nationalPrefixFormattingRule = regionMetaData.nationalPrefixFormattingRule;
- if (format.nationalPrefixFormattingRule)
+ let nationalPrefixFormattingRule = regionMetaData.nationalPrefixFormattingRule;
+ if (format.nationalPrefixFormattingRule) {
nationalPrefixFormattingRule = format.nationalPrefixFormattingRule;
+ }
if (nationalPrefixFormattingRule) {
// The prefix formatting rule contains two magic markers, "$NP" and "$FG".
// "$NP" will be replaced by the national prefix, and "$FG" with the
// first group of numbers.
- var match = number.match(SPLIT_FIRST_GROUP);
+ let match = number.match(SPLIT_FIRST_GROUP);
if (match) {
- var firstGroup = match[1];
- var rest = match[2];
- var prefix = nationalPrefixFormattingRule;
+ let firstGroup = match[1];
+ let rest = match[2];
+ let prefix = nationalPrefixFormattingRule;
prefix = prefix.replace("$NP", regionMetaData.nationalPrefix);
prefix = prefix.replace("$FG", firstGroup);
number = prefix + rest;
}
}
}
return (number == "NA") ? null : number;
}
@@ -205,221 +220,236 @@ this.PhoneNumber = (function (dataBase)
// NationalNumber represents the result of parsing a phone number. We have
// three getters on the prototype that format the number in national and
// international format. Once called, the getters put a direct property
// onto the object, caching the result.
NationalNumber.prototype = {
// +1 949-726-2896
get internationalFormat() {
- var value = FormatNumber(this.regionMetaData, this.number, true);
- Object.defineProperty(this, "internationalFormat", { value: value, enumerable: true });
+ let value = FormatNumber(this.regionMetaData, this.number, true);
+ Object.defineProperty(this, "internationalFormat", {value, enumerable: true});
return value;
},
// (949) 726-2896
get nationalFormat() {
- var value = FormatNumber(this.regionMetaData, this.number, false);
- Object.defineProperty(this, "nationalFormat", { value: value, enumerable: true });
+ let value = FormatNumber(this.regionMetaData, this.number, false);
+ Object.defineProperty(this, "nationalFormat", {value, enumerable: true});
return value;
},
// +19497262896
get internationalNumber() {
- var value = this.internationalFormat ? this.internationalFormat.replace(NON_DIALABLE_CHARS, "")
+ let value = this.internationalFormat ? this.internationalFormat.replace(NON_DIALABLE_CHARS, "")
: null;
- Object.defineProperty(this, "internationalNumber", { value: value, enumerable: true });
+ Object.defineProperty(this, "internationalNumber", {value, enumerable: true});
return value;
},
// 9497262896
get nationalNumber() {
- var value = this.nationalFormat ? this.nationalFormat.replace(NON_DIALABLE_CHARS, "")
+ let value = this.nationalFormat ? this.nationalFormat.replace(NON_DIALABLE_CHARS, "")
: null;
- Object.defineProperty(this, "nationalNumber", { value: value, enumerable: true });
+ Object.defineProperty(this, "nationalNumber", {value, enumerable: true});
return value;
},
// country name 'US'
get countryName() {
- var value = this.region ? this.region : null;
- Object.defineProperty(this, "countryName", { value: value, enumerable: true });
+ let value = this.region ? this.region : null;
+ Object.defineProperty(this, "countryName", {value, enumerable: true});
return value;
},
// country code '+1'
get countryCode() {
- var value = this.regionMetaData.countryCode ? "+" + this.regionMetaData.countryCode : null;
- Object.defineProperty(this, "countryCode", { value: value, enumerable: true });
+ let value = this.regionMetaData.countryCode ? "+" + this.regionMetaData.countryCode : null;
+ Object.defineProperty(this, "countryCode", {value, enumerable: true});
return value;
- }
+ },
};
// Check whether the number is valid for the given region.
function IsValidNumber(number, md) {
return md.possiblePattern.test(number);
}
// Check whether the number is a valid national number for the given region.
+ /* eslint-disable no-unused-vars */
function IsNationalNumber(number, md) {
return IsValidNumber(number, md) && md.nationalPattern.test(number);
}
// Determine the country code a number starts with, or return null if
// its not a valid country code.
function ParseCountryCode(number) {
- for (var n = 1; n <= 3; ++n) {
- var cc = number.substr(0,n);
- if (dataBase[cc])
+ for (let n = 1; n <= 3; ++n) {
+ let cc = number.substr(0, n);
+ if (dataBase[cc]) {
return cc;
+ }
}
return null;
}
- // Parse an international number that starts with the country code. Return
- // null if the number is not a valid international number.
- function ParseInternationalNumber(number) {
- // Parse and strip the country code.
- var countryCode = ParseCountryCode(number);
- if (!countryCode)
- return null;
- number = number.substr(countryCode.length);
-
- return ParseNumberByCountryCode(number, countryCode);
- }
-
- function ParseNumberByCountryCode(number, countryCode) {
- var ret;
-
- // Lookup the meta data for the region (or regions) and if the rest of
- // the number parses for that region, return the parsed number.
- var entry = dataBase[countryCode];
- if (Array.isArray(entry)) {
- for (var n = 0; n < entry.length; ++n) {
- if (typeof entry[n] == "string")
- entry[n] = ParseMetaData(countryCode, entry[n]);
- if (n > 0)
- entry[n].formats = entry[0].formats;
- ret = ParseNationalNumberAndCheckNationalPrefix(number, entry[n]);
- if (ret)
- return ret;
- }
- return null;
- }
- if (typeof entry == "string")
- entry = dataBase[countryCode] = ParseMetaData(countryCode, entry);
- return ParseNationalNumberAndCheckNationalPrefix(number, entry);
- }
-
- function ParseNationalNumberAndCheckNationalPrefix(number, md) {
- var ret;
-
- // This is not an international number. See if its a national one for
- // the current region. National numbers can start with the national
- // prefix, or without.
- if (md.nationalPrefixForParsing) {
- // Some regions have specific national prefix parse rules. Apply those.
- var withoutPrefix = number.replace(md.nationalPrefixForParsing,
- md.nationalPrefixTransformRule || '');
- ret = ParseNationalNumber(withoutPrefix, md)
- if (ret)
- return ret;
- } else {
- // If there is no specific national prefix rule, just strip off the
- // national prefix from the beginning of the number (if there is one).
- var nationalPrefix = md.nationalPrefix;
- if (nationalPrefix && number.indexOf(nationalPrefix) == 0 &&
- (ret = ParseNationalNumber(number.substr(nationalPrefix.length), md))) {
- return ret;
- }
- }
- ret = ParseNationalNumber(number, md)
- if (ret)
- return ret;
- }
-
// Parse a national number for a specific region. Return null if the
// number is not a valid national number (it might still be a possible
// number for parts of that region).
function ParseNationalNumber(number, md) {
if (!md.possiblePattern.test(number) ||
!md.nationalPattern.test(number)) {
return null;
}
// Success.
return new NationalNumber(md, number);
}
+ function ParseNationalNumberAndCheckNationalPrefix(number, md) {
+ let ret;
+
+ // This is not an international number. See if its a national one for
+ // the current region. National numbers can start with the national
+ // prefix, or without.
+ if (md.nationalPrefixForParsing) {
+ // Some regions have specific national prefix parse rules. Apply those.
+ let withoutPrefix = number.replace(md.nationalPrefixForParsing,
+ md.nationalPrefixTransformRule || "");
+ ret = ParseNationalNumber(withoutPrefix, md);
+ if (ret) {
+ return ret;
+ }
+ } else {
+ // If there is no specific national prefix rule, just strip off the
+ // national prefix from the beginning of the number (if there is one).
+ let nationalPrefix = md.nationalPrefix;
+ if (nationalPrefix && number.indexOf(nationalPrefix) == 0 &&
+ (ret = ParseNationalNumber(number.substr(nationalPrefix.length), md))) {
+ return ret;
+ }
+ }
+ ret = ParseNationalNumber(number, md);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ function ParseNumberByCountryCode(number, countryCode) {
+ let ret;
+
+ // Lookup the meta data for the region (or regions) and if the rest of
+ // the number parses for that region, return the parsed number.
+ let entry = dataBase[countryCode];
+ if (Array.isArray(entry)) {
+ for (let n = 0; n < entry.length; ++n) {
+ if (typeof entry[n] == "string") {
+ entry[n] = ParseMetaData(countryCode, entry[n]);
+ }
+ if (n > 0) {
+ entry[n].formats = entry[0].formats;
+ }
+ ret = ParseNationalNumberAndCheckNationalPrefix(number, entry[n]);
+ if (ret) {
+ return ret;
+ }
+ }
+ return null;
+ }
+ if (typeof entry == "string") {
+ entry = dataBase[countryCode] = ParseMetaData(countryCode, entry);
+ }
+ return ParseNationalNumberAndCheckNationalPrefix(number, entry);
+ }
+
+ // Parse an international number that starts with the country code. Return
+ // null if the number is not a valid international number.
+ function ParseInternationalNumber(number) {
+ // Parse and strip the country code.
+ let countryCode = ParseCountryCode(number);
+ if (!countryCode) {
+ return null;
+ }
+ number = number.substr(countryCode.length);
+
+ return ParseNumberByCountryCode(number, countryCode);
+ }
+
// Parse a number and transform it into the national format, removing any
// international dial prefixes and country codes.
function ParseNumber(number, defaultRegion) {
- var ret;
+ let ret;
// Remove formating characters and whitespace.
number = PhoneNumberNormalizer.Normalize(number);
// If there is no defaultRegion or the defaultRegion is the global region,
// we can't parse international access codes.
- if ((!defaultRegion || defaultRegion === '001') && number[0] !== '+')
+ if ((!defaultRegion || defaultRegion === "001") && number[0] !== "+") {
return null;
+ }
// Detect and strip leading '+'.
- if (number[0] === '+')
+ if (number[0] === "+") {
return ParseInternationalNumber(number.replace(LEADING_PLUS_CHARS_PATTERN, ""));
+ }
// If "defaultRegion" is a country code, use it to parse the number directly.
- var matches = String(defaultRegion).match(/^\+?(\d+)/);
+ let matches = String(defaultRegion).match(/^\+?(\d+)/);
if (matches) {
- var countryCode = ParseCountryCode(matches[1]);
+ let countryCode = ParseCountryCode(matches[1]);
if (!countryCode) {
return null;
}
return ParseNumberByCountryCode(number, countryCode);
}
// Lookup the meta data for the given region.
- var md = FindMetaDataForRegion(defaultRegion.toUpperCase());
+ let md = FindMetaDataForRegion(defaultRegion.toUpperCase());
if (!md) {
dump("Couldn't find Meta Data for region: " + defaultRegion + "\n");
return null;
}
// See if the number starts with an international prefix, and if the
// number resulting from stripping the code is valid, then remove the
// prefix and flag the number as international.
if (md.internationalPrefix.test(number)) {
- var possibleNumber = number.replace(md.internationalPrefix, "");
- ret = ParseInternationalNumber(possibleNumber)
- if (ret)
+ let possibleNumber = number.replace(md.internationalPrefix, "");
+ ret = ParseInternationalNumber(possibleNumber);
+ if (ret) {
return ret;
+ }
}
ret = ParseNationalNumberAndCheckNationalPrefix(number, md);
- if (ret)
+ if (ret) {
return ret;
+ }
// Now lets see if maybe its an international number after all, but
// without '+' or the international prefix.
- ret = ParseInternationalNumber(number)
- if (ret)
+ ret = ParseInternationalNumber(number);
+ if (ret) {
return ret;
+ }
// If the number matches the possible numbers of the current region,
// return it as a possible number.
- if (md.possiblePattern.test(number))
+ if (md.possiblePattern.test(number)) {
return new NationalNumber(md, number);
+ }
// We couldn't parse the number at all.
return null;
}
function IsPlainPhoneNumber(number) {
- if (typeof number !== 'string') {
+ if (typeof number !== "string") {
return false;
}
- var length = number.length;
- var isTooLong = (length > MAX_PHONE_NUMBER_LENGTH);
- var isEmpty = (length === 0);
+ let length = number.length;
+ let isTooLong = (length > MAX_PHONE_NUMBER_LENGTH);
+ let isEmpty = (length === 0);
return !(isTooLong || isEmpty || NON_DIALABLE_CHARS_ONCE.test(number));
}
return {
IsPlain: IsPlainPhoneNumber,
Parse: ParseNumber,
};
})(PHONE_NUMBER_META_DATA);
--- a/browser/extensions/formautofill/phonenumberutils/PhoneNumberNormalizer.jsm
+++ b/browser/extensions/formautofill/phonenumberutils/PhoneNumberNormalizer.jsm
@@ -11,46 +11,46 @@ this.EXPORTED_SYMBOLS = ["PhoneNumberNor
this.PhoneNumberNormalizer = (function() {
const UNICODE_DIGITS = /[\uFF10-\uFF19\u0660-\u0669\u06F0-\u06F9]/g;
const VALID_ALPHA_PATTERN = /[a-zA-Z]/g;
const LEADING_PLUS_CHARS_PATTERN = /^[+\uFF0B]+/g;
const NON_DIALABLE_CHARS = /[^,#+\*\d]/g;
// Map letters to numbers according to the ITU E.161 standard
- var E161 = {
- 'a': 2, 'b': 2, 'c': 2,
- 'd': 3, 'e': 3, 'f': 3,
- 'g': 4, 'h': 4, 'i': 4,
- 'j': 5, 'k': 5, 'l': 5,
- 'm': 6, 'n': 6, 'o': 6,
- 'p': 7, 'q': 7, 'r': 7, 's': 7,
- 't': 8, 'u': 8, 'v': 8,
- 'w': 9, 'x': 9, 'y': 9, 'z': 9
+ let E161 = {
+ "a": 2, "b": 2, "c": 2,
+ "d": 3, "e": 3, "f": 3,
+ "g": 4, "h": 4, "i": 4,
+ "j": 5, "k": 5, "l": 5,
+ "m": 6, "n": 6, "o": 6,
+ "p": 7, "q": 7, "r": 7, "s": 7,
+ "t": 8, "u": 8, "v": 8,
+ "w": 9, "x": 9, "y": 9, "z": 9,
};
// Normalize a number by converting unicode numbers and symbols to their
// ASCII equivalents and removing all non-dialable characters.
function NormalizeNumber(number, numbersOnly) {
- if (typeof number !== 'string') {
- return '';
+ if (typeof number !== "string") {
+ return "";
}
number = number.replace(UNICODE_DIGITS,
- function (ch) {
+ function(ch) {
return String.fromCharCode(48 + (ch.charCodeAt(0) & 0xf));
});
if (!numbersOnly) {
number = number.replace(VALID_ALPHA_PATTERN,
- function (ch) {
+ function(ch) {
return String(E161[ch.toLowerCase()] || 0);
});
}
number = number.replace(LEADING_PLUS_CHARS_PATTERN, "+");
number = number.replace(NON_DIALABLE_CHARS, "");
return number;
- };
+ }
return {
- Normalize: NormalizeNumber
+ Normalize: NormalizeNumber,
};
-})();
\ No newline at end of file
+})();