intl-tel-input 25.10.12 → 25.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +62 -59
  2. package/angular/README.md +1 -1
  3. package/angular/build/IntlTelInput.js +887 -688
  4. package/angular/build/IntlTelInputWithUtils.js +891 -692
  5. package/angular/build/types/intl-tel-input/data.d.ts +3 -3
  6. package/angular/build/types/intl-tel-input.d.ts +22 -70
  7. package/angular/build/types/modules/constants.d.ts +84 -0
  8. package/angular/build/types/modules/core/countrySearch.d.ts +17 -0
  9. package/angular/build/types/modules/core/icons.d.ts +7 -0
  10. package/angular/build/types/modules/core/options.d.ts +2 -1
  11. package/angular/build/types/modules/core/ui.d.ts +44 -0
  12. package/angular/build/types/modules/data/country-data.d.ts +5 -5
  13. package/angular/build/types/modules/format/caret.d.ts +1 -1
  14. package/angular/build/types/modules/format/formatting.d.ts +3 -3
  15. package/angular/build/types/modules/types/events.d.ts +9 -0
  16. package/angular/build/types/modules/types/public-api.d.ts +3 -0
  17. package/angular/build/types/modules/utils/dom.d.ts +5 -0
  18. package/build/js/data.js +8 -2
  19. package/build/js/data.min.js +2 -2
  20. package/build/js/intlTelInput.d.ts +214 -83
  21. package/build/js/intlTelInput.js +1034 -770
  22. package/build/js/intlTelInput.min.js +13 -13
  23. package/build/js/intlTelInputWithUtils.js +1038 -774
  24. package/build/js/intlTelInputWithUtils.min.js +13 -13
  25. package/build/js/utils.js +4 -4
  26. package/package.json +3 -1
  27. package/react/README.md +1 -1
  28. package/react/build/IntlTelInput.cjs +1033 -769
  29. package/react/build/IntlTelInput.d.ts +214 -83
  30. package/react/build/IntlTelInput.js +1033 -769
  31. package/react/build/IntlTelInputWithUtils.cjs +1037 -773
  32. package/react/build/IntlTelInputWithUtils.js +1037 -773
  33. package/vue/README.md +1 -1
  34. package/vue/build/IntlTelInput.mjs +967 -739
  35. package/vue/build/IntlTelInputWithUtils.mjs +1279 -1051
@@ -1688,7 +1688,13 @@ for (const c of rawCountryData) {
1688
1688
  areaCodes: c[3] || null,
1689
1689
  nodeById: {},
1690
1690
  // populated by the plugin
1691
- nationalPrefix: c[4] || null
1691
+ nationalPrefix: c[4] || null,
1692
+ normalisedName: "",
1693
+ // populated in the plugin
1694
+ initials: "",
1695
+ // populated in the plugin
1696
+ dialCodePlus: ""
1697
+ // populated in the plugin
1692
1698
  });
1693
1699
  }
1694
1700
  var data_default = allCountries;
@@ -1960,10 +1966,106 @@ var interface_default = interfaceTranslations;
1960
1966
  var allTranslations = Object.assign(Object.assign({}, countries_default), interface_default);
1961
1967
  var en_default = allTranslations;
1962
1968
 
