PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / assets / js / intlTelInput-vanilla.js
wp-2fa / includes / assets / js Last commit date
customize-styles.js 5 days ago exclude-self-prompt.js 5 days ago intlTelInput-vanilla.js 5 days ago policies-hash-state.js 5 days ago save-policies-new.js 5 days ago save-settings-new.js 5 days ago settings-design-logic.js 5 days ago settings-hash-state.js 5 days ago test-email-new.js 5 days ago wizard-new.js 5 days ago
intlTelInput-vanilla.js
635 lines
1 /**
2 * International Telephone Input – Vanilla JS
3 *
4 * Lightweight, jQuery-free replacement for intlTelInput v17.
5 * Reuses the existing flags.png sprite sheet and phone-picker.css
6 * (`.iti__*` class naming convention).
7 *
8 * Usage:
9 * var instance = window.intlTelInputVanilla(inputElement, {
10 * initialCountry: 'auto',
11 * geoIpLookup: function(callback){ ... callback('US'); },
12 * flagsUrl: '/path/to/flags.png',
13 * flagsRetinaUrl: '/path/to/flags@2x.png',
14 * placeholderNumberType: 'MOBILE'
15 * });
16 *
17 * instance.getNumber() // → '+14155552671'
18 * instance.getSelectedCountryData() // → { name, iso2, dialCode, ... }
19 * instance.isValidNumber() // → true/false
20 * instance.getValidationError() // → 0–5 error code
21 * instance.destroy()
22 *
23 * @package wp2fa
24 * @since 4.0.0
25 */
26 (function () {
27 'use strict';
28
29 /* ------------------------------------------------------------------ */
30 /* Country data (same as intlTelInput v17) */
31 /* [Name, iso2, dialCode, priority?, areaCodes?] */
32 /* ------------------------------------------------------------------ */
33 var rawCountries = [
34 ["Afghanistan (‫افغانستان‬‎)","af","93"],["Albania (Shqipëri)","al","355"],["Algeria (‫الجزائر‬‎)","dz","213"],["American Samoa","as","1",5,["684"]],["Andorra","ad","376"],["Angola","ao","244"],["Anguilla","ai","1",6,["264"]],["Antigua and Barbuda","ag","1",7,["268"]],["Argentina","ar","54"],["Armenia (Հայաստան)","am","374"],["Aruba","aw","297"],["Ascension Island","ac","247"],["Australia","au","61",0],["Austria (Österreich)","at","43"],["Azerbaijan (Azərbaycan)","az","994"],["Bahamas","bs","1",8,["242"]],["Bahrain (‫البحرين‬‎)","bh","973"],["Bangladesh (বাংলাদেশ)","bd","880"],["Barbados","bb","1",9,["246"]],["Belarus (Беларусь)","by","375"],["Belgium (België)","be","32"],["Belize","bz","501"],["Benin (Bénin)","bj","229"],["Bermuda","bm","1",10,["441"]],["Bhutan (འབྲུག)","bt","975"],["Bolivia","bo","591"],["Bosnia and Herzegovina (Босна и Херцеговина)","ba","387"],["Botswana","bw","267"],["Brazil (Brasil)","br","55"],["British Indian Ocean Territory","io","246"],["British Virgin Islands","vg","1",11,["284"]],["Brunei","bn","673"],["Bulgaria (България)","bg","359"],["Burkina Faso","bf","226"],["Burundi (Uburundi)","bi","257"],["Cambodia (កម្ពុជា)","kh","855"],["Cameroon (Cameroun)","cm","237"],["Canada","ca","1",1,["204","226","236","249","250","289","306","343","365","387","403","416","418","431","437","438","450","506","514","519","548","579","581","587","604","613","639","647","672","705","709","742","778","780","782","807","819","825","867","873","902","905"]],["Cape Verde (Kabu Verdi)","cv","238"],["Caribbean Netherlands","bq","599",1,["3","4","7"]],["Cayman Islands","ky","1",12,["345"]],["Central African Republic (République centrafricaine)","cf","236"],["Chad (Tchad)","td","235"],["Chile","cl","56"],["China (中国)","cn","86"],["Christmas Island","cx","61",2,["89164"]],["Cocos (Keeling) Islands","cc","61",1,["89162"]],["Colombia","co","57"],["Comoros (‫جزر الق�
35 ر‬‎)","km","269"],["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)","cd","243"],["Congo (Republic) (Congo-Brazzaville)","cg","242"],["Cook Islands","ck","682"],["Costa Rica","cr","506"],["Côte d'Ivoire","ci","225"],["Croatia (Hrvatska)","hr","385"],["Cuba","cu","53"],["Curaçao","cw","599",0],["Cyprus (Κύπρος)","cy","357"],["Czech Republic (Česká republika)","cz","420"],["Denmark (Danmark)","dk","45"],["Djibouti","dj","253"],["Dominica","dm","1",13,["767"]],["Dominican Republic (República Dominicana)","do","1",2,["809","829","849"]],["Ecuador","ec","593"],["Egypt (‫�
36 صر‬‎)","eg","20"],["El Salvador","sv","503"],["Equatorial Guinea (Guinea Ecuatorial)","gq","240"],["Eritrea","er","291"],["Estonia (Eesti)","ee","372"],["Eswatini","sz","268"],["Ethiopia","et","251"],["Falkland Islands (Islas Malvinas)","fk","500"],["Faroe Islands (Føroyar)","fo","298"],["Fiji","fj","679"],["Finland (Suomi)","fi","358",0],["France","fr","33"],["French Guiana (Guyane française)","gf","594"],["French Polynesia (Polynésie française)","pf","689"],["Gabon","ga","241"],["Gambia","gm","220"],["Georgia (საქართველო)","ge","995"],["Germany (Deutschland)","de","49"],["Ghana (Gaana)","gh","233"],["Gibraltar","gi","350"],["Greece (Ελλάδα)","gr","30"],["Greenland (Kalaallit Nunaat)","gl","299"],["Grenada","gd","1",14,["473"]],["Guadeloupe","gp","590",0],["Guam","gu","1",15,["671"]],["Guatemala","gt","502"],["Guernsey","gg","44",1,["1481","7781","7839","7911"]],["Guinea (Guinée)","gn","224"],["Guinea-Bissau (Guiné Bissau)","gw","245"],["Guyana","gy","592"],["Haiti","ht","509"],["Honduras","hn","504"],["Hong Kong (香港)","hk","852"],["Hungary (Magyarország)","hu","36"],["Iceland (Ísland)","is","354"],["India (भारत)","in","91"],["Indonesia","id","62"],["Iran (‫ایران‬‎)","ir","98"],["Iraq (‫العراق‬‎)","iq","964"],["Ireland","ie","353"],["Isle of Man","im","44",2,["1624","74576","7524","7924","7624"]],["Israel (‫ישראל‬‎)","il","972"],["Italy (Italia)","it","39",0],["Jamaica","jm","1",4,["876","658"]],["Japan (日本)","jp","81"],["Jersey","je","44",3,["1534","7509","7700","7797","7829","7937"]],["Jordan (‫الأردن‬‎)","jo","962"],["Kazakhstan (Каза�
37 стан)","kz","7",1,["33","7"]],["Kenya","ke","254"],["Kiribati","ki","686"],["Kosovo","xk","383"],["Kuwait (‫الكويت‬‎)","kw","965"],["Kyrgyzstan (Кыргызстан)","kg","996"],["Laos (ລາວ)","la","856"],["Latvia (Latvija)","lv","371"],["Lebanon (‫لبنان‬‎)","lb","961"],["Lesotho","ls","266"],["Liberia","lr","231"],["Libya (‫ليبيا‬‎)","ly","218"],["Liechtenstein","li","423"],["Lithuania (Lietuva)","lt","370"],["Luxembourg","lu","352"],["Macau (澳門)","mo","853"],["North Macedonia (Македонија)","mk","389"],["Madagascar (Madagasikara)","mg","261"],["Malawi","mw","265"],["Malaysia","my","60"],["Maldives","mv","960"],["Mali","ml","223"],["Malta","mt","356"],["Marshall Islands","mh","692"],["Martinique","mq","596"],["Mauritania (‫�
38 وريتانيا‬‎)","mr","222"],["Mauritius (Moris)","mu","230"],["Mayotte","yt","262",1,["269","639"]],["Mexico (México)","mx","52"],["Micronesia","fm","691"],["Moldova (Republica Moldova)","md","373"],["Monaco","mc","377"],["Mongolia (Монгол)","mn","976"],["Montenegro (Crna Gora)","me","382"],["Montserrat","ms","1",16,["664"]],["Morocco (‫ال�
39 غرب‬‎)","ma","212",0],["Mozambique (Moçambique)","mz","258"],["Myanmar (Burma) (မြန်မာ)","mm","95"],["Namibia (Namibië)","na","264"],["Nauru","nr","674"],["Nepal (नेपाल)","np","977"],["Netherlands (Nederland)","nl","31"],["New Caledonia (Nouvelle-Calédonie)","nc","687"],["New Zealand","nz","64"],["Nicaragua","ni","505"],["Niger (Nijar)","ne","227"],["Nigeria","ng","234"],["Niue","nu","683"],["Norfolk Island","nf","672"],["North Korea (조선 민주주의 인민 공화국)","kp","850"],["Northern Mariana Islands","mp","1",17,["670"]],["Norway (Norge)","no","47",0],["Oman (‫عُ�
40 ان‬‎)","om","968"],["Pakistan (‫پاکستان‬‎)","pk","92"],["Palau","pw","680"],["Palestine (‫فلسطين‬‎)","ps","970"],["Panama (Panamá)","pa","507"],["Papua New Guinea","pg","675"],["Paraguay","py","595"],["Peru (Perú)","pe","51"],["Philippines","ph","63"],["Poland (Polska)","pl","48"],["Portugal","pt","351"],["Puerto Rico","pr","1",3,["787","939"]],["Qatar (‫قطر‬‎)","qa","974"],["Réunion (La Réunion)","re","262",0],["Romania (România)","ro","40"],["Russia (Россия)","ru","7",0],["Rwanda","rw","250"],["Saint Barthélemy","bl","590",1],["Saint Helena","sh","290"],["Saint Kitts and Nevis","kn","1",18,["869"]],["Saint Lucia","lc","1",19,["758"]],["Saint Martin (Saint-Martin (partie française))","mf","590",2],["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)","pm","508"],["Saint Vincent and the Grenadines","vc","1",20,["784"]],["Samoa","ws","685"],["San Marino","sm","378"],["São Tomé and Príncipe (São Tomé e Príncipe)","st","239"],["Saudi Arabia (‫ال�
41
42 لكة العربية السعودية‬‎)","sa","966"],["Senegal (Sénégal)","sn","221"],["Serbia (Србија)","rs","381"],["Seychelles","sc","248"],["Sierra Leone","sl","232"],["Singapore","sg","65"],["Sint Maarten","sx","1",21,["721"]],["Slovakia (Slovensko)","sk","421"],["Slovenia (Slovenija)","si","386"],["Solomon Islands","sb","677"],["Somalia (Soomaaliya)","so","252"],["South Africa","za","27"],["South Korea (대한민국)","kr","82"],["South Sudan (‫جنوب السودان‬‎)","ss","211"],["Spain (España)","es","34"],["Sri Lanka (ශ්‍රී ලංකාව)","lk","94"],["Sudan (‫السودان‬‎)","sd","249"],["Suriname","sr","597"],["Svalbard and Jan Mayen","sj","47",1,["79"]],["Sweden (Sverige)","se","46"],["Switzerland (Schweiz)","ch","41"],["Syria (‫سوريا‬‎)","sy","963"],["Taiwan (台灣)","tw","886"],["Tajikistan","tj","992"],["Tanzania","tz","255"],["Thailand (ไทย)","th","66"],["Timor-Leste","tl","670"],["Togo","tg","228"],["Tokelau","tk","690"],["Tonga","to","676"],["Trinidad and Tobago","tt","1",22,["868"]],["Tunisia (‫تونس‬‎)","tn","216"],["Turkey (Türkiye)","tr","90"],["Turkmenistan","tm","993"],["Turks and Caicos Islands","tc","1",23,["649"]],["Tuvalu","tv","688"],["U.S. Virgin Islands","vi","1",24,["340"]],["Uganda","ug","256"],["Ukraine (Україна)","ua","380"],["United Arab Emirates (‫الإ�
43 ارات العربية ال�
44 تحدة‬‎)","ae","971"],["United Kingdom","gb","44",0],["United States","us","1",0],["Uruguay","uy","598"],["Uzbekistan (Oʻzbekiston)","uz","998"],["Vanuatu","vu","678"],["Vatican City (Città del Vaticano)","va","39",1,["06698"]],["Venezuela","ve","58"],["Vietnam (Việt Nam)","vn","84"],["Wallis and Futuna (Wallis-et-Futuna)","wf","681"],["Western Sahara (‫الصحراء الغربية‬‎)","eh","212",1,["5288","5289"]],["Yemen (‫الي�
45 ن‬‎)","ye","967"],["Zambia","zm","260"],["Zimbabwe","zw","263"],["�
46 land Islands","ax","358",1,["18"]]
47 ];
48
49 var allCountries = rawCountries.map(function (c) {
50 return {
51 name: c[0],
52 iso2: c[1],
53 dialCode: c[2],
54 priority: c[3] || 0,
55 areaCodes: c[4] || null
56 };
57 });
58
59 /* Preferred countries shown at top of dropdown. */
60 var defaultPreferred = ['us', 'gb'];
61
62 /* ------------------------------------------------------------------ */
63 /* Validation error codes (compatible with intlTelInput utils) */
64 /* ------------------------------------------------------------------ */
65 var validationErrors = {
66 IS_POSSIBLE: 0,
67 INVALID_COUNTRY_CODE: 1,
68 TOO_SHORT: 2,
69 TOO_LONG: 3,
70 NOT_A_NUMBER: 4,
71 INVALID_LENGTH: 5
72 };
73
74 /* ------------------------------------------------------------------ */
75 /* Typical phone-number length ranges per dial code. */
76 /* This enables basic validation without loading the full */
77 /* libphonenumber utils bundle. */
78 /* ------------------------------------------------------------------ */
79 var phoneLengthRanges = {
80 '1':[10,10],'7':[10,10],'20':[10,10],'27':[9,9],'30':[10,10],
81 '31':[9,9],'32':[8,9],'33':[9,9],'34':[9,9],'36':[8,9],
82 '39':[9,11],'40':[9,9],'41':[9,9],'43':[4,13],'44':[10,10],
83 '45':[8,8],'46':[7,13],'47':[8,8],'48':[9,9],'49':[3,13],
84 '51':[8,9],'52':[10,10],'53':[8,8],'54':[6,11],'55':[10,11],
85 '56':[8,9],'57':[10,10],'58':[10,10],'60':[7,10],'61':[9,9],
86 '62':[5,12],'63':[10,10],'64':[8,10],'65':[8,8],'66':[9,9],
87 '81':[9,10],'82':[8,11],'84':[9,10],'86':[11,11],'90':[10,10],
88 '91':[10,10],'92':[10,10],'93':[9,9],'94':[9,9],'95':[7,10],
89 '98':[10,10],'211':[9,9],'212':[9,9],'213':[9,9],'216':[8,8],
90 '218':[9,10],'220':[7,7],'221':[9,9],'222':[8,8],'223':[8,8],
91 '224':[8,9],'225':[10,10],'226':[8,8],'227':[8,8],'228':[8,8],
92 '229':[8,8],'230':[7,8],'231':[7,8],'232':[8,8],'233':[9,9],
93 '234':[7,10],'235':[8,8],'236':[8,8],'237':[9,9],'238':[7,7],
94 '239':[7,7],'240':[9,9],'241':[7,8],'242':[9,9],'243':[9,9],
95 '244':[9,9],'245':[7,7],'246':[7,7],'247':[4,4],'248':[7,7],
96 '249':[9,9],'250':[9,9],'251':[9,9],'252':[7,8],'253':[8,8],
97 '254':[9,10],'255':[9,9],'256':[9,9],'257':[8,8],'258':[8,9],
98 '260':[9,9],'261':[9,10],'262':[9,9],'263':[9,9],'264':[7,10],
99 '265':[7,9],'266':[8,8],'267':[7,8],'268':[7,8],'269':[7,7],
100 '290':[4,4],'291':[7,7],'297':[7,7],'298':[6,6],'299':[6,6],
101 '350':[8,8],'351':[9,9],'352':[4,11],'353':[7,9],'354':[7,9],
102 '355':[8,9],'356':[8,8],'357':[8,8],'358':[5,12],'359':[7,9],
103 '370':[8,8],'371':[8,8],'372':[7,8],'373':[8,8],'374':[8,8],
104 '375':[9,10],'376':[6,9],'377':[8,9],'378':[6,10],'380':[9,9],
105 '381':[8,9],'382':[8,8],'383':[8,8],'385':[8,9],'386':[8,8],
106 '387':[8,8],'389':[8,8],'420':[9,9],'421':[9,9],'423':[7,9],
107 '500':[5,5],'501':[7,7],'502':[8,8],'503':[8,8],'504':[8,8],
108 '505':[8,8],'506':[8,8],'507':[7,8],'508':[6,6],'509':[8,8],
109 '590':[9,9],'591':[8,8],'592':[7,7],'593':[8,9],'594':[9,9],
110 '595':[9,9],'596':[9,9],'597':[6,7],'598':[8,8],'599':[7,7],
111 '670':[7,8],'672':[6,6],'673':[7,7],'674':[7,7],'675':[8,8],
112 '676':[5,7],'677':[7,7],'678':[5,7],'679':[7,7],'680':[7,7],
113 '681':[6,6],'682':[5,5],'683':[4,4],'685':[5,7],'686':[8,8],
114 '687':[6,6],'688':[5,6],'689':[6,6],'690':[4,4],'691':[7,7],
115 '692':[7,7],'850':[8,12],'852':[8,8],'853':[8,8],'855':[8,9],
116 '856':[8,10],'870':[9,9],'880':[10,10],'886':[9,9],'960':[7,7],
117 '961':[7,8],'962':[8,9],'963':[7,9],'964':[10,10],'965':[8,8],
118 '966':[9,9],'967':[7,9],'968':[8,8],'970':[9,9],'971':[7,9],
119 '972':[9,9],'973':[8,8],'974':[7,8],'975':[8,8],'976':[8,8],
120 '977':[10,10],'992':[9,9],'993':[8,8],'994':[9,9],'995':[9,9],
121 '996':[9,9],'998':[9,9]
122 };
123
124 /* ------------------------------------------------------------------ */
125 /* Helper: build a dial-code → country lookup. */
126 /* ------------------------------------------------------------------ */
127 var dialCodeMap = {};
128 allCountries.forEach(function (c) {
129 if (!dialCodeMap[c.dialCode]) {
130 dialCodeMap[c.dialCode] = [];
131 }
132 dialCodeMap[c.dialCode].push(c);
133 });
134
135 /* ------------------------------------------------------------------ */
136 /* IntlTelInputVanilla constructor */
137 /* ------------------------------------------------------------------ */
138 function IntlTelInputVanilla(telInput, opts) {
139 this.telInput = telInput;
140 this.options = Object.assign({
141 initialCountry: '',
142 geoIpLookup: null,
143 placeholderNumberType: 'MOBILE',
144 preferredCountries: defaultPreferred,
145 allowDropdown: true,
146 autoHideDialCode: true,
147 nationalMode: true,
148 formatOnDisplay: true
149 }, opts || {});
150
151 this.selectedCountry = null;
152 this._boundDocClick = null;
153 this._built = false;
154
155 this._init();
156 }
157
158 IntlTelInputVanilla.prototype._init = function () {
159 this._buildUI();
160 this._bindEvents();
161
162 var self = this;
163 var initial = (this.options.initialCountry || '').toLowerCase();
164
165 if (initial === 'auto' && typeof this.options.geoIpLookup === 'function') {
166 this.options.geoIpLookup(function (countryCode) {
167 self._setCountry((countryCode || 'us').toLowerCase());
168 });
169 } else {
170 this._setCountry(initial || 'us');
171 }
172 };
173
174 /* ------------------------------------------------------------------ */
175 /* Build the DOM structure around the <input>. */
176 /* ------------------------------------------------------------------ */
177 IntlTelInputVanilla.prototype._buildUI = function () {
178 if (this._built) return;
179 this._built = true;
180
181 var input = this.telInput;
182
183 /* Wrapper */
184 var wrap = document.createElement('div');
185 wrap.className = 'iti iti--allow-dropdown';
186 input.parentNode.insertBefore(wrap, input);
187 wrap.appendChild(input);
188 this.wrap = wrap;
189
190 /* Flag container (left of input) */
191 var flagContainer = document.createElement('div');
192 flagContainer.className = 'iti__flag-container';
193 wrap.insertBefore(flagContainer, input);
194
195 var selectedFlag = document.createElement('div');
196 selectedFlag.className = 'iti__selected-flag';
197 selectedFlag.setAttribute('role', 'combobox');
198 selectedFlag.setAttribute('aria-haspopup', 'listbox');
199 selectedFlag.setAttribute('aria-expanded', 'false');
200 selectedFlag.tabIndex = 0;
201 flagContainer.appendChild(selectedFlag);
202 this.selectedFlagEl = selectedFlag;
203
204 var flagEl = document.createElement('div');
205 flagEl.className = 'iti__flag';
206 selectedFlag.appendChild(flagEl);
207 this.flagEl = flagEl;
208
209 var arrow = document.createElement('div');
210 arrow.className = 'iti__arrow';
211 selectedFlag.appendChild(arrow);
212 this.arrowEl = arrow;
213
214 /* Dial-code label next to flag */
215 var dialLabel = document.createElement('span');
216 dialLabel.className = 'iti__selected-dial-code';
217 dialLabel.style.marginLeft = '6px';
218 dialLabel.style.fontSize = '13px';
219 dialLabel.style.color = '#555';
220 selectedFlag.appendChild(dialLabel);
221 this.dialLabelEl = dialLabel;
222
223 /* Country list dropdown */
224 var list = document.createElement('ul');
225 list.className = 'iti__country-list iti__hide';
226 list.setAttribute('role', 'listbox');
227 list.id = 'iti-' + Date.now() + '-list';
228 flagContainer.appendChild(list);
229 this.listEl = list;
230
231 this._populateList();
232
233 /* Adjust input padding */
234 input.style.paddingLeft = '78px';
235 };
236
237 /* ------------------------------------------------------------------ */
238 /* Populate the country-list dropdown. */
239 /* ------------------------------------------------------------------ */
240 IntlTelInputVanilla.prototype._populateList = function () {
241 var self = this;
242 var list = this.listEl;
243 var frag = document.createDocumentFragment();
244
245 /* Preferred countries at the top */
246 var preferred = this.options.preferredCountries || [];
247 var prefCountries = [];
248 preferred.forEach(function (iso) {
249 var c = self._getCountryByIso2(iso);
250 if (c) prefCountries.push(c);
251 });
252
253 if (prefCountries.length) {
254 prefCountries.forEach(function (c) {
255 frag.appendChild(self._buildCountryItem(c));
256 });
257 var divider = document.createElement('li');
258 divider.className = 'iti__divider';
259 divider.setAttribute('role', 'separator');
260 frag.appendChild(divider);
261 }
262
263 /* All countries */
264 var sorted = allCountries.slice().sort(function (a, b) {
265 return a.name.localeCompare(b.name);
266 });
267 sorted.forEach(function (c) {
268 frag.appendChild(self._buildCountryItem(c));
269 });
270
271 list.appendChild(frag);
272 };
273
274 IntlTelInputVanilla.prototype._buildCountryItem = function (country) {
275 var li = document.createElement('li');
276 li.className = 'iti__country';
277 li.setAttribute('role', 'option');
278 li.setAttribute('data-dial-code', country.dialCode);
279 li.setAttribute('data-country-code', country.iso2);
280
281 var flagBox = document.createElement('div');
282 flagBox.className = 'iti__flag-box';
283 var flag = document.createElement('div');
284 flag.className = 'iti__flag iti__' + country.iso2;
285 flagBox.appendChild(flag);
286 li.appendChild(flagBox);
287
288 var nameSpan = document.createElement('span');
289 nameSpan.className = 'iti__country-name';
290 nameSpan.textContent = country.name;
291 li.appendChild(nameSpan);
292
293 var codeSpan = document.createElement('span');
294 codeSpan.className = 'iti__dial-code';
295 codeSpan.textContent = '+' + country.dialCode;
296 li.appendChild(codeSpan);
297
298 return li;
299 };
300
301 /* ------------------------------------------------------------------ */
302 /* Event bindings */
303 /* ------------------------------------------------------------------ */
304 IntlTelInputVanilla.prototype._bindEvents = function () {
305 var self = this;
306
307 /* Toggle dropdown on flag click */
308 this.selectedFlagEl.addEventListener('click', function (e) {
309 e.stopPropagation();
310 self._toggleDropdown();
311 });
312
313 /* Keyboard on flag (Enter / Space to open, Escape to close) */
314 this.selectedFlagEl.addEventListener('keydown', function (e) {
315 if (e.key === 'Enter' || e.key === ' ') {
316 e.preventDefault();
317 self._toggleDropdown();
318 } else if (e.key === 'Escape') {
319 self._closeDropdown();
320 } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
321 e.preventDefault();
322 if (self.listEl.classList.contains('iti__hide')) {
323 self._openDropdown();
324 }
325 self._highlightNext(e.key === 'ArrowDown' ? 1 : -1);
326 }
327 });
328
329 /* Select country from list */
330 this.listEl.addEventListener('click', function (e) {
331 var li = e.target.closest('.iti__country');
332 if (!li) return;
333 var iso = li.getAttribute('data-country-code');
334 self._setCountry(iso);
335 self._closeDropdown();
336 self.telInput.focus();
337 });
338
339 /* Keyboard navigation inside dropdown */
340 this.listEl.addEventListener('keydown', function (e) {
341 if (e.key === 'Escape') {
342 self._closeDropdown();
343 self.selectedFlagEl.focus();
344 } else if (e.key === 'Enter') {
345 e.preventDefault();
346 var active = self.listEl.querySelector('.iti__highlight');
347 if (active) {
348 var iso = active.getAttribute('data-country-code');
349 self._setCountry(iso);
350 self._closeDropdown();
351 self.telInput.focus();
352 }
353 } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
354 e.preventDefault();
355 self._highlightNext(e.key === 'ArrowDown' ? 1 : -1);
356 } else if (/^[a-zA-Z]$/.test(e.key)) {
357 /* Jump to first country starting with typed letter */
358 self._jumpToLetter(e.key);
359 }
360 });
361
362 /* Close dropdown on outside click */
363 this._boundDocClick = function () {
364 self._closeDropdown();
365 };
366 document.addEventListener('click', this._boundDocClick);
367 };
368
369 /* ------------------------------------------------------------------ */
370 /* Dropdown open / close / toggle */
371 /* ------------------------------------------------------------------ */
372 IntlTelInputVanilla.prototype._toggleDropdown = function () {
373 if (this.listEl.classList.contains('iti__hide')) {
374 this._openDropdown();
375 } else {
376 this._closeDropdown();
377 }
378 };
379
380 IntlTelInputVanilla.prototype._openDropdown = function () {
381 this.listEl.classList.remove('iti__hide');
382 this.listEl.style.maxHeight = '200px';
383 this.arrowEl.classList.add('iti__arrow--up');
384 this.selectedFlagEl.setAttribute('aria-expanded', 'true');
385
386 /* Scroll to selected country */
387 if (this.selectedCountry) {
388 var active = this.listEl.querySelector('[data-country-code="' + this.selectedCountry.iso2 + '"]');
389 if (active) {
390 active.classList.add('iti__highlight');
391 active.scrollIntoView({ block: 'nearest' });
392 }
393 }
394 };
395
396 IntlTelInputVanilla.prototype._closeDropdown = function () {
397 this.listEl.classList.add('iti__hide');
398 this.arrowEl.classList.remove('iti__arrow--up');
399 this.selectedFlagEl.setAttribute('aria-expanded', 'false');
400
401 /* Clear highlights */
402 var items = this.listEl.querySelectorAll('.iti__highlight');
403 for (var i = 0; i < items.length; i++) {
404 items[i].classList.remove('iti__highlight');
405 }
406 };
407
408 IntlTelInputVanilla.prototype._highlightNext = function (dir) {
409 var items = this.listEl.querySelectorAll('.iti__country');
410 if (!items.length) return;
411 var current = this.listEl.querySelector('.iti__highlight');
412 var idx = -1;
413 if (current) {
414 for (var i = 0; i < items.length; i++) {
415 if (items[i] === current) { idx = i; break; }
416 }
417 current.classList.remove('iti__highlight');
418 }
419 idx += dir;
420 if (idx < 0) idx = items.length - 1;
421 if (idx >= items.length) idx = 0;
422 items[idx].classList.add('iti__highlight');
423 items[idx].scrollIntoView({ block: 'nearest' });
424 };
425
426 IntlTelInputVanilla.prototype._jumpToLetter = function (letter) {
427 letter = letter.toLowerCase();
428 var items = this.listEl.querySelectorAll('.iti__country');
429 for (var i = 0; i < items.length; i++) {
430 var name = items[i].querySelector('.iti__country-name');
431 if (name && name.textContent.trim().toLowerCase().charAt(0) === letter) {
432 var prev = this.listEl.querySelector('.iti__highlight');
433 if (prev) prev.classList.remove('iti__highlight');
434 items[i].classList.add('iti__highlight');
435 items[i].scrollIntoView({ block: 'nearest' });
436 break;
437 }
438 }
439 };
440
441 /* ------------------------------------------------------------------ */
442 /* Set / get selected country */
443 /* ------------------------------------------------------------------ */
444 IntlTelInputVanilla.prototype._setCountry = function (iso2) {
445 var country = this._getCountryByIso2(iso2);
446 if (!country) country = this._getCountryByIso2('us');
447 if (!country) return;
448
449 this.selectedCountry = country;
450
451 /* Update flag */
452 this.flagEl.className = 'iti__flag iti__' + country.iso2;
453 this.dialLabelEl.textContent = '+' + country.dialCode;
454
455 /* Update placeholder */
456 this.telInput.setAttribute('placeholder', this._getPlaceholder(country));
457 };
458
459 IntlTelInputVanilla.prototype._getCountryByIso2 = function (iso2) {
460 iso2 = (iso2 || '').toLowerCase();
461 for (var i = 0; i < allCountries.length; i++) {
462 if (allCountries[i].iso2 === iso2) return allCountries[i];
463 }
464 return null;
465 };
466
467 IntlTelInputVanilla.prototype._getPlaceholder = function (country) {
468 /* Build a simple placeholder like "+1 201 555 0123" */
469 var range = phoneLengthRanges[country.dialCode];
470 if (!range) return '+' + country.dialCode;
471 var len = range[0]; /* use minimum length */
472 var digits = '';
473 for (var i = 0; i < len; i++) digits += (i % 3 === 2 && i < len - 1) ? '0 ' : '0';
474 return digits.trim();
475 };
476
477 /* ------------------------------------------------------------------ */
478 /* Public API — compatible with intlTelInput v17 */
479 /* ------------------------------------------------------------------ */
480
481 /**
482 * Return the full international number in E.164 format.
483 */
484 IntlTelInputVanilla.prototype.getNumber = function () {
485 var raw = this.telInput.value.replace(/[^\d]/g, '');
486 if (!raw) return '';
487 if (!this.selectedCountry) return '+' + raw;
488 /* If the user typed the full number with country code, return as-is with + */
489 if (raw.indexOf(this.selectedCountry.dialCode) === 0) {
490 return '+' + raw;
491 }
492 return '+' + this.selectedCountry.dialCode + raw;
493 };
494
495 /**
496 * Return the selected country data object.
497 */
498 IntlTelInputVanilla.prototype.getSelectedCountryData = function () {
499 return this.selectedCountry || {};
500 };
501
502 /**
503 * Basic phone number length validation.
504 */
505 IntlTelInputVanilla.prototype.isValidNumber = function () {
506 return this.getValidationError() === validationErrors.IS_POSSIBLE;
507 };
508
509 /**
510 * Return a validation error code.
511 */
512 IntlTelInputVanilla.prototype.getValidationError = function () {
513 var raw = this.telInput.value.replace(/[^\d]/g, '');
514 if (!raw) return validationErrors.NOT_A_NUMBER;
515 if (!this.selectedCountry) return validationErrors.INVALID_COUNTRY_CODE;
516
517 var dialCode = this.selectedCountry.dialCode;
518
519 /* Strip the dial code if the user typed it */
520 var national = raw;
521 if (raw.indexOf(dialCode) === 0) {
522 national = raw.substring(dialCode.length);
523 }
524
525 if (!national) return validationErrors.TOO_SHORT;
526
527 var range = phoneLengthRanges[dialCode];
528 if (!range) {
529 /* Unknown dial code – accept anything 4-15 digits */
530 if (national.length < 4) return validationErrors.TOO_SHORT;
531 if (national.length > 15) return validationErrors.TOO_LONG;
532 return validationErrors.IS_POSSIBLE;
533 }
534
535 if (national.length < range[0]) return validationErrors.TOO_SHORT;
536 if (national.length > range[1]) return validationErrors.TOO_LONG;
537 return validationErrors.IS_POSSIBLE;
538 };
539
540 /**
541 * Programmatically set the country.
542 */
543 IntlTelInputVanilla.prototype.setCountry = function (iso2) {
544 this._setCountry(iso2);
545 };
546
547 /**
548 * Programmatically set a phone number.
549 * If the number starts with +, try to detect the country from the dial code.
550 */
551 IntlTelInputVanilla.prototype.setNumber = function (number) {
552 if (!number) {
553 this.telInput.value = '';
554 return;
555 }
556 var cleaned = number.replace(/[^\d+]/g, '');
557 if (cleaned.charAt(0) === '+') {
558 var digits = cleaned.substring(1);
559 /* Try to detect country from dial code (longest match first) */
560 for (var len = 3; len >= 1; len--) {
561 var prefix = digits.substring(0, len);
562 var countries = dialCodeMap[prefix];
563 if (countries && countries.length) {
564 this._setCountry(countries[0].iso2);
565 this.telInput.value = digits.substring(len);
566 return;
567 }
568 }
569 }
570 this.telInput.value = cleaned.replace(/^\+/, '');
571 };
572
573 /**
574 * Clean up: remove DOM wrappers and event listeners.
575 */
576 IntlTelInputVanilla.prototype.destroy = function () {
577 if (this._boundDocClick) {
578 document.removeEventListener('click', this._boundDocClick);
579 }
580 /* Unwrap */
581 if (this.wrap && this.wrap.parentNode) {
582 this.wrap.parentNode.insertBefore(this.telInput, this.wrap);
583 this.wrap.parentNode.removeChild(this.wrap);
584 }
585 this.telInput.style.paddingLeft = '';
586 };
587
588 /* ------------------------------------------------------------------ */
589 /* Geo-IP helper using ipinfo.io (vanilla fetch, no jQuery needed) */
590 /* ------------------------------------------------------------------ */
591 IntlTelInputVanilla.geoIpLookup = function (callback) {
592 var done = false;
593 function finish(code) {
594 if (done) return;
595 done = true;
596 callback(code || '');
597 }
598
599 /* Use fetch with a timeout fallback. */
600 if (typeof fetch === 'function') {
601 fetch('https://ipinfo.io/json?token=', { mode: 'cors' })
602 .then(function (r) { return r.json(); })
603 .then(function (data) { finish(data && data.country ? data.country : ''); })
604 .catch(function () { finish(''); });
605 } else {
606 /* Fallback: inject a JSONP-style script tag. */
607 var cbName = '_itiGeoIp' + Date.now();
608 window[cbName] = function (data) {
609 finish(data && data.country ? data.country : '');
610 delete window[cbName];
611 };
612 var s = document.createElement('script');
613 s.src = 'https://ipinfo.io/?callback=' + cbName;
614 s.onerror = function () { finish(''); };
615 document.body.appendChild(s);
616 /* Safety timeout */
617 setTimeout(function () { finish(''); }, 5000);
618 }
619 };
620
621 /* ------------------------------------------------------------------ */
622 /* Factory function */
623 /* ------------------------------------------------------------------ */
624 window.intlTelInputVanilla = function (input, options) {
625 return new IntlTelInputVanilla(input, options);
626 };
627
628 /* Expose geoIpLookup helper */
629 window.intlTelInputVanilla.geoIpLookup = IntlTelInputVanilla.geoIpLookup;
630
631 /* Expose error codes */
632 window.intlTelInputVanilla.validationErrors = validationErrors;
633
634 })();
635