PageSourceSearch

https://www.goodtimes.sc/wp-content/plugins/forms-contact/elements/readyPhone/js/intlTelInput.js?ver=1.0.0

js goodtimes.sc collected 2026-09-24 09:05:29 UTC 77,704 bytes, 1,225 lines download raw bytes

1/*
2 * International Telephone Input v7.1.1
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
7(function(factory) {
8    if (typeof define === "function" && define.amd) {
9        define([ "jquery" ], function($) {
10            factory($, window, document);
11        });
12    } else if (typeof module === "object" && module.exports) {
13        module.exports = factory(require("jquery"), window, document);
14    } else {
15        factory(jQuery, window, document);
16    }
17})(function($, window, document, undefined) {
18    "use strict";
19    // these vars persist through all instances of the plugin
20    var pluginName = "intlTelInput", id = 1, // give each instance it's own id for namespaced event handling
21    defaults = {
22        // typing digits after a valid number will be added to the extension part of the number
23        allowExtensions: false,
24        // automatically format the number according to the selected country
25        formatAsYouType: false,
26        // if there is just a dial code in the input: remove it on blur, and re-add it on focus
27        autoHideDialCode: true,
28        // add or remove input placeholder with an example number for the selected country
29        autoPlaceholder: true,
30        // append menu to a specific element
31        dropdownContainer: "",
32        // don't display these countries
33        excludeCountries: [],
34        // geoIp lookup function
35        geoIpLookup: null,
36        // initial country
37        initialCountry: "",
38        // don't insert international dial codes
39        nationalMode: true,
40        // number type to use for placeholders
41        numberType: "MOBILE",
42        // display only these countries
43        onlyCountries: [],
44        // the countries at the top of the list. defaults to united states and united kingdom
45        preferredCountries: [ "us", "gb" ],
46        // specify the path to the libphonenumber script to enable validation/formatting
47        utilsScript: ""
48    }, keys = {
49        UP: 38,
50        DOWN: 40,
51        ENTER: 13,
52        ESC: 27,
53        PLUS: 43,
54        A: 65,
55        Z: 90,
56        ZERO: 48,
57        NINE: 57,
58        SPACE: 32,
59        BSPACE: 8,
60        TAB: 9,
61        DEL: 46,
62        CTRL: 17,
63        CMD1: 91,
64        // Chrome
65        CMD2: 224
66    }, windowLoaded = false;
67    // keep track of if the window.load event has fired as impossible to check after the fact
68    $(window).load(function() {
69        windowLoaded = true;
70    });
71    function Plugin(element, options) {
72        this.element = element;
73        this.options = $.extend({}, defaults, options);
74        this._defaults = defaults;
75        // event namespace
76        this.ns = "." + pluginName + id++;
77        // Chrome, FF, Safari, IE9+
78        this.isGoodBrowser = Boolean(element.setSelectionRange);
79        this.hadInitialPlaceholder = Boolean($(element).attr("placeholder"));
80        this._name = pluginName;
81    }
82    Plugin.prototype = {
83        _init: function() {
84            // if in nationalMode, disable options relating to dial codes
85            if (this.options.nationalMode) {
86                this.options.autoHideDialCode = false;
87            }
88            // IE Mobile doesn't support the keypress event (see issue 68) which makes formatAsYouType impossible
89            if (navigator.userAgent.match(/IEMobile/i)) {
90                this.options.formatAsYouType = false;
91            }
92            // we cannot just test screen size as some smartphones/website meta tags will report desktop resolutions
93            // Note: for some reason jasmine breaks if you put this in the main Plugin function with the rest of these declarations
94            // Note: to target Android Mobiles (and not Tablets), we must find "Android" and "Mobile"
95            this.isMobile = /Android.+Mobile|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
96            if (this.isMobile) {
97                // trigger the mobile dropdown css
98                $("body").addClass("iti-mobile");
99                // on mobile, we want a full screen dropdown, so we must append it to the body
100                if (!this.options.dropdownContainer) {
101                    this.options.dropdownContainer = "body";
102                }
103            }
104            // we return these deferred objects from the _init() call so they can be watched, and then we resolve them when each specific request returns
105            // Note: again, jasmine breaks when I put these in the Plugin function
106            this.autoCountryDeferred = new $.Deferred();
107            this.utilsScriptDeferred = new $.Deferred();
108            // process all the data: onlyCountries, excludeCountries, preferredCountries etc
109            this._processCountryData();
110            // generate the markup
111            this._generateMarkup();
112            // set the initial state of the input value and the selected flag
113            this._setInitialState();
114            // start all of the event listeners: autoHideDialCode, input keydown, selectedFlag click
115            this._initListeners();
116            // utils script, and auto country
117            this._initRequests();
118            // return the deferreds
119            return [ this.autoCountryDeferred, this.utilsScriptDeferred ];
120        },
121        /********************
122   *  PRIVATE METHODS
123   ********************/
124        // prepare all of the country data, including onlyCountries, excludeCountries and preferredCountries options
125        _processCountryData: function() {
126            // process this instance's countries
127            this._processAllCountries();
128            // process the countryCodes map
129            this._processCountryCodes();
130            // process the preferredCountries
131            this._processPreferredCountries();
132        },
133        // add a country code to this.countryCodes
134        _addCountryCode: function(iso2, dialCode, priority) {
135            if (!(dialCode in this.countryCodes)) {
136                this.countryCodes[dialCode] = [];
137            }
138            var index = priority || 0;
139            this.countryCodes[dialCode][index] = iso2;
140        },
141        // filter the given countries using the process function
142        _filterCountries: function(countryArray, processFunc) {
143            var i;
144            // standardise case
145            for (i = 0; i < countryArray.length; i++) {
146                countryArray[i] = countryArray[i].toLowerCase();
147            }
148            // build instance country array
149            this.countries = [];
150            for (i = 0; i < allCountries.length; i++) {
151                if (processFunc($.inArray(allCountries[i].iso2, countryArray))) {
152                    this.countries.push(allCountries[i]);
153                }
154            }
155        },
156        // process onlyCountries or excludeCountries array if present
157        _processAllCountries: function() {
158            if (this.options.onlyCountries.length) {
159                // process onlyCountries option
160                this._filterCountries(this.options.onlyCountries, function(inArray) {
161                    // if country is in array
162                    return inArray != -1;
163                });
164            } else if (this.options.excludeCountries.length) {
165                // process excludeCountries option
166                this._filterCountries(this.options.excludeCountries, function(inArray) {
167                    // if country is not in array
168                    return inArray == -1;
169                });
170            } else {
171                this.countries = allCountries;
172            }
173        },
174        // process the countryCodes map
175        _processCountryCodes: function() {
176            this.countryCodes = {};
177            for (var i = 0; i < this.countries.length; i++) {
178                var c = this.countries[i];
179                this._addCountryCode(c.iso2, c.dialCode, c.priority);
180                // area codes
181                if (c.areaCodes) {
182                    for (var j = 0; j < c.areaCodes.length; j++) {
183                        // full dial code is country code + dial code
184                        this._addCountryCode(c.iso2, c.dialCode + c.areaCodes[j]);
185                    }
186                }
187            }
188        },
189        // process preferred countries - iterate through the preferences, fetching the country data for each one
190        _processPreferredCountries: function() {
191            this.preferredCountries = [];
192            for (var i = 0; i < this.options.preferredCountries.length; i++) {
193                var countryCode = this.options.preferredCountries[i].toLowerCase(), countryData = this._getCountryData(countryCode, false, true);
194                if (countryData) {
195                    this.preferredCountries.push(countryData);
196                }
197            }
198        },
199        // generate all of the markup for the plugin: the selected flag overlay, and the dropdown
200        _generateMarkup: function() {
201            // telephone input
202            this.telInput = $(this.element);
203            // 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 c
203ould mean the wrong number is saved (esp in nationalMode)
204            this.telInput.attr("autocomplete", "off");
205            // containers (mostly for positioning)
206            this.telInput.wrap($("<div>", {
207                "class": "intl-tel-input"
208            }));
209            this.flagsContainer = $("<div>", {
210                "class": "flag-container"
211            }).insertBefore(this.telInput);
212            // currently selected flag (displayed to left of input)
213            var selectedFlag = $("<div>", {
214                // make element focusable and tab naviagable
215                tabindex: "0",
216                "class": "selected-flag"
217            }).appendTo(this.flagsContainer);
218            this.selectedFlagInner = $("<div>", {
219                "class": "iti-flag"
220            }).appendTo(selectedFlag);
221            // CSS triangle
222            $("<div>", {
223                "class": "iti-arrow"
224            }).appendTo(selectedFlag);
225            // country dropdown: preferred countries, then divider, then all countries
226            this.countryList = $("<ul>", {
227                "class": "country-list hide"
228            });
229            if (this.preferredCountries.length) {
230                this._appendListItems(this.preferredCountries, "preferred");
231                $("<li>", {
232                    "class": "divider"
233                }).appendTo(this.countryList);
234            }
235            this._appendListItems(this.countries, "");
236            // this is useful in lots of places
237            this.countryListItems = this.countryList.children(".country");
238            // create dropdownContainer markup
239            if (this.options.dropdownContainer) {
240                this.dropdown = $("<div>", {
241                    "class": "intl-tel-input iti-container"
242                }).append(this.countryList);
243            } else {
244                this.countryList.appendTo(this.flagsContainer);
245            }
246        },
247        // add a country <li> to the countryList <ul> container
248        _appendListItems: function(countries, className) {
249            // we create so many DOM elements, it is faster to build a temp string
250            // and then add everything to the DOM in one go at the end
251            var tmp = "";
252            // for each country
253            for (var i = 0; i < countries.length; i++) {
254                var c = countries[i];
255                // open the list item
256                tmp += "<li class='country " + className + "' data-dial-code='" + c.dialCode + "' data-country-code='" + c.iso2 + "'>";
257                // add the flag
258                tmp += "<div class='flag-box'><div class='iti-flag " + c.iso2 + "'></div></div>";
259                // and the country name and dial code
260                tmp += "<span class='country-name'>" + c.name + "</span>";
261                tmp += "<span class='dial-code'>+" + c.dialCode + "</span>";
262                // close the list item
263                tmp += "</li>";
264            }
265            this.countryList.append(tmp);
266        },
267        // set the initial state of the input value and the selected flag by either extracting a dial code from the given number, or using initialCountry
268        _setInitialState: function() {
269            var val = this.telInput.val();
270            // if we already have a dial code we can go ahead and set the flag, else fall back to default
271            if (this._getDialCode(val)) {
272                this._updateFlagFromNumber(val);
273            } else if (this.options.initialCountry !== "auto") {
274                // see if we should select a flag
275                if (this.options.initialCountry) {
276                    this._setFlag(this.options.initialCountry);
277                } else {
278                    // no dial code and no initialCountry, so default to first in list
279                    this.defaultCountry = this.preferredCountries.length ? this.preferredCountries[0].iso2 : this.countries[0].iso2;
280                    if (!val) {
281                        this._setFlag(this.defaultCountry);
282                    }
283                }
284                // if empty, insert the default dial code (this function will check !nationalMode and !autoHideDialCode)
285                if (!val) {
286                    var countryData = this._getCountryData(this.defaultCountry, false, false);
287                    this._updateDialCode(countryData.dialCode, false);
288                }
289            }
290            // NOTE: if initialCountry is set to auto, that will be handled separately
291            // format
292            if (val) {
293                // this wont be run after _updateDialCode as that's only called 
293if no val
294                this._updateValFromNumber(val);
295            }
296        },
297        // initialise the main event listeners: input keyup, and click selected flag
298        _initListeners: function() {
299            var that = this;
300            this._initKeyListeners();
301            // formatAsYouType prevents the change event from firing, so we need to check for changes between focus and blur in order to manually trigger it
302            if (this.options.autoHideDialCode || this.options.formatAsYouType) {
303                this._initFocusListeners();
304            }
305            // 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
306            var label = this.telInput.closest("label");
307            if (label.length) {
308                label.on("click" + this.ns, function(e) {
309                    // if the dropdown is closed, then focus the input, else ignore the click
310                    if (that.countryList.hasClass("hide")) {
311                        that.telInput.focus();
312                    } else {
313                        e.preventDefault();
314                    }
315                });
316            }
317            // toggle country dropdown on click
318            var selectedFlag = this.selectedFlagInner.parent();
319            selectedFlag.on("click" + this.ns, function(e) {
320                // only intercept this event if we're opening the dropdown
321                // else let it bubble up to the top ("click-off-to-close" listener)
322                // we cannot just stopPropagation as it may be needed to close another instance
323                if (that.countryList.hasClass("hide") && !that.telInput.prop("disabled") && !that.telInput.prop("readonly")) {
324                    that._showDropdown();
325                }
326            });
327            // open dropdown list if currently focused
328            this.flagsContainer.on("keydown" + that.ns, function(e) {
329                var isDropdownHidden = that.countryList.hasClass("hide");
330                if (isDropdownHidden && (e.which == keys.UP || e.which == keys.DOWN || e.which == keys.SPACE || e.which == keys.ENTER)) {
331                    // prevent form from being submitted if "ENTER" was pressed
332                    e.preventDefault();
333                    // prevent event from being handled again by document
334                    e.stopPropagation();
335                    that._showDropdown();
336                }
337                // allow navigation from dropdown to input on TAB
338                if (e.which == keys.TAB) {
339                    that._closeDropdown();
340                }
341            });
342        },
343        // init many requests: utils script / geo ip lookup
344        _initRequests: function() {
345            var that = this;
346            // if the user has specified the path to the utils script, fetch it on window.load, else resolve
347            if (this.options.utilsScript) {
348                // if the plugin is being initialised after the window.load event has already been fired
349                if (windowLoaded) {
350                    $.fn[pluginName].loadUtils(this.options.utilsScript, this.utilsScriptDeferred);
351                } else {
352                    // wait until the load event so we don't block any other requests e.g. the flags image
353                    $(window).load(function() {
354                        $.fn[pluginName].loadUtils(that.options.utilsScript, that.utilsScriptDeferred);
355                    });
356                }
357            } else {
358                this.utilsScriptDeferred.resolve();
359            }
360            if (this.options.initialCountry === "auto") {
361                this._loadAutoCountry();
362            } else {
363                this.autoCountryDeferred.resolve();
364            }
365        },
366        // perform the geo ip lookup
367        _loadAutoCountry: function() {
368            var that = this;
369            // check for cookie
370            var cookieAutoCountry = window.Cookies ? Cookies.get("itiAutoCountry") : "";
371            if (cookieAutoCountry) {
372                $.fn[pluginName].autoCountry = cookieAutoCountry;
373            }
374            // 3 options:
375            // 1) already loaded (we're done)
376            // 2) not already started loading (start)
377            // 3) already started loading (do nothing - just wait for loading callback to fire)
378            if ($.fn[pluginName].autoCountry) {
379                this.handleAutoCountry();
380            } else if (!$.fn[pluginName].startedLoadingAutoCountry) {
381                // don't do this twice!
382                $.fn[pluginName].startedLoadingAutoCountry = true;
383                if (typeof this.options.geoIpLookup === "function") {
384                    this.options.geoIpLookup(function(countryCode) {
385                        $.fn[pluginName].autoCountry = countryCode.toLowerCase();
386                        if (window.Cookies) {
387                            Cookies.set("itiAutoCountry", $.fn[pluginName].autoCountry, {
388                                path: "/"
389                            });
390                        }
391                        // tell all instances the auto country is ready
392                        // TODO: this should just be the current instances
393                        // 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.
394                        setTimeout(function() {
395                            $(".intl-tel-input input").intlTelInput("handleAutoCountry");
396                        });
397                    });
398                }
399            }
400        },
401        // initialize any key listeners
402        _initKeyListeners: function() {
403            var that = this;
404            if (this.options.formatAsYouType) {
405                // format number and update flag on keypress
406                // use keypress event as we want to ignore all input except for a select few keys,
407                // but we dont want to ignore the navigation keys like the arrows etc.
408                // 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...
409                this.telInput.on("keypress" + this.ns, function(e) {
410                    // 32 is space, and after that it's all chars (not meta/nav keys)
411                    // this fix is needed for Firefox, which triggers keypress event for some meta/nav keys
412                    // Update: also ignore if this is a metaKey e.g. FF and Safari trigger keypress on the v of Ctrl+v
413                    // Update: also ignore if ctrlKey (FF on Windows/Ubuntu)
414                    // Update: also check that we have utils before we do any formatAsYouType stuff
415                    if (e.which >= keys.SPACE && !e.ctrlKey && !e.metaKey && window.intlTelInputUtils && !that.telInput.prop("readonly")) {
416                        e.preventDefault();
417                        // allowed keys are just numeric keys and plus
418                        // 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
419                        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
420                        isBelowMax = max ? val.length < max : true;
421                        // first: ensure we dont go over maxlength. we must do this here to prevent adding digits in the middle of the number
422                        // 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
423                        if (isBelowMax && (isAllowedKey || noSelection)) {
424                            var newChar = isAllowedKey ? String.fromCharCode(e.which) : null;
425                            that._handleInputKey(newChar, true, isAllowedKey
425);
426                            // if something has changed, trigger the input event (which was otherwised squashed by the preventDefault)
427                            if (val != that.telInput.val()) {
428                                that.telInput.trigger("input");
429                            }
430                        }
431                        if (!isAllowedKey) {
432                            that._handleInvalidKey();
433                        }
434                    }
435                });
436            }
437            // handle cut/paste event (now supported in all major browsers)
438            this.telInput.on("cut" + this.ns + " paste" + this.ns, function() {
439                // hack because "paste" event is fired before input is updated
440                setTimeout(function() {
441                    if (that.options.formatAsYouType && window.intlTelInputUtils) {
442                        var cursorAtEnd = that.isGoodBrowser && that.telInput[0].selectionStart == that.telInput.val().length;
443                        that._handleInputKey(null, cursorAtEnd, true);
444                        that._ensurePlus();
445                    } else {
446                        // if no formatAsYouType, just update flag
447                        that._updateFlagFromNumber(that.telInput.val());
448                    }
449                });
450            });
451            // handle keyup event
452            // if formatAsYouType enabled: we use keyup to catch delete events (after the fact)
453            // if no formatAsYouType, this is used to update the flag
454            this.telInput.on("keyup" + this.ns, function(e) {
455                // 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).
456                // ALSO: ignore keyup if readonly
457                if (e.which == keys.ENTER || that.telInput.prop("readonly")) {} else if (that.options.formatAsYouType && window.intlTelInputUtils) {
458                    // cursorAtEnd defaults to false for bad browsers else they would never get a reformat on delete
459                    var cursorAtEnd = that.isGoodBrowser && that.telInput[0].selectionStart == that.telInput.val().length;
460                    if (!that.telInput.val()) {
461                        // if they just cleared the input, update the flag to the default
462                        that._updateFlagFromNumber("");
463                    } else if (e.which == keys.DEL && !cursorAtEnd || e.which == keys.BSPACE) {
464                        // if delete in the middle: reformat with no suffix (no need to reformat if delete at end)
465                        // if backspace: reformat with no suffix (need to reformat if at end to remove any lingering suffix - this is a feature)
466                        // important to remember never to add suffix on any delete key as can confuse ie8 so you can never delete a formatting char at the end
467                        that._handleInputKey(null, false, false);
468                    }
469                    that._ensurePlus();
470                } else {
471                    // if no formatAsYouType, just update flag
472                    that._updateFlagFromNumber(that.telInput.val());
473                }
474            });
475        },
476        // if not in nationalMode: prevent deleting the plus and put the cursor back in the right place
477        _ensurePlus: function() {
478            if (!this.options.nationalMode) {
479                var val = this.telInput.val(), input = this.telInput[0];
480                if (val.charAt(0) != "+") {
481                    // newCursorPos is current pos + 1 to account for the plus we are about to add
482                    var newCursorPos = this.isGoodBrowser ? input.selectionStart + 1 : 0;
483                    this.telInput.val("+" + val);
484                    if (this.isGoodBrowser) {
485                        input.setSelectionRange(newCursorPos, newCursorPos);
486                    }
487                }
488            }
489        },
490        // alert the user to an invalid key event
491        _handleInvalidKey: function() {
492            var that = this;
493            this.telInput.trigger("invalidkey").addClass("iti-invalid-key");
494            setTimeout(function() {
495                that.telInput.removeClass("iti-invalid-key");
496            }, 100);
497        },
498        // when formatAsYouType is enabled: handle various key events on the input:
499        // 1) adding a new number character, which will replace any selection, reformat, and preserve the cursor position
500        // 2) reformatting on backspace/delete
501        // 3) cut/paste event
502        _handleInputKey: function(newNumericChar, addSuffix, isAllowedKey) {
503            var val = this.telInput.val(), cleanBefore = this._getClean(val), originalLeftChars, // raw DOM element
504            input = this.telInput[0], digitsOnRight = 0;
505            if (this.isGoodBrowser) {
506                // 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
507                digitsOnRight = this._getDigitsOnRight(val, input.selectionEnd);
508                // if handling a new number character: insert it in the right place
509                if (newNumericChar) {
510                    // replace any selection they may have made with the new char
511                    val = val.substr(0, input.selectionStart) + newNumericChar + val.substring(input.selectionEnd, val.length);
512                } else {
513                    // 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.
514                    // UPDATE: now have to store 2 chars as extensions formatting contains 2 spaces so you need to be able to distinguish
515                    originalLeftChars = val.substr(input.selectionStart - 2, 2);
516                }
517            } else if (newNumericChar) {
518                val += newNumericChar;
519            }
520            // ensure starts with plus
521            if (!this.options.nationalMode && val.charAt(0) != "+") {
522                val = "+" + val;
523            }
524            // we must update the flag first, which updates this.selectedCountryData, which is used later for formatting the number before displaying it
525            this._updateFlagFromNumber(val);
526            this._updateValAsYouType(val, addSuffix, isAllowedKey);
527            // update the cursor position
528            if (this.isGoodBrowser) {
529                var newCursor;
530                val = this.telInput.val();
531                // if it was at the end, keep it there
532                if (!digitsOnRight) {
533                    newCursor = val.length;
534                } else {
535                    // else count in the same number of digits from the right
536                    newCursor = this._getCursorFromDigitsOnRight(val, digitsOnRight);
537                    // but if delete/paste etc, keep going left until hit the same left char as before
538                    if (!newNumericChar) {
539                        newCursor = this._getCursorFromLeftChar(val, newCursor, originalLeftChars);
540                    }
541                }
542                // set the new cursor
543                input.setSelectionRange(newCursor, newCursor);
544            }
545        },
546        // try to formatNumberAsYouType, then update the input val
547        // NOTE: called from _handleInputKey, _updateDialCode (when change flag) and in the focus handler for autoHideDialCode
548        _updateValAsYouType: function(val, addSuffix, isAllowedKey) {
549            var formatted;
550            if (window.intlTelInputUtils && this.selectedCountryData) {
551                formatted = this._cap(intlTelInputUtils.formatNumberAsYouType(val, this.selectedCountryData.iso2, addSuffix, this.options.allowExtensions, isAllowedKey));
552            } else {
553                formatted = val;
554            }
555            this.telInput.val(formatted);
556        },
557        // adhere to the input's maxlength attr
558        _cap: function(number) {
559            var max = this.telInput.attr("maxlength");
560            return max && number.length > max ? number.substr(0, max) : number;
561        },
562        // 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
563        _getCursorFromLeftChar: function(val, guessCursor, originalLeftChars) {
564            for (var i = guessCursor; i > 0; i--) {
565                var leftChar = val.charAt(i - 1);
566                // UPDATE: now have to store 2 chars as extensions formatting contains 2 spaces so you need to be able to distinguish
567                if ($.isNumeric(leftChar) || val.substr(i - 2, 2) == originalLeftChars) {
568                    return i;
569                }
570            }
571            return 0;
572        },
573        // after a reformat we need to make sure there are still the same number of digits to the right of the cursor
574        _getCursorFromDigitsOnRight: function(val, digitsOnRight) {
575            for (var i = val.length - 1; i >= 0; i--) {
576                if ($.isNumeric(val.charAt(i)) && --digitsOnRight === 0) {
577                    return i;
578                }
579            }
580            return 0;
581        },
582        // get the number of numeric digits to the right of the cursor so we can reposition the cursor correctly after the reformat has happened
583        _getDigitsOnRight: function(val, selectionEnd) {
584            var digitsOnRight = 0;
585            for (var i = selectionEnd; i < val.length; i++) {
586                if ($.isNumeric(val.charAt(i))) {
587                    digitsOnRight++;
588                }
589            }
590            return digitsOnRight;
591        },
592        // listen for focus and blur
593        _initFocusListeners: function() {
594            var that = this;
595            if (this.options.autoHideDialCode) {
596                // 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
597                this.telInput.on("mousedown" + this.ns, function(e) {
598                    if (!that.telInput.is(":focus") && !that.telInput.val()) {
599                        e.preventDefault();
600                        // but this also cancels the focus, so we must trigger that manually
601                        that.telInput.focus();
602                    }
603                });
604            }
605            this.telInput.on("focus" + this.ns, function(e) {
606                var value = that.telInput.val();
607                // save this to compare on blur
608                that.telInput.data("focusVal", value);
609                // on focus: if empty, insert the dial code for the currently selected flag
610                if (that.options.autoHideDialCode && !value && !that.telInput.prop("readonly") && that.selectedCountryData.dialCode) {
611                    // apply as-you-type-formatting as some dial codes are long enough
612                    that._updateValAsYouType("+" + that.selectedCountryData.dialCode, true, false);
613                    // 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
614                    that.telInput.one("keypress.plus" + that.ns, function(e) {
615                        if (e.which == keys.PLUS) {
616                            // if formatAsYouType 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 "+").
617                            var newVal = that.options.formatAsYouType && window.intlTelInputUtils ? "+" : "";
618                            that.telInput.val(newVal);
619                        }
620                    });
621                    // 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
622                    setTimeout(function() {
623                        var input = that.telInput[0];
624                        if (that.isGoodBrowser) {
625                            var len = that.telInput.val().length;
626                            input.setSelectionRange(len, len);
627                        }
628                    });
629                }
630            });
631            this.telInput.on("blur" + this.ns, function() {
632                if (that.options.autoHideDialCode) {
633                    // on blur: if just a dial code then remove it
634                    var value = that.telInput.val(), startsPlus = value.charAt(0) == "+";
635                    if (startsPlus) {
636                        var numeric = that._getNumeric(value);
637                        // if just a plus, or if just a dial code
638                        if (!numeric || that.selectedCountryData.dialCode == numeric) {
639                            that.telInput.val("");
640                        }
641                    }
642                    // remove the keypress listener we added on focus
643                    that.telInput.off("keypress.plus" + that.ns);
644                }
645                // if formatAsYouType, we must manually trigger change event if value has changed
646                if (that.options.formatAsYouType && window.intlTelInputUtils && that.telInput.val() != that.telInput.data("focusVal")) {
647                    that.telInput.trigger("change");
648                }
649            });
650        },
651        // extract the numeric digits from the given string
652        _getNumeric: function(s) {
653            return s.replace(/\D/g, "");
654        },
655        // extract the clean number (numeric with optionally plus)
656        _getClean: function(s) {
657            var prefix = s.charAt(0) == "+" ? "+" : "";
658            return prefix + this._getNumeric(s);
659        },
660        // show the dropdown
661        _showDropdown: function() {
662            this._setDropdownPosition();
663            // update highlighting and scroll to active list item
664            var activeListItem = this.countryList.children(".active");
665            if (activeListItem.length) {
666                this._highlightListItem(activeListItem);
667                this._scrollTo(activeListItem);
668            }
669            // bind all the dropdown-related listeners: mouseover, click, click-off, keydown
670            this._bindDropdownListeners();
671            // update the arrow
672            this.selectedFlagInner.children(".iti-arrow").addClass("up");
673        },
674        // decide where to position dropdown (depends on position within viewport, and scroll)
675        _setDropdownPosition: function() {
676            var that = this;
677            if (this.options.dropdownContainer) {
678                this.dropdown.appendTo(this.options.dropdownContainer);
679            }
680            // show the menu and grab the dropdown height
681            this.dropdownHeight = this.countryList.removeClass("hide").outerHeight();
682            if (!this.isMobile) {
683                var pos = this.telInput.offset(), inputTop = pos.top, windowTop = $(window).scrollTop(), // dropdownFitsBelow = (dropdownBottom < windowBottom)
684                dropdownFitsBelow = inputTop + this.telInput.outerHeight() + this.dropdownHeight < windowTop + $(window).height(), dropdownFitsAbove = inputTop - this.dropdownHeight > windowTop;
685                // by default, the dropdown will be below the input. If we want to position it above the input, we add the dropup class.
686                this.countryList.toggleClass("dropup", !dropdownFitsBelow && dropdownFitsAbove);
687                // if dropdownContainer is enabled, calculate postion
688                if (this.options.dropdownContainer) {
689                    // 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.
690                    var extraTop = !dropdownFitsBelow && dropdownFitsAbove ? 0 : this.telInput.innerHeight();
691                    // calculate placement
692                    this.dropdown.css({
693                        top: inputTop + extraTop,
694                        left: pos.left
695                    });
696                    // close menu on window scroll
697                    $(window).on("scroll" + this.ns, function() {
698                        that._closeDropdown();
699                    });
700                }
701            }
702        },
703        // we only bind dropdown listeners when the dropdown is open
704        _bindDropdownListeners: function() {
705            var that = this;
706            // when mouse over a list item, just highlight that one
707            // we add the class "highlight", so if they hit "enter" we know which one to select
708            this.countryList.on("mouseover" + this.ns, ".country", function(e) {
709                that._highlightListItem($(this));
710            });
711            // listen for country selection
712            this.countryList.on("click" + this.ns, ".country", function(e) {
713                that._selectListItem($(this));
714            });
715            // click off to close
716            // (except when this initial opening click is bubbling up)
717            // we cannot just stopPropagation as it may be needed to close another instance
718            var isOpening = true;
719            $("html").on("click" + this.ns, function(e) {
720                if (!isOpening) {
721                    that._closeDropdown();
722                }
723                isOpening = false;
724            });
725            // listen for up/down scrolling, enter to select, or letters to jump to country name.
726            // use keydown as keypress doesn't fire for non-char keys and we want to catch if they
727            // just hit down and hold it to scroll down (no keyup event).
728            // listen on the document because that's where key events are triggered if no input has focus
729            var query = "", queryTimer = null;
730            $(document).on("keydown" + this.ns, function(e) {
731                // prevent down key from scrolling the whole page,
732                // and enter key from submitting a form etc
733                e.preventDefault();
734                if (e.which == keys.UP || e.which == keys.DOWN) {
735                    // up and down to navigate
736                    that._handleUpDownKey(e.which);
737                } else if (e.which == keys.ENTER) {
738                    // enter to select
739                    that._handleEnterKey();
740                } else if (e.which == keys.ESC) {
741                    // esc to close
742                    that._closeDropdown();
743                } else if (e.which >
743= keys.A && e.which <= keys.Z || e.which == keys.SPACE) {
744                    // upper case letters (note: keyup/keydown only return upper case letters)
745                    // jump to countries that start with the query string
746                    if (queryTimer) {
747                        clearTimeout(queryTimer);
748                    }
749                    query += String.fromCharCode(e.which);
750                    that._searchForCountry(query);
751                    // if the timer hits 1 second, reset the query
752                    queryTimer = setTimeout(function() {
753                        query = "";
754                    }, 1e3);
755                }
756            });
757        },
758        // highlight the next/prev item in the list (and ensure it is visible)
759        _handleUpDownKey: function(key) {
760            var current = this.countryList.children(".highlight").first();
761            var next = key == keys.UP ? current.prev() : current.next();
762            if (next.length) {
763                // skip the divider
764                if (next.hasClass("divider")) {
765                    next = key == keys.UP ? next.prev() : next.next();
766                }
767                this._highlightListItem(next);
768                this._scrollTo(next);
769            }
770        },
771        // select the currently highlighted item
772        _handleEnterKey: function() {
773            var currentCountry = this.countryList.children(".highlight").first();
774            if (currentCountry.length) {
775                this._selectListItem(currentCountry);
776            }
777        },
778        // find the first list item whose name starts with the query string
779        _searchForCountry: function(query) {
780            for (var i = 0; i < this.countries.length; i++) {
781                if (this._startsWith(this.countries[i].name, query)) {
782                    var listItem = this.countryList.children("[data-country-code=" + this.countries[i].iso2 + "]").not(".preferred");
783                    // update highlighting and scroll
784                    this._highlightListItem(listItem);
785                    this._scrollTo(listItem, true);
786                    break;
787                }
788            }
789        },
790        // check if (uppercase) string a starts with string b
791        _startsWith: function(a, b) {
792            return a.substr(0, b.length).toUpperCase() == b;
793        },
794        // update the input's value to the given val
795        // NOTE: this is called from _setInitialState, handleUtils and setNumber
796        _updateValFromNumber: function(number) {
797            // if nationalMode and number starts "+", then first we convert it e.g. add a zero if converting a GB number from intl to ntl mode
798            if (window.intlTelInputUtils && this.selectedCountryData && this.options.nationalMode && number.charAt(0) === "+") {
799                number = intlTelInputUtils.formatNumber(number, this.selectedCountryData.iso2, intlTelInputUtils.numberFormat.NATIONAL);
800            }
801            // we always finish by applying as-you-type-formatting because that's what will happen if the user types anything
802            // NOTE: the result of the above formatNumber call will not be pretty if the number was incomplete
803            this._updateValAsYouType(number, false, false);
804        },
805        // check if need to select a new flag based on the given number
806        _updateFlagFromNumber: function(number) {
807            // 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
808            // 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
809            if (number && this.options.nationalMode && this.selectedCountryData && this.selectedCountryData.dialCode == "1" && number.charAt(0) != "+") {
810                if (number.charAt(0) != "1") {
811                    number = "1" + number;
812                }
813                number = "+" + number;
814            }
815            // try and extract valid dial code from input
816            var dialCode = this._getDialCode(number), countryCode = null;
817            if (dialCode) {
818                // check if one of the matching countries is already selected
819                var countryCodes = this.countryCodes[this._getNumeric(dialCode)], alreadySelected = this.selectedCountryData && $.inArray(this.selectedCountryData.iso2, countryCodes) != -1;
820                // if a matching country is not already selected (or this is an unknown NANP area code): choose the first in the list
821                if (!alreadySelected || this._isUnknownNanp(number, dialCode)) {
822                    // if using onlyCountries option, countryCodes[0] may be empty, so we must find the first non-empty index
823                    for (var j = 0; j < countryCodes.length; j++) {
824                        if (countryCodes[j]) {
825                            countryCode = countryCodes[j];
826                            break;
827                        }
828                    }
829                }
830            } else if (number.charAt(0) == "+" && this._getNumeric(number).length) {
831                // invalid dial code, so empty
832                // Note: use getNumeric here because the number has not been formatted yet, so could contain bad chars
833                countryCode = "";
834            } else if (!number || number == "+") {
835                // empty, or just a plus, so default
836                countryCode = this.defaultCountry;
837            }
838            if (countryCode !== null) {
839                this._setFlag(countryCode);
840            }
841        },
842        // 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
843        _isUnknownNanp: function(number, dialCode) {
844            return dialCode == "+1" && this._getNumeric(number).length >= 4;
845        },
846        // remove highlighting from other list items and highlight the given item
847        _highlightListItem: function(listItem) {
848            this.countryListItems.removeClass("highlight");
849            listItem.addClass("highlight");
850        },
851        // find the country data for the given country code
852        // the ignoreOnlyCountriesOption is only used during init() while parsing the onlyCountries array
853        _getCountryData: function(countryCode, ignoreOnlyCountriesOption, allowFail) {
854            var countryList = ignoreOnlyCountriesOption ? allCountries : this.countries;
855            for (var i = 0; i < countryList.length; i++) {
856                if (countryList[i].iso2 == countryCode) {
857                    return countryList[i];
858                }
859            }
860            if (allowFail) {
861                return null;
862            } else {
863                throw new Error("No country data for '" + countryCode + "'");
864            }
865        },
866        // select the given flag, update the placeholder and the active list item
867        _setFlag: function(countryCode) {
868            // do this first as it will throw an error and stop if c
868ountryCode is invalid
869            this.selectedCountryData = countryCode ? this._getCountryData(countryCode, false, false) : {};
870            // update the defaultCountry - we only need the iso2 from now on, so just store that
871            if (this.selectedCountryData.iso2) {
872                // can't just make this equal to selectedCountryData as would be a ref to that object
873                this.defaultCountry = this.selectedCountryData.iso2;
874            }
875            this.selectedFlagInner.attr("class", "iti-flag " + countryCode);
876            // update the selected country's title attribute
877            var title = countryCode ? this.selectedCountryData.name + ": +" + this.selectedCountryData.dialCode : "Unknown";
878            this.selectedFlagInner.parent().attr("title", title);
879            // and the input's placeholder
880            this._updatePlaceholder();
881            // update the active list item
882            this.countryListItems.removeClass("active");
883            if (countryCode) {
884                this.countryListItems.find(".iti-flag." + countryCode).first().closest(".country").addClass("active");
885            }
886        },
887        // update the input placeholder to an example number from the currently selected country
888        _updatePlaceholder: function() {
889            if (window.intlTelInputUtils && !this.hadInitialPlaceholder && this.options.autoPlaceholder && this.selectedCountryData) {
890                var iso2 = this.selectedCountryData.iso2, numberType = intlTelInputUtils.numberType[this.options.numberType], placeholder = iso2 ? intlTelInputUtils.getExampleNumber(iso2, this.options.nationalMode, numberType) : "";
891                if (typeof this.options.customPlaceholder === "function") {
892                    placeholder = this.options.customPlaceholder(placeholder, this.selectedCountryData);
893                }
894                this.telInput.attr("placeholder", placeholder);
895            }
896        },
897        // called when the user selects a list item from the dropdown
898        _selectListItem: function(listItem) {
899            // update selected flag and active list item
900            this._setFlag(listItem.attr("data-country-code"));
901            this._closeDropdown();
902            this._updateDialCode(listItem.attr("data-dial-code"), true);
903            // trigger a custom event as even in nationalMode the state has changed
904            this.telInput.trigger("country-change");
905            // focus the input
906            this.telInput.focus();
907            // 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
908            if (this.isGoodBrowser) {
909                var len = this.telInput.val().length;
910                this.telInput[0].setSelectionRange(len, len);
911            }
912        },
913        // close the dropdown and unbind any listeners
914        _closeDropdown: function() {
915            this.countryList.addClass("hide");
916            // update the arrow
917            this.selectedFlagInner.children(".iti-arrow").removeClass("up");
918            // unbind key events
919            $(document).off(this.ns);
920            // unbind click-off-to-close
921            $("html").off(this.ns);
922            // unbind hover and click listeners
923            this.countryList.off(this.ns);
924            // remove menu from container
925            if (this.options.dropdownContainer) {
926                if (!this.isMobile) {
927                    $(window).off("scroll" + this.ns);
928                }
929                this.dropdown.detach();
930            }
931        },
932        // check if an element is visible within it's container, else scroll until it is
933        _scrollTo: function(element, middle) {
934            var container = this.countryList, containerHeight = container.height(), containerTop = container.offset().top, containerBottom = containerTop + containerHeight, elementHeight = element.outerHeight(), elementTop = element.offset().top, elementBottom = elementTop + elementHeight, newScrollTop = elementTop - containerTop + container.scrollTop(), middleOffset = containerHeight / 2 - elementHeight / 2;
935            if (elementTop < containerTop) {
936                // scroll up
937                if (middle) {
938                    newScrollTop -= middleOffset;
939                }
940                container.scrollTop(newScrollTop);
941            } else if (elementBottom > containerBottom) {
942                // scroll down
943                if (middle) {
944                    newScrollTop += middleOffset;
945                }
946                var heightDifference = containerHeight - elementHeight;
947                container.scrollTop(newScrollTop - heightDifference);
948            }
949        },
950        // replace any existing dial code with the new one (if not in nationalMode)
951        // 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
952        _updateDialCode: function(newDialCode, focusing) {
953            var inputVal = this.telInput.val(), newNumber;
954            // save having to pass this every time
955            newDialCode = "+" + newDialCode;
956            if (this.options.nationalMode && inputVal.charAt(0) != "+") {
957                // if nationalMode, we just want to re-format
958                newNumber = inputVal;
959            } else if (inputVal) {
960                // if the previous number contained a valid dial code, replace it
961                // (if more than just a plus character)
962                var prevDialCode = this._getDialCode(inputVal);
963                if (prevDialCode.length > 1) {
964                    newNumber = inputVal.replace(prevDialCode, newDialCode);
965                } else {
966                    // if the previous number didn't contain a dial code, we should persist it
967                    var existingNumber = inputVal.charAt(0) != "+" ? $.trim(inputVal) : "";
968                    newNumber = newDialCode + existingNumber;
969                }
970            } else {
971                newNumber = !this.options.autoHideDialCode || focusing ? newDialCode : "";
972            }
973            this._updateValAsYouType(newNumber, focusing, false);
974        },
975        // try and extract a valid international dial code from a full telephone number
976        // Note: returns the raw string inc plus character and any whitespace/dots etc
977        _getDialCode: function(number) {
978            var dialCode = "";
979            // only interested in international numbers (starting with a plus)
980            if (number.charAt(0) == "+") {
981                var numericChars = "";
982                // iterate over chars
983                for (var i = 0; i < number.length; i++) {
984                    var c = number.charAt(i);
985                    // if char is number
986                    if ($.isNumeric(c)) {
987                        numericChars += c;
988                        // if current numericChars make a valid dial code
989                        if (this.countryCodes[numericChars]) {
990                            // store the actual raw string (useful for matching later)
991                            dialCode = number.substr(0, i + 1);
992                        }
993                        // longest dial code is 4 chars
994                        if (numericChars.length == 4) {
995                            break;
996                        }
997                    }
998                }
999            }
1000            return dialCode;
1001        },
1002        /********************
1003   *  PUBLIC METHODS
1004   ********************/
1005        // this is called when the geoip call returns
1006        handleAutoCountry: function() {
1007            if (this.options.initialCountry === "auto") {
1008                // 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
1009                this.defaultCountry = $.fn[pluginName].autoCountry;
1010                // if there's no initial value in the input, then update the flag
1011                if (!this.telInput.val()) {
1012                    this.setCountry(this.defaultCountry);
1013                }
1014                this.autoCountryDeferred.resolve();
1015            }
1016        },
1017        // remove plugin
1018        destroy: function() {
1019            // make sure the dropdown is closed (and unbind listeners)
1020            this._closeDropdown();
1021            // key events, and focus/blur events if autoHideDialCode=true
1022            this.telInput.off(this.ns);
1023            // click event to open dropdown
1024            this.selectedFlagInner.parent().off(this.ns);
1025            // label click hack
1026            this.telInput.closest("label").off(this.ns);
1027            // remove markup
1028            var container = this.telInput.parent();
1029            container.before(this.telInput).remove();
1030        },
1031        // extract the phone number extension if present
1032        getExtension: function() {
1033            return this.telInput.val().split(" ext. ")[1] || "";
1034        },
1035        // format the number to the given format
1036        getNumber: function(format) {
1037            if (window.intlTelInputUtils) {
1038                return intlTelInputUtils.formatNumber(this.telInput.val(), this.selectedCountryData.iso2, format);
1039            }
1040            return "";
1041        },
1042        // get the type of the entered number e.g. landline/mobile
1043        getNumberType: function() {
1044            if (window.intlTelInputUtils) {
1045                return intlTelInputUtils.getNumberType(this.telInput.val(), this.selectedCountryData.iso2);
1046            }
1047            return -99;
1048        },
1049        // get the country data for the currently selected flag
1050        getSelectedCountryData: function() {
1051            // if this is undefined, the plugin will return it's instance instead, so in that case an empty object makes more sense
1052            return this.selectedCountryData || {};
1053        },
1054        // get the validation error
1055        getValidationError: function() {
1056            if (window.intlTelInputUtils) {
1057                return intlTelInputUtils.getValidationError(this.telInput.val(), this.selectedCountryData.iso2);
1058            }
1059            return -99;
1060        },
1061        // validate the input val - assumes the global function isValidNumber (from utilsScript)
1062        isValidNumber: function() {
1063            var val = $.trim(this.telInput.val()), countryCode = this.options.nationalMode ? this.selectedCountryData.iso2 : "";
1064            if (window.intlTelInputUtils) {
1065                return intlTelInputUtils.isValidNumber(val, countryCode);
1066            }
1067            return false;
1068        },
1069        // update the selected flag, and update the input val accordingly ()
1070        setCountry: function(countryCode) {
1071            countryCode = countryCode.toLowerCase();
1072            // check if already selected
1073            if (!this.selectedFlagInner.hasClass(countryCode)) {
1074                this._setFlag(countryCode);
1075                this._updateDialCode(this.selectedCountryData.dialCode, false);
1076            }
1077        },
1078        // set the input value and update the flag
1079        // NOTE: format arg is for public method: to allow devs to format how they want
1080        setNumber: function(number, format) {
1081            // we must update the flag first, which updates this.selectedCountryData, which is used later for formatting the number before displaying it
1082            this._updateFlagFromNumber(number);
1083            // if they specify a format then do that
1084            if ($.isNumeric(format) && window.intlTelInputUtils && this.selectedCountryData) {
1085                this.telInput.val(this._cap(intlTelInputUtils.formatNumber(val, this.selectedCountryData.iso2, format)));
1086            } else {
1087                this._updateValFromNumber(number);
1088            }
1089        },
1090        // this is called when the utils request completes
1091        handleUtils: function() {
1092            // if the request was successful
1093            if (window.intlTelInputUtils) {
1094                // if formatAsYouType is enabled and there's an initial value in the input, then format it
1095                if (this.telInput.val()) {
1096                    this._updateValFromNumber(this.telInput.val());
1097                }
1098                this._updatePlaceholder();
1099            }
1100            this.utilsScriptDeferred.resolve();
1101        }
1102    };
1103    // using https://github.com/jquery-boilerplate/jquery-boilerplate/wiki/Extending-jQuery-Boilerplate
1104    // (adapted to allow public functions)
1105    $.fn[pluginName] = function(options) {
1106        var args = arguments;
1107        // Is the first parameter an object (options), or was omitted,
1108        // instantiate a new instance of the plugin.
1109        if (options === undefined || typeof options === "object") {
1110            var deferreds = [];
1111            this.each(function() {
1112                if (!$.data(this, "plugin_" + pluginName)) {
1113                    var instance = new Plugin(this, options);
1114                    var instanceDeferreds = instance._init();
1115                    // we now have 2 deffereds: 1 for auto country, 1 for utils script
1116                    deferreds.push(instanceDeferreds[0]);
1117                    deferreds.push(instanceDeferreds[1]);
1118                    $.data(this, "plugin_" + pluginName, instance);
1119                }
1120            });
1121            // return the promise from the "master" deferred object that tracks all the others
1122            return $.when.apply(null, deferreds);
1123        } else if (typeof options === "string" && options[0] !== "_") {
1124            // If the first parameter is a string and it doesn't start
1125            // with an underscore or "contains" the `init`-function,
1126            // treat this as a call to a public method.
1127            // Cache the method call to make it possible to return a value
1128            var returns;
1129            this.each(function() {
1130                var instance = $.data(this, "plugin_" + pluginName);
1131                // Tests that there's already a plugin-instance
1132                // and checks that the requested public method exists
1133                if (instance instanceof Plugin && typeof instance[options] === "function") {
1134                    // Call the method of our plugin instance,
1135                    // and pass it the supplied arguments.
1136                    returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
1137                }
1138                // Allow instances to be destroyed via the 'destroy' method
1139                if (options === "destroy") {
1140                    $.data(this, "plugin_" + pluginName, null);
1141                }
1142            });
1143            // If the earlier cached method gives a value back return the value,
1144            // otherwise return this to preserve chainability.
1145            return returns !== undefined ? returns : this;
1146        }
1147    };
1148    /********************
1149 *  STATIC METHODS
1150 ********************/
1151    // get the country data object
1152    $.fn[pluginName].getCountryData = function() {
1153        return allCountries;
1154    };
1155    // load the utils script
1156    $.fn[pluginName].loadUtils = function(path, utilsScriptDeferred) {
1157        if (!$.fn[pluginName].loadedUtilsScript) {
1158            // 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)
1159            $.fn[pluginName].loadedUtilsScript = true;
1160            // dont use $.getScript as it prevents caching
1161            $.ajax({
1162                url: path,
1163                complete: function() {
1164                    // tell all instances that the utils request is complete
1165                    $(".intl-tel-input input").intlTelInput("handleUtils");
1166                },
1167                dataType: "script",
1168                cache: true
1169            });
1170        } else if (utilsScriptDeferred) {
1171            utilsScriptDeferred.resolve();
1172        }
1173    };
1174    // version
1175    $.fn[pluginName].version = "7.1.1";
1176    // Tell JSHint to ignore this warning: "character may get silently deleted by one or more browsers"
1177    // jshint -W100
1178    // Array of country objects for the flag dropdown.
1179    // Each contains a name, country code (ISO 3166-1 alpha-2) and dial code.
1180    // Originally from https://github.com/mledoze/countries
1181    // then with a couple of manual re-arrangements to be alphabetical
1182    // then changed Kazakhstan from +76 to +7
1183    // and Vatican City from +379 to +39 (see issue 50)
1184    // and Caribean Netherlands from +5997 to +599
1185    // and Curacao from +5999 to +599
1186    // Removed:  Kosovo, Pitcairn Islands, South Georgia
1187    // UPDATE Sept 12th 2015
1188    // List of regions that have iso2 country codes, which I have chosen to omit:
1189    // (based on this information: https://en.wikipedia.org/wiki/List_of_country_calling_codes)
1190    // AQ - Antarctica - all different country codes depending on which "base"
1191    // BV - Bouvet Island - no calling code
1192    // GS - South Georgia and the South Sandwich Islands - "inhospitable collection of islands" - same flag and calling code as Falkland Islands
1193    // HM - Heard Island and McDonald Islands - no calling code
1194    // PN - Pitcairn - tiny population (56), same calling code as New Zealand
1195    // TF - French Southern Territories - no calling code
1196    // UM - United States Minor Outlying Islands - no calling code
1197    // UPDATE the criteria of supported countries or territories (see issue 297)
1198    // Have an iso2 code: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
1199    // Have a country calling code: https://en.wikipedia.org/wiki/List_of_country_calling_codes
1200    // Have a flag
1201    // Must be supported by libphonenumber: https://github.com/googlei18n/libphonenumber
1202    // Update: converted objects to arrays to save bytes!
1203    // Update: added "priority" for countries with the same dialCode as others
1204    // Update: added array of area codes for countries with the same dialCode as others
1205    // So each country array has the following information:
1206    // [
1207    //    Country name,
1208    //    iso2 code,
1209    //    International dial code,
1210    //    Order (if >1 country with same dial code),
1211    //    Area codes (if >1 country with same dial code)
1212    // ]
1213    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 Nun
1213aat)", "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 ] ];
1214    // loop over all of the countries above
1215    for (var i = 0; i < allCountries.length; i++) {
1216        var c = allCountries[i];
1217        allCountries[i] = {
1218            name: c[0],
1219            iso2: c[1],
1220            dialCode: c[2],
1221            priority: c[3] || 0,
1222            areaCodes: c[4] || null
1223        };
1224    }
1225});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.