// ********************************************************************* // SETUP // ********************************************************************* var wrjs = wrjs || {}; wrjs.utils = wrjs.utils || {}; wrjs.utils.form = wrjs.utils.form || {}; // ********************************************************************* // FORM UTILITY FUNCTIONS // ********************************************************************* (function () { // *********************************************************** // PRIVATE // *********************************************************** var _init = function() { _createListeners(); }; var _createListeners = function() { $(document).ready(function() { $('body').on('keydown change', '.fielderror :input', function() { _removeErrorState(this) }); $('body').on('change', '.wrjs_modal input[type=radio], .wrjs_modal select', function() { var t = $(this); var form = t.closest('form'); _callModalPixelFix(form); }); // STRIP ANGLED BRACKETS <> $('body').on('keydown paste', 'input.stripspecialchars, .stripspecialchars input, textarea.stripspecialchars, .stripspecialchars textarea', function(e) { var el = this; var t = $(this); setTimeout(function() { var val = t.val(); var position = t.caret().begin; var patt = /[<>]/; if(patt.test(val)) { var newval = val.replace(/[<>]/g, ''); t.val(newval); t.caret(position-1); } }) }) $('body').on('keydown paste', 'input.numericonly, .numericonly input', function(e) { var el = this; var t = $(this); setTimeout(function() { var val = t.val(); var position = t.caret().begin; var containsnonnumber = /\D/ if(containsnonnumber.test(val)) { var newval = val.replace(/\D/g, ""); t.val(newval); t.caret(position-1); } }) }); $('body').on('keydown paste keyup blur', 'input.numericonlyexcludezero, .numericonlyexcludezero input', function(e) { var el = this; var t = $(this); var val = t.val(); // REMOVE STARTING ZERO if(val.substring(0,1) == '0') { val = val.substring(1); t.val(val); }; setTimeout(function() { var position = t.caret().begin; var containsnonnumber = /\D/ if(containsnonnumber.test(val)) { var newval = val.replace(/\D/g, ""); t.val(newval); t.caret(position-1); } },1) }); $('body').on('keydown paste', 'input.numericandperiodonly, .numericandperiodonly input', function(e) { var el = this; var t = $(this); setTimeout(function() { var val = t.val(); var position = t.caret().begin; var containsinvalid = /[^0-9.]/ if(containsinvalid.test(val)) { var newval = val.replace(/[^0-9.]/g, ""); t.val(newval); t.caret(position-1); } }) }); // STRIP SPACES IN EMAIL FIELDS $('body').on('keydown paste', 'input.stripwhitespace, .stripwhitespace input', function(e) { var el = this; var t = $(this); setTimeout(function() { var val = t.val(); var position = t.caret().begin; var patt = /\s/; if(patt.test(val)) { var newval = val.replace(/\s/g, ''); t.val(newval); t.caret(position-1); } }) }); }) }; var _removeErrorState = function(el) { var t = $(el); var row = t.closest('.form-row'); row.find('.error').remove(); row.removeClass('fielderror'); t.removeClass('fielderror'); var form = t.closest('form'); _callModalPixelFix(form); }; var _getFormData = function ($form, formConfig) { formConfig = typeof formConfig == 'undefined' ? false : formConfig; var stripId = formConfig && formConfig.makeFieldIdsUnique; var unindexed_array = $form.serializeArray(); var indexed_array = {}; $.map(unindexed_array, function (n, i) { var name = n['name']; // BY DEFAULT ALL FIELD VALUES SHOULD BE HTML ENCODED (XSS) // HOWEVER, IN SOME SCENARIOS, THIS NEEDS TO BE DISABLED (e.g. Allow sending of angled brackets, etc) // THEREFORE CHECK IF THE PARENT LABEL HAS A CLASS OF "donotencode" ATTACHED, THEN SET ENCODE TO FALSE IF PRESENT var encode = true; var field = $('#' + name, $form); var label = field.closest('label'); if(field.hasClass('donotencode') || (label.length > 0 && label.hasClass('donotencode'))) { encode = false; }; var value = n['value']; if(encode) { value = $.trim(wrjs.utils.htmlEncode(value)); }; // IF WRJS FORM HAS "makeFieldIdsUnique" CONFIG SET TO TRUE // THEN STRIP OF THE FORM ID FROM THE FRONT OF THE FIELD NAME if(stripId) { name = name.split(formConfig.id + '_')[1]; }; if (indexed_array[name]) { if (typeof indexed_array[name] == 'string') { indexed_array[name] = [indexed_array[name], value]; } else { indexed_array[name].push(value); } } else { indexed_array[name] = value; } }); return indexed_array; }; var _isEmailValid = function (email) { var regex = /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$/; return regex.test(email); }; var _isURLValid = function(url) { var regex = /[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/ig return regex.test(url); }; var _trimAndCheckUniqueEmails = function (config) { var emails = config.emails ? config.emails : ''; // PASS A COMMA DELIMITED STRING OF EMAILS var limit = config.limit ? Number(config.limit) : false; // PASS A MAXIMUM NUMBER OF EMAILS ALLOWED TO REMAIN VALID // SET DEFAULTS var toomany = false; var uniqueEmails = []; var invalidemails = []; if(emails.length != 0) { // STRIP OUT ALL WHITESPACE, THEN SPLIT INTO AN ARRAY OF EMAILS emails = emails.replace(/(\r\n|\n|\r)/gm, ''); emails = emails.replace(/\s/g, ''); emails = emails.replace(/,$/, ''); emails = emails.split(','); // LOOP EMAIL ARRAY AND CHECK VALIDITY $.each(emails, function (i, el) { var exists = $.inArray(el, uniqueEmails) > -1; // CHECK FOR DUPLICATES if(!exists) { // IF LIMIT HAS BEEN PASSED, AND THE UNIQUEEMAILS ARRAY ALREADY CONTAINS THE LIMIT AMOUNT if (limit != false && uniqueEmails.length >= limit) { toomany = true; }; // RUN EMAIL THROUGH EMAIL REGEX AND PUSH INTO THE CORRECT ARRAY (VALID OR INVALID) if(el.length > 0) { if (!_isEmailValid(el)) { invalidemails.push(el); }; // ALWAYS PUSH UNIQUE EMAILS, EVEN IF INVALID uniqueEmails.push(el); }; } }); // FORCE INVALID EMAIL MESSAGE IF BLANKS GET PASSED THROUGH SUCCESSFULLY // DE8237 - Can enter ", , , , ," if(uniqueEmails.length == 0) { if(invalidemails.length == 0) { invalidemails.push(''); } }; }; return { emails: uniqueEmails.join(','), invalidemails: invalidemails, toomany: toomany }; }; var _isFormValid = function (form) { if(!form) { return true; } if(typeof form == 'string') { form = $(form); } var formdata = _getFormData(form); var valid = true; var invalidfields = []; // CHECK IF A RANGE OF INPUTS HAVE BEEN PASSED INSTEAD OF THE PARENT FORM if(form && form.length > 0) { form = $(form[0]).closest('form'); } else { form = $('.wrjs_modal') || $('body'); }; // REMOVE ERROR STATES $('.error', form).remove(); $('.fielderror', form).removeClass('fielderror'); // ONLY MASK PARENT WHEN FORM ISN'T PART OF A MULTI-STEP TAB PANEL var steptabs = $('.tabs.steps', form); var parentModal = form.closest('.wrjs_modal'); if(parentModal.length > 0 && !steptabs) { parentModal.mask(ls.text_saving); } $.each(formdata, function (key, val) { val = $.trim(val); var fieldvalid = true; var fieldError = ''; var input = $('#' + key, form); var formRow = input.closest('.form-row'); var email = input.attr('type') == 'email' || input.attr('email') == 'true'; var phone = input.attr('type') == 'tel'; var hidden = input.attr('type') == 'hidden'; var button = input.attr('type') == 'button'; var url = input.attr('type') == 'url'; var minlength = input.attr('minlength'); var allowblank = input.attr('allowblank') == "true" ? true : false; var ignorehidden = form.hasClass('ignorehidden'); var customValidation = input.attr('customValidation'); var validationmessage = input.attr('validationmessage'); var invalidEmailList = ''; if((ignorehidden && hidden) || button) { allowblank = true; }; if(customValidation) { var args = [input, formdata]; // RUN CUSTOM VALIDATION var result = wrjs.utils.executeFunctionByName(customValidation, args); // THIS RETURNS AN OBJECT CONSISTING OF: /* { valid: bool, msg: str } */ //console.log('customValidation', result); fieldvalid = result.valid; fieldError = result.msg; } else { // EMAIL VALIDITY if (email) { var limit = typeof input.attr('limit') == 'undefined' ? 1 : input.attr('limit'); var config = { emails: val, limit: limit }; var result = _trimAndCheckUniqueEmails(config); // EXPECTED RESPONSE {emails: emails, invalidemails: invalidemails, toomany: toomany} var emails = result.emails; var invalidemails = result.invalidemails; var toomany = result.toomany; // PUT VALIDATED VALUE BACK IN THE FIELD FOR DISPLAY input.val(emails); // UPDATE THE FORMDATA VALUE formdata[key] = emails; if (toomany) { fieldvalid = false; fieldError += ls.text_error_emails + ' (' + ls.text_error_emailslimit + ' ' + limit + ')'; }; if(invalidemails.length > 0) { // IF IT'S ALREADY TOO MANY, APPEND EXTRA ERROR AFTER BR if (fieldError.length > 0) { fieldError += '
' }; fieldvalid = false; var invalids = ''; $.each(invalidemails, function (i, email) { invalids += '
  • ' + email + '
  • '; }); invalidEmailList = ''; fieldError = ls.text_invalidemails; }; } if(url) { fieldvalid = _isURLValid(val); fieldError = ls.text_entervalidurl; }; } if(!allowblank) { if (typeof minlength != 'undefined') { if (minlength > val.length) { fieldvalid = false; fieldError = minlength + ' ' + ls.text_charactersrequired; } } else { if (val.length == 0) { fieldvalid = false; fieldError = validationmessage || ls.psMessage72; } } }; if(!fieldvalid) { //console.log('INVALID', input, key, val) valid = false; invalidfields.push(input); if(!formRow.length) { input.addClass('fielderror'); }else{ formRow.addClass('fielderror'); } input.parent().append('

    ' + fieldError + '

    ' + invalidEmailList); } }) if (!valid) { //console.log('FORM'); var firstinvalid = invalidfields[0]; var parent = firstinvalid.closest('.tabcontent'); var form = firstinvalid.closest('form'); if(parentModal.length > 0) { parentModal.unmask(); parentModal.addClass('shake'); setTimeout(function(){ parentModal.removeClass('shake'); }, 500); } if (parent.length > 0) { var parentid = parent.attr('id'); var ullia = $('ul li a[href="#' + parentid + '"]', form); var ul = ullia.closest('ul'); var dis = ul.hasClass('disabled'); // WORK OUT WHETHER TO SHOW/HIDE THE CORRECT STEP BUTTONS var li = ullia.closest('li'); var liindex = li.index() + 1; var licount = $('li', ul).length; var prev = $('.button.prev', form); var next = $('.button.next', form); var finish = $('.button.finish', form); if(liindex == licount) { // LAST STEP - SHOW PREV AND FINISH prev.show(); next.hide(); finish.show(); } else { if(liindex == 1) { // FIRST STEP - SHOW NEXT prev.hide(); next.show(); finish.hide(); } else { // MIDDLE STEP - SHOW PREV AND NEXT prev.show(); next.show(); finish.hide(); } }; // SHOW CORRECT TAB AND TABCONTENT ul.removeClass('disabled'); ullia.trigger('click'); if (dis) { ul.addClass('disabled') }; }; //console.log('INVALID FIELDS: ', invalidfields) }; _callModalPixelFix(form); return valid; }; var _callModalPixelFix = function(form) { if(form && form.length > 0) { var parentModal = form.closest('.wrjs_modal'); if(parentModal.length > 0) { var wrjsModalId = parentModal.attr('wrjs_modal_id'); if(wrjs.get(wrjsModalId)){ wrjs.get(wrjsModalId).pixelFix(); } }; }; }; var _markFieldInvalid = function(selector, str) { var input = $(selector); var name = input.attr('name'); var row = input.closest('.form-row'); // REMOVE PREVIOUS ERROR MESSAGE $('.error', row).remove(); // ADD ERROR STATE row.addClass('fielderror'); var parent = input.parent(); parent.append('

    ' + str + '

    ') }; // *********************************************************** // PUBLIC // *********************************************************** var publicFunctions = { isFormValid: _isFormValid, isEmailValid: _isEmailValid, isURLValid: _isURLValid, getFormData: _getFormData, markFieldInvalid: _markFieldInvalid }; // *********************************************************** // INIT // *********************************************************** $.extend(wrjs.utils.form, publicFunctions); _init(); })()