diff --git a/build/js/intlTelInput.js b/build/js/intlTelInput.js index 6a9aaa151..00c056d01 100644 --- a/build/js/intlTelInput.js +++ b/build/js/intlTelInput.js @@ -290,32 +290,40 @@ https://github.com/Bluefieldscom/intl-tel-input.git } } }, - // handle a key event on the input - deals with replacing any currently selected chars, and formatting + // handle a key event on the input - deals with replacing any currently selected chars, and then formatting and then putting the cursor back in the right place _handleInputKey: function(e, isDelete, newChar, isAllowed) { - var val = this.telInput.val(), newCaretStart = null, newCaretEnd = null, // raw DOM element + var val = this.telInput.val(), newCursor = null, cursorAtEnd = false, // raw DOM element input = this.telInput[0]; - if (isAllowed) { - if (this.isGoodBrowser) { - var selectionEnd = input.selectionEnd, originalLen = val.length; + if (this.isGoodBrowser) { + var selectionStart = input.selectionStart, originalLen = val.length; + // at this point, cursorAtEnd should be false even if selectionEnd is at the end, because if isAllowed is false, we wont be replacing the selection + cursorAtEnd = selectionStart == originalLen; + if (isAllowed) { + var selectionEnd = input.selectionEnd; // replace any selection they may have made with the new char - val = val.substring(0, input.selectionStart) + newChar + val.substring(selectionEnd, val.length); - // if the cursor/end of selection was not at the end, calculate the newCaretPos - if (input.selectionEnd !== originalLen) { - newCaretStart = newCaretEnd = selectionEnd + (val.length - originalLen); + val = val.substring(0, selectionStart) + newChar + val.substring(selectionEnd, originalLen); + // if the cursor/end of selection was at the end, we will make sure it's there afterwards + // else calculate the newCursor position + if (selectionEnd === originalLen) { + cursorAtEnd = true; + } else { + newCursor = selectionEnd + (val.length - originalLen); } - } else { - val += newChar; } - } else if (this.isGoodBrowser) { - // if not an allowed char, preserve their selection - newCaretStart = input.selectionStart; - newCaretEnd = input.selectionEnd; + } else if (isAllowed) { + val += newChar; } - // still re-format the number even if key is not allowed as may need to add formatting suffix - this.setNumber(val, isDelete); - // update the caret position - if (this.isGoodBrowser && newCaretStart) { - input.setSelectionRange(newCaretStart, newCaretEnd); + // if the cursor is at the end, then always trigger a reformat as may want to add extra formatting suffix + if (isAllowed || cursorAtEnd) { + // update the number and flag + this.setNumber(val, isDelete); + // update the cursor position + if (cursorAtEnd) { + newCursor = this.telInput.val().length; + } + if (this.isGoodBrowser && newCursor) { + input.setSelectionRange(newCursor, newCursor); + } } }, // on focus: if empty add dial code. on blur: if just dial code, then empty it diff --git a/build/js/intlTelInput.min.js b/build/js/intlTelInput.min.js index 0edc6f15b..91ec10ebc 100644 --- a/build/js/intlTelInput.min.js +++ b/build/js/intlTelInput.min.js @@ -2,4 +2,4 @@ International Telephone Input v1.2.1 https://github.com/Bluefieldscom/intl-tel-input.git */ -!function(a){"function"==typeof define&&define.amd?define(["jquery"],function(b){a(b,window,document)}):a(jQuery,window,document)}(function(a,b,c,d){"use strict";function e(b,c){this.element=b,this.options=a.extend({},h,c),this._defaults=h,this.ns="."+f+g++,this.isGoodBrowser=Boolean(b.setSelectionRange),this._name=f,this.init()}var f="intlTelInput",g=1,h={autoFormat:!0,autoHideDialCode:!0,defaultCountry:"",nationalMode:!1,onlyCountries:[],preferredCountries:["us","gb"],responsiveDropdown:!1,validationScript:""},i={UP:38,DOWN:40,ENTER:13,ESC:27,PLUS:43,A:65,Z:90,ZERO:48,NINE:57,SPACE:32,BACKSPACE:8,DELETE:46},j=!1;a(b).load(function(){j=!0}),e.prototype={init:function(){this.options.nationalMode&&(this.options.autoFormat=this.options.autoHideDialCode=!1),this._processCountryData(),this._generateMarkup(),this._setInitialState(),this._initListeners()},_processCountryData:function(){this._setInstanceCountryData(),this._setPreferredCountries()},_setInstanceCountryData:function(){var b=this;if(this.options.onlyCountries.length){var c,d,e=[],f={};for(d=0;d1){var i=[];for(d=0;d",{"class":"intl-tel-input"}));var b=a("
",{"class":"flag-dropdown"}).insertAfter(this.telInput),c=a("
",{"class":"selected-flag"}).appendTo(b);this.selectedFlagInner=a("
",{"class":"flag"}).appendTo(c),a("
",{"class":"arrow"}).appendTo(this.selectedFlagInner),this.countryList=a("
    ",{"class":"country-list v-hide"}).appendTo(b),this.preferredCountries.length&&(this._appendListItems(this.preferredCountries,"preferred"),a("
  • ",{"class":"divider"}).appendTo(this.countryList)),this._appendListItems(this.countries,""),this.dropdownHeight=this.countryList.outerHeight(),this.countryList.removeClass("v-hide").addClass("hide"),this.options.responsiveDropdown&&this.countryList.outerWidth(this.telInput.outerWidth()),this.countryListItems=this.countryList.children(".country")},_appendListItems:function(a,b){for(var c="",d=0;d",c+="
    ",c+=""+e.name+"",c+="+"+e.dialCode+"",c+="
  • "}this.countryList.append(c)},_setInitialState:function(){if(!this.setNumber(this.telInput.val())){var a;a=this.options.defaultCountry?this._getCountryData(this.options.defaultCountry,!1,!1):this.preferredCountries.length?this.preferredCountries[0]:this.countries[0],this._selectFlag(a.iso2),this.options.autoHideDialCode||this._resetToDialCode(a.dialCode)}},_initListeners:function(){var d=this;this.options.autoHideDialCode&&this._initAutoHideDialCode();var e=this.telInput.closest("label");e.length&&e.on("click"+this.ns,function(a){d.countryList.hasClass("hide")?d.telInput.focus():a.preventDefault()}),this.options.autoFormat&&this.telInput.on("keypress"+this.ns,function(a){a.preventDefault();var b=a.which==i.PLUS||a.which>=i.ZERO&&a.which<=i.NINE;d._handleInputKey(a,!1,String.fromCharCode(a.which),b)}),this.telInput.on("keyup"+this.ns,function(a){d.options.autoFormat?(a.which==i.BACKSPACE||a.which==i.DELETE)&&d._handleInputKey(a,!0,"",!0):d.setNumber(d.telInput.val())});var f=this.selectedFlagInner.parent();if(f.on("click"+this.ns,function(){d.countryList.hasClass("hide")&&!d.telInput.prop("disabled")&&d._showDropdown()}),this.options.validationScript){var g=function(){var a=c.createElement("script");a.type="text/javascript",a.src=d.options.validationScript,c.body.appendChild(a)};j?g():a(b).load(g)}},_handleInputKey:function(a,b,c,d){var e=this.telInput.val(),f=null,g=null,h=this.telInput[0];if(d)if(this.isGoodBrowser){var i=h.selectionEnd,j=e.length;e=e.substring(0,h.selectionStart)+c+e.substring(i,e.length),h.selectionEnd!==j&&(f=g=i+(e.length-j))}else e+=c;else this.isGoodBrowser&&(f=h.selectionStart,g=h.selectionEnd);this.setNumber(e,b),this.isGoodBrowser&&f&&h.setSelectionRange(f,g)},_initAutoHideDialCode:function(){var b=this;this.telInput.on("mousedown"+this.ns,function(a){b.telInput.is(":focus")||b.telInput.val()||(a.preventDefault(),b._focus())}),this.telInput.on("focus"+this.ns,function(){a.trim(b.telInput.val())||(b._updateVal("+"+b.selectedCountryData.dialCode,!0),b.telInput.one("keypress.plus"+b.ns,function(a){a.which==i.PLUS&&b.telInput.val("+")}))}),this.telInput.on("blur"+this.ns,function(){var c=b.telInput.val(),d="+"==c.substring(0,1),e=c.replace(/\D/g,"");d&&e&&(c="+"+e,a.trim(b._getDialCode(c))==c&&b.telInput.val("")),b.telInput.off("keypress.plus"+b.ns)})},_focus:function(){this.telInput.focus();var a=this.telInput[0];if(this.isGoodBrowser){var b=this.telInput.val().length;a.setSelectionRange(b,b)}},_showDropdown:function(){this._setDropdownPosition();var a=this.countryList.children(".active");this._highlightListItem(a),this.countryList.removeClass("hide"),this._scrollTo(a),this._bindDropdownListeners(),this.selectedFlagInner.children(".arrow").addClass("up")},_setDropdownPosition:function(){var c=this.telInput.offset().top,d=a(b).scrollTop(),e=c+this.telInput.outerHeight()+this.dropdownHeightd,g=!e&&f?"-"+(this.dropdownHeight-1)+"px":"";this.countryList.css("top",g)},_bindDropdownListeners:function(){var b=this;this.countryList.on("mouseover"+this.ns,".country",function(){b._highlightListItem(a(this))}),this.countryList.on("click"+this.ns,".country",function(){b._selectListItem(a(this))});var d=!0;a("html").on("click"+this.ns,function(){d||b._closeDropdown(),d=!1});var e="",f=null;a(c).on("keydown"+this.ns,function(a){a.preventDefault(),a.which==i.UP||a.which==i.DOWN?b._handleUpDownKey(a.which):a.which==i.ENTER?b._handleEnterKey():a.which==i.ESC?b._closeDropdown():(a.which>=i.A&&a.which<=i.Z||a.which==i.SPACE)&&(f&&clearTimeout(f),e+=String.fromCharCode(a.which),b._searchForCountry(e),f=setTimeout(function(){e=""},1e3))})},_handleUpDownKey:function(a){var b=this.countryList.children(".highlight").first(),c=a==i.UP?b.prev():b.next();c.length&&(c.hasClass("divider")&&(c=a==i.UP?c.prev():c.next()),this._highlightListItem(c),this._scrollTo(c))},_handleEnterKey:function(){var a=this.countryList.children(".highlight").first();a.length&&this._selectListItem(a)},_searchForCountry:function(a){for(var b=0;bh)b&&(j-=k),c.scrollTop(j);else if(i>f){b&&(j+=k);var l=d-g;c.scrollTop(j-l)}},_updateDialCode:function(b){var c,d=this.telInput.val(),e=this._getDialCode(d);if(e.length>1)c=d.replace(e,b);else{var f=d&&"+"!=d.substr(0,1)?a.trim(d):"";c=b+f}this._updateVal(c,!0)},_getDialCode:function(b){var c="";if(b=a.trim(b),"+"==b.charAt(0))for(var d="",e=0;e1){var i=[];for(d=0;d",{"class":"intl-tel-input"}));var b=a("
    ",{"class":"flag-dropdown"}).insertAfter(this.telInput),c=a("
    ",{"class":"selected-flag"}).appendTo(b);this.selectedFlagInner=a("
    ",{"class":"flag"}).appendTo(c),a("
    ",{"class":"arrow"}).appendTo(this.selectedFlagInner),this.countryList=a("
      ",{"class":"country-list v-hide"}).appendTo(b),this.preferredCountries.length&&(this._appendListItems(this.preferredCountries,"preferred"),a("
    • ",{"class":"divider"}).appendTo(this.countryList)),this._appendListItems(this.countries,""),this.dropdownHeight=this.countryList.outerHeight(),this.countryList.removeClass("v-hide").addClass("hide"),this.options.responsiveDropdown&&this.countryList.outerWidth(this.telInput.outerWidth()),this.countryListItems=this.countryList.children(".country")},_appendListItems:function(a,b){for(var c="",d=0;d",c+="
      ",c+=""+e.name+"",c+="+"+e.dialCode+"",c+="
    • "}this.countryList.append(c)},_setInitialState:function(){if(!this.setNumber(this.telInput.val())){var a;a=this.options.defaultCountry?this._getCountryData(this.options.defaultCountry,!1,!1):this.preferredCountries.length?this.preferredCountries[0]:this.countries[0],this._selectFlag(a.iso2),this.options.autoHideDialCode||this._resetToDialCode(a.dialCode)}},_initListeners:function(){var d=this;this.options.autoHideDialCode&&this._initAutoHideDialCode();var e=this.telInput.closest("label");e.length&&e.on("click"+this.ns,function(a){d.countryList.hasClass("hide")?d.telInput.focus():a.preventDefault()}),this.options.autoFormat&&this.telInput.on("keypress"+this.ns,function(a){a.preventDefault();var b=a.which==i.PLUS||a.which>=i.ZERO&&a.which<=i.NINE;d._handleInputKey(a,!1,String.fromCharCode(a.which),b)}),this.telInput.on("keyup"+this.ns,function(a){d.options.autoFormat?(a.which==i.BACKSPACE||a.which==i.DELETE)&&d._handleInputKey(a,!0,"",!0):d.setNumber(d.telInput.val())});var f=this.selectedFlagInner.parent();if(f.on("click"+this.ns,function(){d.countryList.hasClass("hide")&&!d.telInput.prop("disabled")&&d._showDropdown()}),this.options.validationScript){var g=function(){var a=c.createElement("script");a.type="text/javascript",a.src=d.options.validationScript,c.body.appendChild(a)};j?g():a(b).load(g)}},_handleInputKey:function(a,b,c,d){var e=this.telInput.val(),f=null,g=!1,h=this.telInput[0];if(this.isGoodBrowser){var i=h.selectionStart,j=e.length;if(g=i==j,d){var k=h.selectionEnd;e=e.substring(0,i)+c+e.substring(k,j),k===j?g=!0:f=k+(e.length-j)}}else d&&(e+=c);(d||g)&&(this.setNumber(e,b),g&&(f=this.telInput.val().length),this.isGoodBrowser&&f&&h.setSelectionRange(f,f))},_initAutoHideDialCode:function(){var b=this;this.telInput.on("mousedown"+this.ns,function(a){b.telInput.is(":focus")||b.telInput.val()||(a.preventDefault(),b._focus())}),this.telInput.on("focus"+this.ns,function(){a.trim(b.telInput.val())||(b._updateVal("+"+b.selectedCountryData.dialCode,!0),b.telInput.one("keypress.plus"+b.ns,function(a){a.which==i.PLUS&&b.telInput.val("+")}))}),this.telInput.on("blur"+this.ns,function(){var c=b.telInput.val(),d="+"==c.substring(0,1),e=c.replace(/\D/g,"");d&&e&&(c="+"+e,a.trim(b._getDialCode(c))==c&&b.telInput.val("")),b.telInput.off("keypress.plus"+b.ns)})},_focus:function(){this.telInput.focus();var a=this.telInput[0];if(this.isGoodBrowser){var b=this.telInput.val().length;a.setSelectionRange(b,b)}},_showDropdown:function(){this._setDropdownPosition();var a=this.countryList.children(".active");this._highlightListItem(a),this.countryList.removeClass("hide"),this._scrollTo(a),this._bindDropdownListeners(),this.selectedFlagInner.children(".arrow").addClass("up")},_setDropdownPosition:function(){var c=this.telInput.offset().top,d=a(b).scrollTop(),e=c+this.telInput.outerHeight()+this.dropdownHeightd,g=!e&&f?"-"+(this.dropdownHeight-1)+"px":"";this.countryList.css("top",g)},_bindDropdownListeners:function(){var b=this;this.countryList.on("mouseover"+this.ns,".country",function(){b._highlightListItem(a(this))}),this.countryList.on("click"+this.ns,".country",function(){b._selectListItem(a(this))});var d=!0;a("html").on("click"+this.ns,function(){d||b._closeDropdown(),d=!1});var e="",f=null;a(c).on("keydown"+this.ns,function(a){a.preventDefault(),a.which==i.UP||a.which==i.DOWN?b._handleUpDownKey(a.which):a.which==i.ENTER?b._handleEnterKey():a.which==i.ESC?b._closeDropdown():(a.which>=i.A&&a.which<=i.Z||a.which==i.SPACE)&&(f&&clearTimeout(f),e+=String.fromCharCode(a.which),b._searchForCountry(e),f=setTimeout(function(){e=""},1e3))})},_handleUpDownKey:function(a){var b=this.countryList.children(".highlight").first(),c=a==i.UP?b.prev():b.next();c.length&&(c.hasClass("divider")&&(c=a==i.UP?c.prev():c.next()),this._highlightListItem(c),this._scrollTo(c))},_handleEnterKey:function(){var a=this.countryList.children(".highlight").first();a.length&&this._selectListItem(a)},_searchForCountry:function(a){for(var b=0;bh)b&&(j-=k),c.scrollTop(j);else if(i>f){b&&(j+=k);var l=d-g;c.scrollTop(j-l)}},_updateDialCode:function(b){var c,d=this.telInput.val(),e=this._getDialCode(d);if(e.length>1)c=d.replace(e,b);else{var f=d&&"+"!=d.substr(0,1)?a.trim(d):"";c=b+f}this._updateVal(c,!0)},_getDialCode:function(b){var c="";if(b=a.trim(b),"+"==b.charAt(0))for(var d="",e=0;e 1) ? keyCodes[key] : key.charCodeAt(0) }); }; diff --git a/src/spec/tests/options/autoFormat.js b/src/spec/tests/options/autoFormat.js index 99be3b4bb..2bd47caea 100644 --- a/src/spec/tests/options/autoFormat.js +++ b/src/spec/tests/options/autoFormat.js @@ -54,11 +54,57 @@ describe("testing autoFormat option", function() { }); it("triggering alpha key at end of input does not add the alpha char and formats the rest", function() { - // updating the input value will be silent (no events triggered) - input.val(unformattedNumber + "A"); + // we dont have to manually alter the input val as when autoFormat is enabled this is all done in the event handler + putCursorAtEnd(); triggerKeyOnInput("A"); expect(input.val()).toEqual(formattedNumber); }); + + + + + it("adding a digit automatically adds any formatting suffix", function() { + input.val("+"); + putCursorAtEnd(); + // this is handled by the keypress handler, and so will insert the char for you + triggerKeyOnInput("1"); + expect(input.val()).toEqual("+1 ("); + }); + + it("deleting a digit automatically removes any remaining formatting suffix", function() { + // backspace key event is handled by the keyup handler, which expects the input val to already be updated, so instead of "+1 (7", I have already removed the 7 + input.val("+1 ("); + putCursorAtEnd(); + triggerKeyOnInput("BACKSPACE"); + expect(input.val()).toEqual("+1"); + }); + + + + + describe("after deleting a char and it removing any format suffix", function() { + + beforeEach(function() { + // e.g. imagine it was "+1 (7" and we deleted the 7 and it auto-removed the rest + input.val("+1"); + putCursorAtEnd(); + }); + + it("hitting a number will re-add the formatting in between", function() { + // this is handled by the keypress handler, and so will insert the char for you + triggerKeyOnInput("7"); + expect(input.val()).toEqual("+1 (7"); + }); + + it("hitting any non-number char (e.g. a space) will re-add the formatting suffix", function() { + // this is handled by the keypress handler, and so will insert the char for you + triggerKeyOnInput(" "); + expect(input.val()).toEqual("+1 ("); + // and move the cursor to the end + expect(input[0].selectionStart).toEqual(input.val().length); + }); + + }); });