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 |