/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ (function($) { $.extend($.fn, { // public plugin functions displayChanger : function (options) { // Attach onchange event handler to displayChanger Element $(".displayChanger").on('change', function (e) { $(".conditional").trigger("checkConditionalState"); }); }, // perform the code which is necessary to toggle the display state of one form element toggleDisplay : function (options) { const defaults = { // just an example of adding translated texts, stepAlert is not used texts: { stepAlert: "Es wurde ein bedingtes Feld in einem anderen Schritt aktiviert. Eventuell funktioniert das Formular nicht mehr." } }; const settings = jQuery.extend(true, {}, defaults, options); const data = options.restricts; const userinputs = $.data($(this).closest("form").get(0), 'userinputs'); if ((!userinputs) || typeof userinputs === "undefined") { return; } // el is a div class="conditional" const el = $(this); // data is a list of all conditional fields // index is id of field // value is a comma separated string of all field IDs and values the command the conditional field to be visible $.each(data, function (index, value) { // a conditional field may have different other fields that make it visible // we will not hide it, when at least one condition is true let hide = true; // find the right set of conditions for the div conditional that is actually processed let elId; if (el.hasClass(index)) { if (elId = el.attr('class').match(index)) { // split the condition string const showWhens = value.split(', '); $.each(showWhens, function (i, v) { // split the condition into a field id and a value that, if selected , will command the field to be visible const showWhen = v.split('__'); if (showWhen.length >= 2) { const fieldId = showWhen[0]; const conditionalValue = showWhen[1]; // Restrictor elements that determine whether field is shown or hidden // we first look if we have a single control with a matching ID let restrictors = $('#' + fieldId); // if not, we deal with a radio or a multi checkbox. Id's are there followed by _n if (restrictors.length < 1) { restrictors = $("[id^='" + fieldId + "_']"); } // rel is restrictor element // check if we have a value in a restrictor element that will command field to be shown $.each(restrictors, function (ri, rel) { // only use values of elements that are enabled if ($(rel).is(':enabled')) { const tagname = rel.tagName.toLowerCase(); switch (tagname) { case "input" : // selected values have checked=checked set if ($(rel).is(':checked')) { if ($(rel).val() == conditionalValue) { hide = false; return hide; } } break; case "select" : const vals = $(rel).find(':selected'); $.each(vals, function (valindex, selectedValue) { if ($(selectedValue).val() == conditionalValue) { hide = false; } return hide; }); break; default : break; } } return hide; }); return hide; } }) } // controls of element to be shown or hidden // we first look for a control with matching id // _code is field for email verification code let controls = $("#" + index + ", #" + index + "_code"); // if not, we deal with a radio or a multi checkbox. Id's are there followed by _n. Or a location Id' are followed by _lat and _lng if (controls.length < 1) { controls = el.find("[id^='" + index + "_']:not([data-disabled])"); } let ctagname = ''; if (controls.get(0)) { ctagname = controls.get(0).tagName.toLowerCase(); } if (hide === false) { if ($(controls).is(':disabled') || ((ctagname == 'hr') && $(controls).hasClass('ignore'))) { // enable controls, remove class ignore and disabled, show div conditional showControls(controls, settings); // check if control is displayChanger if (controls.hasClass('displayChanger')) { // check if depending fields must be displayed too toggleChild(data, index); } // use custom event; #index does not exist, if field is radio or multicheckbox, which is ok, because the cannot be used in calculations $('#' + index).trigger('recalculate'); return false; } } else { if (($(controls).is(':enabled') || (($(controls).is(':disabled')) && ($(controls).attr('data-disabled') != undefined))) || ((ctagname == 'hr') && $(controls).hasClass('ignore') == false)) { // disable controls, set class ignore, hide div conditional hideControls(controls); // check if control is displayChanger if (controls.hasClass('displayChanger')) { // check if depending fields must be hidden too toggleChild(data, index); } // use custom event; #index does not exist, if field is radio or multicheckbox, which is ok, because the cannot be used in calculations $('#' + index).trigger('recalculate'); return false; } } } }); // additional protected class variables can be declared here. // protected helper functions for toggleDisplay /** * Methode to enable controls, remove class ignore and disabled, show div conditional * @param {jQuery selection} controls * @returns {Boolean} */ function showControls (controls, settings) { if (controls.length < 1) { // no controls found, do nothing return false; } $.each(controls, function (cindex, control) { let elid; // only enable multicheckbox option that have no data-disabled attribute if ($(control).attr('data-disabled') == undefined) { $(control).removeAttr('disabled'); $(control).removeClass('ignore'); elid = $(control).get(0).id; } // no radio or checkbox group if (cindex === 0) { $.each(userinputs, function (i, obj) { // set to user input values switch (obj.type) { case "select": case "selectsql": if (obj.label === elid) { if ($.isPlainObject(obj.value)) { const seloptions = $(control).find('option'); $.each(seloptions, function (i, el) { $.each(obj.value, function (i, val) { if ($(el).attr('value') === val) { $(el).prop('selected', true); // you have to return false to break from an each loop return false; } $(el).prop('selected', false); return; }); }); } } break; case "multicheckbox": case "multicheckboxsql": // control is a single input. It's id (elid) has a additional counter _1....) // we cannot use the control to set a checked property of each inputs but have to go one level up and then find each input element and set the property if ($(control).parents("div.conditional").hasClass(obj.label)) { if ($.isPlainObject(obj.value)) { const boxes = $(control).parents("div.conditional").find('input'); $.each(boxes, function (i, el) { $.each(obj.value, function (ix, val) { if ($(el).attr('value') === val) { $(el).prop('checked', true); // you have to return false to break from an each loop return false; } $(el).prop('checked', false); return; }); }); } } break; case "radio": case "radiosql": // control is a single input. It's id (elid) has a additional counter _1....) // we cannot use the control to set a checked property of each inputs but have to go one level up and then find each input element and set the property if ($(control).parents("div.conditional").hasClass(obj.label)) { const radios = $(control).parents("div.conditional").find('input'); $.each(radios, function (i, el) { if ($(el).attr('value') === obj.value) { $(el).prop('checked', true); } else { $(el).prop('checked', false); } }); } break; case "checkbox": if (obj.label === elid) { $("#" + obj.label).prop("checked", obj.value); return; } break; case "signature" : if (obj.label === elid){ $("#" + obj.label).val(obj.value); if (obj.value === "") { $("#" + obj.label + "_sig").jSignature("reset"); } else { $("#" + obj.label + "_sig").jSignature("setData", "data:" + obj.value); } } break; default: if (obj.label === elid) { // used to prevent email cloaking in form used in content (plg or module) $("#" + obj.label).val(obj.value.replace(/@/g, '@')); return ; } break; } }); if ($(control).is('[readonly]') == false) { $(control).parents("div.conditional").find("button").show(); } $(control).parents("div.conditional").show(); // fix bug in google maps: Map in hidden field not displayed properly. $(control).parents("div.conditional").trigger('reloadVfMap'); } }); } /** * Methode to disable controls, set class ignore, hide div conditional * @param {jquery selection} controls * @returns {Boolean} */ function hideControls (controls) { if (controls.length < 1) { // no controls found, do nothing return false; } $.each(controls, function (cindex, control) { $(control).attr('disabled', 'disabled'); $(control).addClass('ignore'); const isCal = $(control).hasClass('isCal'); const isLocation = $(control).hasClass('locationinput'); const isSearchSelect = $(control).hasClass('select2-hidden-accessible'); // do not empty location field value and cal field value if (!(isCal === true) && !(isLocation === true)) { $(control).val(function () { return this.defaultValue; }); } $(control).prop('checked', function() { return this.defaultChecked; }); const elid = $(control).get(0).id; $('#' + elid + ' option').prop('selected', function() { return this.defaultSelected; }); if (isSearchSelect) { const seloptions = $(control).find('option'); $.each(seloptions, function (i, el) { if ($(el).prop('selected') === true) { $('#select2-' + elid + '-container').html($(el).html()); return false; } }); } // no radio or checkbox group if (cindex === 0) { // if it is a file upload field we reset the delete file checkbox to unchecked $(control).parents("div.conditional").hide(); $(control).parents("div.conditional").find(".deleteFile").prop("checked", false); } }); } /** * Basically we use the data object to find all conditional fields, who's display state depends on the state of the control with the id, given as param. * We then find the parent html element with class=conditional for each conditional field and trigger the checkConditionalState event on it * The toggleDisplay function is then performed once again for the conditional field * @param {string} restricts list of all conditionla fields and the field__values that trigger there display * @param {string} id id/class name of parent control * @returns {undefined} */ function toggleChild (restricts, id) { $.each(restricts, function (index, list) { // split the restriction string const showWhens = list.split(', '); $.each(showWhens, function (i, v) { // split the restriction into a field id and a value that, if selected , will command the field to be visible const showWhen = v.split('__'); if (showWhen.length >= 2) { // we have a depending child if (showWhen[0] == id) { // find parent element with class=conditional const conditional = $('.' + index); // check the child conditional.trigger('checkConditionalState'); } } }); }); } } }); }(jQuery)); // mend missing placeholder support in some browsers (function ($) { $.support.placeholder = ('placeholder' in document.createElement('input')); })(jQuery); const visForm = { version : '4.3.1', reloadOptionList : function (event) { event.preventDefault(); const formElement = jQuery(this).closest('form'); const fid = jQuery(formElement).attr('id'); const data = event.data; const formData = jQuery(formElement).serializeArray(); const reloadId = data.reloadId; const baseurl = data.baseurl; const cid = data.cid; formData.push({name: 'reloadId', value: data.reloadId}); jQuery.ajax({ type: 'POST', url: baseurl + '/index.php?option=com_visforms&task=visforms.reloadOptionList&id=' + fid + 'cid=' + cid, data: formData, success: function(data, textStatus, jqXHR) { jQuery('#' + fid + ' #field' + reloadId).empty().append(data); // set any possible default values given by user inputs (edit value or url parameter) visForm.setDefaultValues(fid, reloadId); // perform preSelection of a solitary option if set in field configuration visForm.preSelectSolitaryOption(reloadId); jQuery('#' + fid + ' #field' + reloadId).trigger('change'); visForm.hideSqlOptionList(fid); visForm.hideSqlDataList(fid); }, error: function(jqXHR, textStatus, errorThrown) { // give error feedback visForm.showAjaxError(jqXHR.responseText, errorThrown); }, dataType: 'html', async: true }); }, setDefaultValues : function (formId, fieldId) { const userinputs = jQuery.data(jQuery("#" + formId).get(0), 'userinputs'); if ((!userinputs) || typeof userinputs === "undefined") { return; } jQuery.each(userinputs, function (i, obj) { if (obj.label !== "field" + fieldId) { return; } if (obj.value === "undefined") { return; } if (obj.isDisabled === true && obj.isForbidden !== true) { // these fields stay with there configuration default return; } if (jQuery.isPlainObject(obj.value)) { const seloptions = jQuery("#" + obj.label).find('option'); jQuery.each(seloptions, function (i, el) { jQuery.each(obj.value, function (i, val) { if (jQuery(el).attr('value') === val) { jQuery(el).prop('selected', true); // you have to return false to break from an each loop return false; } jQuery(el).prop('selected', false); return; }); }); } return false; }); }, preSelectSolitaryOption : function (fieldId) { const field = jQuery('#field' + fieldId); if (field.hasClass('preSelectedSolitaryOption')) { let options = field.find('option'); let optionsCount = options.length; if (optionsCount === 1 && jQuery(options[0]).prop('disabled') === false) { jQuery(options[0]).prop('selected', true); } else if (optionsCount === 2) { if (options[0].value === '' && jQuery(options[1]).prop('disabled') === false) { jQuery(options[0]).prop('selected', false); jQuery(options[1]).prop('selected', true); } } } }, hideSqlOptionList : function (formId) { let hideEmpty = 1, hidePrselect = 2, hideBoth = 3, state = 0; jQuery('#' + formId + ' select.hideOnEmptyOptionList, #' + formId + ' select.hideOnPreSelectedSolitaryOption').each(function() { // Do not hide field with php Errors if (jQuery(this).hasClass('error')) { return true; } let fieldId = jQuery(this).attr('id'); let controlGroup = jQuery(this).parents('.' + fieldId); let optionsCount = jQuery(this).find('option').length; if (jQuery(this).hasClass('hideOnEmptyOptionList') && jQuery(this).hasClass('hideOnPreSelectedSolitaryOption')) { state = hideBoth; } else if (jQuery(this).hasClass('hideOnEmptyOptionList')) { state = hideEmpty; } else if (jQuery(this).hasClass('hideOnPreSelectedSolitaryOption')) { state = hidePrselect; } if (optionsCount === 0) { // hide if not option is given and hideEmpty is set if (state === hideEmpty) { jQuery(controlGroup).hide(); } else { jQuery(controlGroup).show(); } } else if (optionsCount === 1) { // if only one option is given and it's value is '' this is the 'select a value' default option if (this.options[0].value === '') { if (state === hideEmpty || state === hideBoth) { jQuery(controlGroup).hide(); } else { jQuery(controlGroup).show(); } } // if only one option is given and it's value is not '' it is a real option (happens, if for example if size attribute is set) else { if (this.selectedIndex === 0 && (state === hideBoth || state === hidePrselect)) { jQuery(controlGroup).hide(); } else { jQuery(controlGroup).show(); } } } // select an option plus one real option else if (optionsCount === 2) { if (this.selectedIndex === 1 && (state === hideBoth || state === hidePrselect)) { jQuery(controlGroup).hide(); } else { jQuery(controlGroup).show(); } } else { jQuery(controlGroup).show(); } }); }, hideSqlDataList : function (formId) { const hideEmpty = 1; let state = 0; jQuery('#' + formId + ' table.hideOnEmptyOptionList, #' + formId).each(function() { const fieldId = jQuery(this).attr('id'); const controlGroup = jQuery(this).parents('.' + fieldId); const rowCount = jQuery(this).find('tr').length; if (jQuery(this).hasClass('hideOnEmptyOptionList')) { state = hideEmpty; } if (rowCount === 0) { // hide if not option is given and hideEmpty is set if (state === hideEmpty) { jQuery(controlGroup).hide(); } else { jQuery(controlGroup).show(); } } else { jQuery(controlGroup).show(); } }); }, showAjaxError : function (responseText, errorThrown) { if (responseText === undefined || responseText === null) { return; } if (responseText.startsWith(' -1) { return true; } const label = visform.oSummaryFirstElementLayout + '' + o.label + ': ' + visform.cSummaryFirstElementLayout; switch (o.type) { case "select": case "selectsql": if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { value = []; selected = jQuery("#" + formid + " .field" + o.id + " :selected"); if (selected.length > 0) { selected.each(function () { if (jQuery(this).val() != "") { value.push(jQuery(this).text()); } }); } tmp = value.join(", "); if ((!visform.hideemptyfieldsinsummary) || (tmp != "")) { summary.push(label + visform.oSummarySecondElementLayout + tmp + visform.cSummarySecondElementLayout); } } return; case 'multicheckbox' : case 'radio' : case 'multicheckboxsql' : case 'radiosql' : const senabled = jQuery("#" + formid + " .field" + o.id + " :input:disabled"); if (!(senabled.length > 0)) { value = []; selected = (jQuery("#" + formid + " .field" + o.id + " :input:checked")); if (selected.length > 0) { selected.each(function (i) { sid = jQuery(this).attr("id"); value.push(jQuery(this).closest(".field" + o.id).find("label[for=\'" + sid + "\']").text()); }); } tmp = value.join(", "); if ((!visform.hideemptyfieldsinsummary) || (tmp != "")) { summary.push(label + visform.oSummarySecondElementLayout + tmp + visform.cSummarySecondElementLayout); } } return; case 'checkbox' : if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { value = ""; if ((jQuery("#" + formid + " #field" + o.id).prop("checked"))) { value = jQuery("#" + formid + " #field" + o.id).val(); } if ((!visform.hideemptyfieldsinsummary) || (value != "")) { summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); } } return; case 'calculation' : if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { value = jQuery("#" + formid + " #field" + o.id).val(); if ((!visform.hideemptyfieldsinsummary) || (value != "")) { if ((!visform.summaryemptycaliszero) || (!(0 == value.replace(",", ".")))) { summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); } } } return; case 'location' : const lat = jQuery("#" + formid + " #field" + o.id + "_lat"); const lng = jQuery("#" + formid + " #field" + o.id + "_lng"); if (!lat.prop("disabled") && !lng.prop("disabled")) { const value_lat = lat.val(); const value_lng = lng.val(); if ((!visform.hideemptyfieldsinsummary) || ((value_lat != "") && (value_lng != ""))) { value = ((value_lat != "") && (value_lng != "")) ? value_lat + ", " + value_lng: ""; summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); } } return; case 'signature' : if (!jQuery("#" + formid + " #field" + o.id).prop("disabled") && !jQuery("#" + formid + " #field" + o.id).hasClass('noSummary')) { const imgData = getVfSignatureImgFromCanvas({sigFieldId: "#" + formid + " #field" + o.id + "_sig"}); if ((!visform.hideemptyfieldsinsummary) || (imgData != "")) { if (imgData.substring(0, 4) !== "data") { value = imgData; } else { value = ''; } summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); } } return; case 'file' : if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { value = jQuery("#" + formid + " #field" + o.id).val(); // input element of upload field is not always displayed in form if (typeof value === "undefined") { return; } if ((!visform.hideemptyfieldsinsummary) || (value != "")) { value = value.replace(/^.+(?=[\\\/])\\/, ""); summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); } } return; default : if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { value = jQuery("#" + formid + " #field" + o.id).val(); if ((!visform.hideemptyfieldsinsummary) || (value != "")) { summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); } } return; } }) if (visform.summaryRowLayout) { htmlsummary = summary.join("<"+ visform.summaryRowLayout +">"); htmlsummary = "<"+ visform.summaryRowLayout +">" + htmlsummary + ""; } else { htmlsummary = summary.join(""); } if (htmlsummary !== "") { htmlsummary = '<' + visform.summaryLayout + ' id="'+formid+'_summary" class="'+ visform.summaryLayoutClass+' visforms_summary">' + htmlsummary + ''; jQuery("#" + formid + "_summarypage").prepend(htmlsummary); } } function verifyMail(fieldid, fid, token, baseurl) { const adr = jQuery("#" + fieldid).val(); const pData = {}; pData[token] = 1; pData['verificationAddr'] = adr; pData['fid'] = fid; jQuery.ajax({ type: 'POST', url: baseurl + '/index.php?option=com_visforms&task=visforms.sendVerficationMail', data : pData, dataType: 'text', success: function (data, textStatus, jqXHR) { alert(data); }, error: function(jqXHR, textStatus, errorThrown) { visForm.showAjaxError(jqXHR.responseText, errorThrown); } }); } function visRecaptchaCallback(val) { jQuery('#g-recaptcha-response').valid(); }