// packages/core/src/js/data.ts
var rawCountryData = [
[
"af",
// Afghanistan
"93",
0,
null,
"0"
],
[
"ax",
// Åland Islands (AKA Aland Islands)
"358",
1,
["18", "4"],
// (4 is a mobile range shared with FI)
// NOTE: https://en.wikipedia.org/wiki/Telephone%20numbers%20in%20%C3%85land says some 4XXX ranges (e.g. 4570) are specific to AX, but LPN doesn't respect this (https://libphonenumber.appspot.com/phonenumberparser?number=%2B3584570123456 says region=FI) so we won't either. Also it's too much of a maintenance burden to keep track of. Keep the 4 area code range here so that if the user selects AX and types this kind of number, we wont change the flag to FI. Whereas if they type a FI-only range then we will.
"0"
],
[
"al",
// Albania
"355",
0,
null,
"0"
],
[
"dz",
// Algeria
"213",
0,
null,
"0"
],
[
"as",
// American Samoa
"1",
5,
["684"],
"1"
],
[
"ad",
// Andorra
"376"
],
[
"ao",
// Angola
"244"
],
[
"ai",
// Anguilla
"1",
6,
["264"],
"1"
],
[
"ag",
// Antigua and Barbuda
"1",
7,
["268"],
"1"
],
[
"ar",
// Argentina
"54",
0,
null,
"0"
],
[
"am",
// Armenia
"374",
0,
null,
"0"
],
[
"aw",
// Aruba
"297"
],
[
"ac",
// Ascension Island
"247"
],
[
"au",
// Australia
"61",
0,
["4"],
// (mobile range shared with CX and CC)
"0"
],
[
"at",
// Austria
"43",
0,
null,
"0"
],
[
"az",
// Azerbaijan
"994",
0,
null,
"0"
],
[
"bs",
// Bahamas
"1",
8,
["242"],
"1"
],
[
"bh",
// Bahrain
"973"
],
[
"bd",
// Bangladesh
"880",
0,
null,
"0"
],
[
"bb",
// Barbados
"1",
9,
["246"],
"1"
],
[
"by",
// Belarus
"375",
0,
null,
"8"
],
[
"be",
// Belgium
"32",
0,
null,
"0"
],
[
"bz",
// Belize
"501"
],
[
"bj",
// Benin
"229"
],
[
"bm",
// Bermuda
"1",
10,
["441"],
"1"
],
[
"bt",
// Bhutan
"975"
],
[
"bo",
// Bolivia
"591",
0,
null,
"0"
],
[
"ba",
// Bosnia and Herzegovina
"387",
0,
null,
"0"
],
[
"bw",
// Botswana
"267"
],
[
"br",
// Brazil
"55",
0,
null,
"0"
],
[
"io",
// British Indian Ocean Territory
"246"
],
[
"vg",
// British Virgin Islands
"1",
11,
["284"],
"1"
],
[
"bn",
// Brunei
"673"
],
[
"bg",
// Bulgaria
"359",
0,
null,
"0"
],
[
"bf",
// Burkina Faso
"226"
],
[
"bi",
// Burundi
"257"
],
[
"kh",
// Cambodia
"855",
0,
null,
"0"
],
[
"cm",
// Cameroon
"237"
],
[
"ca",
// Canada
"1",
1,
[
"204",
"226",
"236",
"249",
"250",
"257",
"263",
"289",
"306",
"343",
"354",
"365",
"367",
"368",
"382",
"403",
"416",
"418",
"428",
"431",
"437",
"438",
"450",
"468",
"474",
"506",
"514",
"519",
"548",
"579",
"581",
"584",
"587",
"604",
"613",
"639",
"647",
"672",
"683",
"705",
"709",
"742",
"753",
"778",
"780",
"782",
"807",
"819",
"825",
"867",
"873",
"879",
"902",
"905",
"942"
],
"1"
],
[
"cv",
// Cape Verde
"238"
],
[
"bq",
// Caribbean Netherlands
"599",
1,
["3", "4", "7"]
],
[
"ky",
// Cayman Islands
"1",
12,
["345"],
"1"
],
[
"cf",
// Central African Republic
"236"
],
[
"td",
// Chad
"235"
],
[
"cl",
// Chile
"56"
],
[
"cn",
// China
"86",
0,
null,
"0"
],
[
"cx",
// Christmas Island
"61",
2,
["4", "89164"],
// (4 is a mobile range shared with AU and CC)
"0"
],
[
"cc",
// Cocos (Keeling) Islands
"61",
1,
["4", "89162"],
// (4 is a mobile range shared with AU and CX)
"0"
],
[
"co",
// Colombia
"57",
0,
null,
"0"
],
[
"km",
// Comoros
"269"
],
[
"cg",
// Congo (Brazzaville)
"242"
],
[
"cd",
// Congo (Kinshasa)
"243",
0,
null,
"0"
],
[
"ck",
// Cook Islands
"682"
],
[
"cr",
// Costa Rica
"506"
],
[
"ci",
// Côte d'Ivoire
"225"
],
[
"hr",
// Croatia
"385",
0,
null,
"0"
],
[
"cu",
// Cuba
"53",
0,
null,
"0"
],
[
"cw",
// Curaçao
"599",
0
],
[
"cy",
// Cyprus
"357"
],
[
"cz",
// Czech Republic
"420"
],
[
"dk",
// Denmark
"45"
],
[
"dj",
// Djibouti
"253"
],
[
"dm",
// Dominica
"1",
13,
["767"],
"1"
],
[
"do",
// Dominican Republic
"1",
2,
["809", "829", "849"],
"1"
],
[
"ec",
// Ecuador
"593",
0,
null,
"0"
],
[
"eg",
// Egypt
"20",
0,
null,
"0"
],
[
"sv",
// El Salvador
"503"
],
[
"gq",
// Equatorial Guinea
"240"
],
[
"er",
// Eritrea
"291",
0,
null,
"0"
],
[
"ee",
// Estonia
"372"
],
[
"sz",
// Eswatini
"268"
],
[
"et",
// Ethiopia
"251",
0,
null,
"0"
],
[
"fk",
// Falkland Islands (Malvinas)
"500"
],
[
"fo",
// Faroe Islands
"298"
],
[
"fj",
// Fiji
"679"
],
[
"fi",
// Finland
"358",
0,
["4"],
// (mobile range shared with AX)
"0"
],
[
"fr",
// France
"33",
0,
null,
"0"
],
[
"gf",
// French Guiana
"594",
0,
null,
"0"
],
[
"pf",
// French Polynesia
"689"
],
[
"ga",
// Gabon
"241"
],
[
"gm",
// Gambia
"220"
],
[
"ge",
// Georgia
"995",
0,
null,
"0"
],
[
"de",
// Germany
"49",
0,
null,
"0"
],
[
"gh",
// Ghana
"233",
0,
null,
"0"
],
[
"gi",
// Gibraltar
"350"
],
[
"gr",
// Greece
"30"
],
[
"gl",
// Greenland
"299"
],
[
"gd",
// Grenada
"1",
14,
["473"],
"1"
],
[
"gp",
// Guadeloupe
"590",
0,
null,
"0"
],
[
"gu",
// Guam
"1",
15,
["671"],
"1"
],
[
"gt",
// Guatemala
"502"
],
[
"gg",
// Guernsey
"44",
1,
["1481", "7781", "7839", "7911"],
"0"
],
[
"gn",
// Guinea
"224"
],
[
"gw",
// Guinea-Bissau
"245"
],
[
"gy",
// Guyana
"592"
],
[
"ht",
// Haiti
"509"
],
[
"hn",
// Honduras
"504"
],
[
"hk",
// Hong Kong SAR China
"852"
],
[
"hu",
// Hungary
"36",
0,
null,
"06"
],
[
"is",
// Iceland
"354"
],
[
"in",
// India
"91",
0,
null,
"0"
],
[
"id",
// Indonesia
"62",
0,
null,
"0"
],
[
"ir",
// Iran
"98",
0,
null,
"0"
],
[
"iq",
// Iraq
"964",
0,
null,
"0"
],
[
"ie",
// Ireland
"353",
0,
null,
"0"
],
[
"im",
// Isle of Man
"44",
2,
["1624", "74576", "7524", "7624", "7924"],
"0"
],
[
"il",
// Israel
"972",
0,
null,
"0"
],
[
"it",
// Italy
"39",
0,
["3"]
// (mobile range shared with VA)
],
[
"jm",
// Jamaica
"1",
4,
["658", "876"],
"1"
],
[
"jp",
// Japan
"81",
0,
null,
"0"
],
[
"je",
// Jersey
"44",
3,
["1534", "7509", "7700", "7797", "7829", "7937"],
"0"
],
[
"jo",
// Jordan
"962",
0,
null,
"0"
],
[
"kz",
// Kazakhstan
"7",
1,
["33", "7"],
// (33 is shared with RU)
"8"
],
[
"ke",
// Kenya
"254",
0,
null,
"0"
],
[
"ki",
// Kiribati
"686",
0,
null,
"0"
],
[
"xk",
// Kosovo
"383",
0,
null,
"0"
],
[
"kw",
// Kuwait
"965"
],
[
"kg",
// Kyrgyzstan
"996",
0,
null,
"0"
],
[
"la",
// Laos
"856",
0,
null,
"0"
],
[
"lv",
// Latvia
"371"
],
[
"lb",
// Lebanon
"961",
0,
null,
"0"
],
[
"ls",
// Lesotho
"266"
],
[
"lr",
// Liberia
"231",
0,
null,
"0"
],
[
"ly",
// Libya
"218",
0,
null,
"0"
],
[
"li",
// Liechtenstein
"423",
0,
null,
"0"
],
[
"lt",
// Lithuania
"370",
0,
null,
"0"
],
[
"lu",
// Luxembourg
"352"
],
[
"mo",
// Macao SAR China
"853"
],
[
"mg",
// Madagascar
"261",
0,
null,
"0"
],
[
"mw",
// Malawi
"265",
0,
null,
"0"
],
[
"my",
// Malaysia
"60",
0,
null,
"0"
],
[
"mv",
// Maldives
"960"
],
[
"ml",
// Mali
"223"
],
[
"mt",
// Malta
"356"
],
[
"mh",
// Marshall Islands
"692",
0,
null,
"1"
],
[
"mq",
// Martinique
"596",
0,
null,
"0"
],
[
"mr",
// Mauritania
"222"
],
[
"mu",
// Mauritius
"230"
],
[
"yt",
// Mayotte
"262",
1,
["269", "639"],
"0"
],
[
"mx",
// Mexico
"52"
],
[
"fm",
// Micronesia
"691"
],
[
"md",
// Moldova
"373",
0,
null,
"0"
],
[
"mc",
// Monaco
"377",
0,
null,
"0"
],
[
"mn",
// Mongolia
"976",
0,
null,
"0"
],
[
"me",
// Montenegro
"382",
0,
null,
"0"
],
[
"ms",
// Montserrat
"1",
16,
["664"],
"1"
],
[
"ma",
// Morocco
"212",
0,
["6", "7"],
// (mobile ranges shared with EH)
"0"
],
[
"mz",
// Mozambique
"258"
],
[
"mm",
// Myanmar (Burma)
"95",
0,
null,
"0"
],
[
"na",
// Namibia
"264",
0,
null,
"0"
],
[
"nr",
// Nauru
"674"
],
[
"np",
// Nepal
"977",
0,
null,
"0"
],
[
"nl",
// Netherlands
"31",
0,
null,
"0"
],
[
"nc",
// New Caledonia
"687"
],
[
"nz",
// New Zealand
"64",
0,
null,
"0"
],
[
"ni",
// Nicaragua
"505"
],
[
"ne",
// Niger
"227"
],
[
"ng",
// Nigeria
"234",
0,
null,
"0"
],
[
"nu",
// Niue
"683"
],
[
"nf",
// Norfolk Island
"672"
],
[
"kp",
// North Korea
"850",
0,
null,
"0"
],
[
"mk",
// North Macedonia
"389",
0,
null,
"0"
],
[
"mp",
// Northern Mariana Islands
"1",
17,
["670"],
"1"
],
[
"no",
// Norway
"47",
0,
["4", "9"]
// (mobile ranges shared with SJ)
],
[
"om",
// Oman
"968"
],
[
"pk",
// Pakistan
"92",
0,
null,
"0"
],
[
"pw",
// Palau
"680"
],
[
"ps",
// Palestinian Territories
"970",
0,
null,
"0"
],
[
"pa",
// Panama
"507"
],
[
"pg",
// Papua New Guinea
"675"
],
[
"py",
// Paraguay
"595",
0,
null,
"0"
],
[
"pe",
// Peru
"51",
0,
null,
"0"
],
[
"ph",
// Philippines
"63",
0,
null,
"0"
],
[
"pl",
// Poland
"48"
],
[
"pt",
// Portugal
"351"
],
[
"pr",
// Puerto Rico
"1",
3,
["787", "939"],
"1"
],
[
"qa",
// Qatar
"974"
],
[
"re",
// Réunion
"262",
0,
null,
"0"
],
[
"ro",
// Romania
"40",
0,
null,
"0"
],
[
"ru",
// Russia
"7",
0,
["33"],
// (shared with KZ)
"8"
],
[
"rw",
// Rwanda
"250",
0,
null,
"0"
],
[
"ws",
// Samoa
"685"
],
[
"sm",
// San Marino
"378"
],
[
"st",
// São Tomé & Príncipe
"239"
],
[
"sa",
// Saudi Arabia
"966",
0,
null,
"0"
],
[
"sn",
// Senegal
"221"
],
[
"rs",
// Serbia
"381",
0,
null,
"0"
],
[
"sc",
// Seychelles
"248"
],
[
"sl",
// Sierra Leone
"232",
0,
null,
"0"
],
[
"sg",
// Singapore
"65"
],
[
"sx",
// Sint Maarten
"1",
21,
["721"],
"1"
],
[
"sk",
// Slovakia
"421",
0,
null,
"0"
],
[
"si",
// Slovenia
"386",
0,
null,
"0"
],
[
"sb",
// Solomon Islands
"677"
],
[
"so",
// Somalia
"252",
0,
null,
"0"
],
[
"za",
// South Africa
"27",
0,
null,
"0"
],
[
"kr",
// South Korea
"82",
0,
null,
"0"
],
[
"ss",
// South Sudan
"211",
0,
null,
"0"
],
[
"es",
// Spain
"34"
],
[
"lk",
// Sri Lanka
"94",
0,
null,
"0"
],
[
"bl",
// St. Barthélemy
"590",
1,
null,
"0"
],
[
"sh",
// St. Helena
"290"
],
[
"kn",
// St. Kitts & Nevis
"1",
18,
["869"],
"1"
],
[
"lc",
// St. Lucia
"1",
19,
["758"],
"1"
],
[
"mf",
// St. Martin
"590",
2,
null,
"0"
],
[
"pm",
// St. Pierre & Miquelon
"508",
0,
null,
"0"
],
[
"vc",
// St. Vincent & Grenadines
"1",
20,
["784"],
"1"
],
[
"sd",
// Sudan
"249",
0,
null,
"0"
],
[
"sr",
// Suriname
"597"
],
[
"sj",
// Svalbard & Jan Mayen
"47",
1,
["4", "79", "9"]
// (4 and 9 are mobile ranges shared with NO)
],
[
"se",
// Sweden
"46",
0,
null,
"0"
],
[
"ch",
// Switzerland
"41",
0,
null,
"0"
],
[
"sy",
// Syria
"963",
0,
null,
"0"
],
[
"tw",
// Taiwan
"886",
0,
null,
"0"
],
[
"tj",
// Tajikistan
"992"
],
[
"tz",
// Tanzania
"255",
0,
null,
"0"
],
[
"th",
// Thailand
"66",
0,
null,
"0"
],
[
"tl",
// Timor-Leste
"670"
],
[
"tg",
// Togo
"228"
],
[
"tk",
// Tokelau
"690"
],
[
"to",
// Tonga
"676"
],
[
"tt",
// Trinidad & Tobago
"1",
22,
["868"],
"1"
],
[
"tn",
// Tunisia
"216"
],
[
"tr",
// Turkey
"90",
0,
null,
"0"
],
[
"tm",
// Turkmenistan
"993",
0,
null,
"8"
],
[
"tc",
// Turks & Caicos Islands
"1",
23,
["649"],
"1"
],
[
"tv",
// Tuvalu
"688"
],
[
"vi",
// U.S. Virgin Islands
"1",
24,
["340"],
"1"
],
[
"ug",
// Uganda
"256",
0,
null,
"0"
],
[
"ua",
// Ukraine
"380",
0,
null,
"0"
],
[
"ae",
// United Arab Emirates
"971",
0,
null,
"0"
],
[
"gb",
// United Kingdom
"44",
0,
null,
"0"
],
[
"us",
// United States
"1",
0,
null,
"1"
],
[
"uy",
// Uruguay
"598",
0,
null,
"0"
],
[
"uz",
// Uzbekistan
"998"
],
[
"vu",
// Vanuatu
"678"
],
[
"va",
// Vatican City
"39",
1,
["06698", "3"]
// (3 is a mobile range shared with IT)
],
[
"ve",
// Venezuela
"58",
0,
null,
"0"
],
[
"vn",
// Vietnam
"84",
0,
null,
"0"
],
[
"wf",
// Wallis & Futuna
"681"
],
[
"eh",
// Western Sahara
"212",
1,
["5288", "5289", "6", "7"],
// (6 and 7 are mobile ranges shared with MA)
"0"
],
[
"ye",
// Yemen
"967",
0,
null,
"0"
],
[
"zm",
// Zambia
"260",
0,
null,
"0"
],
[
"zw",
// Zimbabwe
"263",
0,
null,
"0"
]
];
var allCountries = [];
for (const c of rawCountryData) {
allCountries.push({
name: "",
// populated in the core library
iso2: c[0],
dialCode: c[1],
priority: c[2] || 0,
areaCodes: c[3] || null,
nationalPrefix: c[4] || null
});
}
var iso2Set = new Set(allCountries.map((c) => c.iso2));
var isIso2 = (val) => iso2Set.has(val);
var data_default = allCountries;
// packages/core/src/js/constants.ts
var EVENTS = {
OPEN_COUNTRY_DROPDOWN: "open:countrydropdown",
CLOSE_COUNTRY_DROPDOWN: "close:countrydropdown",
COUNTRY_CHANGE: "countrychange",
INPUT: "input",
// used for synthetic input trigger
STRICT_REJECT: "strict:reject"
};
var CLASSES = {
HIDE: "iti__hide",
V_HIDE: "iti__v-hide",
ARROW_UP: "iti__arrow--up",
GLOBE: "iti__globe",
FLAG: "iti__flag",
LOADING: "iti__loading",
COUNTRY_ITEM: "iti__country",
HIGHLIGHT: "iti__highlight",
STRICT_REJECT_ANIMATION: "iti__strict-reject-animation"
};
var KEYS = {
ARROW_UP: "ArrowUp",
ARROW_DOWN: "ArrowDown",
SPACE: " ",
ENTER: "Enter",
ESC: "Escape",
TAB: "Tab"
};
var INPUT_TYPES = {
PASTE: "insertFromPaste",
DELETE_FORWARD: "deleteContentForward"
};
var REGEX = {
ALPHA_UNICODE: /\p{L}/u,
// any kind of letter from any language
NON_PLUS_NUMERIC: /[^+0-9]/,
// chars that are NOT + or digit
NON_PLUS_NUMERIC_GLOBAL: /[^+0-9]/g,
// chars that are NOT + or digit (global)
HIDDEN_SEARCH_CHAR: /^[a-zA-ZÀ-ÿа-яА-Я ]$/
// single acceptable hidden-search char
};
var TIMINGS = {
SEARCH_DEBOUNCE_MS: 100,
HIDDEN_SEARCH_RESET_MS: 1e3,
NEXT_TICK: 0
};
var LAYOUT = {
NARROW_VIEWPORT_WIDTH: 500,
// keep in sync with .iti__country-list CSS media query
FALLBACK_SELECTED_WITH_DIAL_WIDTH: 78,
// px width fallback when separateDialCode enabled
FALLBACK_SELECTED_NO_DIAL_WIDTH: 42,
// px width fallback when no separate dial code
INPUT_PADDING_EXTRA_LEFT: 6,
// px gap between selected country container and input text
DROPDOWN_MARGIN: 3,
// px margin between dropdown and tel input
FALLBACK_DROPDOWN_HEIGHT: 200
// px height fallback for dropdown
};
var DIAL_CODE = {
PLUS: "+",
NANP: "1"
// North American Numbering Plan
};
var UK = {
ISO2: "gb",
DIAL_CODE: "44",
// +44 United Kingdom
MOBILE_PREFIX: "7",
// UK mobile numbers start with 7 after national trunk (0) or core section
MOBILE_CORE_LENGTH: 10
// core number length (excluding dial code / national prefix) for mobiles
};
var US = {
ISO2: "us",
DIAL_CODE: "1"
// +1 United States
};
var PLACEHOLDER_MODES = {
AGGRESSIVE: "aggressive",
POLITE: "polite",
OFF: "off"
};
var INITIAL_COUNTRY = {
AUTO: "auto"
};
var NUMBER_FORMATS = [
"E164",
"INTERNATIONAL",
"NATIONAL",
"RFC3966"
];
var NUMBER_TYPES = [
"FIXED_LINE",
"MOBILE",
"FIXED_LINE_OR_MOBILE",
"TOLL_FREE",
"PREMIUM_RATE",
"SHARED_COST",
"VOIP",
"PERSONAL_NUMBER",
"PAGER",
"UAN",
"VOICEMAIL",
"UNKNOWN"
];
var VALIDATION_ERRORS = [
"IS_POSSIBLE",
"INVALID_COUNTRY_CODE",
"TOO_SHORT",
"TOO_LONG",
"IS_POSSIBLE_LOCAL_ONLY",
"INVALID_LENGTH"
];
var toEnumObject = (arr) => Object.fromEntries(arr.map((v) => [v, v]));
var NUMBER_FORMAT = toEnumObject(NUMBER_FORMATS);
var NUMBER_TYPE = toEnumObject(NUMBER_TYPES);
var VALIDATION_ERROR = toEnumObject(VALIDATION_ERRORS);
var DATA_KEYS = {
// e.g.
for country items in dropdown
ISO2: "iso2",
DIAL_CODE: "dialCode",
// e.g. on the input element
INSTANCE_ID: "intlTelInputId"
};
var ARIA = {
EXPANDED: "aria-expanded",
LABEL: "aria-label",
SELECTED: "aria-selected",
ACTIVE_DESCENDANT: "aria-activedescendant",
HASPOPUP: "aria-haspopup",
CONTROLS: "aria-controls",
HIDDEN: "aria-hidden",
AUTOCOMPLETE: "aria-autocomplete",
MODAL: "aria-modal"
};
// packages/core/src/js/i18n/en.ts
var interfaceTranslations = {
selectedCountryAriaLabel: "Change country for phone number, currently selected ${countryName} (${dialCode})",
noCountrySelected: "Select country for phone number",
countryListAriaLabel: "List of countries",
searchPlaceholder: "Search",
clearSearchAriaLabel: "Clear search",
searchEmptyState: "No results found",
searchSummaryAria(count) {
if (count === 0) {
return "No results found";
}
if (count === 1) {
return "1 result found";
}
return `${count} results found`;
}
};
var en_default = interfaceTranslations;
// packages/core/src/js/core/options.ts
var mediaQuery = (q) => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(q).matches;
var isNarrowViewport = () => mediaQuery(`(max-width: ${LAYOUT.NARROW_VIEWPORT_WIDTH}px)`);
var computeDefaultUseFullscreenPopup = () => {
if (typeof navigator !== "undefined" && typeof window !== "undefined") {
const isShortViewport = mediaQuery("(max-height: 600px)");
const isCoarsePointer = mediaQuery("(pointer: coarse)");
return isNarrowViewport() || isCoarsePointer && isShortViewport;
}
return false;
};
var defaults = {
//* Whether or not to allow the dropdown.
allowDropdown: true,
//* The number type to enforce during validation.
allowedNumberTypes: [NUMBER_TYPE.MOBILE, NUMBER_TYPE.FIXED_LINE],
//* Whether or not to allow extensions after the main number.
allowNumberExtensions: false,
// Allow alphanumeric "phonewords" (e.g. +1 800 FLOWERS) as valid numbers
allowPhonewords: false,
//* Add a placeholder in the input with an example number for the selected country.
autoPlaceholder: PLACEHOLDER_MODES.POLITE,
//* Add a custom class to the (injected) container element.
containerClass: "",
//* Locale for localising country names via Intl.DisplayNames.
countryNameLocale: "en",
//* Override individual country names by iso2 code.
countryNameOverrides: {},
//* The order of the countries in the dropdown. Defaults to alphabetical.
countryOrder: null,
//* Add a country search input at the top of the dropdown.
countrySearch: true,
//* Modify the auto placeholder.
customPlaceholder: null,
//* Always show the dropdown
dropdownAlwaysOpen: false,
//* Append menu to specified element.
dropdownContainer: null,
//* Don't display these countries.
excludeCountries: null,
//* Fix the dropdown width to the input width (rather than being as wide as the longest country name).
fixDropdownWidth: true,
//* Format the number as the user types
formatAsYouType: true,
//* Format the input value during initialisation and on setNumber.
formatOnDisplay: true,
//* geoIp lookup function.
geoIpLookup: null,
//* Inject a hidden input with the name returned from this function, and on submit, populate it with the result of getNumber.
hiddenInput: null,
//* Internationalise the core library text e.g. search input placeholder, country names.
i18n: {},
//* Initial country.
initialCountry: "",
//* A function to load the utils script.
loadUtils: null,
//* National vs international formatting for numbers e.g. placeholders and displaying existing numbers.
nationalMode: false,
//* Display only these countries.
onlyCountries: null,
//* Number type to use for placeholders.
placeholderNumberType: NUMBER_TYPE.MOBILE,
//* Add custom classes to the search input element.
searchInputClass: "",
//* Display the international dial code next to the selected flag.
separateDialCode: true,
//* When strictMode rejects a key (etc), play a short feedback animation
strictRejectAnimation: true,
//* Show flags - for both the selected country, and in the country dropdown
showFlags: true,
//* Only allow certain chars e.g. a plus followed by numeric digits, and cap at max valid length.
strictMode: true,
//* Use full screen popup instead of dropdown for country list.
useFullscreenPopup: computeDefaultUseFullscreenPopup()
};
var toString = (val) => JSON.stringify(val);
var isPlainObject = (val) => Boolean(val) && typeof val === "object" && !Array.isArray(val);
var isFunction = (val) => typeof val === "function";
var isElLike = (val) => {
if (!val || typeof val !== "object") {
return false;
}
const v = val;
return v.nodeType === 1 && typeof v.tagName === "string" && typeof v.appendChild === "function";
};
var placeholderModeSet = new Set(Object.values(PLACEHOLDER_MODES));
var warn = (message) => {
console.warn(`[intl-tel-input] ${message}`);
};
var warnOption = (optionName, expectedType, actualValue) => {
warn(
`Option '${optionName}' must be ${expectedType}; got ${toString(actualValue)}. Ignoring.`
);
};
var validateIso2Array = (key, value) => {
const expectedType = "an array of iso2 country code strings";
if (!Array.isArray(value)) {
warnOption(key, expectedType, value);
return false;
}
const valid = [];
for (const v of value) {
if (typeof v !== "string") {
warnOption(key, expectedType, value);
return false;
}
const lower = v.toLowerCase();
if (!isIso2(lower)) {
warn(`Invalid iso2 code in '${key}': '${v}'. Skipping.`);
} else {
valid.push(v);
}
}
return valid;
};
var validateOptions = (customOptions) => {
if (customOptions === void 0) {
return {};
}
if (!isPlainObject(customOptions)) {
const error = `The second argument must be an options object; got ${toString(customOptions)}. Using defaults.`;
warn(error);
return {};
}
const validatedOptions = {};
for (const [key, value] of Object.entries(customOptions)) {
if (!Object.hasOwn(defaults, key)) {
warn(`Unknown option '${key}'. Ignoring.`);
continue;
}
switch (key) {
case "allowDropdown":
case "allowNumberExtensions":
case "allowPhonewords":
case "countrySearch":
case "dropdownAlwaysOpen":
case "fixDropdownWidth":
case "formatAsYouType":
case "formatOnDisplay":
case "nationalMode":
case "showFlags":
case "separateDialCode":
case "strictMode":
case "strictRejectAnimation":
case "useFullscreenPopup":
if (typeof value !== "boolean") {
warnOption(key, "a boolean", value);
break;
}
validatedOptions[key] = value;
break;
case "autoPlaceholder":
if (typeof value !== "string" || !placeholderModeSet.has(value)) {
const validModes = Array.from(placeholderModeSet).join(", ");
warnOption("autoPlaceholder", `one of ${validModes}`, value);
break;
}
validatedOptions[key] = value;
break;
case "containerClass":
case "searchInputClass":
case "countryNameLocale":
if (typeof value !== "string") {
warnOption(key, "a string", value);
break;
}
validatedOptions[key] = value;
break;
case "countryOrder": {
if (value === null) {
validatedOptions[key] = value;
} else {
const filtered = validateIso2Array(key, value);
if (filtered !== false) {
validatedOptions[key] = filtered;
}
}
break;
}
case "customPlaceholder":
case "geoIpLookup":
case "hiddenInput":
case "loadUtils":
if (value !== null && !isFunction(value)) {
warnOption(key, "a function or null", value);
break;
}
validatedOptions[key] = value;
break;
case "dropdownContainer":
if (value !== null && !isElLike(value)) {
warnOption("dropdownContainer", "an HTMLElement or null", value);
break;
}
validatedOptions[key] = value;
break;
case "excludeCountries":
case "onlyCountries": {
if (value === null) {
validatedOptions[key] = value;
} else {
const filtered = validateIso2Array(key, value);
if (filtered !== false) {
validatedOptions[key] = filtered;
}
}
break;
}
case "i18n":
if (value && !isPlainObject(value)) {
warnOption("i18n", "an object", value);
break;
}
validatedOptions[key] = value;
break;
case "countryNameOverrides":
if (value && !isPlainObject(value)) {
warnOption("countryNameOverrides", "an object", value);
break;
}
validatedOptions[key] = value;
break;
case "initialCountry": {
if (typeof value !== "string") {
warnOption("initialCountry", "a string", value);
break;
}
const lower = value.toLowerCase();
if (lower && lower !== INITIAL_COUNTRY.AUTO && !isIso2(lower)) {
warnOption(
"initialCountry",
"a valid iso2 country code or 'auto'",
value
);
break;
}
validatedOptions[key] = value;
break;
}
case "placeholderNumberType":
if (typeof value !== "string" || !NUMBER_TYPES.includes(value)) {
const validTypes = NUMBER_TYPES.join(", ");
warnOption("placeholderNumberType", `one of ${validTypes}`, value);
break;
}
validatedOptions[key] = value;
break;
case "allowedNumberTypes":
if (value !== null) {
if (!Array.isArray(value)) {
warnOption(
"allowedNumberTypes",
"an array of number types or null",
value
);
break;
}
let allValid = true;
for (const v of value) {
if (typeof v !== "string" || !NUMBER_TYPES.includes(v)) {
const validTypes = NUMBER_TYPES.join(", ");
warnOption(
"allowedNumberTypes",
`an array of valid number types (${validTypes})`,
v
);
allValid = false;
break;
}
}
if (allValid) {
validatedOptions[key] = value;
}
} else {
validatedOptions[key] = null;
}
break;
}
}
return validatedOptions;
};
var normaliseOptions = (o) => {
if (o.initialCountry) {
o.initialCountry = o.initialCountry.toLowerCase();
}
if (o.onlyCountries?.length) {
o.onlyCountries = o.onlyCountries.map((c) => c.toLowerCase());
}
if (o.excludeCountries?.length) {
o.excludeCountries = o.excludeCountries.map((c) => c.toLowerCase());
}
if (o.countryOrder) {
o.countryOrder = o.countryOrder.map((c) => c.toLowerCase());
}
};
var applyOptionSideEffects = (o) => {
if (o.dropdownAlwaysOpen) {
o.useFullscreenPopup = false;
o.allowDropdown = true;
}
if (o.useFullscreenPopup) {
o.fixDropdownWidth = false;
} else {
if (isNarrowViewport()) {
o.fixDropdownWidth = true;
}
}
if (o.onlyCountries?.length === 1) {
o.initialCountry = o.onlyCountries[0];
}
if (o.separateDialCode) {
o.nationalMode = false;
}
if (o.allowDropdown && !o.showFlags && !o.separateDialCode) {
o.nationalMode = false;
}
if (o.useFullscreenPopup && !o.dropdownContainer) {
o.dropdownContainer = document.body;
}
o.i18n = { ...en_default, ...o.i18n };
};
// packages/core/src/js/helpers/string.ts
var getNumeric = (s) => s.replace(/\D/g, "");
var normaliseString = (s = "") => s.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
// packages/core/src/js/helpers/dom.ts
var buildClassNames = (flags) => Object.keys(flags).filter((k) => Boolean(flags[k])).join(" ");
var createEl = (tagName, attrs, container) => {
const el = document.createElement(tagName);
if (attrs) {
Object.entries(attrs).forEach(
([key, value]) => el.setAttribute(key, value)
);
}
if (container) {
container.appendChild(el);
}
return el;
};
// packages/core/src/js/core/icons.ts
var SVG_NS = "http://www.w3.org/2000/svg";
var buildSvg = ([tag, attrs, children]) => {
const el = document.createElementNS(SVG_NS, tag);
if (attrs) {
for (const k in attrs) {
el.setAttribute(k, String(attrs[k]));
}
}
if (children) {
for (const c of children) {
el.appendChild(buildSvg(c));
}
}
return el;
};
var buildSearchIcon = () => buildSvg(
["svg", { class: "iti__search-icon-svg", width: 14, height: 14, viewBox: "0 0 24 24", focusable: "false", [ARIA.HIDDEN]: "true" }, [
["circle", { cx: 11, cy: 11, r: 7 }],
["line", { x1: 21, y1: 21, x2: 16.65, y2: 16.65 }]
]]
);
var buildClearIcon = (id) => {
const maskId = `iti-${id}-clear-mask`;
return buildSvg(
["svg", { class: "iti__search-clear-svg", width: 12, height: 12, viewBox: "0 0 16 16", [ARIA.HIDDEN]: "true", focusable: "false" }, [
["mask", { id: maskId, maskUnits: "userSpaceOnUse" }, [
["rect", { width: 16, height: 16, fill: "white" }],
["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" }]
]],
["circle", { cx: 8, cy: 8, r: 8, class: "iti__search-clear-bg", mask: `url(#${maskId})` }]
]]
);
};
var buildCheckIcon = () => buildSvg(
["svg", { class: "iti__country-check-svg", width: 14, height: 14, viewBox: "0 0 16 16", fill: "currentColor", focusable: "false", [ARIA.HIDDEN]: "true" }, [
["path", { d: "M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" }]
]]
);
var buildGlobeIcon = () => buildSvg(
["svg", { width: 256, height: 256, viewBox: "0 0 512 512", class: "iti__globe-svg" }, [
["path", { d: "M508 213a240 240 0 0 0-449-87l-2 5-2 5c-8 14-13 30-17 46a65 65 0 0 1 56 4c16-10 35-19 56-27l9-3c-6 23-10 48-10 74h-16l4 6c3 4 5 8 6 13h6c0 22 3 44 8 65l2 10-25-10-4 5 12 18 9 3 6 2 8 3 9 26 1 2 16-7h1l-5-13-1-2c24 6 49 9 75 10v26l11 10 7 7v-30l1-13c22 0 44-3 65-8l10-2-21 48-1 1a317 317 0 0 1-14 23l-21 5h-2c6 16 7 33 1 50a240 240 0 0 0 211-265m-401-56-11 6c19-44 54-79 98-98-11 20-21 44-29 69-21 6-40 15-58 23m154 182v4c-29-1-57-6-81-13-7-25-12-52-13-81h94zm0-109h-94c1-29 6-56 13-81 24-7 52-12 81-13zm0-112c-22 1-44 4-65 8l-10 2 12-30 9-17 1-2a332 332 0 0 1 13-23c13-4 26-6 40-7zm187 69 6 4c4 12 6 25 6 38v1h-68c-1-26-4-51-10-74l48 20 1 1 14 8zm-14-44 10 20c-20-11-43-21-68-29-8-25-18-49-29-69 37 16 67 44 87 78M279 49h1c13 1 27 3 39 7l14 23 1 2a343 343 0 0 1 12 26l2 5 6 16c-23-6-48-9-74-10h-1zm0 87h1c29 1 56 6 81 13 7 24 12 51 12 80v1h-94zm2 207h-2v-94h95c-1 29-6 56-13 81-24 7-51 12-80 13m86 60-20 10c11-20 21-43 29-68 25-8 48-18 68-29-16 37-43 67-77 87m87-115-7 5-16 9-2 1a337 337 0 0 1-47 21c6-24 9-49 10-75h68c0 13-2 27-6 39" }],
["path", { d: "m261 428-2-2-22-21a40 40 0 0 0-32-11h-1a37 37 0 0 0-18 8l-1 1-4 2-2 2-5 4c-9-3-36-31-47-44s-32-45-34-55l3-2a151 151 0 0 0 11-9v-1a39 39 0 0 0 5-48l-3-3-11-19-3-4-5-7h-1l-3-3-4-3-5-2a35 35 0 0 0-16-3h-5c-4 1-14 5-24 11l-4 2-4 3-4 2c-9 8-17 17-18 27a380 380 0 0 0 212 259h3c12 0 25-10 36-21l10-12 6-11a39 39 0 0 0-8-40" }]
]]
);
// packages/core/src/js/core/countrySearch.ts
var normaliseName = (s) => normaliseString(s).replace(/[^\p{L}]+/gu, " ").trim();
var buildSearchTokens = (countries) => {
const tokens = /* @__PURE__ */ new Map();
for (const c of countries) {
const normalisedName = normaliseName(c.name);
const words = normalisedName.split(" ").filter(Boolean);
const initials = words.map((w) => w[0] || "").join("");
tokens.set(c.iso2, {
normalisedName,
words,
initials,
dialCodePlus: `+${c.dialCode}`
});
}
return tokens;
};
var getMatchedCountries = (countries, searchTokens, query) => {
const lowerQuery = normaliseString(query);
const nameQuery = normaliseName(query);
const skipNameBuckets = lowerQuery !== "" && nameQuery === "";
const iso2Matches = [];
const nameStartsWith = [];
const nameContains = [];
const dialCodeMatches = [];
const dialCodeContains = [];
const initialsMatches = [];
const wordMatches = [];
for (const c of countries) {
const t = searchTokens.get(c.iso2);
if (c.iso2 === lowerQuery) {
iso2Matches.push(c);
} else if (!skipNameBuckets && t.normalisedName.startsWith(nameQuery)) {
nameStartsWith.push(c);
} else if (!skipNameBuckets && t.normalisedName.includes(nameQuery)) {
nameContains.push(c);
} else if (lowerQuery === c.dialCode || lowerQuery === t.dialCodePlus) {
dialCodeMatches.push(c);
} else if (t.dialCodePlus.includes(lowerQuery)) {
dialCodeContains.push(c);
} else if (t.initials.includes(lowerQuery)) {
initialsMatches.push(c);
}
}
const queryWords = nameQuery.split(" ").filter(Boolean);
if (queryWords.length > 1 && iso2Matches.length === 0 && nameStartsWith.length === 0 && nameContains.length === 0) {
const claimed = /* @__PURE__ */ new Set([
...dialCodeMatches.map((c) => c.iso2),
...dialCodeContains.map((c) => c.iso2),
...initialsMatches.map((c) => c.iso2)
]);
for (const c of countries) {
if (claimed.has(c.iso2)) {
continue;
}
const t = searchTokens.get(c.iso2);
if (queryWords.some((qw) => t.words.some((sw) => sw.startsWith(qw)))) {
wordMatches.push(c);
}
}
}
const sortByPriority = (a, b) => a.priority - b.priority;
return [
...iso2Matches,
...nameStartsWith,
...nameContains,
// priority sort is only relevant when showing multiple countries with the same dial code (that's what the priority field is used to distinguish between)
...dialCodeMatches.sort(sortByPriority),
...dialCodeContains.sort(sortByPriority),
...initialsMatches,
...wordMatches
];
};
var findFirstCountryStartingWith = (countries, searchTokens, query) => {
const nameQuery = normaliseName(query);
for (const c of countries) {
const { normalisedName } = searchTokens.get(c.iso2);
if (normalisedName.startsWith(nameQuery)) {
return c;
}
}
return null;
};
// packages/core/src/js/core/numerals.ts
var Numerals = class _Numerals {
#userNumeralSet;
//* Stateless conversion of any Arabic-Indic / Persian digits to ASCII 0-9.
//* Use this when you need to normalise digits without affecting any instance's tracked numeral set (e.g. for the country-search query).
static toAscii(str) {
if (!str) {
return "";
}
return str.replace(
/[٠-٩]/g,
(ch) => String.fromCharCode(48 + (ch.charCodeAt(0) - 1632))
).replace(
/[۰-۹]/g,
(ch) => String.fromCharCode(48 + (ch.charCodeAt(0) - 1776))
);
}
constructor(initialValue) {
if (initialValue) {
this.#updateNumeralSet(initialValue);
}
}
// If any Arabic-Indic digits, then label it as that set. Same for Persian. Otherwise assume ASCII.
#updateNumeralSet(str) {
if (/[٠-٩]/.test(str)) {
this.#userNumeralSet = "arabic-indic";
} else if (/[۰-۹]/.test(str)) {
this.#userNumeralSet = "persian";
} else {
this.#userNumeralSet = "ascii";
}
}
// Denormalise ASCII 0-9 to the user's numeral set. If not yet known, return as-is.
// NOTE: normalise is always called before this, so it should be impossible for the numeral set to be unknown at this point.
denormalise(str) {
if (!this.#userNumeralSet || this.#userNumeralSet === "ascii") {
return str;
}
const base = this.#userNumeralSet === "arabic-indic" ? 1632 : 1776;
return str.replace(/[0-9]/g, (d) => String.fromCharCode(base + Number(d)));
}
// Normalize Eastern Arabic (U+0660-0669) and Persian/Extended Arabic-Indic (U+06F0-06F9) numerals to ASCII 0-9.
// Tracks the user's numeral set as a side effect so denormalise can mirror it back.
normalise(str) {
if (!str) {
return "";
}
this.#updateNumeralSet(str);
if (this.#userNumeralSet === "ascii") {
return str;
}
return _Numerals.toAscii(str);
}
isAscii() {
return !this.#userNumeralSet || this.#userNumeralSet === "ascii";
}
};
// packages/core/src/js/core/ui.ts
var UI = class _UI {
// private
#options;
#id;
#isRTL;
#originalPaddingLeft = "";
#countries;
#searchTokens;
#searchDebounceTimer = null;
#inlineDropdownHeight;
#countryContainerEl;
#selectedCountryEl;
#selectedFlagEl;
#selectedDialCodeEl;
#dropdownArrowEl;
#dropdownContentEl;
#searchIconEl;
#searchInputEl;
#searchClearButtonEl;
#countryListEl;
#hiddenInputPhoneEl;
#hiddenInputCountryEl;
#noResultsMessageEl;
#searchResultsLiveRegionEl;
#detachedDropdownEl;
#selectedListItemEl = null;
#highlightedListItemEl = null;
#listItemByIso2 = /* @__PURE__ */ new Map();
#dropdownAbortController = null;
#resizeObserver;
// public
telInputEl;
hadInitialPlaceholder;
constructor(input, options, id) {
input.dataset[DATA_KEYS.INSTANCE_ID] = id.toString();
this.telInputEl = input;
this.#options = options;
this.#id = id;
this.hadInitialPlaceholder = Boolean(input.getAttribute("placeholder"));
this.#isRTL = !!this.telInputEl.closest("[dir=rtl]");
this.#originalPaddingLeft = this.telInputEl.style.paddingLeft;
}
// Validate that the provided element is an HTMLInputElement.
static validateInput(input) {
const tagName = input?.tagName;
const isInputEl = Boolean(input) && typeof input === "object" && tagName === "INPUT" && typeof input.setAttribute === "function";
if (!isInputEl) {
const type = Object.prototype.toString.call(input);
throw new TypeError(
`The first argument must be an HTMLInputElement, not ${type}`
);
}
}
//* Generate all of the markup for the core library: the selected country overlay, and the dropdown.
buildMarkup(countries, searchTokens) {
this.#countries = countries;
this.#searchTokens = searchTokens;
this.telInputEl.classList.add("iti__tel-input");
if (!this.telInputEl.hasAttribute("type")) {
this.telInputEl.setAttribute("type", "tel");
}
if (!this.telInputEl.hasAttribute("autocomplete")) {
this.telInputEl.setAttribute("autocomplete", "tel");
}
if (!this.telInputEl.hasAttribute("inputmode")) {
this.telInputEl.setAttribute("inputmode", "tel");
}
const wrapper = this.#createWrapperAndInsert();
this.#buildCountryContainer(wrapper);
wrapper.appendChild(this.telInputEl);
this.#updateInputPaddingAndReveal();
this.#observeSelectedCountryResize();
this.#buildHiddenInputs(wrapper);
this.ensureDropdownWidthSet();
}
#createWrapperAndInsert() {
const { allowDropdown, showFlags, containerClass, useFullscreenPopup } = this.#options;
const parentClasses = buildClassNames({
iti: true,
"iti--input-container": true,
"iti--allow-dropdown": allowDropdown,
"iti--show-flags": showFlags,
"iti--inline-dropdown": !useFullscreenPopup,
[containerClass]: Boolean(containerClass)
});
const wrapper = createEl("div", { class: parentClasses });
if (this.#isRTL) {
wrapper.setAttribute("dir", "ltr");
}
this.telInputEl.before(wrapper);
return wrapper;
}
#buildCountryContainer(wrapper) {
const { allowDropdown, separateDialCode, showFlags } = this.#options;
if (!allowDropdown && !showFlags && !separateDialCode) {
return;
}
this.#countryContainerEl = createEl(
"div",
// visibly hidden until we measure its width to set the input padding correctly
{ class: `iti__country-container ${CLASSES.V_HIDE}` },
wrapper
);
if (allowDropdown) {
this.#selectedCountryEl = createEl(
"button",
{
type: "button",
class: "iti__selected-country",
[ARIA.EXPANDED]: "false",
[ARIA.LABEL]: this.#options.i18n.noCountrySelected,
[ARIA.HASPOPUP]: "dialog",
[ARIA.CONTROLS]: `iti-${this.#id}__dropdown-content`
},
this.#countryContainerEl
);
if (this.telInputEl.disabled) {
this.#selectedCountryEl.setAttribute("disabled", "true");
}
} else {
this.#selectedCountryEl = createEl(
"div",
{ class: "iti__selected-country" },
this.#countryContainerEl
);
}
const selectedCountryPrimary = createEl(
"div",
{ class: "iti__selected-country-primary" },
this.#selectedCountryEl
);
this.#selectedFlagEl = createEl(
"div",
{ class: CLASSES.FLAG },
selectedCountryPrimary
);
if (allowDropdown) {
this.#dropdownArrowEl = createEl(
"div",
{ class: "iti__arrow", [ARIA.HIDDEN]: "true" },
selectedCountryPrimary
);
}
if (separateDialCode) {
this.#selectedDialCodeEl = createEl(
"div",
{ class: "iti__selected-dial-code" },
this.#selectedCountryEl
);
}
if (allowDropdown) {
this.#buildDropdownContent();
}
}
ensureDropdownWidthSet() {
const { fixDropdownWidth, allowDropdown } = this.#options;
if (!allowDropdown || !fixDropdownWidth || this.#dropdownContentEl.style.width) {
return;
}
const inputWidth = this.telInputEl.offsetWidth;
if (inputWidth > 0) {
this.#dropdownContentEl.style.width = `${inputWidth}px`;
}
}
#buildDropdownContent() {
const {
fixDropdownWidth,
useFullscreenPopup,
countrySearch,
i18n,
dropdownContainer,
containerClass
} = this.#options;
const extraClasses = fixDropdownWidth ? "" : "iti--flexible-dropdown-width";
this.#dropdownContentEl = createEl("div", {
id: `iti-${this.#id}__dropdown-content`,
class: `iti__dropdown-content ${CLASSES.HIDE} ${extraClasses}`,
role: "dialog",
[ARIA.MODAL]: "true"
});
if (this.#isRTL) {
this.#dropdownContentEl.setAttribute("dir", "rtl");
}
if (countrySearch) {
this.#buildSearchUI();
}
this.#countryListEl = createEl(
"ul",
{
class: "iti__country-list",
id: `iti-${this.#id}__country-listbox`,
role: "listbox",
[ARIA.LABEL]: i18n.countryListAriaLabel
},
this.#dropdownContentEl
);
this.#appendListItems();
if (countrySearch) {
this.#updateSearchResultsA11yText();
}
if (!useFullscreenPopup) {
this.#inlineDropdownHeight = this.#getHiddenInlineDropdownHeight();
if (countrySearch) {
this.#dropdownContentEl.style.height = `${this.#inlineDropdownHeight}px`;
}
}
if (dropdownContainer) {
const dropdownClasses = buildClassNames({
iti: true,
"iti--container": true,
"iti--fullscreen-popup": useFullscreenPopup,
"iti--inline-dropdown": !useFullscreenPopup,
[containerClass]: Boolean(containerClass)
});
this.#detachedDropdownEl = createEl("div", { class: dropdownClasses });
this.#detachedDropdownEl.appendChild(this.#dropdownContentEl);
} else {
this.#countryContainerEl.appendChild(this.#dropdownContentEl);
}
}
#buildSearchUI() {
const { i18n, searchInputClass } = this.#options;
const searchWrapper = createEl(
"div",
{ class: "iti__search-input-wrapper" },
this.#dropdownContentEl
);
this.#searchIconEl = createEl(
"span",
{
class: "iti__search-icon",
[ARIA.HIDDEN]: "true"
},
searchWrapper
);
this.#searchIconEl.appendChild(buildSearchIcon());
this.#searchInputEl = createEl(
"input",
{
id: `iti-${this.#id}__search-input`,
// Chrome says inputs need either a name or an id
type: "search",
class: `iti__search-input ${searchInputClass}`,
placeholder: i18n.searchPlaceholder,
// 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
role: "combobox",
[ARIA.EXPANDED]: "true",
[ARIA.LABEL]: i18n.searchPlaceholder,
[ARIA.CONTROLS]: `iti-${this.#id}__country-listbox`,
[ARIA.AUTOCOMPLETE]: "list",
autocomplete: "off"
},
searchWrapper
);
this.#searchClearButtonEl = createEl(
"button",
{
type: "button",
class: `iti__search-clear ${CLASSES.HIDE}`,
[ARIA.LABEL]: i18n.clearSearchAriaLabel,
tabindex: "-1"
},
searchWrapper
);
this.#searchClearButtonEl.appendChild(buildClearIcon(this.#id));
this.#searchResultsLiveRegionEl = createEl(
"span",
{ class: "iti__a11y-text" },
this.#dropdownContentEl
);
this.#noResultsMessageEl = createEl(
"div",
{
class: `iti__no-results ${CLASSES.HIDE}`,
[ARIA.HIDDEN]: "true"
// all a11y messaging happens in this.#searchResultsLiveRegionEl
},
this.#dropdownContentEl
);
this.#noResultsMessageEl.textContent = i18n.searchEmptyState ?? null;
}
#updateInputPaddingAndReveal() {
if (!this.#countryContainerEl) {
return;
}
this.#updateInputPadding();
this.#countryContainerEl.classList.remove(CLASSES.V_HIDE);
}
#buildHiddenInputs(wrapper) {
const { hiddenInput } = this.#options;
if (!hiddenInput) {
return;
}
const telInputName = this.telInputEl.getAttribute("name") || "";
const names = hiddenInput(telInputName);
if (names.phone) {
const existingInput = this.telInputEl.form?.querySelector(
`input[name="${names.phone}"]`
);
if (existingInput) {
this.#hiddenInputPhoneEl = existingInput;
} else {
this.#hiddenInputPhoneEl = createEl("input", {
type: "hidden",
name: names.phone
});
wrapper.appendChild(this.#hiddenInputPhoneEl);
}
}
if (names.country) {
const existingInput = this.telInputEl.form?.querySelector(
`input[name="${names.country}"]`
);
if (existingInput) {
this.#hiddenInputCountryEl = existingInput;
} else {
this.#hiddenInputCountryEl = createEl("input", {
type: "hidden",
name: names.country
});
wrapper.appendChild(this.#hiddenInputCountryEl);
}
}
}
//* For each country: add a country list item to the countryList container.
#appendListItems() {
const frag = document.createDocumentFragment();
for (let i = 0; i < this.#countries.length; i++) {
const c = this.#countries[i];
const liClass = buildClassNames({
[CLASSES.COUNTRY_ITEM]: true
});
const listItem = createEl("li", {
id: `iti-${this.#id}__item-${c.iso2}`,
class: liClass,
tabindex: "-1",
role: "option",
[ARIA.SELECTED]: "false"
});
listItem.dataset[DATA_KEYS.DIAL_CODE] = c.dialCode;
listItem.dataset[DATA_KEYS.ISO2] = c.iso2;
this.#listItemByIso2.set(c.iso2, listItem);
if (this.#options.showFlags) {
createEl("div", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);
}
const nameEl = createEl("span", { class: "iti__country-name" }, listItem);
nameEl.textContent = `${c.name} `;
const dialEl = createEl("span", { class: "iti__dial-code" }, nameEl);
if (this.#isRTL) {
dialEl.setAttribute("dir", "ltr");
}
dialEl.textContent = `(+${c.dialCode})`;
frag.appendChild(listItem);
}
this.#countryListEl.appendChild(frag);
}
//* Update the input padding to make space for (1) the selected country/globe, (2) the arrow, and (3) the separate dial code, all of which are optional, hence handling this in the JS rather than CSS.
#updateInputPadding() {
if (this.#selectedCountryEl) {
const fallbackWidth = this.#options.separateDialCode ? LAYOUT.FALLBACK_SELECTED_WITH_DIAL_WIDTH : LAYOUT.FALLBACK_SELECTED_NO_DIAL_WIDTH;
const selectedCountryWidth = this.#selectedCountryEl.offsetWidth || this.#getHiddenSelectedCountryWidth() || fallbackWidth;
const inputPadding = selectedCountryWidth + LAYOUT.INPUT_PADDING_EXTRA_LEFT;
this.telInputEl.style.paddingLeft = `${inputPadding}px`;
}
}
//* Keep the input padding in sync when the selected country's rendered width changes — e.g. responsive font-size shifts that change the dial code text width. Skip while hidden (offsetWidth === 0) so we don't waste work or clobber the padding using a fallback constant.
#observeSelectedCountryResize() {
if (!this.#selectedCountryEl || typeof ResizeObserver === "undefined") {
return;
}
this.#resizeObserver = new ResizeObserver(() => {
if (this.#selectedCountryEl?.offsetWidth) {
this.#updateInputPadding();
}
});
this.#resizeObserver.observe(this.#selectedCountryEl);
}
static #getBody() {
let body;
try {
body = window.top.document.body;
} catch (e) {
body = document.body;
}
return body;
}
//* When input is in a hidden container during init, we cannot calculate the selected country width.
//* Fix: clone the markup, make it invisible, add it to the end of the DOM, and then measure it's width.
//* To get the right styling to apply, all we need is a shallow clone of the container,
//* and then to inject a deep clone of the selectedCountryEl element.
#getHiddenSelectedCountryWidth() {
if (!this.telInputEl.parentNode) {
return 0;
}
const body = _UI.#getBody();
const containerClone = this.telInputEl.parentNode.cloneNode(
false
);
containerClone.style.visibility = "hidden";
body.appendChild(containerClone);
const countryContainerClone = this.#countryContainerEl.cloneNode();
containerClone.appendChild(countryContainerClone);
const selectedCountryClone = this.#selectedCountryEl.cloneNode(
true
);
countryContainerClone.appendChild(selectedCountryClone);
const width = selectedCountryClone.offsetWidth;
body.removeChild(containerClone);
return width;
}
// Get the dropdown height (before it is added to the DOM)
#getHiddenInlineDropdownHeight() {
const body = _UI.#getBody();
this.#dropdownContentEl.classList.remove(CLASSES.HIDE);
const tempContainer = createEl("div", {
class: "iti iti--inline-dropdown"
});
tempContainer.appendChild(this.#dropdownContentEl);
tempContainer.style.visibility = "hidden";
body.appendChild(tempContainer);
const height = this.#dropdownContentEl.offsetHeight;
body.removeChild(tempContainer);
tempContainer.style.visibility = "";
this.#dropdownContentEl.classList.add(CLASSES.HIDE);
return height > 0 ? height : LAYOUT.FALLBACK_DROPDOWN_HEIGHT;
}
//* Update search results text (for a11y).
#updateSearchResultsA11yText() {
const { i18n } = this.#options;
const count = this.#countryListEl.childElementCount;
this.#searchResultsLiveRegionEl.textContent = i18n.searchSummaryAria(count);
}
//* Country search: Filter the countries according to the search query.
#filterCountriesByQuery(query) {
let matchedCountries;
if (query === "") {
matchedCountries = this.#countries;
} else {
const normalisedQuery = Numerals.toAscii(query);
matchedCountries = getMatchedCountries(
this.#countries,
this.#searchTokens,
normalisedQuery
);
}
this.#showFilteredCountries(matchedCountries);
}
//* Pre-fill the search input with "+" and show all countries
//* (used when user types "+" in the phone input to open the dropdown).
//* Explicitly focus the search input (openDropdown skips this when
//* dropdownAlwaysOpen, but here we need focus to redirect subsequent keystrokes).
prefillSearchWithPlus() {
this.#searchInputEl.value = "+";
this.#searchInputEl.focus();
this.#filterCountriesByQuery("");
}
// Search input handlers
#applySearchFilter() {
const inputQuery = this.#searchInputEl.value.trim();
this.#filterCountriesByQuery(inputQuery);
if (this.#searchInputEl.value) {
this.#searchClearButtonEl.classList.remove(CLASSES.HIDE);
} else {
this.#searchClearButtonEl.classList.add(CLASSES.HIDE);
}
}
#handleSearchChange() {
if (this.#searchDebounceTimer) {
clearTimeout(this.#searchDebounceTimer);
}
this.#searchDebounceTimer = setTimeout(() => {
this.#applySearchFilter();
this.#searchDebounceTimer = null;
}, TIMINGS.SEARCH_DEBOUNCE_MS);
}
#handleSearchClear() {
this.#searchInputEl.value = "";
this.#searchInputEl.focus();
this.#applySearchFilter();
}
//* Check if a country list item element is visible within it's container (the country list), else scroll until it is.
#scrollCountryListToItem(element) {
const container = this.#countryListEl;
const containerRect = container.getBoundingClientRect();
const elementRect = element.getBoundingClientRect();
const offsetTop = elementRect.top - containerRect.top + container.scrollTop;
if (elementRect.top < containerRect.top) {
container.scrollTop = offsetTop;
} else if (elementRect.bottom > containerRect.bottom) {
container.scrollTop = offsetTop - containerRect.height + elementRect.height;
}
}
//* Remove highlighting from the previous list item and highlight the new one.
#highlightListItem(listItem, doScroll = true) {
this.#highlightedListItemEl?.classList.remove(CLASSES.HIGHLIGHT);
if (listItem) {
listItem.classList.add(CLASSES.HIGHLIGHT);
if (this.#options.countrySearch) {
const activeDescendant = listItem.getAttribute("id") || "";
this.#searchInputEl.setAttribute(
ARIA.ACTIVE_DESCENDANT,
activeDescendant
);
}
if (doScroll) {
this.#scrollCountryListToItem(listItem);
}
this.#highlightedListItemEl = listItem;
} else {
this.#highlightedListItemEl = null;
}
}
//* Bind a form-submit listener that syncs the hidden inputs with the current phone number
//* and country iso2. No-op if there are no hidden inputs or the input is not in a form.
bindHiddenInputSubmitListener(signal, getPhone, getCountryIso2) {
const form = this.telInputEl.form;
if (!form || !this.#hiddenInputPhoneEl && !this.#hiddenInputCountryEl) {
return;
}
form.addEventListener(
"submit",
() => {
if (this.#hiddenInputPhoneEl) {
this.#hiddenInputPhoneEl.value = getPhone();
}
if (this.#hiddenInputCountryEl) {
this.#hiddenInputCountryEl.value = getCountryIso2();
}
},
{ signal }
);
}
//* Wire up triggers that open/close the dropdown: label click (focus input or swallow repeat click),
//* selected-country click (open), and keydown on countryContainer (open on arrow/space/enter, close on tab).
bindAllInitialDropdownListeners(signal, onOpen, onClose) {
const label = this.telInputEl.closest("label");
if (label) {
label.addEventListener(
"click",
(e) => {
if (!this.isDropdownOpen()) {
this.telInputEl.focus();
} else {
e.preventDefault();
}
},
{ signal }
);
}
this.#selectedCountryEl.addEventListener(
"click",
() => {
if (!this.isDropdownOpen() && !this.telInputEl.disabled && !this.telInputEl.readOnly) {
onOpen();
}
},
{ signal }
);
this.#countryContainerEl.addEventListener(
"keydown",
(e) => {
const openKeys = [
KEYS.ARROW_UP,
KEYS.ARROW_DOWN,
KEYS.SPACE,
KEYS.ENTER
];
if (!this.isDropdownOpen() && openKeys.includes(e.key)) {
e.preventDefault();
e.stopPropagation();
onOpen();
}
if (e.key === KEYS.TAB) {
onClose();
}
},
{ signal }
);
}
//* Open the dropdown: create a fresh AbortController, do the DOM work, and wire up all
//* dropdown-open listeners (which invoke the caller's onSelect / onClose callbacks).
openDropdown(onSelect, onClose) {
const { countrySearch, dropdownAlwaysOpen, dropdownContainer } = this.#options;
this.#dropdownAbortController = new AbortController();
this.ensureDropdownWidthSet();
if (dropdownContainer) {
this.#injectAndPositionDetachedDropdown();
} else {
const positionBelow = this.#shouldPositionInlineDropdownBelowInput();
const distance = this.telInputEl.offsetHeight + LAYOUT.DROPDOWN_MARGIN;
if (positionBelow) {
this.#dropdownContentEl.style.top = `${distance}px`;
} else {
this.#dropdownContentEl.style.bottom = `${distance}px`;
}
}
this.#dropdownContentEl.classList.remove(CLASSES.HIDE);
this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "true");
const itemToHighlight = this.#selectedListItemEl ?? this.#countryListEl.firstElementChild;
if (itemToHighlight) {
this.#highlightListItem(itemToHighlight);
}
if (countrySearch && !dropdownAlwaysOpen) {
this.#searchInputEl.focus();
}
if (this.#options.useFullscreenPopup && this.#detachedDropdownEl && window.visualViewport) {
window.visualViewport.addEventListener(
"resize",
() => {
this.#adjustFullscreenPopupToViewport();
if (this.#highlightedListItemEl) {
this.#scrollCountryListToItem(this.#highlightedListItemEl);
}
},
{ signal: this.#dropdownAbortController.signal }
);
}
this.#dropdownArrowEl.classList.add(CLASSES.ARROW_UP);
this.#bindDropdownOpenListeners(onSelect, onClose);
}
//* Wire up all listeners needed while the dropdown is open: list-item hover (highlight),
//* list-item click & enter key (select), click-off & escape (close), search input (filter),
//* (when countrySearch disabled) typed-char hidden search, and (when dropdown is in an external
//* container) close on window scroll.
#bindDropdownOpenListeners(onSelect, onClose) {
const signal = this.#dropdownAbortController.signal;
this.#bindListItemHover(signal);
this.#bindListItemClick(signal, onSelect);
if (!this.#options.dropdownAlwaysOpen) {
this.#bindOutsideClickToClose(signal, onClose);
}
this.#bindDropdownKeydownListener(signal, onSelect, onClose);
if (this.#options.countrySearch) {
this.#bindSearchInputListener(signal);
}
if (!this.#options.useFullscreenPopup && this.#options.dropdownContainer) {
window.addEventListener("scroll", onClose, { signal });
}
}
//* When mouse over a list item, just highlight that one (so if they hit "enter" we know which to select).
#bindListItemHover(signal) {
this.#countryListEl.addEventListener(
"mouseover",
(e) => {
const listItem = e.target?.closest(
`.${CLASSES.COUNTRY_ITEM}`
);
if (listItem) {
this.#highlightListItem(listItem, false);
}
},
{ signal }
);
}
//* Delegate clicks on the country list to the caller's onSelect callback, passing the clicked list item.
#bindListItemClick(signal, onSelect) {
this.#countryListEl.addEventListener(
"click",
(e) => {
const listItem = e.target?.closest(
`.${CLASSES.COUNTRY_ITEM}`
);
if (listItem) {
onSelect(listItem);
}
},
{ signal }
);
}
//* Invoke onClickOff when the user clicks anywhere outside the dropdown.
#bindOutsideClickToClose(signal, onClickOff) {
setTimeout(() => {
document.documentElement.addEventListener(
"click",
(e) => {
if (!this.#dropdownContentEl.contains(e.target)) {
onClickOff();
}
},
{ signal }
);
}, 0);
}
//* Keyboard navigation while the dropdown is open: arrow keys navigate, hidden-search keys filter,
//* and enter/escape invoke the caller's callbacks (which handle country selection / dropdown close).
//* Uses keydown rather than keypress so non-char keys (arrow, esc) fire and so holding a key repeats.
#bindDropdownKeydownListener(signal, onEnter, onEscape) {
let query = "";
let queryTimer = null;
const handleKeydown = (e) => {
const allowedKeys = [
KEYS.ARROW_UP,
KEYS.ARROW_DOWN,
KEYS.ENTER,
KEYS.ESC
];
if (allowedKeys.includes(e.key)) {
e.preventDefault();
e.stopPropagation();
if (e.key === KEYS.ARROW_UP || e.key === KEYS.ARROW_DOWN) {
this.#handleUpDownKey(e.key);
} else if (e.key === KEYS.ENTER && !e.isComposing) {
onEnter(this.#highlightedListItemEl);
} else if (e.key === KEYS.ESC) {
onEscape();
this.#selectedCountryEl.focus();
}
}
if (!this.#options.countrySearch && REGEX.HIDDEN_SEARCH_CHAR.test(e.key)) {
e.stopPropagation();
if (queryTimer) {
clearTimeout(queryTimer);
}
query += e.key.toLowerCase();
this.#searchForCountry(query);
queryTimer = setTimeout(() => {
query = "";
}, TIMINGS.HIDDEN_SEARCH_RESET_MS);
}
};
this.#selectedCountryEl?.addEventListener("keydown", handleKeydown, { signal });
this.#dropdownContentEl?.addEventListener("keydown", handleKeydown, { signal });
}
//* Wire up country search input listener: typing filters the list, the clear button resets it.
#bindSearchInputListener(signal) {
this.#searchInputEl.addEventListener(
"input",
() => this.#handleSearchChange(),
{ signal }
);
this.#searchClearButtonEl.addEventListener(
"click",
() => this.#handleSearchClear(),
{ signal }
);
}
//* Hidden search (countrySearch disabled): jump to the first list item whose name starts with the query.
#searchForCountry(query) {
const match = findFirstCountryStartingWith(
this.#countries,
this.#searchTokens,
query
);
if (match) {
const listItem = this.#listItemByIso2.get(match.iso2);
this.#highlightListItem(listItem);
}
}
//* Highlight the next/prev item in the list (and ensure it is visible).
#handleUpDownKey(key) {
let next = key === KEYS.ARROW_UP ? this.#highlightedListItemEl?.previousElementSibling : this.#highlightedListItemEl?.nextElementSibling;
if (!next && this.#countryListEl.childElementCount > 1) {
next = key === KEYS.ARROW_UP ? this.#countryListEl.lastElementChild : this.#countryListEl.firstElementChild;
}
if (next) {
this.#highlightListItem(next);
}
}
// Update the selected list item in the dropdown
#updateSelectedListItem(iso2) {
if (this.#selectedListItemEl && this.#selectedListItemEl.dataset[DATA_KEYS.ISO2] !== iso2) {
this.#selectedListItemEl.setAttribute(ARIA.SELECTED, "false");
this.#selectedListItemEl.querySelector(".iti__country-check")?.remove();
this.#selectedListItemEl = null;
}
if (iso2 && !this.#selectedListItemEl) {
const newListItem = this.#countryListEl.querySelector(
`[data-iso2="${iso2}"]`
);
if (newListItem) {
newListItem.setAttribute(ARIA.SELECTED, "true");
const checkIcon = createEl(
"span",
{ class: "iti__country-check", [ARIA.HIDDEN]: "true" },
newListItem
);
checkIcon.appendChild(buildCheckIcon());
this.#selectedListItemEl = newListItem;
if (this.#options.dropdownAlwaysOpen) {
this.#highlightListItem(newListItem);
}
}
}
}
//* Country search: Filter the country list to the given array of countries.
#showFilteredCountries(matchedCountries) {
this.#countryListEl.replaceChildren();
let noCountriesAddedYet = true;
for (const c of matchedCountries) {
const listItem = this.#listItemByIso2.get(c.iso2);
if (listItem) {
this.#countryListEl.appendChild(listItem);
if (noCountriesAddedYet) {
this.#highlightListItem(listItem, false);
noCountriesAddedYet = false;
}
}
}
if (noCountriesAddedYet) {
this.#highlightListItem(null);
if (this.#noResultsMessageEl) {
this.#noResultsMessageEl.classList.remove(CLASSES.HIDE);
}
} else if (this.#noResultsMessageEl) {
this.#noResultsMessageEl.classList.add(CLASSES.HIDE);
}
this.#countryListEl.scrollTop = 0;
this.#updateSearchResultsA11yText();
}
// UI: Close the dropdown (DOM + abort dropdown-scoped listeners).
closeDropdown() {
const { countrySearch, dropdownContainer } = this.#options;
this.#dropdownAbortController.abort();
this.#dropdownAbortController = null;
this.#dropdownContentEl.classList.add(CLASSES.HIDE);
this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "false");
if (countrySearch) {
this.#searchInputEl.removeAttribute(ARIA.ACTIVE_DESCENDANT);
this.#searchInputEl.value = "";
this.#applySearchFilter();
if (this.#highlightedListItemEl) {
this.#highlightedListItemEl.classList.remove(CLASSES.HIGHLIGHT);
this.#highlightedListItemEl = null;
}
}
this.#dropdownArrowEl.classList.remove(CLASSES.ARROW_UP);
if (dropdownContainer) {
this.#detachedDropdownEl.remove();
this.#detachedDropdownEl.style.top = "";
this.#detachedDropdownEl.style.bottom = "";
this.#detachedDropdownEl.style.paddingLeft = "";
this.#detachedDropdownEl.style.paddingRight = "";
} else {
this.#dropdownContentEl.style.top = "";
this.#dropdownContentEl.style.bottom = "";
}
}
#shouldPositionInlineDropdownBelowInput() {
if (this.#options.dropdownAlwaysOpen) {
return true;
}
const inputPos = this.telInputEl.getBoundingClientRect();
const spaceAbove = inputPos.top;
const spaceBelow = window.innerHeight - inputPos.bottom;
return spaceBelow >= this.#inlineDropdownHeight || spaceBelow >= spaceAbove;
}
// inject dropdown into container and apply positioning styles
#injectAndPositionDetachedDropdown() {
const { dropdownContainer, useFullscreenPopup } = this.#options;
if (useFullscreenPopup) {
if (window.innerWidth >= LAYOUT.NARROW_VIEWPORT_WIDTH) {
const inputPos = this.telInputEl.getBoundingClientRect();
this.#detachedDropdownEl.style.paddingLeft = `${inputPos.left}px`;
this.#detachedDropdownEl.style.paddingRight = `${window.innerWidth - inputPos.right}px`;
}
} else {
const inputPos = this.telInputEl.getBoundingClientRect();
this.#detachedDropdownEl.style.left = `${inputPos.left}px`;
const positionBelow = this.#shouldPositionInlineDropdownBelowInput();
if (positionBelow) {
this.#detachedDropdownEl.style.top = `${inputPos.bottom + LAYOUT.DROPDOWN_MARGIN}px`;
} else {
this.#detachedDropdownEl.style.top = "unset";
this.#detachedDropdownEl.style.bottom = `${window.innerHeight - inputPos.top + LAYOUT.DROPDOWN_MARGIN}px`;
}
}
dropdownContainer.appendChild(this.#detachedDropdownEl);
}
// Adjust the fullscreen popup dimensions to match the visual viewport,
// so it stays above the virtual keyboard on mobile devices.
#adjustFullscreenPopupToViewport() {
const vv = window.visualViewport;
if (!vv || !this.#detachedDropdownEl) {
return;
}
const virtualKeyboardHeight = window.innerHeight - vv.height;
this.#detachedDropdownEl.style.bottom = `${virtualKeyboardHeight}px`;
}
// UI: Whether the dropdown is currently open (visible).
isDropdownOpen() {
return !this.#dropdownContentEl.classList.contains(CLASSES.HIDE);
}
// Toggle the loading spinner on the selected flag (used during auto-country geoIP lookup).
setLoading(isLoading) {
this.#selectedFlagEl.classList.toggle(CLASSES.LOADING, isLoading);
}
//* Play the strict-reject animation (shake, or background-colour flash under prefers-reduced-motion) on the wrapper.
//* Called when strictMode rejects the whole input (keystroke, or whole paste).
//* Uses the wrapper (not the input) so any separateDialCode / country button move together with the input.
playStrictRejectAnimation() {
if (!this.#options.strictRejectAnimation) {
return;
}
const wrapperEl = this.telInputEl.parentElement;
if (!wrapperEl) {
return;
}
wrapperEl.classList.remove(CLASSES.STRICT_REJECT_ANIMATION);
void wrapperEl.offsetWidth;
wrapperEl.classList.add(CLASSES.STRICT_REJECT_ANIMATION);
wrapperEl.addEventListener(
"animationend",
() => wrapperEl.classList.remove(CLASSES.STRICT_REJECT_ANIMATION),
{ once: true }
);
}
isLoading() {
return this.#selectedFlagEl.classList.contains(CLASSES.LOADING);
}
// Set the disabled state of the input and dropdown.
setDisabled(disabled) {
this.telInputEl.disabled = disabled;
if (this.#selectedCountryEl) {
if (disabled) {
this.#selectedCountryEl.setAttribute("disabled", "true");
} else {
this.#selectedCountryEl.removeAttribute("disabled");
}
}
}
// Set the readonly state of the input and dropdown.
setReadonly(readonly) {
this.telInputEl.readOnly = readonly;
if (this.#selectedCountryEl) {
if (readonly) {
this.#selectedCountryEl.setAttribute("disabled", "true");
} else {
this.#selectedCountryEl.removeAttribute("disabled");
}
}
}
setCountry(selectedCountryData) {
const { allowDropdown, showFlags, separateDialCode, i18n } = this.#options;
const name = selectedCountryData?.name;
const dialCode = selectedCountryData?.dialCode;
const iso2 = selectedCountryData?.iso2 ?? "";
if (allowDropdown) {
this.#updateSelectedListItem(iso2);
}
if (this.#selectedCountryEl) {
const flagClass = iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`;
let ariaLabel, title;
let flagContent = null;
if (iso2) {
title = name;
ariaLabel = i18n.selectedCountryAriaLabel.replace("${countryName}", name).replace("${dialCode}", `+${dialCode}`);
if (!showFlags) {
flagContent = buildGlobeIcon();
}
} else {
title = i18n.noCountrySelected;
ariaLabel = i18n.noCountrySelected;
flagContent = buildGlobeIcon();
}
this.#selectedFlagEl.className = flagClass;
this.#selectedCountryEl.setAttribute("title", title);
this.#selectedCountryEl.setAttribute(ARIA.LABEL, ariaLabel);
if (flagContent) {
this.#selectedFlagEl.replaceChildren(flagContent);
} else {
this.#selectedFlagEl.replaceChildren();
}
}
if (separateDialCode) {
const fullDialCode = dialCode ? `+${dialCode}` : "";
this.#selectedDialCodeEl.textContent = fullDialCode;
this.#updateInputPadding();
}
}
destroy() {
this.telInputEl.iti = void 0;
delete this.telInputEl.dataset[DATA_KEYS.INSTANCE_ID];
this.#resizeObserver?.disconnect();
this.telInputEl.style.paddingLeft = this.#originalPaddingLeft;
const wrapper = this.telInputEl.parentNode;
if (wrapper) {
wrapper.before(this.telInputEl);
wrapper.remove();
}
this.#listItemByIso2.clear();
}
};
// packages/core/src/js/data/country-data.ts
var processAllCountries = (options) => {
const { onlyCountries, excludeCountries } = options;
if (onlyCountries?.length) {
return data_default.filter(
(country) => onlyCountries.includes(country.iso2)
);
} else if (excludeCountries?.length) {
return data_default.filter(
(country) => !excludeCountries.includes(country.iso2)
);
}
return [...data_default];
};
var generateCountryNames = (countries, options) => {
const { countryNameLocale, countryNameOverrides } = options;
let displayNames;
try {
const hasDisplayNames = typeof Intl !== "undefined" && typeof Intl.DisplayNames === "function";
if (hasDisplayNames) {
displayNames = new Intl.DisplayNames(countryNameLocale, {
type: "region"
});
} else {
displayNames = null;
}
} catch (e) {
console.error(e);
displayNames = null;
}
for (const c of countries) {
c.name = countryNameOverrides[c.iso2] || displayNames?.of(c.iso2.toUpperCase()) || "";
}
};
var processDialCodes = (countries) => {
const dialCodes = /* @__PURE__ */ new Set();
let dialCodeMaxLength = 0;
const dialCodeToIso2Map = {};
const addToDialCodeMap = (iso2, dialCode) => {
if (!iso2 || !dialCode) {
return;
}
if (dialCode.length > dialCodeMaxLength) {
dialCodeMaxLength = dialCode.length;
}
if (!Object.hasOwn(dialCodeToIso2Map, dialCode)) {
dialCodeToIso2Map[dialCode] = [];
}
const iso2List = dialCodeToIso2Map[dialCode];
if (iso2List.includes(iso2)) {
return;
}
iso2List.push(iso2);
};
const countriesSortedByPriority = [...countries].sort(
(a, b) => a.priority - b.priority
);
for (const c of countriesSortedByPriority) {
if (!dialCodes.has(c.dialCode)) {
dialCodes.add(c.dialCode);
}
for (let k = 1; k < c.dialCode.length; k++) {
const partialDialCode = c.dialCode.substring(0, k);
addToDialCodeMap(c.iso2, partialDialCode);
}
addToDialCodeMap(c.iso2, c.dialCode);
if (c.areaCodes) {
const rootIso2Code = dialCodeToIso2Map[c.dialCode][0];
for (const areaCode of c.areaCodes) {
for (let k = 1; k < areaCode.length; k++) {
const partialAreaCode = areaCode.substring(0, k);
const partialDialCode = c.dialCode + partialAreaCode;
addToDialCodeMap(rootIso2Code, partialDialCode);
addToDialCodeMap(c.iso2, partialDialCode);
}
addToDialCodeMap(c.iso2, c.dialCode + areaCode);
}
}
}
return { dialCodes, dialCodeMaxLength, dialCodeToIso2Map };
};
var sortCountries = (countries, options) => {
const { countryOrder } = options;
countries.sort((a, b) => {
if (countryOrder) {
const aIndex = countryOrder.indexOf(a.iso2);
const bIndex = countryOrder.indexOf(b.iso2);
const aIndexExists = aIndex > -1;
const bIndexExists = bIndex > -1;
if (aIndexExists || bIndexExists) {
if (aIndexExists && bIndexExists) {
return aIndex - bIndex;
}
return aIndexExists ? -1 : 1;
}
}
return a.name.localeCompare(b.name);
});
};
// packages/core/src/js/data/intl-regionless.ts
var regionlessDialCodes = /* @__PURE__ */ new Set([
"800",
"808",
"870",
"881",
"882",
"883",
"888",
"979"
]);
var hasRegionlessDialCode = (number) => {
const dialCode = getNumeric(number).slice(0, 3);
return number.startsWith("+") && regionlessDialCodes.has(dialCode);
};
// packages/core/src/js/format/formatting.ts
var stripSeparateDialCode = (fullNumber, hasValidDialCode, separateDialCode, selectedCountryData) => {
if (!separateDialCode || !hasValidDialCode) {
return fullNumber;
}
const dialCode = `+${selectedCountryData.dialCode}`;
const start = fullNumber[dialCode.length] === " " || fullNumber[dialCode.length] === "-" ? dialCode.length + 1 : dialCode.length;
return fullNumber.substring(start);
};
var formatNumberAsYouType = (fullNumber, telInputValue, utils, selectedCountryData, separateDialCode) => {
const result = utils ? utils.formatNumberAsYouType(fullNumber, selectedCountryData?.iso2) : fullNumber;
const dialCode = selectedCountryData?.dialCode;
if (separateDialCode && telInputValue.charAt(0) !== "+" && result.includes(`+${dialCode}`)) {
const afterDialCode = result.split(`+${dialCode}`)[1] || "";
return afterDialCode.trim();
}
return result;
};
// packages/core/src/js/format/caret.ts
var computeNewCaretPosition = (relevantChars, formattedValue, prevCaretPos, isDeleteForwards) => {
if (prevCaretPos === 0 && !isDeleteForwards) {
return 0;
}
let relevantCharCount = 0;
for (let i = 0; i < formattedValue.length; i++) {
if (/[+0-9]/.test(formattedValue[i])) {
relevantCharCount++;
}
if (relevantCharCount === relevantChars && !isDeleteForwards) {
return i + 1;
}
if (isDeleteForwards && relevantCharCount === relevantChars + 1) {
return i;
}
}
return formattedValue.length;
};
// packages/core/src/js/data/nanp-regionless.ts
var regionlessNanpAreaCodes = /* @__PURE__ */ new Set([
"800",
"822",
"833",
"844",
"855",
"866",
"877",
"880",
"881",
"882",
"883",
"884",
"885",
"886",
"887",
"888",
"889"
]);
var isRegionlessNanp = (number) => {
const numeric = getNumeric(number);
if (numeric.startsWith(DIAL_CODE.NANP) && numeric.length >= 4) {
const areaCode = numeric.substring(1, 4);
return regionlessNanpAreaCodes.has(areaCode);
}
return false;
};
// packages/core/src/js/intlTelInput.ts
var nextId = 0;
var ensureUtils = (methodName) => {
if (!intlTelInput.utils) {
throw new Error(
`intlTelInput.utils is required for ${methodName}(). See: https://intl-tel-input.com/docs/utils`
);
}
};
var createDeferred = () => {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
var Iti = class _Iti {
//* PUBLIC FIELDS - READONLY
//* Can't be private as it's called from intlTelInput convenience wrapper.
id;
// accessed externally via iti.promise.then(...)
promise;
//* PRIVATE FIELDS
#ui;
#options;
#isAndroid;
// country data
#countries;
#dialCodeMaxLength;
#dialCodeToIso2Map;
#dialCodes;
#countryByIso2;
#searchTokens;
#selectedCountry = null;
#maxCoreNumberLength = null;
#fallbackCountryIso2;
// is this instance still active (not destroyed)
#isActive = true;
#abortController;
#numerals;
//* Tracks whether the user has typed/pasted their own formatting chars, so AYT-formatting should back off.
#userOverrideFormatting = false;
#strictPasteSnapshot = null;
#autoCountryDeferred;
#utilsDeferred;
constructor(input, customOptions = {}) {
this.id = nextId++;
UI.validateInput(input);
const validatedOptions = validateOptions(customOptions);
this.#options = { ...defaults, ...validatedOptions };
normaliseOptions(this.#options);
applyOptionSideEffects(this.#options);
this.#ui = new UI(input, this.#options, this.id);
this.#isAndroid = typeof navigator !== "undefined" && /Android/i.test(navigator.userAgent);
this.#numerals = new Numerals(input.value);
this.promise = this.#createInitPromise(this.#options);
this.#countries = processAllCountries(this.#options);
const { dialCodes, dialCodeMaxLength, dialCodeToIso2Map } = processDialCodes(this.#countries);
this.#dialCodes = dialCodes;
this.#dialCodeMaxLength = dialCodeMaxLength;
this.#dialCodeToIso2Map = dialCodeToIso2Map;
this.#countryByIso2 = new Map(this.#countries.map((c) => [c.iso2, c]));
this.#init();
}
#getTelInputValue() {
const inputValue = this.#ui.telInputEl.value.trim();
return this.#numerals.normalise(inputValue);
}
#setTelInputValue(asciiValue) {
this.#ui.telInputEl.value = this.#numerals.denormalise(asciiValue);
}
#createInitPromise(options) {
const { initialCountry, geoIpLookup, loadUtils } = options;
const needsAutoCountryDeferred = initialCountry === INITIAL_COUNTRY.AUTO && Boolean(geoIpLookup);
const needsUtilsDeferred = Boolean(loadUtils) && !intlTelInput.utils;
if (needsAutoCountryDeferred) {
this.#autoCountryDeferred = createDeferred();
}
if (needsUtilsDeferred) {
this.#utilsDeferred = createDeferred();
}
return Promise.all([
this.#autoCountryDeferred?.promise,
this.#utilsDeferred?.promise
]).then(() => {
});
}
#init() {
this.#abortController = new AbortController();
this.#processCountryData();
this.#ui.buildMarkup(this.#countries, this.#searchTokens);
this.#setInitialState();
this.#initListeners();
this.#startAsyncLoads();
if (this.#options.dropdownAlwaysOpen) {
this.#openDropdown();
}
}
//********************
//* PRIVATE METHODS
//********************
//* Prepare all of the country data, including onlyCountries, excludeCountries, countryOrder options.
#processCountryData() {
generateCountryNames(this.#countries, this.#options);
sortCountries(this.#countries, this.#options);
this.#searchTokens = buildSearchTokens(this.#countries);
}
//* Set the initial state of the input value and the selected country by:
//* 1. Extracting a dial code from the given number
//* 2. Using explicit initialCountry
#setInitialState(overrideAutoCountry = false) {
const attributeValueRaw = this.#ui.telInputEl.getAttribute("value");
const attributeValue = this.#numerals.normalise(attributeValueRaw ?? "");
const inputValue = this.#getTelInputValue();
const useAttribute = attributeValue && attributeValue.startsWith("+") && (!inputValue || !inputValue.startsWith("+"));
const value = useAttribute ? attributeValue : inputValue;
const dialCode = this.#getDialCode(value);
const isRegionlessNanpNumber = isRegionlessNanp(value);
const { initialCountry, geoIpLookup } = this.#options;
const isAutoCountry = initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;
const resolvedInitialCountry = isAutoCountry && intlTelInput.autoCountry ? intlTelInput.autoCountry : initialCountry;
const doingAutoCountryLookup = isAutoCountry && !overrideAutoCountry && !intlTelInput.autoCountry;
const isValidInitialCountry = isIso2(resolvedInitialCountry);
if (dialCode) {
if (isRegionlessNanpNumber) {
if (isValidInitialCountry) {
this.#updateSelectedCountry(resolvedInitialCountry);
} else if (!doingAutoCountryLookup) {
this.#updateSelectedCountry(US.ISO2);
}
} else {
if (isValidInitialCountry) {
this.#updateSelectedCountry(resolvedInitialCountry);
}
this.#updateCountryFromNumber(value);
}
} else if (isValidInitialCountry) {
this.#updateSelectedCountry(resolvedInitialCountry);
} else if (!doingAutoCountryLookup) {
this.#updateSelectedCountry("");
}
if (value) {
this.#updateValueFromNumber(value);
}
}
//* Initialise the main event listeners: input keyup, and click selected country.
#initListeners() {
this.#bindAllTelInputListeners();
if (this.#options.allowDropdown) {
this.#ui.bindAllInitialDropdownListeners(
this.#abortController.signal,
() => this.#openDropdown(),
() => this.#closeDropdown()
);
}
this.#ui.bindHiddenInputSubmitListener(
this.#abortController.signal,
() => this.getNumber(),
() => this.#selectedCountry?.iso2 || ""
);
}
//* Init requests: utils script / geo ip lookup.
#startAsyncLoads() {
if (this.#utilsDeferred) {
const { loadUtils } = this.#options;
const doAttachUtils = () => {
intlTelInput.attachUtils(loadUtils).catch(() => {
});
};
if (intlTelInput.documentReady()) {
doAttachUtils();
} else {
window.addEventListener("load", doAttachUtils, {
signal: this.#abortController.signal
});
}
}
if (this.#autoCountryDeferred) {
if (this.#selectedCountry) {
this.#autoCountryDeferred.resolve();
} else {
this.#loadAutoCountry();
}
}
}
//* Perform the geo ip lookup.
async #loadAutoCountry() {
if (intlTelInput.autoCountry) {
this.#handleAutoCountryLoaded();
return;
}
this.#ui.setLoading(true);
if (intlTelInput.startedLoadingAutoCountry) {
return;
}
intlTelInput.startedLoadingAutoCountry = true;
if (typeof this.#options.geoIpLookup === "function") {
let timeoutId;
try {
const iso2 = await Promise.race([
this.#options.geoIpLookup(),
new Promise((_, reject) => {
timeoutId = setTimeout(
() => reject(new Error("intl-tel-input: geoIpLookup timed out after 10s")),
1e4
);
})
]);
const iso2Lower = typeof iso2 === "string" ? iso2.toLowerCase() : "";
if (!isIso2(iso2Lower)) {
intlTelInput.startedLoadingAutoCountry = false;
_Iti.forEachInstance("handleAutoCountryFailure");
return;
}
intlTelInput.autoCountry = iso2Lower;
setTimeout(() => _Iti.forEachInstance("handleAutoCountryLoaded"));
} catch {
intlTelInput.startedLoadingAutoCountry = false;
_Iti.forEachInstance("handleAutoCountryFailure");
} finally {
if (timeoutId !== void 0) {
clearTimeout(timeoutId);
}
}
}
}
#openDropdownWithPlus() {
this.#openDropdown();
this.#ui.prefillSearchWithPlus();
}
//* Delete the character just typed (the one immediately before the caret). Used by Android workarounds where we can't preventDefault on keydown.
#removeJustTypedChar(inputValue) {
const currentCaretPos = this.#ui.telInputEl.selectionStart || 0;
const valueBeforeCaret = inputValue.substring(0, currentCaretPos - 1);
const valueAfterCaret = inputValue.substring(currentCaretPos);
this.#setTelInputValue(valueBeforeCaret + valueAfterCaret);
return currentCaretPos - 1;
}
//* Initialize the tel input listeners.
#bindAllTelInputListeners() {
this.#bindInputListener();
this.#bindKeydownListener();
this.#bindStrictPasteListener();
}
//* Android workaround for handling plus when separateDialCode enabled (as impossible to handle with keydown/keyup, for which e.key always returns "Unidentified", see https://stackoverflow.com/q/59584061/217866)
#handleAndroidPlusKey(inputValue) {
this.#removeJustTypedChar(inputValue);
this.#openDropdownWithPlus();
}
//* Android strictMode workaround: the keydown-based filter can't block these because e.key is "Unidentified" on Android virtual keyboards, so strip them here on input.
#handleAndroidStrictReject(inputValue, rejectedInput) {
const newCaretPos = this.#removeJustTypedChar(inputValue);
this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
this.#ui.playStrictRejectAnimation();
this.#dispatchEvent(EVENTS.STRICT_REJECT, {
source: "key",
rejectedInput,
reason: "invalid"
});
}
//* Format the input value using libphonenumber's AYT formatter, preserving caret position (called after an input event).
#formatAsYouType(inputValue, isDeleteForwards) {
const currentCaretPos = this.#ui.telInputEl.selectionStart || 0;
const valueBeforeCaret = inputValue.substring(0, currentCaretPos);
const relevantCharsBeforeCaret = valueBeforeCaret.replace(
REGEX.NON_PLUS_NUMERIC_GLOBAL,
""
).length;
const fullNumber = this.#getFullNumber();
const formattedValue = formatNumberAsYouType(
fullNumber,
inputValue,
intlTelInput.utils,
this.#selectedCountry,
this.#options.separateDialCode
);
const newCaretPos = computeNewCaretPosition(
relevantCharsBeforeCaret,
formattedValue,
currentCaretPos,
isDeleteForwards
);
this.#setTelInputValue(formattedValue);
this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
}
//* If separateDialCode AND typed dial code (e.g. from paste or autofill, or from typing a dial code when countrySearch disabled), then remove the typed dial code.
//* Only strip when a full dial code is actually present — otherwise a lone typed "+" (or partial prefix) would get erased.
#stripTypedDialCode(inputValue) {
if (inputValue.startsWith("+") && this.#selectedCountry && this.#getDialCode(inputValue)) {
const cleanNumber = stripSeparateDialCode(
inputValue,
true,
true,
this.#selectedCountry
);
this.#setTelInputValue(cleanNumber);
}
}
#bindInputListener() {
this.#userOverrideFormatting = REGEX.ALPHA_UNICODE.test(
this.#getTelInputValue()
);
this.#ui.telInputEl.addEventListener(
"input",
this.#handleInputEvent,
{
signal: this.#abortController.signal
}
);
}
//* On input event: (1) Update selected country, (2) Format-as-you-type.
//* Note that this fires AFTER the input is updated.
#handleInputEvent = (e) => {
const {
strictMode,
formatAsYouType,
separateDialCode,
allowDropdown,
countrySearch
} = this.#options;
const detail = e?.detail;
if (detail?.["isCountryChange"]) {
return;
}
let inputValue = this.#getTelInputValue();
const isPaste = e?.inputType === INPUT_TYPES.PASTE;
const isStrictPaste = strictMode && isPaste;
if (this.#isAndroid && !isPaste && e?.data === "+" && separateDialCode && allowDropdown && countrySearch) {
this.#handleAndroidPlusKey(inputValue);
return;
}
if (this.#isAndroid && !isPaste && strictMode && (e?.data === " " || e?.data === "-" || e?.data === ".")) {
this.#handleAndroidStrictReject(inputValue, e.data);
return;
}
if (isStrictPaste) {
const didRejectPaste = this.#handleStrictPasteInputEvent();
if (didRejectPaste) {
return;
}
inputValue = this.#getTelInputValue();
}
if (this.#updateCountryFromNumber(inputValue)) {
this.#dispatchCountryChangeEvent();
}
const isFormattingChar = !isStrictPaste && e?.data && REGEX.NON_PLUS_NUMERIC.test(e.data);
const isNonStrictPaste = isPaste && inputValue && !strictMode;
if (isFormattingChar || isNonStrictPaste) {
this.#userOverrideFormatting = true;
} else if (!REGEX.NON_PLUS_NUMERIC.test(inputValue)) {
this.#userOverrideFormatting = false;
}
if (formatAsYouType && !this.#userOverrideFormatting && !detail?.["isSetNumber"] && this.#numerals.isAscii()) {
this.#formatAsYouType(
inputValue,
e?.inputType === INPUT_TYPES.DELETE_FORWARD
);
}
if (separateDialCode) {
this.#stripTypedDialCode(inputValue);
}
};
#bindKeydownListener() {
const { strictMode, separateDialCode } = this.#options;
if (!strictMode && !separateDialCode) {
return;
}
this.#ui.telInputEl.addEventListener("keydown", this.#handleKeydownEvent, {
signal: this.#abortController.signal
});
}
//* On keydown event: (1) if strictMode then prevent invalid characters, (2) if separateDialCode then handle plus key
//* Note that this fires BEFORE the input is updated.
#handleKeydownEvent = (e) => {
const { strictMode, separateDialCode, allowDropdown, countrySearch } = this.#options;
if (!e.key || e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) {
return;
}
if (separateDialCode && allowDropdown && countrySearch && e.key === "+") {
e.preventDefault();
this.#openDropdownWithPlus();
return;
}
if (!strictMode) {
return;
}
const inputValue = this.#getTelInputValue();
const alreadyHasPlus = inputValue.startsWith("+");
const isInitialPlus = !alreadyHasPlus && this.#ui.telInputEl.selectionStart === 0 && e.key === "+";
const normalisedKey = this.#numerals.normalise(e.key);
const isNumeric = /^[0-9]$/.test(normalisedKey);
const isAllowedChar = separateDialCode ? isNumeric : isInitialPlus || isNumeric;
const input = this.#ui.telInputEl;
const selStart = input.selectionStart;
const selEnd = input.selectionEnd;
const before = inputValue.slice(0, selStart ?? void 0);
const after = inputValue.slice(selEnd ?? void 0);
const newValue = before + normalisedKey + after;
const newFullNumber = this.#buildFullNumber(newValue);
let hasExceededMaxLength = false;
if (intlTelInput.utils && this.#maxCoreNumberLength) {
const coreNumber = intlTelInput.utils.getCoreNumber(
newFullNumber,
this.#selectedCountry?.iso2
);
hasExceededMaxLength = coreNumber.length > this.#maxCoreNumberLength;
}
const newCountry = this.#resolveCountryChangeFromNumber(newFullNumber);
const isChangingDialCode = newCountry !== null;
if (!isAllowedChar || hasExceededMaxLength && !isChangingDialCode && !isInitialPlus) {
this.#ui.playStrictRejectAnimation();
this.#dispatchEvent(EVENTS.STRICT_REJECT, {
source: "key",
rejectedInput: e.key,
reason: !isAllowedChar ? "invalid" : "max-length"
});
e.preventDefault();
}
};
#bindStrictPasteListener() {
if (!this.#options.strictMode) {
return;
}
this.#ui.telInputEl.addEventListener("paste", this.#handleStrictPasteEvent, {
signal: this.#abortController.signal
});
}
// In strict mode, remember paste details before the browser inserts the pasted text.
// The actual sanitisation runs on the following input event so native paste stays enabled.
#handleStrictPasteEvent = (e) => {
const input = this.#ui.telInputEl;
const inputValue = this.#getTelInputValue();
this.#strictPasteSnapshot = {
pastedRaw: e.clipboardData?.getData("text") ?? "",
value: inputValue,
selectionStart: input.selectionStart ?? inputValue.length,
selectionEnd: input.selectionEnd ?? inputValue.length
};
};
// Handle paste input events when strictMode is enabled by sanitising the pasted content after
// the browser inserts it, and rejecting it entirely if it would result in an invalid number.
#handleStrictPasteInputEvent() {
const input = this.#ui.telInputEl;
const pasteSnapshot = this.#strictPasteSnapshot;
this.#strictPasteSnapshot = null;
if (!pasteSnapshot) {
return false;
}
const pastedRaw = pasteSnapshot.pastedRaw;
const originalValue = pasteSnapshot.value;
const selStart = pasteSnapshot.selectionStart;
const selEnd = pasteSnapshot.selectionEnd;
const before = originalValue.slice(0, selStart);
const after = originalValue.slice(selEnd);
const iso2 = this.#selectedCountry?.iso2;
const pasted = this.#numerals.normalise(pastedRaw);
const initialCharSelected = selStart === 0 && selEnd > 0;
const allowLeadingPlus = !originalValue.startsWith("+") || initialCharSelected;
const allowedChars = pasted.replace(REGEX.NON_PLUS_NUMERIC_GLOBAL, "");
const hasLeadingPlus = allowedChars.startsWith("+");
const numerics = allowedChars.replace(/\+/g, "");
const sanitised = hasLeadingPlus && allowLeadingPlus ? `+${numerics}` : numerics;
let newValue = before + sanitised + after;
let rejectReason = sanitised !== pasted ? "invalid" : null;
if (newValue.length > 30) {
this.#ui.playStrictRejectAnimation();
this.#dispatchEvent(EVENTS.STRICT_REJECT, {
source: "paste",
rejectedInput: pastedRaw,
reason: "max-length"
});
this.#restoreValueBeforeStrictPaste(pasteSnapshot);
return true;
}
if (newValue.length > 5 && intlTelInput.utils) {
let coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
while (coreNumber.length === 0 && newValue.length > 0) {
newValue = newValue.slice(0, -1);
coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
}
if (!coreNumber) {
this.#ui.playStrictRejectAnimation();
this.#dispatchEvent(EVENTS.STRICT_REJECT, {
source: "paste",
rejectedInput: pastedRaw,
reason: "max-length"
});
this.#restoreValueBeforeStrictPaste(pasteSnapshot);
return true;
}
if (this.#maxCoreNumberLength && coreNumber.length > this.#maxCoreNumberLength) {
if (selEnd === originalValue.length) {
const trimLength = coreNumber.length - this.#maxCoreNumberLength;
newValue = newValue.slice(0, newValue.length - trimLength);
rejectReason = "max-length";
} else {
this.#ui.playStrictRejectAnimation();
this.#dispatchEvent(EVENTS.STRICT_REJECT, {
source: "paste",
rejectedInput: pastedRaw,
reason: "max-length"
});
this.#restoreValueBeforeStrictPaste(pasteSnapshot);
return true;
}
}
}
this.#setTelInputValue(newValue);
const caretPos = selStart + sanitised.length;
input.setSelectionRange(caretPos, caretPos);
if (rejectReason) {
if (pasted.length > 0 && sanitised.length === 0) {
this.#ui.playStrictRejectAnimation();
}
this.#dispatchEvent(EVENTS.STRICT_REJECT, {
source: "paste",
rejectedInput: pastedRaw,
reason: rejectReason
});
}
return false;
}
#restoreValueBeforeStrictPaste(pasteSnapshot) {
this.#setTelInputValue(pasteSnapshot.value);
this.#ui.telInputEl.setSelectionRange(
pasteSnapshot.selectionStart,
pasteSnapshot.selectionEnd
);
}
//* Adhere to the input's maxlength attr.
#truncateToMaxLength(number) {
const max = Number(this.#ui.telInputEl.getAttribute("maxlength"));
return max && number.length > max ? number.substring(0, max) : number;
}
//* Trigger a custom event on the input (typed via ItiEventMap).
#dispatchEvent(name, detailProps = {}) {
const e = new CustomEvent(name, {
bubbles: true,
cancelable: true,
detail: detailProps
});
this.#ui.telInputEl.dispatchEvent(e);
}
//* Open the dropdown. Bail if already open — otherwise the existing AbortController gets overwritten
//* and its listeners leak. Reachable via openDropdownWithPlus when dropdownAlwaysOpen is set
#openDropdown() {
if (this.#ui.isDropdownOpen()) {
return;
}
this.#ui.openDropdown(
(li) => this.#selectListItem(li),
() => this.#closeDropdown()
);
this.#dispatchEvent(EVENTS.OPEN_COUNTRY_DROPDOWN);
}
//* Update the input's value to the given number (format first if possible)
//* NOTE: this is called from setInitialState, handleUtilsLoaded and setNumber.
#updateValueFromNumber(fullNumber) {
const { formatOnDisplay, nationalMode, separateDialCode } = this.#options;
let number = fullNumber;
if (formatOnDisplay && intlTelInput.utils && this.#selectedCountry) {
const isRegionless = hasRegionlessDialCode(fullNumber);
const useNational = nationalMode && !isRegionless || !number.startsWith("+") && !separateDialCode;
const format = useNational ? NUMBER_FORMAT.NATIONAL : NUMBER_FORMAT.INTERNATIONAL;
number = intlTelInput.utils.formatNumber(
number,
this.#selectedCountry?.iso2,
format
);
}
number = this.#prepareNumberForInput(number);
this.#setTelInputValue(number);
}
//* Check if need to select a new country based on the given number
//* Note: called from setInitialState, keyup handler, setNumber.
#updateCountryFromNumber(fullNumber) {
const iso2 = this.#resolveCountryChangeFromNumber(fullNumber);
if (iso2 !== null) {
return this.#updateSelectedCountry(iso2);
}
return false;
}
// if there is a selected country, and the number doesn't start with a dial code, then add it
#withDialCodePrefix(number) {
const dialCode = this.#selectedCountry?.dialCode;
const nationalPrefix = this.#selectedCountry?.nationalPrefix;
const alreadyHasPlus = number.startsWith("+");
if (alreadyHasPlus || !dialCode) {
return number;
}
const hasPrefix = nationalPrefix && number.startsWith(nationalPrefix) && !this.#options.separateDialCode;
const cleanNumber = hasPrefix ? number.substring(1) : number;
return `+${dialCode}${cleanNumber}`;
}
//* Get the new country iso2 (or "" for empty/globe state) based on the input number, or return null if no change.
#resolveCountryChangeFromNumber(fullNumber) {
const plusIndex = fullNumber.indexOf("+");
let number = plusIndex > 0 ? fullNumber.substring(plusIndex) : fullNumber;
const selectedIso2 = this.#selectedCountry?.iso2;
number = this.#withDialCodePrefix(number);
const dialCodeMatch = this.#getDialCode(number, true);
const numeric = getNumeric(number);
if (dialCodeMatch) {
const dialCodeMatchNumeric = getNumeric(dialCodeMatch);
const iso2Codes = this.#dialCodeToIso2Map[dialCodeMatchNumeric];
if (iso2Codes.length === 1) {
if (iso2Codes[0] === selectedIso2) {
return null;
}
return iso2Codes[0];
}
return this.#resolveCountryChangeFromMultiMatch(
iso2Codes,
dialCodeMatchNumeric,
numeric
);
} else if (number.startsWith("+") && numeric.length) {
const currentDial = this.#selectedCountry?.dialCode || "";
if (currentDial && currentDial.startsWith(numeric)) {
return null;
}
return "";
} else if ((!number || number === "+") && !selectedIso2 && this.#fallbackCountryIso2) {
return this.#fallbackCountryIso2;
}
return null;
}
//* Resolve the country when multiple countries share the matched dial code.
#resolveCountryChangeFromMultiMatch(iso2Codes, dialCodeMatchNumeric, numeric) {
const selectedIso2 = this.#selectedCountry?.iso2;
const selectedDialCode = this.#selectedCountry?.dialCode;
if (!selectedIso2 && this.#fallbackCountryIso2 && iso2Codes.includes(this.#fallbackCountryIso2)) {
return this.#fallbackCountryIso2;
}
const isRegionlessNanpNumber = selectedDialCode === DIAL_CODE.NANP && isRegionlessNanp(numeric);
if (isRegionlessNanpNumber) {
return null;
}
const areaCodes = this.#selectedCountry?.areaCodes;
const priority = this.#selectedCountry?.priority;
if (areaCodes) {
const dialCodeAreaCodes = areaCodes.map(
(areaCode) => `${selectedDialCode}${areaCode}`
);
for (const dialCodeAreaCode of dialCodeAreaCodes) {
if (numeric.startsWith(dialCodeAreaCode)) {
return null;
}
}
}
const isMainCountry = priority === 0;
const hasAreaCodesButNoneMatched = areaCodes && !isMainCountry && numeric.length > dialCodeMatchNumeric.length;
const isValidSelection = selectedIso2 && iso2Codes.includes(selectedIso2) && !hasAreaCodesButNoneMatched;
const alreadySelected = selectedIso2 === iso2Codes[0];
if (!isValidSelection && !alreadySelected) {
return iso2Codes[0];
}
return null;
}
//* Update the selected country, dial code (if separateDialCode), placeholder, title, and selected list item.
//* Note: called from setInitialState, updateCountryFromNumber, selectListItem, setCountry.
#updateSelectedCountry(iso2) {
const prevIso2 = this.#selectedCountry?.iso2 || "";
this.#selectedCountry = iso2 ? this.#countryByIso2.get(iso2) : null;
if (this.#selectedCountry) {
this.#fallbackCountryIso2 = this.#selectedCountry.iso2;
}
this.#ui.setCountry(this.#selectedCountry);
this.#updatePlaceholder();
this.#updateMaxCoreNumberLength();
return prevIso2 !== iso2;
}
//* Update the maximum valid number length for the currently selected country.
#updateMaxCoreNumberLength() {
const { strictMode, placeholderNumberType, allowedNumberTypes } = this.#options;
if (!strictMode || !intlTelInput.utils) {
return;
}
const iso2 = this.#selectedCountry?.iso2;
if (!iso2) {
this.#maxCoreNumberLength = null;
return;
}
let exampleNumber = intlTelInput.utils.getExampleNumber(
iso2,
false,
placeholderNumberType,
true
);
let validNumber = exampleNumber;
while (intlTelInput.utils.isPossibleNumber(
exampleNumber,
iso2,
allowedNumberTypes
)) {
validNumber = exampleNumber;
exampleNumber += "0";
}
const coreNumber = intlTelInput.utils.getCoreNumber(validNumber, iso2);
this.#maxCoreNumberLength = coreNumber.length;
if (iso2 === "by") {
this.#maxCoreNumberLength = coreNumber.length + 1;
}
}
//* Update the input placeholder to an example number from the currently selected country.
#updatePlaceholder() {
const {
autoPlaceholder,
placeholderNumberType,
nationalMode,
customPlaceholder
} = this.#options;
const shouldSetPlaceholder = autoPlaceholder === PLACEHOLDER_MODES.AGGRESSIVE || !this.#ui.hadInitialPlaceholder && autoPlaceholder === PLACEHOLDER_MODES.POLITE;
if (!intlTelInput.utils || !shouldSetPlaceholder) {
return;
}
let placeholder = this.#selectedCountry ? intlTelInput.utils.getExampleNumber(
this.#selectedCountry.iso2,
nationalMode,
placeholderNumberType
) : "";
placeholder = this.#prepareNumberForInput(placeholder);
if (typeof customPlaceholder === "function") {
placeholder = customPlaceholder(placeholder, this.#selectedCountry);
}
this.#ui.telInputEl.setAttribute("placeholder", placeholder);
}
//* Called when the user selects a list item from the dropdown (no-op if listItem is null).
#selectListItem(listItem) {
if (!listItem) {
return;
}
const iso2 = listItem.dataset[DATA_KEYS.ISO2];
const countryChanged = this.#updateSelectedCountry(iso2);
this.#closeDropdown();
const dialCode = listItem.dataset[DATA_KEYS.DIAL_CODE];
this.#updateDialCode(dialCode);
if (this.#options.formatOnDisplay) {
const inputValue = this.#getTelInputValue();
this.#updateValueFromNumber(inputValue);
}
this.#ui.telInputEl.focus();
if (countryChanged) {
this.#dispatchCountryChangeEvent();
this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
}
}
//* Close the dropdown and unbind any listeners.
#closeDropdown(isDestroy) {
if (!this.#ui.isDropdownOpen() || this.#options.dropdownAlwaysOpen && !isDestroy) {
return;
}
this.#ui.closeDropdown();
this.#dispatchEvent(EVENTS.CLOSE_COUNTRY_DROPDOWN);
}
//* Replace any existing dial code with the new one
//* Note: called from selectListItem and setCountry
#updateDialCode(newDialCodeDigits) {
const inputValue = this.#getTelInputValue();
if (!inputValue.startsWith("+")) {
return;
}
const newDialCode = `+${newDialCodeDigits}`;
const prevDialCode = this.#getDialCode(inputValue);
let newNumber;
if (prevDialCode) {
newNumber = inputValue.replace(prevDialCode, newDialCode);
} else {
newNumber = newDialCode;
}
this.#setTelInputValue(newNumber);
}
//* Try and extract a valid international dial code from a full telephone number.
//* Note: returns the raw string inc plus character and any whitespace/dots etc.
#getDialCode(number, includeAreaCode) {
if (!number.startsWith("+")) {
return "";
}
let dialCode = "";
let numericChars = "";
let foundBaseDialCode = false;
for (let i = 0; i < number.length; i++) {
const c = number.charAt(i);
if (!/[0-9]/.test(c)) {
continue;
}
numericChars += c;
const hasMapEntry = Boolean(this.#dialCodeToIso2Map[numericChars]);
if (!hasMapEntry) {
break;
}
if (this.#dialCodes.has(numericChars)) {
dialCode = number.substring(0, i + 1);
foundBaseDialCode = true;
if (!includeAreaCode) {
break;
}
} else if (includeAreaCode && foundBaseDialCode) {
dialCode = number.substring(0, i + 1);
}
if (numericChars.length === this.#dialCodeMaxLength) {
break;
}
}
return dialCode;
}
//* Build a full number from an already-normalised value, adding the dial code if separateDialCode is enabled.
#buildFullNumber(value) {
const dialCode = this.#selectedCountry?.dialCode;
const numericValue = getNumeric(value);
const usePrefix = this.#options.separateDialCode && !value.startsWith("+") && dialCode && numericValue;
return (usePrefix ? `+${dialCode}` : "") + value;
}
//* Get the input value as a full number, adding the dial code if separateDialCode is enabled.
#getFullNumber() {
const value = this.#getTelInputValue();
return this.#buildFullNumber(value);
}
//* Remove the dial code if separateDialCode is enabled also cap the length if the input has a maxlength attribute
#prepareNumberForInput(fullNumber) {
const hasValidDialCode = Boolean(this.#getDialCode(fullNumber));
const number = stripSeparateDialCode(
fullNumber,
hasValidDialCode,
this.#options.separateDialCode,
this.#selectedCountry
);
return this.#truncateToMaxLength(number);
}
//* Dispatch the 'countrychange' event.
#dispatchCountryChangeEvent() {
this.#dispatchEvent(EVENTS.COUNTRY_CHANGE, this.#selectedCountry ?? null);
}
//**************************
//* INTERNAL METHODS
//**************************
//* Called when the geoip call returns.
#handleAutoCountryLoaded() {
if (!this.#autoCountryDeferred || !intlTelInput.autoCountry) {
return;
}
if (!this.#isActive) {
this.#autoCountryDeferred.resolve();
return;
}
if (this.#ui.isLoading()) {
this.setCountry(intlTelInput.autoCountry);
} else {
this.#fallbackCountryIso2 = intlTelInput.autoCountry;
}
this.#ui.setLoading(false);
this.#autoCountryDeferred.resolve();
}
//* Called when the geoip call fails or times out.
#handleAutoCountryFailure() {
if (!this.#isActive) {
this.#autoCountryDeferred?.reject();
return;
}
this.#setInitialState(true);
this.#ui.setLoading(false);
this.#autoCountryDeferred?.reject();
}
//* Called when the utils request completes.
#handleUtilsLoaded() {
if (!this.#isActive) {
this.#utilsDeferred?.resolve();
return;
}
if (!intlTelInput.utils) {
this.#utilsDeferred?.resolve();
return;
}
const inputValue = this.#getTelInputValue();
if (inputValue) {
this.#updateValueFromNumber(inputValue);
}
if (this.#selectedCountry) {
this.#updatePlaceholder();
this.#updateMaxCoreNumberLength();
}
this.#utilsDeferred?.resolve();
}
//* Called when the utils request fails or times out.
#handleUtilsFailure(error) {
if (!this.#isActive) {
this.#utilsDeferred?.reject(error);
return;
}
this.#utilsDeferred?.reject(error);
}
//********************
//* PUBLIC METHODS
//********************
//* Remove core library.
destroy() {
if (!this.#isActive) {
return;
}
this.#isActive = false;
if (this.#options.allowDropdown) {
this.#closeDropdown(true);
}
this.#abortController.abort();
this.#ui.destroy();
intlTelInput.instances.delete(String(this.id));
}
// check if the instance is still valid (not destroyed)
isActive() {
return this.#isActive;
}
//* Get the extension from the current number.
getExtension() {
if (!this.#isActive) {
return "";
}
ensureUtils("getExtension");
return intlTelInput.utils.getExtension(
this.#getFullNumber(),
this.#selectedCountry?.iso2
);
}
//* Format the number to the given format (defaults to "E164").
getNumber(format) {
if (!this.#isActive) {
return "";
}
ensureUtils("getNumber");
const iso2 = this.#selectedCountry?.iso2;
const fullNumber = this.#getFullNumber();
const formattedNumber = intlTelInput.utils.formatNumber(
fullNumber,
iso2,
format
);
return this.#numerals.denormalise(formattedNumber);
}
//* Get the type of the entered number e.g. "FIXED_LINE" / "MOBILE", or null if it can't be determined / instance is destroyed.
getNumberType() {
if (!this.#isActive) {
return null;
}
ensureUtils("getNumberType");
return intlTelInput.utils.getNumberType(
this.#getFullNumber(),
this.#selectedCountry?.iso2
);
}
//* Get the country data for the currently selected country.
getSelectedCountryData() {
return this.#selectedCountry ?? null;
}
//* Get the validation error e.g. "TOO_SHORT" / "TOO_LONG", or null if it can't be determined / instance is destroyed.
getValidationError() {
if (!this.#isActive) {
return null;
}
ensureUtils("getValidationError");
const iso2 = this.#selectedCountry?.iso2;
return intlTelInput.utils.getValidationError(this.#getFullNumber(), iso2);
}
//* Validate the input value using number length only
isValidNumber() {
if (!this.#isActive) {
return null;
}
ensureUtils("isValidNumber");
const dialCode = this.#selectedCountry?.dialCode;
const iso2 = this.#selectedCountry?.iso2;
const number = this.#getFullNumber();
const coreNumber = intlTelInput.utils.getCoreNumber(number, iso2);
if (coreNumber) {
if (dialCode === UK.DIAL_CODE) {
if (coreNumber[0] === UK.MOBILE_PREFIX && coreNumber.length !== UK.MOBILE_CORE_LENGTH) {
return false;
}
}
const hasAlphaChar = REGEX.ALPHA_UNICODE.test(number);
if (!hasAlphaChar && dialCode) {
const nationalPortion = number.startsWith("+") ? number.slice(1 + dialCode.length) : number;
const nationalDigitCount = getNumeric(nationalPortion).length;
if (coreNumber.length > nationalDigitCount) {
return false;
}
}
}
return this.#validateNumber("possible");
}
//* Validate the input value with precise validation
isValidNumberPrecise() {
if (!this.#isActive) {
return null;
}
ensureUtils("isValidNumberPrecise");
return this.#validateNumber("precise");
}
//* Shared internal validation logic to handle alpha character extension rules.
#validateNumber(mode) {
const { allowNumberExtensions, allowPhonewords, allowedNumberTypes } = this.#options;
const iso2 = this.#selectedCountry?.iso2;
const value = this.#getFullNumber();
if (!this.#selectedCountry && !hasRegionlessDialCode(value)) {
return false;
}
const check = mode === "precise" ? intlTelInput.utils.isValidNumber : intlTelInput.utils.isPossibleNumber;
if (!check(value, iso2, allowedNumberTypes)) {
return false;
}
if (REGEX.ALPHA_UNICODE.test(value)) {
const hasExtension = Boolean(
intlTelInput.utils.getExtension(value, iso2)
);
return hasExtension ? allowNumberExtensions : allowPhonewords;
}
return true;
}
//* Update the selected country, and update the input value accordingly.
setCountry(iso2) {
if (!this.#isActive) {
return;
}
const iso2Lower = iso2?.toLowerCase();
if (!isIso2(iso2Lower)) {
throw new Error(`Invalid iso2 code: '${iso2Lower}'`);
}
const currentCountry = this.#selectedCountry?.iso2;
const isCountryChange = iso2 && iso2Lower !== currentCountry || !iso2 && currentCountry;
if (!isCountryChange) {
return;
}
this.#updateSelectedCountry(iso2Lower);
this.#updateDialCode(this.#selectedCountry?.dialCode || "");
if (this.#options.formatOnDisplay) {
const inputValue = this.#getTelInputValue();
this.#updateValueFromNumber(inputValue);
}
this.#dispatchCountryChangeEvent();
this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
}
//* Set the input value and update the country.
setNumber(number) {
if (!this.#isActive) {
return;
}
const normalisedNumber = this.#numerals.normalise(number);
const countryChanged = this.#updateCountryFromNumber(normalisedNumber);
this.#updateValueFromNumber(normalisedNumber);
if (countryChanged) {
this.#dispatchCountryChangeEvent();
}
this.#dispatchEvent(EVENTS.INPUT, { isSetNumber: true });
}
//* Set the placeholder number type
setPlaceholderNumberType(type) {
if (!this.#isActive) {
return;
}
this.#options.placeholderNumberType = type;
this.#updatePlaceholder();
}
// Set the disabled state of the input and dropdown.
setDisabled(disabled) {
if (!this.#isActive) {
return;
}
this.#ui.setDisabled(disabled);
}
// Set the readonly state of the input and dropdown.
setReadonly(readonly) {
if (!this.#isActive) {
return;
}
this.#ui.setReadonly(readonly);
}
//********************
//* STATIC METHODS
//********************
// Internal instance notification used by utils/geoip loaders.
// Kept public so module-level helpers (e.g. attachUtils) can call it, while still allowing
// access to private instance methods.
static forEachInstance(method, ...args) {
const values = [...intlTelInput.instances.values()];
const arg = args[0];
values.forEach((instance) => {
if (!(instance instanceof _Iti)) {
return;
}
switch (method) {
case "handleUtilsLoaded":
instance.#handleUtilsLoaded();
break;
case "handleUtilsFailure":
instance.#handleUtilsFailure(arg);
break;
case "handleAutoCountryLoaded":
instance.#handleAutoCountryLoaded();
break;
case "handleAutoCountryFailure":
instance.#handleAutoCountryFailure();
break;
}
});
}
};
var attachUtils = async (source) => {
if (intlTelInput.utils || intlTelInput.startedLoadingUtils) {
return null;
}
if (typeof source !== "function") {
throw new TypeError(
`The argument passed to attachUtils must be a function that returns a promise for the utils module, not ${typeof source}`
);
}
intlTelInput.startedLoadingUtils = true;
try {
const module = await source();
const utils = module?.default;
if (!utils || typeof utils !== "object") {
throw new TypeError(
"The loader function passed to attachUtils did not resolve to a module object with utils as its default export."
);
}
intlTelInput.utils = utils;
Iti.forEachInstance("handleUtilsLoaded");
return true;
} catch (error) {
Iti.forEachInstance("handleUtilsFailure", error);
throw error;
}
};
var intlTelInput = Object.assign(
(input, options) => {
const iti = new Iti(input, options);
intlTelInput.instances.set(String(iti.id), iti);
input.iti = iti;
return iti;
},
{
defaults,
//* Using a static var like this allows us to mock it in the tests.
documentReady: () => document.readyState === "complete",
//* Get the country data object.
getCountryData: () => data_default,
//* A getter for the core library instance.
getInstance: (input) => {
const id = input.dataset[DATA_KEYS.INSTANCE_ID];
return id ? intlTelInput.instances.get(id) ?? null : null;
},
//* A map from instance ID to instance object.
instances: /* @__PURE__ */ new Map(),
attachUtils,
startedLoadingUtils: false,
startedLoadingAutoCountry: false,
version: "28.1.0",
NUMBER_FORMAT,
NUMBER_TYPE,
VALIDATION_ERROR
}
);
var intlTelInput_default = intlTelInput;
export {
Iti,
NUMBER_FORMAT,
NUMBER_TYPE,
VALIDATION_ERROR,
intlTelInput_default as default
};