From fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:14:29 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- .../AutofillProfileAutoComplete.sys.mjs | 2 +- .../formautofill/AutofillTelemetry.sys.mjs | 629 --------------------- toolkit/components/formautofill/Constants.ios.mjs | 3 +- .../formautofill/FormAutofill.ios.sys.mjs | 2 +- .../components/formautofill/FormAutofill.sys.mjs | 4 +- .../formautofill/FormAutofillChild.ios.sys.mjs | 14 +- .../formautofill/FormAutofillChild.sys.mjs | 524 +++++++++++++---- .../formautofill/FormAutofillContent.sys.mjs | 367 +----------- .../components/formautofill/FormAutofillNative.cpp | 20 +- .../formautofill/FormAutofillParent.sys.mjs | 6 +- .../formautofill/FormAutofillStorageBase.sys.mjs | 193 ++----- .../formautofill/FormAutofillSync.sys.mjs | 2 +- toolkit/components/formautofill/Helpers.ios.mjs | 100 +++- toolkit/components/formautofill/Overrides.ios.js | 1 - .../formautofill/ProfileAutoCompleteResult.sys.mjs | 62 +- .../android/FormAutofillPrompter.sys.mjs | 8 +- .../android/FormAutofillStorage.sys.mjs | 10 +- .../default/FormAutofillPrompter.sys.mjs | 6 +- toolkit/components/formautofill/moz.build | 5 + .../phonenumberutils/PhoneNumber.sys.mjs | 474 ---------------- .../phonenumberutils/PhoneNumberMetaData.sys.mjs | 291 ---------- .../phonenumberutils/PhoneNumberNormalizer.sys.mjs | 67 --- .../formautofill/shared/AddressComponent.sys.mjs | 10 +- .../formautofill/shared/AddressParser.sys.mjs | 2 +- .../formautofill/shared/AddressRecord.sys.mjs | 119 ++++ .../formautofill/shared/AutofillTelemetry.sys.mjs | 629 +++++++++++++++++++++ .../formautofill/shared/FieldScanner.sys.mjs | 13 + .../shared/FormAutofillHeuristics.sys.mjs | 72 ++- .../shared/FormAutofillSection.sys.mjs | 24 +- .../formautofill/shared/FormAutofillUtils.sys.mjs | 39 +- .../formautofill/shared/FormStateManager.sys.mjs | 2 +- .../formautofill/shared/PhoneNumber.sys.mjs | 474 ++++++++++++++++ .../shared/PhoneNumberMetaData.sys.mjs | 291 ++++++++++ .../shared/PhoneNumberNormalizer.sys.mjs | 67 +++ 34 files changed, 2344 insertions(+), 2188 deletions(-) delete mode 100644 toolkit/components/formautofill/AutofillTelemetry.sys.mjs delete mode 100644 toolkit/components/formautofill/phonenumberutils/PhoneNumber.sys.mjs delete mode 100644 toolkit/components/formautofill/phonenumberutils/PhoneNumberMetaData.sys.mjs delete mode 100644 toolkit/components/formautofill/phonenumberutils/PhoneNumberNormalizer.sys.mjs create mode 100644 toolkit/components/formautofill/shared/AddressRecord.sys.mjs create mode 100644 toolkit/components/formautofill/shared/AutofillTelemetry.sys.mjs create mode 100644 toolkit/components/formautofill/shared/PhoneNumber.sys.mjs create mode 100644 toolkit/components/formautofill/shared/PhoneNumberMetaData.sys.mjs create mode 100644 toolkit/components/formautofill/shared/PhoneNumberNormalizer.sys.mjs (limited to 'toolkit/components/formautofill') diff --git a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs index 2d87f7931d..fc3f0454b0 100644 --- a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs +++ b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs @@ -349,7 +349,7 @@ export const ProfileAutocomplete = { Services.obs.removeObserver(this, "autocomplete-will-enter-text"); }, - async observe(subject, topic, data) { + async observe(_subject, topic, _data) { switch (topic) { case "autocomplete-will-enter-text": { if (!lazy.FormAutofillContent.activeInput) { diff --git a/toolkit/components/formautofill/AutofillTelemetry.sys.mjs b/toolkit/components/formautofill/AutofillTelemetry.sys.mjs deleted file mode 100644 index 93aa99a4b8..0000000000 --- a/toolkit/components/formautofill/AutofillTelemetry.sys.mjs +++ /dev/null @@ -1,629 +0,0 @@ -/* 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/. */ - -import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs"; -import { FormAutofillCreditCardSection } from "resource://gre/modules/shared/FormAutofillSection.sys.mjs"; - -const { FIELD_STATES } = FormAutofillUtils; - -class AutofillTelemetryBase { - SUPPORTED_FIELDS = {}; - - EVENT_CATEGORY = null; - EVENT_OBJECT_FORM_INTERACTION = null; - - SCALAR_DETECTED_SECTION_COUNT = null; - SCALAR_SUBMITTED_SECTION_COUNT = null; - - HISTOGRAM_NUM_USES = null; - HISTOGRAM_PROFILE_NUM_USES = null; - HISTOGRAM_PROFILE_NUM_USES_KEY = null; - - #initFormEventExtra(value) { - let extra = {}; - for (const field of Object.values(this.SUPPORTED_FIELDS)) { - extra[field] = value; - } - return extra; - } - - #setFormEventExtra(extra, key, value) { - if (!this.SUPPORTED_FIELDS[key]) { - return; - } - - extra[this.SUPPORTED_FIELDS[key]] = value; - } - - /** - * Building the extra keys object that is included in the Legacy Telemetry event `cc_form_v2` - * or `address_form` event and the Glean event `cc_form`, and `address_form`. - * It indicates the detected credit card or address fields and which method (autocomplete property, regular expression heuristics or fathom) identified them. - * - * @param {object} section Using section.fieldDetails to extract which fields were identified and how - * @param {string} undetected Default value when a field is not detected: 'undetected' (Glean) and 'false' in (Legacy) - * @param {string} autocomplete Value when a field is identified with autocomplete property: 'autocomplete' (Glean), 'true' (Legacy) - * @param {string} regexp Value when a field is identified with regex expression heuristics: 'regexp' (Glean), '0' (Legacy) - * @param {boolean} includeMultiPart Include multi part data or not - * @returns {object} Extra keys to include in the form event - */ - #buildFormDetectedEventExtra( - section, - undetected, - autocomplete, - regexp, - includeMultiPart - ) { - let extra = this.#initFormEventExtra(undetected); - - let identified = new Set(); - section.fieldDetails.forEach(detail => { - identified.add(detail.fieldName); - - if (detail.reason == "autocomplete") { - this.#setFormEventExtra(extra, detail.fieldName, autocomplete); - } else { - // confidence exists only when a field is identified by fathom. - let confidence = - detail.confidence > 0 ? Math.floor(100 * detail.confidence) / 100 : 0; - - this.#setFormEventExtra( - extra, - detail.fieldName, - confidence ? confidence.toString() : regexp - ); - } - - if ( - detail.fieldName === "cc-number" && - this.SUPPORTED_FIELDS[detail.fieldName] && - includeMultiPart - ) { - extra.cc_number_multi_parts = detail.part ?? 1; - } - }); - return extra; - } - - recordFormDetected(section) { - this.recordFormEvent( - "detected", - section.flowId, - this.#buildFormDetectedEventExtra(section, "false", "true", "0", false) - ); - - this.recordGleanFormEvent( - "formDetected", - section.flowId, - this.#buildFormDetectedEventExtra( - section, - "undetected", - "autocomplete", - "regexp", - true - ) - ); - } - - recordPopupShown(section, fieldName) { - const extra = { field_name: fieldName }; - this.recordFormEvent("popup_shown", section.flowId, extra); - this.recordGleanFormEvent("formPopupShown", section.flowId, extra); - } - - recordFormFilled(section, profile) { - // Calculate values for telemetry - let extra = this.#initFormEventExtra("unavailable"); - - for (let fieldDetail of section.fieldDetails) { - let element = fieldDetail.element; - let state = profile[fieldDetail.fieldName] ? "filled" : "not_filled"; - if ( - section.handler.getFilledStateByElement(element) == - FIELD_STATES.NORMAL && - (HTMLSelectElement.isInstance(element) || - (HTMLInputElement.isInstance(element) && element.value.length)) - ) { - state = "user_filled"; - } - this.#setFormEventExtra(extra, fieldDetail.fieldName, state); - } - - this.recordFormEvent("filled", section.flowId, extra); - this.recordGleanFormEvent("formFilled", section.flowId, extra); - } - - recordFilledModified(section, fieldName) { - const extra = { field_name: fieldName }; - this.recordFormEvent("filled_modified", section.flowId, extra); - this.recordGleanFormEvent("formFilledModified", section.flowId, extra); - } - - recordFormSubmitted(section, record, form) { - let extra = this.#initFormEventExtra("unavailable"); - - if (record.guid !== null) { - // If the `guid` is not null, it means we're editing an existing record. - // In that case, all fields in the record are autofilled, and fields in - // `untouchedFields` are unmodified. - for (const [fieldName, value] of Object.entries(record.record)) { - if (record.untouchedFields?.includes(fieldName)) { - this.#setFormEventExtra(extra, fieldName, "autofilled"); - } else if (value) { - this.#setFormEventExtra(extra, fieldName, "user_filled"); - } else { - this.#setFormEventExtra(extra, fieldName, "not_filled"); - } - } - } else { - Object.keys(record.record).forEach(fieldName => - this.#setFormEventExtra(extra, fieldName, "user_filled") - ); - } - - this.recordFormEvent("submitted", section.flowId, extra); - this.recordGleanFormEvent("formSubmitted", section.flowId, extra); - } - - recordFormCleared(section, fieldName) { - const extra = { field_name: fieldName }; - - // Note that when a form is cleared, we also record `filled_modified` events - // for all the fields that have been cleared. - this.recordFormEvent("cleared", section.flowId, extra); - this.recordGleanFormEvent("formCleared", section.flowId, extra); - } - - recordFormEvent(method, flowId, extra) { - Services.telemetry.recordEvent( - this.EVENT_CATEGORY, - method, - this.EVENT_OBJECT_FORM_INTERACTION, - flowId, - extra - ); - } - - recordGleanFormEvent(eventName, flowId, extra) { - throw new Error("Not implemented."); - } - - recordFormInteractionEvent( - method, - section, - { fieldName, profile, record, form } = {} - ) { - if (!this.EVENT_OBJECT_FORM_INTERACTION) { - return undefined; - } - switch (method) { - case "detected": - return this.recordFormDetected(section); - case "popup_shown": - return this.recordPopupShown(section, fieldName); - case "filled": - return this.recordFormFilled(section, profile); - case "filled_modified": - return this.recordFilledModified(section, fieldName); - case "submitted": - return this.recordFormSubmitted(section, record, form); - case "cleared": - return this.recordFormCleared(section, fieldName); - } - return undefined; - } - - recordDoorhangerEvent(method, object, flowId) { - Services.telemetry.recordEvent(this.EVENT_CATEGORY, method, object, flowId); - } - - recordManageEvent(method) { - Services.telemetry.recordEvent(this.EVENT_CATEGORY, method, "manage"); - } - - recordAutofillProfileCount(count) { - throw new Error("Not implemented."); - } - - recordDetectedSectionCount() { - if (!this.SCALAR_DETECTED_SECTION_COUNT) { - return; - } - - Services.telemetry.scalarAdd(this.SCALAR_DETECTED_SECTION_COUNT, 1); - } - - recordSubmittedSectionCount(count) { - if (!this.SCALAR_SUBMITTED_SECTION_COUNT || !count) { - return; - } - - Services.telemetry.scalarAdd(this.SCALAR_SUBMITTED_SECTION_COUNT, count); - } - - recordNumberOfUse(records) { - let histogram = Services.telemetry.getKeyedHistogramById( - this.HISTOGRAM_PROFILE_NUM_USES - ); - histogram.clear(); - - for (let record of records) { - histogram.add(this.HISTOGRAM_PROFILE_NUM_USES_KEY, record.timesUsed); - } - } -} - -export class AddressTelemetry extends AutofillTelemetryBase { - EVENT_CATEGORY = "address"; - EVENT_OBJECT_FORM_INTERACTION = "address_form"; - EVENT_OBJECT_FORM_INTERACTION_EXT = "address_form_ext"; - - SCALAR_DETECTED_SECTION_COUNT = - "formautofill.addresses.detected_sections_count"; - SCALAR_SUBMITTED_SECTION_COUNT = - "formautofill.addresses.submitted_sections_count"; - SCALAR_AUTOFILL_PROFILE_COUNT = - "formautofill.addresses.autofill_profiles_count"; - - HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES"; - HISTOGRAM_PROFILE_NUM_USES_KEY = "address"; - - // Fields that are record in `address_form` and `address_form_ext` telemetry - SUPPORTED_FIELDS = { - "street-address": "street_address", - "address-line1": "address_line1", - "address-line2": "address_line2", - "address-line3": "address_line3", - "address-level1": "address_level1", - "address-level2": "address_level2", - "postal-code": "postal_code", - country: "country", - name: "name", - "given-name": "given_name", - "additional-name": "additional_name", - "family-name": "family_name", - email: "email", - organization: "organization", - tel: "tel", - }; - - // Fields that are record in `address_form` event telemetry extra_keys - static SUPPORTED_FIELDS_IN_FORM = [ - "street_address", - "address_line1", - "address_line2", - "address_line3", - "address_level2", - "address_level1", - "postal_code", - "country", - ]; - - // Fields that are record in `address_form_ext` event telemetry extra_keys - static SUPPORTED_FIELDS_IN_FORM_EXT = [ - "name", - "given_name", - "additional_name", - "family_name", - "email", - "organization", - "tel", - ]; - - recordGleanFormEvent(eventName, flowId, extra) { - // To be implemented when migrating the legacy event address.address_form to Glean - } - - recordFormEvent(method, flowId, extra) { - let extExtra = {}; - if (["detected", "filled", "submitted"].includes(method)) { - for (const [key, value] of Object.entries(extra)) { - if (AddressTelemetry.SUPPORTED_FIELDS_IN_FORM_EXT.includes(key)) { - extExtra[key] = value; - delete extra[key]; - } - } - } - - Services.telemetry.recordEvent( - this.EVENT_CATEGORY, - method, - this.EVENT_OBJECT_FORM_INTERACTION, - flowId, - extra - ); - - if (Object.keys(extExtra).length) { - Services.telemetry.recordEvent( - this.EVENT_CATEGORY, - method, - this.EVENT_OBJECT_FORM_INTERACTION_EXT, - flowId, - extExtra - ); - } - } - - recordAutofillProfileCount(count) { - Services.telemetry.scalarSet(this.SCALAR_AUTOFILL_PROFILE_COUNT, count); - } -} - -class CreditCardTelemetry extends AutofillTelemetryBase { - EVENT_CATEGORY = "creditcard"; - EVENT_OBJECT_FORM_INTERACTION = "cc_form_v2"; - - SCALAR_DETECTED_SECTION_COUNT = - "formautofill.creditCards.detected_sections_count"; - SCALAR_SUBMITTED_SECTION_COUNT = - "formautofill.creditCards.submitted_sections_count"; - - HISTOGRAM_NUM_USES = "CREDITCARD_NUM_USES"; - HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES"; - HISTOGRAM_PROFILE_NUM_USES_KEY = "credit_card"; - - // Mapping of field name used in formautofill code to the field name - // used in the telemetry. - SUPPORTED_FIELDS = { - "cc-name": "cc_name", - "cc-number": "cc_number", - "cc-type": "cc_type", - "cc-exp": "cc_exp", - "cc-exp-month": "cc_exp_month", - "cc-exp-year": "cc_exp_year", - }; - - recordLegacyFormEvent(method, flowId, extra = null) { - Services.telemetry.recordEvent( - this.EVENT_CATEGORY, - method, - "cc_form", - flowId, - extra - ); - } - - recordGleanFormEvent(eventName, flowId, extra) { - extra.flow_id = flowId; - Glean.formautofillCreditcards[eventName].record(extra); - } - - recordFormDetected(section) { - super.recordFormDetected(section); - - let identified = new Set(); - section.fieldDetails.forEach(detail => { - identified.add(detail.fieldName); - }); - let extra = { - cc_name_found: identified.has("cc-name") ? "true" : "false", - cc_number_found: identified.has("cc-number") ? "true" : "false", - cc_exp_found: - identified.has("cc-exp") || - (identified.has("cc-exp-month") && identified.has("cc-exp-year")) - ? "true" - : "false", - }; - - this.recordLegacyFormEvent("detected", section.flowId, extra); - } - - recordPopupShown(section, fieldName) { - super.recordPopupShown(section, fieldName); - - this.recordLegacyFormEvent("popup_shown", section.flowId); - } - - recordFormFilled(section, profile) { - super.recordFormFilled(section, profile); - // Calculate values for telemetry - let extra = { - cc_name: "unavailable", - cc_number: "unavailable", - cc_exp: "unavailable", - }; - - for (let fieldDetail of section.fieldDetails) { - let element = fieldDetail.element; - let state = profile[fieldDetail.fieldName] ? "filled" : "not_filled"; - if ( - section.handler.getFilledStateByElement(element) == - FIELD_STATES.NORMAL && - (HTMLSelectElement.isInstance(element) || - (HTMLInputElement.isInstance(element) && element.value.length)) - ) { - state = "user_filled"; - } - switch (fieldDetail.fieldName) { - case "cc-name": - extra.cc_name = state; - break; - case "cc-number": - extra.cc_number = state; - break; - case "cc-exp": - case "cc-exp-month": - case "cc-exp-year": - extra.cc_exp = state; - break; - } - } - - this.recordLegacyFormEvent("filled", section.flowId, extra); - } - - recordFilledModified(section, fieldName) { - super.recordFilledModified(section, fieldName); - - let extra = { field_name: fieldName }; - this.recordLegacyFormEvent("filled_modified", section.flowId, extra); - } - - /** - * Called when a credit card form is submitted - * - * @param {object} section Section that produces this record - * @param {object} record Credit card record filled in the form. - * @param {Array} form Form that contains the section - */ - recordFormSubmitted(section, record, form) { - super.recordFormSubmitted(section, record, form); - - // For legacy cc_form event telemetry - let extra = { - fields_not_auto: "0", - fields_auto: "0", - fields_modified: "0", - }; - - if (record.guid !== null) { - let totalCount = form.elements.length; - let autofilledCount = Object.keys(record.record).length; - let unmodifiedCount = record.untouchedFields.length; - - extra.fields_not_auto = (totalCount - autofilledCount).toString(); - extra.fields_auto = autofilledCount.toString(); - extra.fields_modified = (autofilledCount - unmodifiedCount).toString(); - } else { - // If the `guid` is null, we're filling a new form. - // In that case, all not-null fields are manually filled. - extra.fields_not_auto = Array.from(form.elements) - .filter(element => !!element.value?.trim().length) - .length.toString(); - } - - this.recordLegacyFormEvent("submitted", section.flowId, extra); - } - - recordNumberOfUse(records) { - super.recordNumberOfUse(records); - - if (!this.HISTOGRAM_NUM_USES) { - return; - } - - let histogram = Services.telemetry.getHistogramById( - this.HISTOGRAM_NUM_USES - ); - histogram.clear(); - - for (let record of records) { - histogram.add(record.timesUsed); - } - } - - recordAutofillProfileCount(count) { - Glean.formautofillCreditcards.autofillProfilesCount.set(count); - } -} - -export class AutofillTelemetry { - static #creditCardTelemetry = new CreditCardTelemetry(); - static #addressTelemetry = new AddressTelemetry(); - - // const for `type` parameter used in the utility functions - static ADDRESS = "address"; - static CREDIT_CARD = "creditcard"; - - static #getTelemetryBySection(section) { - return section instanceof FormAutofillCreditCardSection - ? this.#creditCardTelemetry - : this.#addressTelemetry; - } - - static #getTelemetryByType(type) { - return type == AutofillTelemetry.CREDIT_CARD - ? this.#creditCardTelemetry - : this.#addressTelemetry; - } - - /** - * Utility functions for `doorhanger` event (defined in Events.yaml) - * - * Category: address or creditcard - * Event name: doorhanger - */ - static recordDoorhangerShown(type, object, flowId) { - const telemetry = this.#getTelemetryByType(type); - telemetry.recordDoorhangerEvent("show", object, flowId); - } - - static recordDoorhangerClicked(type, method, object, flowId) { - const telemetry = this.#getTelemetryByType(type); - - // We don't have `create` method in telemetry, we treat `create` as `save` - switch (method) { - case "create": - method = "save"; - break; - case "open-pref": - method = "pref"; - break; - case "learn-more": - method = "learn_more"; - break; - } - - telemetry.recordDoorhangerEvent(method, object, flowId); - } - - /** - * Utility functions for form event (defined in Events.yaml) - * - * Category: address or creditcard - * Event name: cc_form, cc_form_v2, or address_form - */ - - static recordFormInteractionEvent( - method, - section, - { fieldName, profile, record, form } = {} - ) { - const telemetry = this.#getTelemetryBySection(section); - telemetry.recordFormInteractionEvent(method, section, { - fieldName, - profile, - record, - form, - }); - } - - /** - * Utility functions for submitted section count scalar (defined in Scalars.yaml) - * - * Category: formautofill.creditCards or formautofill.addresses - * Scalar name: submitted_sections_count - */ - static recordDetectedSectionCount(section) { - const telemetry = this.#getTelemetryBySection(section); - telemetry.recordDetectedSectionCount(); - } - - static recordSubmittedSectionCount(type, count) { - const telemetry = this.#getTelemetryByType(type); - telemetry.recordSubmittedSectionCount(count); - } - - static recordManageEvent(type, method) { - const telemetry = this.#getTelemetryByType(type); - telemetry.recordManageEvent(method); - } - - static recordAutofillProfileCount(type, count) { - const telemetry = this.#getTelemetryByType(type); - telemetry.recordAutofillProfileCount(count); - } - - /** - * Utility functions for address/credit card number of use - */ - static recordNumberOfUse(type, records) { - const telemetry = this.#getTelemetryByType(type); - telemetry.recordNumberOfUse(records); - } - - static recordFormSubmissionHeuristicCount(label) { - Glean.formautofill.formSubmissionHeuristic[label].add(1); - } -} diff --git a/toolkit/components/formautofill/Constants.ios.mjs b/toolkit/components/formautofill/Constants.ios.mjs index b78e47198d..290e690ea6 100644 --- a/toolkit/components/formautofill/Constants.ios.mjs +++ b/toolkit/components/formautofill/Constants.ios.mjs @@ -17,7 +17,7 @@ const IOS_DEFAULT_PREFERENCES = { "browser.search.region": "US", "extensions.formautofill.creditCards.supportedCountries": "US,CA,GB,FR,DE", "extensions.formautofill.addresses.enabled": true, - "extensions.formautofill.addresses.experiments.enabled": false, // TODO(FXCM-765): fetch this value from swift + "extensions.formautofill.addresses.experiments.enabled": true, "extensions.formautofill.addresses.capture.enabled": false, "extensions.formautofill.addresses.supportedCountries": "", "extensions.formautofill.creditCards.enabled": true, @@ -31,6 +31,7 @@ const IOS_DEFAULT_PREFERENCES = { "extensions.formautofill.heuristics.captureOnFormRemoval": false, "extensions.formautofill.heuristics.captureOnPageNavigation": false, "extensions.formautofill.focusOnAutofill": false, + "extensions.formautofill.test.ignoreVisibilityCheck": false, }; // Used Mimic the behavior of .getAutocompleteInfo() diff --git a/toolkit/components/formautofill/FormAutofill.ios.sys.mjs b/toolkit/components/formautofill/FormAutofill.ios.sys.mjs index 8e205c16c6..0b87fee30a 100644 --- a/toolkit/components/formautofill/FormAutofill.ios.sys.mjs +++ b/toolkit/components/formautofill/FormAutofill.ios.sys.mjs @@ -4,7 +4,7 @@ import { FormAutofill } from "resource://autofill/FormAutofill.sys.mjs"; -FormAutofill.defineLogGetter = (scope, logPrefix) => ({ +FormAutofill.defineLogGetter = (_scope, _logPrefix) => ({ // TODO: Bug 1828405. Explore how logging should be handled. // Maybe it makes more sense to do it on swift side and have JS just send messages. info: () => {}, diff --git a/toolkit/components/formautofill/FormAutofill.sys.mjs b/toolkit/components/formautofill/FormAutofill.sys.mjs index 77502afbbe..8f50aad7bd 100644 --- a/toolkit/components/formautofill/FormAutofill.sys.mjs +++ b/toolkit/components/formautofill/FormAutofill.sys.mjs @@ -81,7 +81,9 @@ export const FormAutofill = { return false; }, isAutofillAddressesAvailableInCountry(country) { - return FormAutofill._addressAutofillSupportedCountries.includes(country); + return FormAutofill._addressAutofillSupportedCountries.includes( + country.toUpperCase() + ); }, get isAutofillEnabled() { return this.isAutofillAddressesEnabled || this.isAutofillCreditCardsEnabled; diff --git a/toolkit/components/formautofill/FormAutofillChild.ios.sys.mjs b/toolkit/components/formautofill/FormAutofillChild.ios.sys.mjs index 1aa713b5b7..3183319fd9 100644 --- a/toolkit/components/formautofill/FormAutofillChild.ios.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillChild.ios.sys.mjs @@ -6,6 +6,7 @@ import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs"; import { FormStateManager } from "resource://gre/modules/shared/FormStateManager.sys.mjs"; import { CreditCardRecord } from "resource://gre/modules/shared/CreditCardRecord.sys.mjs"; +import { AddressRecord } from "resource://gre/modules/shared/AddressRecord.sys.mjs"; export class FormAutofillChild { /** @@ -77,7 +78,7 @@ export class FormAutofillChild { this._doIdentifyAutofillFields(element); } - onSubmit(evt) { + onSubmit(_event) { if (!this.fieldDetailsManager.activeHandler) { return; } @@ -100,6 +101,17 @@ export class FormAutofillChild { } fillFormFields(payload) { + // In iOS, we have access only to valid fields (https://github.com/mozilla/application-services/blob/9054db4bb5031881550ceab3448665ef6499a706/components/autofill/src/autofill.udl#L59-L76) for an address; + // all additional data must be computed. On Desktop, computed fields are handled in FormAutofillStorageBase.sys.mjs at the time of saving. Ideally, we should centralize + // all transformations, computations, and normalization processes within AddressRecord.sys.mjs to maintain a unified implementation across both platforms. + // This will be addressed in FXCM-810, aiming to simplify our data representation for both credit cards and addresses. + if ( + FormAutofillUtils.isAddressField( + this.fieldDetailsManager.activeFieldDetail?.fieldName + ) + ) { + AddressRecord.computeFields(payload); + } this.fieldDetailsManager.activeHandler.autofillFormFields(payload); } } diff --git a/toolkit/components/formautofill/FormAutofillChild.sys.mjs b/toolkit/components/formautofill/FormAutofillChild.sys.mjs index c40bfddbce..8678a7bd45 100644 --- a/toolkit/components/formautofill/FormAutofillChild.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillChild.sys.mjs @@ -2,16 +2,36 @@ * 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/. */ +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; + const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { AutoCompleteChild: "resource://gre/actors/AutoCompleteChild.sys.mjs", + AutofillTelemetry: "resource://gre/modules/shared/AutofillTelemetry.sys.mjs", FormAutofill: "resource://autofill/FormAutofill.sys.mjs", FormAutofillContent: "resource://autofill/FormAutofillContent.sys.mjs", FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs", + FormStateManager: "resource://gre/modules/shared/FormStateManager.sys.mjs", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", + ProfileAutocomplete: + "resource://autofill/AutofillProfileAutoComplete.sys.mjs", setTimeout: "resource://gre/modules/Timer.sys.mjs", + FORM_SUBMISSION_REASON: "resource://gre/actors/FormHandlerChild.sys.mjs", }); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "DELEGATE_AUTOCOMPLETE", + "toolkit.autocomplete.delegate", + false +); + +const formFillController = Cc[ + "@mozilla.org/satchel/form-fill-controller;1" +].getService(Ci.nsIFormFillController); + const observer = { QueryInterface: ChromeUtils.generateQI([ "nsIWebProgressListener", @@ -31,7 +51,7 @@ const observer = { formAutofillChild.onPageNavigation(); }, - onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + onStateChange(aWebProgress, aRequest, aStateFlags, _aStatus) { if ( // if restoring a previously-rendered presentation (bfcache) aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING && @@ -77,21 +97,34 @@ export class FormAutofillChild extends JSWindowActorChild { constructor() { super(); + this.log = lazy.FormAutofill.defineLogGetter(this, "FormAutofillChild"); + this.debug("init"); + this._nextHandleElement = null; - this._alreadyDOMContentLoaded = false; this._hasDOMContentLoadedHandler = false; this._hasPendingTask = false; - this.testListener = null; + + // Flag indicating whether the form is waiting to be filled by Autofill. + this._autofillPending = false; + + /** + * @type {FormAutofillFieldDetailsManager} handling state management of current forms and handlers. + */ + this._fieldDetailsManager = new lazy.FormStateManager( + this.formSubmitted.bind(this), + this.formAutofilled.bind(this) + ); lazy.AutoCompleteChild.addPopupStateListener(this); } didDestroy() { + this._fieldDetailsManager.didDestroy(); + lazy.AutoCompleteChild.removePopupStateListener(this); - lazy.FormAutofillContent.didDestroy(); } - popupStateChanged(messageName, data, target) { + popupStateChanged(messageName, data, _target) { let docShell; try { docShell = this.docShell; @@ -108,50 +141,56 @@ export class FormAutofillChild extends JSWindowActorChild { switch (messageName) { case "FormAutoComplete:PopupClosed": { - lazy.FormAutofillContent.onPopupClosed(data.selectedRowStyle); + this.onPopupClosed(data.selectedRowStyle); Services.tm.dispatchToMainThread(() => { - chromeEventHandler.removeEventListener( - "keydown", - lazy.FormAutofillContent._onKeyDown, - true - ); + chromeEventHandler.removeEventListener("keydown", this, true); }); break; } case "FormAutoComplete:PopupOpened": { - lazy.FormAutofillContent.onPopupOpened(); - chromeEventHandler.addEventListener( - "keydown", - lazy.FormAutofillContent._onKeyDown, - true - ); + this.onPopupOpened(); + chromeEventHandler.addEventListener("keydown", this, true); break; } } } /** - * Invokes the FormAutofillContent to identify the autofill fields - * and consider opening the dropdown menu for the focused field - * + * Identifies and marks each autofill field */ - _doIdentifyAutofillFields() { + identifyAutofillFields() { if (this._hasPendingTask) { return; } this._hasPendingTask = true; lazy.setTimeout(() => { - const isAnyFieldIdentified = - lazy.FormAutofillContent.identifyAutofillFields( - this._nextHandleElement - ); - if (isAnyFieldIdentified) { + const element = this._nextHandleElement; + this.debug( + `identifyAutofillFields: ${element.ownerDocument.location?.hostname}` + ); + + if ( + lazy.DELEGATE_AUTOCOMPLETE || + !lazy.FormAutofillContent.savedFieldNames + ) { + this.debug("identifyAutofillFields: savedFieldNames are not known yet"); + + // Init can be asynchronous because we don't need anything from the parent + // at this point. + this.sendAsyncMessage("FormAutofill:InitStorage"); + } + + const validDetails = + this._fieldDetailsManager.identifyAutofillFields(element); + + validDetails?.forEach(detail => + this._markAsAutofillField(detail.element) + ); + if (validDetails.length) { if (lazy.FormAutofill.captureOnFormRemoval) { - this.registerDOMDocFetchSuccessEventListener( - this._nextHandleElement.ownerDocument - ); + this.registerDOMDocFetchSuccessEventListener(); } if (lazy.FormAutofill.captureOnPageNavigation) { this.registerProgressListener(); @@ -163,7 +202,7 @@ export class FormAutofillChild extends JSWindowActorChild { // This is for testing purpose only which sends a notification to indicate that the // form has been identified, and ready to open popup. this.sendAsyncMessage("FormAutofill:FieldsIdentified"); - lazy.FormAutofillContent.updateActiveInput(); + this.updateActiveInput(); }); } @@ -192,13 +231,18 @@ export class FormAutofillChild extends JSWindowActorChild { /** * After being notified of a page navigation, we check whether * the navigated window is the active window or one of its parents - * (active window = FormAutofillContent.activeHandler.window) + * (active window = activeHandler.window) * * @returns {boolean} whether the navigation affects the active window */ isActiveWindowNavigation() { - const activeWindow = lazy.FormAutofillContent.activeHandler.window; + const activeWindow = lazy.FormAutofillContent.activeHandler?.window; const navigatedWindow = this.document.defaultView; + + if (!activeWindow || !navigatedWindow) { + return false; + } + const navigatedBrowsingContext = BrowsingContext.getFromWindow(navigatedWindow); @@ -218,19 +262,23 @@ export class FormAutofillChild extends JSWindowActorChild { * Infer a form submission after document is navigated */ onPageNavigation() { - const activeElement = - lazy.FormAutofillContent.activeFieldDetail?.elementWeakRef.deref(); - if (!this.isActiveWindowNavigation()) { return; } - const formSubmissionReason = - lazy.FormAutofillUtils.FORM_SUBMISSION_REASON.PAGE_NAVIGATION; + // TODO: We should not use FormAutofillContent and let the + // parent decides which child to notify + const activeChild = lazy.FormAutofillContent.activeAutofillChild; + const activeElement = activeChild.activeFieldDetail?.elementWeakRef.deref(); + if (!activeElement) { + return; + } + + const formSubmissionReason = lazy.FORM_SUBMISSION_REASON.PAGE_NAVIGATION; // We only capture the form of the active field right now, // this means that we might miss some fields (see bug 1871356) - lazy.FormAutofillContent.formSubmitted(activeElement, formSubmissionReason); + activeChild.formSubmitted(activeElement, formSubmissionReason); } /** @@ -267,11 +315,9 @@ export class FormAutofillChild extends JSWindowActorChild { /** * After a focusin event and after we identify formautofill fields, * we set up an event listener for the DOMDocFetchSuccess event - * - * @param {Document} document The document we want to be notified by of a DOMDocFetchSuccess event */ - registerDOMDocFetchSuccessEventListener(document) { - document.setNotifyFetchSuccess(true); + registerDOMDocFetchSuccessEventListener() { + this.document.setNotifyFetchSuccess(true); // Is removed after a DOMDocFetchSuccess event (bug 1864855) /* eslint-disable mozilla/balanced-listeners */ @@ -284,11 +330,9 @@ export class FormAutofillChild extends JSWindowActorChild { /** * After a DOMDocFetchSuccess event, we register an event listener for the DOMFormRemoved event - * - * @param {Document} document The document we want to be notified by of a DOMFormRemoved event */ - registerDOMFormRemovedEventListener(document) { - document.setNotifyFormOrPasswordRemoved(true); + registerDOMFormRemovedEventListener() { + this.document.setNotifyFormOrPasswordRemoved(true); // Is removed after a DOMFormRemoved event (bug 1864855) /* eslint-disable mozilla/balanced-listeners */ @@ -301,11 +345,9 @@ export class FormAutofillChild extends JSWindowActorChild { /** * After a DOMDocFetchSuccess event we remove the DOMDocFetchSuccess event listener - * - * @param {Document} document The document we are notified by of a DOMDocFetchSuccess event */ - unregisterDOMDocFetchSuccessEventListener(document) { - document.setNotifyFetchSuccess(false); + unregisterDOMDocFetchSuccessEventListener() { + this.document.setNotifyFetchSuccess(false); this.docShell.chromeEventHandler.removeEventListener( "DOMDocFetchSuccess", this @@ -314,11 +356,9 @@ export class FormAutofillChild extends JSWindowActorChild { /** * After a DOMFormRemoved event we remove the DOMFormRemoved event listener - * - * @param {Document} document The document we are notified by of a DOMFormRemoved event */ - unregisterDOMFormRemovedEventListener(document) { - document.setNotifyFormOrPasswordRemoved(false); + unregisterDOMFormRemovedEventListener() { + this.document.setNotifyFormOrPasswordRemoved(false); this.docShell.chromeEventHandler.removeEventListener( "DOMFormRemoved", this @@ -327,11 +367,7 @@ export class FormAutofillChild extends JSWindowActorChild { shouldIgnoreFormAutofillEvent(event) { let nodePrincipal = event.target.nodePrincipal; - return ( - nodePrincipal.isSystemPrincipal || - nodePrincipal.isNullPrincipal || - nodePrincipal.schemeIs("about") - ); + return nodePrincipal.isSystemPrincipal || nodePrincipal.schemeIs("about"); } handleEvent(evt) { @@ -342,16 +378,20 @@ export class FormAutofillChild extends JSWindowActorChild { return; } + if (!this.windowContext) { + // !this.windowContext must not be null, because we need the + // windowContext and/or docShell to (un)register form submission listeners + return; + } + switch (evt.type) { - case "focusin": { - if (lazy.FormAutofill.isAutofillEnabled) { - this.onFocusIn(evt); - } + case "keydown": { + this._onKeyDown(evt); break; } - case "DOMFormBeforeSubmit": { + case "focusin": { if (lazy.FormAutofill.isAutofillEnabled) { - this.onDOMFormBeforeSubmit(evt); + this.onFocusIn(evt); } break; } @@ -360,7 +400,13 @@ export class FormAutofillChild extends JSWindowActorChild { break; } case "DOMDocFetchSuccess": { - this.onDOMDocFetchSuccess(evt); + this.onDOMDocFetchSuccess(); + break; + } + case "form-submission-detected": { + if (lazy.FormAutofill.isAutofillEnabled) { + this.onFormSubmission(evt); + } break; } @@ -371,45 +417,42 @@ export class FormAutofillChild extends JSWindowActorChild { } onFocusIn(evt) { - lazy.FormAutofillContent.updateActiveInput(); + this.updateActiveInput(); - let element = evt.target; + const element = evt.target; if (!lazy.FormAutofillUtils.isCreditCardOrAddressFieldType(element)) { return; } - this._nextHandleElement = element; - if (!this._alreadyDOMContentLoaded) { - let doc = element.ownerDocument; - if (doc.readyState === "loading") { - if (!this._hasDOMContentLoadedHandler) { - this._hasDOMContentLoadedHandler = true; - doc.addEventListener( - "DOMContentLoaded", - () => this._doIdentifyAutofillFields(), - { once: true } - ); - } - return; + this._nextHandleElement = element; + const doc = element.ownerDocument; + if (doc.readyState === "loading") { + // For auto-focused input, we might receive focus event before document becomes ready. + // When this happens, run field identification after receiving `DOMContentLoaded` event + if (!this._hasDOMContentLoadedHandler) { + this._hasDOMContentLoadedHandler = true; + doc.addEventListener( + "DOMContentLoaded", + () => this.identifyAutofillFields(), + { once: true } + ); } - this._alreadyDOMContentLoaded = true; + return; } - this._doIdentifyAutofillFields(); + this.identifyAutofillFields(); } /** - * Handle the DOMFormBeforeSubmit event. + * Handle form-submission-detected event (dispatched by FormHandlerChild) * - * @param {Event} evt + * @param {CustomEvent} evt form-submission-detected event */ - onDOMFormBeforeSubmit(evt) { - const formElement = evt.target; - - const formSubmissionReason = - lazy.FormAutofillUtils.FORM_SUBMISSION_REASON.FORM_SUBMIT_EVENT; + onFormSubmission(evt) { + const formElement = evt.detail.form; + const formSubmissionReason = evt.detail.reason; - lazy.FormAutofillContent.formSubmitted(formElement, formSubmissionReason); + this.formSubmitted(formElement, formSubmissionReason); } /** @@ -421,14 +464,10 @@ export class FormAutofillChild extends JSWindowActorChild { * @param {Event} evt DOMFormRemoved */ onDOMFormRemoved(evt) { - const document = evt.composedTarget.ownerDocument; - const formSubmissionReason = - lazy.FormAutofillUtils.FORM_SUBMISSION_REASON.FORM_REMOVAL_AFTER_FETCH; - - lazy.FormAutofillContent.formSubmitted(evt.target, formSubmissionReason); + lazy.FORM_SUBMISSION_REASON.FORM_REMOVAL_AFTER_FETCH; - this.unregisterDOMFormRemovedEventListener(document); + this.formSubmitted(evt.target, formSubmissionReason); } /** @@ -436,15 +475,21 @@ export class FormAutofillChild extends JSWindowActorChild { * * Sets up an event listener for the DOMFormRemoved event * and unregisters the event listener for DOMDocFetchSuccess event. - * - * @param {Event} evt DOMDocFetchSuccess */ - onDOMDocFetchSuccess(evt) { - const document = evt.target; + onDOMDocFetchSuccess() { + this.registerDOMFormRemovedEventListener(); - this.registerDOMFormRemovedEventListener(document); + this.unregisterDOMDocFetchSuccessEventListener(); + } - this.unregisterDOMDocFetchSuccessEventListener(document); + /** + * Unregister all listeners that notify of a form submission, + * because we just detected and acted on a form submission + */ + unregisterFormSubmissionListeners() { + this.unregisterDOMDocFetchSuccessEventListener(); + this.unregisterDOMFormRemovedEventListener(); + this.unregisterProgressListener(); } receiveMessage(message) { @@ -456,17 +501,284 @@ export class FormAutofillChild extends JSWindowActorChild { switch (message.name) { case "FormAutofill:PreviewProfile": { - lazy.FormAutofillContent.previewProfile(doc); + this.previewProfile(doc); break; } case "FormAutofill:ClearForm": { - lazy.FormAutofillContent.clearForm(); + this.clearForm(); break; } case "FormAutofill:FillForm": { - lazy.FormAutofillContent.activeHandler.autofillFormFields(message.data); + this.activeHandler.autofillFormFields(message.data); break; } } } + + get activeFieldDetail() { + return this._fieldDetailsManager.activeFieldDetail; + } + + get activeFormDetails() { + return this._fieldDetailsManager.activeFormDetails; + } + + get activeInput() { + return this._fieldDetailsManager.activeInput; + } + + get activeHandler() { + return this._fieldDetailsManager.activeHandler; + } + + get activeSection() { + return this._fieldDetailsManager.activeSection; + } + + /** + * Handle a form submission and early return when: + * 1. In private browsing mode. + * 2. Could not map any autofill handler by form element. + * 3. Number of filled fields is less than autofill threshold + * + * @param {HTMLElement} formElement Root element which receives submit event. + * @param {string} formSubmissionReason Reason for invoking the form submission + * (see options for FORM_SUBMISSION_REASON in FormAutofillUtils)) + * @param {Window} domWin Content window; passed for unit tests and when + * invoked by the FormAutofillSection + * @param {object} handler FormAutofillHander, if known by caller + */ + formSubmitted( + formElement, + formSubmissionReason, + domWin = formElement.ownerGlobal, + handler = undefined + ) { + this.debug(`Handling form submission - infered by ${formSubmissionReason}`); + + lazy.AutofillTelemetry.recordFormSubmissionHeuristicCount( + formSubmissionReason + ); + + if (!lazy.FormAutofill.isAutofillEnabled) { + this.debug("Form Autofill is disabled"); + return; + } + + // The `domWin` truthiness test is used by unit tests to bypass this check. + if (domWin && lazy.PrivateBrowsingUtils.isContentWindowPrivate(domWin)) { + this.debug("Ignoring submission in a private window"); + return; + } + + handler = handler || this._fieldDetailsManager._getFormHandler(formElement); + const records = this._fieldDetailsManager.getRecords(formElement, handler); + + if (!records || !handler) { + this.debug("Form element could not map to an existing handler"); + return; + } + + // Unregister the form submission listeners after handling a form submission + this.debug("Unregistering form submission listeners"); + this.unregisterFormSubmissionListeners(); + + [records.address, records.creditCard].forEach((rs, idx) => { + lazy.AutofillTelemetry.recordSubmittedSectionCount( + idx == 0 + ? lazy.AutofillTelemetry.ADDRESS + : lazy.AutofillTelemetry.CREDIT_CARD, + rs?.length + ); + + rs?.forEach(r => { + lazy.AutofillTelemetry.recordFormInteractionEvent( + "submitted", + r.section, + { + record: r, + form: handler.form, + } + ); + delete r.section; + }); + }); + + this.sendAsyncMessage("FormAutofill:OnFormSubmit", records); + } + + formAutofilled() { + lazy.FormAutofillContent.showPopup(); + } + + /** + * All active items should be updated according the active element of + * `formFillController.focusedInput`. All of them including element, + * handler, section, and field detail, can be retrieved by their own getters. + * + * @param {HTMLElement|null} element The active item should be updated based + * on this or `formFillController.focusedInput` will be taken. + */ + updateActiveInput(element) { + element = element || formFillController.focusedInput; + if (!element) { + this.debug("updateActiveElement: no element selected"); + return; + } + lazy.FormAutofillContent.updateActiveAutofillChild(this); + + this._fieldDetailsManager.updateActiveInput(element); + this.debug("updateActiveElement: checking for popup-on-focus"); + // We know this element just received focus. If it's a credit card field, + // open its popup. + if (this._autofillPending) { + this.debug("updateActiveElement: skipping check; autofill is imminent"); + } else if (element.value?.length !== 0) { + this.debug( + `updateActiveElement: Not opening popup because field is not empty.` + ); + } else { + this.debug( + "updateActiveElement: checking if empty field is cc-*: ", + this.activeFieldDetail?.fieldName + ); + + if ( + this.activeFieldDetail?.fieldName?.startsWith("cc-") || + AppConstants.platform === "android" + ) { + lazy.FormAutofillContent.showPopup(); + } + } + } + + set autofillPending(flag) { + this.debug("Setting autofillPending to", flag); + this._autofillPending = flag; + } + + clearForm() { + let focusedInput = + this.activeInput || + lazy.ProfileAutocomplete._lastAutoCompleteFocusedInput; + if (!focusedInput) { + return; + } + + this.activeSection.clearPopulatedForm(); + + let fieldName = this.activeFieldDetail?.fieldName; + if (lazy.FormAutofillUtils.isCreditCardField(fieldName)) { + lazy.AutofillTelemetry.recordFormInteractionEvent( + "cleared", + this.activeSection, + { fieldName } + ); + } + } + + previewProfile(doc) { + let docWin = doc.ownerGlobal; + let selectedIndex = lazy.ProfileAutocomplete._getSelectedIndex(docWin); + let lastAutoCompleteResult = + lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; + let focusedInput = this.activeInput; + + if ( + selectedIndex === -1 || + !focusedInput || + !lastAutoCompleteResult || + lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile" + ) { + this.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {}); + + lazy.ProfileAutocomplete._clearProfilePreview(); + } else { + let focusedInputDetails = this.activeFieldDetail; + let profile = JSON.parse( + lastAutoCompleteResult.getCommentAt(selectedIndex) + ); + let allFieldNames = this.activeSection.allFieldNames; + let profileFields = allFieldNames.filter( + fieldName => !!profile[fieldName] + ); + + let focusedCategory = lazy.FormAutofillUtils.getCategoryFromFieldName( + focusedInputDetails.fieldName + ); + let categories = + lazy.FormAutofillUtils.getCategoriesFromFieldNames(profileFields); + this.sendAsyncMessage("FormAutofill:UpdateWarningMessage", { + focusedCategory, + categories, + }); + + lazy.ProfileAutocomplete._previewSelectedProfile(selectedIndex); + } + } + + onPopupClosed(selectedRowStyle) { + this.debug("Popup has closed."); + lazy.ProfileAutocomplete._clearProfilePreview(); + + let lastAutoCompleteResult = + lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; + let focusedInput = this.activeInput; + if ( + lastAutoCompleteResult && + this._keyDownEnterForInput && + focusedInput === this._keyDownEnterForInput && + focusedInput === + lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput + ) { + if (selectedRowStyle == "autofill-footer") { + this.sendAsyncMessage("FormAutofill:OpenPreferences"); + } else if (selectedRowStyle == "autofill-clear-button") { + this.clearForm(); + } + } + } + + onPopupOpened() { + this.debug( + "Popup has opened, automatic =", + formFillController.passwordPopupAutomaticallyOpened + ); + + let fieldName = this.activeFieldDetail?.fieldName; + if (fieldName && this.activeSection) { + lazy.AutofillTelemetry.recordFormInteractionEvent( + "popup_shown", + this.activeSection, + { fieldName } + ); + } + } + + _markAsAutofillField(field) { + // Since Form Autofill popup is only for input element, any non-Input + // element should be excluded here. + if (!HTMLInputElement.isInstance(field)) { + return; + } + + formFillController.markAsAutofillField(field); + } + + _onKeyDown(e) { + delete this._keyDownEnterForInput; + let lastAutoCompleteResult = + lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; + let focusedInput = this.activeInput; + if ( + e.keyCode != e.DOM_VK_RETURN || + !lastAutoCompleteResult || + !focusedInput || + focusedInput != + lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput + ) { + return; + } + this._keyDownEnterForInput = focusedInput; + } } diff --git a/toolkit/components/formautofill/FormAutofillContent.sys.mjs b/toolkit/components/formautofill/FormAutofillContent.sys.mjs index 133e5e1d0a..c07e0d67b3 100644 --- a/toolkit/components/formautofill/FormAutofillContent.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillContent.sys.mjs @@ -8,43 +8,18 @@ /* eslint-disable no-use-before-define */ -import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; -import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; - const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { FormAutofill: "resource://autofill/FormAutofill.sys.mjs", - FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs", - PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", - FormStateManager: "resource://gre/modules/shared/FormStateManager.sys.mjs", ProfileAutocomplete: "resource://autofill/AutofillProfileAutoComplete.sys.mjs", - AutofillTelemetry: "resource://autofill/AutofillTelemetry.sys.mjs", }); -XPCOMUtils.defineLazyPreferenceGetter( - lazy, - "DELEGATE_AUTOCOMPLETE", - "toolkit.autocomplete.delegate", - false -); - const formFillController = Cc[ "@mozilla.org/satchel/form-fill-controller;1" ].getService(Ci.nsIFormFillController); -function getActorFromWindow(contentWindow, name = "FormAutofill") { - // In unit tests, contentWindow isn't a real window. - if (!contentWindow) { - return null; - } - - return contentWindow.windowGlobalChild - ? contentWindow.windowGlobalChild.getActor(name) - : null; -} - /** * Handles content's interactions for the process. * @@ -63,12 +38,6 @@ export var FormAutofillContent = { */ _popupPending: false, - /** - * @type {boolean} Flag indicating whether the form is waiting to be - * filled by Autofill. - */ - _autofillPending: false, - init() { this.log = lazy.FormAutofill.defineLogGetter(this, "FormAutofillContent"); this.debug("init"); @@ -76,11 +45,13 @@ export var FormAutofillContent = { // eslint-disable-next-line mozilla/balanced-listeners Services.cpmm.sharedData.addEventListener("change", this); - let autofillEnabled = Services.cpmm.sharedData.get("FormAutofill:enabled"); + const autofillEnabled = Services.cpmm.sharedData.get( + "FormAutofill:enabled" + ); // If storage hasn't be initialized yet autofillEnabled is undefined but we need to ensure // autocomplete is registered before the focusin so register it in this case as long as the // pref is true. - let shouldEnableAutofill = + const shouldEnableAutofill = autofillEnabled === undefined && (lazy.FormAutofill.isAutofillAddressesEnabled || lazy.FormAutofill.isAutofillCreditCardsEnabled); @@ -88,120 +59,49 @@ export var FormAutofillContent = { lazy.ProfileAutocomplete.ensureRegistered(); } - /** - * @type {FormAutofillFieldDetailsManager} handling state management of current forms and handlers. - */ - this._fieldDetailsManager = new lazy.FormStateManager( - this.formSubmitted.bind(this), - this._showPopup.bind(this) - ); + this.activeAutofillChild = null; }, get activeFieldDetail() { - return this._fieldDetailsManager.activeFieldDetail; + return this.activeAutofillChild?.activeFieldDetail; }, get activeFormDetails() { - return this._fieldDetailsManager.activeFormDetails; + return this.activeAutofillChild?.activeFormDetails; }, get activeInput() { - return this._fieldDetailsManager.activeInput; + return this.activeAutofillChild?.activeInput; }, get activeHandler() { - return this._fieldDetailsManager.activeHandler; + return this.activeAutofillChild?.activeHandler; }, get activeSection() { - return this._fieldDetailsManager.activeSection; - }, - - /** - * Send the profile to parent for doorhanger and storage saving/updating. - * - * @param {object} profile Submitted form's address/creditcard guid and record. - * @param {object} domWin Current content window. - */ - _onFormSubmit(profile, domWin) { - let actor = getActorFromWindow(domWin); - actor.sendAsyncMessage("FormAutofill:OnFormSubmit", profile); + return this.activeAutofillChild?.activeSection; }, - /** - * Handle a form submission and early return when: - * 1. In private browsing mode. - * 2. Could not map any autofill handler by form element. - * 3. Number of filled fields is less than autofill threshold - * - * @param {HTMLElement} formElement Root element which receives submit event. - * @param {string} formSubmissionReason Reason for invoking the form submission - * (see options for FORM_SUBMISSION_REASON in FormAutofillUtils)) - * @param {Window} domWin Content window; passed for unit tests and when - * invoked by the FormAutofillSection - * @param {object} handler FormAutofillHander, if known by caller - */ - formSubmitted( - formElement, - formSubmissionReason, - domWin = formElement.ownerGlobal, - handler = undefined - ) { - this.debug(`Handling form submission - infered by ${formSubmissionReason}`); - - // Unregister the progress listener since we detected a form submission - // (domWin is null in unit tests) - getActorFromWindow(domWin)?.unregisterProgressListener(); - - lazy.AutofillTelemetry.recordFormSubmissionHeuristicCount( - formSubmissionReason - ); - - if (!lazy.FormAutofill.isAutofillEnabled) { - this.debug("Form Autofill is disabled"); - return; - } - - // The `domWin` truthiness test is used by unit tests to bypass this check. - if (domWin && lazy.PrivateBrowsingUtils.isContentWindowPrivate(domWin)) { - this.debug("Ignoring submission in a private window"); - return; - } - - handler = handler || this._fieldDetailsManager._getFormHandler(formElement); - const records = this._fieldDetailsManager.getRecords(formElement, handler); - - if (!records || !handler) { - this.debug("Form element could not map to an existing handler"); - return; + set autofillPending(flag) { + if (this.activeAutofillChild) { + this.activeAutofillChild.autofillPending = flag; } + }, - [records.address, records.creditCard].forEach((rs, idx) => { - lazy.AutofillTelemetry.recordSubmittedSectionCount( - idx == 0 - ? lazy.AutofillTelemetry.ADDRESS - : lazy.AutofillTelemetry.CREDIT_CARD, - rs?.length - ); - - rs?.forEach(r => { - lazy.AutofillTelemetry.recordFormInteractionEvent( - "submitted", - r.section, - { - record: r, - form: handler.form, - } - ); - delete r.section; - }); - }); - - this._onFormSubmit(records, domWin); + updateActiveAutofillChild(autofillChild) { + this.activeAutofillChild = autofillChild; }, - _showPopup() { - formFillController.showPopup(); + showPopup() { + if (Services.cpmm.sharedData.get("FormAutofill:enabled")) { + this.debug("updateActiveElement: opening pop up"); + formFillController.showPopup(); + } else { + this.debug( + "updateActiveElement: Deferring pop-up until Autofill is ready" + ); + this._popupPending = true; + } }, handleEvent(evt) { @@ -215,7 +115,7 @@ export var FormAutofillContent = { if (this._popupPending) { this._popupPending = false; this.debug("handleEvent: Opening deferred popup"); - this._showPopup(); + formFillController.showPopup(); } } else { lazy.ProfileAutocomplete.ensureUnregistered(); @@ -224,219 +124,6 @@ export var FormAutofillContent = { } } }, - - /** - * All active items should be updated according the active element of - * `formFillController.focusedInput`. All of them including element, - * handler, section, and field detail, can be retrieved by their own getters. - * - * @param {HTMLElement|null} element The active item should be updated based - * on this or `formFillController.focusedInput` will be taken. - */ - updateActiveInput(element) { - element = element || formFillController.focusedInput; - if (!element) { - this.debug("updateActiveElement: no element selected"); - return; - } - this._fieldDetailsManager.updateActiveInput(element); - this.debug("updateActiveElement: checking for popup-on-focus"); - // We know this element just received focus. If it's a credit card field, - // open its popup. - if (this._autofillPending) { - this.debug("updateActiveElement: skipping check; autofill is imminent"); - } else if (element.value?.length !== 0) { - this.debug( - `updateActiveElement: Not opening popup because field is not empty.` - ); - } else { - this.debug( - "updateActiveElement: checking if empty field is cc-*: ", - this.activeFieldDetail?.fieldName - ); - - if ( - this.activeFieldDetail?.fieldName?.startsWith("cc-") || - AppConstants.platform === "android" - ) { - if (Services.cpmm.sharedData.get("FormAutofill:enabled")) { - this.debug("updateActiveElement: opening pop up"); - this._showPopup(); - } else { - this.debug( - "updateActiveElement: Deferring pop-up until Autofill is ready" - ); - this._popupPending = true; - } - } - } - }, - - set autofillPending(flag) { - this.debug("Setting autofillPending to", flag); - this._autofillPending = flag; - }, - - /** - * Identifies and marks each autofill field - * - * @param {HTMLElement} element - * Element that serves as an anchor for the formautofill heuristics to retrieve - * the root form and run the formautofill heuristics on the form elements - * @returns {boolean} - * whether any autofill fields were identified - */ - identifyAutofillFields(element) { - this.debug( - `identifyAutofillFields: ${element.ownerDocument.location?.hostname}` - ); - - if (lazy.DELEGATE_AUTOCOMPLETE || !this.savedFieldNames) { - this.debug("identifyAutofillFields: savedFieldNames are not known yet"); - let actor = getActorFromWindow(element.ownerGlobal); - if (actor) { - actor.sendAsyncMessage("FormAutofill:InitStorage"); - } - } - - const validDetails = - this._fieldDetailsManager.identifyAutofillFields(element); - - validDetails?.forEach(detail => this._markAsAutofillField(detail.element)); - - return !!validDetails.length; - }, - - clearForm() { - let focusedInput = - this.activeInput || - lazy.ProfileAutocomplete._lastAutoCompleteFocusedInput; - if (!focusedInput) { - return; - } - - this.activeSection.clearPopulatedForm(); - - let fieldName = FormAutofillContent.activeFieldDetail?.fieldName; - if (lazy.FormAutofillUtils.isCreditCardField(fieldName)) { - lazy.AutofillTelemetry.recordFormInteractionEvent( - "cleared", - this.activeSection, - { fieldName } - ); - } - }, - - previewProfile(doc) { - let docWin = doc.ownerGlobal; - let selectedIndex = lazy.ProfileAutocomplete._getSelectedIndex(docWin); - let lastAutoCompleteResult = - lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; - let focusedInput = this.activeInput; - let actor = getActorFromWindow(docWin); - - if ( - selectedIndex === -1 || - !focusedInput || - !lastAutoCompleteResult || - lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile" - ) { - actor.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {}); - - lazy.ProfileAutocomplete._clearProfilePreview(); - } else { - let focusedInputDetails = this.activeFieldDetail; - let profile = JSON.parse( - lastAutoCompleteResult.getCommentAt(selectedIndex) - ); - let allFieldNames = FormAutofillContent.activeSection.allFieldNames; - let profileFields = allFieldNames.filter( - fieldName => !!profile[fieldName] - ); - - let focusedCategory = lazy.FormAutofillUtils.getCategoryFromFieldName( - focusedInputDetails.fieldName - ); - let categories = - lazy.FormAutofillUtils.getCategoriesFromFieldNames(profileFields); - actor.sendAsyncMessage("FormAutofill:UpdateWarningMessage", { - focusedCategory, - categories, - }); - - lazy.ProfileAutocomplete._previewSelectedProfile(selectedIndex); - } - }, - - onPopupClosed(selectedRowStyle) { - this.debug("Popup has closed."); - lazy.ProfileAutocomplete._clearProfilePreview(); - - let lastAutoCompleteResult = - lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; - let focusedInput = FormAutofillContent.activeInput; - if ( - lastAutoCompleteResult && - FormAutofillContent._keyDownEnterForInput && - focusedInput === FormAutofillContent._keyDownEnterForInput && - focusedInput === - lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput - ) { - if (selectedRowStyle == "autofill-footer") { - let actor = getActorFromWindow(focusedInput.ownerGlobal); - actor.sendAsyncMessage("FormAutofill:OpenPreferences"); - } else if (selectedRowStyle == "autofill-clear-button") { - FormAutofillContent.clearForm(); - } - } - }, - - onPopupOpened() { - this.debug( - "Popup has opened, automatic =", - formFillController.passwordPopupAutomaticallyOpened - ); - - let fieldName = FormAutofillContent.activeFieldDetail?.fieldName; - if (fieldName && this.activeSection) { - lazy.AutofillTelemetry.recordFormInteractionEvent( - "popup_shown", - this.activeSection, - { fieldName } - ); - } - }, - - _markAsAutofillField(field) { - // Since Form Autofill popup is only for input element, any non-Input - // element should be excluded here. - if (!HTMLInputElement.isInstance(field)) { - return; - } - - formFillController.markAsAutofillField(field); - }, - - _onKeyDown(e) { - delete FormAutofillContent._keyDownEnterForInput; - let lastAutoCompleteResult = - lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; - let focusedInput = FormAutofillContent.activeInput; - if ( - e.keyCode != e.DOM_VK_RETURN || - !lastAutoCompleteResult || - !focusedInput || - focusedInput != - lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput - ) { - return; - } - FormAutofillContent._keyDownEnterForInput = focusedInput; - }, - - didDestroy() { - this._fieldDetailsManager.didDestroy(); - }, }; FormAutofillContent.init(); diff --git a/toolkit/components/formautofill/FormAutofillNative.cpp b/toolkit/components/formautofill/FormAutofillNative.cpp index 57af789861..08c462aa44 100644 --- a/toolkit/components/formautofill/FormAutofillNative.cpp +++ b/toolkit/components/formautofill/FormAutofillNative.cpp @@ -200,14 +200,16 @@ enum class CCExpYearParams : uint8_t { }; struct AutofillParams { - EnumeratedArray + EnumeratedArray mCCNumberParams; - EnumeratedArray mCCNameParams; - EnumeratedArray mCCTypeParams; - EnumeratedArray mCCExpParams; - EnumeratedArray + EnumeratedArray + mCCNameParams; + EnumeratedArray + mCCTypeParams; + EnumeratedArray mCCExpParams; + EnumeratedArray mCCExpMonthParams; - EnumeratedArray + EnumeratedArray mCCExpYearParams; }; @@ -667,13 +669,11 @@ class FormAutofillImpl { // Array contains regular expressions to match the corresponding // field. Ex, CC number, CC type, etc. using RegexStringArray = - EnumeratedArray; + EnumeratedArray; RegexStringArray mRuleMap; // Array that holds RegexWrapper that created by regex::ffi::regex_new - using RegexWrapperArray = - EnumeratedArray; + using RegexWrapperArray = EnumeratedArray; RegexWrapperArray mRegexes; }; diff --git a/toolkit/components/formautofill/FormAutofillParent.sys.mjs b/toolkit/components/formautofill/FormAutofillParent.sys.mjs index ba0d769906..61c4bd2943 100644 --- a/toolkit/components/formautofill/FormAutofillParent.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillParent.sys.mjs @@ -5,10 +5,10 @@ /* * Implements a service used to access storage and communicate with content. * - * A "fields" array is used to communicate with FormAutofillContent. Each item + * A "fields" array is used to communicate with FormAutofillChild. Each item * represents a single input field in the content page as well as its * @autocomplete properties. The schema is as below. Please refer to - * FormAutofillContent.js for more details. + * FormAutofillChild.js for more details. * * [ * { @@ -293,7 +293,7 @@ export class FormAutofillParent extends JSWindowActorParent { } /** - * Handles the message coming from FormAutofillContent. + * Handles the message coming from FormAutofillChild. * * @param {object} message * @param {string} message.name The name of the message. diff --git a/toolkit/components/formautofill/FormAutofillStorageBase.sys.mjs b/toolkit/components/formautofill/FormAutofillStorageBase.sys.mjs index 591bfc1578..f360be4fa6 100644 --- a/toolkit/components/formautofill/FormAutofillStorageBase.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillStorageBase.sys.mjs @@ -130,18 +130,19 @@ */ import { FormAutofill } from "resource://autofill/FormAutofill.sys.mjs"; +import { AddressRecord } from "resource://gre/modules/shared/AddressRecord.sys.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { - AutofillTelemetry: "resource://autofill/AutofillTelemetry.sys.mjs", + AutofillTelemetry: "resource://gre/modules/shared/AutofillTelemetry.sys.mjs", CreditCard: "resource://gre/modules/CreditCard.sys.mjs", CreditCardRecord: "resource://gre/modules/shared/CreditCardRecord.sys.mjs", FormAutofillNameUtils: "resource://gre/modules/shared/FormAutofillNameUtils.sys.mjs", FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs", OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs", - PhoneNumber: "resource://autofill/phonenumberutils/PhoneNumber.sys.mjs", + PhoneNumber: "resource://gre/modules/shared/PhoneNumber.sys.mjs", }); const CryptoHash = Components.Constructor( @@ -166,23 +167,6 @@ export const ADDRESS_SCHEMA_VERSION = 1; // Please talk to the sync team before changing this! export const CREDIT_CARD_SCHEMA_VERSION = 3; -const NAME_COMPONENTS = ["given-name", "additional-name", "family-name"]; - -const STREET_ADDRESS_COMPONENTS = [ - "address-line1", - "address-line2", - "address-line3", -]; - -const TEL_COMPONENTS = [ - "tel-country-code", - "tel-national", - "tel-area-code", - "tel-local", - "tel-local-prefix", - "tel-local-suffix", -]; - const VALID_ADDRESS_FIELDS = [ "name", "organization", @@ -198,9 +182,9 @@ const VALID_ADDRESS_FIELDS = [ const VALID_ADDRESS_COMPUTED_FIELDS = [ "country-name", - ...NAME_COMPONENTS, - ...STREET_ADDRESS_COMPONENTS, - ...TEL_COMPONENTS, + ...AddressRecord.NAME_COMPONENTS, + ...AddressRecord.STREET_ADDRESS_COMPONENTS, + ...AddressRecord.TEL_COMPONENTS, ]; const VALID_CREDIT_CARD_FIELDS = [ @@ -299,20 +283,18 @@ class AutofillRecords { }); } - observe(subject, topic, data) { - switch (topic) { - case "formautofill-storage-changed": - let collectionName = subject.wrappedJSObject.collectionName; - if (collectionName != this._collectionName) { - return; - } - const telemetryType = - subject.wrappedJSObject.collectionName == "creditCards" - ? lazy.AutofillTelemetry.CREDIT_CARD - : lazy.AutofillTelemetry.ADDRESS; - const count = this._data.filter(entry => !entry.deleted).length; - lazy.AutofillTelemetry.recordAutofillProfileCount(telemetryType, count); - break; + observe(subject, topic, _data) { + if (topic == "formautofill-storage-changed") { + let collectionName = subject.wrappedJSObject.collectionName; + if (collectionName != this._collectionName) { + return; + } + const telemetryType = + subject.wrappedJSObject.collectionName == "creditCards" + ? lazy.AutofillTelemetry.CREDIT_CARD + : lazy.AutofillTelemetry.ADDRESS; + const count = this._data.filter(entry => !entry.deleted).length; + lazy.AutofillTelemetry.recordAutofillProfileCount(telemetryType, count); } } @@ -675,7 +657,7 @@ class AutofillRecords { // Excluding *-name fields from the sync payload would prevent older devices from // synchronizing with newer devices. To maintain backward compatibility, keep those deprecated // ields in the payload, ensuring that older devices can still sync with newer devices. - const fieldsToKeep = NAME_COMPONENTS; + const fieldsToKeep = AddressRecord.NAME_COMPONENTS; await this._stripComputedFields(clonedRecord, fieldsToKeep); } else { this._recordReadProcessor(clonedRecord); @@ -703,7 +685,7 @@ class AutofillRecords { await Promise.all( clonedRecords.map(async record => { if (rawData) { - const fieldsToKeep = NAME_COMPONENTS; + const fieldsToKeep = AddressRecord.NAME_COMPONENTS; await this._stripComputedFields(record, fieldsToKeep); } else { this._recordReadProcessor(record); @@ -1398,7 +1380,12 @@ class AutofillRecords { return hasChanges; } - hasChanges |= await this.computeFields(record); + const originalNumFields = Object.keys(record).length; + await this.computeFields(record); + const hasNewComputedFields = + Object.keys(record).length != originalNumFields; + + hasChanges |= hasNewComputedFields; return hasChanges; } @@ -1486,36 +1473,36 @@ class AutofillRecords { } // An interface to be inherited. - _recordReadProcessor(record) {} + _recordReadProcessor(_record) {} // An interface to be inherited. - async computeFields(record) {} + async computeFields(_record) {} /** * An interface to be inherited to mutate the argument to normalize it. * - * @param {object} partialRecord containing the record passed by the consumer of + * @param {object} _partialRecord containing the record passed by the consumer of * storage and in the case of `update` with * `preserveOldProperties` will only include the * properties that the user is changing so the * lack of a field doesn't mean that the record * won't have that field. */ - _normalizeFields(partialRecord) {} + _normalizeFields(_partialRecord) {} /** * An interface to be inherited to validate that the complete record is * consistent and isn't missing required fields. Overrides should throw for * invalid records. * - * @param {object} record containing the complete record that would be stored + * @param {object} _record containing the complete record that would be stored * if this doesn't throw due to an error. * @throws */ - _validateFields(record) {} + _validateFields(_record) {} // An interface to be inherited. - migrateRemoteRecord(remoteRecord) {} + migrateRemoteRecord(_remoteRecord) {} } export class AddressesBase extends AutofillRecords { @@ -1578,99 +1565,9 @@ export class AddressesBase extends AutofillRecords { // NOTE: Computed fields should be always present in the storage no matter // it's empty or not. - let hasNewComputedFields = false; - - if (address.deleted) { - return hasNewComputedFields; - } - - // Compute split names - if (!("given-name" in address)) { - const nameParts = lazy.FormAutofillNameUtils.splitName(address.name); - address["given-name"] = nameParts.given; - address["additional-name"] = nameParts.middle; - address["family-name"] = nameParts.family; - hasNewComputedFields = true; - } - - // Compute address lines - if (!("address-line1" in address)) { - let streetAddress = []; - if (address["street-address"]) { - streetAddress = address["street-address"] - .split("\n") - .map(s => s.trim()); - } - for (let i = 0; i < 3; i++) { - address[`address-line${i + 1}`] = streetAddress[i] || ""; - } - if (streetAddress.length > 3) { - address["address-line3"] = lazy.FormAutofillUtils.toOneLineAddress( - streetAddress.slice(2) - ); - } - hasNewComputedFields = true; - } - - // Compute country name - if (!("country-name" in address)) { - if (address.country) { - try { - address["country-name"] = Services.intl.getRegionDisplayNames( - undefined, - [address.country] - ); - } catch (e) { - address["country-name"] = ""; - } - } else { - address["country-name"] = ""; - } - hasNewComputedFields = true; + if (!address.deleted) { + AddressRecord.computeFields(address); } - - // Compute tel - if (!("tel-national" in address)) { - if (address.tel) { - let tel = lazy.PhoneNumber.Parse( - address.tel, - address.country || FormAutofill.DEFAULT_REGION - ); - if (tel) { - if (tel.countryCode) { - address["tel-country-code"] = tel.countryCode; - } - if (tel.nationalNumber) { - address["tel-national"] = tel.nationalNumber; - } - - // PhoneNumberUtils doesn't support parsing the components of a telephone - // number so we hard coded the parser for US numbers only. We will need - // to figure out how to parse numbers from other regions when we support - // new countries in the future. - if (tel.nationalNumber && tel.countryCode == "+1") { - let telComponents = tel.nationalNumber.match( - /(\d{3})((\d{3})(\d{4}))$/ - ); - if (telComponents) { - address["tel-area-code"] = telComponents[1]; - address["tel-local"] = telComponents[2]; - address["tel-local-prefix"] = telComponents[3]; - address["tel-local-suffix"] = telComponents[4]; - } - } - } else { - // Treat "tel" as "tel-national" directly if it can't be parsed. - address["tel-national"] = address.tel; - } - } - - TEL_COMPONENTS.forEach(c => { - address[c] = address[c] || ""; - }); - } - - return hasNewComputedFields; } _normalizeFields(address) { @@ -1700,7 +1597,7 @@ export class AddressesBase extends AutofillRecords { } _normalizeAddressFields(address) { - if (STREET_ADDRESS_COMPONENTS.some(c => !!address[c])) { + if (AddressRecord.STREET_ADDRESS_COMPONENTS.some(c => !!address[c])) { // Treat "street-address" as "address-line1" if it contains only one line // and "address-line1" is omitted. if ( @@ -1714,14 +1611,14 @@ export class AddressesBase extends AutofillRecords { // Concatenate "address-line*" if "street-address" is omitted. if (!address["street-address"]) { - address["street-address"] = STREET_ADDRESS_COMPONENTS.map( + address["street-address"] = AddressRecord.STREET_ADDRESS_COMPONENTS.map( c => address[c] ) .join("\n") .replace(/\n+$/, ""); } } - STREET_ADDRESS_COMPONENTS.forEach(c => delete address[c]); + AddressRecord.STREET_ADDRESS_COMPONENTS.forEach(c => delete address[c]); } _normalizeCountryFields(address) { @@ -1753,7 +1650,7 @@ export class AddressesBase extends AutofillRecords { } _normalizeTelFields(address) { - if (address.tel || TEL_COMPONENTS.some(c => !!address[c])) { + if (address.tel || AddressRecord.TEL_COMPONENTS.some(c => !!address[c])) { lazy.FormAutofillUtils.compressTel(address); let possibleRegion = address.country || FormAutofill.DEFAULT_REGION; @@ -1764,7 +1661,7 @@ export class AddressesBase extends AutofillRecords { address.tel = tel.internationalNumber; } } - TEL_COMPONENTS.forEach(c => delete address[c]); + AddressRecord.TEL_COMPONENTS.forEach(c => delete address[c]); } /** @@ -1793,12 +1690,14 @@ export class AddressesBase extends AutofillRecords { // we will rebuild it and replace the local `name` field with "Jane Poe". if ( !("name" in remoteRecord) && - NAME_COMPONENTS.some(c => c in remoteRecord) + AddressRecord.NAME_COMPONENTS.some(c => c in remoteRecord) ) { const localRecord = this._findByGUID(remoteRecord.guid); if ( localRecord && - NAME_COMPONENTS.every(c => remoteRecord[c] == localRecord[c]) + AddressRecord.NAME_COMPONENTS.every( + c => remoteRecord[c] == localRecord[c] + ) ) { remoteRecord.name = localRecord.name; } else { @@ -1815,7 +1714,7 @@ export class AddressesBase extends AutofillRecords { // This also means that the incoming remote record will also contain *-name fields. // However, since the autofill storage does not expect remote records to contain // computed fields while merging, we remove them from the remote record. - NAME_COMPONENTS.forEach(f => delete remoteRecord[f]); + AddressRecord.NAME_COMPONENTS.forEach(f => delete remoteRecord[f]); } } @@ -1879,7 +1778,7 @@ export class CreditCardsBase extends AutofillRecords { return hasNewComputedFields; } - async _encryptNumber(creditCard) { + async _encryptNumber(_creditCard) { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); } diff --git a/toolkit/components/formautofill/FormAutofillSync.sys.mjs b/toolkit/components/formautofill/FormAutofillSync.sys.mjs index 4540737e38..15ae9b60b5 100644 --- a/toolkit/components/formautofill/FormAutofillSync.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillSync.sys.mjs @@ -244,7 +244,7 @@ class AutofillChangeset extends Changeset { super(); } - getModifiedTimestamp(id) { + getModifiedTimestamp(_id) { throw new Error("Don't use timestamps to resolve autofill merge conflicts"); } diff --git a/toolkit/components/formautofill/Helpers.ios.mjs b/toolkit/components/formautofill/Helpers.ios.mjs index 4144d3e98c..56bb49f0e9 100644 --- a/toolkit/components/formautofill/Helpers.ios.mjs +++ b/toolkit/components/formautofill/Helpers.ios.mjs @@ -45,6 +45,12 @@ HTMLElement.prototype.getAutocompleteInfo = function () { }; }; +// Bug 1835024. Webkit doesn't support `checkVisibility` API +// https://drafts.csswg.org/cssom-view-1/#dom-element-checkvisibility +HTMLElement.prototype.checkVisibility = function (_options) { + throw new Error(`Not implemented: WebKit doesn't support checkVisibility `); +}; + // This function helps us debug better when an error occurs because a certain mock is missing const withNotImplementedError = obj => new Proxy(obj, { @@ -58,6 +64,15 @@ const withNotImplementedError = obj => }, }); +// This function will create a proxy for each undefined property +// This is useful when the accessed property name is unkonwn beforehand +const undefinedProxy = () => + new Proxy(() => {}, { + get() { + return undefinedProxy(); + }, + }); + // Webpack needs to be able to statically analyze require statements in order to build the dependency graph // In order to require modules dynamically at runtime, we use require.context() to create a dynamic require // that is still able to be parsed by Webpack at compile time. The "./" and ".mjs" tells webpack that files @@ -128,23 +143,10 @@ export const OSKeyStore = withNotImplementedError({ ensureLoggedIn: () => true, }); -// Checks an element's focusability and accessibility via keyboard navigation -const checkFocusability = element => { - return ( - !element.disabled && - !element.hidden && - element.style.display != "none" && - element.tabIndex != "-1" - ); -}; - // Define mock for Services // NOTE: Services is a global so we need to attach it to the window // eslint-disable-next-line no-shadow export const Services = withNotImplementedError({ - focus: withNotImplementedError({ - elementIsFocusable: checkFocusability, - }), locale: withNotImplementedError({ isAppLocaleRTL: false }), prefs: withNotImplementedError({ prefIsLocked: () => false }), strings: withNotImplementedError({ @@ -154,7 +156,64 @@ export const Services = withNotImplementedError({ formatStringFromName: () => "", }), }), - uuid: withNotImplementedError({ generateUUID: () => "" }), + telemetry: withNotImplementedError({ + scalarAdd: (scalarName, scalarValue) => { + // For now, we only care about the address form telemetry + // TODO(FXCM-935): move address telemetry to Glean so we can remove this + // Data format of the sent message is: + // { + // type: "scalar", + // name: "formautofill.addresses.detected_sections_count", + // value: Number, + // } + if (scalarName !== "formautofill.addresses.detected_sections_count") { + return; + } + + // eslint-disable-next-line no-undef + webkit.messageHandlers.addressFormTelemetryMessageHandler.postMessage( + JSON.stringify({ + type: "scalar", + object: scalarName, + value: scalarValue, + }) + ); + }, + recordEvent: (category, method, object, value, extra) => { + // For now, we only care about the address form telemetry + // TODO(FXCM-935): move address telemetry to Glean so we can remove this + // Data format of the sent message is: + // { + // type: "event", + // category: "address", + // method: "detected" | "filled" | "filled_modified", + // object: "address_form" | "address_form_ext", + // value: String, + // extra: Any, + // } + if (category !== "address") { + return; + } + + // eslint-disable-next-line no-undef + webkit.messageHandlers.addressFormTelemetryMessageHandler.postMessage( + JSON.stringify({ + type: "event", + category, + method, + object, + value, + extra, + }) + ); + }, + }), + // TODO(FXCM-936): we should use crypto.randomUUID() instead of Services.uuid.generateUUID() in our codebase + // Underneath crypto.randomUUID() uses the same implementation as generateUUID() + // https://searchfox.org/mozilla-central/rev/d405168c4d3c0fb900a7354ae17bb34e939af996/dom/base/Crypto.cpp#96 + // The only limitation is that it's not available in insecure contexts, which should be fine for both iOS and Desktop + // since we only autofill in secure contexts + uuid: withNotImplementedError({ generateUUID: () => crypto.randomUUID() }), }); window.Services = Services; @@ -163,15 +222,18 @@ window.Localization = function () { return { formatValueSync: () => "" }; }; +// For now, we ignore all calls to glean. +// TODO(FXCM-935): move address telemetry to Glean so we can create a universal mock for glean that +// dispatches telemetry messages to the iOS. +window.Glean = { + formautofillCreditcards: undefinedProxy(), + formautofill: undefinedProxy(), +}; + export const windowUtils = withNotImplementedError({ removeManuallyManagedState: () => {}, addManuallyManagedState: () => {}, }); window.windowUtils = windowUtils; -export const AutofillTelemetry = withNotImplementedError({ - recordFormInteractionEvent: () => {}, - recordDetectedSectionCount: () => {}, -}); - export { IOSAppConstants as AppConstants } from "resource://gre/modules/shared/Constants.ios.mjs"; diff --git a/toolkit/components/formautofill/Overrides.ios.js b/toolkit/components/formautofill/Overrides.ios.js index a0023a267c..ae5998992b 100644 --- a/toolkit/components/formautofill/Overrides.ios.js +++ b/toolkit/components/formautofill/Overrides.ios.js @@ -7,7 +7,6 @@ // This array defines overrides that webpack will use when bundling the JS on iOS // in order to load the right modules const ModuleOverrides = { - "AutofillTelemetry.sys.mjs": "Helpers.ios.mjs", "AppConstants.sys.mjs": "Helpers.ios.mjs", "XPCOMUtils.sys.mjs": "Helpers.ios.mjs", "Region.sys.mjs": "Helpers.ios.mjs", diff --git a/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs b/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs index 15fc1a520c..52ed8bed03 100644 --- a/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs +++ b/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs @@ -12,7 +12,7 @@ ChromeUtils.defineESModuleGetters(lazy, { ChromeUtils.defineLazyGetter( lazy, "l10n", - () => new Localization(["browser/preferences/formAutofill.ftl"], true) + () => new Localization(["toolkit/formautofill/formAutofill.ftl"], true) ); class ProfileAutoCompleteResult { @@ -100,16 +100,16 @@ class ProfileAutoCompleteResult { * Get the secondary label based on the focused field name and related field names * in the same form. * - * @param {string} focusedFieldName The field name of the focused input - * @param {Array} allFieldNames The field names in the same section - * @param {object} profile The profile providing the labels to show. + * @param {string} _focusedFieldName The field name of the focused input + * @param {Array} _allFieldNames The field names in the same section + * @param {object} _profile The profile providing the labels to show. * @returns {string} The secondary label */ - _getSecondaryLabel(focusedFieldName, allFieldNames, profile) { + _getSecondaryLabel(_focusedFieldName, _allFieldNames, _profile) { return ""; } - _generateLabels(focusedFieldName, allFieldNames, profiles) {} + _generateLabels(_focusedFieldName, _allFieldNames, _profiles) {} /** * Get the value of the result at the given index. @@ -190,19 +190,19 @@ class ProfileAutoCompleteResult { /** * Returns true if the value at the given index is removable * - * @param {number} index The index of the result to remove + * @param {number} _index The index of the result to remove * @returns {boolean} True if the value is removable */ - isRemovableAt(index) { + isRemovableAt(_index) { return false; } /** * Removes a result from the resultset * - * @param {number} index The index of the result to remove + * @param {number} _index The index of the result to remove */ - removeValueAt(index) { + removeValueAt(_index) { // There is no plan to support removing profiles via autocomplete. } } @@ -277,10 +277,19 @@ export class AddressResult extends ProfileAutoCompleteResult { } _generateLabels(focusedFieldName, allFieldNames, profiles) { + const manageLabel = lazy.l10n.formatValueSync( + "autofill-manage-addresses-label" + ); + if (this._isInputAutofilled) { return [ { primary: "", secondary: "" }, // Clear button - { primary: "", secondary: "" }, // Footer + // Footer + { + primary: "", + secondary: "", + manageLabel, + }, ]; } @@ -306,6 +315,10 @@ export class AddressResult extends ProfileAutoCompleteResult { ), }; }); + + const focusedCategory = + lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName); + // Add an empty result entry for footer. Its content will come from // the footer binding, so don't assign any value to it. // The additional properties: categories and focusedCategory are required of @@ -313,12 +326,11 @@ export class AddressResult extends ProfileAutoCompleteResult { labels.push({ primary: "", secondary: "", + manageLabel, categories: lazy.FormAutofillUtils.getCategoriesFromFieldNames( this._allFieldNames ), - focusedCategory: lazy.FormAutofillUtils.getCategoryFromFieldName( - this._focusedFieldName - ), + focusedCategory, }); return labels; @@ -385,10 +397,19 @@ export class CreditCardResult extends ProfileAutoCompleteResult { ]; } + const manageLabel = lazy.l10n.formatValueSync( + "autofill-manage-payment-methods-label" + ); + if (this._isInputAutofilled) { return [ { primary: "", secondary: "" }, // Clear button - { primary: "", secondary: "" }, // Footer + // Footer + { + primary: "", + secondary: "", + manageLabel, + }, ]; } @@ -431,8 +452,17 @@ export class CreditCardResult extends ProfileAutoCompleteResult { image, }; }); + + const focusedCategory = + lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName); + // Add an empty result entry for footer. - labels.push({ primary: "", secondary: "" }); + labels.push({ + primary: "", + secondary: "", + manageLabel, + focusedCategory, + }); return labels; } diff --git a/toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs b/toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs index 6bb0e991b1..5e737a018b 100644 --- a/toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs +++ b/toolkit/components/formautofill/android/FormAutofillPrompter.sys.mjs @@ -34,10 +34,10 @@ export let FormAutofillPrompter = { }, async promptToSaveAddress( - browser, - storage, - flowId, - { oldRecord, newRecord } + _browser, + _storage, + _flowId, + { _oldRecord, _newRecord } ) { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); }, diff --git a/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs index 0d11880ff5..964be31d06 100644 --- a/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs +++ b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs @@ -115,17 +115,17 @@ class Addresses extends AddressesBase { return super.getSavedFieldNames(); } - async reconcile(remoteRecord) { + async reconcile(_remoteRecord) { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); } - async findDuplicateGUID(remoteRecord) { + async findDuplicateGUID(_remoteRecord) { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); } } class CreditCards extends CreditCardsBase { - async _encryptNumber(creditCard) { + async _encryptNumber(_creditCard) { // Don't encrypt or obfuscate for GV, since we don't store or show // the number. The API has to always provide the original number. } @@ -220,11 +220,11 @@ class CreditCards extends CreditCardsBase { return null; } - async reconcile(remoteRecord) { + async reconcile(_remoteRecord) { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); } - async findDuplicateGUID(remoteRecord) { + async findDuplicateGUID(_remoteRecord) { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); } } diff --git a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs index ecf787137e..f166716de5 100644 --- a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs +++ b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs @@ -11,7 +11,7 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; import { FormAutofill } from "resource://autofill/FormAutofill.sys.mjs"; import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs"; -import { AutofillTelemetry } from "resource://autofill/AutofillTelemetry.sys.mjs"; +import { AutofillTelemetry } from "resource://gre/modules/shared/AutofillTelemetry.sys.mjs"; import { showConfirmation } from "resource://gre/modules/FillHelpers.sys.mjs"; const lazy = {}; @@ -187,7 +187,7 @@ export class AutofillDoorhanger { renderHeader() { // Render the header text - const text = this.header.querySelector(`p`); + const text = this.header.querySelector(`h1`); this.doc.l10n.setAttributes(text, this.ui.header.l10nId); // Render the menu button @@ -529,6 +529,8 @@ export class AddressSaveDoorhanger extends AutofillDoorhanger { //const img = this.doc.createElement("img"); const img = this.doc.createXULElement("image"); img.setAttribute("class", imgClass); + // ToDo: provide meaningful alt values (bug 1870155): + img.setAttribute("alt", ""); section.appendChild(img); // Each line is consisted of multiple to form diff style texts diff --git a/toolkit/components/formautofill/moz.build b/toolkit/components/formautofill/moz.build index 542fc595e0..d2091dc850 100644 --- a/toolkit/components/formautofill/moz.build +++ b/toolkit/components/formautofill/moz.build @@ -15,6 +15,8 @@ EXTRA_JS_MODULES.shared += [ "shared/AddressMetaDataExtension.sys.mjs", "shared/AddressMetaDataLoader.sys.mjs", "shared/AddressParser.sys.mjs", + "shared/AddressRecord.sys.mjs", + "shared/AutofillTelemetry.sys.mjs", "shared/CreditCardRecord.sys.mjs", "shared/CreditCardRuleset.sys.mjs", "shared/FieldScanner.sys.mjs", @@ -26,6 +28,9 @@ EXTRA_JS_MODULES.shared += [ "shared/FormStateManager.sys.mjs", "shared/HeuristicsRegExp.sys.mjs", "shared/LabelUtils.sys.mjs", + "shared/PhoneNumber.sys.mjs", + "shared/PhoneNumberMetaData.sys.mjs", + "shared/PhoneNumberNormalizer.sys.mjs", ] EXPORTS.mozilla += ["FormAutofillNative.h"] diff --git a/toolkit/components/formautofill/phonenumberutils/PhoneNumber.sys.mjs b/toolkit/components/formautofill/phonenumberutils/PhoneNumber.sys.mjs deleted file mode 100644 index 80b5e43acb..0000000000 --- a/toolkit/components/formautofill/phonenumberutils/PhoneNumber.sys.mjs +++ /dev/null @@ -1,474 +0,0 @@ -/* This Source Code Form is subject to the terms of the Apache License, Version - * 2.0. If a copy of the Apache License was not distributed with this file, You - * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */ - -// This library came from https://github.com/andreasgal/PhoneNumber.js but will -// be further maintained by our own in Form Autofill codebase. - -import { PHONE_NUMBER_META_DATA } from "resource://autofill/phonenumberutils/PhoneNumberMetaData.sys.mjs"; - -const lazy = {}; - -ChromeUtils.defineESModuleGetters(lazy, { - PhoneNumberNormalizer: - "resource://autofill/phonenumberutils/PhoneNumberNormalizer.sys.mjs", -}); - -export var 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 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 FORMAT_ENCODING = [ - "^pattern$", - "nationalFormat", - "^leadingDigits", - "nationalPrefixFormattingRule", - "internationalFormat", - ]; - - 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 (let n = 0; n < encoding.length; ++n) { - let value = array[n]; - if (!value) { - continue; - } - 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) { - let array = JSON.parse(md); - md = ParseArray(array, META_DATA_ENCODING, { countryCode }); - regionCache[md.region] = md; - return md; - } - - // Parse string encoded format data into a convenient object - // representation. - function ParseFormat(md) { - let formats = md.formats; - if (!formats) { - return; - } - // Bail if we already parsed the format definitions. - if (!Array.isArray(formats[0])) { - return; - } - 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). - let md = regionCache[region]; - if (md) { - return md; - } - 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 (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") { - entry[0] = ParseMetaData(countryCode, entry[0]); - } - let formats = entry[0].formats; - let current = ParseMetaData(countryCode, entry[n]); - current.formats = formats; - 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) { - 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); - let formats = regionMetaData.formats; - if (!formats) { - return null; - } - 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)) { - continue; - } - if (!format.pattern.test(number)) { - continue; - } - if (intl) { - // If there is no international format, just fall back to the national - // format. - 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") { - 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. - 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. - let match = number.match(SPLIT_FIRST_GROUP); - if (match) { - 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; - } - return null; - } - - function NationalNumber(regionMetaData, number) { - this.region = regionMetaData.region; - this.regionMetaData = regionMetaData; - this.number = number; - } - - // 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() { - let value = FormatNumber(this.regionMetaData, this.number, true); - Object.defineProperty(this, "internationalFormat", { - value, - enumerable: true, - }); - return value; - }, - // (949) 726-2896 - get nationalFormat() { - let value = FormatNumber(this.regionMetaData, this.number, false); - Object.defineProperty(this, "nationalFormat", { - value, - enumerable: true, - }); - return value; - }, - // +19497262896 - get internationalNumber() { - let value = this.internationalFormat - ? this.internationalFormat.replace(NON_DIALABLE_CHARS, "") - : null; - Object.defineProperty(this, "internationalNumber", { - value, - enumerable: true, - }); - return value; - }, - // 9497262896 - get nationalNumber() { - let value = this.nationalFormat - ? this.nationalFormat.replace(NON_DIALABLE_CHARS, "") - : null; - Object.defineProperty(this, "nationalNumber", { - value, - enumerable: true, - }); - return value; - }, - // country name 'US' - get countryName() { - let value = this.region ? this.region : null; - Object.defineProperty(this, "countryName", { value, enumerable: true }); - return value; - }, - // country code '+1' - get countryCode() { - 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 (let n = 1; n <= 3; ++n) { - let cc = number.substr(0, n); - if (dataBase[cc]) { - return cc; - } - } - return null; - } - - // 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) { - let ret; - - // Remove formating characters and whitespace. - number = lazy.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] !== "+") { - return null; - } - - // Detect and strip leading '+'. - if (number[0] === "+") { - return ParseInternationalNumber( - number.replace(LEADING_PLUS_CHARS_PATTERN, "") - ); - } - - // If "defaultRegion" is a country code, use it to parse the number directly. - let matches = String(defaultRegion).match(/^\+?(\d+)/); - if (matches) { - let countryCode = ParseCountryCode(matches[1]); - if (!countryCode) { - return null; - } - return ParseNumberByCountryCode(number, countryCode); - } - - // Lookup the meta data for the given region. - 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)) { - let possibleNumber = number.replace(md.internationalPrefix, ""); - ret = ParseInternationalNumber(possibleNumber); - if (ret) { - return ret; - } - } - - ret = ParseNationalNumberAndCheckNationalPrefix(number, md); - 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) { - return ret; - } - - // If the number matches the possible numbers of the current region, - // return it as a possible 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") { - return false; - } - - 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, - IsValid: IsValidNumber, - Parse: ParseNumber, - FindMetaDataForRegion, - }; -})(PHONE_NUMBER_META_DATA); diff --git a/toolkit/components/formautofill/phonenumberutils/PhoneNumberMetaData.sys.mjs b/toolkit/components/formautofill/phonenumberutils/PhoneNumberMetaData.sys.mjs deleted file mode 100644 index 3338ce7c16..0000000000 --- a/toolkit/components/formautofill/phonenumberutils/PhoneNumberMetaData.sys.mjs +++ /dev/null @@ -1,291 +0,0 @@ -/* This Source Code Form is subject to the terms of the Apache License, Version - * 2.0. If a copy of the Apache License was not distributed with this file, You - * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */ - -/* - * This data was generated base on libphonenumber v8.4.1 via the script in - * https://github.com/andreasgal/PhoneNumber.js - * - * The XML format of libphonenumber has changed since v8.4.2 so we can only stay - * in this version for now. - */ - -export var PHONE_NUMBER_META_DATA = { - 46: '["SE","00","0",null,null,"$NP$FG","\\\\d{6,12}","[1-35-9]\\\\d{5,11}|4\\\\d{6,8}",[["(8)(\\\\d{2,3})(\\\\d{2,3})(\\\\d{2})","$1-$2 $3 $4","8",null,"$1 $2 $3 $4"],["([1-69]\\\\d)(\\\\d{2,3})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4","1[013689]|2[0136]|3[1356]|4[0246]|54|6[03]|90",null,"$1 $2 $3 $4"],["([1-469]\\\\d)(\\\\d{3})(\\\\d{2})","$1-$2 $3","1[136]|2[136]|3[356]|4[0246]|6[03]|90",null,"$1 $2 $3"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4","1[2457]|2(?:[247-9]|5[0138])|3[0247-9]|4[1357-9]|5[0-35-9]|6(?:[124-689]|7[0-2])|9(?:[125-8]|3[0-5]|4[0-3])",null,"$1 $2 $3 $4"],["(\\\\d{3})(\\\\d{2,3})(\\\\d{2})","$1-$2 $3","1[2457]|2(?:[247-9]|5[0138])|3[0247-9]|4[1357-9]|5[0-35-9]|6(?:[124-689]|7[0-2])|9(?:[125-8]|3[0-5]|4[0-3])",null,"$1 $2 $3"],["(7\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4","7",null,"$1 $2 $3 $4"],["(77)(\\\\d{2})(\\\\d{2})","$1-$2$3","7",null,"$1 $2 $3"],["(20)(\\\\d{2,3})(\\\\d{2})","$1-$2 $3","20",null,"$1 $2 $3"],["(9[034]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1-$2 $3 $4","9[034]",null,"$1 $2 $3 $4"],["(9[034]\\\\d)(\\\\d{4})","$1-$2","9[034]",null,"$1 $2"],["(\\\\d{3})(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4 $5","25[245]|67[3-6]",null,"$1 $2 $3 $4 $5"]]]', - 299: '["GL","00",null,null,null,null,"\\\\d{6}","[1-689]\\\\d{5}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', - 385: '["HR","00","0",null,null,"$NP$FG","\\\\d{6,9}","[1-7]\\\\d{5,8}|[89]\\\\d{6,8}",[["(1)(\\\\d{4})(\\\\d{3})","$1 $2 $3","1",null],["([2-5]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[2-5]",null],["(9\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","9",null],["(6[01])(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","6[01]",null],["([67]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[67]",null],["(80[01])(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","8",null],["(80[01])(\\\\d{3})(\\\\d{3})","$1 $2 $3","8",null]]]', - 670: '["TL","00",null,null,null,null,"\\\\d{7,8}","[2-489]\\\\d{6}|7\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[2-489]",null],["(\\\\d{4})(\\\\d{4})","$1 $2","7",null]]]', - 258: '["MZ","00",null,null,null,null,"\\\\d{8,9}","[28]\\\\d{7,8}",[["([28]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2|8[2-7]",null],["(80\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","80",null]]]', - 359: '["BG","00","0",null,null,"$NP$FG","\\\\d{5,9}","[23567]\\\\d{5,7}|[489]\\\\d{6,8}",[["(2)(\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","2",null],["(2)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2",null],["(\\\\d{3})(\\\\d{4})","$1 $2","43[124-7]|70[1-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1 $2 $3","43[124-7]|70[1-9]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","[78]00",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","999",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2,3})","$1 $2 $3","[356]|4[124-7]|7[1-9]|8[1-6]|9[1-7]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","48|8[7-9]|9[08]",null]]]', - 682: '["CK","00",null,null,null,null,"\\\\d{5}","[2-8]\\\\d{4}",[["(\\\\d{2})(\\\\d{3})","$1 $2",null,null]]]', - 852: '["HK","00(?:[126-9]|30|5[09])?",null,null,null,null,"\\\\d{5,11}","[235-7]\\\\d{7}|8\\\\d{7,8}|9\\\\d{4,10}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[235-7]|[89](?:0[1-9]|[1-9])",null],["(800)(\\\\d{3})(\\\\d{3})","$1 $2 $3","800",null],["(900)(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","900",null],["(900)(\\\\d{2,5})","$1 $2","900",null]]]', - 998: '["UZ","810","8",null,null,"$NP $FG","\\\\d{7,9}","[679]\\\\d{8}",[["([679]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 291: '["ER","00","0",null,null,"$NP$FG","\\\\d{6,7}","[178]\\\\d{6}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', - 95: '["MM","00","0",null,null,"$NP$FG","\\\\d{5,10}","[1478]\\\\d{5,7}|[256]\\\\d{5,8}|9(?:[279]\\\\d{0,2}|[58]|[34]\\\\d{1,2}|6\\\\d?)\\\\d{6}",[["(\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","1|2[245]",null],["(2)(\\\\d{4})(\\\\d{4})","$1 $2 $3","251",null],["(\\\\d)(\\\\d{2})(\\\\d{3})","$1 $2 $3","16|2",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","67|81",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3,4})","$1 $2 $3","[4-8]",null],["(9)(\\\\d{3})(\\\\d{4,6})","$1 $2 $3","9(?:2[0-4]|[35-9]|4[137-9])",null],["(9)([34]\\\\d{4})(\\\\d{4})","$1 $2 $3","9(?:3[0-36]|4[0-57-9])",null],["(9)(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","92[56]",null],["(9)(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1 $2 $3 $4","93",null]]]', - 266: '["LS","00",null,null,null,null,"\\\\d{8}","[2568]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', - 245: '["GW","00",null,null,null,null,"\\\\d{7,9}","(?:4(?:0\\\\d{5}|4\\\\d{7})|9\\\\d{8})",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","44|9[567]",null],["(\\\\d{3})(\\\\d{4})","$1 $2","40",null]]]', - 374: '["AM","00","0",null,null,"($NP$FG)","\\\\d{5,8}","[1-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{6})","$1 $2","1|47",null],["(\\\\d{2})(\\\\d{6})","$1 $2","4[1349]|[5-7]|9[1-9]","$NP$FG"],["(\\\\d{3})(\\\\d{5})","$1 $2","[23]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","8|90","$NP $FG"]]]', - 61: [ - '["AU","(?:14(?:1[14]|34|4[17]|[56]6|7[47]|88))?001[14-689]","0",null,null,null,"\\\\d{6,10}","1\\\\d{4,9}|[2-578]\\\\d{8}",[["([2378])(\\\\d{4})(\\\\d{4})","$1 $2 $3","[2378]","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[45]|14","$NP$FG"],["(16)(\\\\d{3,4})","$1 $2","16","$NP$FG"],["(16)(\\\\d{3})(\\\\d{2,4})","$1 $2 $3","16","$NP$FG"],["(1[389]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1(?:[38]0|90)","$FG"],["(180)(2\\\\d{3})","$1 $2","180","$FG"],["(19\\\\d)(\\\\d{3})","$1 $2","19[13]","$FG"],["(19\\\\d{2})(\\\\d{4})","$1 $2","19[679]","$FG"],["(13)(\\\\d{2})(\\\\d{2})","$1 $2 $3","13[1-9]","$FG"]]]', - '["CC","(?:14(?:1[14]|34|4[17]|[56]6|7[47]|88))?001[14-689]","0",null,null,null,"\\\\d{6,10}","[1458]\\\\d{5,9}"]', - '["CX","(?:14(?:1[14]|34|4[17]|[56]6|7[47]|88))?001[14-689]","0",null,null,null,"\\\\d{6,10}","[1458]\\\\d{5,9}"]', - ], - 500: '["FK","00",null,null,null,null,"\\\\d{5}","[2-7]\\\\d{4}"]', - 261: '["MG","00","0",null,null,"$NP$FG","\\\\d{7,9}","[23]\\\\d{8}",[["([23]\\\\d)(\\\\d{2})(\\\\d{3})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 92: '["PK","00","0",null,null,"($NP$FG)","\\\\d{6,12}","1\\\\d{8}|[2-8]\\\\d{5,11}|9(?:[013-9]\\\\d{4,9}|2\\\\d(?:111\\\\d{6}|\\\\d{3,7}))",[["(\\\\d{2})(111)(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","(?:2[125]|4[0-246-9]|5[1-35-7]|6[1-8]|7[14]|8[16]|91)1",null],["(\\\\d{3})(111)(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","2[349]|45|54|60|72|8[2-5]|9[2-9]",null],["(\\\\d{2})(\\\\d{7,8})","$1 $2","(?:2[125]|4[0-246-9]|5[1-35-7]|6[1-8]|7[14]|8[16]|91)[2-9]",null],["(\\\\d{3})(\\\\d{6,7})","$1 $2","2[349]|45|54|60|72|8[2-5]|9[2-9]",null],["(3\\\\d{2})(\\\\d{7})","$1 $2","3","$NP$FG"],["([15]\\\\d{3})(\\\\d{5,6})","$1 $2","58[12]|1",null],["(586\\\\d{2})(\\\\d{5})","$1 $2","586",null],["([89]00)(\\\\d{3})(\\\\d{2})","$1 $2 $3","[89]00","$NP$FG"]]]', - 234: '["NG","009","0",null,null,"$NP$FG","\\\\d{5,14}","[1-6]\\\\d{5,8}|9\\\\d{5,9}|[78]\\\\d{5,13}",[["(\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[12]|9(?:0[3-9]|[1-9])",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2,3})","$1 $2 $3","[3-6]|7(?:[1-79]|0[1-9])|8[2-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","70|8[01]|90[235-9]",null],["([78]00)(\\\\d{4})(\\\\d{4,5})","$1 $2 $3","[78]00",null],["([78]00)(\\\\d{5})(\\\\d{5,6})","$1 $2 $3","[78]00",null],["(78)(\\\\d{2})(\\\\d{3})","$1 $2 $3","78",null]]]', - 350: '["GI","00",null,null,null,null,"\\\\d{8}","[2568]\\\\d{7}",[["(\\\\d{3})(\\\\d{5})","$1 $2","2",null]]]', - 45: '["DK","00",null,null,null,null,"\\\\d{8}","[2-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 963: '["SY","00","0",null,null,"$NP$FG","\\\\d{6,9}","[1-59]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[1-5]",null],["(9\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","9",null]]]', - 226: '["BF","00",null,null,null,null,"\\\\d{8}","[25-7]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 974: '["QA","00",null,null,null,null,"\\\\d{7,8}","[2-8]\\\\d{6,7}",[["([28]\\\\d{2})(\\\\d{4})","$1 $2","[28]",null],["([3-7]\\\\d{3})(\\\\d{4})","$1 $2","[3-7]",null]]]', - 218: '["LY","00","0",null,null,"$NP$FG","\\\\d{7,9}","[25679]\\\\d{8}",[["([25679]\\\\d)(\\\\d{7})","$1-$2",null,null]]]', - 51: '["PE","19(?:1[124]|77|90)00","0",null,null,"($NP$FG)","\\\\d{6,9}","[14-9]\\\\d{7,8}",[["(1)(\\\\d{7})","$1 $2","1",null],["([4-8]\\\\d)(\\\\d{6})","$1 $2","[4-7]|8[2-4]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","80",null],["(9\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","9","$FG"]]]', - 62: '["ID","0(?:0[1789]|10(?:00|1[67]))","0",null,null,"$NP$FG","\\\\d{5,12}","(?:[1-79]\\\\d{6,10}|8\\\\d{7,11})",[["(\\\\d{2})(\\\\d{5,8})","$1 $2","2[124]|[36]1","($NP$FG)"],["(\\\\d{3})(\\\\d{5,8})","$1 $2","[4579]|2[035-9]|[36][02-9]","($NP$FG)"],["(8\\\\d{2})(\\\\d{3,4})(\\\\d{3})","$1-$2-$3","8[1-35-9]",null],["(8\\\\d{2})(\\\\d{4})(\\\\d{4,5})","$1-$2-$3","8[1-35-9]",null],["(1)(500)(\\\\d{3})","$1 $2 $3","15","$FG"],["(177)(\\\\d{6,8})","$1 $2","17",null],["(800)(\\\\d{5,7})","$1 $2","800",null],["(804)(\\\\d{3})(\\\\d{4})","$1 $2 $3","804",null],["(80\\\\d)(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","80[79]",null]]]', - 298: '["FO","00",null,"(10(?:01|[12]0|88))",null,null,"\\\\d{6}","[2-9]\\\\d{5}",[["(\\\\d{6})","$1",null,null]]]', - 381: '["RS","00","0",null,null,"$NP$FG","\\\\d{5,12}","[126-9]\\\\d{4,11}|3(?:[0-79]\\\\d{3,10}|8[2-9]\\\\d{2,9})",[["([23]\\\\d{2})(\\\\d{4,9})","$1 $2","(?:2[389]|39)0",null],["([1-3]\\\\d)(\\\\d{5,10})","$1 $2","1|2(?:[0-24-7]|[389][1-9])|3(?:[0-8]|9[1-9])",null],["(6\\\\d)(\\\\d{6,8})","$1 $2","6",null],["([89]\\\\d{2})(\\\\d{3,9})","$1 $2","[89]",null],["(7[26])(\\\\d{4,9})","$1 $2","7[26]",null],["(7[08]\\\\d)(\\\\d{4,9})","$1 $2","7[08]",null]]]', - 975: '["BT","00",null,null,null,null,"\\\\d{6,8}","[1-8]\\\\d{6,7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","1|77",null],["([2-8])(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-68]|7[246]",null]]]', - 34: '["ES","00",null,null,null,null,"\\\\d{9}","[5-9]\\\\d{8}",[["([89]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[89]00",null],["([5-9]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[568]|[79][0-8]",null]]]', - 881: '["001",null,null,null,null,null,"\\\\d{9}","[67]\\\\d{8}",[["(\\\\d)(\\\\d{3})(\\\\d{5})","$1 $2 $3","[67]",null]]]', - 855: '["KH","00[14-9]","0",null,null,null,"\\\\d{6,10}","[1-9]\\\\d{7,9}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","1\\\\d[1-9]|[2-9]","$NP$FG"],["(1[89]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1[89]0",null]]]', - 420: '["CZ","00",null,null,null,null,"\\\\d{9,12}","[2-8]\\\\d{8}|9\\\\d{8,11}",[["([2-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-8]|9[015-7]",null],["(96\\\\d)(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","96",null],["(9\\\\d)(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","9[36]",null]]]', - 216: '["TN","00",null,null,null,null,"\\\\d{8}","[2-57-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', - 673: '["BN","00",null,null,null,null,"\\\\d{7}","[2-578]\\\\d{6}",[["([2-578]\\\\d{2})(\\\\d{4})","$1 $2",null,null]]]', - 290: [ - '["SH","00",null,null,null,null,"\\\\d{4,5}","[256]\\\\d{4}"]', - '["TA","00",null,null,null,null,"\\\\d{4}","8\\\\d{3}"]', - ], - 882: '["001",null,null,null,null,null,"\\\\d{7,12}","[13]\\\\d{6,11}",[["(\\\\d{2})(\\\\d{4})(\\\\d{3})","$1 $2 $3","3[23]",null],["(\\\\d{2})(\\\\d{5})","$1 $2","16|342",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","34[57]",null],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","348",null],["(\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","16",null],["(\\\\d{2})(\\\\d{4,5})(\\\\d{5})","$1 $2 $3","16|39",null]]]', - 267: '["BW","00",null,null,null,null,"\\\\d{7,8}","[2-79]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[2-6]",null],["(7\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","7",null],["(90)(\\\\d{5})","$1 $2","9",null]]]', - 94: '["LK","00","0",null,null,"$NP$FG","\\\\d{7,9}","[1-9]\\\\d{8}",[["(\\\\d{2})(\\\\d{1})(\\\\d{6})","$1 $2 $3","[1-689]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","7",null]]]', - 356: '["MT","00",null,null,null,null,"\\\\d{8}","[2357-9]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', - 375: '["BY","810","8","8?0?",null,null,"\\\\d{5,11}","[1-4]\\\\d{8}|800\\\\d{3,7}|[89]\\\\d{9,10}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","17[0-3589]|2[4-9]|[34]","$NP 0$FG"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","1(?:5[24]|6[235]|7[467])|2(?:1[246]|2[25]|3[26])","$NP 0$FG"],["(\\\\d{4})(\\\\d{2})(\\\\d{3})","$1 $2-$3","1(?:5[169]|6[3-5]|7[179])|2(?:1[35]|2[34]|3[3-5])","$NP 0$FG"],["([89]\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","8[01]|9","$NP $FG"],["(82\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","82","$NP $FG"],["(800)(\\\\d{3})","$1 $2","800","$NP $FG"],["(800)(\\\\d{2})(\\\\d{2,4})","$1 $2 $3","800","$NP $FG"]]]', - 690: '["TK","00",null,null,null,null,"\\\\d{4,7}","[2-47]\\\\d{3,6}"]', - 507: '["PA","00",null,null,null,null,"\\\\d{7,8}","[1-9]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1-$2","[1-57-9]",null],["(\\\\d{4})(\\\\d{4})","$1-$2","6",null]]]', - 692: '["MH","011","1",null,null,null,"\\\\d{7}","[2-6]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1-$2",null,null]]]', - 250: '["RW","00","0",null,null,null,"\\\\d{8,9}","[027-9]\\\\d{7,8}",[["(2\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","2","$FG"],["([7-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[7-9]","$NP$FG"],["(0\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","0",null]]]', - 81: '["JP","010","0",null,null,"$NP$FG","\\\\d{8,17}","[1-9]\\\\d{8,9}|00(?:[36]\\\\d{7,14}|7\\\\d{5,7}|8\\\\d{7})",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1-$2-$3","(?:12|57|99)0",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1-$2-$3","800",null],["(\\\\d{4})(\\\\d{4})","$1-$2","0077","$FG","NA"],["(\\\\d{4})(\\\\d{2})(\\\\d{3,4})","$1-$2-$3","0077","$FG","NA"],["(\\\\d{4})(\\\\d{2})(\\\\d{4})","$1-$2-$3","0088","$FG","NA"],["(\\\\d{4})(\\\\d{3})(\\\\d{3,4})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{4})(\\\\d{4})(\\\\d{4,5})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{4})(\\\\d{5})(\\\\d{5,6})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{4})(\\\\d{6})(\\\\d{6,7})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1-$2-$3","[2579]0|80[1-9]",null],["(\\\\d{4})(\\\\d)(\\\\d{4})","$1-$2-$3","1(?:26|3[79]|4[56]|5[4-68]|6[3-5])|5(?:76|97)|499|746|8(?:3[89]|63|47|51)|9(?:49|80|9[16])",null],["(\\\\d{3})(\\\\d{2})(\\\\d{4})","$1-$2-$3","1(?:2[3-6]|3[3-9]|4[2-6]|5[2-8]|[68][2-7]|7[2-689]|9[1-578])|2(?:2[03-689]|3[3-58]|4[0-468]|5[04-8]|6[013-8]|7[06-9]|8[02-57-9]|9[13])|4(?:2[28]|3[689]|6[035-7]|7[05689]|80|9[3-5])|5(?:3[1-36-9]|4[4578]|5[013-8]|6[1-9]|7[2-8]|8[14-7]|9[4-9])|7(?:2[15]|3[5-9]|4[02-9]|6[135-8]|7[0-4689]|9[014-9])|8(?:2[49]|3[3-8]|4[5-8]|5[2-9]|6[35-9]|7[579]|8[03-579]|9[2-8])|9(?:[23]0|4[02-46-9]|5[024-79]|6[4-9]|7[2-47-9]|8[02-7]|9[3-7])",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3","1|2(?:2[37]|5[5-9]|64|78|8[39]|91)|4(?:2[2689]|64|7[347])|5(?:[2-589]|39)|60|8(?:[46-9]|3[279]|2[124589])|9(?:[235-8]|93)",null],["(\\\\d{3})(\\\\d{2})(\\\\d{4})","$1-$2-$3","2(?:9[14-79]|74|[34]7|[56]9)|82|993",null],["(\\\\d)(\\\\d{4})(\\\\d{4})","$1-$2-$3","3|4(?:2[09]|7[01])|6[1-9]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3","[2479][1-9]",null]]]', - 237: '["CM","00",null,null,null,null,"\\\\d{8,9}","[2368]\\\\d{7,8}",[["([26])(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","[26]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[23]|88",null],["(800)(\\\\d{2})(\\\\d{3})","$1 $2 $3","80",null]]]', - 351: '["PT","00",null,null,null,null,"\\\\d{9}","[2-46-9]\\\\d{8}",[["(2\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2[12]",null],["([2-46-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","2[3-9]|[346-9]",null]]]', - 246: '["IO","00",null,null,null,null,"\\\\d{7}","3\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 227: '["NE","00",null,null,null,null,"\\\\d{8}","[0289]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[289]|09",null],["(08)(\\\\d{3})(\\\\d{3})","$1 $2 $3","08",null]]]', - 27: '["ZA","00","0",null,null,"$NP$FG","\\\\d{5,9}","[1-79]\\\\d{8}|8\\\\d{4,8}",[["(860)(\\\\d{3})(\\\\d{3})","$1 $2 $3","860",null],["(\\\\d{2})(\\\\d{3,4})","$1 $2","8[1-4]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2,3})","$1 $2 $3","8[1-4]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[1-79]|8(?:[0-57]|6[1-9])",null]]]', - 962: '["JO","00","0",null,null,"$NP$FG","\\\\d{8,9}","[235-9]\\\\d{7,8}",[["(\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2356]|87","($NP$FG)"],["(7)(\\\\d{4})(\\\\d{4})","$1 $2 $3","7[457-9]",null],["(\\\\d{3})(\\\\d{5,6})","$1 $2","70|8[0158]|9",null]]]', - 387: '["BA","00","0",null,null,"$NP$FG","\\\\d{6,9}","[3-9]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2-$3","[3-5]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","6[1-356]|[7-9]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","6[047]",null]]]', - 33: '["FR","00","0",null,null,"$NP$FG","\\\\d{9}","[1-9]\\\\d{8}",[["([1-79])(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","[1-79]",null],["(1\\\\d{2})(\\\\d{3})","$1 $2","11","$FG","NA"],["(8\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","8","$NP $FG"]]]', - 972: '["IL","0(?:0|1[2-9])","0",null,null,"$FG","\\\\d{4,12}","1\\\\d{6,11}|[2-589]\\\\d{3}(?:\\\\d{3,6})?|6\\\\d{3}|7\\\\d{6,9}",[["([2-489])(\\\\d{3})(\\\\d{4})","$1-$2-$3","[2-489]","$NP$FG"],["([57]\\\\d)(\\\\d{3})(\\\\d{4})","$1-$2-$3","[57]","$NP$FG"],["(153)(\\\\d{1,2})(\\\\d{3})(\\\\d{4})","$1 $2 $3 $4","153",null],["(1)([7-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1-$2-$3-$4","1[7-9]",null],["(1255)(\\\\d{3})","$1-$2","125",null],["(1200)(\\\\d{3})(\\\\d{3})","$1-$2-$3","120",null],["(1212)(\\\\d{2})(\\\\d{2})","$1-$2-$3","121",null],["(1599)(\\\\d{6})","$1-$2","15",null],["(\\\\d{4})","*$1","[2-689]",null]]]', - 248: '["SC","0(?:[02]|10?)",null,null,null,null,"\\\\d{6,7}","[24689]\\\\d{5,6}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[246]",null]]]', - 297: '["AW","00",null,null,null,null,"\\\\d{7}","[25-9]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 421: '["SK","00","0",null,null,"$NP$FG","\\\\d{6,9}","(?:[2-68]\\\\d{5,8}|9\\\\d{6,8})",[["(2)(1[67])(\\\\d{3,4})","$1 $2 $3","21[67]",null],["([3-5]\\\\d)(1[67])(\\\\d{2,3})","$1 $2 $3","[3-5]",null],["(2)(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1/$2 $3 $4","2",null],["([3-5]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1/$2 $3 $4","[3-5]",null],["([689]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[689]",null],["(9090)(\\\\d{3})","$1 $2","9090",null]]]', - 672: '["NF","00",null,null,null,null,"\\\\d{5,6}","[13]\\\\d{5}",[["(\\\\d{2})(\\\\d{4})","$1 $2","1",null],["(\\\\d)(\\\\d{5})","$1 $2","3",null]]]', - 870: '["001",null,null,null,null,null,"\\\\d{9}","[35-7]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', - 883: '["001",null,null,null,null,null,"\\\\d{9}(?:\\\\d{3})?","51\\\\d{7}(?:\\\\d{3})?",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","510",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","510",null],["(\\\\d{4})(\\\\d{4})(\\\\d{4})","$1 $2 $3","51[13]",null]]]', - 264: '["NA","00","0",null,null,"$NP$FG","\\\\d{8,9}","[68]\\\\d{7,8}",[["(8\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","8[1235]",null],["(6\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","6",null],["(88)(\\\\d{3})(\\\\d{3})","$1 $2 $3","88",null],["(870)(\\\\d{3})(\\\\d{3})","$1 $2 $3","870",null]]]', - 878: '["001",null,null,null,null,null,"\\\\d{12}","1\\\\d{11}",[["(\\\\d{2})(\\\\d{5})(\\\\d{5})","$1 $2 $3",null,null]]]', - 239: '["ST","00",null,null,null,null,"\\\\d{7}","[29]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 357: '["CY","00",null,null,null,null,"\\\\d{8}","[257-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{6})","$1 $2",null,null]]]', - 240: '["GQ","00",null,null,null,null,"\\\\d{9}","[23589]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[235]",null],["(\\\\d{3})(\\\\d{6})","$1 $2","[89]",null]]]', - 506: '["CR","00",null,"(19(?:0[012468]|1[09]|20|66|77|99))",null,null,"\\\\d{8,10}","[24-9]\\\\d{7,9}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[24-7]|8[3-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1-$2-$3","[89]0",null]]]', - 86: '["CN","(1(?:[129]\\\\d{3}|79\\\\d{2}))?00","0","(1(?:[129]\\\\d{3}|79\\\\d{2}))|0",null,null,"\\\\d{4,12}","[1-7]\\\\d{6,11}|8[0-357-9]\\\\d{6,9}|9\\\\d{7,10}",[["(80\\\\d{2})(\\\\d{4})","$1 $2","80[2678]","$NP$FG"],["([48]00)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[48]00",null],["(\\\\d{5,6})","$1","100|95",null,"NA"],["(\\\\d{2})(\\\\d{5,6})","$1 $2","(?:10|2\\\\d)[19]","$NP$FG"],["(\\\\d{3})(\\\\d{5,6})","$1 $2","[3-9]","$NP$FG"],["(\\\\d{3,4})(\\\\d{4})","$1 $2","[2-9]",null,"NA"],["(21)(\\\\d{4})(\\\\d{4,6})","$1 $2 $3","21","$NP$FG"],["([12]\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","10[1-9]|2[02-9]","$NP$FG"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","3(?:1[02-9]|35|49|5|7[02-68]|9[1-68])|4(?:1[02-9]|2[179]|[35][2-9]|6[4789]|7\\\\d|8[23])|5(?:3[03-9]|4[36]|5[02-9]|6[1-46]|7[028]|80|9[2-46-9])|6(?:3[1-5]|6[0238]|9[12])|7(?:01|[1579]|2[248]|3[04-9]|4[3-6]|6[2368])|8(?:1[236-8]|2[5-7]|3|5[1-9]|7[02-9]|8[3678]|9[1-7])|9(?:0[1-3689]|1[1-79]|[379]|4[13]|5[1-5])","$NP$FG"],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","3(?:11|7[179])|4(?:[15]1|3[1-35])|5(?:1|2[37]|3[12]|51|7[13-79]|9[15])|7(?:31|5[457]|6[09]|91)|8(?:[57]1|98)","$NP$FG"],["(\\\\d{4})(\\\\d{3})(\\\\d{4})","$1 $2 $3","807","$NP$FG"],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","1[3-578]",null],["(10800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","108",null],["(\\\\d{3})(\\\\d{7,8})","$1 $2","950",null]]]', - 257: '["BI","00",null,null,null,null,"\\\\d{8}","[267]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 683: '["NU","00",null,null,null,null,"\\\\d{4}","[1-5]\\\\d{3}"]', - 43: '["AT","00","0",null,null,"$NP$FG","\\\\d{3,13}","[1-9]\\\\d{3,12}",[["(116\\\\d{3})","$1","116","$FG"],["(1)(\\\\d{3,12})","$1 $2","1",null],["(5\\\\d)(\\\\d{3,5})","$1 $2","5[079]",null],["(5\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","5[079]",null],["(5\\\\d)(\\\\d{4})(\\\\d{4,7})","$1 $2 $3","5[079]",null],["(\\\\d{3})(\\\\d{3,10})","$1 $2","316|46|51|732|6(?:5[0-3579]|[6-9])|7(?:[28]0)|[89]",null],["(\\\\d{4})(\\\\d{3,9})","$1 $2","2|3(?:1[1-578]|[3-8])|4[2378]|5[2-6]|6(?:[12]|4[1-9]|5[468])|7(?:2[1-8]|35|4[1-8]|[5-79])",null]]]', - 247: '["AC","00",null,null,null,null,"\\\\d{5,6}","[46]\\\\d{4}|[01589]\\\\d{5}"]', - 675: '["PG","00",null,null,null,null,"\\\\d{7,8}","[1-9]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[13-689]|27",null],["(\\\\d{4})(\\\\d{4})","$1 $2","20|7",null]]]', - 376: '["AD","00",null,null,null,null,"\\\\d{6,9}","[16]\\\\d{5,8}|[37-9]\\\\d{5}",[["(\\\\d{3})(\\\\d{3})","$1 $2","[137-9]|6[0-8]",null],["(\\\\d{4})(\\\\d{4})","$1 $2","180",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","690",null]]]', - 63: '["PH","00","0",null,null,null,"\\\\d{5,13}","2\\\\d{5,7}|[3-9]\\\\d{7,9}|1800\\\\d{7,9}",[["(2)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2","($NP$FG)"],["(2)(\\\\d{5})","$1 $2","2","($NP$FG)"],["(\\\\d{4})(\\\\d{4,6})","$1 $2","3(?:23|39|46)|4(?:2[3-6]|[35]9|4[26]|76)|5(?:22|44)|642|8(?:62|8[245])","($NP$FG)"],["(\\\\d{5})(\\\\d{4})","$1 $2","346|4(?:27|9[35])|883","($NP$FG)"],["([3-8]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[3-8]","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","81|9","$NP$FG"],["(1800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["(1800)(\\\\d{1,2})(\\\\d{3})(\\\\d{4})","$1 $2 $3 $4","1",null]]]', - 236: '["CF","00",null,null,null,null,"\\\\d{8}","[278]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 590: [ - '["GP","00","0",null,null,"$NP$FG","\\\\d{9}","[56]\\\\d{8}",[["([56]90)(\\\\d{2})(\\\\d{4})","$1 $2-$3",null,null]]]', - '["BL","00","0",null,null,null,"\\\\d{9}","[56]\\\\d{8}"]', - '["MF","00","0",null,null,null,"\\\\d{9}","[56]\\\\d{8}"]', - ], - 53: '["CU","119","0",null,null,"($NP$FG)","\\\\d{4,8}","[2-57]\\\\d{5,7}",[["(\\\\d)(\\\\d{6,7})","$1 $2","7",null],["(\\\\d{2})(\\\\d{4,6})","$1 $2","[2-4]",null],["(\\\\d)(\\\\d{7})","$1 $2","5","$NP$FG"]]]', - 64: '["NZ","0(?:0|161)","0",null,null,"$NP$FG","\\\\d{7,11}","6[235-9]\\\\d{6}|[2-57-9]\\\\d{7,10}",[["([34679])(\\\\d{3})(\\\\d{4})","$1-$2 $3","[346]|7[2-57-9]|9[1-9]",null],["(24099)(\\\\d{3})","$1 $2","240",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","21",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,5})","$1 $2 $3","2(?:1[1-9]|[69]|7[0-35-9])|70|86",null],["(2\\\\d)(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","2[028]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2(?:10|74)|5|[89]0",null]]]', - 965: '["KW","00",null,null,null,null,"\\\\d{7,8}","[12569]\\\\d{6,7}",[["(\\\\d{4})(\\\\d{3,4})","$1 $2","[16]|2(?:[0-35-9]|4[0-35-9])|9[024-9]|52[25]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","244|5(?:[015]|66)",null]]]', - 224: '["GN","00",null,null,null,null,"\\\\d{8,9}","[367]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","3",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[67]",null]]]', - 973: '["BH","00",null,null,null,null,"\\\\d{8}","[136-9]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', - 32: '["BE","00","0",null,null,"$NP$FG","\\\\d{8,9}","[1-9]\\\\d{7,8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","4[6-9]",null],["(\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[23]|4[23]|9[2-4]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[156]|7[018]|8(?:0[1-9]|[1-79])",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","(?:80|9)0",null]]]', - 249: '["SD","00","0",null,null,"$NP$FG","\\\\d{9}","[19]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3",null,null]]]', - 678: '["VU","00",null,null,null,null,"\\\\d{5,7}","[2-57-9]\\\\d{4,6}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[579]",null]]]', - 52: '["MX","0[09]","01","0[12]|04[45](\\\\d{10})","1$1","$NP $FG","\\\\d{7,11}","[1-9]\\\\d{9,10}",[["([358]\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","33|55|81",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2467]|3[0-2457-9]|5[089]|8[02-9]|9[0-35-9]",null],["(1)([358]\\\\d)(\\\\d{4})(\\\\d{4})","044 $2 $3 $4","1(?:33|55|81)","$FG","$1 $2 $3 $4"],["(1)(\\\\d{3})(\\\\d{3})(\\\\d{4})","044 $2 $3 $4","1(?:[2467]|3[0-2457-9]|5[089]|8[2-9]|9[1-35-9])","$FG","$1 $2 $3 $4"]]]', - 968: '["OM","00",null,null,null,null,"\\\\d{7,9}","(?:5|[279]\\\\d)\\\\d{6}|800\\\\d{5,6}",[["(2\\\\d)(\\\\d{6})","$1 $2","2",null],["([79]\\\\d{3})(\\\\d{4})","$1 $2","[79]",null],["([58]00)(\\\\d{4,6})","$1 $2","[58]",null]]]', - 599: [ - '["CW","00",null,null,null,null,"\\\\d{7,8}","[169]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[13-7]",null],["(9)(\\\\d{3})(\\\\d{4})","$1 $2 $3","9",null]]]', - '["BQ","00",null,null,null,null,"\\\\d{7}","[347]\\\\d{6}"]', - ], - 800: '["001",null,null,null,null,null,"\\\\d{8}","\\\\d{8}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', - 386: '["SI","00","0",null,null,"$NP$FG","\\\\d{5,8}","[1-7]\\\\d{6,7}|[89]\\\\d{4,7}",[["(\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[12]|3[24-8]|4[24-8]|5[2-8]|7[3-8]","($NP$FG)"],["([3-7]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[37][01]|4[0139]|51|6",null],["([89][09])(\\\\d{3,6})","$1 $2","[89][09]",null],["([58]\\\\d{2})(\\\\d{5})","$1 $2","59|8[1-3]",null]]]', - 679: '["FJ","0(?:0|52)",null,null,null,null,"\\\\d{7}(?:\\\\d{4})?","[35-9]\\\\d{6}|0\\\\d{10}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[35-9]",null],["(\\\\d{4})(\\\\d{3})(\\\\d{4})","$1 $2 $3","0",null]]]', - 238: '["CV","0",null,null,null,null,"\\\\d{7}","[259]\\\\d{6}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', - 691: '["FM","00",null,null,null,null,"\\\\d{7}","[39]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 262: [ - '["RE","00","0",null,null,"$NP$FG","\\\\d{9}","[268]\\\\d{8}",[["([268]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - '["YT","00","0",null,null,"$NP$FG","\\\\d{9}","[268]\\\\d{8}"]', - ], - 241: '["GA","00",null,null,null,null,"\\\\d{7,8}","0?\\\\d{7}",[["(\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[2-7]","0$FG"],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","0",null]]]', - 370: '["LT","00","8","[08]",null,"($NP-$FG)","\\\\d{8}","[3-9]\\\\d{7}",[["([34]\\\\d)(\\\\d{6})","$1 $2","37|4(?:1|5[45]|6[2-4])",null],["([3-6]\\\\d{2})(\\\\d{5})","$1 $2","3[148]|4(?:[24]|6[09])|528|6",null],["([7-9]\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","[7-9]","$NP $FG"],["(5)(2\\\\d{2})(\\\\d{4})","$1 $2 $3","52[0-79]",null]]]', - 256: '["UG","00[057]","0",null,null,"$NP$FG","\\\\d{5,9}","\\\\d{9}",[["(\\\\d{3})(\\\\d{6})","$1 $2","[7-9]|20(?:[013-8]|2[5-9])|4(?:6[45]|[7-9])",null],["(\\\\d{2})(\\\\d{7})","$1 $2","3|4(?:[1-5]|6[0-36-9])",null],["(2024)(\\\\d{5})","$1 $2","2024",null]]]', - 677: '["SB","0[01]",null,null,null,null,"\\\\d{5,7}","[1-9]\\\\d{4,6}",[["(\\\\d{2})(\\\\d{5})","$1 $2","[7-9]",null]]]', - 377: '["MC","00","0",null,null,"$NP$FG","\\\\d{8,9}","[34689]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[39]","$FG"],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","4",null],["(6)(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","6",null],["(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1 $2 $3","8","$FG"]]]', - 382: '["ME","00","0",null,null,"$NP$FG","\\\\d{6,9}","[2-9]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-57-9]|6[036-9]",null]]]', - 231: '["LR","00","0",null,null,"$NP$FG","\\\\d{7,9}","2\\\\d{7,8}|[378]\\\\d{8}|4\\\\d{6}|5\\\\d{6,8}",[["(2\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","2",null],["([4-5])(\\\\d{3})(\\\\d{3})","$1 $2 $3","[45]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[23578]",null]]]', - 591: '["BO","00(1\\\\d)?","0","0(1\\\\d)?",null,null,"\\\\d{7,8}","[23467]\\\\d{7}",[["([234])(\\\\d{7})","$1 $2","[234]",null],["([67]\\\\d{7})","$1","[67]",null]]]', - 808: '["001",null,null,null,null,null,"\\\\d{8}","\\\\d{8}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', - 964: '["IQ","00","0",null,null,"$NP$FG","\\\\d{6,10}","[1-7]\\\\d{7,9}",[["(1)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["([2-6]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[2-6]",null],["(7\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","7",null]]]', - 225: '["CI","00",null,null,null,null,"\\\\d{8}","[02-8]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 992: '["TJ","810","8",null,null,"$FG","\\\\d{3,9}","[3-57-9]\\\\d{8}",[["([349]\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3","[34]7|91[78]",null],["([457-9]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","4[148]|[578]|9(?:1[59]|[0235-9])",null],["(331700)(\\\\d)(\\\\d{2})","$1 $2 $3","331",null],["(\\\\d{4})(\\\\d)(\\\\d{4})","$1 $2 $3","3[1-5]",null]]]', - 55: '["BR","00(?:1[245]|2[1-35]|31|4[13]|[56]5|99)","0","(?:0|90)(?:(1[245]|2[135]|[34]1)(\\\\d{10,11}))?","$2",null,"\\\\d{8,11}","[1-46-9]\\\\d{7,10}|5(?:[0-4]\\\\d{7,9}|5(?:[2-8]\\\\d{7}|9\\\\d{7,8}))",[["(\\\\d{4})(\\\\d{4})","$1-$2","[2-9](?:[1-9]|0[1-9])","$FG","NA"],["(\\\\d{5})(\\\\d{4})","$1-$2","9(?:[1-9]|0[1-9])","$FG","NA"],["(\\\\d{3,5})","$1","1[125689]","$FG","NA"],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2-$3","[1-9][1-9]","($FG)"],["(\\\\d{2})(\\\\d{5})(\\\\d{4})","$1 $2-$3","(?:[14689][1-9]|2[12478]|3[1-578]|5[1-5]|7[13-579])9","($FG)"],["(\\\\d{4})(\\\\d{4})","$1-$2","(?:300|40(?:0|20))",null],["([3589]00)(\\\\d{2,3})(\\\\d{4})","$1 $2 $3","[3589]00","$NP$FG"]]]', - 674: '["NR","00",null,null,null,null,"\\\\d{7}","[458]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 967: '["YE","00","0",null,null,"$NP$FG","\\\\d{6,9}","[1-7]\\\\d{6,8}",[["([1-7])(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[1-6]|7[24-68]",null],["(7\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","7[0137]",null]]]', - 49: '["DE","00","0",null,null,"$NP$FG","\\\\d{2,15}","[1-35-9]\\\\d{3,14}|4(?:[0-8]\\\\d{3,12}|9(?:[0-37]\\\\d|4(?:[1-35-8]|4\\\\d?)|5\\\\d{1,2}|6[1-8]\\\\d?)\\\\d{2,8})",[["(1\\\\d{2})(\\\\d{7,8})","$1 $2","1[67]",null],["(15\\\\d{3})(\\\\d{6})","$1 $2","15[0568]",null],["(1\\\\d{3})(\\\\d{7})","$1 $2","15",null],["(\\\\d{2})(\\\\d{3,11})","$1 $2","3[02]|40|[68]9",null],["(\\\\d{3})(\\\\d{3,11})","$1 $2","2(?:\\\\d1|0[2389]|1[24]|28|34)|3(?:[3-9][15]|40)|[4-8][1-9]1|9(?:06|[1-9]1)",null],["(\\\\d{4})(\\\\d{2,11})","$1 $2","[24-6]|[7-9](?:\\\\d[1-9]|[1-9]\\\\d)|3(?:[3569][02-46-9]|4[2-4679]|7[2-467]|8[2-46-8])",null],["(3\\\\d{4})(\\\\d{1,10})","$1 $2","3",null],["(800)(\\\\d{7,12})","$1 $2","800",null],["(\\\\d{3})(\\\\d)(\\\\d{4,10})","$1 $2 $3","(?:18|90)0|137",null],["(1\\\\d{2})(\\\\d{5,11})","$1 $2","181",null],["(18\\\\d{3})(\\\\d{6})","$1 $2","185",null],["(18\\\\d{2})(\\\\d{7})","$1 $2","18[68]",null],["(18\\\\d)(\\\\d{8})","$1 $2","18[2-579]",null],["(700)(\\\\d{4})(\\\\d{4})","$1 $2 $3","700",null],["(138)(\\\\d{4})","$1 $2","138",null],["(15[013-68])(\\\\d{2})(\\\\d{8})","$1 $2 $3","15[013-68]",null],["(15[279]\\\\d)(\\\\d{2})(\\\\d{7})","$1 $2 $3","15[279]",null],["(1[67]\\\\d)(\\\\d{2})(\\\\d{7,8})","$1 $2 $3","1(?:6[023]|7)",null]]]', - 31: '["NL","00","0",null,null,"$NP$FG","\\\\d{5,10}","1\\\\d{4,8}|[2-7]\\\\d{8}|[89]\\\\d{6,9}",[["([1-578]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[035]|2[0346]|3[03568]|4[0356]|5[0358]|7|8[4578]",null],["([1-5]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1[16-8]|2[259]|3[124]|4[17-9]|5[124679]",null],["(6)(\\\\d{8})","$1 $2","6[0-57-9]",null],["(66)(\\\\d{7})","$1 $2","66",null],["(14)(\\\\d{3,4})","$1 $2","14","$FG"],["([89]0\\\\d)(\\\\d{4,7})","$1 $2","80|9",null]]]', - 970: '["PS","00","0",null,null,"$NP$FG","\\\\d{4,10}","[24589]\\\\d{7,8}|1(?:[78]\\\\d{8}|[49]\\\\d{2,3})",[["([2489])(2\\\\d{2})(\\\\d{4})","$1 $2 $3","[2489]",null],["(5[69]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","5",null],["(1[78]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1[78]","$FG"]]]', - 58: '["VE","00","0",null,null,"$NP$FG","\\\\d{7,10}","[24589]\\\\d{9}",[["(\\\\d{3})(\\\\d{7})","$1-$2",null,null]]]', - 856: '["LA","00","0",null,null,"$NP$FG","\\\\d{6,10}","[2-8]\\\\d{7,9}",[["(20)(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","20",null],["([2-8]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","2[13]|3[14]|[4-8]",null],["(30)(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","30",null]]]', - 354: '["IS","1(?:0(?:01|10|20)|100)|00",null,null,null,null,"\\\\d{7,9}","[4-9]\\\\d{6}|38\\\\d{7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[4-9]",null],["(3\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","3",null]]]', - 242: '["CG","00",null,null,null,null,"\\\\d{9}","[028]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[02]",null],["(\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","8",null]]]', - 423: '["LI","00","0","0|10(?:01|20|66)",null,null,"\\\\d{7,9}","6\\\\d{8}|[23789]\\\\d{6}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3","[23789]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","6[56]",null],["(69)(7\\\\d{2})(\\\\d{4})","$1 $2 $3","697",null]]]', - 213: '["DZ","00","0",null,null,"$NP$FG","\\\\d{8,9}","(?:[1-4]|[5-9]\\\\d)\\\\d{7}",[["([1-4]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[1-4]",null],["([5-8]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[5-8]",null],["(9\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","9",null]]]', - 371: '["LV","00",null,null,null,null,"\\\\d{8}","[2689]\\\\d{7}",[["([2689]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', - 503: '["SV","00",null,null,null,null,"\\\\d{7,8}|\\\\d{11}","[267]\\\\d{7}|[89]\\\\d{6}(?:\\\\d{4})?",[["(\\\\d{4})(\\\\d{4})","$1 $2","[267]",null],["(\\\\d{3})(\\\\d{4})","$1 $2","[89]",null],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","[89]",null]]]', - 685: '["WS","0",null,null,null,null,"\\\\d{5,7}","[2-8]\\\\d{4,6}",[["(8\\\\d{2})(\\\\d{3,4})","$1 $2","8",null],["(7\\\\d)(\\\\d{5})","$1 $2","7",null],["(\\\\d{5})","$1","[2-6]",null]]]', - 880: '["BD","00","0",null,null,"$NP$FG","\\\\d{6,10}","[2-79]\\\\d{5,9}|1\\\\d{9}|8[0-7]\\\\d{4,8}",[["(2)(\\\\d{7,8})","$1-$2","2",null],["(\\\\d{2})(\\\\d{4,6})","$1-$2","[3-79]1",null],["(\\\\d{4})(\\\\d{3,6})","$1-$2","1|3(?:0|[2-58]2)|4(?:0|[25]2|3[23]|[4689][25])|5(?:[02-578]2|6[25])|6(?:[0347-9]2|[26][25])|7[02-9]2|8(?:[023][23]|[4-7]2)|9(?:[02][23]|[458]2|6[016])",null],["(\\\\d{3})(\\\\d{3,7})","$1-$2","[3-79][2-9]|8",null]]]', - 265: '["MW","00","0",null,null,"$NP$FG","\\\\d{7,9}","(?:1(?:\\\\d{2})?|[2789]\\\\d{2})\\\\d{6}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1",null],["(2\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","2",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[1789]",null]]]', - 65: '["SG","0[0-3]\\\\d",null,null,null,null,"\\\\d{8,11}","[36]\\\\d{7}|[17-9]\\\\d{7,10}",[["([3689]\\\\d{3})(\\\\d{4})","$1 $2","[369]|8[1-9]",null],["(1[89]00)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[89]",null],["(7000)(\\\\d{4})(\\\\d{3})","$1 $2 $3","70",null],["(800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80",null]]]', - 504: '["HN","00",null,null,null,null,"\\\\d{8}","[237-9]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1-$2",null,null]]]', - 688: '["TV","00",null,null,null,null,"\\\\d{5,7}","[279]\\\\d{4,6}"]', - 84: '["VN","00","0",null,null,"$NP$FG","\\\\d{7,10}","[167]\\\\d{6,9}|[2-59]\\\\d{7,9}|8\\\\d{6,8}",[["([17]99)(\\\\d{4})","$1 $2","[17]99",null],["([48])(\\\\d{4})(\\\\d{4})","$1 $2 $3","4|8(?:[1-57]|6[0-79]|9[0-7])",null],["([235-7]\\\\d)(\\\\d{4})(\\\\d{3})","$1 $2 $3","2[025-79]|3[0136-9]|5[2-9]|6[0-46-8]|7[02-79]",null],["(80)(\\\\d{5})","$1 $2","80",null],["(69\\\\d)(\\\\d{4,5})","$1 $2","69",null],["([235-7]\\\\d{2})(\\\\d{4})(\\\\d{3})","$1 $2 $3","2[0-489]|3[25]|5[01]|65|7[18]",null],["([89]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","8(?:68|8|9[89])|9",null],["(1[2689]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1(?:[26]|8[68]|99)",null],["(1[89]00)(\\\\d{4,6})","$1 $2","1[89]0","$FG"]]]', - 255: '["TZ","00[056]","0",null,null,"$NP$FG","\\\\d{7,9}","\\\\d{9}",[["([24]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[24]",null],["([67]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[67]",null],["([89]\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3","[89]",null]]]', - 222: '["MR","00",null,null,null,null,"\\\\d{8}","[2-48]\\\\d{7}",[["([2-48]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 230: '["MU","0(?:0|[2-7]0|33)",null,null,null,null,"\\\\d{7,8}","[2-9]\\\\d{6,7}",[["([2-46-9]\\\\d{2})(\\\\d{4})","$1 $2","[2-46-9]",null],["(5\\\\d{3})(\\\\d{4})","$1 $2","5",null]]]', - 592: '["GY","001",null,null,null,null,"\\\\d{7}","[2-46-9]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 41: '["CH","00","0",null,null,"$NP$FG","\\\\d{9}(?:\\\\d{3})?","[2-9]\\\\d{8}|860\\\\d{9}",[["([2-9]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[2-7]|[89]1",null],["([89]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","8[047]|90",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","860",null]]]', - 39: [ - '["IT","00",null,null,null,null,"\\\\d{6,11}","[01589]\\\\d{5,10}|3(?:[12457-9]\\\\d{8}|[36]\\\\d{7,9})",[["(\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","0[26]|55",null],["(0[26])(\\\\d{4})(\\\\d{5})","$1 $2 $3","0[26]",null],["(0[26])(\\\\d{4,6})","$1 $2","0[26]",null],["(0\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","0[13-57-9][0159]",null],["(\\\\d{3})(\\\\d{3,6})","$1 $2","0[13-57-9][0159]|8(?:03|4[17]|9[245])",null],["(0\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","0[13-57-9][2-46-8]",null],["(0\\\\d{3})(\\\\d{2,6})","$1 $2","0[13-57-9][2-46-8]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[13]|8(?:00|4[08]|9[59])",null],["(\\\\d{4})(\\\\d{4})","$1 $2","894",null],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","3",null]]]', - '["VA","00",null,null,null,null,"\\\\d{6,11}","(?:0(?:878\\\\d{5}|6698\\\\d{5})|[1589]\\\\d{5,10}|3(?:[12457-9]\\\\d{8}|[36]\\\\d{7,9}))"]', - ], - 993: '["TM","810","8",null,null,"($NP $FG)","\\\\d{8}","[1-6]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","12",null],["(\\\\d{2})(\\\\d{6})","$1 $2","6","$NP $FG"],["(\\\\d{3})(\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","13|[2-5]",null]]]', - 888: '["001",null,null,null,null,null,"\\\\d{11}","\\\\d{11}",[["(\\\\d{3})(\\\\d{3})(\\\\d{5})","$1 $2 $3",null,null]]]', - 353: '["IE","00","0",null,null,"($NP$FG)","\\\\d{5,10}","[124-9]\\\\d{6,9}",[["(1)(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d{2})(\\\\d{5})","$1 $2","2[24-9]|47|58|6[237-9]|9[35-9]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","40[24]|50[45]",null],["(48)(\\\\d{4})(\\\\d{4})","$1 $2 $3","48",null],["(818)(\\\\d{3})(\\\\d{3})","$1 $2 $3","81",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[24-69]|7[14]",null],["([78]\\\\d)(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","76|8[35-9]","$NP$FG"],["(700)(\\\\d{3})(\\\\d{3})","$1 $2 $3","70","$NP$FG"],["(\\\\d{4})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1(?:8[059]|5)","$FG"]]]', - 966: '["SA","00","0",null,null,"$NP$FG","\\\\d{7,10}","1\\\\d{7,8}|(?:[2-467]|92)\\\\d{7}|5\\\\d{8}|8\\\\d{9}",[["([1-467])(\\\\d{3})(\\\\d{4})","$1 $2 $3","[1-467]",null],["(1\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[1-467]",null],["(5\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","5",null],["(92\\\\d{2})(\\\\d{5})","$1 $2","92","$FG"],["(800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80","$FG"],["(811)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","81",null]]]', - 380: '["UA","00","0",null,null,"$NP$FG","\\\\d{5,9}","[3-9]\\\\d{8}",[["([3-9]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[38]9|4(?:[45][0-5]|87)|5(?:0|6[37]|7[37])|6[36-8]|7|9[1-9]",null],["([3-689]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","3[1-8]2|4[13678]2|5(?:[12457]2|6[24])|6(?:[49]2|[12][29]|5[24])|8[0-8]|90",null],["([3-6]\\\\d{3})(\\\\d{5})","$1 $2","3(?:5[013-9]|[1-46-8])|4(?:[137][013-9]|6|[45][6-9]|8[4-6])|5(?:[1245][013-9]|6[0135-9]|3|7[4-6])|6(?:[49][013-9]|5[0135-9]|[12][13-8])",null]]]', - 98: '["IR","00","0",null,null,"$NP$FG","\\\\d{4,10}","[1-8]\\\\d{9}|9(?:[0-4]\\\\d{8}|9\\\\d{2,8})",[["(21)(\\\\d{3,5})","$1 $2","21",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","[1-8]",null],["(\\\\d{3})(\\\\d{3})","$1 $2","9",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","9",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","9",null]]]', - 971: '["AE","00","0",null,null,"$NP$FG","\\\\d{5,12}","[2-79]\\\\d{7,8}|800\\\\d{2,9}",[["([2-4679])(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2-4679][2-8]",null],["(5\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","5",null],["([479]00)(\\\\d)(\\\\d{5})","$1 $2 $3","[479]0","$FG"],["([68]00)(\\\\d{2,9})","$1 $2","60|8","$FG"]]]', - 30: '["GR","00",null,null,null,null,"\\\\d{10}","[26-9]\\\\d{9}",[["([27]\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","21|7",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","2[2-9]1|[689]",null],["(2\\\\d{3})(\\\\d{6})","$1 $2","2[2-9][02-9]",null]]]', - 228: '["TG","00",null,null,null,null,"\\\\d{8}","[29]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[29]",null]]]', - 48: '["PL","00",null,null,null,null,"\\\\d{6,9}","[12]\\\\d{6,8}|[3-57-9]\\\\d{8}|6\\\\d{5,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[14]|2[0-57-9]|3[2-4]|5[24-689]|6[1-3578]|7[14-7]|8[1-79]|9[145]",null],["(\\\\d{2})(\\\\d{1})(\\\\d{4})","$1 $2 $3","[12]2",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","26|39|5[0137]|6[0469]|7[02389]|8[08]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","64",null],["(\\\\d{3})(\\\\d{3})","$1 $2","64",null]]]', - 886: '["TW","0(?:0[25679]|19)","0",null,null,"$NP$FG","\\\\d{7,10}","2\\\\d{6,8}|[3-689]\\\\d{7,8}|7\\\\d{7,9}",[["(20)(\\\\d)(\\\\d{4})","$1 $2 $3","202",null],["(20)(\\\\d{3})(\\\\d{4})","$1 $2 $3","20[013-9]",null],["([2-8])(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","2[23-8]|[3-6]|[78][1-9]",null],["([89]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","80|9",null],["(70)(\\\\d{4})(\\\\d{4})","$1 $2 $3","70",null]]]', - 212: [ - '["MA","00","0",null,null,"$NP$FG","\\\\d{9}","[5-9]\\\\d{8}",[["([5-7]\\\\d{2})(\\\\d{6})","$1-$2","5(?:2[015-7]|3[0-4])|[67]",null],["([58]\\\\d{3})(\\\\d{5})","$1-$2","5(?:2[2-489]|3[5-9]|92)|892",null],["(5\\\\d{4})(\\\\d{4})","$1-$2","5(?:29|38)",null],["([5]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","5(?:4[067]|5[03])",null],["(8[09])(\\\\d{7})","$1-$2","8(?:0|9[013-9])",null]]]', - '["EH","00","0",null,null,"$NP$FG","\\\\d{9}","[5-9]\\\\d{8}"]', - ], - 372: '["EE","00",null,null,null,null,"\\\\d{4,10}","1\\\\d{3,4}|[3-9]\\\\d{6,7}|800\\\\d{6,7}",[["([3-79]\\\\d{2})(\\\\d{4})","$1 $2","[369]|4[3-8]|5(?:[0-2]|5[0-478]|6[45])|7[1-9]",null],["(70)(\\\\d{2})(\\\\d{4})","$1 $2 $3","70",null],["(8000)(\\\\d{3})(\\\\d{3})","$1 $2 $3","800",null],["([458]\\\\d{3})(\\\\d{3,4})","$1 $2","40|5|8(?:00|[1-5])",null]]]', - 598: '["UY","0(?:1[3-9]\\\\d|0)","0",null,null,null,"\\\\d{7,8}","[2489]\\\\d{6,7}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[24]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","9[1-9]","$NP$FG"],["(\\\\d{3})(\\\\d{4})","$1 $2","[89]0","$NP$FG"]]]', - 502: '["GT","00",null,null,null,null,"\\\\d{8}(?:\\\\d{3})?","[2-7]\\\\d{7}|1[89]\\\\d{9}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[2-7]",null],["(\\\\d{4})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null]]]', - 82: '["KR","00(?:[124-68]|3\\\\d{2}|7(?:[0-8]\\\\d|9[0-79]))","0","0(8[1-46-8]|85\\\\d{2})?",null,"$NP$FG","\\\\d{3,14}","007\\\\d{9,11}|[1-7]\\\\d{3,9}|8\\\\d{8}",[["(\\\\d{5})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","00798","$FG","NA"],["(\\\\d{5})(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3 $4","00798","$FG","NA"],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1-$2-$3","1(?:0|1[19]|[69]9|5[458])|[57]0",null],["(\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1-$2-$3","1(?:[01]|5[1-4]|6[2-8]|[7-9])|[68]0|[3-6][1-9][1-9]",null],["(\\\\d{3})(\\\\d)(\\\\d{4})","$1-$2-$3","131",null],["(\\\\d{3})(\\\\d{2})(\\\\d{4})","$1-$2-$3","131",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1-$2-$3","13[2-9]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3-$4","30",null],["(\\\\d)(\\\\d{3,4})(\\\\d{4})","$1-$2-$3","2[1-9]",null],["(\\\\d)(\\\\d{3,4})","$1-$2","21[0-46-9]",null],["(\\\\d{2})(\\\\d{3,4})","$1-$2","[3-6][1-9]1",null],["(\\\\d{4})(\\\\d{4})","$1-$2","1(?:5[246-9]|6[04678]|8[03579])","$FG"]]]', - 253: '["DJ","00",null,null,null,null,"\\\\d{8}","[27]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 91: '["IN","00","0",null,null,"$NP$FG","\\\\d{6,13}","008\\\\d{9}|1\\\\d{7,12}|[2-9]\\\\d{9,10}",[["(\\\\d{5})(\\\\d{5})","$1 $2","600|7(?:[02-8]|19|9[037-9])|8(?:0[015-9]|[1-9]|20)|9",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","11|2[02]|33|4[04]|79[1-9]|80[2-46]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1(?:2[0-249]|3[0-25]|4[145]|[59][14]|7[1257]|[68][1-9])|2(?:1[257]|3[013]|4[01]|5[0137]|6[0158]|78|8[1568]|9[14])|3(?:26|4[1-3]|5[34]|6[01489]|7[02-46]|8[159])|4(?:1[36]|2[1-47]|3[15]|5[12]|6[0-26-9]|7[0-24-9]|8[013-57]|9[014-7])|5(?:1[025]|[36][25]|22|4[28]|5[12]|[78]1|9[15])|6(?:12|[2-4]1|5[17]|6[13]|7[14]|80)|7(?:12|2[14]|3[134]|4[47]|5[15]|[67]1|88)|8(?:16|2[014]|3[126]|6[136]|7[078]|8[34]|91)",null],["(\\\\d{4})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1(?:[23579]|[468][1-9])|[2-8]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})(\\\\d{3})","$1 $2 $3 $4","008",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","140","$FG"],["(\\\\d{4})(\\\\d{2})(\\\\d{4})","$1 $2 $3","160","$FG"],["(\\\\d{4})(\\\\d{4,5})","$1 $2","180","$FG"],["(\\\\d{4})(\\\\d{2,4})(\\\\d{4})","$1 $2 $3","180","$FG"],["(\\\\d{4})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","186","$FG"],["(\\\\d{4})(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","18[06]","$FG"]]]', - 389: '["MK","00","0",null,null,"$NP$FG","\\\\d{6,8}","[2-578]\\\\d{7}",[["(2)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2",null],["([347]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[347]",null],["([58]\\\\d{2})(\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[58]",null]]]', - 1: [ - '["US","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2-9]\\\\d{9}",[["(\\\\d{3})(\\\\d{4})","$1-$2",null,null,"NA"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","($1) $2-$3",null,null,"$1-$2-$3"]]]', - '["AI","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', - '["AS","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', - '["BB","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', - '["BM","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[4589]\\\\d{9}"]', - '["BS","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', - '["CA","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2-9]\\\\d{9}|3\\\\d{6}"]', - '["DM","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[57-9]\\\\d{9}"]', - '["DO","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', - '["GD","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[4589]\\\\d{9}"]', - '["GU","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', - '["JM","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', - '["KN","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', - '["KY","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[3589]\\\\d{9}"]', - '["LC","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', - '["MP","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', - '["MS","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', - '["PR","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', - '["SX","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', - '["TC","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', - '["TT","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', - '["AG","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', - '["VC","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', - '["VG","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', - '["VI","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[3589]\\\\d{9}"]', - ], - 60: '["MY","00","0",null,null,null,"\\\\d{6,10}","[13-9]\\\\d{7,9}",[["([4-79])(\\\\d{3})(\\\\d{4})","$1-$2 $3","[4-79]","$NP$FG"],["(3)(\\\\d{4})(\\\\d{4})","$1-$2 $3","3","$NP$FG"],["([18]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1-$2 $3","1[02-46-9][1-9]|8","$NP$FG"],["(1)([36-8]00)(\\\\d{2})(\\\\d{4})","$1-$2-$3-$4","1[36-8]0",null],["(11)(\\\\d{4})(\\\\d{4})","$1-$2 $3","11","$NP$FG"],["(15[49])(\\\\d{3})(\\\\d{4})","$1-$2 $3","15","$NP$FG"]]]', - 355: '["AL","00","0",null,null,"$NP$FG","\\\\d{5,9}","[2-57]\\\\d{7}|6\\\\d{8}|8\\\\d{5,7}|9\\\\d{5}",[["(4)(\\\\d{3})(\\\\d{4})","$1 $2 $3","4[0-6]",null],["(6\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","6",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2358][2-5]|4[7-9]",null],["(\\\\d{3})(\\\\d{3,5})","$1 $2","[235][16-9]|8[016-9]|[79]",null]]]', - 254: '["KE","000","0","005|0",null,"$NP$FG","\\\\d{7,10}","20\\\\d{6,7}|[4-9]\\\\d{6,9}",[["(\\\\d{2})(\\\\d{5,7})","$1 $2","[24-6]",null],["(\\\\d{3})(\\\\d{6})","$1 $2","7",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[89]",null]]]', - 223: '["ML","00",null,null,null,null,"\\\\d{8}","[246-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[246-9]",null],["(\\\\d{4})","$1","67|74",null,"NA"]]]', - 686: '["KI","00",null,"0",null,null,"\\\\d{5,8}","[2458]\\\\d{4}|3\\\\d{4,7}|7\\\\d{7}"]', - 994: '["AZ","00","0",null,null,"($NP$FG)","\\\\d{7,9}","[1-9]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","(?:1[28]|2(?:[45]2|[0-36])|365)",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[4-8]","$NP$FG"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","9","$NP$FG"]]]', - 979: '["001",null,null,null,null,null,"\\\\d{9}","\\\\d{9}",[["(\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3",null,null]]]', - 66: '["TH","00","0",null,null,"$NP$FG","\\\\d{4}|\\\\d{8,10}","[2-9]\\\\d{7,8}|1\\\\d{3}(?:\\\\d{5,6})?",[["(2)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2",null],["([13-9]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","14|[3-9]",null],["(1[89]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1","$FG"]]]', - 233: '["GH","00","0",null,null,"$NP$FG","\\\\d{7,9}","[235]\\\\d{8}|8\\\\d{7}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[235]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","8",null]]]', - 593: '["EC","00","0",null,null,"($NP$FG)","\\\\d{7,11}","1\\\\d{9,10}|[2-8]\\\\d{7}|9\\\\d{8}",[["(\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2-$3","[247]|[356][2-8]",null,"$1-$2-$3"],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","9","$NP$FG"],["(1800)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","1","$FG"]]]', - 509: '["HT","00",null,null,null,null,"\\\\d{8}","[2-489]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3",null,null]]]', - 54: '["AR","00","0","0?(?:(11|2(?:2(?:02?|[13]|2[13-79]|4[1-6]|5[2457]|6[124-8]|7[1-4]|8[13-6]|9[1267])|3(?:02?|1[467]|2[03-6]|3[13-8]|[49][2-6]|5[2-8]|[67])|4(?:7[3-578]|9)|6(?:[0136]|2[24-6]|4[6-8]?|5[15-8])|80|9(?:0[1-3]|[19]|2\\\\d|3[1-6]|4[02568]?|5[2-4]|6[2-46]|72?|8[23]?))|3(?:3(?:2[79]|6|8[2578])|4(?:0[0-24-9]|[12]|3[5-8]?|4[24-7]|5[4-68]?|6[02-9]|7[126]|8[2379]?|9[1-36-8])|5(?:1|2[1245]|3[237]?|4[1-46-9]|6[2-4]|7[1-6]|8[2-5]?)|6[24]|7(?:[069]|1[1568]|2[15]|3[145]|4[13]|5[14-8]|7[2-57]|8[126])|8(?:[01]|2[15-7]|3[2578]?|4[13-6]|5[4-8]?|6[1-357-9]|7[36-8]?|8[5-8]?|9[124])))?15)?","9$1","$NP$FG","\\\\d{6,11}","11\\\\d{8}|[2368]\\\\d{9}|9\\\\d{10}",[["([68]\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3","[68]",null],["(\\\\d{2})(\\\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\\\d{3})(\\\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\\\d{4})(\\\\d{4})","$1-$2","[2-9]","$FG","NA"],["(9)(11)(\\\\d{4})(\\\\d{4})","$2 15-$3-$4","911",null,"$1 $2 $3-$4"],["(9)(\\\\d{3})(\\\\d{3})(\\\\d{4})","$2 15-$3-$4","9(?:2[234689]|3[3-8])",null,"$1 $2 $3-$4"],["(9)(\\\\d{4})(\\\\d{2})(\\\\d{4})","$2 15-$3-$4","9[23]",null,"$1 $2 $3-$4"],["(11)(\\\\d{4})(\\\\d{4})","$1 $2-$3","1",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2-$3","2(?:2[013]|3[067]|49|6[01346]|80|9[147-9])|3(?:36|4[1-358]|5[138]|6[24]|7[069]|8[013578])",null],["(\\\\d{4})(\\\\d{2})(\\\\d{4})","$1 $2-$3","[23]",null],["(\\\\d{3})","$1","1[012]|911","$FG","NA"]]]', - 57: '["CO","00(?:4(?:[14]4|56)|[579])","0","0([3579]|4(?:44|56))?",null,null,"\\\\d{7,11}","(?:[13]\\\\d{0,3}|[24-8])\\\\d{7}",[["(\\\\d)(\\\\d{7})","$1 $2","1(?:8[2-9]|9[0-3]|[2-7])|[24-8]","($FG)"],["(\\\\d{3})(\\\\d{7})","$1 $2","3",null],["(1)(\\\\d{3})(\\\\d{7})","$1-$2-$3","1(?:80|9[04])","$NP$FG","$1 $2 $3"]]]', - 597: '["SR","00",null,null,null,null,"\\\\d{6,7}","[2-8]\\\\d{5,6}",[["(\\\\d{3})(\\\\d{3})","$1-$2","[2-4]|5[2-58]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1-$2-$3","56",null],["(\\\\d{3})(\\\\d{4})","$1-$2","[6-8]",null]]]', - 676: '["TO","00",null,null,null,null,"\\\\d{5,7}","[02-8]\\\\d{4,6}",[["(\\\\d{2})(\\\\d{3})","$1-$2","[1-6]|7[0-4]|8[05]",null],["(\\\\d{3})(\\\\d{4})","$1 $2","7[5-9]|8[47-9]",null],["(\\\\d{4})(\\\\d{3})","$1 $2","0",null]]]', - 505: '["NI","00",null,null,null,null,"\\\\d{8}","[12578]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', - 850: '["KP","00|99","0",null,null,"$NP$FG","\\\\d{6,8}|\\\\d{10}","1\\\\d{9}|[28]\\\\d{7}",[["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","8",null]]]', - 7: [ - '["RU","810","8",null,null,"$NP ($FG)","\\\\d{10}","[3489]\\\\d{9}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1-$2-$3","[1-79]","$FG","NA"],["([3489]\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","[34689]",null],["(7\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","7",null]]]', - '["KZ","810","8",null,null,null,"\\\\d{10}","(?:33\\\\d|7\\\\d{2}|80[09])\\\\d{7}"]', - ], - 268: '["SZ","00",null,null,null,null,"\\\\d{8}","[027]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[027]",null]]]', - 501: '["BZ","00",null,null,null,null,"\\\\d{7}(?:\\\\d{4})?","[2-8]\\\\d{6}|0\\\\d{10}",[["(\\\\d{3})(\\\\d{4})","$1-$2","[2-8]",null],["(0)(800)(\\\\d{4})(\\\\d{3})","$1-$2-$3-$4","0",null]]]', - 252: '["SO","00","0",null,null,null,"\\\\d{6,9}","[1-9]\\\\d{5,8}",[["(\\\\d{6})","$1","[134]",null],["(\\\\d)(\\\\d{6})","$1 $2","2[0-79]|[13-5]",null],["(\\\\d)(\\\\d{7})","$1 $2","24|[67]",null],["(\\\\d{2})(\\\\d{4})","$1 $2","8[125]",null],["(\\\\d{2})(\\\\d{5,7})","$1 $2","15|28|6[1-35-9]|799|9[2-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","3[59]|4[89]|6[24-6]|79|8[08]|90",null]]]', - 229: '["BJ","00",null,null,null,null,"\\\\d{4,8}","[2689]\\\\d{7}|7\\\\d{3}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 680: '["PW","01[12]",null,null,null,null,"\\\\d{7}","[2-8]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 263: '["ZW","00","0",null,null,"$NP$FG","\\\\d{3,10}","2(?:[012457-9]\\\\d{3,8}|6(?:[14]\\\\d{7}|\\\\d{4}))|[13-79]\\\\d{4,9}|8[06]\\\\d{8}",[["([49])(\\\\d{3})(\\\\d{2,4})","$1 $2 $3","4|9[2-9]",null],["(7\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","7",null],["(86\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","86[24]",null],["([2356]\\\\d{2})(\\\\d{3,5})","$1 $2","2(?:0[45]|2[278]|[49]8|[78])|3(?:08|17|3[78]|7[1569]|8[37]|98)|5[15][78]|6(?:[29]8|[38]7|6[78]|75|[89]8)",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2(?:1[39]|2[0157]|6[14]|7[35]|84)|329",null],["([1-356]\\\\d)(\\\\d{3,5})","$1 $2","1[3-9]|2[0569]|3[0-69]|5[05689]|6[0-46-9]",null],["([235]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[23]9|54",null],["([25]\\\\d{3})(\\\\d{3,5})","$1 $2","(?:25|54)8",null],["(8\\\\d{3})(\\\\d{6})","$1 $2","86",null],["(80\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80",null]]]', - 90: '["TR","00","0",null,null,null,"\\\\d{7,10}","[2-589]\\\\d{9}|444\\\\d{4}",[["(\\\\d{3})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[23]|4(?:[0-35-9]|4[0-35-9])","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","5[02-69]","$NP$FG"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","51|[89]","$NP$FG"],["(444)(\\\\d{1})(\\\\d{3})","$1 $2 $3","444",null]]]', - 352: '["LU","00",null,"(15(?:0[06]|1[12]|35|4[04]|55|6[26]|77|88|99)\\\\d)",null,null,"\\\\d{4,11}","[24-9]\\\\d{3,10}|3(?:[0-46-9]\\\\d{2,9}|5[013-9]\\\\d{1,8})",[["(\\\\d{2})(\\\\d{3})","$1 $2","[2-5]|7[1-9]|[89](?:[1-9]|0[2-9])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3","[2-5]|7[1-9]|[89](?:[1-9]|0[2-9])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","20",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{1,2})","$1 $2 $3 $4","2(?:[0367]|4[3-8])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","20",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{1,2})","$1 $2 $3 $4 $5","2(?:[0367]|4[3-8])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{1,4})","$1 $2 $3 $4","2(?:[12589]|4[12])|[3-5]|7[1-9]|8(?:[1-9]|0[2-9])|9(?:[1-9]|0[2-46-9])",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","70|80[01]|90[015]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","6",null]]]', - 47: [ - '["NO","00",null,null,null,null,"\\\\d{5}(?:\\\\d{3})?","0\\\\d{4}|[2-9]\\\\d{7}",[["([489]\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","[489]",null],["([235-7]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[235-7]",null]]]', - '["SJ","00",null,null,null,null,"\\\\d{5}(?:\\\\d{3})?","0\\\\d{4}|[45789]\\\\d{7}"]', - ], - 243: '["CD","00","0",null,null,"$NP$FG","\\\\d{7,9}","[2-6]\\\\d{6}|[18]\\\\d{6,8}|9\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","12",null],["([89]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","8[0-2459]|9",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","88",null],["(\\\\d{2})(\\\\d{5})","$1 $2","[1-6]",null]]]', - 220: '["GM","00",null,null,null,null,"\\\\d{7}","[2-9]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 687: '["NC","00",null,null,null,null,"\\\\d{6}","[2-57-9]\\\\d{5}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1.$2.$3","[2-46-9]|5[0-4]",null]]]', - 995: '["GE","00","0",null,null,null,"\\\\d{6,9}","[34578]\\\\d{8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[348]","$NP$FG"],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","7","$NP$FG"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","5","$FG"]]]', - 961: '["LB","00","0",null,null,null,"\\\\d{7,8}","[13-9]\\\\d{6,7}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[13-6]|7(?:[2-57]|62|8[0-7]|9[04-9])|8[02-9]|9","$NP$FG"],["([7-9]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[89][01]|7(?:[01]|6[013-9]|8[89]|9[1-3])",null]]]', - 40: '["RO","00","0",null,null,"$NP$FG","\\\\d{6,9}","[23]\\\\d{5,8}|[7-9]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[23]1",null],["(\\\\d{2})(\\\\d{4})","$1 $2","[23]1",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[23][3-7]|[7-9]",null],["(2\\\\d{2})(\\\\d{3})","$1 $2","2[3-6]",null]]]', - 232: '["SL","00","0",null,null,"($NP$FG)","\\\\d{6,8}","[2-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{6})","$1 $2",null,null]]]', - 594: '["GF","00","0",null,null,"$NP$FG","\\\\d{9}","[56]\\\\d{8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 976: '["MN","001","0",null,null,"$NP$FG","\\\\d{6,10}","[12]\\\\d{7,9}|[57-9]\\\\d{7}",[["([12]\\\\d)(\\\\d{2})(\\\\d{4})","$1 $2 $3","[12]1",null],["([12]2\\\\d)(\\\\d{5,6})","$1 $2","[12]2[1-3]",null],["([12]\\\\d{3})(\\\\d{5})","$1 $2","[12](?:27|[3-5])",null],["(\\\\d{4})(\\\\d{4})","$1 $2","[57-9]","$FG"],["([12]\\\\d{4})(\\\\d{4,5})","$1 $2","[12](?:27|[3-5])",null]]]', - 20: '["EG","00","0",null,null,"$NP$FG","\\\\d{5,10}","1\\\\d{4,9}|[2456]\\\\d{8}|3\\\\d{7}|[89]\\\\d{8,9}",[["(\\\\d)(\\\\d{7,8})","$1 $2","[23]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[012]|[89]00",null],["(\\\\d{2})(\\\\d{6,7})","$1 $2","1[35]|[4-6]|[89][2-9]",null]]]', - 689: '["PF","00",null,null,null,null,"\\\\d{6}(?:\\\\d{2})?","4\\\\d{5,7}|8\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","4[09]|8[79]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3","44",null]]]', - 56: '["CL","(?:0|1(?:1[0-69]|2[0-57]|5[13-58]|69|7[0167]|8[018]))0","0","0|(1(?:1[0-69]|2[0-57]|5[13-58]|69|7[0167]|8[018]))",null,"$NP$FG","\\\\d{7,11}","(?:[2-9]|600|123)\\\\d{7,8}",[["(\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","2[23]","($FG)"],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[357]|4[1-35]|6[13-57]","($FG)"],["(9)(\\\\d{4})(\\\\d{4})","$1 $2 $3","9",null],["(44)(\\\\d{3})(\\\\d{4})","$1 $2 $3","44",null],["([68]00)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","60|8","$FG"],["(600)(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","60","$FG"],["(1230)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1","$FG"],["(\\\\d{5})(\\\\d{4})","$1 $2","219","($FG)"],["(\\\\d{4,5})","$1","[1-9]","$FG","NA"]]]', - 596: '["MQ","00","0",null,null,"$NP$FG","\\\\d{9}","[56]\\\\d{8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 508: '["PM","00","0",null,null,"$NP$FG","\\\\d{6}","[45]\\\\d{5}",[["([45]\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', - 269: '["KM","00",null,null,null,null,"\\\\d{7}","[3478]\\\\d{6}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', - 358: [ - '["FI","00|99(?:[02469]|5(?:11|33|5[59]|88|9[09]))","0",null,null,"$NP$FG","\\\\d{5,12}","1\\\\d{4,11}|[2-9]\\\\d{4,10}",[["(\\\\d{3})(\\\\d{3,7})","$1 $2","(?:[1-3]00|[6-8]0)",null],["(116\\\\d{3})","$1","116","$FG"],["(\\\\d{2})(\\\\d{4,10})","$1 $2","[14]|2[09]|50|7[135]",null],["(\\\\d)(\\\\d{4,11})","$1 $2","[25689][1-8]|3",null]]]', - '["AX","00|99(?:[02469]|5(?:11|33|5[59]|88|9[09]))","0",null,null,"$NP$FG","\\\\d{5,12}","1\\\\d{5,11}|[35]\\\\d{5,9}|[27]\\\\d{4,9}|4\\\\d{5,10}|6\\\\d{7,9}|8\\\\d{6,9}"]', - ], - 251: '["ET","00","0",null,null,"$NP$FG","\\\\d{7,9}","[1-59]\\\\d{8}",[["([1-59]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3",null,null]]]', - 681: '["WF","00",null,null,null,null,"\\\\d{6}","[4-8]\\\\d{5}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', - 853: '["MO","00",null,null,null,null,"\\\\d{8}","[268]\\\\d{7}",[["([268]\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', - 44: [ - '["GB","00","0",null,null,"$NP$FG","\\\\d{4,10}","\\\\d{7,10}",[["(7\\\\d{3})(\\\\d{6})","$1 $2","7(?:[1-5789]|62)",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","2|5[56]|7[06]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1(?:1|\\\\d1)|3|9[018]",null],["(\\\\d{5})(\\\\d{4,5})","$1 $2","1(?:38|5[23]|69|76|94)",null],["(1\\\\d{3})(\\\\d{5,6})","$1 $2","1",null],["(800)(\\\\d{4})","$1 $2","800",null],["(845)(46)(4\\\\d)","$1 $2 $3","845",null],["(8\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","8(?:4[2-5]|7[0-3])",null],["(80\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80",null],["([58]00)(\\\\d{6})","$1 $2","[58]00",null]]]', - '["GG","00","0",null,null,"$NP$FG","\\\\d{6,10}","[135789]\\\\d{6,9}"]', - '["IM","00","0",null,null,"$NP$FG","\\\\d{6,10}","[135789]\\\\d{6,9}"]', - '["JE","00","0",null,null,"$NP$FG","\\\\d{6,10}","[135789]\\\\d{6,9}"]', - ], - 244: '["AO","00",null,null,null,null,"\\\\d{9}","[29]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', - 211: '["SS","00","0",null,null,null,"\\\\d{9}","[19]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,"$NP$FG"]]]', - 373: '["MD","00","0",null,null,"$NP$FG","\\\\d{8}","[235-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","22|3",null],["([25-7]\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","2[13-9]|[5-7]",null],["([89]\\\\d{2})(\\\\d{5})","$1 $2","[89]",null]]]', - 996: '["KG","00","0",null,null,"$NP$FG","\\\\d{5,10}","[235-8]\\\\d{8,9}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[25-7]|31[25]",null],["(\\\\d{4})(\\\\d{5})","$1 $2","3(?:1[36]|[2-9])",null],["(\\\\d{3})(\\\\d{3})(\\\\d)(\\\\d{3})","$1 $2 $3 $4","8",null]]]', - 93: '["AF","00","0",null,null,"$NP$FG","\\\\d{7,9}","[2-7]\\\\d{8}",[["([2-7]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2-7]",null]]]', - 260: '["ZM","00","0",null,null,"$NP$FG","\\\\d{9}","[289]\\\\d{8}",[["([29]\\\\d)(\\\\d{7})","$1 $2","[29]",null],["(800)(\\\\d{3})(\\\\d{3})","$1 $2 $3","8",null]]]', - 378: '["SM","00",null,"(?:0549)?([89]\\\\d{5})","0549$1",null,"\\\\d{6,10}","[05-7]\\\\d{7,9}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[5-7]",null],["(0549)(\\\\d{6})","$1 $2","0",null,"($1) $2"],["(\\\\d{6})","0549 $1","[89]",null,"(0549) $1"]]]', - 235: '["TD","00|16",null,null,null,null,"\\\\d{8}","[2679]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', - 960: '["MV","0(?:0|19)",null,null,null,null,"\\\\d{7,10}","[346-8]\\\\d{6,9}|9(?:00\\\\d{7}|\\\\d{6})",[["(\\\\d{3})(\\\\d{4})","$1-$2","[3467]|9(?:[1-9]|0[1-9])",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[89]00",null]]]', - 221: '["SN","00",null,null,null,null,"\\\\d{9}","[3789]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[379]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","8",null]]]', - 595: '["PY","00","0",null,null,null,"\\\\d{5,9}","5[0-5]\\\\d{4,7}|[2-46-9]\\\\d{5,8}",[["(\\\\d{2})(\\\\d{5})","$1 $2","(?:[26]1|3[289]|4[124678]|7[123]|8[1236])","($NP$FG)"],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","(?:[26]1|3[289]|4[124678]|7[123]|8[1236])","($NP$FG)"],["(\\\\d{3})(\\\\d{3,6})","$1 $2","[2-9]0","$NP$FG"],["(\\\\d{3})(\\\\d{6})","$1 $2","9[1-9]","$NP$FG"],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","8700",null],["(\\\\d{3})(\\\\d{4,5})","$1 $2","[2-8][1-9]","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-8][1-9]","$NP$FG"]]]', - 977: '["NP","00","0",null,null,"$NP$FG","\\\\d{6,10}","[1-8]\\\\d{7}|9(?:[1-69]\\\\d{6,8}|7[2-6]\\\\d{5,7}|8\\\\d{8})",[["(1)(\\\\d{7})","$1-$2","1[2-6]",null],["(\\\\d{2})(\\\\d{6})","$1-$2","1[01]|[2-8]|9(?:[1-69]|7[15-9])",null],["(9\\\\d{2})(\\\\d{7})","$1-$2","9(?:6[013]|7[245]|8)","$FG"]]]', - 36: '["HU","00","06",null,null,"($FG)","\\\\d{6,9}","[1-9]\\\\d{7,8}",[["(1)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[2-9]",null]]]', -}; diff --git a/toolkit/components/formautofill/phonenumberutils/PhoneNumberNormalizer.sys.mjs b/toolkit/components/formautofill/phonenumberutils/PhoneNumberNormalizer.sys.mjs deleted file mode 100644 index 604eefe314..0000000000 --- a/toolkit/components/formautofill/phonenumberutils/PhoneNumberNormalizer.sys.mjs +++ /dev/null @@ -1,67 +0,0 @@ -/* This Source Code Form is subject to the terms of the Apache License, Version - * 2.0. If a copy of the Apache License was not distributed with this file, You - * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */ - -// This library came from https://github.com/andreasgal/PhoneNumber.js but will -// be further maintained by our own in Form Autofill codebase. - -export var 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 - 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 ""; - } - - number = number.replace(UNICODE_DIGITS, function (ch) { - return String.fromCharCode(48 + (ch.charCodeAt(0) & 0xf)); - }); - if (!numbersOnly) { - number = number.replace(VALID_ALPHA_PATTERN, 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, - }; -})(); diff --git a/toolkit/components/formautofill/shared/AddressComponent.sys.mjs b/toolkit/components/formautofill/shared/AddressComponent.sys.mjs index a849e889b2..40e00b66a0 100644 --- a/toolkit/components/formautofill/shared/AddressComponent.sys.mjs +++ b/toolkit/components/formautofill/shared/AddressComponent.sys.mjs @@ -11,9 +11,9 @@ ChromeUtils.defineESModuleGetters(lazy, { FormAutofillNameUtils: "resource://gre/modules/shared/FormAutofillNameUtils.sys.mjs", FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs", - PhoneNumber: "resource://autofill/phonenumberutils/PhoneNumber.sys.mjs", + PhoneNumber: "resource://gre/modules/shared/PhoneNumber.sys.mjs", PhoneNumberNormalizer: - "resource://autofill/phonenumberutils/PhoneNumberNormalizer.sys.mjs", + "resource://gre/modules/shared/PhoneNumberNormalizer.sys.mjs", }); /** @@ -201,7 +201,7 @@ class StreetAddress extends AddressField { super(value, region); this.#structuredStreetAddress = lazy.AddressParser.parseStreetAddress( - lazy.AddressParser.replaceControlCharacters(this.userValue, " ") + lazy.AddressParser.replaceControlCharacters(this.userValue) ); } @@ -491,7 +491,7 @@ class Country extends AddressField { return this.country_code == other.country_code; } - contains(other) { + contains(_other) { return false; } @@ -841,7 +841,7 @@ class Email extends AddressField { ); } - contains(other) { + contains(_other) { return false; } diff --git a/toolkit/components/formautofill/shared/AddressParser.sys.mjs b/toolkit/components/formautofill/shared/AddressParser.sys.mjs index 5cb76934c1..1d36b71bba 100644 --- a/toolkit/components/formautofill/shared/AddressParser.sys.mjs +++ b/toolkit/components/formautofill/shared/AddressParser.sys.mjs @@ -271,7 +271,7 @@ export class AddressParser { return s?.replace(/[.,\/#!$%\^&\*;:{}=\-_~()]/g, ""); } - static replaceControlCharacters(s, replace) { + static replaceControlCharacters(s) { return s?.replace(/[\t\n\r]/g, " "); } diff --git a/toolkit/components/formautofill/shared/AddressRecord.sys.mjs b/toolkit/components/formautofill/shared/AddressRecord.sys.mjs new file mode 100644 index 0000000000..599a802dcd --- /dev/null +++ b/toolkit/components/formautofill/shared/AddressRecord.sys.mjs @@ -0,0 +1,119 @@ +/* eslint-disable no-useless-concat */ +/* 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/. */ + +import { FormAutofillNameUtils } from "resource://gre/modules/shared/FormAutofillNameUtils.sys.mjs"; +import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs"; +import { PhoneNumber } from "resource://gre/modules/shared/PhoneNumber.sys.mjs"; +import { FormAutofill } from "resource://autofill/FormAutofill.sys.mjs"; + +/** + * The AddressRecord class serves to handle and normalize internal address records. + * AddressRecord is used for processing and consistent data representation. + */ +export class AddressRecord { + static NAME_COMPONENTS = ["given-name", "additional-name", "family-name"]; + + static STREET_ADDRESS_COMPONENTS = [ + "address-line1", + "address-line2", + "address-line3", + ]; + static TEL_COMPONENTS = [ + "tel-country-code", + "tel-national", + "tel-area-code", + "tel-local", + "tel-local-prefix", + "tel-local-suffix", + ]; + + static computeFields(address) { + this.#computeNameFields(address); + this.#computeAddressLineFields(address); + this.#computeCountryFields(address); + this.#computeTelFields(address); + } + + static #computeNameFields(address) { + // Compute split names + if (!("given-name" in address)) { + const nameParts = FormAutofillNameUtils.splitName(address.name); + address["given-name"] = nameParts.given; + address["additional-name"] = nameParts.middle; + address["family-name"] = nameParts.family; + } + } + + static #computeAddressLineFields(address) { + // Compute address lines + if (!("address-line1" in address)) { + let streetAddress = []; + if (address["street-address"]) { + streetAddress = address["street-address"] + .split("\n") + .map(s => s.trim()); + } + for (let i = 0; i < 3; i++) { + address[`address-line${i + 1}`] = streetAddress[i] || ""; + } + if (streetAddress.length > 3) { + address["address-line3"] = FormAutofillUtils.toOneLineAddress( + streetAddress.slice(2) + ); + } + } + } + + static #computeCountryFields(address) { + // Compute country name + if (!("country-name" in address)) { + address["country-name"] = + FormAutofill.countries.get(address.country) ?? ""; + } + } + + static #computeTelFields(address) { + // Compute tel + if (!("tel-national" in address)) { + if (address.tel) { + let tel = PhoneNumber.Parse( + address.tel, + address.country || FormAutofill.DEFAULT_REGION + ); + if (tel) { + if (tel.countryCode) { + address["tel-country-code"] = tel.countryCode; + } + if (tel.nationalNumber) { + address["tel-national"] = tel.nationalNumber; + } + + // PhoneNumberUtils doesn't support parsing the components of a telephone + // number so we hard coded the parser for US numbers only. We will need + // to figure out how to parse numbers from other regions when we support + // new countries in the future. + if (tel.nationalNumber && tel.countryCode == "+1") { + let telComponents = tel.nationalNumber.match( + /(\d{3})((\d{3})(\d{4}))$/ + ); + if (telComponents) { + address["tel-area-code"] = telComponents[1]; + address["tel-local"] = telComponents[2]; + address["tel-local-prefix"] = telComponents[3]; + address["tel-local-suffix"] = telComponents[4]; + } + } + } else { + // Treat "tel" as "tel-national" directly if it can't be parsed. + address["tel-national"] = address.tel; + } + } + + this.TEL_COMPONENTS.forEach(c => { + address[c] = address[c] || ""; + }); + } + } +} diff --git a/toolkit/components/formautofill/shared/AutofillTelemetry.sys.mjs b/toolkit/components/formautofill/shared/AutofillTelemetry.sys.mjs new file mode 100644 index 0000000000..6a1fa974cc --- /dev/null +++ b/toolkit/components/formautofill/shared/AutofillTelemetry.sys.mjs @@ -0,0 +1,629 @@ +/* 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/. */ + +import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs"; +import { FormAutofillCreditCardSection } from "resource://gre/modules/shared/FormAutofillSection.sys.mjs"; + +const { FIELD_STATES } = FormAutofillUtils; + +class AutofillTelemetryBase { + SUPPORTED_FIELDS = {}; + + EVENT_CATEGORY = null; + EVENT_OBJECT_FORM_INTERACTION = null; + + SCALAR_DETECTED_SECTION_COUNT = null; + SCALAR_SUBMITTED_SECTION_COUNT = null; + + HISTOGRAM_NUM_USES = null; + HISTOGRAM_PROFILE_NUM_USES = null; + HISTOGRAM_PROFILE_NUM_USES_KEY = null; + + #initFormEventExtra(value) { + let extra = {}; + for (const field of Object.values(this.SUPPORTED_FIELDS)) { + extra[field] = value; + } + return extra; + } + + #setFormEventExtra(extra, key, value) { + if (!this.SUPPORTED_FIELDS[key]) { + return; + } + + extra[this.SUPPORTED_FIELDS[key]] = value; + } + + /** + * Building the extra keys object that is included in the Legacy Telemetry event `cc_form_v2` + * or `address_form` event and the Glean event `cc_form`, and `address_form`. + * It indicates the detected credit card or address fields and which method (autocomplete property, regular expression heuristics or fathom) identified them. + * + * @param {object} section Using section.fieldDetails to extract which fields were identified and how + * @param {string} undetected Default value when a field is not detected: 'undetected' (Glean) and 'false' in (Legacy) + * @param {string} autocomplete Value when a field is identified with autocomplete property: 'autocomplete' (Glean), 'true' (Legacy) + * @param {string} regexp Value when a field is identified with regex expression heuristics: 'regexp' (Glean), '0' (Legacy) + * @param {boolean} includeMultiPart Include multi part data or not + * @returns {object} Extra keys to include in the form event + */ + #buildFormDetectedEventExtra( + section, + undetected, + autocomplete, + regexp, + includeMultiPart + ) { + let extra = this.#initFormEventExtra(undetected); + + let identified = new Set(); + section.fieldDetails.forEach(detail => { + identified.add(detail.fieldName); + + if (detail.reason == "autocomplete") { + this.#setFormEventExtra(extra, detail.fieldName, autocomplete); + } else { + // confidence exists only when a field is identified by fathom. + let confidence = + detail.confidence > 0 ? Math.floor(100 * detail.confidence) / 100 : 0; + + this.#setFormEventExtra( + extra, + detail.fieldName, + confidence ? confidence.toString() : regexp + ); + } + + if ( + detail.fieldName === "cc-number" && + this.SUPPORTED_FIELDS[detail.fieldName] && + includeMultiPart + ) { + extra.cc_number_multi_parts = detail.part ?? 1; + } + }); + return extra; + } + + recordFormDetected(section) { + this.recordFormEvent( + "detected", + section.flowId, + this.#buildFormDetectedEventExtra(section, "false", "true", "0", false) + ); + + this.recordGleanFormEvent( + "formDetected", + section.flowId, + this.#buildFormDetectedEventExtra( + section, + "undetected", + "autocomplete", + "regexp", + true + ) + ); + } + + recordPopupShown(section, fieldName) { + const extra = { field_name: fieldName }; + this.recordFormEvent("popup_shown", section.flowId, extra); + this.recordGleanFormEvent("formPopupShown", section.flowId, extra); + } + + recordFormFilled(section, profile) { + // Calculate values for telemetry + let extra = this.#initFormEventExtra("unavailable"); + + for (let fieldDetail of section.fieldDetails) { + let element = fieldDetail.element; + let state = profile[fieldDetail.fieldName] ? "filled" : "not_filled"; + if ( + section.handler.getFilledStateByElement(element) == + FIELD_STATES.NORMAL && + (HTMLSelectElement.isInstance(element) || + (HTMLInputElement.isInstance(element) && element.value.length)) + ) { + state = "user_filled"; + } + this.#setFormEventExtra(extra, fieldDetail.fieldName, state); + } + + this.recordFormEvent("filled", section.flowId, extra); + this.recordGleanFormEvent("formFilled", section.flowId, extra); + } + + recordFilledModified(section, fieldName) { + const extra = { field_name: fieldName }; + this.recordFormEvent("filled_modified", section.flowId, extra); + this.recordGleanFormEvent("formFilledModified", section.flowId, extra); + } + + recordFormSubmitted(section, record, _form) { + let extra = this.#initFormEventExtra("unavailable"); + + if (record.guid !== null) { + // If the `guid` is not null, it means we're editing an existing record. + // In that case, all fields in the record are autofilled, and fields in + // `untouchedFields` are unmodified. + for (const [fieldName, value] of Object.entries(record.record)) { + if (record.untouchedFields?.includes(fieldName)) { + this.#setFormEventExtra(extra, fieldName, "autofilled"); + } else if (value) { + this.#setFormEventExtra(extra, fieldName, "user_filled"); + } else { + this.#setFormEventExtra(extra, fieldName, "not_filled"); + } + } + } else { + Object.keys(record.record).forEach(fieldName => + this.#setFormEventExtra(extra, fieldName, "user_filled") + ); + } + + this.recordFormEvent("submitted", section.flowId, extra); + this.recordGleanFormEvent("formSubmitted", section.flowId, extra); + } + + recordFormCleared(section, fieldName) { + const extra = { field_name: fieldName }; + + // Note that when a form is cleared, we also record `filled_modified` events + // for all the fields that have been cleared. + this.recordFormEvent("cleared", section.flowId, extra); + this.recordGleanFormEvent("formCleared", section.flowId, extra); + } + + recordFormEvent(method, flowId, extra) { + Services.telemetry.recordEvent( + this.EVENT_CATEGORY, + method, + this.EVENT_OBJECT_FORM_INTERACTION, + flowId, + extra + ); + } + + recordGleanFormEvent(_eventName, _flowId, _extra) { + throw new Error("Not implemented."); + } + + recordFormInteractionEvent( + method, + section, + { fieldName, profile, record, form } = {} + ) { + if (!this.EVENT_OBJECT_FORM_INTERACTION) { + return undefined; + } + switch (method) { + case "detected": + return this.recordFormDetected(section); + case "popup_shown": + return this.recordPopupShown(section, fieldName); + case "filled": + return this.recordFormFilled(section, profile); + case "filled_modified": + return this.recordFilledModified(section, fieldName); + case "submitted": + return this.recordFormSubmitted(section, record, form); + case "cleared": + return this.recordFormCleared(section, fieldName); + } + return undefined; + } + + recordDoorhangerEvent(method, object, flowId) { + Services.telemetry.recordEvent(this.EVENT_CATEGORY, method, object, flowId); + } + + recordManageEvent(method) { + Services.telemetry.recordEvent(this.EVENT_CATEGORY, method, "manage"); + } + + recordAutofillProfileCount(_count) { + throw new Error("Not implemented."); + } + + recordDetectedSectionCount() { + if (!this.SCALAR_DETECTED_SECTION_COUNT) { + return; + } + + Services.telemetry.scalarAdd(this.SCALAR_DETECTED_SECTION_COUNT, 1); + } + + recordSubmittedSectionCount(count) { + if (!this.SCALAR_SUBMITTED_SECTION_COUNT || !count) { + return; + } + + Services.telemetry.scalarAdd(this.SCALAR_SUBMITTED_SECTION_COUNT, count); + } + + recordNumberOfUse(records) { + let histogram = Services.telemetry.getKeyedHistogramById( + this.HISTOGRAM_PROFILE_NUM_USES + ); + histogram.clear(); + + for (let record of records) { + histogram.add(this.HISTOGRAM_PROFILE_NUM_USES_KEY, record.timesUsed); + } + } +} + +export class AddressTelemetry extends AutofillTelemetryBase { + EVENT_CATEGORY = "address"; + EVENT_OBJECT_FORM_INTERACTION = "address_form"; + EVENT_OBJECT_FORM_INTERACTION_EXT = "address_form_ext"; + + SCALAR_DETECTED_SECTION_COUNT = + "formautofill.addresses.detected_sections_count"; + SCALAR_SUBMITTED_SECTION_COUNT = + "formautofill.addresses.submitted_sections_count"; + SCALAR_AUTOFILL_PROFILE_COUNT = + "formautofill.addresses.autofill_profiles_count"; + + HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES"; + HISTOGRAM_PROFILE_NUM_USES_KEY = "address"; + + // Fields that are record in `address_form` and `address_form_ext` telemetry + SUPPORTED_FIELDS = { + "street-address": "street_address", + "address-line1": "address_line1", + "address-line2": "address_line2", + "address-line3": "address_line3", + "address-level1": "address_level1", + "address-level2": "address_level2", + "postal-code": "postal_code", + country: "country", + name: "name", + "given-name": "given_name", + "additional-name": "additional_name", + "family-name": "family_name", + email: "email", + organization: "organization", + tel: "tel", + }; + + // Fields that are record in `address_form` event telemetry extra_keys + static SUPPORTED_FIELDS_IN_FORM = [ + "street_address", + "address_line1", + "address_line2", + "address_line3", + "address_level2", + "address_level1", + "postal_code", + "country", + ]; + + // Fields that are record in `address_form_ext` event telemetry extra_keys + static SUPPORTED_FIELDS_IN_FORM_EXT = [ + "name", + "given_name", + "additional_name", + "family_name", + "email", + "organization", + "tel", + ]; + + recordGleanFormEvent(_eventName, _flowId, _extra) { + // To be implemented when migrating the legacy event address.address_form to Glean + } + + recordFormEvent(method, flowId, extra) { + let extExtra = {}; + if (["detected", "filled", "submitted"].includes(method)) { + for (const [key, value] of Object.entries(extra)) { + if (AddressTelemetry.SUPPORTED_FIELDS_IN_FORM_EXT.includes(key)) { + extExtra[key] = value; + delete extra[key]; + } + } + } + + Services.telemetry.recordEvent( + this.EVENT_CATEGORY, + method, + this.EVENT_OBJECT_FORM_INTERACTION, + flowId, + extra + ); + + if (Object.keys(extExtra).length) { + Services.telemetry.recordEvent( + this.EVENT_CATEGORY, + method, + this.EVENT_OBJECT_FORM_INTERACTION_EXT, + flowId, + extExtra + ); + } + } + + recordAutofillProfileCount(count) { + Services.telemetry.scalarSet(this.SCALAR_AUTOFILL_PROFILE_COUNT, count); + } +} + +class CreditCardTelemetry extends AutofillTelemetryBase { + EVENT_CATEGORY = "creditcard"; + EVENT_OBJECT_FORM_INTERACTION = "cc_form_v2"; + + SCALAR_DETECTED_SECTION_COUNT = + "formautofill.creditCards.detected_sections_count"; + SCALAR_SUBMITTED_SECTION_COUNT = + "formautofill.creditCards.submitted_sections_count"; + + HISTOGRAM_NUM_USES = "CREDITCARD_NUM_USES"; + HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES"; + HISTOGRAM_PROFILE_NUM_USES_KEY = "credit_card"; + + // Mapping of field name used in formautofill code to the field name + // used in the telemetry. + SUPPORTED_FIELDS = { + "cc-name": "cc_name", + "cc-number": "cc_number", + "cc-type": "cc_type", + "cc-exp": "cc_exp", + "cc-exp-month": "cc_exp_month", + "cc-exp-year": "cc_exp_year", + }; + + recordLegacyFormEvent(method, flowId, extra = null) { + Services.telemetry.recordEvent( + this.EVENT_CATEGORY, + method, + "cc_form", + flowId, + extra + ); + } + + recordGleanFormEvent(eventName, flowId, extra) { + extra.flow_id = flowId; + Glean.formautofillCreditcards[eventName].record(extra); + } + + recordFormDetected(section) { + super.recordFormDetected(section); + + let identified = new Set(); + section.fieldDetails.forEach(detail => { + identified.add(detail.fieldName); + }); + let extra = { + cc_name_found: identified.has("cc-name") ? "true" : "false", + cc_number_found: identified.has("cc-number") ? "true" : "false", + cc_exp_found: + identified.has("cc-exp") || + (identified.has("cc-exp-month") && identified.has("cc-exp-year")) + ? "true" + : "false", + }; + + this.recordLegacyFormEvent("detected", section.flowId, extra); + } + + recordPopupShown(section, fieldName) { + super.recordPopupShown(section, fieldName); + + this.recordLegacyFormEvent("popup_shown", section.flowId); + } + + recordFormFilled(section, profile) { + super.recordFormFilled(section, profile); + // Calculate values for telemetry + let extra = { + cc_name: "unavailable", + cc_number: "unavailable", + cc_exp: "unavailable", + }; + + for (let fieldDetail of section.fieldDetails) { + let element = fieldDetail.element; + let state = profile[fieldDetail.fieldName] ? "filled" : "not_filled"; + if ( + section.handler.getFilledStateByElement(element) == + FIELD_STATES.NORMAL && + (HTMLSelectElement.isInstance(element) || + (HTMLInputElement.isInstance(element) && element.value.length)) + ) { + state = "user_filled"; + } + switch (fieldDetail.fieldName) { + case "cc-name": + extra.cc_name = state; + break; + case "cc-number": + extra.cc_number = state; + break; + case "cc-exp": + case "cc-exp-month": + case "cc-exp-year": + extra.cc_exp = state; + break; + } + } + + this.recordLegacyFormEvent("filled", section.flowId, extra); + } + + recordFilledModified(section, fieldName) { + super.recordFilledModified(section, fieldName); + + let extra = { field_name: fieldName }; + this.recordLegacyFormEvent("filled_modified", section.flowId, extra); + } + + /** + * Called when a credit card form is submitted + * + * @param {object} section Section that produces this record + * @param {object} record Credit card record filled in the form. + * @param {Array} form Form that contains the section + */ + recordFormSubmitted(section, record, form) { + super.recordFormSubmitted(section, record, form); + + // For legacy cc_form event telemetry + let extra = { + fields_not_auto: "0", + fields_auto: "0", + fields_modified: "0", + }; + + if (record.guid !== null) { + let totalCount = form.elements.length; + let autofilledCount = Object.keys(record.record).length; + let unmodifiedCount = record.untouchedFields.length; + + extra.fields_not_auto = (totalCount - autofilledCount).toString(); + extra.fields_auto = autofilledCount.toString(); + extra.fields_modified = (autofilledCount - unmodifiedCount).toString(); + } else { + // If the `guid` is null, we're filling a new form. + // In that case, all not-null fields are manually filled. + extra.fields_not_auto = Array.from(form.elements) + .filter(element => !!element.value?.trim().length) + .length.toString(); + } + + this.recordLegacyFormEvent("submitted", section.flowId, extra); + } + + recordNumberOfUse(records) { + super.recordNumberOfUse(records); + + if (!this.HISTOGRAM_NUM_USES) { + return; + } + + let histogram = Services.telemetry.getHistogramById( + this.HISTOGRAM_NUM_USES + ); + histogram.clear(); + + for (let record of records) { + histogram.add(record.timesUsed); + } + } + + recordAutofillProfileCount(count) { + Glean.formautofillCreditcards.autofillProfilesCount.set(count); + } +} + +export class AutofillTelemetry { + static #creditCardTelemetry = new CreditCardTelemetry(); + static #addressTelemetry = new AddressTelemetry(); + + // const for `type` parameter used in the utility functions + static ADDRESS = "address"; + static CREDIT_CARD = "creditcard"; + + static #getTelemetryBySection(section) { + return section instanceof FormAutofillCreditCardSection + ? this.#creditCardTelemetry + : this.#addressTelemetry; + } + + static #getTelemetryByType(type) { + return type == AutofillTelemetry.CREDIT_CARD + ? this.#creditCardTelemetry + : this.#addressTelemetry; + } + + /** + * Utility functions for `doorhanger` event (defined in Events.yaml) + * + * Category: address or creditcard + * Event name: doorhanger + */ + static recordDoorhangerShown(type, object, flowId) { + const telemetry = this.#getTelemetryByType(type); + telemetry.recordDoorhangerEvent("show", object, flowId); + } + + static recordDoorhangerClicked(type, method, object, flowId) { + const telemetry = this.#getTelemetryByType(type); + + // We don't have `create` method in telemetry, we treat `create` as `save` + switch (method) { + case "create": + method = "save"; + break; + case "open-pref": + method = "pref"; + break; + case "learn-more": + method = "learn_more"; + break; + } + + telemetry.recordDoorhangerEvent(method, object, flowId); + } + + /** + * Utility functions for form event (defined in Events.yaml) + * + * Category: address or creditcard + * Event name: cc_form, cc_form_v2, or address_form + */ + + static recordFormInteractionEvent( + method, + section, + { fieldName, profile, record, form } = {} + ) { + const telemetry = this.#getTelemetryBySection(section); + telemetry.recordFormInteractionEvent(method, section, { + fieldName, + profile, + record, + form, + }); + } + + /** + * Utility functions for submitted section count scalar (defined in Scalars.yaml) + * + * Category: formautofill.creditCards or formautofill.addresses + * Scalar name: submitted_sections_count + */ + static recordDetectedSectionCount(section) { + const telemetry = this.#getTelemetryBySection(section); + telemetry.recordDetectedSectionCount(); + } + + static recordSubmittedSectionCount(type, count) { + const telemetry = this.#getTelemetryByType(type); + telemetry.recordSubmittedSectionCount(count); + } + + static recordManageEvent(type, method) { + const telemetry = this.#getTelemetryByType(type); + telemetry.recordManageEvent(method); + } + + static recordAutofillProfileCount(type, count) { + const telemetry = this.#getTelemetryByType(type); + telemetry.recordAutofillProfileCount(count); + } + + /** + * Utility functions for address/credit card number of use + */ + static recordNumberOfUse(type, records) { + const telemetry = this.#getTelemetryByType(type); + telemetry.recordNumberOfUse(records); + } + + static recordFormSubmissionHeuristicCount(label) { + Glean.formautofill.formSubmissionHeuristic[label].add(1); + } +} diff --git a/toolkit/components/formautofill/shared/FieldScanner.sys.mjs b/toolkit/components/formautofill/shared/FieldScanner.sys.mjs index 22adfdabe8..2118de3de8 100644 --- a/toolkit/components/formautofill/shared/FieldScanner.sys.mjs +++ b/toolkit/components/formautofill/shared/FieldScanner.sys.mjs @@ -2,6 +2,11 @@ * 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 lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs", +}); + /** * Represents the detailed information about a form field, including * the inferred field name, the approach used for inferring, and additional metadata. @@ -73,6 +78,14 @@ export class FieldDetail { get sectionName() { return this.section || this.addressType; } + + #isVisible = null; + get isVisible() { + if (this.#isVisible == null) { + this.#isVisible = lazy.FormAutofillUtils.isFieldVisible(this.element); + } + return this.#isVisible; + } } /** diff --git a/toolkit/components/formautofill/shared/FormAutofillHeuristics.sys.mjs b/toolkit/components/formautofill/shared/FormAutofillHeuristics.sys.mjs index 4ee1fc1fe1..fb96e47cae 100644 --- a/toolkit/components/formautofill/shared/FormAutofillHeuristics.sys.mjs +++ b/toolkit/components/formautofill/shared/FormAutofillHeuristics.sys.mjs @@ -182,7 +182,7 @@ export const FormAutofillHeuristics = { * Return true if there is any field can be recognized in the parser, * otherwise false. */ - _parsePhoneFields(scanner, detail) { + _parsePhoneFields(scanner, _fieldDetail) { let matchingResult; const GRAMMARS = this.PHONE_FIELD_GRAMMARS; @@ -277,7 +277,7 @@ export const FormAutofillHeuristics = { * Return true if there is any field can be recognized in the parser, * otherwise false. */ - _parseStreetAddressFields(scanner, fieldDetail) { + _parseStreetAddressFields(scanner, _fieldDetail) { const INTERESTED_FIELDS = [ "street-address", "address-line1", @@ -547,7 +547,9 @@ export const FormAutofillHeuristics = { * all sections within its field details in the form. */ getFormInfo(form) { - let elements = this.getFormElements(form); + const elements = Array.from(form.elements).filter(element => + lazy.FormAutofillUtils.isCreditCardOrAddressFieldType(element) + ); const scanner = new lazy.FieldScanner(elements, element => this.inferFieldInfo(element, elements) @@ -596,22 +598,6 @@ export const FormAutofillHeuristics = { ); }, - /** - * Get focusable form elements that are of credit card or address type - * - * @param {HTMLElement} form - * @returns {Array} focusable elements - */ - getFormElements(form) { - let elements = Array.from(form.elements).filter( - element => - lazy.FormAutofillUtils.isCreditCardOrAddressFieldType(element) && - lazy.FormAutofillUtils.isFieldFocusable(element) - ); - - return elements; - }, - /** * The result is an array contains the sections with its belonging field details. * @@ -621,46 +607,54 @@ export const FormAutofillHeuristics = { _classifySections(fieldDetails) { let sections = []; for (let i = 0; i < fieldDetails.length; i++) { - const fieldName = fieldDetails[i].fieldName; - const sectionName = fieldDetails[i].sectionName; - + const cur = fieldDetails[i]; const [currentSection] = sections.slice(-1); - // The section this field might belong to + // The section this field might be placed into. let candidateSection = null; - // If the field doesn't have a section name, MAYBE put it to the previous - // section if exists. If the field has a section name, maybe put it to the - // nearest section that either has the same name or it doesn't has a name. - // Otherwise, create a new section. - if (!currentSection || !sectionName) { + // Use name group from autocomplete attribute (ex, section-xxx) to look for the section + // we might place this field into. + // If the field doesn't have a section name, the candidate section is the previous section. + if (!currentSection || !cur.sectionName) { candidateSection = currentSection; - } else if (sectionName) { + } else if (cur.sectionName) { + // If the field has a section name, the candidate section is the nearest section that + // either shares the same name or lacks a name. for (let idx = sections.length - 1; idx >= 0; idx--) { - if (!sections[idx].name || sections[idx].name == sectionName) { + if (!sections[idx].name || sections[idx].name == cur.sectionName) { candidateSection = sections[idx]; break; } } } - // We got an candidate section to put the field to, check whether the section - // already has a field with the same field name. If yes, only add the field to when - // the type of the field might appear multiple times in a row. if (candidateSection) { let createNewSection = true; - if (candidateSection.fieldDetails.find(f => f.fieldName == fieldName)) { + + // We might create a new section instead of placing the field in the candiate section if + // the section already has a field with the same field name. + // We also check visibility for both the fields with the same field name because we don't + // wanht to create a new section for an invisible field. + if ( + candidateSection.fieldDetails.find( + f => f.fieldName == cur.fieldName && f.isVisible && cur.isVisible + ) + ) { + // For some field type, it is common to have multiple fields in one section, for example, + // email. In that case, we will not create a new section even when the candidate section + // already has a field with the same field name. const [lastFieldDetail] = candidateSection.fieldDetails.slice(-1); - if (lastFieldDetail.fieldName == fieldName) { - if (MULTI_FIELD_NAMES.includes(fieldName)) { + if (lastFieldDetail.fieldName == cur.fieldName) { + if (MULTI_FIELD_NAMES.includes(cur.fieldName)) { createNewSection = false; - } else if (fieldName in MULTI_N_FIELD_NAMES) { + } else if (cur.fieldName in MULTI_N_FIELD_NAMES) { // This is the heuristic to handle special cases where we can have multiple // fields in one section, but only if the field has appeared N times in a row. // For example, websites can use 4 consecutive 4-digit `cc-number` fields // instead of one 16-digit `cc-number` field. - const N = MULTI_N_FIELD_NAMES[fieldName]; + const N = MULTI_N_FIELD_NAMES[cur.fieldName]; if (lastFieldDetail.part) { // If `part` is set, we have already identified this field can be // merged previously @@ -673,7 +667,7 @@ export const FormAutofillHeuristics = { N == 2 || fieldDetails .slice(i + 1, i + N - 1) - .every(f => f.fieldName == fieldName) + .every(f => f.fieldName == cur.fieldName) ) { lastFieldDetail.part = 1; fieldDetails[i].part = 2; diff --git a/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs b/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs index 1c7696432a..7bda4c167b 100644 --- a/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs +++ b/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs @@ -7,7 +7,7 @@ import { FormAutofill } from "resource://autofill/FormAutofill.sys.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { - AutofillTelemetry: "resource://autofill/AutofillTelemetry.sys.mjs", + AutofillTelemetry: "resource://gre/modules/shared/AutofillTelemetry.sys.mjs", CreditCard: "resource://gre/modules/CreditCard.sys.mjs", FormAutofillNameUtils: "resource://gre/modules/shared/FormAutofillNameUtils.sys.mjs", @@ -79,40 +79,40 @@ export class FormAutofillSection { * Examine the section is createable for storing the profile. This method * must be overrided. * - * @param {Object} record The record for examining createable + * @param {Object} _record The record for examining createable * @returns {boolean} True for the record is createable, otherwise false * */ - isRecordCreatable(record) { + isRecordCreatable(_record) { throw new TypeError("isRecordCreatable method must be overridden"); } /** * Override this method if the profile is needed to apply some transformers. * - * @param {object} profile + * @param {object} _profile * A profile should be converted based on the specific requirement. */ - applyTransformers(profile) {} + applyTransformers(_profile) {} /** * Override this method if the profile is needed to be customized for * previewing values. * - * @param {object} profile + * @param {object} _profile * A profile for pre-processing before previewing values. */ - preparePreviewProfile(profile) {} + preparePreviewProfile(_profile) {} /** * Override this method if the profile is needed to be customized for filling * values. * - * @param {object} profile + * @param {object} _profile * A profile for pre-processing before filling values. * @returns {boolean} Whether the profile should be filled. */ - async prepareFillingProfile(profile) { + async prepareFillingProfile(_profile) { return true; } @@ -846,6 +846,10 @@ export class FormAutofillAddressSection extends FormAutofillSection { value = FormAutofillUtils.getAbbreviatedSubregionName([value, text]) || text; } + } else if (fieldDetail.fieldName == "country") { + // This is a temporary fix. Ideally we should have either case-insensitive comparaison of country codes + // or handle this elsewhere see Bug 1889234 for more context. + value = value.toUpperCase(); } return value; } @@ -884,7 +888,7 @@ export class FormAutofillCreditCardSection extends FormAutofillSection { } } - _handlePageHide(event) { + _handlePageHide(_event) { this.handler.window.removeEventListener( "pagehide", this._handlePageHide.bind(this) diff --git a/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs b/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs index ce10c71ce1..e86f14975c 100644 --- a/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs +++ b/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs @@ -192,7 +192,7 @@ FormAutofillUtils = { getAddressLabel(address) { // TODO: Implement a smarter way for deciding what to display // as option text. Possibly improve the algorithm in - // ProfileAutoCompleteResult.jsm and reuse it here. + // ProfileAutoCompleteResult.sys.mjs and reuse it here. let fieldOrder = [ "name", "-moz-street-address-one-line", // Street address @@ -302,20 +302,27 @@ FormAutofillUtils = { }, /** - * Determines if an element is focusable - * and accessible via keyboard navigation or not. + * Determines if an element is visually hidden or not. * * @param {HTMLElement} element - * - * @returns {bool} true if the element is focusable and accessible + * @param {boolean} visibilityCheck true to run visiblity check against + * element.checkVisibility API. Otherwise, test by only checking + * `hidden` and `display` attributes + * @returns {boolean} true if the element is visible */ - isFieldFocusable(element) { - return ( - // The Services.focus.elementIsFocusable API considers elements with - // tabIndex="-1" set as focusable. But since they are not accessible - // via keyboard navigation we treat them as non-interactive - Services.focus.elementIsFocusable(element, 0) && element.tabIndex != "-1" - ); + isFieldVisible(element, visibilityCheck = true) { + if ( + visibilityCheck && + element.checkVisibility && + !FormAutofillUtils.ignoreVisibilityCheck + ) { + return element.checkVisibility({ + checkOpacity: true, + checkVisibilityCSS: true, + }); + } + + return !element.hidden && element.style.display != "none"; }, /** @@ -1127,3 +1134,11 @@ XPCOMUtils.defineLazyPreferenceGetter( "extensions.formautofill.focusOnAutofill", true ); + +// This is only used for testing +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofillUtils, + "ignoreVisibilityCheck", + "extensions.formautofill.test.ignoreVisibilityCheck", + false +); diff --git a/toolkit/components/formautofill/shared/FormStateManager.sys.mjs b/toolkit/components/formautofill/shared/FormStateManager.sys.mjs index 064b4e5356..7481a5981c 100644 --- a/toolkit/components/formautofill/shared/FormStateManager.sys.mjs +++ b/toolkit/components/formautofill/shared/FormStateManager.sys.mjs @@ -150,7 +150,7 @@ export class FormStateManager { } didDestroy() { - this._activeItems = null; + this._activeItems = {}; } } diff --git a/toolkit/components/formautofill/shared/PhoneNumber.sys.mjs b/toolkit/components/formautofill/shared/PhoneNumber.sys.mjs new file mode 100644 index 0000000000..5288765181 --- /dev/null +++ b/toolkit/components/formautofill/shared/PhoneNumber.sys.mjs @@ -0,0 +1,474 @@ +/* This Source Code Form is subject to the terms of the Apache License, Version + * 2.0. If a copy of the Apache License was not distributed with this file, You + * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */ + +// This library came from https://github.com/andreasgal/PhoneNumber.js but will +// be further maintained by our own in Form Autofill codebase. + +import { PHONE_NUMBER_META_DATA } from "resource://gre/modules/shared/PhoneNumberMetaData.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PhoneNumberNormalizer: + "resource://gre/modules/shared/PhoneNumberNormalizer.sys.mjs", +}); + +export var 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 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 FORMAT_ENCODING = [ + "^pattern$", + "nationalFormat", + "^leadingDigits", + "nationalPrefixFormattingRule", + "internationalFormat", + ]; + + 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 (let n = 0; n < encoding.length; ++n) { + let value = array[n]; + if (!value) { + continue; + } + 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) { + let array = JSON.parse(md); + md = ParseArray(array, META_DATA_ENCODING, { countryCode }); + regionCache[md.region] = md; + return md; + } + + // Parse string encoded format data into a convenient object + // representation. + function ParseFormat(md) { + let formats = md.formats; + if (!formats) { + return; + } + // Bail if we already parsed the format definitions. + if (!Array.isArray(formats[0])) { + return; + } + 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). + let md = regionCache[region]; + if (md) { + return md; + } + 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 (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") { + entry[0] = ParseMetaData(countryCode, entry[0]); + } + let formats = entry[0].formats; + let current = ParseMetaData(countryCode, entry[n]); + current.formats = formats; + 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) { + 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); + let formats = regionMetaData.formats; + if (!formats) { + return null; + } + 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)) { + continue; + } + if (!format.pattern.test(number)) { + continue; + } + if (intl) { + // If there is no international format, just fall back to the national + // format. + 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") { + 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. + 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. + let match = number.match(SPLIT_FIRST_GROUP); + if (match) { + 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; + } + return null; + } + + function NationalNumber(regionMetaData, number) { + this.region = regionMetaData.region; + this.regionMetaData = regionMetaData; + this.number = number; + } + + // 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() { + let value = FormatNumber(this.regionMetaData, this.number, true); + Object.defineProperty(this, "internationalFormat", { + value, + enumerable: true, + }); + return value; + }, + // (949) 726-2896 + get nationalFormat() { + let value = FormatNumber(this.regionMetaData, this.number, false); + Object.defineProperty(this, "nationalFormat", { + value, + enumerable: true, + }); + return value; + }, + // +19497262896 + get internationalNumber() { + let value = this.internationalFormat + ? this.internationalFormat.replace(NON_DIALABLE_CHARS, "") + : null; + Object.defineProperty(this, "internationalNumber", { + value, + enumerable: true, + }); + return value; + }, + // 9497262896 + get nationalNumber() { + let value = this.nationalFormat + ? this.nationalFormat.replace(NON_DIALABLE_CHARS, "") + : null; + Object.defineProperty(this, "nationalNumber", { + value, + enumerable: true, + }); + return value; + }, + // country name 'US' + get countryName() { + let value = this.region ? this.region : null; + Object.defineProperty(this, "countryName", { value, enumerable: true }); + return value; + }, + // country code '+1' + get countryCode() { + 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 (let n = 1; n <= 3; ++n) { + let cc = number.substr(0, n); + if (dataBase[cc]) { + return cc; + } + } + return null; + } + + // 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) { + let ret; + + // Remove formating characters and whitespace. + number = lazy.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] !== "+") { + return null; + } + + // Detect and strip leading '+'. + if (number[0] === "+") { + return ParseInternationalNumber( + number.replace(LEADING_PLUS_CHARS_PATTERN, "") + ); + } + + // If "defaultRegion" is a country code, use it to parse the number directly. + let matches = String(defaultRegion).match(/^\+?(\d+)/); + if (matches) { + let countryCode = ParseCountryCode(matches[1]); + if (!countryCode) { + return null; + } + return ParseNumberByCountryCode(number, countryCode); + } + + // Lookup the meta data for the given region. + 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)) { + let possibleNumber = number.replace(md.internationalPrefix, ""); + ret = ParseInternationalNumber(possibleNumber); + if (ret) { + return ret; + } + } + + ret = ParseNationalNumberAndCheckNationalPrefix(number, md); + 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) { + return ret; + } + + // If the number matches the possible numbers of the current region, + // return it as a possible 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") { + return false; + } + + 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, + IsValid: IsValidNumber, + Parse: ParseNumber, + FindMetaDataForRegion, + }; +})(PHONE_NUMBER_META_DATA); diff --git a/toolkit/components/formautofill/shared/PhoneNumberMetaData.sys.mjs b/toolkit/components/formautofill/shared/PhoneNumberMetaData.sys.mjs new file mode 100644 index 0000000000..3338ce7c16 --- /dev/null +++ b/toolkit/components/formautofill/shared/PhoneNumberMetaData.sys.mjs @@ -0,0 +1,291 @@ +/* This Source Code Form is subject to the terms of the Apache License, Version + * 2.0. If a copy of the Apache License was not distributed with this file, You + * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */ + +/* + * This data was generated base on libphonenumber v8.4.1 via the script in + * https://github.com/andreasgal/PhoneNumber.js + * + * The XML format of libphonenumber has changed since v8.4.2 so we can only stay + * in this version for now. + */ + +export var PHONE_NUMBER_META_DATA = { + 46: '["SE","00","0",null,null,"$NP$FG","\\\\d{6,12}","[1-35-9]\\\\d{5,11}|4\\\\d{6,8}",[["(8)(\\\\d{2,3})(\\\\d{2,3})(\\\\d{2})","$1-$2 $3 $4","8",null,"$1 $2 $3 $4"],["([1-69]\\\\d)(\\\\d{2,3})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4","1[013689]|2[0136]|3[1356]|4[0246]|54|6[03]|90",null,"$1 $2 $3 $4"],["([1-469]\\\\d)(\\\\d{3})(\\\\d{2})","$1-$2 $3","1[136]|2[136]|3[356]|4[0246]|6[03]|90",null,"$1 $2 $3"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4","1[2457]|2(?:[247-9]|5[0138])|3[0247-9]|4[1357-9]|5[0-35-9]|6(?:[124-689]|7[0-2])|9(?:[125-8]|3[0-5]|4[0-3])",null,"$1 $2 $3 $4"],["(\\\\d{3})(\\\\d{2,3})(\\\\d{2})","$1-$2 $3","1[2457]|2(?:[247-9]|5[0138])|3[0247-9]|4[1357-9]|5[0-35-9]|6(?:[124-689]|7[0-2])|9(?:[125-8]|3[0-5]|4[0-3])",null,"$1 $2 $3"],["(7\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4","7",null,"$1 $2 $3 $4"],["(77)(\\\\d{2})(\\\\d{2})","$1-$2$3","7",null,"$1 $2 $3"],["(20)(\\\\d{2,3})(\\\\d{2})","$1-$2 $3","20",null,"$1 $2 $3"],["(9[034]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1-$2 $3 $4","9[034]",null,"$1 $2 $3 $4"],["(9[034]\\\\d)(\\\\d{4})","$1-$2","9[034]",null,"$1 $2"],["(\\\\d{3})(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1-$2 $3 $4 $5","25[245]|67[3-6]",null,"$1 $2 $3 $4 $5"]]]', + 299: '["GL","00",null,null,null,null,"\\\\d{6}","[1-689]\\\\d{5}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', + 385: '["HR","00","0",null,null,"$NP$FG","\\\\d{6,9}","[1-7]\\\\d{5,8}|[89]\\\\d{6,8}",[["(1)(\\\\d{4})(\\\\d{3})","$1 $2 $3","1",null],["([2-5]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[2-5]",null],["(9\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","9",null],["(6[01])(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","6[01]",null],["([67]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[67]",null],["(80[01])(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","8",null],["(80[01])(\\\\d{3})(\\\\d{3})","$1 $2 $3","8",null]]]', + 670: '["TL","00",null,null,null,null,"\\\\d{7,8}","[2-489]\\\\d{6}|7\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[2-489]",null],["(\\\\d{4})(\\\\d{4})","$1 $2","7",null]]]', + 258: '["MZ","00",null,null,null,null,"\\\\d{8,9}","[28]\\\\d{7,8}",[["([28]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2|8[2-7]",null],["(80\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","80",null]]]', + 359: '["BG","00","0",null,null,"$NP$FG","\\\\d{5,9}","[23567]\\\\d{5,7}|[489]\\\\d{6,8}",[["(2)(\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","2",null],["(2)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2",null],["(\\\\d{3})(\\\\d{4})","$1 $2","43[124-7]|70[1-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1 $2 $3","43[124-7]|70[1-9]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","[78]00",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","999",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2,3})","$1 $2 $3","[356]|4[124-7]|7[1-9]|8[1-6]|9[1-7]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","48|8[7-9]|9[08]",null]]]', + 682: '["CK","00",null,null,null,null,"\\\\d{5}","[2-8]\\\\d{4}",[["(\\\\d{2})(\\\\d{3})","$1 $2",null,null]]]', + 852: '["HK","00(?:[126-9]|30|5[09])?",null,null,null,null,"\\\\d{5,11}","[235-7]\\\\d{7}|8\\\\d{7,8}|9\\\\d{4,10}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[235-7]|[89](?:0[1-9]|[1-9])",null],["(800)(\\\\d{3})(\\\\d{3})","$1 $2 $3","800",null],["(900)(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","900",null],["(900)(\\\\d{2,5})","$1 $2","900",null]]]', + 998: '["UZ","810","8",null,null,"$NP $FG","\\\\d{7,9}","[679]\\\\d{8}",[["([679]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 291: '["ER","00","0",null,null,"$NP$FG","\\\\d{6,7}","[178]\\\\d{6}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', + 95: '["MM","00","0",null,null,"$NP$FG","\\\\d{5,10}","[1478]\\\\d{5,7}|[256]\\\\d{5,8}|9(?:[279]\\\\d{0,2}|[58]|[34]\\\\d{1,2}|6\\\\d?)\\\\d{6}",[["(\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","1|2[245]",null],["(2)(\\\\d{4})(\\\\d{4})","$1 $2 $3","251",null],["(\\\\d)(\\\\d{2})(\\\\d{3})","$1 $2 $3","16|2",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","67|81",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3,4})","$1 $2 $3","[4-8]",null],["(9)(\\\\d{3})(\\\\d{4,6})","$1 $2 $3","9(?:2[0-4]|[35-9]|4[137-9])",null],["(9)([34]\\\\d{4})(\\\\d{4})","$1 $2 $3","9(?:3[0-36]|4[0-57-9])",null],["(9)(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","92[56]",null],["(9)(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1 $2 $3 $4","93",null]]]', + 266: '["LS","00",null,null,null,null,"\\\\d{8}","[2568]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', + 245: '["GW","00",null,null,null,null,"\\\\d{7,9}","(?:4(?:0\\\\d{5}|4\\\\d{7})|9\\\\d{8})",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","44|9[567]",null],["(\\\\d{3})(\\\\d{4})","$1 $2","40",null]]]', + 374: '["AM","00","0",null,null,"($NP$FG)","\\\\d{5,8}","[1-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{6})","$1 $2","1|47",null],["(\\\\d{2})(\\\\d{6})","$1 $2","4[1349]|[5-7]|9[1-9]","$NP$FG"],["(\\\\d{3})(\\\\d{5})","$1 $2","[23]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","8|90","$NP $FG"]]]', + 61: [ + '["AU","(?:14(?:1[14]|34|4[17]|[56]6|7[47]|88))?001[14-689]","0",null,null,null,"\\\\d{6,10}","1\\\\d{4,9}|[2-578]\\\\d{8}",[["([2378])(\\\\d{4})(\\\\d{4})","$1 $2 $3","[2378]","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[45]|14","$NP$FG"],["(16)(\\\\d{3,4})","$1 $2","16","$NP$FG"],["(16)(\\\\d{3})(\\\\d{2,4})","$1 $2 $3","16","$NP$FG"],["(1[389]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1(?:[38]0|90)","$FG"],["(180)(2\\\\d{3})","$1 $2","180","$FG"],["(19\\\\d)(\\\\d{3})","$1 $2","19[13]","$FG"],["(19\\\\d{2})(\\\\d{4})","$1 $2","19[679]","$FG"],["(13)(\\\\d{2})(\\\\d{2})","$1 $2 $3","13[1-9]","$FG"]]]', + '["CC","(?:14(?:1[14]|34|4[17]|[56]6|7[47]|88))?001[14-689]","0",null,null,null,"\\\\d{6,10}","[1458]\\\\d{5,9}"]', + '["CX","(?:14(?:1[14]|34|4[17]|[56]6|7[47]|88))?001[14-689]","0",null,null,null,"\\\\d{6,10}","[1458]\\\\d{5,9}"]', + ], + 500: '["FK","00",null,null,null,null,"\\\\d{5}","[2-7]\\\\d{4}"]', + 261: '["MG","00","0",null,null,"$NP$FG","\\\\d{7,9}","[23]\\\\d{8}",[["([23]\\\\d)(\\\\d{2})(\\\\d{3})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 92: '["PK","00","0",null,null,"($NP$FG)","\\\\d{6,12}","1\\\\d{8}|[2-8]\\\\d{5,11}|9(?:[013-9]\\\\d{4,9}|2\\\\d(?:111\\\\d{6}|\\\\d{3,7}))",[["(\\\\d{2})(111)(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","(?:2[125]|4[0-246-9]|5[1-35-7]|6[1-8]|7[14]|8[16]|91)1",null],["(\\\\d{3})(111)(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","2[349]|45|54|60|72|8[2-5]|9[2-9]",null],["(\\\\d{2})(\\\\d{7,8})","$1 $2","(?:2[125]|4[0-246-9]|5[1-35-7]|6[1-8]|7[14]|8[16]|91)[2-9]",null],["(\\\\d{3})(\\\\d{6,7})","$1 $2","2[349]|45|54|60|72|8[2-5]|9[2-9]",null],["(3\\\\d{2})(\\\\d{7})","$1 $2","3","$NP$FG"],["([15]\\\\d{3})(\\\\d{5,6})","$1 $2","58[12]|1",null],["(586\\\\d{2})(\\\\d{5})","$1 $2","586",null],["([89]00)(\\\\d{3})(\\\\d{2})","$1 $2 $3","[89]00","$NP$FG"]]]', + 234: '["NG","009","0",null,null,"$NP$FG","\\\\d{5,14}","[1-6]\\\\d{5,8}|9\\\\d{5,9}|[78]\\\\d{5,13}",[["(\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[12]|9(?:0[3-9]|[1-9])",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2,3})","$1 $2 $3","[3-6]|7(?:[1-79]|0[1-9])|8[2-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","70|8[01]|90[235-9]",null],["([78]00)(\\\\d{4})(\\\\d{4,5})","$1 $2 $3","[78]00",null],["([78]00)(\\\\d{5})(\\\\d{5,6})","$1 $2 $3","[78]00",null],["(78)(\\\\d{2})(\\\\d{3})","$1 $2 $3","78",null]]]', + 350: '["GI","00",null,null,null,null,"\\\\d{8}","[2568]\\\\d{7}",[["(\\\\d{3})(\\\\d{5})","$1 $2","2",null]]]', + 45: '["DK","00",null,null,null,null,"\\\\d{8}","[2-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 963: '["SY","00","0",null,null,"$NP$FG","\\\\d{6,9}","[1-59]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[1-5]",null],["(9\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","9",null]]]', + 226: '["BF","00",null,null,null,null,"\\\\d{8}","[25-7]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 974: '["QA","00",null,null,null,null,"\\\\d{7,8}","[2-8]\\\\d{6,7}",[["([28]\\\\d{2})(\\\\d{4})","$1 $2","[28]",null],["([3-7]\\\\d{3})(\\\\d{4})","$1 $2","[3-7]",null]]]', + 218: '["LY","00","0",null,null,"$NP$FG","\\\\d{7,9}","[25679]\\\\d{8}",[["([25679]\\\\d)(\\\\d{7})","$1-$2",null,null]]]', + 51: '["PE","19(?:1[124]|77|90)00","0",null,null,"($NP$FG)","\\\\d{6,9}","[14-9]\\\\d{7,8}",[["(1)(\\\\d{7})","$1 $2","1",null],["([4-8]\\\\d)(\\\\d{6})","$1 $2","[4-7]|8[2-4]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","80",null],["(9\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","9","$FG"]]]', + 62: '["ID","0(?:0[1789]|10(?:00|1[67]))","0",null,null,"$NP$FG","\\\\d{5,12}","(?:[1-79]\\\\d{6,10}|8\\\\d{7,11})",[["(\\\\d{2})(\\\\d{5,8})","$1 $2","2[124]|[36]1","($NP$FG)"],["(\\\\d{3})(\\\\d{5,8})","$1 $2","[4579]|2[035-9]|[36][02-9]","($NP$FG)"],["(8\\\\d{2})(\\\\d{3,4})(\\\\d{3})","$1-$2-$3","8[1-35-9]",null],["(8\\\\d{2})(\\\\d{4})(\\\\d{4,5})","$1-$2-$3","8[1-35-9]",null],["(1)(500)(\\\\d{3})","$1 $2 $3","15","$FG"],["(177)(\\\\d{6,8})","$1 $2","17",null],["(800)(\\\\d{5,7})","$1 $2","800",null],["(804)(\\\\d{3})(\\\\d{4})","$1 $2 $3","804",null],["(80\\\\d)(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","80[79]",null]]]', + 298: '["FO","00",null,"(10(?:01|[12]0|88))",null,null,"\\\\d{6}","[2-9]\\\\d{5}",[["(\\\\d{6})","$1",null,null]]]', + 381: '["RS","00","0",null,null,"$NP$FG","\\\\d{5,12}","[126-9]\\\\d{4,11}|3(?:[0-79]\\\\d{3,10}|8[2-9]\\\\d{2,9})",[["([23]\\\\d{2})(\\\\d{4,9})","$1 $2","(?:2[389]|39)0",null],["([1-3]\\\\d)(\\\\d{5,10})","$1 $2","1|2(?:[0-24-7]|[389][1-9])|3(?:[0-8]|9[1-9])",null],["(6\\\\d)(\\\\d{6,8})","$1 $2","6",null],["([89]\\\\d{2})(\\\\d{3,9})","$1 $2","[89]",null],["(7[26])(\\\\d{4,9})","$1 $2","7[26]",null],["(7[08]\\\\d)(\\\\d{4,9})","$1 $2","7[08]",null]]]', + 975: '["BT","00",null,null,null,null,"\\\\d{6,8}","[1-8]\\\\d{6,7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","1|77",null],["([2-8])(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-68]|7[246]",null]]]', + 34: '["ES","00",null,null,null,null,"\\\\d{9}","[5-9]\\\\d{8}",[["([89]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[89]00",null],["([5-9]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[568]|[79][0-8]",null]]]', + 881: '["001",null,null,null,null,null,"\\\\d{9}","[67]\\\\d{8}",[["(\\\\d)(\\\\d{3})(\\\\d{5})","$1 $2 $3","[67]",null]]]', + 855: '["KH","00[14-9]","0",null,null,null,"\\\\d{6,10}","[1-9]\\\\d{7,9}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","1\\\\d[1-9]|[2-9]","$NP$FG"],["(1[89]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1[89]0",null]]]', + 420: '["CZ","00",null,null,null,null,"\\\\d{9,12}","[2-8]\\\\d{8}|9\\\\d{8,11}",[["([2-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-8]|9[015-7]",null],["(96\\\\d)(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","96",null],["(9\\\\d)(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","9[36]",null]]]', + 216: '["TN","00",null,null,null,null,"\\\\d{8}","[2-57-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', + 673: '["BN","00",null,null,null,null,"\\\\d{7}","[2-578]\\\\d{6}",[["([2-578]\\\\d{2})(\\\\d{4})","$1 $2",null,null]]]', + 290: [ + '["SH","00",null,null,null,null,"\\\\d{4,5}","[256]\\\\d{4}"]', + '["TA","00",null,null,null,null,"\\\\d{4}","8\\\\d{3}"]', + ], + 882: '["001",null,null,null,null,null,"\\\\d{7,12}","[13]\\\\d{6,11}",[["(\\\\d{2})(\\\\d{4})(\\\\d{3})","$1 $2 $3","3[23]",null],["(\\\\d{2})(\\\\d{5})","$1 $2","16|342",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","34[57]",null],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","348",null],["(\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","16",null],["(\\\\d{2})(\\\\d{4,5})(\\\\d{5})","$1 $2 $3","16|39",null]]]', + 267: '["BW","00",null,null,null,null,"\\\\d{7,8}","[2-79]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[2-6]",null],["(7\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","7",null],["(90)(\\\\d{5})","$1 $2","9",null]]]', + 94: '["LK","00","0",null,null,"$NP$FG","\\\\d{7,9}","[1-9]\\\\d{8}",[["(\\\\d{2})(\\\\d{1})(\\\\d{6})","$1 $2 $3","[1-689]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","7",null]]]', + 356: '["MT","00",null,null,null,null,"\\\\d{8}","[2357-9]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', + 375: '["BY","810","8","8?0?",null,null,"\\\\d{5,11}","[1-4]\\\\d{8}|800\\\\d{3,7}|[89]\\\\d{9,10}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","17[0-3589]|2[4-9]|[34]","$NP 0$FG"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","1(?:5[24]|6[235]|7[467])|2(?:1[246]|2[25]|3[26])","$NP 0$FG"],["(\\\\d{4})(\\\\d{2})(\\\\d{3})","$1 $2-$3","1(?:5[169]|6[3-5]|7[179])|2(?:1[35]|2[34]|3[3-5])","$NP 0$FG"],["([89]\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","8[01]|9","$NP $FG"],["(82\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","82","$NP $FG"],["(800)(\\\\d{3})","$1 $2","800","$NP $FG"],["(800)(\\\\d{2})(\\\\d{2,4})","$1 $2 $3","800","$NP $FG"]]]', + 690: '["TK","00",null,null,null,null,"\\\\d{4,7}","[2-47]\\\\d{3,6}"]', + 507: '["PA","00",null,null,null,null,"\\\\d{7,8}","[1-9]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1-$2","[1-57-9]",null],["(\\\\d{4})(\\\\d{4})","$1-$2","6",null]]]', + 692: '["MH","011","1",null,null,null,"\\\\d{7}","[2-6]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1-$2",null,null]]]', + 250: '["RW","00","0",null,null,null,"\\\\d{8,9}","[027-9]\\\\d{7,8}",[["(2\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","2","$FG"],["([7-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[7-9]","$NP$FG"],["(0\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","0",null]]]', + 81: '["JP","010","0",null,null,"$NP$FG","\\\\d{8,17}","[1-9]\\\\d{8,9}|00(?:[36]\\\\d{7,14}|7\\\\d{5,7}|8\\\\d{7})",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1-$2-$3","(?:12|57|99)0",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1-$2-$3","800",null],["(\\\\d{4})(\\\\d{4})","$1-$2","0077","$FG","NA"],["(\\\\d{4})(\\\\d{2})(\\\\d{3,4})","$1-$2-$3","0077","$FG","NA"],["(\\\\d{4})(\\\\d{2})(\\\\d{4})","$1-$2-$3","0088","$FG","NA"],["(\\\\d{4})(\\\\d{3})(\\\\d{3,4})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{4})(\\\\d{4})(\\\\d{4,5})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{4})(\\\\d{5})(\\\\d{5,6})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{4})(\\\\d{6})(\\\\d{6,7})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1-$2-$3","[2579]0|80[1-9]",null],["(\\\\d{4})(\\\\d)(\\\\d{4})","$1-$2-$3","1(?:26|3[79]|4[56]|5[4-68]|6[3-5])|5(?:76|97)|499|746|8(?:3[89]|63|47|51)|9(?:49|80|9[16])",null],["(\\\\d{3})(\\\\d{2})(\\\\d{4})","$1-$2-$3","1(?:2[3-6]|3[3-9]|4[2-6]|5[2-8]|[68][2-7]|7[2-689]|9[1-578])|2(?:2[03-689]|3[3-58]|4[0-468]|5[04-8]|6[013-8]|7[06-9]|8[02-57-9]|9[13])|4(?:2[28]|3[689]|6[035-7]|7[05689]|80|9[3-5])|5(?:3[1-36-9]|4[4578]|5[013-8]|6[1-9]|7[2-8]|8[14-7]|9[4-9])|7(?:2[15]|3[5-9]|4[02-9]|6[135-8]|7[0-4689]|9[014-9])|8(?:2[49]|3[3-8]|4[5-8]|5[2-9]|6[35-9]|7[579]|8[03-579]|9[2-8])|9(?:[23]0|4[02-46-9]|5[024-79]|6[4-9]|7[2-47-9]|8[02-7]|9[3-7])",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3","1|2(?:2[37]|5[5-9]|64|78|8[39]|91)|4(?:2[2689]|64|7[347])|5(?:[2-589]|39)|60|8(?:[46-9]|3[279]|2[124589])|9(?:[235-8]|93)",null],["(\\\\d{3})(\\\\d{2})(\\\\d{4})","$1-$2-$3","2(?:9[14-79]|74|[34]7|[56]9)|82|993",null],["(\\\\d)(\\\\d{4})(\\\\d{4})","$1-$2-$3","3|4(?:2[09]|7[01])|6[1-9]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3","[2479][1-9]",null]]]', + 237: '["CM","00",null,null,null,null,"\\\\d{8,9}","[2368]\\\\d{7,8}",[["([26])(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","[26]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[23]|88",null],["(800)(\\\\d{2})(\\\\d{3})","$1 $2 $3","80",null]]]', + 351: '["PT","00",null,null,null,null,"\\\\d{9}","[2-46-9]\\\\d{8}",[["(2\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2[12]",null],["([2-46-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","2[3-9]|[346-9]",null]]]', + 246: '["IO","00",null,null,null,null,"\\\\d{7}","3\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 227: '["NE","00",null,null,null,null,"\\\\d{8}","[0289]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[289]|09",null],["(08)(\\\\d{3})(\\\\d{3})","$1 $2 $3","08",null]]]', + 27: '["ZA","00","0",null,null,"$NP$FG","\\\\d{5,9}","[1-79]\\\\d{8}|8\\\\d{4,8}",[["(860)(\\\\d{3})(\\\\d{3})","$1 $2 $3","860",null],["(\\\\d{2})(\\\\d{3,4})","$1 $2","8[1-4]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2,3})","$1 $2 $3","8[1-4]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[1-79]|8(?:[0-57]|6[1-9])",null]]]', + 962: '["JO","00","0",null,null,"$NP$FG","\\\\d{8,9}","[235-9]\\\\d{7,8}",[["(\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2356]|87","($NP$FG)"],["(7)(\\\\d{4})(\\\\d{4})","$1 $2 $3","7[457-9]",null],["(\\\\d{3})(\\\\d{5,6})","$1 $2","70|8[0158]|9",null]]]', + 387: '["BA","00","0",null,null,"$NP$FG","\\\\d{6,9}","[3-9]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2-$3","[3-5]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","6[1-356]|[7-9]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","6[047]",null]]]', + 33: '["FR","00","0",null,null,"$NP$FG","\\\\d{9}","[1-9]\\\\d{8}",[["([1-79])(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","[1-79]",null],["(1\\\\d{2})(\\\\d{3})","$1 $2","11","$FG","NA"],["(8\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","8","$NP $FG"]]]', + 972: '["IL","0(?:0|1[2-9])","0",null,null,"$FG","\\\\d{4,12}","1\\\\d{6,11}|[2-589]\\\\d{3}(?:\\\\d{3,6})?|6\\\\d{3}|7\\\\d{6,9}",[["([2-489])(\\\\d{3})(\\\\d{4})","$1-$2-$3","[2-489]","$NP$FG"],["([57]\\\\d)(\\\\d{3})(\\\\d{4})","$1-$2-$3","[57]","$NP$FG"],["(153)(\\\\d{1,2})(\\\\d{3})(\\\\d{4})","$1 $2 $3 $4","153",null],["(1)([7-9]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1-$2-$3-$4","1[7-9]",null],["(1255)(\\\\d{3})","$1-$2","125",null],["(1200)(\\\\d{3})(\\\\d{3})","$1-$2-$3","120",null],["(1212)(\\\\d{2})(\\\\d{2})","$1-$2-$3","121",null],["(1599)(\\\\d{6})","$1-$2","15",null],["(\\\\d{4})","*$1","[2-689]",null]]]', + 248: '["SC","0(?:[02]|10?)",null,null,null,null,"\\\\d{6,7}","[24689]\\\\d{5,6}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[246]",null]]]', + 297: '["AW","00",null,null,null,null,"\\\\d{7}","[25-9]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 421: '["SK","00","0",null,null,"$NP$FG","\\\\d{6,9}","(?:[2-68]\\\\d{5,8}|9\\\\d{6,8})",[["(2)(1[67])(\\\\d{3,4})","$1 $2 $3","21[67]",null],["([3-5]\\\\d)(1[67])(\\\\d{2,3})","$1 $2 $3","[3-5]",null],["(2)(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1/$2 $3 $4","2",null],["([3-5]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1/$2 $3 $4","[3-5]",null],["([689]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[689]",null],["(9090)(\\\\d{3})","$1 $2","9090",null]]]', + 672: '["NF","00",null,null,null,null,"\\\\d{5,6}","[13]\\\\d{5}",[["(\\\\d{2})(\\\\d{4})","$1 $2","1",null],["(\\\\d)(\\\\d{5})","$1 $2","3",null]]]', + 870: '["001",null,null,null,null,null,"\\\\d{9}","[35-7]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', + 883: '["001",null,null,null,null,null,"\\\\d{9}(?:\\\\d{3})?","51\\\\d{7}(?:\\\\d{3})?",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","510",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","510",null],["(\\\\d{4})(\\\\d{4})(\\\\d{4})","$1 $2 $3","51[13]",null]]]', + 264: '["NA","00","0",null,null,"$NP$FG","\\\\d{8,9}","[68]\\\\d{7,8}",[["(8\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","8[1235]",null],["(6\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","6",null],["(88)(\\\\d{3})(\\\\d{3})","$1 $2 $3","88",null],["(870)(\\\\d{3})(\\\\d{3})","$1 $2 $3","870",null]]]', + 878: '["001",null,null,null,null,null,"\\\\d{12}","1\\\\d{11}",[["(\\\\d{2})(\\\\d{5})(\\\\d{5})","$1 $2 $3",null,null]]]', + 239: '["ST","00",null,null,null,null,"\\\\d{7}","[29]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 357: '["CY","00",null,null,null,null,"\\\\d{8}","[257-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{6})","$1 $2",null,null]]]', + 240: '["GQ","00",null,null,null,null,"\\\\d{9}","[23589]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[235]",null],["(\\\\d{3})(\\\\d{6})","$1 $2","[89]",null]]]', + 506: '["CR","00",null,"(19(?:0[012468]|1[09]|20|66|77|99))",null,null,"\\\\d{8,10}","[24-9]\\\\d{7,9}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[24-7]|8[3-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1-$2-$3","[89]0",null]]]', + 86: '["CN","(1(?:[129]\\\\d{3}|79\\\\d{2}))?00","0","(1(?:[129]\\\\d{3}|79\\\\d{2}))|0",null,null,"\\\\d{4,12}","[1-7]\\\\d{6,11}|8[0-357-9]\\\\d{6,9}|9\\\\d{7,10}",[["(80\\\\d{2})(\\\\d{4})","$1 $2","80[2678]","$NP$FG"],["([48]00)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[48]00",null],["(\\\\d{5,6})","$1","100|95",null,"NA"],["(\\\\d{2})(\\\\d{5,6})","$1 $2","(?:10|2\\\\d)[19]","$NP$FG"],["(\\\\d{3})(\\\\d{5,6})","$1 $2","[3-9]","$NP$FG"],["(\\\\d{3,4})(\\\\d{4})","$1 $2","[2-9]",null,"NA"],["(21)(\\\\d{4})(\\\\d{4,6})","$1 $2 $3","21","$NP$FG"],["([12]\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","10[1-9]|2[02-9]","$NP$FG"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","3(?:1[02-9]|35|49|5|7[02-68]|9[1-68])|4(?:1[02-9]|2[179]|[35][2-9]|6[4789]|7\\\\d|8[23])|5(?:3[03-9]|4[36]|5[02-9]|6[1-46]|7[028]|80|9[2-46-9])|6(?:3[1-5]|6[0238]|9[12])|7(?:01|[1579]|2[248]|3[04-9]|4[3-6]|6[2368])|8(?:1[236-8]|2[5-7]|3|5[1-9]|7[02-9]|8[3678]|9[1-7])|9(?:0[1-3689]|1[1-79]|[379]|4[13]|5[1-5])","$NP$FG"],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","3(?:11|7[179])|4(?:[15]1|3[1-35])|5(?:1|2[37]|3[12]|51|7[13-79]|9[15])|7(?:31|5[457]|6[09]|91)|8(?:[57]1|98)","$NP$FG"],["(\\\\d{4})(\\\\d{3})(\\\\d{4})","$1 $2 $3","807","$NP$FG"],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","1[3-578]",null],["(10800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","108",null],["(\\\\d{3})(\\\\d{7,8})","$1 $2","950",null]]]', + 257: '["BI","00",null,null,null,null,"\\\\d{8}","[267]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 683: '["NU","00",null,null,null,null,"\\\\d{4}","[1-5]\\\\d{3}"]', + 43: '["AT","00","0",null,null,"$NP$FG","\\\\d{3,13}","[1-9]\\\\d{3,12}",[["(116\\\\d{3})","$1","116","$FG"],["(1)(\\\\d{3,12})","$1 $2","1",null],["(5\\\\d)(\\\\d{3,5})","$1 $2","5[079]",null],["(5\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","5[079]",null],["(5\\\\d)(\\\\d{4})(\\\\d{4,7})","$1 $2 $3","5[079]",null],["(\\\\d{3})(\\\\d{3,10})","$1 $2","316|46|51|732|6(?:5[0-3579]|[6-9])|7(?:[28]0)|[89]",null],["(\\\\d{4})(\\\\d{3,9})","$1 $2","2|3(?:1[1-578]|[3-8])|4[2378]|5[2-6]|6(?:[12]|4[1-9]|5[468])|7(?:2[1-8]|35|4[1-8]|[5-79])",null]]]', + 247: '["AC","00",null,null,null,null,"\\\\d{5,6}","[46]\\\\d{4}|[01589]\\\\d{5}"]', + 675: '["PG","00",null,null,null,null,"\\\\d{7,8}","[1-9]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[13-689]|27",null],["(\\\\d{4})(\\\\d{4})","$1 $2","20|7",null]]]', + 376: '["AD","00",null,null,null,null,"\\\\d{6,9}","[16]\\\\d{5,8}|[37-9]\\\\d{5}",[["(\\\\d{3})(\\\\d{3})","$1 $2","[137-9]|6[0-8]",null],["(\\\\d{4})(\\\\d{4})","$1 $2","180",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","690",null]]]', + 63: '["PH","00","0",null,null,null,"\\\\d{5,13}","2\\\\d{5,7}|[3-9]\\\\d{7,9}|1800\\\\d{7,9}",[["(2)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2","($NP$FG)"],["(2)(\\\\d{5})","$1 $2","2","($NP$FG)"],["(\\\\d{4})(\\\\d{4,6})","$1 $2","3(?:23|39|46)|4(?:2[3-6]|[35]9|4[26]|76)|5(?:22|44)|642|8(?:62|8[245])","($NP$FG)"],["(\\\\d{5})(\\\\d{4})","$1 $2","346|4(?:27|9[35])|883","($NP$FG)"],["([3-8]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[3-8]","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","81|9","$NP$FG"],["(1800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["(1800)(\\\\d{1,2})(\\\\d{3})(\\\\d{4})","$1 $2 $3 $4","1",null]]]', + 236: '["CF","00",null,null,null,null,"\\\\d{8}","[278]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 590: [ + '["GP","00","0",null,null,"$NP$FG","\\\\d{9}","[56]\\\\d{8}",[["([56]90)(\\\\d{2})(\\\\d{4})","$1 $2-$3",null,null]]]', + '["BL","00","0",null,null,null,"\\\\d{9}","[56]\\\\d{8}"]', + '["MF","00","0",null,null,null,"\\\\d{9}","[56]\\\\d{8}"]', + ], + 53: '["CU","119","0",null,null,"($NP$FG)","\\\\d{4,8}","[2-57]\\\\d{5,7}",[["(\\\\d)(\\\\d{6,7})","$1 $2","7",null],["(\\\\d{2})(\\\\d{4,6})","$1 $2","[2-4]",null],["(\\\\d)(\\\\d{7})","$1 $2","5","$NP$FG"]]]', + 64: '["NZ","0(?:0|161)","0",null,null,"$NP$FG","\\\\d{7,11}","6[235-9]\\\\d{6}|[2-57-9]\\\\d{7,10}",[["([34679])(\\\\d{3})(\\\\d{4})","$1-$2 $3","[346]|7[2-57-9]|9[1-9]",null],["(24099)(\\\\d{3})","$1 $2","240",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","21",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,5})","$1 $2 $3","2(?:1[1-9]|[69]|7[0-35-9])|70|86",null],["(2\\\\d)(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","2[028]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2(?:10|74)|5|[89]0",null]]]', + 965: '["KW","00",null,null,null,null,"\\\\d{7,8}","[12569]\\\\d{6,7}",[["(\\\\d{4})(\\\\d{3,4})","$1 $2","[16]|2(?:[0-35-9]|4[0-35-9])|9[024-9]|52[25]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","244|5(?:[015]|66)",null]]]', + 224: '["GN","00",null,null,null,null,"\\\\d{8,9}","[367]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","3",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[67]",null]]]', + 973: '["BH","00",null,null,null,null,"\\\\d{8}","[136-9]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', + 32: '["BE","00","0",null,null,"$NP$FG","\\\\d{8,9}","[1-9]\\\\d{7,8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","4[6-9]",null],["(\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[23]|4[23]|9[2-4]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[156]|7[018]|8(?:0[1-9]|[1-79])",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","(?:80|9)0",null]]]', + 249: '["SD","00","0",null,null,"$NP$FG","\\\\d{9}","[19]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3",null,null]]]', + 678: '["VU","00",null,null,null,null,"\\\\d{5,7}","[2-57-9]\\\\d{4,6}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[579]",null]]]', + 52: '["MX","0[09]","01","0[12]|04[45](\\\\d{10})","1$1","$NP $FG","\\\\d{7,11}","[1-9]\\\\d{9,10}",[["([358]\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","33|55|81",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2467]|3[0-2457-9]|5[089]|8[02-9]|9[0-35-9]",null],["(1)([358]\\\\d)(\\\\d{4})(\\\\d{4})","044 $2 $3 $4","1(?:33|55|81)","$FG","$1 $2 $3 $4"],["(1)(\\\\d{3})(\\\\d{3})(\\\\d{4})","044 $2 $3 $4","1(?:[2467]|3[0-2457-9]|5[089]|8[2-9]|9[1-35-9])","$FG","$1 $2 $3 $4"]]]', + 968: '["OM","00",null,null,null,null,"\\\\d{7,9}","(?:5|[279]\\\\d)\\\\d{6}|800\\\\d{5,6}",[["(2\\\\d)(\\\\d{6})","$1 $2","2",null],["([79]\\\\d{3})(\\\\d{4})","$1 $2","[79]",null],["([58]00)(\\\\d{4,6})","$1 $2","[58]",null]]]', + 599: [ + '["CW","00",null,null,null,null,"\\\\d{7,8}","[169]\\\\d{6,7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[13-7]",null],["(9)(\\\\d{3})(\\\\d{4})","$1 $2 $3","9",null]]]', + '["BQ","00",null,null,null,null,"\\\\d{7}","[347]\\\\d{6}"]', + ], + 800: '["001",null,null,null,null,null,"\\\\d{8}","\\\\d{8}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', + 386: '["SI","00","0",null,null,"$NP$FG","\\\\d{5,8}","[1-7]\\\\d{6,7}|[89]\\\\d{4,7}",[["(\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[12]|3[24-8]|4[24-8]|5[2-8]|7[3-8]","($NP$FG)"],["([3-7]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[37][01]|4[0139]|51|6",null],["([89][09])(\\\\d{3,6})","$1 $2","[89][09]",null],["([58]\\\\d{2})(\\\\d{5})","$1 $2","59|8[1-3]",null]]]', + 679: '["FJ","0(?:0|52)",null,null,null,null,"\\\\d{7}(?:\\\\d{4})?","[35-9]\\\\d{6}|0\\\\d{10}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[35-9]",null],["(\\\\d{4})(\\\\d{3})(\\\\d{4})","$1 $2 $3","0",null]]]', + 238: '["CV","0",null,null,null,null,"\\\\d{7}","[259]\\\\d{6}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', + 691: '["FM","00",null,null,null,null,"\\\\d{7}","[39]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 262: [ + '["RE","00","0",null,null,"$NP$FG","\\\\d{9}","[268]\\\\d{8}",[["([268]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + '["YT","00","0",null,null,"$NP$FG","\\\\d{9}","[268]\\\\d{8}"]', + ], + 241: '["GA","00",null,null,null,null,"\\\\d{7,8}","0?\\\\d{7}",[["(\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[2-7]","0$FG"],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","0",null]]]', + 370: '["LT","00","8","[08]",null,"($NP-$FG)","\\\\d{8}","[3-9]\\\\d{7}",[["([34]\\\\d)(\\\\d{6})","$1 $2","37|4(?:1|5[45]|6[2-4])",null],["([3-6]\\\\d{2})(\\\\d{5})","$1 $2","3[148]|4(?:[24]|6[09])|528|6",null],["([7-9]\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","[7-9]","$NP $FG"],["(5)(2\\\\d{2})(\\\\d{4})","$1 $2 $3","52[0-79]",null]]]', + 256: '["UG","00[057]","0",null,null,"$NP$FG","\\\\d{5,9}","\\\\d{9}",[["(\\\\d{3})(\\\\d{6})","$1 $2","[7-9]|20(?:[013-8]|2[5-9])|4(?:6[45]|[7-9])",null],["(\\\\d{2})(\\\\d{7})","$1 $2","3|4(?:[1-5]|6[0-36-9])",null],["(2024)(\\\\d{5})","$1 $2","2024",null]]]', + 677: '["SB","0[01]",null,null,null,null,"\\\\d{5,7}","[1-9]\\\\d{4,6}",[["(\\\\d{2})(\\\\d{5})","$1 $2","[7-9]",null]]]', + 377: '["MC","00","0",null,null,"$NP$FG","\\\\d{8,9}","[34689]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[39]","$FG"],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","4",null],["(6)(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","6",null],["(\\\\d{3})(\\\\d{3})(\\\\d{2})","$1 $2 $3","8","$FG"]]]', + 382: '["ME","00","0",null,null,"$NP$FG","\\\\d{6,9}","[2-9]\\\\d{7,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-57-9]|6[036-9]",null]]]', + 231: '["LR","00","0",null,null,"$NP$FG","\\\\d{7,9}","2\\\\d{7,8}|[378]\\\\d{8}|4\\\\d{6}|5\\\\d{6,8}",[["(2\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","2",null],["([4-5])(\\\\d{3})(\\\\d{3})","$1 $2 $3","[45]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[23578]",null]]]', + 591: '["BO","00(1\\\\d)?","0","0(1\\\\d)?",null,null,"\\\\d{7,8}","[23467]\\\\d{7}",[["([234])(\\\\d{7})","$1 $2","[234]",null],["([67]\\\\d{7})","$1","[67]",null]]]', + 808: '["001",null,null,null,null,null,"\\\\d{8}","\\\\d{8}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', + 964: '["IQ","00","0",null,null,"$NP$FG","\\\\d{6,10}","[1-7]\\\\d{7,9}",[["(1)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["([2-6]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[2-6]",null],["(7\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","7",null]]]', + 225: '["CI","00",null,null,null,null,"\\\\d{8}","[02-8]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 992: '["TJ","810","8",null,null,"$FG","\\\\d{3,9}","[3-57-9]\\\\d{8}",[["([349]\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3","[34]7|91[78]",null],["([457-9]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","4[148]|[578]|9(?:1[59]|[0235-9])",null],["(331700)(\\\\d)(\\\\d{2})","$1 $2 $3","331",null],["(\\\\d{4})(\\\\d)(\\\\d{4})","$1 $2 $3","3[1-5]",null]]]', + 55: '["BR","00(?:1[245]|2[1-35]|31|4[13]|[56]5|99)","0","(?:0|90)(?:(1[245]|2[135]|[34]1)(\\\\d{10,11}))?","$2",null,"\\\\d{8,11}","[1-46-9]\\\\d{7,10}|5(?:[0-4]\\\\d{7,9}|5(?:[2-8]\\\\d{7}|9\\\\d{7,8}))",[["(\\\\d{4})(\\\\d{4})","$1-$2","[2-9](?:[1-9]|0[1-9])","$FG","NA"],["(\\\\d{5})(\\\\d{4})","$1-$2","9(?:[1-9]|0[1-9])","$FG","NA"],["(\\\\d{3,5})","$1","1[125689]","$FG","NA"],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2-$3","[1-9][1-9]","($FG)"],["(\\\\d{2})(\\\\d{5})(\\\\d{4})","$1 $2-$3","(?:[14689][1-9]|2[12478]|3[1-578]|5[1-5]|7[13-579])9","($FG)"],["(\\\\d{4})(\\\\d{4})","$1-$2","(?:300|40(?:0|20))",null],["([3589]00)(\\\\d{2,3})(\\\\d{4})","$1 $2 $3","[3589]00","$NP$FG"]]]', + 674: '["NR","00",null,null,null,null,"\\\\d{7}","[458]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 967: '["YE","00","0",null,null,"$NP$FG","\\\\d{6,9}","[1-7]\\\\d{6,8}",[["([1-7])(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[1-6]|7[24-68]",null],["(7\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","7[0137]",null]]]', + 49: '["DE","00","0",null,null,"$NP$FG","\\\\d{2,15}","[1-35-9]\\\\d{3,14}|4(?:[0-8]\\\\d{3,12}|9(?:[0-37]\\\\d|4(?:[1-35-8]|4\\\\d?)|5\\\\d{1,2}|6[1-8]\\\\d?)\\\\d{2,8})",[["(1\\\\d{2})(\\\\d{7,8})","$1 $2","1[67]",null],["(15\\\\d{3})(\\\\d{6})","$1 $2","15[0568]",null],["(1\\\\d{3})(\\\\d{7})","$1 $2","15",null],["(\\\\d{2})(\\\\d{3,11})","$1 $2","3[02]|40|[68]9",null],["(\\\\d{3})(\\\\d{3,11})","$1 $2","2(?:\\\\d1|0[2389]|1[24]|28|34)|3(?:[3-9][15]|40)|[4-8][1-9]1|9(?:06|[1-9]1)",null],["(\\\\d{4})(\\\\d{2,11})","$1 $2","[24-6]|[7-9](?:\\\\d[1-9]|[1-9]\\\\d)|3(?:[3569][02-46-9]|4[2-4679]|7[2-467]|8[2-46-8])",null],["(3\\\\d{4})(\\\\d{1,10})","$1 $2","3",null],["(800)(\\\\d{7,12})","$1 $2","800",null],["(\\\\d{3})(\\\\d)(\\\\d{4,10})","$1 $2 $3","(?:18|90)0|137",null],["(1\\\\d{2})(\\\\d{5,11})","$1 $2","181",null],["(18\\\\d{3})(\\\\d{6})","$1 $2","185",null],["(18\\\\d{2})(\\\\d{7})","$1 $2","18[68]",null],["(18\\\\d)(\\\\d{8})","$1 $2","18[2-579]",null],["(700)(\\\\d{4})(\\\\d{4})","$1 $2 $3","700",null],["(138)(\\\\d{4})","$1 $2","138",null],["(15[013-68])(\\\\d{2})(\\\\d{8})","$1 $2 $3","15[013-68]",null],["(15[279]\\\\d)(\\\\d{2})(\\\\d{7})","$1 $2 $3","15[279]",null],["(1[67]\\\\d)(\\\\d{2})(\\\\d{7,8})","$1 $2 $3","1(?:6[023]|7)",null]]]', + 31: '["NL","00","0",null,null,"$NP$FG","\\\\d{5,10}","1\\\\d{4,8}|[2-7]\\\\d{8}|[89]\\\\d{6,9}",[["([1-578]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[035]|2[0346]|3[03568]|4[0356]|5[0358]|7|8[4578]",null],["([1-5]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1[16-8]|2[259]|3[124]|4[17-9]|5[124679]",null],["(6)(\\\\d{8})","$1 $2","6[0-57-9]",null],["(66)(\\\\d{7})","$1 $2","66",null],["(14)(\\\\d{3,4})","$1 $2","14","$FG"],["([89]0\\\\d)(\\\\d{4,7})","$1 $2","80|9",null]]]', + 970: '["PS","00","0",null,null,"$NP$FG","\\\\d{4,10}","[24589]\\\\d{7,8}|1(?:[78]\\\\d{8}|[49]\\\\d{2,3})",[["([2489])(2\\\\d{2})(\\\\d{4})","$1 $2 $3","[2489]",null],["(5[69]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","5",null],["(1[78]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1[78]","$FG"]]]', + 58: '["VE","00","0",null,null,"$NP$FG","\\\\d{7,10}","[24589]\\\\d{9}",[["(\\\\d{3})(\\\\d{7})","$1-$2",null,null]]]', + 856: '["LA","00","0",null,null,"$NP$FG","\\\\d{6,10}","[2-8]\\\\d{7,9}",[["(20)(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","20",null],["([2-8]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","2[13]|3[14]|[4-8]",null],["(30)(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","30",null]]]', + 354: '["IS","1(?:0(?:01|10|20)|100)|00",null,null,null,null,"\\\\d{7,9}","[4-9]\\\\d{6}|38\\\\d{7}",[["(\\\\d{3})(\\\\d{4})","$1 $2","[4-9]",null],["(3\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","3",null]]]', + 242: '["CG","00",null,null,null,null,"\\\\d{9}","[028]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[02]",null],["(\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","8",null]]]', + 423: '["LI","00","0","0|10(?:01|20|66)",null,null,"\\\\d{7,9}","6\\\\d{8}|[23789]\\\\d{6}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3","[23789]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","6[56]",null],["(69)(7\\\\d{2})(\\\\d{4})","$1 $2 $3","697",null]]]', + 213: '["DZ","00","0",null,null,"$NP$FG","\\\\d{8,9}","(?:[1-4]|[5-9]\\\\d)\\\\d{7}",[["([1-4]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[1-4]",null],["([5-8]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[5-8]",null],["(9\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","9",null]]]', + 371: '["LV","00",null,null,null,null,"\\\\d{8}","[2689]\\\\d{7}",[["([2689]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', + 503: '["SV","00",null,null,null,null,"\\\\d{7,8}|\\\\d{11}","[267]\\\\d{7}|[89]\\\\d{6}(?:\\\\d{4})?",[["(\\\\d{4})(\\\\d{4})","$1 $2","[267]",null],["(\\\\d{3})(\\\\d{4})","$1 $2","[89]",null],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","[89]",null]]]', + 685: '["WS","0",null,null,null,null,"\\\\d{5,7}","[2-8]\\\\d{4,6}",[["(8\\\\d{2})(\\\\d{3,4})","$1 $2","8",null],["(7\\\\d)(\\\\d{5})","$1 $2","7",null],["(\\\\d{5})","$1","[2-6]",null]]]', + 880: '["BD","00","0",null,null,"$NP$FG","\\\\d{6,10}","[2-79]\\\\d{5,9}|1\\\\d{9}|8[0-7]\\\\d{4,8}",[["(2)(\\\\d{7,8})","$1-$2","2",null],["(\\\\d{2})(\\\\d{4,6})","$1-$2","[3-79]1",null],["(\\\\d{4})(\\\\d{3,6})","$1-$2","1|3(?:0|[2-58]2)|4(?:0|[25]2|3[23]|[4689][25])|5(?:[02-578]2|6[25])|6(?:[0347-9]2|[26][25])|7[02-9]2|8(?:[023][23]|[4-7]2)|9(?:[02][23]|[458]2|6[016])",null],["(\\\\d{3})(\\\\d{3,7})","$1-$2","[3-79][2-9]|8",null]]]', + 265: '["MW","00","0",null,null,"$NP$FG","\\\\d{7,9}","(?:1(?:\\\\d{2})?|[2789]\\\\d{2})\\\\d{6}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1",null],["(2\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","2",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[1789]",null]]]', + 65: '["SG","0[0-3]\\\\d",null,null,null,null,"\\\\d{8,11}","[36]\\\\d{7}|[17-9]\\\\d{7,10}",[["([3689]\\\\d{3})(\\\\d{4})","$1 $2","[369]|8[1-9]",null],["(1[89]00)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[89]",null],["(7000)(\\\\d{4})(\\\\d{3})","$1 $2 $3","70",null],["(800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80",null]]]', + 504: '["HN","00",null,null,null,null,"\\\\d{8}","[237-9]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1-$2",null,null]]]', + 688: '["TV","00",null,null,null,null,"\\\\d{5,7}","[279]\\\\d{4,6}"]', + 84: '["VN","00","0",null,null,"$NP$FG","\\\\d{7,10}","[167]\\\\d{6,9}|[2-59]\\\\d{7,9}|8\\\\d{6,8}",[["([17]99)(\\\\d{4})","$1 $2","[17]99",null],["([48])(\\\\d{4})(\\\\d{4})","$1 $2 $3","4|8(?:[1-57]|6[0-79]|9[0-7])",null],["([235-7]\\\\d)(\\\\d{4})(\\\\d{3})","$1 $2 $3","2[025-79]|3[0136-9]|5[2-9]|6[0-46-8]|7[02-79]",null],["(80)(\\\\d{5})","$1 $2","80",null],["(69\\\\d)(\\\\d{4,5})","$1 $2","69",null],["([235-7]\\\\d{2})(\\\\d{4})(\\\\d{3})","$1 $2 $3","2[0-489]|3[25]|5[01]|65|7[18]",null],["([89]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","8(?:68|8|9[89])|9",null],["(1[2689]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1(?:[26]|8[68]|99)",null],["(1[89]00)(\\\\d{4,6})","$1 $2","1[89]0","$FG"]]]', + 255: '["TZ","00[056]","0",null,null,"$NP$FG","\\\\d{7,9}","\\\\d{9}",[["([24]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[24]",null],["([67]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[67]",null],["([89]\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3","[89]",null]]]', + 222: '["MR","00",null,null,null,null,"\\\\d{8}","[2-48]\\\\d{7}",[["([2-48]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 230: '["MU","0(?:0|[2-7]0|33)",null,null,null,null,"\\\\d{7,8}","[2-9]\\\\d{6,7}",[["([2-46-9]\\\\d{2})(\\\\d{4})","$1 $2","[2-46-9]",null],["(5\\\\d{3})(\\\\d{4})","$1 $2","5",null]]]', + 592: '["GY","001",null,null,null,null,"\\\\d{7}","[2-46-9]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 41: '["CH","00","0",null,null,"$NP$FG","\\\\d{9}(?:\\\\d{3})?","[2-9]\\\\d{8}|860\\\\d{9}",[["([2-9]\\\\d)(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[2-7]|[89]1",null],["([89]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","8[047]|90",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4 $5","860",null]]]', + 39: [ + '["IT","00",null,null,null,null,"\\\\d{6,11}","[01589]\\\\d{5,10}|3(?:[12457-9]\\\\d{8}|[36]\\\\d{7,9})",[["(\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","0[26]|55",null],["(0[26])(\\\\d{4})(\\\\d{5})","$1 $2 $3","0[26]",null],["(0[26])(\\\\d{4,6})","$1 $2","0[26]",null],["(0\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","0[13-57-9][0159]",null],["(\\\\d{3})(\\\\d{3,6})","$1 $2","0[13-57-9][0159]|8(?:03|4[17]|9[245])",null],["(0\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","0[13-57-9][2-46-8]",null],["(0\\\\d{3})(\\\\d{2,6})","$1 $2","0[13-57-9][2-46-8]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[13]|8(?:00|4[08]|9[59])",null],["(\\\\d{4})(\\\\d{4})","$1 $2","894",null],["(\\\\d{3})(\\\\d{4})(\\\\d{4})","$1 $2 $3","3",null]]]', + '["VA","00",null,null,null,null,"\\\\d{6,11}","(?:0(?:878\\\\d{5}|6698\\\\d{5})|[1589]\\\\d{5,10}|3(?:[12457-9]\\\\d{8}|[36]\\\\d{7,9}))"]', + ], + 993: '["TM","810","8",null,null,"($NP $FG)","\\\\d{8}","[1-6]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","12",null],["(\\\\d{2})(\\\\d{6})","$1 $2","6","$NP $FG"],["(\\\\d{3})(\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","13|[2-5]",null]]]', + 888: '["001",null,null,null,null,null,"\\\\d{11}","\\\\d{11}",[["(\\\\d{3})(\\\\d{3})(\\\\d{5})","$1 $2 $3",null,null]]]', + 353: '["IE","00","0",null,null,"($NP$FG)","\\\\d{5,10}","[124-9]\\\\d{6,9}",[["(1)(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d{2})(\\\\d{5})","$1 $2","2[24-9]|47|58|6[237-9]|9[35-9]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","40[24]|50[45]",null],["(48)(\\\\d{4})(\\\\d{4})","$1 $2 $3","48",null],["(818)(\\\\d{3})(\\\\d{3})","$1 $2 $3","81",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[24-69]|7[14]",null],["([78]\\\\d)(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","76|8[35-9]","$NP$FG"],["(700)(\\\\d{3})(\\\\d{3})","$1 $2 $3","70","$NP$FG"],["(\\\\d{4})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1(?:8[059]|5)","$FG"]]]', + 966: '["SA","00","0",null,null,"$NP$FG","\\\\d{7,10}","1\\\\d{7,8}|(?:[2-467]|92)\\\\d{7}|5\\\\d{8}|8\\\\d{9}",[["([1-467])(\\\\d{3})(\\\\d{4})","$1 $2 $3","[1-467]",null],["(1\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[1-467]",null],["(5\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","5",null],["(92\\\\d{2})(\\\\d{5})","$1 $2","92","$FG"],["(800)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80","$FG"],["(811)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","81",null]]]', + 380: '["UA","00","0",null,null,"$NP$FG","\\\\d{5,9}","[3-9]\\\\d{8}",[["([3-9]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[38]9|4(?:[45][0-5]|87)|5(?:0|6[37]|7[37])|6[36-8]|7|9[1-9]",null],["([3-689]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","3[1-8]2|4[13678]2|5(?:[12457]2|6[24])|6(?:[49]2|[12][29]|5[24])|8[0-8]|90",null],["([3-6]\\\\d{3})(\\\\d{5})","$1 $2","3(?:5[013-9]|[1-46-8])|4(?:[137][013-9]|6|[45][6-9]|8[4-6])|5(?:[1245][013-9]|6[0135-9]|3|7[4-6])|6(?:[49][013-9]|5[0135-9]|[12][13-8])",null]]]', + 98: '["IR","00","0",null,null,"$NP$FG","\\\\d{4,10}","[1-8]\\\\d{9}|9(?:[0-4]\\\\d{8}|9\\\\d{2,8})",[["(21)(\\\\d{3,5})","$1 $2","21",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","[1-8]",null],["(\\\\d{3})(\\\\d{3})","$1 $2","9",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","9",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","9",null]]]', + 971: '["AE","00","0",null,null,"$NP$FG","\\\\d{5,12}","[2-79]\\\\d{7,8}|800\\\\d{2,9}",[["([2-4679])(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2-4679][2-8]",null],["(5\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","5",null],["([479]00)(\\\\d)(\\\\d{5})","$1 $2 $3","[479]0","$FG"],["([68]00)(\\\\d{2,9})","$1 $2","60|8","$FG"]]]', + 30: '["GR","00",null,null,null,null,"\\\\d{10}","[26-9]\\\\d{9}",[["([27]\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","21|7",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","2[2-9]1|[689]",null],["(2\\\\d{3})(\\\\d{6})","$1 $2","2[2-9][02-9]",null]]]', + 228: '["TG","00",null,null,null,null,"\\\\d{8}","[29]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[29]",null]]]', + 48: '["PL","00",null,null,null,null,"\\\\d{6,9}","[12]\\\\d{6,8}|[3-57-9]\\\\d{8}|6\\\\d{5,8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[14]|2[0-57-9]|3[2-4]|5[24-689]|6[1-3578]|7[14-7]|8[1-79]|9[145]",null],["(\\\\d{2})(\\\\d{1})(\\\\d{4})","$1 $2 $3","[12]2",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","26|39|5[0137]|6[0469]|7[02389]|8[08]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2,3})","$1 $2 $3","64",null],["(\\\\d{3})(\\\\d{3})","$1 $2","64",null]]]', + 886: '["TW","0(?:0[25679]|19)","0",null,null,"$NP$FG","\\\\d{7,10}","2\\\\d{6,8}|[3-689]\\\\d{7,8}|7\\\\d{7,9}",[["(20)(\\\\d)(\\\\d{4})","$1 $2 $3","202",null],["(20)(\\\\d{3})(\\\\d{4})","$1 $2 $3","20[013-9]",null],["([2-8])(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","2[23-8]|[3-6]|[78][1-9]",null],["([89]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","80|9",null],["(70)(\\\\d{4})(\\\\d{4})","$1 $2 $3","70",null]]]', + 212: [ + '["MA","00","0",null,null,"$NP$FG","\\\\d{9}","[5-9]\\\\d{8}",[["([5-7]\\\\d{2})(\\\\d{6})","$1-$2","5(?:2[015-7]|3[0-4])|[67]",null],["([58]\\\\d{3})(\\\\d{5})","$1-$2","5(?:2[2-489]|3[5-9]|92)|892",null],["(5\\\\d{4})(\\\\d{4})","$1-$2","5(?:29|38)",null],["([5]\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","5(?:4[067]|5[03])",null],["(8[09])(\\\\d{7})","$1-$2","8(?:0|9[013-9])",null]]]', + '["EH","00","0",null,null,"$NP$FG","\\\\d{9}","[5-9]\\\\d{8}"]', + ], + 372: '["EE","00",null,null,null,null,"\\\\d{4,10}","1\\\\d{3,4}|[3-9]\\\\d{6,7}|800\\\\d{6,7}",[["([3-79]\\\\d{2})(\\\\d{4})","$1 $2","[369]|4[3-8]|5(?:[0-2]|5[0-478]|6[45])|7[1-9]",null],["(70)(\\\\d{2})(\\\\d{4})","$1 $2 $3","70",null],["(8000)(\\\\d{3})(\\\\d{3})","$1 $2 $3","800",null],["([458]\\\\d{3})(\\\\d{3,4})","$1 $2","40|5|8(?:00|[1-5])",null]]]', + 598: '["UY","0(?:1[3-9]\\\\d|0)","0",null,null,null,"\\\\d{7,8}","[2489]\\\\d{6,7}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[24]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","9[1-9]","$NP$FG"],["(\\\\d{3})(\\\\d{4})","$1 $2","[89]0","$NP$FG"]]]', + 502: '["GT","00",null,null,null,null,"\\\\d{8}(?:\\\\d{3})?","[2-7]\\\\d{7}|1[89]\\\\d{9}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[2-7]",null],["(\\\\d{4})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null]]]', + 82: '["KR","00(?:[124-68]|3\\\\d{2}|7(?:[0-8]\\\\d|9[0-79]))","0","0(8[1-46-8]|85\\\\d{2})?",null,"$NP$FG","\\\\d{3,14}","007\\\\d{9,11}|[1-7]\\\\d{3,9}|8\\\\d{8}",[["(\\\\d{5})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","00798","$FG","NA"],["(\\\\d{5})(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3 $4","00798","$FG","NA"],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1-$2-$3","1(?:0|1[19]|[69]9|5[458])|[57]0",null],["(\\\\d{2})(\\\\d{3,4})(\\\\d{4})","$1-$2-$3","1(?:[01]|5[1-4]|6[2-8]|[7-9])|[68]0|[3-6][1-9][1-9]",null],["(\\\\d{3})(\\\\d)(\\\\d{4})","$1-$2-$3","131",null],["(\\\\d{3})(\\\\d{2})(\\\\d{4})","$1-$2-$3","131",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1-$2-$3","13[2-9]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3-$4","30",null],["(\\\\d)(\\\\d{3,4})(\\\\d{4})","$1-$2-$3","2[1-9]",null],["(\\\\d)(\\\\d{3,4})","$1-$2","21[0-46-9]",null],["(\\\\d{2})(\\\\d{3,4})","$1-$2","[3-6][1-9]1",null],["(\\\\d{4})(\\\\d{4})","$1-$2","1(?:5[246-9]|6[04678]|8[03579])","$FG"]]]', + 253: '["DJ","00",null,null,null,null,"\\\\d{8}","[27]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 91: '["IN","00","0",null,null,"$NP$FG","\\\\d{6,13}","008\\\\d{9}|1\\\\d{7,12}|[2-9]\\\\d{9,10}",[["(\\\\d{5})(\\\\d{5})","$1 $2","600|7(?:[02-8]|19|9[037-9])|8(?:0[015-9]|[1-9]|20)|9",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","11|2[02]|33|4[04]|79[1-9]|80[2-46]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1(?:2[0-249]|3[0-25]|4[145]|[59][14]|7[1257]|[68][1-9])|2(?:1[257]|3[013]|4[01]|5[0137]|6[0158]|78|8[1568]|9[14])|3(?:26|4[1-3]|5[34]|6[01489]|7[02-46]|8[159])|4(?:1[36]|2[1-47]|3[15]|5[12]|6[0-26-9]|7[0-24-9]|8[013-57]|9[014-7])|5(?:1[025]|[36][25]|22|4[28]|5[12]|[78]1|9[15])|6(?:12|[2-4]1|5[17]|6[13]|7[14]|80)|7(?:12|2[14]|3[134]|4[47]|5[15]|[67]1|88)|8(?:16|2[014]|3[126]|6[136]|7[078]|8[34]|91)",null],["(\\\\d{4})(\\\\d{3})(\\\\d{3})","$1 $2 $3","1(?:[23579]|[468][1-9])|[2-8]",null],["(\\\\d{2})(\\\\d{3})(\\\\d{4})(\\\\d{3})","$1 $2 $3 $4","008",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","140","$FG"],["(\\\\d{4})(\\\\d{2})(\\\\d{4})","$1 $2 $3","160","$FG"],["(\\\\d{4})(\\\\d{4,5})","$1 $2","180","$FG"],["(\\\\d{4})(\\\\d{2,4})(\\\\d{4})","$1 $2 $3","180","$FG"],["(\\\\d{4})(\\\\d{3,4})(\\\\d{4})","$1 $2 $3","186","$FG"],["(\\\\d{4})(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3 $4","18[06]","$FG"]]]', + 389: '["MK","00","0",null,null,"$NP$FG","\\\\d{6,8}","[2-578]\\\\d{7}",[["(2)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2",null],["([347]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[347]",null],["([58]\\\\d{2})(\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[58]",null]]]', + 1: [ + '["US","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2-9]\\\\d{9}",[["(\\\\d{3})(\\\\d{4})","$1-$2",null,null,"NA"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","($1) $2-$3",null,null,"$1-$2-$3"]]]', + '["AI","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', + '["AS","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', + '["BB","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', + '["BM","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[4589]\\\\d{9}"]', + '["BS","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', + '["CA","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2-9]\\\\d{9}|3\\\\d{6}"]', + '["DM","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[57-9]\\\\d{9}"]', + '["DO","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', + '["GD","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[4589]\\\\d{9}"]', + '["GU","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', + '["JM","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', + '["KN","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', + '["KY","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[3589]\\\\d{9}"]', + '["LC","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', + '["MP","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', + '["MS","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', + '["PR","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', + '["SX","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', + '["TC","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5689]\\\\d{9}"]', + '["TT","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[589]\\\\d{9}"]', + '["AG","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', + '["VC","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[5789]\\\\d{9}"]', + '["VG","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[2589]\\\\d{9}"]', + '["VI","011","1",null,null,null,"\\\\d{7}(?:\\\\d{3})?","[3589]\\\\d{9}"]', + ], + 60: '["MY","00","0",null,null,null,"\\\\d{6,10}","[13-9]\\\\d{7,9}",[["([4-79])(\\\\d{3})(\\\\d{4})","$1-$2 $3","[4-79]","$NP$FG"],["(3)(\\\\d{4})(\\\\d{4})","$1-$2 $3","3","$NP$FG"],["([18]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1-$2 $3","1[02-46-9][1-9]|8","$NP$FG"],["(1)([36-8]00)(\\\\d{2})(\\\\d{4})","$1-$2-$3-$4","1[36-8]0",null],["(11)(\\\\d{4})(\\\\d{4})","$1-$2 $3","11","$NP$FG"],["(15[49])(\\\\d{3})(\\\\d{4})","$1-$2 $3","15","$NP$FG"]]]', + 355: '["AL","00","0",null,null,"$NP$FG","\\\\d{5,9}","[2-57]\\\\d{7}|6\\\\d{8}|8\\\\d{5,7}|9\\\\d{5}",[["(4)(\\\\d{3})(\\\\d{4})","$1 $2 $3","4[0-6]",null],["(6\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","6",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2358][2-5]|4[7-9]",null],["(\\\\d{3})(\\\\d{3,5})","$1 $2","[235][16-9]|8[016-9]|[79]",null]]]', + 254: '["KE","000","0","005|0",null,"$NP$FG","\\\\d{7,10}","20\\\\d{6,7}|[4-9]\\\\d{6,9}",[["(\\\\d{2})(\\\\d{5,7})","$1 $2","[24-6]",null],["(\\\\d{3})(\\\\d{6})","$1 $2","7",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[89]",null]]]', + 223: '["ML","00",null,null,null,null,"\\\\d{8}","[246-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[246-9]",null],["(\\\\d{4})","$1","67|74",null,"NA"]]]', + 686: '["KI","00",null,"0",null,null,"\\\\d{5,8}","[2458]\\\\d{4}|3\\\\d{4,7}|7\\\\d{7}"]', + 994: '["AZ","00","0",null,null,"($NP$FG)","\\\\d{7,9}","[1-9]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","(?:1[28]|2(?:[45]2|[0-36])|365)",null],["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[4-8]","$NP$FG"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","9","$NP$FG"]]]', + 979: '["001",null,null,null,null,null,"\\\\d{9}","\\\\d{9}",[["(\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3",null,null]]]', + 66: '["TH","00","0",null,null,"$NP$FG","\\\\d{4}|\\\\d{8,10}","[2-9]\\\\d{7,8}|1\\\\d{3}(?:\\\\d{5,6})?",[["(2)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2",null],["([13-9]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","14|[3-9]",null],["(1[89]00)(\\\\d{3})(\\\\d{3})","$1 $2 $3","1","$FG"]]]', + 233: '["GH","00","0",null,null,"$NP$FG","\\\\d{7,9}","[235]\\\\d{8}|8\\\\d{7}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[235]",null],["(\\\\d{3})(\\\\d{5})","$1 $2","8",null]]]', + 593: '["EC","00","0",null,null,"($NP$FG)","\\\\d{7,11}","1\\\\d{9,10}|[2-8]\\\\d{7}|9\\\\d{8}",[["(\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2-$3","[247]|[356][2-8]",null,"$1-$2-$3"],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","9","$NP$FG"],["(1800)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","1","$FG"]]]', + 509: '["HT","00",null,null,null,null,"\\\\d{8}","[2-489]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{4})","$1 $2 $3",null,null]]]', + 54: '["AR","00","0","0?(?:(11|2(?:2(?:02?|[13]|2[13-79]|4[1-6]|5[2457]|6[124-8]|7[1-4]|8[13-6]|9[1267])|3(?:02?|1[467]|2[03-6]|3[13-8]|[49][2-6]|5[2-8]|[67])|4(?:7[3-578]|9)|6(?:[0136]|2[24-6]|4[6-8]?|5[15-8])|80|9(?:0[1-3]|[19]|2\\\\d|3[1-6]|4[02568]?|5[2-4]|6[2-46]|72?|8[23]?))|3(?:3(?:2[79]|6|8[2578])|4(?:0[0-24-9]|[12]|3[5-8]?|4[24-7]|5[4-68]?|6[02-9]|7[126]|8[2379]?|9[1-36-8])|5(?:1|2[1245]|3[237]?|4[1-46-9]|6[2-4]|7[1-6]|8[2-5]?)|6[24]|7(?:[069]|1[1568]|2[15]|3[145]|4[13]|5[14-8]|7[2-57]|8[126])|8(?:[01]|2[15-7]|3[2578]?|4[13-6]|5[4-8]?|6[1-357-9]|7[36-8]?|8[5-8]?|9[124])))?15)?","9$1","$NP$FG","\\\\d{6,11}","11\\\\d{8}|[2368]\\\\d{9}|9\\\\d{10}",[["([68]\\\\d{2})(\\\\d{3})(\\\\d{4})","$1-$2-$3","[68]",null],["(\\\\d{2})(\\\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\\\d{3})(\\\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\\\d{4})(\\\\d{4})","$1-$2","[2-9]","$FG","NA"],["(9)(11)(\\\\d{4})(\\\\d{4})","$2 15-$3-$4","911",null,"$1 $2 $3-$4"],["(9)(\\\\d{3})(\\\\d{3})(\\\\d{4})","$2 15-$3-$4","9(?:2[234689]|3[3-8])",null,"$1 $2 $3-$4"],["(9)(\\\\d{4})(\\\\d{2})(\\\\d{4})","$2 15-$3-$4","9[23]",null,"$1 $2 $3-$4"],["(11)(\\\\d{4})(\\\\d{4})","$1 $2-$3","1",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2-$3","2(?:2[013]|3[067]|49|6[01346]|80|9[147-9])|3(?:36|4[1-358]|5[138]|6[24]|7[069]|8[013578])",null],["(\\\\d{4})(\\\\d{2})(\\\\d{4})","$1 $2-$3","[23]",null],["(\\\\d{3})","$1","1[012]|911","$FG","NA"]]]', + 57: '["CO","00(?:4(?:[14]4|56)|[579])","0","0([3579]|4(?:44|56))?",null,null,"\\\\d{7,11}","(?:[13]\\\\d{0,3}|[24-8])\\\\d{7}",[["(\\\\d)(\\\\d{7})","$1 $2","1(?:8[2-9]|9[0-3]|[2-7])|[24-8]","($FG)"],["(\\\\d{3})(\\\\d{7})","$1 $2","3",null],["(1)(\\\\d{3})(\\\\d{7})","$1-$2-$3","1(?:80|9[04])","$NP$FG","$1 $2 $3"]]]', + 597: '["SR","00",null,null,null,null,"\\\\d{6,7}","[2-8]\\\\d{5,6}",[["(\\\\d{3})(\\\\d{3})","$1-$2","[2-4]|5[2-58]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1-$2-$3","56",null],["(\\\\d{3})(\\\\d{4})","$1-$2","[6-8]",null]]]', + 676: '["TO","00",null,null,null,null,"\\\\d{5,7}","[02-8]\\\\d{4,6}",[["(\\\\d{2})(\\\\d{3})","$1-$2","[1-6]|7[0-4]|8[05]",null],["(\\\\d{3})(\\\\d{4})","$1 $2","7[5-9]|8[47-9]",null],["(\\\\d{4})(\\\\d{3})","$1 $2","0",null]]]', + 505: '["NI","00",null,null,null,null,"\\\\d{8}","[12578]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2",null,null]]]', + 850: '["KP","00|99","0",null,null,"$NP$FG","\\\\d{6,8}|\\\\d{10}","1\\\\d{9}|[28]\\\\d{7}",[["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","2",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","8",null]]]', + 7: [ + '["RU","810","8",null,null,"$NP ($FG)","\\\\d{10}","[3489]\\\\d{9}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1-$2-$3","[1-79]","$FG","NA"],["([3489]\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2-$3-$4","[34689]",null],["(7\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","7",null]]]', + '["KZ","810","8",null,null,null,"\\\\d{10}","(?:33\\\\d|7\\\\d{2}|80[09])\\\\d{7}"]', + ], + 268: '["SZ","00",null,null,null,null,"\\\\d{8}","[027]\\\\d{7}",[["(\\\\d{4})(\\\\d{4})","$1 $2","[027]",null]]]', + 501: '["BZ","00",null,null,null,null,"\\\\d{7}(?:\\\\d{4})?","[2-8]\\\\d{6}|0\\\\d{10}",[["(\\\\d{3})(\\\\d{4})","$1-$2","[2-8]",null],["(0)(800)(\\\\d{4})(\\\\d{3})","$1-$2-$3-$4","0",null]]]', + 252: '["SO","00","0",null,null,null,"\\\\d{6,9}","[1-9]\\\\d{5,8}",[["(\\\\d{6})","$1","[134]",null],["(\\\\d)(\\\\d{6})","$1 $2","2[0-79]|[13-5]",null],["(\\\\d)(\\\\d{7})","$1 $2","24|[67]",null],["(\\\\d{2})(\\\\d{4})","$1 $2","8[125]",null],["(\\\\d{2})(\\\\d{5,7})","$1 $2","15|28|6[1-35-9]|799|9[2-9]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","3[59]|4[89]|6[24-6]|79|8[08]|90",null]]]', + 229: '["BJ","00",null,null,null,null,"\\\\d{4,8}","[2689]\\\\d{7}|7\\\\d{3}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 680: '["PW","01[12]",null,null,null,null,"\\\\d{7}","[2-8]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 263: '["ZW","00","0",null,null,"$NP$FG","\\\\d{3,10}","2(?:[012457-9]\\\\d{3,8}|6(?:[14]\\\\d{7}|\\\\d{4}))|[13-79]\\\\d{4,9}|8[06]\\\\d{8}",[["([49])(\\\\d{3})(\\\\d{2,4})","$1 $2 $3","4|9[2-9]",null],["(7\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","7",null],["(86\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","86[24]",null],["([2356]\\\\d{2})(\\\\d{3,5})","$1 $2","2(?:0[45]|2[278]|[49]8|[78])|3(?:08|17|3[78]|7[1569]|8[37]|98)|5[15][78]|6(?:[29]8|[38]7|6[78]|75|[89]8)",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","2(?:1[39]|2[0157]|6[14]|7[35]|84)|329",null],["([1-356]\\\\d)(\\\\d{3,5})","$1 $2","1[3-9]|2[0569]|3[0-69]|5[05689]|6[0-46-9]",null],["([235]\\\\d)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[23]9|54",null],["([25]\\\\d{3})(\\\\d{3,5})","$1 $2","(?:25|54)8",null],["(8\\\\d{3})(\\\\d{6})","$1 $2","86",null],["(80\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80",null]]]', + 90: '["TR","00","0",null,null,null,"\\\\d{7,10}","[2-589]\\\\d{9}|444\\\\d{4}",[["(\\\\d{3})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[23]|4(?:[0-35-9]|4[0-35-9])","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","5[02-69]","$NP$FG"],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","51|[89]","$NP$FG"],["(444)(\\\\d{1})(\\\\d{3})","$1 $2 $3","444",null]]]', + 352: '["LU","00",null,"(15(?:0[06]|1[12]|35|4[04]|55|6[26]|77|88|99)\\\\d)",null,null,"\\\\d{4,11}","[24-9]\\\\d{3,10}|3(?:[0-46-9]\\\\d{2,9}|5[013-9]\\\\d{1,8})",[["(\\\\d{2})(\\\\d{3})","$1 $2","[2-5]|7[1-9]|[89](?:[1-9]|0[2-9])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3","[2-5]|7[1-9]|[89](?:[1-9]|0[2-9])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","20",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{1,2})","$1 $2 $3 $4","2(?:[0367]|4[3-8])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","20",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{1,2})","$1 $2 $3 $4 $5","2(?:[0367]|4[3-8])",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{1,4})","$1 $2 $3 $4","2(?:[12589]|4[12])|[3-5]|7[1-9]|8(?:[1-9]|0[2-9])|9(?:[1-9]|0[2-46-9])",null],["(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3","70|80[01]|90[015]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","6",null]]]', + 47: [ + '["NO","00",null,null,null,null,"\\\\d{5}(?:\\\\d{3})?","0\\\\d{4}|[2-9]\\\\d{7}",[["([489]\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","[489]",null],["([235-7]\\\\d)(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[235-7]",null]]]', + '["SJ","00",null,null,null,null,"\\\\d{5}(?:\\\\d{3})?","0\\\\d{4}|[45789]\\\\d{7}"]', + ], + 243: '["CD","00","0",null,null,"$NP$FG","\\\\d{7,9}","[2-6]\\\\d{6}|[18]\\\\d{6,8}|9\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","12",null],["([89]\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","8[0-2459]|9",null],["(\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","88",null],["(\\\\d{2})(\\\\d{5})","$1 $2","[1-6]",null]]]', + 220: '["GM","00",null,null,null,null,"\\\\d{7}","[2-9]\\\\d{6}",[["(\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 687: '["NC","00",null,null,null,null,"\\\\d{6}","[2-57-9]\\\\d{5}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1.$2.$3","[2-46-9]|5[0-4]",null]]]', + 995: '["GE","00","0",null,null,null,"\\\\d{6,9}","[34578]\\\\d{8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[348]","$NP$FG"],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","7","$NP$FG"],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","5","$FG"]]]', + 961: '["LB","00","0",null,null,null,"\\\\d{7,8}","[13-9]\\\\d{6,7}",[["(\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[13-6]|7(?:[2-57]|62|8[0-7]|9[04-9])|8[02-9]|9","$NP$FG"],["([7-9]\\\\d)(\\\\d{3})(\\\\d{3})","$1 $2 $3","[89][01]|7(?:[01]|6[013-9]|8[89]|9[1-3])",null]]]', + 40: '["RO","00","0",null,null,"$NP$FG","\\\\d{6,9}","[23]\\\\d{5,8}|[7-9]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[23]1",null],["(\\\\d{2})(\\\\d{4})","$1 $2","[23]1",null],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[23][3-7]|[7-9]",null],["(2\\\\d{2})(\\\\d{3})","$1 $2","2[3-6]",null]]]', + 232: '["SL","00","0",null,null,"($NP$FG)","\\\\d{6,8}","[2-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{6})","$1 $2",null,null]]]', + 594: '["GF","00","0",null,null,"$NP$FG","\\\\d{9}","[56]\\\\d{8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 976: '["MN","001","0",null,null,"$NP$FG","\\\\d{6,10}","[12]\\\\d{7,9}|[57-9]\\\\d{7}",[["([12]\\\\d)(\\\\d{2})(\\\\d{4})","$1 $2 $3","[12]1",null],["([12]2\\\\d)(\\\\d{5,6})","$1 $2","[12]2[1-3]",null],["([12]\\\\d{3})(\\\\d{5})","$1 $2","[12](?:27|[3-5])",null],["(\\\\d{4})(\\\\d{4})","$1 $2","[57-9]","$FG"],["([12]\\\\d{4})(\\\\d{4,5})","$1 $2","[12](?:27|[3-5])",null]]]', + 20: '["EG","00","0",null,null,"$NP$FG","\\\\d{5,10}","1\\\\d{4,9}|[2456]\\\\d{8}|3\\\\d{7}|[89]\\\\d{8,9}",[["(\\\\d)(\\\\d{7,8})","$1 $2","[23]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1[012]|[89]00",null],["(\\\\d{2})(\\\\d{6,7})","$1 $2","1[35]|[4-6]|[89][2-9]",null]]]', + 689: '["PF","00",null,null,null,null,"\\\\d{6}(?:\\\\d{2})?","4\\\\d{5,7}|8\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","4[09]|8[79]",null],["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3","44",null]]]', + 56: '["CL","(?:0|1(?:1[0-69]|2[0-57]|5[13-58]|69|7[0167]|8[018]))0","0","0|(1(?:1[0-69]|2[0-57]|5[13-58]|69|7[0167]|8[018]))",null,"$NP$FG","\\\\d{7,11}","(?:[2-9]|600|123)\\\\d{7,8}",[["(\\\\d)(\\\\d{4})(\\\\d{4})","$1 $2 $3","2[23]","($FG)"],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[357]|4[1-35]|6[13-57]","($FG)"],["(9)(\\\\d{4})(\\\\d{4})","$1 $2 $3","9",null],["(44)(\\\\d{3})(\\\\d{4})","$1 $2 $3","44",null],["([68]00)(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","60|8","$FG"],["(600)(\\\\d{3})(\\\\d{2})(\\\\d{3})","$1 $2 $3 $4","60","$FG"],["(1230)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1","$FG"],["(\\\\d{5})(\\\\d{4})","$1 $2","219","($FG)"],["(\\\\d{4,5})","$1","[1-9]","$FG","NA"]]]', + 596: '["MQ","00","0",null,null,"$NP$FG","\\\\d{9}","[56]\\\\d{8}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 508: '["PM","00","0",null,null,"$NP$FG","\\\\d{6}","[45]\\\\d{5}",[["([45]\\\\d)(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', + 269: '["KM","00",null,null,null,null,"\\\\d{7}","[3478]\\\\d{6}",[["(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', + 358: [ + '["FI","00|99(?:[02469]|5(?:11|33|5[59]|88|9[09]))","0",null,null,"$NP$FG","\\\\d{5,12}","1\\\\d{4,11}|[2-9]\\\\d{4,10}",[["(\\\\d{3})(\\\\d{3,7})","$1 $2","(?:[1-3]00|[6-8]0)",null],["(116\\\\d{3})","$1","116","$FG"],["(\\\\d{2})(\\\\d{4,10})","$1 $2","[14]|2[09]|50|7[135]",null],["(\\\\d)(\\\\d{4,11})","$1 $2","[25689][1-8]|3",null]]]', + '["AX","00|99(?:[02469]|5(?:11|33|5[59]|88|9[09]))","0",null,null,"$NP$FG","\\\\d{5,12}","1\\\\d{5,11}|[35]\\\\d{5,9}|[27]\\\\d{4,9}|4\\\\d{5,10}|6\\\\d{7,9}|8\\\\d{6,9}"]', + ], + 251: '["ET","00","0",null,null,"$NP$FG","\\\\d{7,9}","[1-59]\\\\d{8}",[["([1-59]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3",null,null]]]', + 681: '["WF","00",null,null,null,null,"\\\\d{6}","[4-8]\\\\d{5}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3",null,null]]]', + 853: '["MO","00",null,null,null,null,"\\\\d{8}","[268]\\\\d{7}",[["([268]\\\\d{3})(\\\\d{4})","$1 $2",null,null]]]', + 44: [ + '["GB","00","0",null,null,"$NP$FG","\\\\d{4,10}","\\\\d{7,10}",[["(7\\\\d{3})(\\\\d{6})","$1 $2","7(?:[1-5789]|62)",null],["(\\\\d{2})(\\\\d{4})(\\\\d{4})","$1 $2 $3","2|5[56]|7[06]",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","1(?:1|\\\\d1)|3|9[018]",null],["(\\\\d{5})(\\\\d{4,5})","$1 $2","1(?:38|5[23]|69|76|94)",null],["(1\\\\d{3})(\\\\d{5,6})","$1 $2","1",null],["(800)(\\\\d{4})","$1 $2","800",null],["(845)(46)(4\\\\d)","$1 $2 $3","845",null],["(8\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","8(?:4[2-5]|7[0-3])",null],["(80\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","80",null],["([58]00)(\\\\d{6})","$1 $2","[58]00",null]]]', + '["GG","00","0",null,null,"$NP$FG","\\\\d{6,10}","[135789]\\\\d{6,9}"]', + '["IM","00","0",null,null,"$NP$FG","\\\\d{6,10}","[135789]\\\\d{6,9}"]', + '["JE","00","0",null,null,"$NP$FG","\\\\d{6,10}","[135789]\\\\d{6,9}"]', + ], + 244: '["AO","00",null,null,null,null,"\\\\d{9}","[29]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,null]]]', + 211: '["SS","00","0",null,null,null,"\\\\d{9}","[19]\\\\d{8}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3",null,"$NP$FG"]]]', + 373: '["MD","00","0",null,null,"$NP$FG","\\\\d{8}","[235-9]\\\\d{7}",[["(\\\\d{2})(\\\\d{3})(\\\\d{3})","$1 $2 $3","22|3",null],["([25-7]\\\\d{2})(\\\\d{2})(\\\\d{3})","$1 $2 $3","2[13-9]|[5-7]",null],["([89]\\\\d{2})(\\\\d{5})","$1 $2","[89]",null]]]', + 996: '["KG","00","0",null,null,"$NP$FG","\\\\d{5,10}","[235-8]\\\\d{8,9}",[["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[25-7]|31[25]",null],["(\\\\d{4})(\\\\d{5})","$1 $2","3(?:1[36]|[2-9])",null],["(\\\\d{3})(\\\\d{3})(\\\\d)(\\\\d{3})","$1 $2 $3 $4","8",null]]]', + 93: '["AF","00","0",null,null,"$NP$FG","\\\\d{7,9}","[2-7]\\\\d{8}",[["([2-7]\\\\d)(\\\\d{3})(\\\\d{4})","$1 $2 $3","[2-7]",null]]]', + 260: '["ZM","00","0",null,null,"$NP$FG","\\\\d{9}","[289]\\\\d{8}",[["([29]\\\\d)(\\\\d{7})","$1 $2","[29]",null],["(800)(\\\\d{3})(\\\\d{3})","$1 $2 $3","8",null]]]', + 378: '["SM","00",null,"(?:0549)?([89]\\\\d{5})","0549$1",null,"\\\\d{6,10}","[05-7]\\\\d{7,9}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[5-7]",null],["(0549)(\\\\d{6})","$1 $2","0",null,"($1) $2"],["(\\\\d{6})","0549 $1","[89]",null,"(0549) $1"]]]', + 235: '["TD","00|16",null,null,null,null,"\\\\d{8}","[2679]\\\\d{7}",[["(\\\\d{2})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4",null,null]]]', + 960: '["MV","0(?:0|19)",null,null,null,null,"\\\\d{7,10}","[346-8]\\\\d{6,9}|9(?:00\\\\d{7}|\\\\d{6})",[["(\\\\d{3})(\\\\d{4})","$1-$2","[3467]|9(?:[1-9]|0[1-9])",null],["(\\\\d{3})(\\\\d{3})(\\\\d{4})","$1 $2 $3","[89]00",null]]]', + 221: '["SN","00",null,null,null,null,"\\\\d{9}","[3789]\\\\d{8}",[["(\\\\d{2})(\\\\d{3})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","[379]",null],["(\\\\d{3})(\\\\d{2})(\\\\d{2})(\\\\d{2})","$1 $2 $3 $4","8",null]]]', + 595: '["PY","00","0",null,null,null,"\\\\d{5,9}","5[0-5]\\\\d{4,7}|[2-46-9]\\\\d{5,8}",[["(\\\\d{2})(\\\\d{5})","$1 $2","(?:[26]1|3[289]|4[124678]|7[123]|8[1236])","($NP$FG)"],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","(?:[26]1|3[289]|4[124678]|7[123]|8[1236])","($NP$FG)"],["(\\\\d{3})(\\\\d{3,6})","$1 $2","[2-9]0","$NP$FG"],["(\\\\d{3})(\\\\d{6})","$1 $2","9[1-9]","$NP$FG"],["(\\\\d{2})(\\\\d{3})(\\\\d{4})","$1 $2 $3","8700",null],["(\\\\d{3})(\\\\d{4,5})","$1 $2","[2-8][1-9]","($NP$FG)"],["(\\\\d{3})(\\\\d{3})(\\\\d{3})","$1 $2 $3","[2-8][1-9]","$NP$FG"]]]', + 977: '["NP","00","0",null,null,"$NP$FG","\\\\d{6,10}","[1-8]\\\\d{7}|9(?:[1-69]\\\\d{6,8}|7[2-6]\\\\d{5,7}|8\\\\d{8})",[["(1)(\\\\d{7})","$1-$2","1[2-6]",null],["(\\\\d{2})(\\\\d{6})","$1-$2","1[01]|[2-8]|9(?:[1-69]|7[15-9])",null],["(9\\\\d{2})(\\\\d{7})","$1-$2","9(?:6[013]|7[245]|8)","$FG"]]]', + 36: '["HU","00","06",null,null,"($FG)","\\\\d{6,9}","[1-9]\\\\d{7,8}",[["(1)(\\\\d{3})(\\\\d{4})","$1 $2 $3","1",null],["(\\\\d{2})(\\\\d{3})(\\\\d{3,4})","$1 $2 $3","[2-9]",null]]]', +}; diff --git a/toolkit/components/formautofill/shared/PhoneNumberNormalizer.sys.mjs b/toolkit/components/formautofill/shared/PhoneNumberNormalizer.sys.mjs new file mode 100644 index 0000000000..604eefe314 --- /dev/null +++ b/toolkit/components/formautofill/shared/PhoneNumberNormalizer.sys.mjs @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Apache License, Version + * 2.0. If a copy of the Apache License was not distributed with this file, You + * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */ + +// This library came from https://github.com/andreasgal/PhoneNumber.js but will +// be further maintained by our own in Form Autofill codebase. + +export var 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 + 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 ""; + } + + number = number.replace(UNICODE_DIGITS, function (ch) { + return String.fromCharCode(48 + (ch.charCodeAt(0) & 0xf)); + }); + if (!numbersOnly) { + number = number.replace(VALID_ALPHA_PATTERN, 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, + }; +})(); -- cgit v1.2.3