Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddressMetaDataExtension:
});
export class AddressMetaDataLoader {
// Status of address data loading. We'll load all the countries with basic level 1
// information while requesting conutry information, and set country to true.
// Level 1 Set is for recording which country's level 1/level 2 data is loaded,
// since we only load this when getCountryAddressData called with level 1 parameter.
static dataLoaded = {
country: false,
level1: new Set(),
};
static addressData = {};
static DATA_PREFIX = "data/";
/**
* Load address meta data and extension into one object.
*
* @returns {object}
* An object containing address data object with properties from extension.
*/
static loadAddressMetaData() {
const addressMetaData = lazy.AddressMetaData;
for (const key in lazy.AddressMetaDataExtension) {
let addressDataForKey = addressMetaData[key];
if (!addressDataForKey) {
addressDataForKey = addressMetaData[key] = {};
}
Object.assign(addressDataForKey, lazy.AddressMetaDataExtension[key]);
}
return addressMetaData;
}
/**
* Convert certain properties' string value into array. We should make sure
* the cached data is parsed.
*
* @param {object} data Original metadata from addressReferences.
* @returns {object} parsed metadata with property value that converts to array.
*/
static #parse(data) {
if (!data) {
return null;
}
const properties = [
"languages",
"sub_keys",
"sub_isoids",
"sub_names",
"sub_lnames",
];
for (const key of properties) {
if (!data[key]) {
continue;
}
// No need to normalize data if the value is array already.
if (Array.isArray(data[key])) {
return data;
}
data[key] = data[key].split("~");
}
return data;
}
/**
* We'll cache addressData in the loader once the data loaded from scripts.
* It'll become the example below after loading addressReferences with extension:
* addressData: {
* "data/US": {"lang": ["en"], ...// Data defined in libaddressinput metadata
* "alternative_names": ... // Data defined in extension }
* "data/CA": {} // Other supported country metadata
* "data/TW": {} // Other supported country metadata
* "data/TW/台北市": {} // Other supported country level 1 metadata
* }
*
* @param {string} country
* @param {string?} level1
* @returns {object} Default locale metadata
*/
static #loadData(country, level1 = null) {
// Load the addressData if needed
if (!this.dataLoaded.country) {
this.addressData = this.loadAddressMetaData();
this.dataLoaded.country = true;
}
if (!level1) {
return this.#parse(this.addressData[`${this.DATA_PREFIX}${country}`]);
}
// If level1 is set, load addressReferences under country folder with specific
// country/level 1 for level 2 information.
if (!this.dataLoaded.level1.has(country)) {
Object.assign(this.addressData, this.loadAddressMetaData());
this.dataLoaded.level1.add(country);
}
return this.#parse(
this.addressData[`${this.DATA_PREFIX}${country}/${level1}`]
);
}
/**
* Return the region metadata with default locale and other locales (if exists).
*
* @param {string} country
* @param {string?} level1
* @returns {object} Return default locale and other locales metadata.
*/
static getData(country, level1 = null) {
const defaultLocale = this.#loadData(country, level1);
if (!defaultLocale) {
return null;
}
const countryData = this.#parse(
this.addressData[`${this.DATA_PREFIX}${country}`]
);
let locales = [];
// TODO: Should be able to support multi-locale level 1/ level 2 metadata query
// in Bug 1421886
if (countryData.languages) {
const list = countryData.languages.filter(
key => key !== countryData.lang
);
locales = list.map(key =>
this.#parse(this.addressData[`${defaultLocale.id}--${key}`])
);
}
return { defaultLocale, locales };
}
/**
* Return an array containing countries alpha2 codes.
*
* @returns {Array} Return an array containing countries alpha2 codes.
*/
static get #countryCodes() {
return Object.keys(lazy.AddressMetaDataExtension).map(dataKey =>
dataKey.replace(this.DATA_PREFIX, "")
);
}
static getCountries(locales = []) {
const displayNames = new Intl.DisplayNames(locales, {
type: "region",
fallback: "none",
});
const countriesMap = new Map();
for (const countryCode of this.#countryCodes) {
countriesMap.set(countryCode, displayNames.of(countryCode));
}
return countriesMap;
}
}
export default AddressMetaDataLoader;