Welcome to the LimeSurvey Community Forum

Ask the community, share ideas, and connect with other LimeSurvey users!

Automatic javascript masks for input fields (new workaround)

  • leandroarndt
  • leandroarndt's Avatar Topic Author
  • Offline
  • New Member
  • New Member
More
8 years 9 months ago #122152 by leandroarndt
I had some difficulties implementing masks at my work (XSS protection seemed to be enabled), so I developed a new workaround, which detects validation patterns and automatically applies masks to the corresponding <input> elements.

First of all, I created a new template and added the following line before closing the <head> element at startpage.pstpl:
Code:
<script type="text/javascript" src=" https://cdn.rawgit.com/igorescobar/jQuery-Mask-Plugin/master/src/jquery.mask.js"></script>

Then I added the following code inside $(document).ready() at template.js:
Code:
function() { // Easy creation of masks according to the validation regular expression
  // New validation function
  var validate = function(qid, input, length) {
    var v = function(l) {
      if (input.value.length == l) {
        $('#vmsg_'+qid+'_regex_validation').removeClass('error').addClass('good');
        $("#"+input.id).removeClass('error').addClass('good');
        return true;
      }
      else {
        $('#vmsg_'+qid+'_regex_validation').removeClass('good').addClass('error');
        $("#"+input.id).removeClass('good').addClass('error');
        return false;
      }
    }
 
    if (!isNaN(length)) { // We got a numeric length
      v(length);
    } else { // We got an array of allowed lengths
      for (var i = 0; i<length.length; i++) {
        if (v(length[i])) { break; }
      }
    }
  }
 
  // telefone and telefoneOptions come from http://igorescobar.github.io/jQuery-Mask-Plugin/
 
  var telefone = function (val) {
    return val.replace(/\D/g, '').length === 11 ? '(00) 00000-0000' : '(00) 0000-00009';
  }
 
  var telefoneOptions = {
    onKeyPress: function(val, e, field, options) {
      field.mask(telefone.apply({}, arguments), options);
    }
  }
 
  // Our mask-options-regex-length correpondences
  var masks = [
       ["00.000.000/0000-00", {}, "/^[0-9]{2}\\\\.[0-9]{3}\\\\.[0-9]{3}\\\\/[0-9]{4}-[0-9]{2}$/", 18], // CNPJ
       ["00000-000", {}, "/^[0-9]{5}-[0-9]{3}$/", 9], // CEP
       ["000.000.000-00", {}, "/^[0-9]{3}.[0-9]{3}.[0-9]{3}-[0-9]{2}$/", 14], // CPF
       [telefone, telefoneOptions, "/^\\\\([0-9]{2}\\\\) [0-9]{4,5}-[0-9]{4}$/", [14, 15]], // 8 or 9-digit Brazilian telephone numbers
       ];
 
  // Form pairs of [qid, regex] for later search 
  var scripts = $("script")
  var regexes = []
  for (var i=0; i<scripts.length; i++) {
 
    // We first try to get qid from the line "function LEMval????(sgqa){"
    var script = scripts[i].innerHTML;
    var currIndex = scripts[i].innerHTML.indexOf("function LEMval");
    while (currIndex >= 0) {
      script = script.substring(currIndex+15); // Get rid of "function LEMval"
      if (!isNaN(script.substring(0,1))) { // We found a qid after "function LEMval"
        var valqid = script.substring(0,script.indexOf("("));
 
        // Now we try to get the validation regex, which is delimited by double
        // quotes as in LEMregexMatch("/^[0-9]{2}-[0-9]{8,9}$/"
        var regex = script.substring(script.indexOf("LEMregexMatch(\"")+15);
        regex = regex.substring(0, regex.indexOf("\""));
 
        // If we find it, appendo to regexes list as a pair of [qid, regex]
        regexes.push([valqid, regex]);
      }
      currIndex = script.indexOf("function LEMval"); // Next...
    }
  }
 
  // Iterate through found questions and create masks
  for (var i=0; i<regexes.length; i++) {
    var qid = regexes[i][0];
    var input = $("#question"+qid+" input")[0];
    for (var ii=0; ii<masks.length; ii++) {
      if (masks[ii][2] == regexes[i][1]) {
        var length = masks[ii][3];
        $("#"+ input.id).mask(masks[ii][0], masks[ii][1], masks[ii][2]);
        // We now have to manipulate validation functions. We may have a delay on mask update
        window["LEMval"+qid] = function (a, b, c) {
          return function(sgqa) {
            setTimeout(validate(a, b, c), 500);
          }
        }(qid, input, length); // We must separate from the outer scope
        $("#"+ input.id).focusout(window["LEMval"+qid]);
      }
    }
  }
}

This script is ready for some Brazilian masks (ZIP code, phone numbers etc.), but may easily adapted to other masks.

The downside of this solution is that it is very dependent on LimeSurvey code, and version changes might brake it. Works with 2.05+ Build 141229.
The topic has been locked.

Lime-years ahead

Online-surveys for every purse and purpose