1963
- // angular/build/temp/modules/core/options.js
1964
- var mq = (q) => {
1965
- return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(q).matches;
1969
+ // angular/build/temp/modules/constants.js
1970
+ var EVENTS = {
1971
+ OPEN_COUNTRY_DROPDOWN: "open:countrydropdown",
1972
+ CLOSE_COUNTRY_DROPDOWN: "close:countrydropdown",
1973
+ COUNTRY_CHANGE: "countrychange",
1974
+ INPUT: "input"
1975
+ // used for synthetic input trigger
1976
+ };
1977
+ var CLASSES = {
1978
+ HIDE: "iti__hide",
1979
+ V_HIDE: "iti__v-hide",
1980
+ ARROW_UP: "iti__arrow--up",
1981
+ GLOBE: "iti__globe",
1982
+ FLAG: "iti__flag",
1983
+ COUNTRY_ITEM: "iti__country",
1984
+ HIGHLIGHT: "iti__highlight"
1985
+ };
1986
+ var KEYS = {
1987
+ ARROW_UP: "ArrowUp",
1988
+ ARROW_DOWN: "ArrowDown",
1989
+ SPACE: " ",
1990
+ ENTER: "Enter",
1991
+ ESC: "Escape",
1992
+ TAB: "Tab"
1993
+ };
1994
+ var INPUT_TYPES = {
1995
+ PASTE: "insertFromPaste",
1996
+ DELETE_FWD: "deleteContentForward"
1997
+ };
1998
+ var REGEX = {
1999
+ ALPHA_UNICODE: /\p{L}/u,
2000
+ // any kind of letter from any language
2001
+ NON_PLUS_NUMERIC: /[^+0-9]/,
2002
+ // chars that are NOT + or digit
2003
+ NON_PLUS_NUMERIC_GLOBAL: /[^+0-9]/g,
2004
+ // chars that are NOT + or digit (global)
2005
+ HIDDEN_SEARCH_CHAR: /^[a-zA-ZÀ-ÿа-яА-Я ]$/
2006
+ // single acceptable hidden-search char
2007
+ };
2008
+ var TIMINGS = {
2009
+ SEARCH_DEBOUNCE_MS: 100,
2010
+ HIDDEN_SEARCH_RESET_MS: 1e3,
2011
+ NEXT_TICK: 0
2012
+ };
2013
+ var SENTINELS = {
2014
+ UNKNOWN_NUMBER_TYPE: -99,
2015
+ UNKNOWN_VALIDATION_ERROR: -99
2016
+ };
2017
+ var LAYOUT = {
2018
+ SANE_SELECTED_WITH_DIAL_WIDTH: 78,
2019
+ // px width fallback when separateDialCode enabled
2020
+ SANE_SELECTED_NO_DIAL_WIDTH: 42,
2021
+ // px width fallback when no separate dial code
2022
+ INPUT_PADDING_EXTRA_LEFT: 6
2023
+ // px gap between selected country container and input text
2024
+ };
2025
+ var DIAL = {
2026
+ PLUS: "+",
2027
+ NANP: "1"
2028
+ // North American Numbering Plan
2029
+ };
2030
+ var UK = {
2031
+ ISO2: "gb",
2032
+ DIAL_CODE: "44",
2033
+ // +44 United Kingdom
2034
+ MOBILE_PREFIX: "7",
2035
+ // UK mobile numbers start with 7 after national trunk (0) or core section
2036
+ MOBILE_CORE_LENGTH: 10
2037
+ // core number length (excluding dial code / national prefix) for mobiles
2038
+ };
2039
+ var US = {
2040
+ ISO2: "us",
2041
+ DIAL_CODE: "1"
2042
+ // +1 United States
2043
+ };
2044
+ var PLACEHOLDER_MODES = {
2045
+ AGGRESSIVE: "aggressive",
2046
+ POLITE: "polite"
1966
2047
  };
2048
+ var INITIAL_COUNTRY = {
2049
+ AUTO: "auto"
2050
+ };
2051
+ var DATA_KEYS = {
2052
+ COUNTRY_CODE: "countryCode",
2053
+ DIAL_CODE: "dialCode"
2054
+ };
2055
+ var ARIA = {
2056
+ EXPANDED: "aria-expanded",
2057
+ LABEL: "aria-label",
2058
+ SELECTED: "aria-selected",
2059
+ ACTIVE_DESCENDANT: "aria-activedescendant",
2060
+ HASPOPUP: "aria-haspopup",
2061
+ CONTROLS: "aria-controls",
2062
+ HIDDEN: "aria-hidden",
2063
+ AUTOCOMPLETE: "aria-autocomplete",
2064
+ MODAL: "aria-modal"
2065
+ };
2066
+
2067
+ // angular/build/temp/modules/core/options.js
2068
+ var mq = (q) => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(q).matches;
1967
2069
  var computeDefaultUseFullscreenPopup = () => {
1968
2070
  if (typeof navigator !== "undefined" && typeof window !== "undefined") {
1969
2071
  const isMobileUserAgent = /Android.+Mobile|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
@@ -1980,7 +2082,7 @@ var defaults = {
1980
2082
  //* Whether or not to allow the dropdown.
1981
2083
  allowDropdown: true,
1982
2084
  //* Add a placeholder in the input with an example number for the selected country.
1983
- autoPlaceholder: "polite",
2085
+ autoPlaceholder: PLACEHOLDER_MODES.POLITE,
1984
2086
  //* Modify the parentClass.
1985
2087
  containerClass: "",
1986
2088
  //* The order of the countries in the dropdown. Defaults to alphabetical.
@@ -2026,7 +2128,7 @@ var defaults = {
2026
2128
  //* The number type to enforce during validation.
2027
2129
  validationNumberTypes: ["MOBILE"]
2028
2130
  };
2029
- function applyOptionSideEffects(o) {
2131
+ var applyOptionSideEffects = (o, defaultEnglishStrings) => {
2030
2132
  if (o.useFullscreenPopup) {
2031
2133
  o.fixDropdownWidth = false;
2032
2134
  }
@@ -2042,14 +2144,60 @@ function applyOptionSideEffects(o) {
2042
2144
  if (o.useFullscreenPopup && !o.dropdownContainer) {
2043
2145
  o.dropdownContainer = document.body;
2044
2146
  }
2045
- o.i18n = Object.assign(Object.assign({}, en_default), o.i18n);
2046
- }
2147
+ o.i18n = Object.assign(Object.assign({}, defaultEnglishStrings), o.i18n);
2148
+ };
2047
2149
 
2048
2150
  // angular/build/temp/modules/utils/string.js
2049
2151
  var getNumeric = (s) => s.replace(/\D/g, "");
2050
2152
  var normaliseString = (s = "") => s.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
2051
2153
 
2154
+ // angular/build/temp/modules/core/countrySearch.js
2155
+ var getMatchedCountries = (countries, query) => {
2156
+ const normalisedQuery = normaliseString(query);
2157
+ const iso2Matches = [];
2158
+ const nameStartWith = [];
2159
+ const nameContains = [];
2160
+ const dialCodeMatches = [];
2161
+ const dialCodeContains = [];
2162
+ const initialsMatches = [];
2163
+ for (const c of countries) {
2164
+ if (c.iso2 === normalisedQuery) {
2165
+ iso2Matches.push(c);
2166
+ } else if (c.normalisedName.startsWith(normalisedQuery)) {
2167
+ nameStartWith.push(c);
2168
+ } else if (c.normalisedName.includes(normalisedQuery)) {
2169
+ nameContains.push(c);
2170
+ } else if (normalisedQuery === c.dialCode || normalisedQuery === c.dialCodePlus) {
2171
+ dialCodeMatches.push(c);
2172
+ } else if (c.dialCodePlus.includes(normalisedQuery)) {
2173
+ dialCodeContains.push(c);
2174
+ } else if (c.initials.includes(normalisedQuery)) {
2175
+ initialsMatches.push(c);
2176
+ }
2177
+ }
2178
+ const sortByPriority = (a, b) => a.priority - b.priority;
2179
+ return [
2180
+ ...iso2Matches.sort(sortByPriority),
2181
+ ...nameStartWith.sort(sortByPriority),
2182
+ ...nameContains.sort(sortByPriority),
2183
+ ...dialCodeMatches.sort(sortByPriority),
2184
+ ...dialCodeContains.sort(sortByPriority),
2185
+ ...initialsMatches.sort(sortByPriority)
2186
+ ];
2187
+ };
2188
+ var findFirstCountryStartingWith = (countries, query) => {
2189
+ const lowerQuery = query.toLowerCase();
2190
+ for (const c of countries) {
2191
+ const lowerName = c.name.toLowerCase();
2192
+ if (lowerName.startsWith(lowerQuery)) {
2193
+ return c;
2194
+ }
2195
+ }
2196
+ return null;
2197
+ };
2198
+
2052
2199
  // angular/build/temp/modules/utils/dom.js
2200
+ var buildClassNames = (flags) => Object.keys(flags).filter((k) => Boolean(flags[k])).join(" ");
2053
2201
  var createEl = (tagName, attrs, container) => {
2054
2202
  const el = document.createElement(tagName);
2055
2203
  if (attrs) {
@@ -2061,8 +2209,405 @@ var createEl = (tagName, attrs, container) => {
2061
2209
  return el;
2062
2210
  };
2063
2211
 
2212
+ // angular/build/temp/modules/core/icons.js
2213
+ var buildSearchIcon = () => `
2214
+ <svg class="iti__search-icon-svg" width="14" height="14" viewBox="0 0 24 24" focusable="false" ${ARIA.HIDDEN}="true">
2215
+ <circle cx="11" cy="11" r="7" />
2216
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
2217
+ </svg>`;
2218
+ var buildClearIcon = (id2) => {
2219
+ const maskId = `iti-${id2}-clear-mask`;
2220
+ return `
2221
+ <svg class="iti__search-clear-svg" width="12" height="12" viewBox="0 0 16 16" ${ARIA.HIDDEN}="true" focusable="false">
2222
+ <mask id="${maskId}" maskUnits="userSpaceOnUse">
2223
+ <rect width="16" height="16" fill="white" />
2224
+ <path d="M5.2 5.2 L10.8 10.8 M10.8 5.2 L5.2 10.8" stroke="black" stroke-linecap="round" class="iti__search-clear-x" />
2225
+ </mask>
2226
+ <circle cx="8" cy="8" r="8" class="iti__search-clear-bg" mask="url(#${maskId})" />
2227
+ </svg>`;
2228
+ };
2229
+
2230
+ // angular/build/temp/modules/core/ui.js
2231
+ var UI = class {
2232
+ constructor(input, options, id2) {
2233
+ this.highlightedItem = null;
2234
+ input.dataset.intlTelInputId = id2.toString();
2235
+ this.telInput = input;
2236
+ this.options = options;
2237
+ this.id = id2;
2238
+ this.hadInitialPlaceholder = Boolean(input.getAttribute("placeholder"));
2239
+ this.isRTL = !!this.telInput.closest("[dir=rtl]");
2240
+ if (this.options.separateDialCode) {
2241
+ this.originalPaddingLeft = this.telInput.style.paddingLeft;
2242
+ }
2243
+ }
2244
+ //* Generate all of the markup for the plugin: the selected country overlay, and the dropdown.
2245
+ generateMarkup(countries) {
2246
+ this.countries = countries;
2247
+ this._prepareTelInput();
2248
+ const wrapper = this._createWrapperAndInsert();
2249
+ this._maybeBuildCountryContainer(wrapper);
2250
+ wrapper.appendChild(this.telInput);
2251
+ this._maybeUpdateInputPaddingAndReveal();
2252
+ this._maybeBuildHiddenInputs(wrapper);
2253
+ }
2254
+ _prepareTelInput() {
2255
+ var _a;
2256
+ this.telInput.classList.add("iti__tel-input");
2257
+ if (!this.telInput.hasAttribute("autocomplete") && !((_a = this.telInput.form) === null || _a === void 0 ? void 0 : _a.hasAttribute("autocomplete"))) {
2258
+ this.telInput.setAttribute("autocomplete", "off");
2259
+ }
2260
+ }
2261
+ _createWrapperAndInsert() {
2262
+ const { allowDropdown, showFlags, containerClass, useFullscreenPopup } = this.options;
2263
+ const parentClasses = buildClassNames({
2264
+ iti: true,
2265
+ "iti--allow-dropdown": allowDropdown,
2266
+ "iti--show-flags": showFlags,
2267
+ "iti--inline-dropdown": !useFullscreenPopup,
2268
+ [containerClass]: Boolean(containerClass)
2269
+ });
2270
+ const wrapper = createEl("div", { class: parentClasses });
2271
+ if (this.isRTL) {
2272
+ wrapper.setAttribute("dir", "ltr");
2273
+ }
2274
+ this.telInput.before(wrapper);
2275
+ return wrapper;
2276
+ }
2277
+ _maybeBuildCountryContainer(wrapper) {
2278
+ const { allowDropdown, separateDialCode, showFlags } = this.options;
2279
+ if (allowDropdown || showFlags || separateDialCode) {
2280
+ this.countryContainer = createEl(
2281
+ "div",
2282
+ // visibly hidden until we measure it's width to set the input padding correctly
2283
+ { class: `iti__country-container ${CLASSES.V_HIDE}` },
2284
+ wrapper
2285
+ );
2286
+ if (allowDropdown) {
2287
+ this.selectedCountry = createEl("button", {
2288
+ type: "button",
2289
+ class: "iti__selected-country",
2290
+ [ARIA.EXPANDED]: "false",
2291
+ [ARIA.LABEL]: this.options.i18n.noCountrySelected,
2292
+ [ARIA.HASPOPUP]: "dialog",
2293
+ [ARIA.CONTROLS]: `iti-${this.id}__dropdown-content`
2294
+ }, this.countryContainer);
2295
+ if (this.telInput.disabled) {
2296
+ this.selectedCountry.setAttribute("disabled", "true");
2297
+ }
2298
+ } else {
2299
+ this.selectedCountry = createEl("div", { class: "iti__selected-country" }, this.countryContainer);
2300
+ }
2301
+ const selectedCountryPrimary = createEl("div", { class: "iti__selected-country-primary" }, this.selectedCountry);
2302
+ this.selectedCountryInner = createEl("div", { class: CLASSES.FLAG }, selectedCountryPrimary);
2303
+ if (allowDropdown) {
2304
+ this.dropdownArrow = createEl("div", { class: "iti__arrow", [ARIA.HIDDEN]: "true" }, selectedCountryPrimary);
2305
+ }
2306
+ if (separateDialCode) {
2307
+ this.selectedDialCode = createEl("div", { class: "iti__selected-dial-code" }, this.selectedCountry);
2308
+ }
2309
+ if (allowDropdown) {
2310
+ this._buildDropdownContent();
2311
+ }
2312
+ }
2313
+ }
2314
+ _buildDropdownContent() {
2315
+ const { fixDropdownWidth, useFullscreenPopup, countrySearch, i18n, dropdownContainer, containerClass } = this.options;
2316
+ const extraClasses = fixDropdownWidth ? "" : "iti--flexible-dropdown-width";
2317
+ this.dropdownContent = createEl("div", {
2318
+ id: `iti-${this.id}__dropdown-content`,
2319
+ class: `iti__dropdown-content ${CLASSES.HIDE} ${extraClasses}`,
2320
+ role: "dialog",
2321
+ [ARIA.MODAL]: "true"
2322
+ });
2323
+ if (this.isRTL) {
2324
+ this.dropdownContent.setAttribute("dir", "rtl");
2325
+ }
2326
+ if (countrySearch) {
2327
+ this._buildSearchUI();
2328
+ }
2329
+ this.countryList = createEl("ul", {
2330
+ class: "iti__country-list",
2331
+ id: `iti-${this.id}__country-listbox`,
2332
+ role: "listbox",
2333
+ [ARIA.LABEL]: i18n.countryListAriaLabel
2334
+ }, this.dropdownContent);
2335
+ this._appendListItems();
2336
+ if (countrySearch) {
2337
+ this.updateSearchResultsA11yText();
2338
+ }
2339
+ if (dropdownContainer) {
2340
+ const dropdownClasses = buildClassNames({
2341
+ iti: true,
2342
+ "iti--container": true,
2343
+ "iti--fullscreen-popup": useFullscreenPopup,
2344
+ "iti--inline-dropdown": !useFullscreenPopup,
2345
+ [containerClass]: Boolean(containerClass)
2346
+ });
2347
+ this.dropdown = createEl("div", { class: dropdownClasses });
2348
+ this.dropdown.appendChild(this.dropdownContent);
2349
+ } else {
2350
+ this.countryContainer.appendChild(this.dropdownContent);
2351
+ }
2352
+ }
2353
+ _buildSearchUI() {
2354
+ const { i18n } = this.options;
2355
+ const searchWrapper = createEl("div", { class: "iti__search-input-wrapper" }, this.dropdownContent);
2356
+ this.searchIcon = createEl("span", {
2357
+ class: "iti__search-icon",
2358
+ [ARIA.HIDDEN]: "true"
2359
+ }, searchWrapper);
2360
+ this.searchIcon.innerHTML = buildSearchIcon();
2361
+ this.searchInput = createEl("input", {
2362
+ id: `iti-${this.id}__search-input`,
2363
+ // Chrome says inputs need either a name or an id
2364
+ type: "search",
2365
+ class: "iti__search-input",
2366
+ placeholder: i18n.searchPlaceholder,
2367
+ // role=combobox + aria-autocomplete=list + aria-activedescendant allows maintaining focus on the search input while allowing users to navigate search results with up/down keyboard keys
2368
+ role: "combobox",
2369
+ [ARIA.EXPANDED]: "true",
2370
+ [ARIA.LABEL]: i18n.searchPlaceholder,
2371
+ [ARIA.CONTROLS]: `iti-${this.id}__country-listbox`,
2372
+ [ARIA.AUTOCOMPLETE]: "list",
2373
+ autocomplete: "off"
2374
+ }, searchWrapper);
2375
+ this.searchClearButton = createEl("button", {
2376
+ type: "button",
2377
+ class: `iti__search-clear ${CLASSES.HIDE}`,
2378
+ [ARIA.LABEL]: i18n.clearSearchAriaLabel,
2379
+ tabindex: "-1"
2380
+ }, searchWrapper);
2381
+ this.searchClearButton.innerHTML = buildClearIcon(this.id);
2382
+ this.searchResultsA11yText = createEl("span", { class: "iti__a11y-text" }, this.dropdownContent);
2383
+ this.searchNoResults = createEl("div", {
2384
+ class: `iti__no-results ${CLASSES.HIDE}`,
2385
+ [ARIA.HIDDEN]: "true"
2386
+ // all a11y messaging happens in this.searchResultsA11yText
2387
+ }, this.dropdownContent);
2388
+ this.searchNoResults.textContent = i18n.zeroSearchResults;
2389
+ }
2390
+ _maybeUpdateInputPaddingAndReveal() {
2391
+ if (this.countryContainer) {
2392
+ this.updateInputPadding();
2393
+ this.countryContainer.classList.remove(CLASSES.V_HIDE);
2394
+ }
2395
+ }
2396
+ _maybeBuildHiddenInputs(wrapper) {
2397
+ var _a, _b;
2398
+ const { hiddenInput } = this.options;
2399
+ if (hiddenInput) {
2400
+ const telInputName = this.telInput.getAttribute("name") || "";
2401
+ const names = hiddenInput(telInputName);
2402
+ if (names.phone) {
2403
+ const existingInput = (_a = this.telInput.form) === null || _a === void 0 ? void 0 : _a.querySelector(`input[name="${names.phone}"]`);
2404
+ if (existingInput) {
2405
+ this.hiddenInput = existingInput;
2406
+ } else {
2407
+ this.hiddenInput = createEl("input", {
2408
+ type: "hidden",
2409
+ name: names.phone
2410
+ });
2411
+ wrapper.appendChild(this.hiddenInput);
2412
+ }
2413
+ }
2414
+ if (names.country) {
2415
+ const existingInput = (_b = this.telInput.form) === null || _b === void 0 ? void 0 : _b.querySelector(`input[name="${names.country}"]`);
2416
+ if (existingInput) {
2417
+ this.hiddenInputCountry = existingInput;
2418
+ } else {
2419
+ this.hiddenInputCountry = createEl("input", {
2420
+ type: "hidden",
2421
+ name: names.country
2422
+ });
2423
+ wrapper.appendChild(this.hiddenInputCountry);
2424
+ }
2425
+ }
2426
+ }
2427
+ }
2428
+ //* For each country: add a country list item <li> to the countryList <ul> container.
2429
+ _appendListItems() {
2430
+ const frag = document.createDocumentFragment();
2431
+ for (let i = 0; i < this.countries.length; i++) {
2432
+ const c = this.countries[i];
2433
+ const liClass = buildClassNames({
2434
+ [CLASSES.COUNTRY_ITEM]: true,
2435
+ [CLASSES.HIGHLIGHT]: i === 0
2436
+ });
2437
+ const listItem = createEl("li", {
2438
+ id: `iti-${this.id}__item-${c.iso2}`,
2439
+ class: liClass,
2440
+ tabindex: "-1",
2441
+ role: "option",
2442
+ [ARIA.SELECTED]: "false"
2443
+ });
2444
+ listItem.dataset.dialCode = c.dialCode;
2445
+ listItem.dataset.countryCode = c.iso2;
2446
+ c.nodeById[this.id] = listItem;
2447
+ if (this.options.showFlags) {
2448
+ createEl("div", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);
2449
+ }
2450
+ const nameEl = createEl("span", { class: "iti__country-name" }, listItem);
2451
+ nameEl.textContent = c.name;
2452
+ const dialEl = createEl("span", { class: "iti__dial-code" }, listItem);
2453
+ if (this.isRTL) {
2454
+ dialEl.setAttribute("dir", "ltr");
2455
+ }
2456
+ dialEl.textContent = `+${c.dialCode}`;
2457
+ frag.appendChild(listItem);
2458
+ }
2459
+ this.countryList.appendChild(frag);
2460
+ }
2461
+ //* Update the input padding to make space for the selected country/dial code.
2462
+ updateInputPadding() {
2463
+ if (this.selectedCountry) {
2464
+ const fallbackWidth = this.options.separateDialCode ? LAYOUT.SANE_SELECTED_WITH_DIAL_WIDTH : LAYOUT.SANE_SELECTED_NO_DIAL_WIDTH;
2465
+ const selectedCountryWidth = this.selectedCountry.offsetWidth || this._getHiddenSelectedCountryWidth() || fallbackWidth;
2466
+ const inputPadding = selectedCountryWidth + LAYOUT.INPUT_PADDING_EXTRA_LEFT;
2467
+ this.telInput.style.paddingLeft = `${inputPadding}px`;
2468
+ }
2469
+ }
2470
+ //* When input is in a hidden container during init, we cannot calculate the selected country width.
2471
+ //* Fix: clone the markup, make it invisible, add it to the end of the DOM, and then measure it's width.
2472
+ //* To get the right styling to apply, all we need is a shallow clone of the container,
2473
+ //* and then to inject a deep clone of the selectedCountry element.
2474
+ _getHiddenSelectedCountryWidth() {
2475
+ if (this.telInput.parentNode) {
2476
+ let body;
2477
+ try {
2478
+ body = window.top.document.body;
2479
+ } catch (e) {
2480
+ body = document.body;
2481
+ }
2482
+ const containerClone = this.telInput.parentNode.cloneNode(false);
2483
+ containerClone.style.visibility = "hidden";
2484
+ body.appendChild(containerClone);
2485
+ const countryContainerClone = this.countryContainer.cloneNode();
2486
+ containerClone.appendChild(countryContainerClone);
2487
+ const selectedCountryClone = this.selectedCountry.cloneNode(true);
2488
+ countryContainerClone.appendChild(selectedCountryClone);
2489
+ const width = selectedCountryClone.offsetWidth;
2490
+ body.removeChild(containerClone);
2491
+ return width;
2492
+ }
2493
+ return 0;
2494
+ }
2495
+ //* Update search results text (for a11y).
2496
+ updateSearchResultsA11yText() {
2497
+ const { i18n } = this.options;
2498
+ const count = this.countryList.childElementCount;
2499
+ let searchText;
2500
+ if (count === 0) {
2501
+ searchText = i18n.zeroSearchResults;
2502
+ } else {
2503
+ if (i18n.searchResultsText) {
2504
+ searchText = i18n.searchResultsText(count);
2505
+ } else if (count === 1) {
2506
+ searchText = i18n.oneSearchResult;
2507
+ } else {
2508
+ searchText = i18n.multipleSearchResults.replace("${count}", count.toString());
2509
+ }
2510
+ }
2511
+ this.searchResultsA11yText.textContent = searchText;
2512
+ }
2513
+ //* Check if an element is visible within it's container, else scroll until it is.
2514
+ scrollTo(element) {
2515
+ const container = this.countryList;
2516
+ const scrollTop = document.documentElement.scrollTop;
2517
+ const containerHeight = container.offsetHeight;
2518
+ const containerTop = container.getBoundingClientRect().top + scrollTop;
2519
+ const containerBottom = containerTop + containerHeight;
2520
+ const elementHeight = element.offsetHeight;
2521
+ const elementTop = element.getBoundingClientRect().top + scrollTop;
2522
+ const elementBottom = elementTop + elementHeight;
2523
+ const newScrollTop = elementTop - containerTop + container.scrollTop;
2524
+ if (elementTop < containerTop) {
2525
+ container.scrollTop = newScrollTop;
2526
+ } else if (elementBottom > containerBottom) {
2527
+ const heightDifference = containerHeight - elementHeight;
2528
+ container.scrollTop = newScrollTop - heightDifference;
2529
+ }
2530
+ }
2531
+ //* Remove highlighting from other list items and highlight the given item.
2532
+ highlightListItem(listItem, shouldFocus) {
2533
+ const prevItem = this.highlightedItem;
2534
+ if (prevItem) {
2535
+ prevItem.classList.remove(CLASSES.HIGHLIGHT);
2536
+ prevItem.setAttribute(ARIA.SELECTED, "false");
2537
+ }
2538
+ this.highlightedItem = listItem;
2539
+ if (this.highlightedItem) {
2540
+ this.highlightedItem.classList.add(CLASSES.HIGHLIGHT);
2541
+ this.highlightedItem.setAttribute(ARIA.SELECTED, "true");
2542
+ if (this.options.countrySearch) {
2543
+ const activeDescendant = this.highlightedItem.getAttribute("id") || "";
2544
+ this.searchInput.setAttribute(ARIA.ACTIVE_DESCENDANT, activeDescendant);
2545
+ }
2546
+ }
2547
+ if (shouldFocus) {
2548
+ this.highlightedItem.focus();
2549
+ }
2550
+ }
2551
+ //* Country search: Filter the country list to the given array of countries.
2552
+ filterCountries(matchedCountries) {
2553
+ this.countryList.innerHTML = "";
2554
+ let noCountriesAddedYet = true;
2555
+ for (const c of matchedCountries) {
2556
+ const listItem = c.nodeById[this.id];
2557
+ if (listItem) {
2558
+ this.countryList.appendChild(listItem);
2559
+ if (noCountriesAddedYet) {
2560
+ this.highlightListItem(listItem, false);
2561
+ noCountriesAddedYet = false;
2562
+ }
2563
+ }
2564
+ }
2565
+ if (noCountriesAddedYet) {
2566
+ this.highlightListItem(null, false);
2567
+ if (this.searchNoResults) {
2568
+ this.searchNoResults.classList.remove(CLASSES.HIDE);
2569
+ }
2570
+ } else if (this.searchNoResults) {
2571
+ this.searchNoResults.classList.add(CLASSES.HIDE);
2572
+ }
2573
+ this.countryList.scrollTop = 0;
2574
+ this.updateSearchResultsA11yText();
2575
+ }
2576
+ destroy() {
2577
+ this.telInput.iti = void 0;
2578
+ delete this.telInput.dataset.intlTelInputId;
2579
+ if (this.options.separateDialCode) {
2580
+ this.telInput.style.paddingLeft = this.originalPaddingLeft;
2581
+ }
2582
+ const wrapper = this.telInput.parentNode;
2583
+ wrapper.before(this.telInput);
2584
+ wrapper.remove();
2585
+ this.telInput = null;
2586
+ this.countryContainer = null;
2587
+ this.selectedCountry = null;
2588
+ this.selectedCountryInner = null;
2589
+ this.selectedDialCode = null;
2590
+ this.dropdownArrow = null;
2591
+ this.dropdownContent = null;
2592
+ this.searchInput = null;
2593
+ this.searchIcon = null;
2594
+ this.searchClearButton = null;
2595
+ this.searchNoResults = null;
2596
+ this.searchResultsA11yText = null;
2597
+ this.countryList = null;
2598
+ this.dropdown = null;
2599
+ this.hiddenInput = null;
2600
+ this.hiddenInputCountry = null;
2601
+ this.highlightedItem = null;
2602
+ for (const c of this.countries) {
2603
+ delete c.nodeById[this.id];
2604
+ }
2605
+ this.countries = null;
2606
+ }
2607
+ };
2608
+
2064
2609
  // angular/build/temp/modules/data/country-data.js
2065
- function processAllCountries(options) {
2610
+ var processAllCountries = (options) => {
2066
2611
  const { onlyCountries, excludeCountries } = options;
2067
2612
  if (onlyCountries.length) {
2068
2613
  const lowerCaseOnlyCountries = onlyCountries.map((country) => country.toLowerCase());
@@ -2072,16 +2617,16 @@ function processAllCountries(options) {
2072
2617
  return data_default.filter((country) => !lowerCaseExcludeCountries.includes(country.iso2));
2073
2618
  }
2074
2619
  return data_default;
2075
- }
2076
- function translateCountryNames(countries, options) {
2620
+ };
2621
+ var translateCountryNames = (countries, options) => {
2077
2622
  for (const c of countries) {
2078
2623
  const iso2 = c.iso2.toLowerCase();
2079
2624
  if (options.i18n[iso2]) {
2080
2625
  c.name = options.i18n[iso2];
2081
2626
  }
2082
2627
  }
2083
- }
2084
- function processDialCodes(countries, options) {
2628
+ };
2629
+ var processDialCodes = (countries, options) => {
2085
2630
  const dialCodes = /* @__PURE__ */ new Set();
2086
2631
  let dialCodeMaxLen = 0;
2087
2632
  const dialCodeToIso2Map = {};
@@ -2132,8 +2677,8 @@ function processDialCodes(countries, options) {
2132
2677
  }
2133
2678
  }
2134
2679
  return { dialCodes, dialCodeMaxLen, dialCodeToIso2Map };
2135
- }
2136
- function sortCountries(countries, options) {
2680
+ };
2681
+ var sortCountries = (countries, options) => {
2137
2682
  if (options.countryOrder) {
2138
2683
  options.countryOrder = options.countryOrder.map((iso2) => iso2.toLowerCase());
2139
2684
  }
@@ -2153,17 +2698,17 @@ function sortCountries(countries, options) {
2153
2698
  }
2154
2699
  return a.name.localeCompare(b.name);
2155
2700
  });
2156
- }
2157
- function cacheSearchTokens(countries) {
2701
+ };
2702
+ var cacheSearchTokens = (countries) => {
2158
2703
  for (const c of countries) {
2159
2704
  c.normalisedName = normaliseString(c.name);
2160
- c.initials = c.name.split(/[^a-zA-ZÀ-ÿа-яА-Я]/).map((word) => word[0]).join("").toLowerCase();
2705
+ c.initials = c.normalisedName.split(/[^a-z]/).map((word) => word[0]).join("");
2161
2706
  c.dialCodePlus = `+${c.dialCode}`;
2162
2707
  }
2163
- }
2708
+ };
2164
2709
 
2165
2710
  // angular/build/temp/modules/format/formatting.js
2166
- function beforeSetNumber(fullNumber, dialCode, separateDialCode, selectedCountryData) {
2711
+ var beforeSetNumber = (fullNumber, dialCode, separateDialCode, selectedCountryData) => {
2167
2712
  let number = fullNumber;
2168
2713
  if (separateDialCode) {
2169
2714
  if (dialCode) {
@@ -2173,8 +2718,8 @@ function beforeSetNumber(fullNumber, dialCode, separateDialCode, selectedCountry
2173
2718
  }
2174
2719
  }
2175
2720
  return number;
2176
- }
2177
- function formatNumberAsYouType(fullNumber, telInputValue, utils, selectedCountryData, separateDialCode) {
2721
+ };
2722
+ var formatNumberAsYouType = (fullNumber, telInputValue, utils, selectedCountryData, separateDialCode) => {
2178
2723
  const result = utils ? utils.formatNumberAsYouType(fullNumber, selectedCountryData.iso2) : fullNumber;
2179
2724
  const { dialCode } = selectedCountryData;
2180
2725
  if (separateDialCode && telInputValue.charAt(0) !== "+" && result.includes(`+${dialCode}`)) {
@@ -2182,10 +2727,10 @@ function formatNumberAsYouType(fullNumber, telInputValue, utils, selectedCountry
2182
2727
  return afterDialCode.trim();
2183
2728
  }
2184
2729
  return result;
2185
- }
2730
+ };
2186
2731
 
2187
2732
  // angular/build/temp/modules/format/caret.js
2188
- function translateCursorPosition(relevantChars, formattedValue, prevCaretPos, isDeleteForwards) {
2733
+ var translateCursorPosition = (relevantChars, formattedValue, prevCaretPos, isDeleteForwards) => {
2189
2734
  if (prevCaretPos === 0 && !isDeleteForwards) {
2190
2735
  return 0;
2191
2736
  }
@@ -2202,7 +2747,7 @@ function translateCursorPosition(relevantChars, formattedValue, prevCaretPos, is
2202
2747
  }
2203
2748
  }
2204
2749
  return formattedValue.length;
2205
- }
2750
+ };
2206
2751
 
2207
2752
  // angular/build/temp/modules/data/nanp-regionless.js
2208
2753
  var regionlessNanpNumbers = [
@@ -2226,7 +2771,7 @@ var regionlessNanpNumbers = [
2226
2771
  ];
2227
2772
  var isRegionlessNanp = (number) => {
2228
2773
  const numeric = getNumeric(number);
2229
- if (numeric.charAt(0) === "1") {
2774
+ if (numeric.startsWith(DIAL.NANP) && numeric.length >= 4) {
2230
2775
  const areaCode = numeric.substring(1, 4);
2231
2776
  return regionlessNanpNumbers.includes(areaCode);
2232
2777
  }
@@ -2239,302 +2784,68 @@ for (const c of data_default) {
2239
2784
  }
2240
2785
  var id = 0;
2241
2786
  var iso2Set = new Set(data_default.map((c) => c.iso2));
2242
- var isIso2 = (val) => iso2Set.has(val);
2243
- var forEachInstance = (method, ...args) => {
2244
- const { instances } = intlTelInput;
2245
- Object.values(instances).forEach((instance) => instance[method](...args));
2246
- };
2247
- var Iti = class _Iti {
2248
- /**
2249
- * Build a space-delimited class string from an object map of className -> truthy/falsey.
2250
- * Only keys with truthy values are included.
2251
- */
2252
- static _buildClassNames(flags) {
2253
- return Object.keys(flags).filter((k) => Boolean(flags[k])).join(" ");
2254
- }
2255
- constructor(input, customOptions = {}) {
2256
- this.id = id++;
2257
- this.telInput = input;
2258
- this.highlightedItem = null;
2259
- this.options = Object.assign({}, defaults, customOptions);
2260
- this.hadInitialPlaceholder = Boolean(input.getAttribute("placeholder"));
2261
- }
2262
- _detectEnvironmentAndLayout() {
2263
- this.isAndroid = typeof navigator !== "undefined" ? /Android/i.test(navigator.userAgent) : false;
2264
- this.isRTL = !!this.telInput.closest("[dir=rtl]");
2265
- if (this.options.separateDialCode) {
2266
- this.originalPaddingLeft = this.telInput.style.paddingLeft;
2267
- }
2268
- }
2269
- _createInitPromises() {
2270
- const autoCountryPromise = new Promise((resolve, reject) => {
2271
- this.resolveAutoCountryPromise = resolve;
2272
- this.rejectAutoCountryPromise = reject;
2273
- });
2274
- const utilsScriptPromise = new Promise((resolve, reject) => {
2275
- this.resolveUtilsScriptPromise = resolve;
2276
- this.rejectUtilsScriptPromise = reject;
2277
- });
2278
- this.promise = Promise.all([autoCountryPromise, utilsScriptPromise]);
2279
- }
2280
- //* Can't be private as it's called from intlTelInput convenience wrapper.
2281
- _init() {
2282
- applyOptionSideEffects(this.options);
2283
- this._detectEnvironmentAndLayout();
2284
- this._createInitPromises();
2285
- this.selectedCountryData = {};
2286
- this._processCountryData();
2287
- this._generateMarkup();
2288
- this._setInitialState();
2289
- this._initListeners();
2290
- this._initRequests();
2291
- }
2292
- //********************
2293
- //* PRIVATE METHODS
2294
- //********************
2295
- //* Prepare all of the country data, including onlyCountries, excludeCountries, countryOrder options.
2296
- _processCountryData() {
2297
- this.countries = processAllCountries(this.options);
2298
- const dialRes = processDialCodes(this.countries, this.options);
2299
- this.dialCodes = dialRes.dialCodes;
2300
- this.dialCodeMaxLen = dialRes.dialCodeMaxLen;
2301
- this.dialCodeToIso2Map = dialRes.dialCodeToIso2Map;
2302
- translateCountryNames(this.countries, this.options);
2303
- sortCountries(this.countries, this.options);
2304
- this.countryByIso2 = new Map(this.countries.map((c) => [c.iso2, c]));
2305
- cacheSearchTokens(this.countries);
2306
- }
2307
- //* Generate all of the markup for the plugin: the selected country overlay, and the dropdown.
2308
- _generateMarkup() {
2309
- this._prepareTelInput();
2310
- const wrapper = this._createWrapperAndInsert();
2311
- this._maybeBuildCountryContainer(wrapper);
2312
- wrapper.appendChild(this.telInput);
2313
- this._maybeUpdateInputPaddingAndReveal();
2314
- this._maybeBuildHiddenInputs(wrapper);
2315
- }
2316
- _prepareTelInput() {
2317
- this.telInput.classList.add("iti__tel-input");
2318
- if (!this.telInput.hasAttribute("autocomplete") && !(this.telInput.form && this.telInput.form.hasAttribute("autocomplete"))) {
2319
- this.telInput.setAttribute("autocomplete", "off");
2320
- }
2321
- }
2322
- _createWrapperAndInsert() {
2323
- var _a;
2324
- const { allowDropdown, showFlags, containerClass, useFullscreenPopup } = this.options;
2325
- const parentClasses = _Iti._buildClassNames({
2326
- "iti": true,
2327
- "iti--allow-dropdown": allowDropdown,
2328
- "iti--show-flags": showFlags,
2329
- "iti--inline-dropdown": !useFullscreenPopup,
2330
- [containerClass]: Boolean(containerClass)
2331
- });
2332
- const wrapper = createEl("div", { class: parentClasses });
2333
- if (this.isRTL) {
2334
- wrapper.setAttribute("dir", "ltr");
2335
- }
2336
- (_a = this.telInput.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(wrapper, this.telInput);
2337
- return wrapper;
2338
- }
2339
- _maybeBuildCountryContainer(wrapper) {
2340
- const { allowDropdown, separateDialCode, showFlags } = this.options;
2341
- if (allowDropdown || showFlags || separateDialCode) {
2342
- this.countryContainer = createEl(
2343
- "div",
2344
- // visibly hidden until we measure it's width to set the input padding correctly
2345
- { class: "iti__country-container iti__v-hide" },
2346
- wrapper
2347
- );
2348
- if (allowDropdown) {
2349
- this.selectedCountry = createEl("button", {
2350
- type: "button",
2351
- class: "iti__selected-country",
2352
- "aria-expanded": "false",
2353
- "aria-label": this.options.i18n.noCountrySelected,
2354
- "aria-haspopup": "dialog",
2355
- "aria-controls": `iti-${this.id}__dropdown-content`
2356
- }, this.countryContainer);
2357
- if (this.telInput.disabled) {
2358
- this.selectedCountry.setAttribute("disabled", "true");
2359
- }
2360
- } else {
2361
- this.selectedCountry = createEl("div", { class: "iti__selected-country" }, this.countryContainer);
2362
- }
2363
- const selectedCountryPrimary = createEl("div", { class: "iti__selected-country-primary" }, this.selectedCountry);
2364
- this.selectedCountryInner = createEl("div", { class: "iti__flag" }, selectedCountryPrimary);
2365
- if (allowDropdown) {
2366
- this.dropdownArrow = createEl("div", { class: "iti__arrow", "aria-hidden": "true" }, selectedCountryPrimary);
2367
- }
2368
- if (separateDialCode) {
2369
- this.selectedDialCode = createEl("div", { class: "iti__selected-dial-code" }, this.selectedCountry);
2370
- }
2371
- if (allowDropdown) {
2372
- this._buildDropdownContent();
2373
- }
2374
- }
2375
- }
2376
- _buildDropdownContent() {
2377
- const { fixDropdownWidth, useFullscreenPopup, countrySearch, i18n, dropdownContainer, containerClass } = this.options;
2378
- const extraClasses = fixDropdownWidth ? "" : "iti--flexible-dropdown-width";
2379
- this.dropdownContent = createEl("div", {
2380
- id: `iti-${this.id}__dropdown-content`,
2381
- class: `iti__dropdown-content iti__hide ${extraClasses}`,
2382
- role: "dialog",
2383
- "aria-modal": "true"
2384
- });
2385
- if (this.isRTL) {
2386
- this.dropdownContent.setAttribute("dir", "rtl");
2387
- }
2388
- if (countrySearch) {
2389
- this._buildSearchUI();
2390
- }
2391
- this.countryList = createEl("ul", {
2392
- class: "iti__country-list",
2393
- id: `iti-${this.id}__country-listbox`,
2394
- role: "listbox",
2395
- "aria-label": i18n.countryListAriaLabel
2396
- }, this.dropdownContent);
2397
- this._appendListItems();
2398
- if (countrySearch) {
2399
- this._updateSearchResultsA11yText();
2400
- }
2401
- if (dropdownContainer) {
2402
- const dropdownClasses = _Iti._buildClassNames({
2403
- "iti": true,
2404
- "iti--container": true,
2405
- "iti--fullscreen-popup": useFullscreenPopup,
2406
- "iti--inline-dropdown": !useFullscreenPopup,
2407
- [containerClass]: Boolean(containerClass)
2408
- });
2409
- this.dropdown = createEl("div", { class: dropdownClasses });
2410
- this.dropdown.appendChild(this.dropdownContent);
2411
- } else {
2412
- this.countryContainer.appendChild(this.dropdownContent);
2413
- }
2787
+ var isIso2 = (val) => iso2Set.has(val);
2788
+ var Iti = class _Iti {
2789
+ constructor(input, customOptions = {}) {
2790
+ this.id = id++;
2791
+ this.options = Object.assign(Object.assign({}, defaults), customOptions);
2792
+ applyOptionSideEffects(this.options, en_default);
2793
+ this.ui = new UI(input, this.options, this.id);
2794
+ this.isAndroid = _Iti._getIsAndroid();
2795
+ this.promise = this._createInitPromises();
2796
+ this.countries = processAllCountries(this.options);
2797
+ const { dialCodes, dialCodeMaxLen, dialCodeToIso2Map } = processDialCodes(this.countries, this.options);
2798
+ this.dialCodes = dialCodes;
2799
+ this.dialCodeMaxLen = dialCodeMaxLen;
2800
+ this.dialCodeToIso2Map = dialCodeToIso2Map;
2801
+ this.countryByIso2 = new Map(this.countries.map((c) => [c.iso2, c]));
2802
+ this._init();
2414
2803
  }
2415
- _buildSearchUI() {
2416
- const { i18n } = this.options;
2417
- const searchWrapper = createEl("div", { class: "iti__search-input-wrapper" }, this.dropdownContent);
2418
- this.searchIcon = createEl("span", {
2419
- class: "iti__search-icon",
2420
- "aria-hidden": "true"
2421
- }, searchWrapper);
2422
- this.searchIcon.innerHTML = `
2423
- <svg class="iti__search-icon-svg" width="14" height="14" viewBox="0 0 24 24" focusable="false" aria-hidden="true">
2424
- <circle cx="11" cy="11" r="7" />
2425
- <line x1="21" y1="21" x2="16.65" y2="16.65" />
2426
- </svg>`;
2427
- this.searchInput = createEl("input", {
2428
- id: `iti-${this.id}__search-input`,
2429
- // Chrome says inputs need either a name or an id
2430
- type: "search",
2431
- class: "iti__search-input",
2432
- placeholder: i18n.searchPlaceholder,
2433
- // role=combobox + aria-autocomplete=list + aria-activedescendant allows maintaining focus on the search input while allowing users to navigate search results with up/down keyboard keys
2434
- role: "combobox",
2435
- "aria-expanded": "true",
2436
- "aria-label": i18n.searchPlaceholder,
2437
- "aria-controls": `iti-${this.id}__country-listbox`,
2438
- "aria-autocomplete": "list",
2439
- "autocomplete": "off"
2440
- }, searchWrapper);
2441
- this.searchClearButton = createEl("button", {
2442
- type: "button",
2443
- class: "iti__search-clear iti__hide",
2444
- "aria-label": i18n.clearSearchAriaLabel,
2445
- tabindex: "-1"
2446
- }, searchWrapper);
2447
- const maskId = `iti-${this.id}-clear-mask`;
2448
- this.searchClearButton.innerHTML = `
2449
- <svg class="iti__search-clear-svg" width="12" height="12" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
2450
- <mask id="${maskId}" maskUnits="userSpaceOnUse">
2451
- <rect width="16" height="16" fill="white" />
2452
- <path d="M5.2 5.2 L10.8 10.8 M10.8 5.2 L5.2 10.8" stroke="black" stroke-linecap="round" class="iti__search-clear-x" />
2453
- </mask>
2454
- <circle cx="8" cy="8" r="8" class="iti__search-clear-bg" mask="url(#${maskId})" />
2455
- </svg>`;
2456
- this.searchResultsA11yText = createEl("span", { class: "iti__a11y-text" }, this.dropdownContent);
2457
- this.searchNoResults = createEl("div", {
2458
- class: "iti__no-results iti__hide",
2459
- "aria-hidden": "true"
2460
- // all a11y messaging happens in this.searchResultsA11yText
2461
- }, this.dropdownContent);
2462
- this.searchNoResults.textContent = i18n.zeroSearchResults;
2804
+ static _getIsAndroid() {
2805
+ return typeof navigator !== "undefined" ? /Android/i.test(navigator.userAgent) : false;
2463
2806
  }
2464
- _maybeUpdateInputPaddingAndReveal() {
2465
- if (this.countryContainer) {
2466
- this._updateInputPadding();
2467
- this.countryContainer.classList.remove("iti__v-hide");
2468
- }
2807
+ _createInitPromises() {
2808
+ const autoCountryPromise = new Promise((resolve, reject) => {
2809
+ this.resolveAutoCountryPromise = resolve;
2810
+ this.rejectAutoCountryPromise = reject;
2811
+ });
2812
+ const utilsScriptPromise = new Promise((resolve, reject) => {
2813
+ this.resolveUtilsScriptPromise = resolve;
2814
+ this.rejectUtilsScriptPromise = reject;
2815
+ });
2816
+ return Promise.all([autoCountryPromise, utilsScriptPromise]);
2469
2817
  }
2470
- _maybeBuildHiddenInputs(wrapper) {
2471
- var _a, _b;
2472
- const { hiddenInput } = this.options;
2473
- if (hiddenInput) {
2474
- const telInputName = this.telInput.getAttribute("name") || "";
2475
- const names = hiddenInput(telInputName);
2476
- if (names.phone) {
2477
- const existingInput = (_a = this.telInput.form) === null || _a === void 0 ? void 0 : _a.querySelector(`input[name="${names.phone}"]`);
2478
- if (existingInput) {
2479
- this.hiddenInput = existingInput;
2480
- } else {
2481
- this.hiddenInput = createEl("input", {
2482
- type: "hidden",
2483
- name: names.phone
2484
- });
2485
- wrapper.appendChild(this.hiddenInput);
2486
- }
2487
- }
2488
- if (names.country) {
2489
- const existingInput = (_b = this.telInput.form) === null || _b === void 0 ? void 0 : _b.querySelector(`input[name="${names.country}"]`);
2490
- if (existingInput) {
2491
- this.hiddenInputCountry = existingInput;
2492
- } else {
2493
- this.hiddenInputCountry = createEl("input", {
2494
- type: "hidden",
2495
- name: names.country
2496
- });
2497
- wrapper.appendChild(this.hiddenInputCountry);
2498
- }
2499
- }
2500
- }
2818
+ //* Can't be private as it's called from intlTelInput convenience wrapper.
2819
+ _init() {
2820
+ this.selectedCountryData = {};
2821
+ this.abortController = new AbortController();
2822
+ this._processCountryData();
2823
+ this.ui.generateMarkup(this.countries);
2824
+ this._setInitialState();
2825
+ this._initListeners();
2826
+ this._initRequests();
2501
2827
  }
2502
- //* For each country: add a country list item <li> to the countryList <ul> container.
2503
- _appendListItems() {
2504
- for (let i = 0; i < this.countries.length; i++) {
2505
- const c = this.countries[i];
2506
- const extraClass = i === 0 ? "iti__highlight" : "";
2507
- const listItem = createEl("li", {
2508
- id: `iti-${this.id}__item-${c.iso2}`,
2509
- class: `iti__country ${extraClass}`,
2510
- tabindex: "-1",
2511
- role: "option",
2512
- "data-dial-code": c.dialCode,
2513
- "data-country-code": c.iso2,
2514
- "aria-selected": "false"
2515
- }, this.countryList);
2516
- c.nodeById[this.id] = listItem;
2517
- let content = "";
2518
- if (this.options.showFlags) {
2519
- content += `<div class='iti__flag iti__${c.iso2}'></div>`;
2520
- }
2521
- content += `<span class='iti__country-name'>${c.name}</span>`;
2522
- content += `<span class='iti__dial-code' dir='ltr'>+${c.dialCode}</span>`;
2523
- listItem.insertAdjacentHTML("beforeend", content);
2524
- }
2828
+ //********************
2829
+ //* PRIVATE METHODS
2830
+ //********************
2831
+ //* Prepare all of the country data, including onlyCountries, excludeCountries, countryOrder options.
2832
+ _processCountryData() {
2833
+ translateCountryNames(this.countries, this.options);
2834
+ sortCountries(this.countries, this.options);
2835
+ cacheSearchTokens(this.countries);
2525
2836
  }
2526
2837
  //* Set the initial state of the input value and the selected country by:
2527
2838
  //* 1. Extracting a dial code from the given number
2528
2839
  //* 2. Using explicit initialCountry
2529
2840
  _setInitialState(overrideAutoCountry = false) {
2530
- const attributeValue = this.telInput.getAttribute("value");
2531
- const inputValue = this.telInput.value;
2532
- const useAttribute = attributeValue && attributeValue.charAt(0) === "+" && (!inputValue || inputValue.charAt(0) !== "+");
2841
+ const attributeValue = this.ui.telInput.getAttribute("value");
2842
+ const inputValue = this.ui.telInput.value;
2843
+ const useAttribute = attributeValue && attributeValue.startsWith("+") && (!inputValue || !inputValue.startsWith("+"));
2533
2844
  const val = useAttribute ? attributeValue : inputValue;
2534
2845
  const dialCode = this._getDialCode(val);
2535
2846
  const isRegionlessNanpNumber = isRegionlessNanp(val);
2536
2847
  const { initialCountry, geoIpLookup } = this.options;
2537
- const isAutoCountry = initialCountry === "auto" && geoIpLookup;
2848
+ const isAutoCountry = initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;
2538
2849
  if (dialCode && !isRegionlessNanpNumber) {
2539
2850
  this._updateCountryFromNumber(val);
2540
2851
  } else if (!isAutoCountry || overrideAutoCountry) {
@@ -2543,7 +2854,7 @@ var Iti = class _Iti {
2543
2854
  this._setCountry(lowerInitialCountry);
2544
2855
  } else {
2545
2856
  if (dialCode && isRegionlessNanpNumber) {
2546
- this._setCountry("us");
2857
+ this._setCountry(US.ISO2);
2547
2858
  } else {
2548
2859
  this._setCountry("");
2549
2860
  }
@@ -2559,77 +2870,84 @@ var Iti = class _Iti {
2559
2870
  if (this.options.allowDropdown) {
2560
2871
  this._initDropdownListeners();
2561
2872
  }
2562
- if ((this.hiddenInput || this.hiddenInputCountry) && this.telInput.form) {
2873
+ if ((this.ui.hiddenInput || this.ui.hiddenInputCountry) && this.ui.telInput.form) {
2563
2874
  this._initHiddenInputListener();
2564
2875
  }
2565
2876
  }
2566
2877
  //* Update hidden input on form submit.
2567
2878
  _initHiddenInputListener() {
2568
2879
  var _a;
2569
- this._handleHiddenInputSubmit = () => {
2570
- if (this.hiddenInput) {
2571
- this.hiddenInput.value = this.getNumber();
2880
+ const handleHiddenInputSubmit = () => {
2881
+ if (this.ui.hiddenInput) {
2882
+ this.ui.hiddenInput.value = this.getNumber();
2572
2883
  }
2573
- if (this.hiddenInputCountry) {
2574
- this.hiddenInputCountry.value = this.getSelectedCountryData().iso2 || "";
2884
+ if (this.ui.hiddenInputCountry) {
2885
+ this.ui.hiddenInputCountry.value = this.selectedCountryData.iso2 || "";
2575
2886
  }
2576
2887
  };
2577
- (_a = this.telInput.form) === null || _a === void 0 ? void 0 : _a.addEventListener("submit", this._handleHiddenInputSubmit);
2888
+ (_a = this.ui.telInput.form) === null || _a === void 0 ? void 0 : _a.addEventListener("submit", handleHiddenInputSubmit, {
2889
+ signal: this.abortController.signal
2890
+ });
2578
2891
  }
2579
2892
  //* initialise the dropdown listeners.
2580
2893
  _initDropdownListeners() {
2581
- this._handleLabelClick = (e) => {
2582
- if (this.dropdownContent.classList.contains("iti__hide")) {
2583
- this.telInput.focus();
2894
+ const signal = this.abortController.signal;
2895
+ const handleLabelClick = (e) => {
2896
+ if (this.ui.dropdownContent.classList.contains(CLASSES.HIDE)) {
2897
+ this.ui.telInput.focus();
2584
2898
  } else {
2585
2899
  e.preventDefault();
2586
2900
  }
2587
2901
  };
2588
- const label = this.telInput.closest("label");
2902
+ const label = this.ui.telInput.closest("label");
2589
2903
  if (label) {
2590
- label.addEventListener("click", this._handleLabelClick);
2904
+ label.addEventListener("click", handleLabelClick, { signal });
2591
2905
  }
2592
- this._handleClickSelectedCountry = () => {
2593
- const dropdownClosed = this.dropdownContent.classList.contains("iti__hide");
2594
- if (dropdownClosed && !this.telInput.disabled && !this.telInput.readOnly) {
2906
+ const handleClickSelectedCountry = () => {
2907
+ const dropdownClosed = this.ui.dropdownContent.classList.contains(CLASSES.HIDE);
2908
+ if (dropdownClosed && !this.ui.telInput.disabled && !this.ui.telInput.readOnly) {
2595
2909
  this._openDropdown();
2596
2910
  }
2597
2911
  };
2598
- this.selectedCountry.addEventListener("click", this._handleClickSelectedCountry);
2599
- this._handleCountryContainerKeydown = (e) => {
2600
- const isDropdownHidden = this.dropdownContent.classList.contains("iti__hide");
2601
- if (isDropdownHidden && ["ArrowUp", "ArrowDown", " ", "Enter"].includes(e.key)) {
2912
+ this.ui.selectedCountry.addEventListener("click", handleClickSelectedCountry, {
2913
+ signal
2914
+ });
2915
+ const handleCountryContainerKeydown = (e) => {
2916
+ const isDropdownHidden = this.ui.dropdownContent.classList.contains(CLASSES.HIDE);
2917
+ if (isDropdownHidden && [KEYS.ARROW_UP, KEYS.ARROW_DOWN, KEYS.SPACE, KEYS.ENTER].includes(e.key)) {
2602
2918
  e.preventDefault();
2603
2919
  e.stopPropagation();
2604
2920
  this._openDropdown();
2605
2921
  }
2606
- if (e.key === "Tab") {
2922
+ if (e.key === KEYS.TAB) {
2607
2923
  this._closeDropdown();
2608
2924
  }
2609
2925
  };
2610
- this.countryContainer.addEventListener("keydown", this._handleCountryContainerKeydown);
2926
+ this.ui.countryContainer.addEventListener("keydown", handleCountryContainerKeydown, { signal });
2611
2927
  }
2612
2928
  //* Init many requests: utils script / geo ip lookup.
2613
2929
  _initRequests() {
2614
- let { loadUtils, initialCountry, geoIpLookup } = this.options;
2930
+ const { loadUtils, initialCountry, geoIpLookup } = this.options;
2615
2931
  if (loadUtils && !intlTelInput.utils) {
2616
- this._doAttachUtils = () => {
2932
+ const doAttachUtils = () => {
2617
2933
  var _a;
2618
2934
  (_a = intlTelInput.attachUtils(loadUtils)) === null || _a === void 0 ? void 0 : _a.catch(() => {
2619
2935
  });
2620
2936
  };
2621
2937
  if (intlTelInput.documentReady()) {
2622
- this._doAttachUtils();
2938
+ doAttachUtils();
2623
2939
  } else {
2624
- this._handlePageLoad = () => {
2625
- this._doAttachUtils();
2940
+ const handlePageLoad = () => {
2941
+ doAttachUtils();
2626
2942
  };
2627
- window.addEventListener("load", this._handlePageLoad);
2943
+ window.addEventListener("load", handlePageLoad, {
2944
+ signal: this.abortController.signal
2945
+ });
2628
2946
  }
2629
2947
  } else {
2630
2948
  this.resolveUtilsScriptPromise();
2631
2949
  }
2632
- const isAutoCountry = initialCountry === "auto" && geoIpLookup;
2950
+ const isAutoCountry = initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;
2633
2951
  if (isAutoCountry && !this.selectedCountryData.iso2) {
2634
2952
  this._loadAutoCountry();
2635
2953
  } else {
@@ -2661,8 +2979,8 @@ var Iti = class _Iti {
2661
2979
  }
2662
2980
  _openDropdownWithPlus() {
2663
2981
  this._openDropdown();
2664
- this.searchInput.value = "+";
2665
- this._filterCountries("");
2982
+ this.ui.searchInput.value = "+";
2983
+ this._filterCountriesByQuery("");
2666
2984
  }
2667
2985
  //* Initialize the tel input listeners.
2668
2986
  _initTelInputListeners() {
@@ -2673,47 +2991,49 @@ var Iti = class _Iti {
2673
2991
  _bindInputListener() {
2674
2992
  const { strictMode, formatAsYouType, separateDialCode, allowDropdown, countrySearch } = this.options;
2675
2993
  let userOverrideFormatting = false;
2676
- if (/\p{L}/u.test(this.telInput.value)) {
2994
+ if (REGEX.ALPHA_UNICODE.test(this.ui.telInput.value)) {
2677
2995
  userOverrideFormatting = true;
2678
2996
  }
2679
- this._handleInputEvent = (e) => {
2997
+ const handleInputEvent = (e) => {
2680
2998
  if (this.isAndroid && (e === null || e === void 0 ? void 0 : e.data) === "+" && separateDialCode && allowDropdown && countrySearch) {
2681
- const currentCaretPos = this.telInput.selectionStart || 0;
2682
- const valueBeforeCaret = this.telInput.value.substring(0, currentCaretPos - 1);
2683
- const valueAfterCaret = this.telInput.value.substring(currentCaretPos);
2684
- this.telInput.value = valueBeforeCaret + valueAfterCaret;
2999
+ const currentCaretPos = this.ui.telInput.selectionStart || 0;
3000
+ const valueBeforeCaret = this.ui.telInput.value.substring(0, currentCaretPos - 1);
3001
+ const valueAfterCaret = this.ui.telInput.value.substring(currentCaretPos);
3002
+ this.ui.telInput.value = valueBeforeCaret + valueAfterCaret;
2685
3003
  this._openDropdownWithPlus();
2686
3004
  return;
2687
3005
  }
2688
- if (this._updateCountryFromNumber(this.telInput.value)) {
3006
+ if (this._updateCountryFromNumber(this.ui.telInput.value)) {
2689
3007
  this._triggerCountryChange();
2690
3008
  }
2691
- const isFormattingChar = (e === null || e === void 0 ? void 0 : e.data) && /[^+0-9]/.test(e.data);
2692
- const isPaste = (e === null || e === void 0 ? void 0 : e.inputType) === "insertFromPaste" && this.telInput.value;
3009
+ const isFormattingChar = (e === null || e === void 0 ? void 0 : e.data) && REGEX.NON_PLUS_NUMERIC.test(e.data);
3010
+ const isPaste = (e === null || e === void 0 ? void 0 : e.inputType) === INPUT_TYPES.PASTE && this.ui.telInput.value;
2693
3011
  if (isFormattingChar || isPaste && !strictMode) {
2694
3012
  userOverrideFormatting = true;
2695
- } else if (!/[^+0-9]/.test(this.telInput.value)) {
3013
+ } else if (!REGEX.NON_PLUS_NUMERIC.test(this.ui.telInput.value)) {
2696
3014
  userOverrideFormatting = false;
2697
3015
  }
2698
3016
  const isSetNumber = (e === null || e === void 0 ? void 0 : e.detail) && e.detail["isSetNumber"];
2699
3017
  if (formatAsYouType && !userOverrideFormatting && !isSetNumber) {
2700
- const currentCaretPos = this.telInput.selectionStart || 0;
2701
- const valueBeforeCaret = this.telInput.value.substring(0, currentCaretPos);
2702
- const relevantCharsBeforeCaret = valueBeforeCaret.replace(/[^+0-9]/g, "").length;
2703
- const isDeleteForwards = (e === null || e === void 0 ? void 0 : e.inputType) === "deleteContentForward";
3018
+ const currentCaretPos = this.ui.telInput.selectionStart || 0;
3019
+ const valueBeforeCaret = this.ui.telInput.value.substring(0, currentCaretPos);
3020
+ const relevantCharsBeforeCaret = valueBeforeCaret.replace(REGEX.NON_PLUS_NUMERIC_GLOBAL, "").length;
3021
+ const isDeleteForwards = (e === null || e === void 0 ? void 0 : e.inputType) === INPUT_TYPES.DELETE_FWD;
2704
3022
  const fullNumber = this._getFullNumber();
2705
- const formattedValue = formatNumberAsYouType(fullNumber, this.telInput.value, intlTelInput.utils, this.selectedCountryData, this.options.separateDialCode);
3023
+ const formattedValue = formatNumberAsYouType(fullNumber, this.ui.telInput.value, intlTelInput.utils, this.selectedCountryData, this.options.separateDialCode);
2706
3024
  const newCaretPos = translateCursorPosition(relevantCharsBeforeCaret, formattedValue, currentCaretPos, isDeleteForwards);
2707
- this.telInput.value = formattedValue;
2708
- this.telInput.setSelectionRange(newCaretPos, newCaretPos);
3025
+ this.ui.telInput.value = formattedValue;
3026
+ this.ui.telInput.setSelectionRange(newCaretPos, newCaretPos);
2709
3027
  }
2710
3028
  };
2711
- this.telInput.addEventListener("input", this._handleInputEvent);
3029
+ this.ui.telInput.addEventListener("input", handleInputEvent, {
3030
+ signal: this.abortController.signal
3031
+ });
2712
3032
  }
2713
3033
  _maybeBindKeydownListener() {
2714
3034
  const { strictMode, separateDialCode, allowDropdown, countrySearch } = this.options;
2715
3035
  if (strictMode || separateDialCode) {
2716
- this._handleKeydownEvent = (e) => {
3036
+ const handleKeydownEvent = (e) => {
2717
3037
  if (e.key && e.key.length === 1 && !e.altKey && !e.ctrlKey && !e.metaKey) {
2718
3038
  if (separateDialCode && allowDropdown && countrySearch && e.key === "+") {
2719
3039
  e.preventDefault();
@@ -2721,12 +3041,12 @@ var Iti = class _Iti {
2721
3041
  return;
2722
3042
  }
2723
3043
  if (strictMode) {
2724
- const value = this.telInput.value;
2725
- const alreadyHasPlus = value.charAt(0) === "+";
2726
- const isInitialPlus = !alreadyHasPlus && this.telInput.selectionStart === 0 && e.key === "+";
3044
+ const value = this.ui.telInput.value;
3045
+ const alreadyHasPlus = value.startsWith("+");
3046
+ const isInitialPlus = !alreadyHasPlus && this.ui.telInput.selectionStart === 0 && e.key === "+";
2727
3047
  const isNumeric = /^[0-9]$/.test(e.key);
2728
3048
  const isAllowedChar = separateDialCode ? isNumeric : isInitialPlus || isNumeric;
2729
- const newValue = value.slice(0, this.telInput.selectionStart) + e.key + value.slice(this.telInput.selectionEnd);
3049
+ const newValue = value.slice(0, this.ui.telInput.selectionStart) + e.key + value.slice(this.ui.telInput.selectionEnd);
2730
3050
  const newFullNumber = this._getFullNumber(newValue);
2731
3051
  const coreNumber = intlTelInput.utils.getCoreNumber(newFullNumber, this.selectedCountryData.iso2);
2732
3052
  const hasExceededMaxLength = this.maxCoreNumberLength && coreNumber.length > this.maxCoreNumberLength;
@@ -2738,14 +3058,16 @@ var Iti = class _Iti {
2738
3058
  }
2739
3059
  }
2740
3060
  };
2741
- this.telInput.addEventListener("keydown", this._handleKeydownEvent);
3061
+ this.ui.telInput.addEventListener("keydown", handleKeydownEvent, {
3062
+ signal: this.abortController.signal
3063
+ });
2742
3064
  }
2743
3065
  }
2744
3066
  _maybeBindPasteListener() {
2745
3067
  if (this.options.strictMode) {
2746
- this._handlePasteEvent = (e) => {
3068
+ const handlePasteEvent = (e) => {
2747
3069
  e.preventDefault();
2748
- const input = this.telInput;
3070
+ const input = this.ui.telInput;
2749
3071
  const selStart = input.selectionStart;
2750
3072
  const selEnd = input.selectionEnd;
2751
3073
  const before = input.value.slice(0, selStart);
@@ -2754,7 +3076,7 @@ var Iti = class _Iti {
2754
3076
  const pasted = e.clipboardData.getData("text");
2755
3077
  const initialCharSelected = selStart === 0 && selEnd > 0;
2756
3078
  const allowLeadingPlus = !input.value.startsWith("+") || initialCharSelected;
2757
- const allowedChars = pasted.replace(/[^0-9+]/g, "");
3079
+ const allowedChars = pasted.replace(REGEX.NON_PLUS_NUMERIC_GLOBAL, "");
2758
3080
  const hasLeadingPlus = allowedChars.startsWith("+");
2759
3081
  const numerics = allowedChars.replace(/\+/g, "");
2760
3082
  const sanitised = hasLeadingPlus && allowLeadingPlus ? `+${numerics}` : numerics;
@@ -2780,79 +3102,107 @@ var Iti = class _Iti {
2780
3102
  input.setSelectionRange(caretPos, caretPos);
2781
3103
  input.dispatchEvent(new InputEvent("input", { bubbles: true }));
2782
3104
  };
2783
- this.telInput.addEventListener("paste", this._handlePasteEvent);
3105
+ this.ui.telInput.addEventListener("paste", handlePasteEvent, {
3106
+ signal: this.abortController.signal
3107
+ });
2784
3108
  }
2785
3109
  }
2786
3110
  //* Adhere to the input's maxlength attr.
2787
3111
  _cap(number) {
2788
- const max = parseInt(this.telInput.getAttribute("maxlength") || "", 10);
3112
+ const max = Number(this.ui.telInput.getAttribute("maxlength"));
2789
3113
  return max && number.length > max ? number.substring(0, max) : number;
2790
3114
  }
2791
- //* Trigger a custom event on the input.
3115
+ //* Trigger a custom event on the input (typed via ItiEventMap).
2792
3116
  _trigger(name, detailProps = {}) {
2793
3117
  const e = new CustomEvent(name, {
2794
3118
  bubbles: true,
2795
3119
  cancelable: true,
2796
3120
  detail: detailProps
2797
3121
  });
2798
- this.telInput.dispatchEvent(e);
3122
+ this.ui.telInput.dispatchEvent(e);
2799
3123
  }
2800
3124
  //* Open the dropdown.
2801
3125
  _openDropdown() {
2802
3126
  const { fixDropdownWidth, countrySearch } = this.options;
3127
+ this.dropdownAbortController = new AbortController();
2803
3128
  if (fixDropdownWidth) {
2804
- this.dropdownContent.style.width = `${this.telInput.offsetWidth}px`;
3129
+ this.ui.dropdownContent.style.width = `${this.ui.telInput.offsetWidth}px`;
2805
3130
  }
2806
- this.dropdownContent.classList.remove("iti__hide");
2807
- this.selectedCountry.setAttribute("aria-expanded", "true");
3131
+ this.ui.dropdownContent.classList.remove(CLASSES.HIDE);
3132
+ this.ui.selectedCountry.setAttribute(ARIA.EXPANDED, "true");
2808
3133
  this._setDropdownPosition();
2809
3134
  if (countrySearch) {
2810
- const firstCountryItem = this.countryList.firstElementChild;
3135
+ const firstCountryItem = this.ui.countryList.firstElementChild;
2811
3136
  if (firstCountryItem) {
2812
- this._highlightListItem(firstCountryItem, false);
2813
- this.countryList.scrollTop = 0;
3137
+ this.ui.highlightListItem(firstCountryItem, false);
3138
+ this.ui.countryList.scrollTop = 0;
2814
3139
  }
2815
- this.searchInput.focus();
3140
+ this.ui.searchInput.focus();
2816
3141
  }
2817
3142
  this._bindDropdownListeners();
2818
- this.dropdownArrow.classList.add("iti__arrow--up");
2819
- this._trigger("open:countrydropdown");
3143
+ this.ui.dropdownArrow.classList.add(CLASSES.ARROW_UP);
3144
+ this._trigger(EVENTS.OPEN_COUNTRY_DROPDOWN);
2820
3145
  }
2821
3146
  //* Set the dropdown position
2822
3147
  _setDropdownPosition() {
2823
3148
  if (this.options.dropdownContainer) {
2824
- this.options.dropdownContainer.appendChild(this.dropdown);
3149
+ this.options.dropdownContainer.appendChild(this.ui.dropdown);
2825
3150
  }
2826
3151
  if (!this.options.useFullscreenPopup) {
2827
- const inputPosRelativeToVP = this.telInput.getBoundingClientRect();
2828
- const inputHeight = this.telInput.offsetHeight;
3152
+ const inputPosRelativeToVP = this.ui.telInput.getBoundingClientRect();
3153
+ const inputHeight = this.ui.telInput.offsetHeight;
2829
3154
  if (this.options.dropdownContainer) {
2830
- this.dropdown.style.top = `${inputPosRelativeToVP.top + inputHeight}px`;
2831
- this.dropdown.style.left = `${inputPosRelativeToVP.left}px`;
2832
- this._handleWindowScroll = () => this._closeDropdown();
2833
- window.addEventListener("scroll", this._handleWindowScroll);
3155
+ this.ui.dropdown.style.top = `${inputPosRelativeToVP.top + inputHeight}px`;
3156
+ this.ui.dropdown.style.left = `${inputPosRelativeToVP.left}px`;
3157
+ const handleWindowScroll = () => this._closeDropdown();
3158
+ window.addEventListener("scroll", handleWindowScroll, {
3159
+ signal: this.dropdownAbortController.signal
3160
+ });
2834
3161
  }
2835
3162
  }
2836
3163
  }
2837
3164
  //* We only bind dropdown listeners when the dropdown is open.
2838
3165
  _bindDropdownListeners() {
2839
- this._handleMouseoverCountryList = (e) => {
3166
+ const signal = this.dropdownAbortController.signal;
3167
+ this._bindDropdownMouseoverListener(signal);
3168
+ this._bindDropdownCountryClickListener(signal);
3169
+ this._bindDropdownClickOffListener(signal);
3170
+ this._bindDropdownKeydownListener(signal);
3171
+ if (this.options.countrySearch) {
3172
+ this._bindDropdownSearchListeners(signal);
3173
+ }
3174
+ }
3175
+ //* When mouse over a list item, just highlight that one
3176
+ //* we add the class "highlight", so if they hit "enter" we know which one to select.
3177
+ _bindDropdownMouseoverListener(signal) {
3178
+ const handleMouseoverCountryList = (e) => {
2840
3179
  var _a;
2841
- const listItem = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest(".iti__country");
3180
+ const listItem = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest(`.${CLASSES.COUNTRY_ITEM}`);
2842
3181
  if (listItem) {
2843
- this._highlightListItem(listItem, false);
3182
+ this.ui.highlightListItem(listItem, false);
2844
3183
  }
2845
3184
  };
2846
- this.countryList.addEventListener("mouseover", this._handleMouseoverCountryList);
2847
- this._handleClickCountryList = (e) => {
3185
+ this.ui.countryList.addEventListener("mouseover", handleMouseoverCountryList, {
3186
+ signal
3187
+ });
3188
+ }
3189
+ //* Listen for country selection.
3190
+ _bindDropdownCountryClickListener(signal) {
3191
+ const handleClickCountryList = (e) => {
2848
3192
  var _a;
2849
- const listItem = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest(".iti__country");
3193
+ const listItem = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest(`.${CLASSES.COUNTRY_ITEM}`);
2850
3194
  if (listItem) {
2851
3195
  this._selectListItem(listItem);
2852
3196
  }
2853
3197
  };
2854
- this.countryList.addEventListener("click", this._handleClickCountryList);
2855
- this._handleClickOffToClose = (e) => {
3198
+ this.ui.countryList.addEventListener("click", handleClickCountryList, {
3199
+ signal
3200
+ });
3201
+ }
3202
+ //* Click off to close (except when this initial opening click is bubbling up).
3203
+ //* We cannot just stopPropagation as it may be needed to close another instance.
3204
+ _bindDropdownClickOffListener(signal) {
3205
+ const handleClickOffToClose = (e) => {
2856
3206
  const target = e.target;
2857
3207
  const clickedInsideDropdown = !!target.closest(`#iti-${this.id}__dropdown-content`);
2858
3208
  if (!clickedInsideDropdown) {
@@ -2860,23 +3210,35 @@ var Iti = class _Iti {
2860
3210
  }
2861
3211
  };
2862
3212
  setTimeout(() => {
2863
- document.documentElement.addEventListener("click", this._handleClickOffToClose);
3213
+ document.documentElement.addEventListener("click", handleClickOffToClose, { signal });
2864
3214
  }, 0);
3215
+ }
3216
+ //* Listen for up/down scrolling, enter to select, or escape to close.
3217
+ //* Use keydown as keypress doesn't fire for non-char keys and we want to catch if they
3218
+ //* just hit down and hold it to scroll down (no keyup event).
3219
+ //* Listen on the document because that's where key events are triggered if no input has focus.
3220
+ _bindDropdownKeydownListener(signal) {
2865
3221
  let query = "";
2866
3222
  let queryTimer = null;
2867
- this._handleKeydownOnDropdown = (e) => {
2868
- if (["ArrowUp", "ArrowDown", "Enter", "Escape"].includes(e.key)) {
3223
+ const handleKeydownOnDropdown = (e) => {
3224
+ const allowedKeys = [
3225
+ KEYS.ARROW_UP,
3226
+ KEYS.ARROW_DOWN,
3227
+ KEYS.ENTER,
3228
+ KEYS.ESC
3229
+ ];
3230
+ if (allowedKeys.includes(e.key)) {
2869
3231
  e.preventDefault();
2870
3232
  e.stopPropagation();
2871
- if (e.key === "ArrowUp" || e.key === "ArrowDown") {
3233
+ if (e.key === KEYS.ARROW_UP || e.key === KEYS.ARROW_DOWN) {
2872
3234
  this._handleUpDownKey(e.key);
2873
- } else if (e.key === "Enter") {
3235
+ } else if (e.key === KEYS.ENTER) {
2874
3236
  this._handleEnterKey();
2875
- } else if (e.key === "Escape") {
3237
+ } else if (e.key === KEYS.ESC) {
2876
3238
  this._closeDropdown();
2877
3239
  }
2878
3240
  }
2879
- if (!this.options.countrySearch && /^[a-zA-ZÀ-ÿа-яА-Я ]$/.test(e.key)) {
3241
+ if (!this.options.countrySearch && REGEX.HIDDEN_SEARCH_CHAR.test(e.key)) {
2880
3242
  e.stopPropagation();
2881
3243
  if (queryTimer) {
2882
3244
  clearTimeout(queryTimer);
@@ -2885,148 +3247,79 @@ var Iti = class _Iti {
2885
3247
  this._searchForCountry(query);
2886
3248
  queryTimer = setTimeout(() => {
2887
3249
  query = "";
2888
- }, 1e3);
3250
+ }, TIMINGS.HIDDEN_SEARCH_RESET_MS);
2889
3251
  }
2890
3252
  };
2891
- document.addEventListener("keydown", this._handleKeydownOnDropdown);
2892
- if (this.options.countrySearch) {
2893
- const doFilter = () => {
2894
- const inputQuery = this.searchInput.value.trim();
2895
- this._filterCountries(inputQuery);
2896
- if (this.searchInput.value) {
2897
- this.searchClearButton.classList.remove("iti__hide");
2898
- } else {
2899
- this.searchClearButton.classList.add("iti__hide");
2900
- }
2901
- };
2902
- let keyupTimer = null;
2903
- this._handleSearchChange = () => {
2904
- if (keyupTimer) {
2905
- clearTimeout(keyupTimer);
2906
- }
2907
- keyupTimer = setTimeout(() => {
2908
- doFilter();
2909
- keyupTimer = null;
2910
- }, 100);
2911
- };
2912
- this.searchInput.addEventListener("input", this._handleSearchChange);
2913
- this._handleSearchClear = () => {
2914
- this.searchInput.value = "";
2915
- this.searchInput.focus();
3253
+ document.addEventListener("keydown", handleKeydownOnDropdown, { signal });
3254
+ }
3255
+ //* Search input listeners when countrySearch enabled.
3256
+ _bindDropdownSearchListeners(signal) {
3257
+ const doFilter = () => {
3258
+ const inputQuery = this.ui.searchInput.value.trim();
3259
+ this._filterCountriesByQuery(inputQuery);
3260
+ if (this.ui.searchInput.value) {
3261
+ this.ui.searchClearButton.classList.remove(CLASSES.HIDE);
3262
+ } else {
3263
+ this.ui.searchClearButton.classList.add(CLASSES.HIDE);
3264
+ }
3265
+ };
3266
+ let keyupTimer = null;
3267
+ const handleSearchChange = () => {
3268
+ if (keyupTimer) {
3269
+ clearTimeout(keyupTimer);
3270
+ }
3271
+ keyupTimer = setTimeout(() => {
2916
3272
  doFilter();
2917
- };
2918
- this.searchClearButton.addEventListener("click", this._handleSearchClear);
2919
- }
3273
+ keyupTimer = null;
3274
+ }, 100);
3275
+ };
3276
+ this.ui.searchInput.addEventListener("input", handleSearchChange, {
3277
+ signal
3278
+ });
3279
+ const handleSearchClear = () => {
3280
+ this.ui.searchInput.value = "";
3281
+ this.ui.searchInput.focus();
3282
+ doFilter();
3283
+ };
3284
+ this.ui.searchClearButton.addEventListener("click", handleSearchClear, {
3285
+ signal
3286
+ });
2920
3287
  }
2921
3288
  //* Hidden search (countrySearch disabled): Find the first list item whose name starts with the query string.
2922
3289
  _searchForCountry(query) {
2923
- for (const c of this.countries) {
2924
- const startsWith = c.name.substring(0, query.length).toLowerCase() === query;
2925
- if (startsWith) {
2926
- const listItem = c.nodeById[this.id];
2927
- this._highlightListItem(listItem, false);
2928
- this._scrollTo(listItem);
2929
- break;
2930
- }
3290
+ const match = findFirstCountryStartingWith(this.countries, query);
3291
+ if (match) {
3292
+ const listItem = match.nodeById[this.id];
3293
+ this.ui.highlightListItem(listItem, false);
3294
+ this.ui.scrollTo(listItem);
2931
3295
  }
2932
3296
  }
2933
- //* Country search enabled: Filter the countries according to the search query.
2934
- _filterCountries(query) {
2935
- this.countryList.innerHTML = "";
3297
+ //* Country search: Filter the countries according to the search query.
3298
+ _filterCountriesByQuery(query) {
2936
3299
  let matchedCountries;
2937
3300
  if (query === "") {
2938
3301
  matchedCountries = this.countries;
2939
3302
  } else {
2940
- matchedCountries = this._getMatchedCountries(query);
2941
- }
2942
- let noCountriesAddedYet = true;
2943
- for (const c of matchedCountries) {
2944
- const listItem = c.nodeById[this.id];
2945
- if (listItem) {
2946
- this.countryList.appendChild(listItem);
2947
- if (noCountriesAddedYet) {
2948
- this._highlightListItem(listItem, false);
2949
- noCountriesAddedYet = false;
2950
- }
2951
- }
2952
- }
2953
- if (noCountriesAddedYet) {
2954
- this._highlightListItem(null, false);
2955
- if (this.searchNoResults) {
2956
- this.searchNoResults.classList.remove("iti__hide");
2957
- }
2958
- } else if (this.searchNoResults) {
2959
- this.searchNoResults.classList.add("iti__hide");
2960
- }
2961
- this.countryList.scrollTop = 0;
2962
- this._updateSearchResultsA11yText();
2963
- }
2964
- _getMatchedCountries(query) {
2965
- const normalisedQuery = normaliseString(query);
2966
- const iso2Matches = [];
2967
- const nameStartWith = [];
2968
- const nameContains = [];
2969
- const dialCodeMatches = [];
2970
- const dialCodeContains = [];
2971
- const initialsMatches = [];
2972
- for (const c of this.countries) {
2973
- if (c.iso2 === normalisedQuery) {
2974
- iso2Matches.push(c);
2975
- } else if (c.normalisedName.startsWith(normalisedQuery)) {
2976
- nameStartWith.push(c);
2977
- } else if (c.normalisedName.includes(normalisedQuery)) {
2978
- nameContains.push(c);
2979
- } else if (normalisedQuery === c.dialCode || normalisedQuery === c.dialCodePlus) {
2980
- dialCodeMatches.push(c);
2981
- } else if (c.dialCodePlus.includes(normalisedQuery)) {
2982
- dialCodeContains.push(c);
2983
- } else if (c.initials.includes(normalisedQuery)) {
2984
- initialsMatches.push(c);
2985
- }
2986
- }
2987
- return [
2988
- ...iso2Matches.sort((a, b) => a.priority - b.priority),
2989
- ...nameStartWith.sort((a, b) => a.priority - b.priority),
2990
- ...nameContains.sort((a, b) => a.priority - b.priority),
2991
- ...dialCodeMatches.sort((a, b) => a.priority - b.priority),
2992
- ...dialCodeContains.sort((a, b) => a.priority - b.priority),
2993
- ...initialsMatches.sort((a, b) => a.priority - b.priority)
2994
- ];
2995
- }
2996
- //* Update search results text (for a11y).
2997
- _updateSearchResultsA11yText() {
2998
- const { i18n } = this.options;
2999
- const count = this.countryList.childElementCount;
3000
- let searchText;
3001
- if (count === 0) {
3002
- searchText = i18n.zeroSearchResults;
3003
- } else {
3004
- if (i18n.searchResultsText) {
3005
- searchText = i18n.searchResultsText(count);
3006
- } else if (count === 1) {
3007
- searchText = i18n.oneSearchResult;
3008
- } else {
3009
- searchText = i18n.multipleSearchResults.replace("${count}", count.toString());
3010
- }
3303
+ matchedCountries = getMatchedCountries(this.countries, query);
3011
3304
  }
3012
- this.searchResultsA11yText.textContent = searchText;
3305
+ this.ui.filterCountries(matchedCountries);
3013
3306
  }
3014
3307
  //* Highlight the next/prev item in the list (and ensure it is visible).
3015
3308
  _handleUpDownKey(key) {
3016
3309
  var _a, _b;
3017
- let next = key === "ArrowUp" ? (_a = this.highlightedItem) === null || _a === void 0 ? void 0 : _a.previousElementSibling : (_b = this.highlightedItem) === null || _b === void 0 ? void 0 : _b.nextElementSibling;
3018
- if (!next && this.countryList.childElementCount > 1) {
3019
- next = key === "ArrowUp" ? this.countryList.lastElementChild : this.countryList.firstElementChild;
3310
+ let next = key === KEYS.ARROW_UP ? (_a = this.ui.highlightedItem) === null || _a === void 0 ? void 0 : _a.previousElementSibling : (_b = this.ui.highlightedItem) === null || _b === void 0 ? void 0 : _b.nextElementSibling;
3311
+ if (!next && this.ui.countryList.childElementCount > 1) {
3312
+ next = key === KEYS.ARROW_UP ? this.ui.countryList.lastElementChild : this.ui.countryList.firstElementChild;
3020
3313
  }
3021
3314
  if (next) {
3022
- this._scrollTo(next);
3023
- this._highlightListItem(next, false);
3315
+ this.ui.scrollTo(next);
3316
+ this.ui.highlightListItem(next, false);
3024
3317
  }
3025
3318
  }
3026
3319
  //* Select the currently highlighted item.
3027
3320
  _handleEnterKey() {
3028
- if (this.highlightedItem) {
3029
- this._selectListItem(this.highlightedItem);
3321
+ if (this.ui.highlightedItem) {
3322
+ this._selectListItem(this.ui.highlightedItem);
3030
3323
  }
3031
3324
  }
3032
3325
  //* Update the input's value to the given val (format first if possible)
@@ -3034,13 +3327,13 @@ var Iti = class _Iti {
3034
3327
  _updateValFromNumber(fullNumber) {
3035
3328
  let number = fullNumber;
3036
3329
  if (this.options.formatOnDisplay && intlTelInput.utils && this.selectedCountryData) {
3037
- const useNational = this.options.nationalMode || number.charAt(0) !== "+" && !this.options.separateDialCode;
3330
+ const useNational = this.options.nationalMode || !number.startsWith("+") && !this.options.separateDialCode;
3038
3331
  const { NATIONAL, INTERNATIONAL } = intlTelInput.utils.numberFormat;
3039
3332
  const format = useNational ? NATIONAL : INTERNATIONAL;
3040
3333
  number = intlTelInput.utils.formatNumber(number, this.selectedCountryData.iso2, format);
3041
3334
  }
3042
3335
  number = this._beforeSetNumber(number);
3043
- this.telInput.value = number;
3336
+ this.ui.telInput.value = number;
3044
3337
  }
3045
3338
  //* Check if need to select a new country based on the given number
3046
3339
  //* Note: called from _setInitialState, keyup handler, setNumber.
@@ -3054,11 +3347,11 @@ var Iti = class _Iti {
3054
3347
  // if there is a selected country, and the number doesn't start with a dial code, then add it
3055
3348
  _ensureHasDialCode(number) {
3056
3349
  const { dialCode, nationalPrefix } = this.selectedCountryData;
3057
- const alreadyHasPlus = number.charAt(0) === "+";
3350
+ const alreadyHasPlus = number.startsWith("+");
3058
3351
  if (alreadyHasPlus || !dialCode) {
3059
3352
  return number;
3060
3353
  }
3061
- const hasPrefix = nationalPrefix && number.charAt(0) === nationalPrefix && !this.options.separateDialCode;
3354
+ const hasPrefix = nationalPrefix && number.startsWith(nationalPrefix) && !this.options.separateDialCode;
3062
3355
  const cleanNumber = hasPrefix ? number.substring(1) : number;
3063
3356
  return `+${dialCode}${cleanNumber}`;
3064
3357
  }
@@ -3085,7 +3378,7 @@ var Iti = class _Iti {
3085
3378
  if (!selectedIso2 && this.defaultCountry && iso2Codes.includes(this.defaultCountry)) {
3086
3379
  return this.defaultCountry;
3087
3380
  }
3088
- const isRegionlessNanpNumber = selectedDialCode === "1" && isRegionlessNanp(numeric);
3381
+ const isRegionlessNanpNumber = selectedDialCode === DIAL.NANP && isRegionlessNanp(numeric);
3089
3382
  if (isRegionlessNanpNumber) {
3090
3383
  return null;
3091
3384
  }
@@ -3105,33 +3398,13 @@ var Iti = class _Iti {
3105
3398
  if (!isValidSelection && !alreadySelected) {
3106
3399
  return iso2Codes[0];
3107
3400
  }
3108
- } else if (number.charAt(0) === "+" && numeric.length) {
3401
+ } else if (number.startsWith("+") && numeric.length) {
3109
3402
  return "";
3110
3403
  } else if ((!number || number === "+") && !selectedIso2) {
3111
3404
  return this.defaultCountry;
3112
3405
  }
3113
3406
  return null;
3114
3407
  }
3115
- //* Remove highlighting from other list items and highlight the given item.
3116
- _highlightListItem(listItem, shouldFocus) {
3117
- const prevItem = this.highlightedItem;
3118
- if (prevItem) {
3119
- prevItem.classList.remove("iti__highlight");
3120
- prevItem.setAttribute("aria-selected", "false");
3121
- }
3122
- this.highlightedItem = listItem;
3123
- if (this.highlightedItem) {
3124
- this.highlightedItem.classList.add("iti__highlight");
3125
- this.highlightedItem.setAttribute("aria-selected", "true");
3126
- if (this.options.countrySearch) {
3127
- const activeDescendant = this.highlightedItem.getAttribute("id") || "";
3128
- this.searchInput.setAttribute("aria-activedescendant", activeDescendant);
3129
- }
3130
- }
3131
- if (shouldFocus) {
3132
- this.highlightedItem.focus();
3133
- }
3134
- }
3135
3408
  //* Update the selected country, dial code (if separateDialCode), placeholder, title, and active list item.
3136
3409
  //* Note: called from _setInitialState, _updateCountryFromNumber, _selectListItem, setCountry.
3137
3410
  _setCountry(iso2) {
@@ -3141,8 +3414,8 @@ var Iti = class _Iti {
3141
3414
  if (this.selectedCountryData.iso2) {
3142
3415
  this.defaultCountry = this.selectedCountryData.iso2;
3143
3416
  }
3144
- if (this.selectedCountry) {
3145
- const flagClass = iso2 && showFlags ? `iti__flag iti__${iso2}` : "iti__flag iti__globe";
3417
+ if (this.ui.selectedCountry) {
3418
+ const flagClass = iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`;
3146
3419
  let ariaLabel, title;
3147
3420
  if (iso2) {
3148
3421
  const { name, dialCode } = this.selectedCountryData;
@@ -3152,28 +3425,19 @@ var Iti = class _Iti {
3152
3425
  title = i18n.noCountrySelected;
3153
3426
  ariaLabel = i18n.noCountrySelected;
3154
3427
  }
3155
- this.selectedCountryInner.className = flagClass;
3156
- this.selectedCountry.setAttribute("title", title);
3157
- this.selectedCountry.setAttribute("aria-label", ariaLabel);
3428
+ this.ui.selectedCountryInner.className = flagClass;
3429
+ this.ui.selectedCountry.setAttribute("title", title);
3430
+ this.ui.selectedCountry.setAttribute(ARIA.LABEL, ariaLabel);
3158
3431
  }
3159
3432
  if (separateDialCode) {
3160
3433
  const dialCode = this.selectedCountryData.dialCode ? `+${this.selectedCountryData.dialCode}` : "";
3161
- this.selectedDialCode.innerHTML = dialCode;
3162
- this._updateInputPadding();
3434
+ this.ui.selectedDialCode.textContent = dialCode;
3435
+ this.ui.updateInputPadding();
3163
3436
  }
3164
3437
  this._updatePlaceholder();
3165
3438
  this._updateMaxLength();
3166
3439
  return prevIso2 !== iso2;
3167
3440
  }
3168
- //* Update the input padding to make space for the selected country/dial code.
3169
- _updateInputPadding() {
3170
- if (this.selectedCountry) {
3171
- const saneDefaultWidth = this.options.separateDialCode ? 78 : 42;
3172
- const selectedCountryWidth = this.selectedCountry.offsetWidth || this._getHiddenSelectedCountryWidth() || saneDefaultWidth;
3173
- const inputPadding = selectedCountryWidth + 6;
3174
- this.telInput.style.paddingLeft = `${inputPadding}px`;
3175
- }
3176
- }
3177
3441
  //* Update the maximum valid number length for the currently selected country.
3178
3442
  _updateMaxLength() {
3179
3443
  const { strictMode, placeholderNumberType, validationNumberTypes } = this.options;
@@ -3197,35 +3461,10 @@ var Iti = class _Iti {
3197
3461
  }
3198
3462
  }
3199
3463
  }
3200
- //* When input is in a hidden container during init, we cannot calculate the selected country width.
3201
- //* Fix: clone the markup, make it invisible, add it to the end of the DOM, and then measure it's width.
3202
- //* To get the right styling to apply, all we need is a shallow clone of the container,
3203
- //* and then to inject a deep clone of the selectedCountry element.
3204
- _getHiddenSelectedCountryWidth() {
3205
- if (this.telInput.parentNode) {
3206
- let body;
3207
- try {
3208
- body = window.top.document.body;
3209
- } catch (e) {
3210
- body = document.body;
3211
- }
3212
- const containerClone = this.telInput.parentNode.cloneNode(false);
3213
- containerClone.style.visibility = "hidden";
3214
- body.appendChild(containerClone);
3215
- const countryContainerClone = this.countryContainer.cloneNode();
3216
- containerClone.appendChild(countryContainerClone);
3217
- const selectedCountryClone = this.selectedCountry.cloneNode(true);
3218
- countryContainerClone.appendChild(selectedCountryClone);
3219
- const width = selectedCountryClone.offsetWidth;
3220
- body.removeChild(containerClone);
3221
- return width;
3222
- }
3223
- return 0;
3224
- }
3225
3464
  //* Update the input placeholder to an example number from the currently selected country.
3226
3465
  _updatePlaceholder() {
3227
3466
  const { autoPlaceholder, placeholderNumberType, nationalMode, customPlaceholder } = this.options;
3228
- const shouldSetPlaceholder = autoPlaceholder === "aggressive" || !this.hadInitialPlaceholder && autoPlaceholder === "polite";
3467
+ const shouldSetPlaceholder = autoPlaceholder === "aggressive" || !this.ui.hadInitialPlaceholder && autoPlaceholder === "polite";
3229
3468
  if (intlTelInput.utils && shouldSetPlaceholder) {
3230
3469
  const numberType = intlTelInput.utils.numberType[placeholderNumberType];
3231
3470
  let placeholder = this.selectedCountryData.iso2 ? intlTelInput.utils.getExampleNumber(this.selectedCountryData.iso2, nationalMode, numberType) : "";
@@ -3233,92 +3472,66 @@ var Iti = class _Iti {
3233
3472
  if (typeof customPlaceholder === "function") {
3234
3473
  placeholder = customPlaceholder(placeholder, this.selectedCountryData);
3235
3474
  }
3236
- this.telInput.setAttribute("placeholder", placeholder);
3475
+ this.ui.telInput.setAttribute("placeholder", placeholder);
3237
3476
  }
3238
3477
  }
3239
3478
  //* Called when the user selects a list item from the dropdown.
3240
3479
  _selectListItem(listItem) {
3241
- const iso2 = listItem.getAttribute("data-country-code");
3480
+ const iso2 = listItem.dataset[DATA_KEYS.COUNTRY_CODE];
3242
3481
  const countryChanged = this._setCountry(iso2);
3243
3482
  this._closeDropdown();
3244
- const dialCode = listItem.getAttribute("data-dial-code");
3483
+ const dialCode = listItem.dataset[DATA_KEYS.DIAL_CODE];
3245
3484
  this._updateDialCode(dialCode);
3246
3485
  if (this.options.formatOnDisplay) {
3247
- this._updateValFromNumber(this.telInput.value);
3486
+ this._updateValFromNumber(this.ui.telInput.value);
3248
3487
  }
3249
- this.telInput.focus();
3488
+ this.ui.telInput.focus();
3250
3489
  if (countryChanged) {
3251
3490
  this._triggerCountryChange();
3252
3491
  }
3253
3492
  }
3254
3493
  //* Close the dropdown and unbind any listeners.
3255
3494
  _closeDropdown() {
3256
- this.dropdownContent.classList.add("iti__hide");
3257
- this.selectedCountry.setAttribute("aria-expanded", "false");
3258
- if (this.highlightedItem) {
3259
- this.highlightedItem.setAttribute("aria-selected", "false");
3495
+ if (this.ui.dropdownContent.classList.contains(CLASSES.HIDE)) {
3496
+ return;
3260
3497
  }
3261
- if (this.options.countrySearch) {
3262
- this.searchInput.removeAttribute("aria-activedescendant");
3498
+ this.ui.dropdownContent.classList.add(CLASSES.HIDE);
3499
+ this.ui.selectedCountry.setAttribute(ARIA.EXPANDED, "false");
3500
+ if (this.ui.highlightedItem) {
3501
+ this.ui.highlightedItem.setAttribute(ARIA.SELECTED, "false");
3263
3502
  }
3264
- this.dropdownArrow.classList.remove("iti__arrow--up");
3265
3503
  if (this.options.countrySearch) {
3266
- this.searchInput.removeEventListener("input", this._handleSearchChange);
3267
- this.searchClearButton.removeEventListener("click", this._handleSearchClear);
3504
+ this.ui.searchInput.removeAttribute(ARIA.ACTIVE_DESCENDANT);
3268
3505
  }
3269
- document.removeEventListener("keydown", this._handleKeydownOnDropdown);
3270
- document.documentElement.removeEventListener("click", this._handleClickOffToClose);
3271
- this.countryList.removeEventListener("mouseover", this._handleMouseoverCountryList);
3272
- this.countryList.removeEventListener("click", this._handleClickCountryList);
3506
+ this.ui.dropdownArrow.classList.remove(CLASSES.ARROW_UP);
3507
+ this.dropdownAbortController.abort();
3508
+ this.dropdownAbortController = null;
3273
3509
  if (this.options.dropdownContainer) {
3274
- if (!this.options.useFullscreenPopup) {
3275
- window.removeEventListener("scroll", this._handleWindowScroll);
3276
- }
3277
- if (this.dropdown.parentNode) {
3278
- this.dropdown.parentNode.removeChild(this.dropdown);
3279
- }
3280
- }
3281
- this._trigger("close:countrydropdown");
3282
- }
3283
- //* Check if an element is visible within it's container, else scroll until it is.
3284
- _scrollTo(element) {
3285
- const container = this.countryList;
3286
- const scrollTop = document.documentElement.scrollTop;
3287
- const containerHeight = container.offsetHeight;
3288
- const containerTop = container.getBoundingClientRect().top + scrollTop;
3289
- const containerBottom = containerTop + containerHeight;
3290
- const elementHeight = element.offsetHeight;
3291
- const elementTop = element.getBoundingClientRect().top + scrollTop;
3292
- const elementBottom = elementTop + elementHeight;
3293
- const newScrollTop = elementTop - containerTop + container.scrollTop;
3294
- if (elementTop < containerTop) {
3295
- container.scrollTop = newScrollTop;
3296
- } else if (elementBottom > containerBottom) {
3297
- const heightDifference = containerHeight - elementHeight;
3298
- container.scrollTop = newScrollTop - heightDifference;
3510
+ this.ui.dropdown.remove();
3299
3511
  }
3512
+ this._trigger(EVENTS.CLOSE_COUNTRY_DROPDOWN);
3300
3513
  }
3301
3514
  //* Replace any existing dial code with the new one
3302
3515
  //* Note: called from _selectListItem and setCountry
3303
3516
  _updateDialCode(newDialCodeBare) {
3304
- const inputVal = this.telInput.value;
3517
+ const inputVal = this.ui.telInput.value;
3305
3518
  const newDialCode = `+${newDialCodeBare}`;
3306
3519
  let newNumber;
3307
- if (inputVal.charAt(0) === "+") {
3520
+ if (inputVal.startsWith("+")) {
3308
3521
  const prevDialCode = this._getDialCode(inputVal);
3309
3522
  if (prevDialCode) {
3310
3523
  newNumber = inputVal.replace(prevDialCode, newDialCode);
3311
3524
  } else {
3312
3525
  newNumber = newDialCode;
3313
3526
  }
3314
- this.telInput.value = newNumber;
3527
+ this.ui.telInput.value = newNumber;
3315
3528
  }
3316
3529
  }
3317
3530
  //* Try and extract a valid international dial code from a full telephone number.
3318
3531
  //* Note: returns the raw string inc plus character and any whitespace/dots etc.
3319
3532
  _getDialCode(number, includeAreaCode) {
3320
3533
  let dialCode = "";
3321
- if (number.charAt(0) === "+") {
3534
+ if (number.startsWith("+")) {
3322
3535
  let numericChars = "";
3323
3536
  for (let i = 0; i < number.length; i++) {
3324
3537
  const c = number.charAt(i);
@@ -3344,11 +3557,11 @@ var Iti = class _Iti {
3344
3557
  }
3345
3558
  //* Get the input val, adding the dial code if separateDialCode is enabled.
3346
3559
  _getFullNumber(overrideVal) {
3347
- const val = overrideVal || this.telInput.value.trim();
3560
+ const val = overrideVal || this.ui.telInput.value.trim();
3348
3561
  const { dialCode } = this.selectedCountryData;
3349
3562
  let prefix;
3350
3563
  const numericVal = getNumeric(val);
3351
- if (this.options.separateDialCode && val.charAt(0) !== "+" && dialCode && numericVal) {
3564
+ if (this.options.separateDialCode && !val.startsWith("+") && dialCode && numericVal) {
3352
3565
  prefix = `+${dialCode}`;
3353
3566
  } else {
3354
3567
  prefix = "";
@@ -3363,16 +3576,16 @@ var Iti = class _Iti {
3363
3576
  }
3364
3577
  //* Trigger the 'countrychange' event.
3365
3578
  _triggerCountryChange() {
3366
- this._trigger("countrychange");
3579
+ this._trigger(EVENTS.COUNTRY_CHANGE);
3367
3580
  }
3368
3581
  //**************************
3369
3582
  //* SECRET PUBLIC METHODS
3370
3583
  //**************************
3371
3584
  //* This is called when the geoip call returns.
3372
3585
  handleAutoCountry() {
3373
- if (this.options.initialCountry === "auto" && intlTelInput.autoCountry) {
3586
+ if (this.options.initialCountry === INITIAL_COUNTRY.AUTO && intlTelInput.autoCountry) {
3374
3587
  this.defaultCountry = intlTelInput.autoCountry;
3375
- const hasSelectedCountryOrGlobe = this.selectedCountryData.iso2 || this.selectedCountryInner.classList.contains("iti__globe");
3588
+ const hasSelectedCountryOrGlobe = this.selectedCountryData.iso2 || this.ui.selectedCountryInner.classList.contains(CLASSES.GLOBE);
3376
3589
  if (!hasSelectedCountryOrGlobe) {
3377
3590
  this.setCountry(this.defaultCountry);
3378
3591
  }
@@ -3382,8 +3595,8 @@ var Iti = class _Iti {
3382
3595
  //* This is called when the utils request completes.
3383
3596
  handleUtils() {
3384
3597
  if (intlTelInput.utils) {
3385
- if (this.telInput.value) {
3386
- this._updateValFromNumber(this.telInput.value);
3598
+ if (this.ui.telInput.value) {
3599
+ this._updateValFromNumber(this.ui.telInput.value);
3387
3600
  }
3388
3601
  if (this.selectedCountryData.iso2) {
3389
3602
  this._updatePlaceholder();
@@ -3397,40 +3610,20 @@ var Iti = class _Iti {
3397
3610
  //********************
3398
3611
  //* Remove plugin.
3399
3612
  destroy() {
3400
- var _a, _b;
3401
- this.telInput.iti = void 0;
3402
- const { allowDropdown, separateDialCode } = this.options;
3403
- if (allowDropdown) {
3404
- this._closeDropdown();
3405
- this.selectedCountry.removeEventListener("click", this._handleClickSelectedCountry);
3406
- this.countryContainer.removeEventListener("keydown", this._handleCountryContainerKeydown);
3407
- const label = this.telInput.closest("label");
3408
- if (label) {
3409
- label.removeEventListener("click", this._handleLabelClick);
3410
- }
3411
- }
3412
- const { form } = this.telInput;
3413
- if (this._handleHiddenInputSubmit && form) {
3414
- form.removeEventListener("submit", this._handleHiddenInputSubmit);
3415
- }
3416
- this.telInput.removeEventListener("input", this._handleInputEvent);
3417
- if (this._handleKeydownEvent) {
3418
- this.telInput.removeEventListener("keydown", this._handleKeydownEvent);
3419
- }
3420
- if (this._handlePasteEvent) {
3421
- this.telInput.removeEventListener("paste", this._handlePasteEvent);
3613
+ if (!this.ui.telInput) {
3614
+ return;
3422
3615
  }
3423
- if (this._handlePageLoad) {
3424
- window.removeEventListener("load", this._handlePageLoad);
3616
+ if (this.options.allowDropdown) {
3617
+ this._closeDropdown();
3425
3618
  }
3426
- this.telInput.removeAttribute("data-intl-tel-input-id");
3427
- if (separateDialCode) {
3428
- this.telInput.style.paddingLeft = this.originalPaddingLeft;
3619
+ this.abortController.abort();
3620
+ this.abortController = null;
3621
+ this.ui.destroy();
3622
+ if (intlTelInput.instances instanceof Map) {
3623
+ intlTelInput.instances.delete(this.id);
3624
+ } else {
3625
+ delete intlTelInput.instances[this.id];
3429
3626
  }
3430
- const wrapper = this.telInput.parentNode;
3431
- (_a = wrapper === null || wrapper === void 0 ? void 0 : wrapper.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(this.telInput, wrapper);
3432
- (_b = wrapper === null || wrapper === void 0 ? void 0 : wrapper.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(wrapper);
3433
- delete intlTelInput.instances[this.id];
3434
3627
  }
3435
3628
  //* Get the extension from the current number.
3436
3629
  getExtension() {
@@ -3452,7 +3645,7 @@ var Iti = class _Iti {
3452
3645
  if (intlTelInput.utils) {
3453
3646
  return intlTelInput.utils.getNumberType(this._getFullNumber(), this.selectedCountryData.iso2);
3454
3647
  }
3455
- return -99;
3648
+ return SENTINELS.UNKNOWN_NUMBER_TYPE;
3456
3649
  }
3457
3650
  //* Get the country data for the currently selected country.
3458
3651
  getSelectedCountryData() {
@@ -3464,15 +3657,15 @@ var Iti = class _Iti {
3464
3657
  const { iso2 } = this.selectedCountryData;
3465
3658
  return intlTelInput.utils.getValidationError(this._getFullNumber(), iso2);
3466
3659
  }
3467
- return -99;
3660
+ return SENTINELS.UNKNOWN_VALIDATION_ERROR;
3468
3661
  }
3469
3662
  //* Validate the input val using number length only
3470
3663
  isValidNumber() {
3471
3664
  const { dialCode, iso2 } = this.selectedCountryData;
3472
- if (dialCode === "44" && intlTelInput.utils) {
3665
+ if (dialCode === UK.DIAL_CODE && intlTelInput.utils) {
3473
3666
  const number = this._getFullNumber();
3474
3667
  const coreNumber = intlTelInput.utils.getCoreNumber(number, iso2);
3475
- if (coreNumber[0] === "7" && coreNumber.length !== 10) {
3668
+ if (coreNumber[0] === UK.MOBILE_PREFIX && coreNumber.length !== UK.MOBILE_CORE_LENGTH) {
3476
3669
  return false;
3477
3670
  }
3478
3671
  }
@@ -3495,7 +3688,7 @@ var Iti = class _Iti {
3495
3688
  }
3496
3689
  const testValidity = (s) => precise ? this._utilsIsValidNumber(s) : this._utilsIsPossibleNumber(s);
3497
3690
  const val = this._getFullNumber();
3498
- const alphaCharPosition = val.search(/\p{L}/u);
3691
+ const alphaCharPosition = val.search(REGEX.ALPHA_UNICODE);
3499
3692
  const hasAlphaChar = alphaCharPosition > -1;
3500
3693
  if (hasAlphaChar && !this.options.allowPhonewords) {
3501
3694
  const beforeAlphaChar = val.substring(0, alphaCharPosition);
@@ -3520,7 +3713,7 @@ var Iti = class _Iti {
3520
3713
  this._setCountry(iso2Lower);
3521
3714
  this._updateDialCode(this.selectedCountryData.dialCode);
3522
3715
  if (this.options.formatOnDisplay) {
3523
- this._updateValFromNumber(this.telInput.value);
3716
+ this._updateValFromNumber(this.ui.telInput.value);
3524
3717
  }
3525
3718
  this._triggerCountryChange();
3526
3719
  }
@@ -3532,7 +3725,7 @@ var Iti = class _Iti {
3532
3725
  if (countryChanged) {
3533
3726
  this._triggerCountryChange();
3534
3727
  }
3535
- this._trigger("input", { isSetNumber: true });
3728
+ this._trigger(EVENTS.INPUT, { isSetNumber: true });
3536
3729
  }
3537
3730
  //* Set the placeholder number typ
3538
3731
  setPlaceholderNumberType(type) {
@@ -3540,11 +3733,11 @@ var Iti = class _Iti {
3540
3733
  this._updatePlaceholder();
3541
3734
  }
3542
3735
  setDisabled(disabled) {
3543
- this.telInput.disabled = disabled;
3736
+ this.ui.telInput.disabled = disabled;
3544
3737
  if (disabled) {
3545
- this.selectedCountry.setAttribute("disabled", "true");
3738
+ this.ui.selectedCountry.setAttribute("disabled", "true");
3546
3739
  } else {
3547
- this.selectedCountry.removeAttribute("disabled");
3740
+ this.ui.selectedCountry.removeAttribute("disabled");
3548
3741
  }
3549
3742
  }
3550
3743
  };
@@ -3576,10 +3769,16 @@ var attachUtils = (source) => {
3576
3769
  }
3577
3770
  return null;
3578
3771
  };
3772
+ var forEachInstance = (method, ...args) => {
3773
+ Object.values(intlTelInput.instances).forEach((instance) => {
3774
+ const fn = instance[method];
3775
+ if (typeof fn === "function") {
3776
+ fn.apply(instance, args);
3777
+ }
3778
+ });
3779
+ };
3579
3780
  var intlTelInput = Object.assign((input, options) => {
3580
3781
  const iti = new Iti(input, options);
3581
- iti._init();
3582
- input.setAttribute("data-intl-tel-input-id", iti.id.toString());
3583
3782
  intlTelInput.instances[iti.id] = iti;
3584
3783
  input.iti = iti;
3585
3784
  return iti;
@@ -3591,7 +3790,7 @@ var intlTelInput = Object.assign((input, options) => {
3591
3790
  getCountryData: () => data_default,
3592
3791
  //* A getter for the plugin instance.
3593
3792
  getInstance: (input) => {
3594
- const id2 = input.getAttribute("data-intl-tel-input-id");
3793
+ const id2 = input.dataset.intlTelInputId;
3595
3794
  return id2 ? intlTelInput.instances[id2] : null;
3596
3795
  },
3597
3796
  //* A map from instance ID to instance object.
@@ -3599,7 +3798,7 @@ var intlTelInput = Object.assign((input, options) => {
3599
3798
  attachUtils,
3600
3799
  startedLoadingUtilsScript: false,
3601
3800
  startedLoadingAutoCountry: false,
3602
- version: "25.10.12"
3801
+ version: "25.11.1"
3603
3802
  });
3604
3803
  var intl_tel_input_default = intlTelInput;
3605
3804