intl-tel-input 6.0.4 → 8.4.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a164649718e4a119936ad164836823591a36a0a2
4
- data.tar.gz: f8510375924e65af4ecc1554078ea74035ffc0cd
3
+ metadata.gz: 8bcd2575cb0194eb84176b2a55ecd684d1de8af9
4
+ data.tar.gz: f2bf5adf402d0b65fba2b774e12e1cb9bbaf0925
5
5
  SHA512:
6
- metadata.gz: 8cfd0ae51cf1d5247124b92fd62771199eb1aa7b251ef5201fedb5e4b1691450901c2fc37bd321a1c7b0e7cc33aad0da288534c6922c0846ba5044d9fe9bee98
7
- data.tar.gz: a9b211b0cf3c8f44d93f30219e2a4745fcb4761739c03b6a1316013dcc20ed4890f7b4b4e391ce73ee159e44dae8d8094d41487e1a50cb54df01b93872d46808
6
+ metadata.gz: 1ccc162c8930296b36f301312f652094aeb5a2137ff2900efad9debf1b933354d6db2435012b678a3c82ab4cb4d1cd9d9b68ca3a2ffe836e859d6a6518bba772
7
+ data.tar.gz: b739245a85d2290fe846394ec3ace310f386aae258285dcac545b2faf05a090234ae52d56df294af694da6806a091598190e627d8e89d3daf29673e0bd16809e
data/README.md CHANGED
@@ -10,3 +10,18 @@ Check the original documentation here:
10
10
  Include the gem in your Gemfile:
11
11
 
12
12
  gem 'intl-tel-input'
