KO.validation via rules from jQuery Inputmask

Working revisions: #6 - validating via ko.subscribe #8 - validating via ko.extenders #10 - validating via ko.validation.rules (at last!) #17 - validation as in #10, but added 'auto mask detection' via phone-codes

by Ilya

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
<script src="https://bowercdn.net/c/jquery.inputmask-3.2.5/extra/phone-codes/phone-codes.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.inputmask/3.2.7/jquery.inputmask.bundle.min.js"></script>
<h2>Validation using Knockout and jQuery Inputmask</h2>
Libs used: <br> 
<ul>
  <li>jQuery 1.11.0 (~any version will do)</li>
  <li>knockout 3.4.0</li>
  <li>knockout-validation 2.0.3</li>
  <li>jquery.inputmask 3.2.7 (with its 'phone-codes.js')</li>
  <li>Flag icon css (https://github.com/lipis/flag-icon-css)</li>
</ul>
<hr>

Phone* <span class="phone-country"></span>  : <br>
<input type="text" id="forinputmask" data-bind="value: phone, valueUpdate:'afterkeydown'" />
<span class="flag"></span>



<br><br>email :<br>  
<input type="text" data-bind="value: email, myvalue: phone, valueUpdate:'afterkeydown'" />

<br><br><span>viewmodel dump:</span> <span data-bind="text: ko.toJSON($data)"></span>
<hr>
<em style="color:gray"> * Phone validation causes high CPU load on Inputmask versions less than 3.2.6-24. So far it's fixed by v3.2.7 (the author has added some internal cache) and a 'rateLimit' when extending. <br>
** <u>Not all flags (like US, CA, UK) are shown correctly</u>, since flag codes differ from inputmask codes. Can be fixed manually (either in flag codes, or phone-codes.js) on a live project. Most flags are okay. KZ flags are detected as RU, because their phone codes are alike. This could be fixed manually in 'onKeyValidation' method in Inputmask.
</em>
<br>

CSS

.flag{
    background: 50% 50% no-repeat transparent;
    /*background:none;*/
    width: 25px;
    height: 19px;
    background-size: cover;
    display: inline-block;
}

span.validationMessage {
    /*float: left;*/
}

JavaScript

$(document).ready(function(){ 

/* THIS 'Inputmask.extendAliases' is just to override the paths 
to 'phone-codes.js' that are hardcoded in the 'jquery.inputmask.bundle.min.js'. */
  var maskList = [];
  var ajaxRes = $.ajax({
                        url: "https://bowercdn.net/c/jquery.inputmask-3.2.5/extra/phone-codes/phone-codes.js",
                        async: !1,
                        dataType: "json",
                        success: function(response) {
                            maskList = response;
                        },
                        error: function(xhr, ajaxOptions, thrownError) {
                           // alert(thrownError + " - " + opts.url);
                        }
                    });
                    
  Inputmask.extendAliases({
    phone: {
      url: "https://bowercdn.net/c/jquery.inputmask-3.2.5/extra/phone-codes/phone-codes.js",
      countrycode: "",  /* You can set the default code for the mask, like, for example, 380 for Ukraine */
      mask: function(opts) {
        opts.definitions["#"] = opts.definitions[9];
        //var maskList = [];

        return ajaxRes, maskList = maskList.sort(function(a, b) {
          return (a.mask || a) < (b.mask || b) ? -1 : 1;
        });
      },
      keepStatic: !1,
      nojumps: !0,
      nojumpsThreshold: 1,
      onBeforeMask: function(value, opts) {
        var processedValue = value.replace(/^0{1,2}/g, "").replace(/[-\s]/g, "");
        return (processedValue.indexOf(opts.countrycode) > 1 || -1 === processedValue.indexOf(opts.countrycode)) && (processedValue = "+" + opts.countrycode + processedValue), 
          processedValue;
          
      }
    },
    phonebe: {
      alias: "phone",
      url: "https://bowercdn.net/c/jquery.inputmask-3.2.5/extra/phone-codes/phone-be.js",
      countrycode: "32",
      nojumpsThreshold: 4
    }
  });
        
    //Inputmask('phone').mask('#forinputmask'); /* a variant of setting a mask */
   ...