PageSourceSearch

https://portal.miira.app/manage/contact-location.js?v=20260730-residence-1

js miira.app collected 2026-09-25 03:33:02 UTC 9,095 bytes, 153 lines download raw bytes

1/*
2 * Contact residence — country + state/region reference data.
3 *
4 * Backs the contact editor's Country select and State input, and the reporting
5 * Local vs overseas widget (§4.6), which needs ISO → display name.
6 *
7 * This is the BUYER'S RESIDENCE (contact.residenceCountry / .residenceState),
8 * not contact.country — that one is an SMS routing code derived from the phone
9 * number on write (ADR-006 §5) and must never be conflated with where someone
10 * actually lives.
11 *
12 * States are suggestions only (a datalist, never enforced): the input stays free
13 * text so an unlisted region is still recordable. Subdivisions are listed for the
14 * markets we sell into — AU, NZ, US, CA, GB, MY, ID — and omitted elsewhere,
15 * where a city-state or a locally-phrased region reads better than a wrong list.
16 *
17 * Dual-export shim: window.MiiraContactLocation in the browser (script tag before
18 * tabs.js), CommonJS under Jest/Node.
19 */
20(function (root, factory) {
21  var api = factory();
22  if (typeof module !== 'undefined' && module.exports) {
23    module.exports = api;
24  } else {
25    root.MiiraContactLocation = api;
26  }
27})(typeof self !== 'undefined' ? self : this, function () {
28  'use strict';
29
30  /* ISO 3166-1 alpha-2, "CC:Name" pipe-delimited to keep the payload small.
31   * Full list rather than a curated shortlist: overseas buyers turn up from
32   * everywhere, and a missing row is a dead end for whoever is typing. */
33  var COUNTRY_DATA =
34    'AF:Afghanistan|AX:Åland Islands|AL:Albania|DZ:Algeria|AS:American Samoa|AD:Andorra|AO:Angola|' +
35    'AI:Anguilla|AG:Antigua & Barbuda|AR:Argentina|AM:Armenia|AW:Aruba|AU:Australia|AT:Austria|' +
36    'AZ:Azerbaijan|BS:Bahamas|BH:Bahrain|BD:Bangladesh|BB:Barbados|BY:Belarus|BE:Belgium|BZ:Belize|' +
37    'BJ:Benin|BM:Bermuda|BT:Bhutan|BO:Bolivia|BA:Bosnia & Herzegovina|BW:Botswana|BR:Brazil|' +
38    'BN:Brunei|BG:Bulgaria|BF:Burkina Faso|BI:Burundi|KH:Cambodia|CM:Cameroon|CA:Canada|' +
39    'CV:Cape Verde|KY:Cayman Islands|CF:Central African Republic|TD:Chad|CL:Chile|CN:China|' +
40    'CO:Colombia|KM:Comoros|CG:Congo - Brazzaville|CD:Congo - Kinshasa|CK:Cook Islands|' +
41    'CR:Costa Rica|CI:Côte d’Ivoire|HR:Croatia|CU:Cuba|CW:Curaçao|CY:Cyprus|CZ:Czechia|' +
42    'DK:Denmark|DJ:Djibouti|DM:Dominica|DO:Dominican Republic|EC:Ecuador|EG:Egypt|SV:El Salvador|' +
43    'GQ:Equatorial Guinea|ER:Eritrea|EE:Estonia|SZ:Eswatini|ET:Ethiopia|FO:Faroe Islands|' +
44    'FJ:Fiji|FI:Finland|FR:France|GF:French Guiana|PF:French Polynesia|GA:Gabon|GM:Gambia|' +
45    'GE:Georgia|DE:Germany|GH:Ghana|GI:Gibraltar|GR:Greece|GL:Greenland|GD:Grenada|GP:Guadeloupe|' +
46    'GU:Guam|GT:Guatemala|GG:Guernsey|GN:Guinea|GW:Guinea-Bissau|GY:Guyana|HT:Haiti|HN:Honduras|' +
47    'HK:Hong Kong SAR|HU:Hungary|IS:Iceland|IN:India|ID:Indonesia|IR:Iran|IQ:Iraq|IE:Ireland|' +
48    'IM:Isle of Man|IL:Israel|IT:Italy|JM:Jamaica|JP:Japan|JE:Jersey|JO:Jordan|KZ:Kazakhstan|' +
49    'KE:Kenya|KI:Kiribati|KW:Kuwait|KG:Kyrgyzstan|LA:Laos|LV:Latvia|LB:Lebanon|LS:Lesotho|' +
50    'LR:Liberia|LY:Libya|LI:Liechtenstein|LT:Lithuania|LU:Luxembourg|MO:Macao SAR|MG:Madagascar|' +
51    'MW:Malawi|MY:Malaysia|MV:Maldives|ML:Mali|MT:Malta|MH:Marshall Islands|MQ:Martinique|' +
52    'MR:Mauritania|MU:Mauritius|MX:Mexico|FM:Micronesia|MD:Moldova|MC:Monaco|MN:Mongolia|' +
53    'ME:Montenegro|MS:Montserrat|MA:Morocco|MZ:Mozambique|MM:Myanmar|NA:Namibia|NR:Nauru|' +
54    'NP:Nepal|NL:Netherlands|NC:New Caledonia|NZ:New Zealand|NI:Nicaragua|NE:Niger|NG:Nigeria|' +
55    'NU:Niue|MK:North Macedonia|KP:North Korea|NO:Norway|OM:Oman|PK:Pakistan|PW:Palau|' +
56    'PS:Palestine|PA:Panama|PG:Papua New Guinea|PY:Paraguay|PE:Peru|PH:Philippines|PL:Poland|' +
57    'PT:Portugal|PR:Puerto Rico|QA:Qatar|RE:Réunion|RO:Romania|RU:Russia|RW:Rwanda|' +
58    'WS:Samoa|SM:San Marino|ST:São Tomé & Príncipe|SA:Saudi Arabia|SN:Senegal|RS:Serbia|' +
59    'SC:Seychelles|SL:Sierra Leone|SG:Singapore|SX:Sint Maarten|SK:Slovakia|SI:Slovenia|' +
60    'SB:Solomon Islands|SO:Somalia|ZA:South Africa|KR:South Korea|SS:South Sudan|ES:Spain|' +
61    'LK:Sri Lanka|BL:St Barthélemy|KN:St Kitts & Nevis|LC:St Lucia|MF:St Martin|' +
62    'VC:St Vincent & Grenadines|SD:Sudan|SR:Suriname|SE:Sweden|CH:Switzerland|SY:Syria|' +
63    'TW:Taiwan|TJ:Tajikistan|TZ:Tanzania|TH:Thailand|TL:Timor-Leste|TG:Togo|TO:Tonga|' +
64    'TT:Trinidad & Tobago|TN:Tunisia|TR:Türkiye|TM:Turkmenistan|TC:Turks & Caicos|TV:Tuvalu|' +
65    'UG:Uganda|UA:Ukraine|AE:United Arab Emirates|GB:United Kingdom|US:United States|' +
66    'UY:Uruguay|UZ:Uzbekistan|VU:Vanuatu|VA:Vatican City|VE:Venezuela|VN:Vietnam|' +
67    'VG:British Virgin Islands|VI:US Virgin Islands|YE:Yemen|ZM:Zambia|ZW:Zimbabwe';
68
69  var COUNTRIES = COUNTRY_DATA.split('|').map(function (row) {
70    var i = row.indexOf(':');
71    return { iso: row.slice(0, i), name: row.slice(i + 1) };
72  });
73
74  var NAME_BY_ISO = {};
75  COUNTRIES.forEach(function (c) { NAME_BY_ISO[c.iso] = c.name; });
76
77  /* First-level subdivisions, as suggestions. Stored verbatim — the widget groups on
78   * the string, so the datalist keeps spelling consistent without enforcing it. */
79  var REGIONS = {
80    AU: ['Australian Capital Territory', 'New South Wales', 'Northern Territory', 'Queensland',
81      'South Australia', 'Tasmania', 'Victoria', 'Western Australia'],
82    NZ: ['Auckland', 'Bay of Plenty', 'Canterbury', 'Gisborne', "Hawke's Bay", 'Manawatū-Whanganui',
83      'Marlborough', 'Nelson', 'Northland', 'Otago', 'Southland', 'Taranaki', 'Tasman',
84      'Waikato', 'Wellington', 'West Coast'],
85    US: ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut',
86      'Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois',
87      'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts',
88      'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada',
89      'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota',
90      'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina',
91      'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington',
92      'West Virginia', 'Wisconsin', 'Wyoming'],
93    CA: ['Alberta', 'British Columbia', 'Manitoba', 'New Brunswick', 'Newfoundland & Labrador',
94      'Northwest Territories', 'Nova Scotia', 'Nunavut', 'Ontario', 'Prince Edward Island',
95      'Quebec', 'Saskatchewan', 'Yukon'],
96    GB: ['England', 'Northern Ireland', 'Scotland', 'Wales'],
97    MY: ['Johor', 'Kedah', 'Kelantan', 'Kuala Lumpur', 'Labuan', 'Melaka', 'Negeri Sembilan',
98      'Pahang', 'Penang', 'Perak', 'Perlis', 'Putrajaya', 'Sabah', 'Sarawak', 'Selangor',
99      'Terengganu'],
100    ID: ['Aceh', 'Bali', 'Bangka Belitung', 'Banten', 'Bengkulu', 'Central Java',
101      'Central Kalimantan', 'Central Sulawesi', 'East Java', 'East Kalimantan',
102      'East Nusa Tenggara', 'Gorontalo', 'Jakarta', 'Jambi', 'Lampung', 'Maluku',
103      'North Kalimantan', 'North Maluku', 'North Sulawesi', 'North Sumatra', 'Papua', 'Riau',
104      'Riau Islands', 'South Kalimantan', 'South Sulawesi', 'South Sumatra',
105      'Southeast Sulawesi', 'West Java', 'West Kalimantan', 'West Nusa Tenggara', 'West Papua',
106      'West Sulawesi', 'West Sumatra', 'Yogyakarta'],
107  };
108
109  /* Dialling code → country, for the markets the phone-prefix picker offers. Reporting §4.6
110   * reads a project's `defaultPhonePrefix` through this to work out which country counts as
111   * "local" — projects carry no country field of their own, only a free-text address.
112   * Keep in sync with /manage/phone-prefixes.js, the canonical list. '+1' resolves to US: the picker
113   * offers it as "United States & Canada", and US is the larger of the two by a wide margin. */
114  var ISO_BY_DIAL = {
115    '+61': 'AU', '+64': 'NZ', '+62': 'ID', '+65': 'SG', '+60': 'MY', '+63': 'PH', '+66': 'TH',
116    '+84': 'VN', '+852': 'HK', '+86': 'CN', '+81': 'JP', '+82': 'KR', '+91': 'IN', '+971': 'AE',
117    '+44': 'GB', '+1': 'US',
118  };
119
120  /** ISO alpha-2 if it names a country we know, else ''. Accepts any case / padding. */
121  function normalizeCountry(raw) {
122    var s = String(raw == null ? '' : raw).trim().toUpperCase();
123    return NAME_BY_ISO[s] ? s : '';
124  }
125
126  /** Display name for an ISO code; falls back to the code itself so nothing renders blank. */
127  function countryName(iso) {
128    var s = String(iso == null ? '' : iso).trim().toUpperCase();
129    return NAME_BY_ISO[s] || s;
130  }
131
132  /** Suggested subdivisions for a country, or [] where we don't list any. */
133  function regionsFor(iso) {
134    return REGIONS[normalizeCountry(iso)] || [];
135  }
136
137  /** ISO alpha-2 for a dialling prefix ("+61" or "61"), or '' when unmapped. */
138  function isoForDialCode(dial) {
139    var s = String(dial == null ? '' : dial).trim();
140    if (!s) return '';
141    if (s.charAt(0) !== '+') s = '+' + s;
142    return ISO_BY_DIAL[s] || '';
143  }
144
145  return {
146    COUNTRIES: COUNTRIES,
147    REGIONS: REGIONS,
148    normalizeCountry: normalizeCountry,
149    countryName: countryName,
150    regionsFor: regionsFor,
151    isoForDialCode: isoForDialCode,
152  };
153});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.