13
+
14
+
15
+ Add the following to your application.js:
16
+
17
+ ```
18
+ //= require intl-tel-input
19
+ ```
20
+
21
+ Also add to your application.css:
22
+
23
+ ```
24
+ /*
25
+ *= require intl-tel-input
26
+ */
27
+ ```
@@ -1,13 +1,16 @@
1
1
  /*
2
- International Telephone Input v6.0.4
3
- https://github.com/Bluefieldscom/intl-tel-input.git
4
- */
5
- // wrap in UMD - see https://github.com/umdjs/umd/blob/master/jqueryPlugin.js
2
+ * International Telephone Input v8.4.9
3
+ * https://github.com/jackocnr/intl-tel-input.git
4
+ * Licensed under the MIT license
5
+ */
6
+ // wrap in UMD - see https://github.com/umdjs/umd/blob/master/jqueryPluginCommonjs.js
6
7
  (function(factory) {
7
8
  if (typeof define === "function" && define.amd) {
8
9
  define([ "jquery" ], function($) {
9
10
  factory($, window, document);
10
11
  });
12
+ } else if (typeof module === "object" && module.exports) {
13
+ module.exports = factory(require("jquery"), window, document);
11
14
  } else {
12
15
  factory(jQuery, window, document);
13
16
  }
@@ -16,18 +19,24 @@ https://github.com/Bluefieldscom/intl-tel-input.git
16
19
  // these vars persist through all instances of the plugin
17
20
  var pluginName = "intlTelInput", id = 1, // give each instance it's own id for namespaced event handling
18
21
  defaults = {
19
- // typing digits after a valid number will be added to the extension part of the number
20
- allowExtensions: false,
21
- // automatically format the number according to the selected country
22
- autoFormat: true,
22
+ // whether or not to allow the dropdown
23
+ allowDropdown: true,
23
24
  // if there is just a dial code in the input: remove it on blur, and re-add it on focus
24
25
  autoHideDialCode: true,
25
26
  // add or remove input placeholder with an example number for the selected country
26
27
  autoPlaceholder: true,
27
- // default country
28
- defaultCountry: "",
28
+ // modify the auto placeholder
29
+ customPlaceholder: null,
30
+ // append menu to a specific element
31
+ dropdownContainer: "",
32
+ // don't display these countries
33
+ excludeCountries: [],
34
+ // format the input value during initialisation
35
+ formatOnInit: true,
29
36
  // geoIp lookup function
30
37
  geoIpLookup: null,
38
+ // initial country
39
+ initialCountry: "",
31
40
  // don't insert international dial codes
32
41
  nationalMode: true,
33
42
  // number type to use for placeholders
@@ -36,6 +45,8 @@ https://github.com/Bluefieldscom/intl-tel-input.git
36
45
  onlyCountries: [],
37
46
  // the countries at the top of the list. defaults to united states and united kingdom
38
47
  preferredCountries: [ "us", "gb" ],
48
+ // display the country dial code next to the selected flag so it's not part of the typed number
49
+ separateDialCode: false,
39
50
  // specify the path to the libphonenumber script to enable validation/formatting
40
51
  utilsScript: ""
41
52
  }, keys = {
@@ -46,31 +57,22 @@ https://github.com/Bluefieldscom/intl-tel-input.git
46
57
  PLUS: 43,
47
58
  A: 65,
48
59
  Z: 90,
49
- ZERO: 48,
50
- NINE: 57,
51
60
  SPACE: 32,
52
- BSPACE: 8,
53
- TAB: 9,
54
- DEL: 46,
55
- CTRL: 17,
56
- CMD1: 91,
57
- // Chrome
58
- CMD2: 224
59
- }, windowLoaded = false;
61
+ TAB: 9
62
+ };
60
63
  // keep track of if the window.load event has fired as impossible to check after the fact
61
64
  $(window).load(function() {
62
- windowLoaded = true;
65
+ // UPDATE: use a public static field so we can fudge it in the tests
66
+ $.fn[pluginName].windowLoaded = true;
63
67
  });
64
68
  function Plugin(element, options) {
65
- this.element = element;
69
+ this.telInput = $(element);
66
70
  this.options = $.extend({}, defaults, options);
67
- this._defaults = defaults;
68
71
  // event namespace
69
72
  this.ns = "." + pluginName + id++;
70
73
  // Chrome, FF, Safari, IE9+
71
74
  this.isGoodBrowser = Boolean(element.setSelectionRange);
72
75
  this.hadInitialPlaceholder = Boolean($(element).attr("placeholder"));
73
- this._name = pluginName;
74
76
  }
75
77
  Plugin.prototype = {
76
78
  _init: function() {
@@ -78,19 +80,29 @@ https://github.com/Bluefieldscom/intl-tel-input.git
78
80
  if (this.options.nationalMode) {
79
81
  this.options.autoHideDialCode = false;
80
82
  }
81
- // IE Mobile doesn't support the keypress event (see issue 68) which makes autoFormat impossible
82
- if (navigator.userAgent.match(/IEMobile/i)) {
83
- this.options.autoFormat = false;
83
+ // if separateDialCode then doesn't make sense to A) insert dial code into input (autoHideDialCode), and B) display national numbers (because we're displaying the country dial code next to them)
84
+ if (this.options.separateDialCode) {
85
+ this.options.autoHideDialCode = this.options.nationalMode = false;
86
+ // let's force this for now for simplicity - we can support this later if need be
87
+ this.options.allowDropdown = true;
84
88
  }
85
89
  // we cannot just test screen size as some smartphones/website meta tags will report desktop resolutions
86
- // Note: for some reason jasmine fucks up if you put this in the main Plugin function with the rest of these declarations
90
+ // Note: for some reason jasmine breaks if you put this in the main Plugin function with the rest of these declarations
87
91
  // Note: to target Android Mobiles (and not Tablets), we must find "Android" and "Mobile"
88
92
  this.isMobile = /Android.+Mobile|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
93
+ if (this.isMobile) {
94
+ // trigger the mobile dropdown css
95
+ $("body").addClass("iti-mobile");
96
+ // on mobile, we want a full screen dropdown, so we must append it to the body
97
+ if (!this.options.dropdownContainer) {
98
+ this.options.dropdownContainer = "body";
99
+ }
100
+ }
89
101
  // we return these deferred objects from the _init() call so they can be watched, and then we resolve them when each specific request returns
90
- // Note: again, jasmine had a spazz when I put these in the Plugin function
102
+ // Note: again, jasmine breaks when I put these in the Plugin function
91
103
  this.autoCountryDeferred = new $.Deferred();
92
104
  this.utilsScriptDeferred = new $.Deferred();
93
- // process all the data: onlyCountries, preferredCountries etc
105
+ // process all the data: onlyCountries, excludeCountries, preferredCountries etc
94
106
  this._processCountryData();
95
107
  // generate the markup
96
108
  this._generateMarkup();
@@ -106,12 +118,14 @@ https://github.com/Bluefieldscom/intl-tel-input.git
106
118
  /********************
107
119
  * PRIVATE METHODS
108
120
  ********************/
109
- // prepare all of the country data, including onlyCountries and preferredCountries options
121
+ // prepare all of the country data, including onlyCountries, excludeCountries and preferredCountries options
110
122
  _processCountryData: function() {
111
- // set the instances country data objects
112
- this._setInstanceCountryData();
113
- // set the preferredCountries property
114
- this._setPreferredCountries();
123
+ // process onlyCountries or excludeCountries array if present
124
+ this._processAllCountries();
125
+ // process the countryCodes map
126
+ this._processCountryCodes();
127
+ // process the preferredCountries
128
+ this._processPreferredCountries();
115
129
  },
116
130
  // add a country code to this.countryCodes
117
131
  _addCountryCode: function(iso2, dialCode, priority) {
@@ -121,28 +135,43 @@ https://github.com/Bluefieldscom/intl-tel-input.git
121
135
  var index = priority || 0;
122
136
  this.countryCodes[dialCode][index] = iso2;
123
137
  },
124
- // process onlyCountries array if present, and generate the countryCodes map
125
- _setInstanceCountryData: function() {
138
+ // filter the given countries using the process function
139
+ _filterCountries: function(countryArray, processFunc) {
126
140
  var i;
127
- // process onlyCountries option
128
- if (this.options.onlyCountries.length) {
129
- // standardise case
130
- for (i = 0; i < this.options.onlyCountries.length; i++) {
131
- this.options.onlyCountries[i] = this.options.onlyCountries[i].toLowerCase();
132
- }
133
- // build instance country array
134
- this.countries = [];
135
- for (i = 0; i < allCountries.length; i++) {
136
- if ($.inArray(allCountries[i].iso2, this.options.onlyCountries) != -1) {
137
- this.countries.push(allCountries[i]);
138
- }
141
+ // standardise case
142
+ for (i = 0; i < countryArray.length; i++) {
143
+ countryArray[i] = countryArray[i].toLowerCase();
144
+ }
145
+ // build instance country array
146
+ this.countries = [];
147
+ for (i = 0; i < allCountries.length; i++) {
148
+ if (processFunc($.inArray(allCountries[i].iso2, countryArray))) {
149
+ this.countries.push(allCountries[i]);
139
150
  }
151
+ }
152
+ },
153
+ // process onlyCountries or excludeCountries array if present
154
+ _processAllCountries: function() {
155
+ if (this.options.onlyCountries.length) {
156
+ // process onlyCountries option
157
+ this._filterCountries(this.options.onlyCountries, function(inArray) {
158
+ // if country is in array
159
+ return inArray != -1;
160
+ });
161
+ } else if (this.options.excludeCountries.length) {
162
+ // process excludeCountries option
163
+ this._filterCountries(this.options.excludeCountries, function(inArray) {
164
+ // if country is not in array
165
+ return inArray == -1;
166
+ });
140
167
  } else {
141
168
  this.countries = allCountries;
142
169
  }
143
- // generate countryCodes map
170
+ },
171
+ // process the countryCodes map
172
+ _processCountryCodes: function() {
144
173
  this.countryCodes = {};
145
- for (i = 0; i < this.countries.length; i++) {
174
+ for (var i = 0; i < this.countries.length; i++) {
146
175
  var c = this.countries[i];
147
176
  this._addCountryCode(c.iso2, c.dialCode, c.priority);
148
177
  // area codes
@@ -154,9 +183,8 @@ https://github.com/Bluefieldscom/intl-tel-input.git
154
183
  }
155
184
  }
156
185
  },
157
- // process preferred countries - iterate through the preferences,
158
- // fetching the country data for each one
159
- _setPreferredCountries: function() {
186
+ // process preferred countries - iterate through the preferences, fetching the country data for each one
187
+ _processPreferredCountries: function() {
160
188
  this.preferredCountries = [];
161
189
  for (var i = 0; i < this.options.preferredCountries.length; i++) {
162
190
  var countryCode = this.options.preferredCountries[i].toLowerCase(), countryData = this._getCountryData(countryCode, false, true);
@@ -167,59 +195,69 @@ https://github.com/Bluefieldscom/intl-tel-input.git
167
195
  },
168
196
  // generate all of the markup for the plugin: the selected flag overlay, and the dropdown
169
197
  _generateMarkup: function() {
170
- // telephone input
171
- this.telInput = $(this.element);
172
198
  // prevent autocomplete as there's no safe, cross-browser event we can react to, so it can easily put the plugin in an inconsistent state e.g. the wrong flag selected for the autocompleted number, which on submit could mean the wrong number is saved (esp in nationalMode)
173
199
  this.telInput.attr("autocomplete", "off");
174
200
  // containers (mostly for positioning)
201
+ var parentClass = "intl-tel-input";
202
+ if (this.options.allowDropdown) {
203
+ parentClass += " allow-dropdown";
204
+ }
205
+ if (this.options.separateDialCode) {
206
+ parentClass += " separate-dial-code";
207
+ }
175
208
  this.telInput.wrap($("<div>", {
176
- "class": "intl-tel-input"
209
+ "class": parentClass
177
210
  }));
178
211
  this.flagsContainer = $("<div>", {
179
- "class": "flag-dropdown"
212
+ "class": "flag-container"
180
213
  }).insertBefore(this.telInput);
181
214
  // currently selected flag (displayed to left of input)
182
215
  var selectedFlag = $("<div>", {
183
- // make element focusable and tab naviagable
184
- tabindex: "0",
185
216
  "class": "selected-flag"
186
- }).appendTo(this.flagsContainer);
217
+ });
218
+ selectedFlag.appendTo(this.flagsContainer);
187
219
  this.selectedFlagInner = $("<div>", {
188
220
  "class": "iti-flag"
189
221
  }).appendTo(selectedFlag);
190
- // CSS triangle
191
- $("<div>", {
192
- "class": "arrow"
193
- }).appendTo(selectedFlag);
194
- // country list
195
- // mobile is just a native select element
196
- // desktop is a proper list containing: preferred countries, then divider, then all countries
197
- if (this.isMobile) {
198
- this.countryList = $("<select>", {
199
- "class": "iti-mobile-select"
200
- }).appendTo(this.flagsContainer);
201
- } else {
222
+ if (this.options.separateDialCode) {
223
+ this.selectedDialCode = $("<div>", {
224
+ "class": "selected-dial-code"
225
+ }).appendTo(selectedFlag);
226
+ }
227
+ if (this.options.allowDropdown) {
228
+ // make element focusable and tab naviagable
229
+ selectedFlag.attr("tabindex", "0");
230
+ // CSS triangle
231
+ $("<div>", {
232
+ "class": "iti-arrow"
233
+ }).appendTo(selectedFlag);
234
+ // country dropdown: preferred countries, then divider, then all countries
202
235
  this.countryList = $("<ul>", {
203
- "class": "country-list v-hide"
204
- }).appendTo(this.flagsContainer);
205
- if (this.preferredCountries.length && !this.isMobile) {
236
+ "class": "country-list hide"
237
+ });
238
+ if (this.preferredCountries.length) {
206
239
  this._appendListItems(this.preferredCountries, "preferred");
207
240
  $("<li>", {
208
241
  "class": "divider"
209
242
  }).appendTo(this.countryList);
210
243
  }
211
- }
212
- this._appendListItems(this.countries, "");
213
- if (!this.isMobile) {
214
- // now we can grab the dropdown height, and hide it properly
215
- this.dropdownHeight = this.countryList.outerHeight();
216
- this.countryList.removeClass("v-hide").addClass("hide");
244
+ this._appendListItems(this.countries, "");
217
245
  // this is useful in lots of places
218
246
  this.countryListItems = this.countryList.children(".country");
247
+ // create dropdownContainer markup
248
+ if (this.options.dropdownContainer) {
249
+ this.dropdown = $("<div>", {
250
+ "class": "intl-tel-input iti-container"
251
+ }).append(this.countryList);
252
+ } else {
253
+ this.countryList.appendTo(this.flagsContainer);
254
+ }
255
+ } else {
256
+ // a little hack so we don't break anything
257
+ this.countryListItems = $();
219
258
  }
220
259
  },
221
260
  // add a country <li> to the countryList <ul> container
222
- // UPDATE: if isMobile, add an <option> to the countryList <select> container
223
261
  _appendListItems: function(countries, className) {
224
262
  // we create so many DOM elements, it is faster to build a temp string
225
263
  // and then add everything to the DOM in one go at the end
@@ -227,85 +265,86 @@ https://github.com/Bluefieldscom/intl-tel-input.git
227
265
  // for each country
228
266
  for (var i = 0; i < countries.length; i++) {
229
267
  var c = countries[i];
230
- if (this.isMobile) {
231
- tmp += "<option data-dial-code='" + c.dialCode + "' value='" + c.iso2 + "'>";
232
- tmp += c.name + " +" + c.dialCode;
233
- tmp += "</option>";
234
- } else {
235
- // open the list item
236
- tmp += "<li class='country " + className + "' data-dial-code='" + c.dialCode + "' data-country-code='" + c.iso2 + "'>";
237
- // add the flag
238
- tmp += "<div class='flag'><div class='iti-flag " + c.iso2 + "'></div></div>";
239
- // and the country name and dial code
240
- tmp += "<span class='country-name'>" + c.name + "</span>";
241
- tmp += "<span class='dial-code'>+" + c.dialCode + "</span>";
242
- // close the list item
243
- tmp += "</li>";
244
- }
268
+ // open the list item
269
+ tmp += "<li class='country " + className + "' data-dial-code='" + c.dialCode + "' data-country-code='" + c.iso2 + "'>";
270
+ // add the flag
271
+ tmp += "<div class='flag-box'><div class='iti-flag " + c.iso2 + "'></div></div>";
272
+ // and the country name and dial code
273
+ tmp += "<span class='country-name'>" + c.name + "</span>";
274
+ tmp += "<span class='dial-code'>+" + c.dialCode + "</span>";
275
+ // close the list item
276
+ tmp += "</li>";
245
277
  }
246
278
  this.countryList.append(tmp);
247
279
  },
248
- // set the initial state of the input value and the selected flag
280
+ // set the initial state of the input value and the selected flag by:
281
+ // 1. extracting a dial code from the given number
282
+ // 2. using explicit initialCountry
283
+ // 3. picking the first preferred country
284
+ // 4. picking the first country
249
285
  _setInitialState: function() {
250
286
  var val = this.telInput.val();
251
- // if there is a number, and it's valid, we can go ahead and set the flag, else fall back to default
287
+ // if we already have a dial code we can go ahead and set the flag, else fall back to default
252
288
  if (this._getDialCode(val)) {
253
289
  this._updateFlagFromNumber(val, true);
254
- } else if (this.options.defaultCountry != "auto") {
255
- // check the defaultCountry option, else fall back to the first in the list
256
- if (this.options.defaultCountry) {
257
- this.options.defaultCountry = this._getCountryData(this.options.defaultCountry.toLowerCase(), false, false);
290
+ } else if (this.options.initialCountry !== "auto") {
291
+ // see if we should select a flag
292
+ if (this.options.initialCountry) {
293
+ this._setFlag(this.options.initialCountry, true);
258
294
  } else {
259
- this.options.defaultCountry = this.preferredCountries.length ? this.preferredCountries[0] : this.countries[0];
295
+ // no dial code and no initialCountry, so default to first in list
296
+ this.defaultCountry = this.preferredCountries.length ? this.preferredCountries[0].iso2 : this.countries[0].iso2;
297
+ if (!val) {
298
+ this._setFlag(this.defaultCountry, true);
299
+ }
260
300
  }
261
- this._selectFlag(this.options.defaultCountry.iso2);
262
- // if empty, insert the default dial code (this function will check !nationalMode and !autoHideDialCode)
263
- if (!val) {
264
- this._updateDialCode(this.options.defaultCountry.dialCode, false);
301
+ // if empty and no nationalMode and no autoHideDialCode then insert the default dial code
302
+ if (!val && !this.options.nationalMode && !this.options.autoHideDialCode && !this.options.separateDialCode) {
303
+ this.telInput.val("+" + this.selectedCountryData.dialCode);
265
304
  }
266
305
  }
306
+ // NOTE: if initialCountry is set to auto, that will be handled separately
267
307
  // format
268
308
  if (val) {
269
309
  // this wont be run after _updateDialCode as that's only called if no val
270
- this._updateVal(val);
310
+ this._updateValFromNumber(val, this.options.formatOnInit);
271
311
  }
272
312
  },
273
313
  // initialise the main event listeners: input keyup, and click selected flag
274
314
  _initListeners: function() {
275
- var that = this;
276
315
  this._initKeyListeners();
277
- // autoFormat prevents the change event from firing, so we need to check for changes between focus and blur in order to manually trigger it
278
- if (this.options.autoHideDialCode || this.options.autoFormat) {
316
+ if (this.options.autoHideDialCode) {
279
317
  this._initFocusListeners();
280
318
  }
281
- if (this.isMobile) {
282
- this.countryList.on("change" + this.ns, function(e) {
283
- that._selectListItem($(this).find("option:selected"));
284
- });
285
- } else {
286
- // hack for input nested inside label: clicking the selected-flag to open the dropdown would then automatically trigger a 2nd click on the input which would close it again
287
- var label = this.telInput.closest("label");
288
- if (label.length) {
289
- label.on("click" + this.ns, function(e) {
290
- // if the dropdown is closed, then focus the input, else ignore the click
291
- if (that.countryList.hasClass("hide")) {
292
- that.telInput.focus();
293
- } else {
294
- e.preventDefault();
295
- }
296
- });
297
- }
298
- // toggle country dropdown on click
299
- var selectedFlag = this.selectedFlagInner.parent();
300
- selectedFlag.on("click" + this.ns, function(e) {
301
- // only intercept this event if we're opening the dropdown
302
- // else let it bubble up to the top ("click-off-to-close" listener)
303
- // we cannot just stopPropagation as it may be needed to close another instance
304
- if (that.countryList.hasClass("hide") && !that.telInput.prop("disabled") && !that.telInput.prop("readonly")) {
305
- that._showDropdown();
319
+ if (this.options.allowDropdown) {
320
+ this._initDropdownListeners();
321
+ }
322
+ },
323
+ // initialise the dropdown listeners
324
+ _initDropdownListeners: function() {
325
+ var that = this;
326
+ // hack for input nested inside label: clicking the selected-flag to open the dropdown would then automatically trigger a 2nd click on the input which would close it again
327
+ var label = this.telInput.closest("label");
328
+ if (label.length) {
329
+ label.on("click" + this.ns, function(e) {
330
+ // if the dropdown is closed, then focus the input, else ignore the click
331
+ if (that.countryList.hasClass("hide")) {
332
+ that.telInput.focus();
333
+ } else {
334
+ e.preventDefault();
306
335
  }
307
336
  });
308
337
  }
338
+ // toggle country dropdown on click
339
+ var selectedFlag = this.selectedFlagInner.parent();
340
+ selectedFlag.on("click" + this.ns, function(e) {
341
+ // only intercept this event if we're opening the dropdown
342
+ // else let it bubble up to the top ("click-off-to-close" listener)
343
+ // we cannot just stopPropagation as it may be needed to close another instance
344
+ if (that.countryList.hasClass("hide") && !that.telInput.prop("disabled") && !that.telInput.prop("readonly")) {
345
+ that._showDropdown();
346
+ }
347
+ });
309
348
  // open dropdown list if currently focused
310
349
  this.flagsContainer.on("keydown" + that.ns, function(e) {
311
350
  var isDropdownHidden = that.countryList.hasClass("hide");
@@ -322,32 +361,34 @@ https://github.com/Bluefieldscom/intl-tel-input.git
322
361
  }
323
362
  });
324
363
  },
364
+ // init many requests: utils script / geo ip lookup
325
365
  _initRequests: function() {
326
366
  var that = this;
327
- // if the user has specified the path to the utils script, fetch it on window.load
367
+ // if the user has specified the path to the utils script, fetch it on window.load, else resolve
328
368
  if (this.options.utilsScript) {
329
369
  // if the plugin is being initialised after the window.load event has already been fired
330
- if (windowLoaded) {
331
- this.loadUtils();
370
+ if ($.fn[pluginName].windowLoaded) {
371
+ $.fn[pluginName].loadUtils(this.options.utilsScript, this.utilsScriptDeferred);
332
372
  } else {
333
373
  // wait until the load event so we don't block any other requests e.g. the flags image
334
374
  $(window).load(function() {
335
- that.loadUtils();
375
+ $.fn[pluginName].loadUtils(that.options.utilsScript, that.utilsScriptDeferred);
336
376
  });
337
377
  }
338
378
  } else {
339
379
  this.utilsScriptDeferred.resolve();
340
380
  }
341
- if (this.options.defaultCountry == "auto") {
381
+ if (this.options.initialCountry === "auto") {
342
382
  this._loadAutoCountry();
343
383
  } else {
344
384
  this.autoCountryDeferred.resolve();
345
385
  }
346
386
  },
387
+ // perform the geo ip lookup
347
388
  _loadAutoCountry: function() {
348
389
  var that = this;
349
390
  // check for cookie
350
- var cookieAutoCountry = $.cookie ? $.cookie("itiAutoCountry") : "";
391
+ var cookieAutoCountry = window.Cookies ? Cookies.get("itiAutoCountry") : "";
351
392
  if (cookieAutoCountry) {
352
393
  $.fn[pluginName].autoCountry = cookieAutoCountry;
353
394
  }
@@ -356,221 +397,69 @@ https://github.com/Bluefieldscom/intl-tel-input.git
356
397
  // 2) not already started loading (start)
357
398
  // 3) already started loading (do nothing - just wait for loading callback to fire)
358
399
  if ($.fn[pluginName].autoCountry) {
359
- this.autoCountryLoaded();
400
+ this.handleAutoCountry();
360
401
  } else if (!$.fn[pluginName].startedLoadingAutoCountry) {
361
402
  // don't do this twice!
362
403
  $.fn[pluginName].startedLoadingAutoCountry = true;
363
404
  if (typeof this.options.geoIpLookup === "function") {
364
405
  this.options.geoIpLookup(function(countryCode) {
365
406
  $.fn[pluginName].autoCountry = countryCode.toLowerCase();
366
- if ($.cookie) {
367
- $.cookie("itiAutoCountry", $.fn[pluginName].autoCountry, {
407
+ if (window.Cookies) {
408
+ Cookies.set("itiAutoCountry", $.fn[pluginName].autoCountry, {
368
409
  path: "/"
369
410
  });
370
411
  }
371
412
  // tell all instances the auto country is ready
372
413
  // TODO: this should just be the current instances
373
- $(".intl-tel-input input").intlTelInput("autoCountryLoaded");
414
+ // UPDATE: use setTimeout in case their geoIpLookup function calls this callback straight away (e.g. if they have already done the geo ip lookup somewhere else). Using setTimeout means that the current thread of execution will finish before executing this, which allows the plugin to finish initialising.
415
+ setTimeout(function() {
416
+ $(".intl-tel-input input").intlTelInput("handleAutoCountry");
417
+ });
374
418
  });
375
419
  }
376
420
  }
377
421
  },
422
+ // initialize any key listeners
378
423
  _initKeyListeners: function() {
379
424
  var that = this;
380
- if (this.options.autoFormat) {
381
- // format number and update flag on keypress
382
- // use keypress event as we want to ignore all input except for a select few keys,
383
- // but we dont want to ignore the navigation keys like the arrows etc.
384
- // NOTE: no point in refactoring this to only bind these listeners on focus/blur because then you would need to have those 2 listeners running the whole time anyway...
385
- this.telInput.on("keypress" + this.ns, function(e) {
386
- // 32 is space, and after that it's all chars (not meta/nav keys)
387
- // this fix is needed for Firefox, which triggers keypress event for some meta/nav keys
388
- // Update: also ignore if this is a metaKey e.g. FF and Safari trigger keypress on the v of Ctrl+v
389
- // Update: also ignore if ctrlKey (FF on Windows/Ubuntu)
390
- // Update: also check that we have utils before we do any autoFormat stuff
391
- if (e.which >= keys.SPACE && !e.ctrlKey && !e.metaKey && window.intlTelInputUtils && !that.telInput.prop("readonly")) {
392
- e.preventDefault();
393
- // allowed keys are just numeric keys and plus
394
- // we must allow plus for the case where the user does select-all and then hits plus to start typing a new number. we could refine this logic to first check that the selection contains a plus, but that wont work in old browsers, and I think it's overkill anyway
395
- var isAllowedKey = e.which >= keys.ZERO && e.which <= keys.NINE || e.which == keys.PLUS, input = that.telInput[0], noSelection = that.isGoodBrowser && input.selectionStart == input.selectionEnd, max = that.telInput.attr("maxlength"), val = that.telInput.val(), // assumes that if max exists, it is >0
396
- isBelowMax = max ? val.length < max : true;
397
- // first: ensure we dont go over maxlength. we must do this here to prevent adding digits in the middle of the number
398
- // still reformat even if not an allowed key as they could by typing a formatting char, but ignore if there's a selection as doesn't make sense to replace selection with illegal char and then immediately remove it
399
- if (isBelowMax && (isAllowedKey || noSelection)) {
400
- var newChar = isAllowedKey ? String.fromCharCode(e.which) : null;
401
- that._handleInputKey(newChar, true, isAllowedKey);
402
- // if something has changed, trigger the input event (which was otherwised squashed by the preventDefault)
403
- if (val != that.telInput.val()) {
404
- that.telInput.trigger("input");
405
- }
406
- }
407
- if (!isAllowedKey) {
408
- that._handleInvalidKey();
409
- }
410
- }
411
- });
412
- }
413
- // handle cut/paste event (now supported in all major browsers)
414
- this.telInput.on("cut" + this.ns + " paste" + this.ns, function() {
425
+ // update flag on keyup
426
+ // (keep this listener separate otherwise the setTimeout breaks all the tests)
427
+ this.telInput.on("keyup" + this.ns, function() {
428
+ that._updateFlagFromNumber(that.telInput.val());
429
+ });
430
+ // update flag on cut/paste events (now supported in all major browsers)
431
+ this.telInput.on("cut" + this.ns + " paste" + this.ns + " keyup" + this.ns, function() {
415
432
  // hack because "paste" event is fired before input is updated
416
433
  setTimeout(function() {
417
- if (that.options.autoFormat && window.intlTelInputUtils) {
418
- var cursorAtEnd = that.isGoodBrowser && that.telInput[0].selectionStart == that.telInput.val().length;
419
- that._handleInputKey(null, cursorAtEnd);
420
- that._ensurePlus();
421
- } else {
422
- // if no autoFormat, just update flag
423
- that._updateFlagFromNumber(that.telInput.val());
424
- }
425
- });
426
- });
427
- // handle keyup event
428
- // if autoFormat enabled: we use keyup to catch delete events (after the fact)
429
- // if no autoFormat, this is used to update the flag
430
- this.telInput.on("keyup" + this.ns, function(e) {
431
- // the "enter" key event from selecting a dropdown item is triggered here on the input, because the document.keydown handler that initially handles that event triggers a focus on the input, and so the keyup for that same key event gets triggered here. weird, but just make sure we dont bother doing any re-formatting in this case (we've already done preventDefault in the keydown handler, so it wont actually submit the form or anything).
432
- // ALSO: ignore keyup if readonly
433
- if (e.which == keys.ENTER || that.telInput.prop("readonly")) {} else if (that.options.autoFormat && window.intlTelInputUtils) {
434
- // cursorAtEnd defaults to false for bad browsers else they would never get a reformat on delete
435
- var cursorAtEnd = that.isGoodBrowser && that.telInput[0].selectionStart == that.telInput.val().length;
436
- if (!that.telInput.val()) {
437
- // if they just cleared the input, update the flag to the default
438
- that._updateFlagFromNumber("");
439
- } else if (e.which == keys.DEL && !cursorAtEnd || e.which == keys.BSPACE) {
440
- // if delete in the middle: reformat with no suffix (no need to reformat if delete at end)
441
- // if backspace: reformat with no suffix (need to reformat if at end to remove any lingering suffix - this is a feature)
442
- // important to remember never to add suffix on any delete key as can fuck up in ie8 so you can never delete a formatting char at the end
443
- that._handleInputKey();
444
- }
445
- that._ensurePlus();
446
- } else {
447
- // if no autoFormat, just update flag
448
434
  that._updateFlagFromNumber(that.telInput.val());
449
- }
435
+ });
450
436
  });
451
437
  },
452
- // prevent deleting the plus (if not in nationalMode)
453
- _ensurePlus: function() {
454
- if (!this.options.nationalMode) {
455
- var val = this.telInput.val(), input = this.telInput[0];
456
- if (val.charAt(0) != "+") {
457
- // newCursorPos is current pos + 1 to account for the plus we are about to add
458
- var newCursorPos = this.isGoodBrowser ? input.selectionStart + 1 : 0;
459
- this.telInput.val("+" + val);
460
- if (this.isGoodBrowser) {
461
- input.setSelectionRange(newCursorPos, newCursorPos);
462
- }
463
- }
464
- }
465
- },
466
- // alert the user to an invalid key event
467
- _handleInvalidKey: function() {
468
- var that = this;
469
- this.telInput.trigger("invalidkey").addClass("iti-invalid-key");
470
- setTimeout(function() {
471
- that.telInput.removeClass("iti-invalid-key");
472
- }, 100);
473
- },
474
- // when autoFormat is enabled: handle various key events on the input:
475
- // 1) adding a new number character, which will replace any selection, reformat, and preserve the cursor position
476
- // 2) reformatting on backspace/delete
477
- // 3) cut/paste event
478
- _handleInputKey: function(newNumericChar, addSuffix, isAllowedKey) {
479
- var val = this.telInput.val(), cleanBefore = this._getClean(val), originalLeftChars, // raw DOM element
480
- input = this.telInput[0], digitsOnRight = 0;
481
- if (this.isGoodBrowser) {
482
- // cursor strategy: maintain the number of digits on the right. we use the right instead of the left so that A) we dont have to account for the new digit (or multiple digits if paste event), and B) we're always on the right side of formatting suffixes
483
- digitsOnRight = this._getDigitsOnRight(val, input.selectionEnd);
484
- // if handling a new number character: insert it in the right place
485
- if (newNumericChar) {
486
- // replace any selection they may have made with the new char
487
- val = val.substr(0, input.selectionStart) + newNumericChar + val.substring(input.selectionEnd, val.length);
488
- } else {
489
- // here we're not handling a new char, we're just doing a re-format (e.g. on delete/backspace/paste, after the fact), but we still need to maintain the cursor position. so make note of the char on the left, and then after the re-format, we'll count in the same number of digits from the right, and then keep going through any formatting chars until we hit the same left char that we had before.
490
- // UPDATE: now have to store 2 chars as extensions formatting contains 2 spaces so you need to be able to distinguish
491
- originalLeftChars = val.substr(input.selectionStart - 2, 2);
492
- }
493
- } else if (newNumericChar) {
494
- val += newNumericChar;
495
- }
496
- // update the number and flag
497
- this.setNumber(val, null, addSuffix, true, isAllowedKey);
498
- // update the cursor position
499
- if (this.isGoodBrowser) {
500
- var newCursor;
501
- val = this.telInput.val();
502
- // if it was at the end, keep it there
503
- if (!digitsOnRight) {
504
- newCursor = val.length;
505
- } else {
506
- // else count in the same number of digits from the right
507
- newCursor = this._getCursorFromDigitsOnRight(val, digitsOnRight);
508
- // but if delete/paste etc, keep going left until hit the same left char as before
509
- if (!newNumericChar) {
510
- newCursor = this._getCursorFromLeftChar(val, newCursor, originalLeftChars);
511
- }
512
- }
513
- // set the new cursor
514
- input.setSelectionRange(newCursor, newCursor);
515
- }
516
- },
517
- // we start from the position in guessCursor, and work our way left until we hit the originalLeftChars or a number to make sure that after reformatting the cursor has the same char on the left in the case of a delete etc
518
- _getCursorFromLeftChar: function(val, guessCursor, originalLeftChars) {
519
- for (var i = guessCursor; i > 0; i--) {
520
- var leftChar = val.charAt(i - 1);
521
- if ($.isNumeric(leftChar) || val.substr(i - 2, 2) == originalLeftChars) {
522
- return i;
523
- }
524
- }
525
- return 0;
438
+ // adhere to the input's maxlength attr
439
+ _cap: function(number) {
440
+ var max = this.telInput.attr("maxlength");
441
+ return max && number.length > max ? number.substr(0, max) : number;
526
442
  },
527
- // after a reformat we need to make sure there are still the same number of digits to the right of the cursor
528
- _getCursorFromDigitsOnRight: function(val, digitsOnRight) {
529
- for (var i = val.length - 1; i >= 0; i--) {
530
- if ($.isNumeric(val.charAt(i))) {
531
- if (--digitsOnRight === 0) {
532
- return i;
533
- }
534
- }
535
- }
536
- return 0;
537
- },
538
- // get the number of numeric digits to the right of the cursor so we can reposition the cursor correctly after the reformat has happened
539
- _getDigitsOnRight: function(val, selectionEnd) {
540
- var digitsOnRight = 0;
541
- for (var i = selectionEnd; i < val.length; i++) {
542
- if ($.isNumeric(val.charAt(i))) {
543
- digitsOnRight++;
544
- }
545
- }
546
- return digitsOnRight;
547
- },
548
- // listen for focus and blur
443
+ // listen for mousedown, focus and blur
549
444
  _initFocusListeners: function() {
550
445
  var that = this;
551
- if (this.options.autoHideDialCode) {
552
- // mousedown decides where the cursor goes, so if we're focusing we must preventDefault as we'll be inserting the dial code, and we want the cursor to be at the end no matter where they click
553
- this.telInput.on("mousedown" + this.ns, function(e) {
554
- if (!that.telInput.is(":focus") && !that.telInput.val()) {
555
- e.preventDefault();
556
- // but this also cancels the focus, so we must trigger that manually
557
- that.telInput.focus();
558
- }
559
- });
560
- }
446
+ // mousedown decides where the cursor goes, so if we're focusing we must preventDefault as we'll be inserting the dial code, and we want the cursor to be at the end no matter where they click
447
+ this.telInput.on("mousedown" + this.ns, function(e) {
448
+ if (!that.telInput.is(":focus") && !that.telInput.val()) {
449
+ e.preventDefault();
450
+ // but this also cancels the focus, so we must trigger that manually
451
+ that.telInput.focus();
452
+ }
453
+ });
454
+ // on focus: if empty, insert the dial code for the currently selected flag
561
455
  this.telInput.on("focus" + this.ns, function(e) {
562
- var value = that.telInput.val();
563
- // save this to compare on blur
564
- that.telInput.data("focusVal", value);
565
- // on focus: if empty, insert the dial code for the currently selected flag
566
- if (that.options.autoHideDialCode && !value && !that.telInput.prop("readonly") && that.selectedCountryData.dialCode) {
567
- that._updateVal("+" + that.selectedCountryData.dialCode, null, true);
456
+ if (!that.telInput.val() && !that.telInput.prop("readonly") && that.selectedCountryData.dialCode) {
457
+ // insert the dial code
458
+ that.telInput.val("+" + that.selectedCountryData.dialCode);
568
459
  // after auto-inserting a dial code, if the first key they hit is '+' then assume they are entering a new number, so remove the dial code. use keypress instead of keydown because keydown gets triggered for the shift key (required to hit the + key), and instead of keyup because that shows the new '+' before removing the old one
569
460
  that.telInput.one("keypress.plus" + that.ns, function(e) {
570
461
  if (e.which == keys.PLUS) {
571
- // if autoFormat is enabled, this key event will have already have been handled by another keypress listener (hence we need to add the "+"). if disabled, it will be handled after this by a keyup listener (hence no need to add the "+").
572
- var newVal = that.options.autoFormat && window.intlTelInputUtils ? "+" : "";
573
- that.telInput.val(newVal);
462
+ that.telInput.val("");
574
463
  }
575
464
  });
576
465
  // after tabbing in, make sure the cursor is at the end we must use setTimeout to get outside of the focus handler as it seems the selection happens after that
@@ -583,34 +472,24 @@ https://github.com/Bluefieldscom/intl-tel-input.git
583
472
  });
584
473
  }
585
474
  });
475
+ // on blur: if just a dial code then remove it
586
476
  this.telInput.on("blur" + this.ns, function() {
587
- if (that.options.autoHideDialCode) {
588
- // on blur: if just a dial code then remove it
589
- var value = that.telInput.val(), startsPlus = value.charAt(0) == "+";
590
- if (startsPlus) {
591
- var numeric = that._getNumeric(value);
592
- // if just a plus, or if just a dial code
593
- if (!numeric || that.selectedCountryData.dialCode == numeric) {
594
- that.telInput.val("");
595
- }
477
+ var value = that.telInput.val(), startsPlus = value.charAt(0) == "+";
478
+ if (startsPlus) {
479
+ var numeric = that._getNumeric(value);
480
+ // if just a plus, or if just a dial code
481
+ if (!numeric || that.selectedCountryData.dialCode == numeric) {
482
+ that.telInput.val("");
596
483
  }
597
- // remove the keypress listener we added on focus
598
- that.telInput.off("keypress.plus" + that.ns);
599
- }
600
- // if autoFormat, we must manually trigger change event if value has changed
601
- if (that.options.autoFormat && window.intlTelInputUtils && that.telInput.val() != that.telInput.data("focusVal")) {
602
- that.telInput.trigger("change");
603
484
  }
485
+ // remove the keypress listener we added on focus
486
+ that.telInput.off("keypress.plus" + that.ns);
604
487
  });
605
488
  },
606
489
  // extract the numeric digits from the given string
607
490
  _getNumeric: function(s) {
608
491
  return s.replace(/\D/g, "");
609
492
  },
610
- _getClean: function(s) {
611
- var prefix = s.charAt(0) == "+" ? "+" : "";
612
- return prefix + this._getNumeric(s);
613
- },
614
493
  // show the dropdown
615
494
  _showDropdown: function() {
616
495
  this._setDropdownPosition();
@@ -618,24 +497,41 @@ https://github.com/Bluefieldscom/intl-tel-input.git
618
497
  var activeListItem = this.countryList.children(".active");
619
498
  if (activeListItem.length) {
620
499
  this._highlightListItem(activeListItem);
621
- }
622
- // show it
623
- this.countryList.removeClass("hide");
624
- if (activeListItem.length) {
625
500
  this._scrollTo(activeListItem);
626
501
  }
627
502
  // bind all the dropdown-related listeners: mouseover, click, click-off, keydown
628
503
  this._bindDropdownListeners();
629
504
  // update the arrow
630
- this.selectedFlagInner.children(".arrow").addClass("up");
505
+ this.selectedFlagInner.children(".iti-arrow").addClass("up");
631
506
  },
632
507
  // decide where to position dropdown (depends on position within viewport, and scroll)
633
508
  _setDropdownPosition: function() {
634
- var inputTop = this.telInput.offset().top, windowTop = $(window).scrollTop(), // dropdownFitsBelow = (dropdownBottom < windowBottom)
635
- dropdownFitsBelow = inputTop + this.telInput.outerHeight() + this.dropdownHeight < windowTop + $(window).height(), dropdownFitsAbove = inputTop - this.dropdownHeight > windowTop;
636
- // dropdownHeight - 1 for border
637
- var cssTop = !dropdownFitsBelow && dropdownFitsAbove ? "-" + (this.dropdownHeight - 1) + "px" : "";
638
- this.countryList.css("top", cssTop);
509
+ var that = this;
510
+ if (this.options.dropdownContainer) {
511
+ this.dropdown.appendTo(this.options.dropdownContainer);
512
+ }
513
+ // show the menu and grab the dropdown height
514
+ this.dropdownHeight = this.countryList.removeClass("hide").outerHeight();
515
+ if (!this.isMobile) {
516
+ var pos = this.telInput.offset(), inputTop = pos.top, windowTop = $(window).scrollTop(), // dropdownFitsBelow = (dropdownBottom < windowBottom)
517
+ dropdownFitsBelow = inputTop + this.telInput.outerHeight() + this.dropdownHeight < windowTop + $(window).height(), dropdownFitsAbove = inputTop - this.dropdownHeight > windowTop;
518
+ // by default, the dropdown will be below the input. If we want to position it above the input, we add the dropup class.
519
+ this.countryList.toggleClass("dropup", !dropdownFitsBelow && dropdownFitsAbove);
520
+ // if dropdownContainer is enabled, calculate postion
521
+ if (this.options.dropdownContainer) {
522
+ // by default the dropdown will be directly over the input because it's not in the flow. If we want to position it below, we need to add some extra top value.
523
+ var extraTop = !dropdownFitsBelow && dropdownFitsAbove ? 0 : this.telInput.innerHeight();
524
+ // calculate placement
525
+ this.dropdown.css({
526
+ top: inputTop + extraTop,
527
+ left: pos.left
528
+ });
529
+ // close menu on window scroll
530
+ $(window).on("scroll" + this.ns, function() {
531
+ that._closeDropdown();
532
+ });
533
+ }
534
+ }
639
535
  },
640
536
  // we only bind dropdown listeners when the dropdown is open
641
537
  _bindDropdownListeners: function() {
@@ -728,36 +624,22 @@ https://github.com/Bluefieldscom/intl-tel-input.git
728
624
  _startsWith: function(a, b) {
729
625
  return a.substr(0, b.length).toUpperCase() == b;
730
626
  },
731
- // update the input's value to the given val
732
- // if autoFormat=true, format it first according to the country-specific formatting rules
733
- // Note: preventConversion will be false (i.e. we allow conversion) on init and when dev calls public method setNumber
734
- _updateVal: function(val, format, addSuffix, preventConversion, isAllowedKey) {
735
- var formatted;
736
- if (this.options.autoFormat && window.intlTelInputUtils && this.selectedCountryData) {
737
- if (typeof format == "number" && intlTelInputUtils.isValidNumber(val, this.selectedCountryData.iso2)) {
738
- // if user specified a format, and it's a valid number, then format it accordingly
739
- formatted = intlTelInputUtils.formatNumberByType(val, this.selectedCountryData.iso2, format);
740
- } else if (!preventConversion && this.options.nationalMode && val.charAt(0) == "+" && intlTelInputUtils.isValidNumber(val, this.selectedCountryData.iso2)) {
741
- // if nationalMode and we have a valid intl number, convert it to ntl
742
- formatted = intlTelInputUtils.formatNumberByType(val, this.selectedCountryData.iso2, intlTelInputUtils.numberFormat.NATIONAL);
743
- } else {
744
- // else do the regular AsYouType formatting
745
- formatted = intlTelInputUtils.formatNumber(val, this.selectedCountryData.iso2, addSuffix, this.options.allowExtensions, isAllowedKey);
627
+ // update the input's value to the given val (format first if possible)
628
+ // NOTE: this is called from _setInitialState, handleUtils and setNumber
629
+ _updateValFromNumber: function(number, doFormat, format) {
630
+ if (doFormat && window.intlTelInputUtils && this.selectedCountryData) {
631
+ if (!$.isNumeric(format)) {
632
+ format = this.options.nationalMode || number.charAt(0) != "+" ? intlTelInputUtils.numberFormat.NATIONAL : intlTelInputUtils.numberFormat.INTERNATIONAL;
746
633
  }
747
- // ensure we dont go over maxlength. we must do this here to truncate any formatting suffix, and also handle paste events
748
- var max = this.telInput.attr("maxlength");
749
- if (max && formatted.length > max) {
750
- formatted = formatted.substr(0, max);
751
- }
752
- } else {
753
- // no autoFormat, so just insert the original value
754
- formatted = val;
634
+ number = intlTelInputUtils.formatNumber(number, this.selectedCountryData.iso2, format);
755
635
  }
756
- this.telInput.val(formatted);
636
+ number = this._beforeSetNumber(number);
637
+ this.telInput.val(number);
757
638
  },
758
639
  // check if need to select a new flag based on the given number
759
- _updateFlagFromNumber: function(number, updateDefault) {
760
- // if we're in nationalMode and we're on US/Canada, make sure the number starts with a +1 so _getDialCode will be able to extract the area code
640
+ // Note: called from _setInitialState, keyup handler, setNumber
641
+ _updateFlagFromNumber: function(number, isInit) {
642
+ // if we're in nationalMode and we already have US/Canada selected, make sure the number starts with a +1 so _getDialCode will be able to extract the area code
761
643
  // update: if we dont yet have selectedCountryData, but we're here (trying to update the flag from the number), that means we're initialising the plugin with a number that already has a dial code, so fine to ignore this bit
762
644
  if (number && this.options.nationalMode && this.selectedCountryData && this.selectedCountryData.dialCode == "1" && number.charAt(0) != "+") {
763
645
  if (number.charAt(0) != "1") {
@@ -782,17 +664,17 @@ https://github.com/Bluefieldscom/intl-tel-input.git
782
664
  }
783
665
  } else if (number.charAt(0) == "+" && this._getNumeric(number).length) {
784
666
  // invalid dial code, so empty
785
- // Note: use getNumeric here because the number has not been formatted yet, so could contain bad shit
667
+ // Note: use getNumeric here because the number has not been formatted yet, so could contain bad chars
786
668
  countryCode = "";
787
669
  } else if (!number || number == "+") {
788
670
  // empty, or just a plus, so default
789
- countryCode = this.options.defaultCountry.iso2;
671
+ countryCode = this.defaultCountry;
790
672
  }
791
673
  if (countryCode !== null) {
792
- this._selectFlag(countryCode, updateDefault);
674
+ this._setFlag(countryCode, isInit);
793
675
  }
794
676
  },
795
- // check if the given number contains an unknown area code from the North American Numbering Plan i.e. the only dialCode that could be extracted was +1 but the actual number's length is >=4
677
+ // check if the given number contains an unknown area code from the North American Numbering Plan i.e. the only dialCode that could be extracted was +1 (instead of say +1 702) and the actual number's length is >=4
796
678
  _isUnknownNanp: function(number, dialCode) {
797
679
  return dialCode == "+1" && this._getNumeric(number).length >= 4;
798
680
  },
@@ -817,50 +699,58 @@ https://github.com/Bluefieldscom/intl-tel-input.git
817
699
  }
818
700
  },
819
701
  // select the given flag, update the placeholder and the active list item
820
- _selectFlag: function(countryCode, updateDefault) {
702
+ // Note: called from _setInitialState, _updateFlagFromNumber, _selectListItem, setCountry
703
+ _setFlag: function(countryCode, isInit) {
704
+ var prevCountry = this.selectedCountryData && this.selectedCountryData.iso2 ? this.selectedCountryData : {};
821
705
  // do this first as it will throw an error and stop if countryCode is invalid
822
706
  this.selectedCountryData = countryCode ? this._getCountryData(countryCode, false, false) : {};
823
- // update the "defaultCountry" - we only need the iso2 from now on, so just store that
824
- if (updateDefault && this.selectedCountryData.iso2) {
825
- // can't just make this equal to selectedCountryData as would be a ref to that object
826
- this.options.defaultCountry = {
827
- iso2: this.selectedCountryData.iso2
828
- };
707
+ // update the defaultCountry - we only need the iso2 from now on, so just store that
708
+ if (this.selectedCountryData.iso2) {
709
+ this.defaultCountry = this.selectedCountryData.iso2;
829
710
  }
830
711
  this.selectedFlagInner.attr("class", "iti-flag " + countryCode);
831
712
  // update the selected country's title attribute
832
713
  var title = countryCode ? this.selectedCountryData.name + ": +" + this.selectedCountryData.dialCode : "Unknown";
833
714
  this.selectedFlagInner.parent().attr("title", title);
715
+ if (this.options.separateDialCode) {
716
+ var dialCode = this.selectedCountryData.dialCode ? "+" + this.selectedCountryData.dialCode : "", parent = this.telInput.parent();
717
+ if (prevCountry.dialCode) {
718
+ parent.removeClass("iti-sdc-" + (prevCountry.dialCode.length + 1));
719
+ }
720
+ if (dialCode) {
721
+ parent.addClass("iti-sdc-" + dialCode.length);
722
+ }
723
+ this.selectedDialCode.text(dialCode);
724
+ }
834
725
  // and the input's placeholder
835
726
  this._updatePlaceholder();
836
- if (this.isMobile) {
837
- this.countryList.val(countryCode);
838
- } else {
839
- // update the active list item
840
- this.countryListItems.removeClass("active");
841
- if (countryCode) {
842
- this.countryListItems.find(".iti-flag." + countryCode).first().closest(".country").addClass("active");
843
- }
727
+ // update the active list item
728
+ this.countryListItems.removeClass("active");
729
+ if (countryCode) {
730
+ this.countryListItems.find(".iti-flag." + countryCode).first().closest(".country").addClass("active");
731
+ }
732
+ // on change flag, trigger a custom event
733
+ if (!isInit && prevCountry.iso2 !== countryCode) {
734
+ this.telInput.trigger("countrychange", this.selectedCountryData);
844
735
  }
845
736
  },
846
737
  // update the input placeholder to an example number from the currently selected country
847
738
  _updatePlaceholder: function() {
848
739
  if (window.intlTelInputUtils && !this.hadInitialPlaceholder && this.options.autoPlaceholder && this.selectedCountryData) {
849
- var iso2 = this.selectedCountryData.iso2, numberType = intlTelInputUtils.numberType[this.options.numberType || "FIXED_LINE"], placeholder = iso2 ? intlTelInputUtils.getExampleNumber(iso2, this.options.nationalMode, numberType) : "";
740
+ var numberType = intlTelInputUtils.numberType[this.options.numberType], placeholder = this.selectedCountryData.iso2 ? intlTelInputUtils.getExampleNumber(this.selectedCountryData.iso2, this.options.nationalMode, numberType) : "";
741
+ placeholder = this._beforeSetNumber(placeholder);
742
+ if (typeof this.options.customPlaceholder === "function") {
743
+ placeholder = this.options.customPlaceholder(placeholder, this.selectedCountryData);
744
+ }
850
745
  this.telInput.attr("placeholder", placeholder);
851
746
  }
852
747
  },
853
748
  // called when the user selects a list item from the dropdown
854
749
  _selectListItem: function(listItem) {
855
- var countryCodeAttr = this.isMobile ? "value" : "data-country-code";
856
750
  // update selected flag and active list item
857
- this._selectFlag(listItem.attr(countryCodeAttr), true);
858
- if (!this.isMobile) {
859
- this._closeDropdown();
860
- }
751
+ this._setFlag(listItem.attr("data-country-code"));
752
+ this._closeDropdown();
861
753
  this._updateDialCode(listItem.attr("data-dial-code"), true);
862
- // always fire the change event as even if nationalMode=true (and we haven't updated the input val), the system as a whole has still changed - see country-sync example. think of it as making a selection from a select element.
863
- this.telInput.trigger("change");
864
754
  // focus the input
865
755
  this.telInput.focus();
866
756
  // fix for FF and IE11 (with nationalMode=false i.e. auto inserting dial code), who try to put the cursor at the beginning the first time
@@ -873,13 +763,20 @@ https://github.com/Bluefieldscom/intl-tel-input.git
873
763
  _closeDropdown: function() {
874
764
  this.countryList.addClass("hide");
875
765
  // update the arrow
876
- this.selectedFlagInner.children(".arrow").removeClass("up");
766
+ this.selectedFlagInner.children(".iti-arrow").removeClass("up");
877
767
  // unbind key events
878
768
  $(document).off(this.ns);
879
769
  // unbind click-off-to-close
880
770
  $("html").off(this.ns);
881
771
  // unbind hover and click listeners
882
772
  this.countryList.off(this.ns);
773
+ // remove menu from container
774
+ if (this.options.dropdownContainer) {
775
+ if (!this.isMobile) {
776
+ $(window).off("scroll" + this.ns);
777
+ }
778
+ this.dropdown.detach();
779
+ }
883
780
  },
884
781
  // check if an element is visible within it's container, else scroll until it is
885
782
  _scrollTo: function(element, middle) {
@@ -899,30 +796,39 @@ https://github.com/Bluefieldscom/intl-tel-input.git
899
796
  container.scrollTop(newScrollTop - heightDifference);
900
797
  }
901
798
  },
902
- // replace any existing dial code with the new one (if not in nationalMode)
903
- // also we need to know if we're focusing for a couple of reasons e.g. if so, we want to add any formatting suffix, also if the input is empty and we're not in nationalMode, then we want to insert the dial code
904
- _updateDialCode: function(newDialCode, focusing) {
799
+ // replace any existing dial code with the new one
800
+ // Note: called from _selectListItem and setCountry
801
+ _updateDialCode: function(newDialCode, hasSelectedListItem) {
905
802
  var inputVal = this.telInput.val(), newNumber;
906
803
  // save having to pass this every time
907
804
  newDialCode = "+" + newDialCode;
908
- if (this.options.nationalMode && inputVal.charAt(0) != "+") {
909
- // if nationalMode, we just want to re-format
910
- newNumber = inputVal;
911
- } else if (inputVal) {
912
- // if the previous number contained a valid dial code, replace it
913
- // (if more than just a plus character)
805
+ if (inputVal.charAt(0) == "+") {
806
+ // there's a plus so we're dealing with a replacement (doesn't matter if nationalMode or not)
914
807
  var prevDialCode = this._getDialCode(inputVal);
915
- if (prevDialCode.length > 1) {
808
+ if (prevDialCode) {
809
+ // current number contains a valid dial code, so replace it
916
810
  newNumber = inputVal.replace(prevDialCode, newDialCode);
917
811
  } else {
918
- // if the previous number didn't contain a dial code, we should persist it
919
- var existingNumber = inputVal.charAt(0) != "+" ? $.trim(inputVal) : "";
920
- newNumber = newDialCode + existingNumber;
812
+ // current number contains an invalid dial code, so ditch it
813
+ // (no way to determine where the invalid dial code ends and the rest of the number begins)
814
+ newNumber = newDialCode;
921
815
  }
816
+ } else if (this.options.nationalMode || this.options.separateDialCode) {
817
+ // don't do anything
818
+ return;
922
819
  } else {
923
- newNumber = !this.options.autoHideDialCode || focusing ? newDialCode : "";
820
+ // nationalMode is disabled
821
+ if (inputVal) {
822
+ // there is an existing value with no dial code: prefix the new dial code
823
+ newNumber = newDialCode + inputVal;
824
+ } else if (hasSelectedListItem || !this.options.autoHideDialCode) {
825
+ // no existing value and either they've just selected a list item, or autoHideDialCode is disabled: insert new dial code
826
+ newNumber = newDialCode;
827
+ } else {
828
+ return;
829
+ }
924
830
  }
925
- this._updateVal(newNumber, null, focusing);
831
+ this.telInput.val(newNumber);
926
832
  },
927
833
  // try and extract a valid international dial code from a full telephone number
928
834
  // Note: returns the raw string inc plus character and any whitespace/dots etc
@@ -951,53 +857,80 @@ https://github.com/Bluefieldscom/intl-tel-input.git
951
857
  }
952
858
  return dialCode;
953
859
  },
860
+ // get the input val, adding the dial code if separateDialCode is enabled
861
+ _getFullNumber: function() {
862
+ var prefix = this.options.separateDialCode ? "+" + this.selectedCountryData.dialCode : "";
863
+ return prefix + this.telInput.val();
864
+ },
865
+ // remove the dial code if separateDialCode is enabled
866
+ _beforeSetNumber: function(number) {
867
+ if (this.options.separateDialCode) {
868
+ var dialCode = this._getDialCode(number);
869
+ if (dialCode) {
870
+ // US dialCode is "+1", which is what we want
871
+ // CA dialCode is "+1 123", which is wrong - should be "+1" (as it has multiple area codes)
872
+ // AS dialCode is "+1 684", which is what we want
873
+ // Solution: if the country has area codes, then revert to just the dial code
874
+ if (this.selectedCountryData.areaCodes !== null) {
875
+ dialCode = "+" + this.selectedCountryData.dialCode;
876
+ }
877
+ // a lot of numbers will have a space separating the dial code and the main number, and some NANP numbers will have a hyphen e.g. +1 684-733-1234 - in both cases we want to get rid of it
878
+ // NOTE: don't just trim all non-numerics as may want to preserve an open parenthesis etc
879
+ var start = number[dialCode.length] === " " || number[dialCode.length] === "-" ? dialCode.length + 1 : dialCode.length;
880
+ number = number.substr(start);
881
+ }
882
+ }
883
+ return this._cap(number);
884
+ },
954
885
  /********************
955
886
  * PUBLIC METHODS
956
887
  ********************/
957
888
  // this is called when the geoip call returns
958
- autoCountryLoaded: function() {
959
- if (this.options.defaultCountry == "auto") {
960
- this.options.defaultCountry = $.fn[pluginName].autoCountry;
961
- this._setInitialState();
889
+ handleAutoCountry: function() {
890
+ if (this.options.initialCountry === "auto") {
891
+ // we must set this even if there is an initial val in the input: in case the initial val is invalid and they delete it - they should see their auto country
892
+ this.defaultCountry = $.fn[pluginName].autoCountry;
893
+ // if there's no initial value in the input, then update the flag
894
+ if (!this.telInput.val()) {
895
+ this.setCountry(this.defaultCountry);
896
+ }
962
897
  this.autoCountryDeferred.resolve();
963
898
  }
964
899
  },
965
900
  // remove plugin
966
901
  destroy: function() {
967
- if (!this.isMobile) {
902
+ if (this.allowDropdown) {
968
903
  // make sure the dropdown is closed (and unbind listeners)
969
904
  this._closeDropdown();
970
- }
971
- // key events, and focus/blur events if autoHideDialCode=true
972
- this.telInput.off(this.ns);
973
- if (this.isMobile) {
974
- // change event on select country
975
- this.countryList.off(this.ns);
976
- } else {
977
905
  // click event to open dropdown
978
906
  this.selectedFlagInner.parent().off(this.ns);
979
907
  // label click hack
980
908
  this.telInput.closest("label").off(this.ns);
981
909
  }
982
- // remove markup
910
+ // unbind all events: key events, and focus/blur events if autoHideDialCode=true
911
+ this.telInput.off(this.ns);
912
+ // remove markup (but leave the original input)
983
913
  var container = this.telInput.parent();
984
914
  container.before(this.telInput).remove();
985
915
  },
986
- // extract the phone number extension if present
916
+ // get the extension from the current number
987
917
  getExtension: function() {
988
- return this.telInput.val().split(" ext. ")[1] || "";
918
+ if (window.intlTelInputUtils) {
919
+ return intlTelInputUtils.getExtension(this._getFullNumber(), this.selectedCountryData.iso2);
920
+ }
921
+ return "";
989
922
  },
990
- // format the number to the given type
991
- getNumber: function(type) {
923
+ // format the number to the given format
924
+ getNumber: function(format) {
992
925
  if (window.intlTelInputUtils) {
993
- return intlTelInputUtils.formatNumberByType(this.telInput.val(), this.selectedCountryData.iso2, type);
926
+ return intlTelInputUtils.formatNumber(this._getFullNumber(), this.selectedCountryData.iso2, format);
994
927
  }
995
928
  return "";
996
929
  },
997
930
  // get the type of the entered number e.g. landline/mobile
998
931
  getNumberType: function() {
999
932
  if (window.intlTelInputUtils) {
1000
- return intlTelInputUtils.getNumberType(this.telInput.val(), this.selectedCountryData.iso2);
933
+ return intlTelInputUtils.getNumberType(this._getFullNumber(), this.selectedCountryData.iso2);
1001
934
  }
1002
935
  return -99;
1003
936
  },
@@ -1009,77 +942,52 @@ https://github.com/Bluefieldscom/intl-tel-input.git
1009
942
  // get the validation error
1010
943
  getValidationError: function() {
1011
944
  if (window.intlTelInputUtils) {
1012
- return intlTelInputUtils.getValidationError(this.telInput.val(), this.selectedCountryData.iso2);
945
+ return intlTelInputUtils.getValidationError(this._getFullNumber(), this.selectedCountryData.iso2);
1013
946
  }
1014
947
  return -99;
1015
948
  },
1016
949
  // validate the input val - assumes the global function isValidNumber (from utilsScript)
1017
950
  isValidNumber: function() {
1018
- var val = $.trim(this.telInput.val()), countryCode = this.options.nationalMode ? this.selectedCountryData.iso2 : "";
1019
- if (window.intlTelInputUtils) {
1020
- return intlTelInputUtils.isValidNumber(val, countryCode);
1021
- }
1022
- return false;
1023
- },
1024
- // load the utils script
1025
- loadUtils: function(path) {
1026
- var that = this;
1027
- var utilsScript = path || this.options.utilsScript;
1028
- if (!$.fn[pluginName].loadedUtilsScript && utilsScript) {
1029
- // don't do this twice! (dont just check if the global intlTelInputUtils exists as if init plugin multiple times in quick succession, it may not have finished loading yet)
1030
- $.fn[pluginName].loadedUtilsScript = true;
1031
- // dont use $.getScript as it prevents caching
1032
- $.ajax({
1033
- url: utilsScript,
1034
- success: function() {
1035
- // tell all instances the utils are ready
1036
- $(".intl-tel-input input").intlTelInput("utilsLoaded");
1037
- },
1038
- complete: function() {
1039
- that.utilsScriptDeferred.resolve();
1040
- },
1041
- dataType: "script",
1042
- cache: true
1043
- });
1044
- } else {
1045
- this.utilsScriptDeferred.resolve();
1046
- }
951
+ var val = $.trim(this._getFullNumber()), countryCode = this.options.nationalMode ? this.selectedCountryData.iso2 : "";
952
+ return window.intlTelInputUtils ? intlTelInputUtils.isValidNumber(val, countryCode) : null;
1047
953
  },
1048
954
  // update the selected flag, and update the input val accordingly
1049
- selectCountry: function(countryCode) {
955
+ setCountry: function(countryCode) {
1050
956
  countryCode = countryCode.toLowerCase();
1051
957
  // check if already selected
1052
958
  if (!this.selectedFlagInner.hasClass(countryCode)) {
1053
- this._selectFlag(countryCode, true);
959
+ this._setFlag(countryCode);
1054
960
  this._updateDialCode(this.selectedCountryData.dialCode, false);
1055
961
  }
1056
962
  },
1057
963
  // set the input value and update the flag
1058
- setNumber: function(number, format, addSuffix, preventConversion, isAllowedKey) {
1059
- // ensure starts with plus
1060
- if (!this.options.nationalMode && number.charAt(0) != "+") {
1061
- number = "+" + number;
1062
- }
1063
- // we must update the flag first, which updates this.selectedCountryData, which is used later for formatting the number before displaying it
964
+ // NOTE: format arg is for public method: to allow devs to format how they want
965
+ setNumber: function(number, format) {
966
+ // we must update the flag first, which updates this.selectedCountryData, which is used for formatting the number before displaying it
1064
967
  this._updateFlagFromNumber(number);
1065
- this._updateVal(number, format, addSuffix, preventConversion, isAllowedKey);
968
+ this._updateValFromNumber(number, $.isNumeric(format), format);
1066
969
  },
1067
- // this is called when the utils are ready
1068
- utilsLoaded: function() {
1069
- // if autoFormat is enabled and there's an initial value in the input, then format it
1070
- if (this.options.autoFormat && this.telInput.val()) {
1071
- this._updateVal(this.telInput.val());
970
+ // this is called when the utils request completes
971
+ handleUtils: function() {
972
+ // if the request was successful
973
+ if (window.intlTelInputUtils) {
974
+ // if there's an initial value in the input, then format it
975
+ if (this.telInput.val()) {
976
+ this._updateValFromNumber(this.telInput.val(), this.options.formatOnInit);
977
+ }
978
+ this._updatePlaceholder();
1072
979
  }
1073
- this._updatePlaceholder();
980
+ this.utilsScriptDeferred.resolve();
1074
981
  }
1075
982
  };
1076
- // adapted to allow public functions
1077
983
  // using https://github.com/jquery-boilerplate/jquery-boilerplate/wiki/Extending-jQuery-Boilerplate
984
+ // (adapted to allow public functions)
1078
985
  $.fn[pluginName] = function(options) {
1079
986
  var args = arguments;
1080
987
  // Is the first parameter an object (options), or was omitted,
1081
988
  // instantiate a new instance of the plugin.
1082
989
  if (options === undefined || typeof options === "object") {
990
+ // collect all of the deferred objects for all instances created with this selector
1083
991
  var deferreds = [];
1084
992
  this.each(function() {
1085
993
  if (!$.data(this, "plugin_" + pluginName)) {
@@ -1125,36 +1033,53 @@ https://github.com/Bluefieldscom/intl-tel-input.git
1125
1033
  $.fn[pluginName].getCountryData = function() {
1126
1034
  return allCountries;
1127
1035
  };
1128
- $.fn[pluginName].version = "6.0.4";
1036
+ // load the utils script
1037
+ $.fn[pluginName].loadUtils = function(path, utilsScriptDeferred) {
1038
+ if (!$.fn[pluginName].loadedUtilsScript) {
1039
+ // don't do this twice! (dont just check if window.intlTelInputUtils exists as if init plugin multiple times in quick succession, it may not have finished loading yet)
1040
+ $.fn[pluginName].loadedUtilsScript = true;
1041
+ // dont use $.getScript as it prevents caching
1042
+ $.ajax({
1043
+ url: path,
1044
+ complete: function() {
1045
+ // tell all instances that the utils request is complete
1046
+ $(".intl-tel-input input").intlTelInput("handleUtils");
1047
+ },
1048
+ dataType: "script",
1049
+ cache: true
1050
+ });
1051
+ } else if (utilsScriptDeferred) {
1052
+ utilsScriptDeferred.resolve();
1053
+ }
1054
+ };
1055
+ // version
1056
+ $.fn[pluginName].version = "8.4.9";
1129
1057
  // Tell JSHint to ignore this warning: "character may get silently deleted by one or more browsers"
1130
1058
  // jshint -W100
1131
1059
  // Array of country objects for the flag dropdown.
1132
1060
  // Each contains a name, country code (ISO 3166-1 alpha-2) and dial code.
1133
1061
  // Originally from https://github.com/mledoze/countries
1134
- // then modified using the following JavaScript (NOW OUT OF DATE):
1135
- /*
1136
- var result = [];
1137
- _.each(countries, function(c) {
1138
- // ignore countries without a dial code
1139
- if (c.callingCode[0].length) {
1140
- result.push({
1141
- // var locals contains country names with localised versions in brackets
1142
- n: _.findWhere(locals, {
1143
- countryCode: c.cca2
1144
- }).name,
1145
- i: c.cca2.toLowerCase(),
1146
- d: c.callingCode[0]
1147
- });
1148
- }
1149
- });
1150
- JSON.stringify(result);
1151
- */
1152
1062
  // then with a couple of manual re-arrangements to be alphabetical
1153
1063
  // then changed Kazakhstan from +76 to +7
1154
1064
  // and Vatican City from +379 to +39 (see issue 50)
1155
1065
  // and Caribean Netherlands from +5997 to +599
1156
1066
  // and Curacao from +5999 to +599
1157
- // Removed: Åland Islands, Christmas Island, Cocos Islands, Guernsey, Isle of Man, Jersey, Kosovo, Mayotte, Pitcairn Islands, South Georgia, Svalbard, Western Sahara
1067
+ // Removed: Kosovo, Pitcairn Islands, South Georgia
1068
+ // UPDATE Sept 12th 2015
1069
+ // List of regions that have iso2 country codes, which I have chosen to omit:
1070
+ // (based on this information: https://en.wikipedia.org/wiki/List_of_country_calling_codes)
1071
+ // AQ - Antarctica - all different country codes depending on which "base"
1072
+ // BV - Bouvet Island - no calling code
1073
+ // GS - South Georgia and the South Sandwich Islands - "inhospitable collection of islands" - same flag and calling code as Falkland Islands
1074
+ // HM - Heard Island and McDonald Islands - no calling code
1075
+ // PN - Pitcairn - tiny population (56), same calling code as New Zealand
1076
+ // TF - French Southern Territories - no calling code
1077
+ // UM - United States Minor Outlying Islands - no calling code
1078
+ // UPDATE the criteria of supported countries or territories (see issue 297)
1079
+ // Have an iso2 code: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
1080
+ // Have a country calling code: https://en.wikipedia.org/wiki/List_of_country_calling_codes
1081
+ // Have a flag
1082
+ // Must be supported by libphonenumber: https://github.com/googlei18n/libphonenumber
1158
1083
  // Update: converted objects to arrays to save bytes!
1159
1084
  // Update: added "priority" for countries with the same dialCode as others
1160
1085
  // Update: added array of area codes for countries with the same dialCode as others
@@ -1166,7 +1091,7 @@ JSON.stringify(result);
1166
1091
  // Order (if >1 country with same dial code),
1167
1092
  // Area codes (if >1 country with same dial code)
1168
1093
  // ]
1169
- var allCountries = [ [ "Afghanistan (‫افغانستان‬‎)", "af", "93" ], [ "Albania (Shqipëri)", "al", "355" ], [ "Algeria (‫الجزائر‬‎)", "dz", "213" ], [ "American Samoa", "as", "1684" ], [ "Andorra", "ad", "376" ], [ "Angola", "ao", "244" ], [ "Anguilla", "ai", "1264" ], [ "Antigua and Barbuda", "ag", "1268" ], [ "Argentina", "ar", "54" ], [ "Armenia (Հայաստան)", "am", "374" ], [ "Aruba", "aw", "297" ], [ "Australia", "au", "61" ], [ "Austria (Österreich)", "at", "43" ], [ "Azerbaijan (Azərbaycan)", "az", "994" ], [ "Bahamas", "bs", "1242" ], [ "Bahrain (‫البحرين‬‎)", "bh", "973" ], [ "Bangladesh (বাংলাদেশ)", "bd", "880" ], [ "Barbados", "bb", "1246" ], [ "Belarus (Беларусь)", "by", "375" ], [ "Belgium (België)", "be", "32" ], [ "Belize", "bz", "501" ], [ "Benin (Bénin)", "bj", "229" ], [ "Bermuda", "bm", "1441" ], [ "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", "1284" ], [ "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 ], [ "Cayman Islands", "ky", "1345" ], [ "Central African Republic (République centrafricaine)", "cf", "236" ], [ "Chad (Tchad)", "td", "235" ], [ "Chile", "cl", "56" ], [ "China (中国)", "cn", "86" ], [ "Colombia", "co", "57" ], [ "Comoros (‫جزر القمر‬‎)", "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", "1767" ], [ "Dominican Republic (República Dominicana)", "do", "1", 2, [ "809", "829", "849" ] ], [ "Ecuador", "ec", "593" ], [ "Egypt (‫مصر‬‎)", "eg", "20" ], [ "El Salvador", "sv", "503" ], [ "Equatorial Guinea (Guinea Ecuatorial)", "gq", "240" ], [ "Eritrea", "er", "291" ], [ "Estonia (Eesti)", "ee", "372" ], [ "Ethiopia", "et", "251" ], [ "Falkland Islands (Islas Malvinas)", "fk", "500" ], [ "Faroe Islands (Føroyar)", "fo", "298" ], [ "Fiji", "fj", "679" ], [ "Finland (Suomi)", "fi", "358" ], [ "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", "1473" ], [ "Guadeloupe", "gp", "590", 0 ], [ "Guam", "gu", "1671" ], [ "Guatemala", "gt", "502" ], [ "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" ], [ "Israel (‫ישראל‬‎)", "il", "972" ], [ "Italy (Italia)", "it", "39", 0 ], [ "Jamaica", "jm", "1876" ], [ "Japan (日本)", "jp", "81" ], [ "Jordan (‫الأردن‬‎)", "jo", "962" ], [ "Kazakhstan (Казахстан)", "kz", "7", 1 ], [ "Kenya", "ke", "254" ], [ "Kiribati", "ki", "686" ], [ "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" ], [ "Macedonia (FYROM) (Македонија)", "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 (‫موريتانيا‬‎)", "mr", "222" ], [ "Mauritius (Moris)", "mu", "230" ], [ "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", "1664" ], [ "Morocco (‫المغرب‬‎)", "ma", "212" ], [ "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", "1670" ], [ "Norway (Norge)", "no", "47" ], [ "Oman (‫عُمان‬‎)", "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" ], [ "Romania (România)", "ro", "40" ], [ "Russia (Россия)", "ru", "7", 0 ], [ "Rwanda", "rw", "250" ], [ "Saint Barthélemy (Saint-Barthélemy)", "bl", "590", 1 ], [ "Saint Helena", "sh", "290" ], [ "Saint Kitts and Nevis", "kn", "1869" ], [ "Saint Lucia", "lc", "1758" ], [ "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", "1784" ], [ "Samoa", "ws", "685" ], [ "San Marino", "sm", "378" ], [ "São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239" ], [ "Saudi Arabia (‫المملكة العربية السعودية‬‎)", "sa", "966" ], [ "Senegal (Sénégal)", "sn", "221" ], [ "Serbia (Србија)", "rs", "381" ], [ "Seychelles", "sc", "248" ], [ "Sierra Leone", "sl", "232" ], [ "Singapore", "sg", "65" ], [ "Sint Maarten", "sx", "1721" ], [ "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" ], [ "Swaziland", "sz", "268" ], [ "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", "1868" ], [ "Tunisia (‫تونس‬‎)", "tn", "216" ], [ "Turkey (Türkiye)", "tr", "90" ], [ "Turkmenistan", "tm", "993" ], [ "Turks and Caicos Islands", "tc", "1649" ], [ "Tuvalu", "tv", "688" ], [ "U.S. Virgin Islands", "vi", "1340" ], [ "Uganda", "ug", "256" ], [ "Ukraine (Україна)", "ua", "380" ], [ "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)", "ae", "971" ], [ "United Kingdom", "gb", "44" ], [ "United States", "us", "1", 0 ], [ "Uruguay", "uy", "598" ], [ "Uzbekistan (Oʻzbekiston)", "uz", "998" ], [ "Vanuatu", "vu", "678" ], [ "Vatican City (Città del Vaticano)", "va", "39", 1 ], [ "Venezuela", "ve", "58" ], [ "Vietnam (Việt Nam)", "vn", "84" ], [ "Wallis and Futuna", "wf", "681" ], [ "Yemen (‫اليمن‬‎)", "ye", "967" ], [ "Zambia", "zm", "260" ], [ "Zimbabwe", "zw", "263" ] ];
1094
+ var allCountries = [ [ "Afghanistan (‫افغانستان‬‎)", "af", "93" ], [ "Albania (Shqipëri)", "al", "355" ], [ "Algeria (‫الجزائر‬‎)", "dz", "213" ], [ "American Samoa", "as", "1684" ], [ "Andorra", "ad", "376" ], [ "Angola", "ao", "244" ], [ "Anguilla", "ai", "1264" ], [ "Antigua and Barbuda", "ag", "1268" ], [ "Argentina", "ar", "54" ], [ "Armenia (Հայաստան)", "am", "374" ], [ "Aruba", "aw", "297" ], [ "Australia", "au", "61", 0 ], [ "Austria (Österreich)", "at", "43" ], [ "Azerbaijan (Azərbaycan)", "az", "994" ], [ "Bahamas", "bs", "1242" ], [ "Bahrain (‫البحرين‬‎)", "bh", "973" ], [ "Bangladesh (বাংলাদেশ)", "bd", "880" ], [ "Barbados", "bb", "1246" ], [ "Belarus (Беларусь)", "by", "375" ], [ "Belgium (België)", "be", "32" ], [ "Belize", "bz", "501" ], [ "Benin (Bénin)", "bj", "229" ], [ "Bermuda", "bm", "1441" ], [ "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", "1284" ], [ "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 ], [ "Cayman Islands", "ky", "1345" ], [ "Central African Republic (République centrafricaine)", "cf", "236" ], [ "Chad (Tchad)", "td", "235" ], [ "Chile", "cl", "56" ], [ "China (中国)", "cn", "86" ], [ "Christmas Island", "cx", "61", 2 ], [ "Cocos (Keeling) Islands", "cc", "61", 1 ], [ "Colombia", "co", "57" ], [ "Comoros (‫جزر القمر‬‎)", "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", "1767" ], [ "Dominican Republic (República Dominicana)", "do", "1", 2, [ "809", "829", "849" ] ], [ "Ecuador", "ec", "593" ], [ "Egypt (‫مصر‬‎)", "eg", "20" ], [ "El Salvador", "sv", "503" ], [ "Equatorial Guinea (Guinea Ecuatorial)", "gq", "240" ], [ "Eritrea", "er", "291" ], [ "Estonia (Eesti)", "ee", "372" ], [ "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", "1473" ], [ "Guadeloupe", "gp", "590", 0 ], [ "Guam", "gu", "1671" ], [ "Guatemala", "gt", "502" ], [ "Guernsey", "gg", "44", 1 ], [ "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 ], [ "Israel (‫ישראל‬‎)", "il", "972" ], [ "Italy (Italia)", "it", "39", 0 ], [ "Jamaica", "jm", "1876" ], [ "Japan (日本)", "jp", "81" ], [ "Jersey", "je", "44", 3 ], [ "Jordan (‫الأردن‬‎)", "jo", "962" ], [ "Kazakhstan (Казахстан)", "kz", "7", 1 ], [ "Kenya", "ke", "254" ], [ "Kiribati", "ki", "686" ], [ "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" ], [ "Macedonia (FYROM) (Македонија)", "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 (‫موريتانيا‬‎)", "mr", "222" ], [ "Mauritius (Moris)", "mu", "230" ], [ "Mayotte", "yt", "262", 1 ], [ "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", "1664" ], [ "Morocco (‫المغرب‬‎)", "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", "1670" ], [ "Norway (Norge)", "no", "47", 0 ], [ "Oman (‫عُمان‬‎)", "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 (Saint-Barthélemy)", "bl", "590", 1 ], [ "Saint Helena", "sh", "290" ], [ "Saint Kitts and Nevis", "kn", "1869" ], [ "Saint Lucia", "lc", "1758" ], [ "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", "1784" ], [ "Samoa", "ws", "685" ], [ "San Marino", "sm", "378" ], [ "São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239" ], [ "Saudi Arabia (‫المملكة العربية السعودية‬‎)", "sa", "966" ], [ "Senegal (Sénégal)", "sn", "221" ], [ "Serbia (Србија)", "rs", "381" ], [ "Seychelles", "sc", "248" ], [ "Sierra Leone", "sl", "232" ], [ "Singapore", "sg", "65" ], [ "Sint Maarten", "sx", "1721" ], [ "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 ], [ "Swaziland", "sz", "268" ], [ "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", "1868" ], [ "Tunisia (‫تونس‬‎)", "tn", "216" ], [ "Turkey (Türkiye)", "tr", "90" ], [ "Turkmenistan", "tm", "993" ], [ "Turks and Caicos Islands", "tc", "1649" ], [ "Tuvalu", "tv", "688" ], [ "U.S. Virgin Islands", "vi", "1340" ], [ "Uganda", "ug", "256" ], [ "Ukraine (Україна)", "ua", "380" ], [ "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)", "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 ], [ "Venezuela", "ve", "58" ], [ "Vietnam (Việt Nam)", "vn", "84" ], [ "Wallis and Futuna", "wf", "681" ], [ "Western Sahara (‫الصحراء الغربية‬‎)", "eh", "212", 1 ], [ "Yemen (‫اليمن‬‎)", "ye", "967" ], [ "Zambia", "zm", "260" ], [ "Zimbabwe", "zw", "263" ], [ "Åland Islands", "ax", "358", 1 ] ];
1170
1095
  // loop over all of the countries above
1171
1096
  for (var i = 0; i < allCountries.length; i++) {
1172
1097
  var c = allCountries[i];
@@ -1178,4 +1103,4 @@ JSON.stringify(result);
1178
1103
  areaCodes: c[4] || null
1179
1104
  };
1180
1105
  }
1181
- });
1106
+ });