1(function($) { 2 $.extend($.fn, { 3 // public plugin functions 4 displayChanger : function (options) { 5 // Attach onchange event handler to displayChanger Element 6 $(".displayChanger").on('change', function (e) { 7 $(".conditional").trigger("checkConditionalState"); 8 }); 9 }, 10 // perform the code which is necessary to toggle the display state of one form element 11 toggleDisplay : function (options) { 12 const defaults = { 13 // just an example of adding translated texts, stepAlert is not used 14 texts: { 15 stepAlert: "Es wurde ein bedingtes Feld in einem anderen Schritt aktiviert. Eventuell funktioniert das Formular nicht mehr." 16 } 17 }; 18 const settings = jQuery.extend(true, {}, defaults, options); 19 const data = options.restricts; 20 const userinputs = $.data($(this).closest("form").get(0), 'userinputs'); 21 if ((!userinputs) || typeof userinputs === "undefined") { 22 return; 23 } 24 // el is a div class="conditional" 25 const el = $(this); 26 // data is a list of all conditional fields 27 // index is id of field 28 // value is a comma separated string of all field IDs and values the command the conditional field to be visible 29 $.each(data, function (index, value) { 30 // a conditional field may have different other fields that make it visible 31 // we will not hide it, when at least one condition is true 32 let hide = true; 33 // find the right set of conditions for the div conditional that is actually processed 34 let elId; 35 if (el.hasClass(index)) { 36 if (elId = el.attr('class').match(index)) { 37 // split the condition string 38 const showWhens = value.split(', '); 39 $.each(showWhens, function (i, v) { 40 // split the condition into a field id and a value that, if selected , will command the field to be visible 41 const showWhen = v.split('__'); 42 if (showWhen.length >= 2) { 43 const fieldId = showWhen[0]; 44 const conditionalValue = showWhen[1]; 45 // Restrictor elements that determine whether field is shown or hidden 46 // we first look if we have a single control with a matching ID 47 let restrictors = $('#' + fieldId); 48 // if not, we deal with a radio or a multi checkbox. Id's are there followed by _n 49 if (restrictors.length < 1) { 50 restrictors = $("[id^='" + fieldId + "_']"); 51 } 52 // rel is restrictor element 53 // check if we have a value in a restrictor element that will command field to be shown 54 $.each(restrictors, function (ri, rel) { 55 // only use values of elements that are enabled 56 if ($(rel).is(':enabled')) { 57 const tagname = rel.tagName.toLowerCase(); 58 switch (tagname) { 59 case "input" : 60 // selected values have checked=checked set 61 if ($(rel).is(':checked')) { 62 if ($(rel).val() == conditionalValue) { 63 hide = false;
64 return hide; 65 } 66 } 67 break; 68 case "select" : 69 const vals = $(rel).find(':selected'); 70 $.each(vals, function (valindex, selectedValue) { 71 if ($(selectedValue).val() == conditionalValue) { 72 hide = false; 73 } 74 return hide; 75 }); 76 break; 77 default : 78 break; 79 } 80 } 81 return hide; 82 }); 83 84 return hide; 85 } 86 }) 87 } 88 89 // controls of element to be shown or hidden 90 // we first look for a control with matching id 91 // _code is field for email verification code 92 let controls = $("#" + index + ", #" + index + "_code"); 93 // 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 94 if (controls.length < 1) { 95 // data-disabled: Unique values validation: Use to mark options, which are already used up 96 controls = el.find("[id^='" + index + "_']:not([data-disabled])"); 97 } 98 let ctagname = ''; 99 if (controls.get(0)) { 100 ctagname = controls.get(0).tagName.toLowerCase(); 101 } 102 if (hide === false) { 103 if ($(controls).is(':disabled') || ((ctagname == 'hr') && $(controls).hasClass('ignore'))) { 104 // enable controls, remove class ignore and disabled, show div conditional 105 showControls(controls, settings); 106 // check if control is displayChanger 107 if (controls.hasClass('displayChanger')) { 108 // check if depending fields must be displayed too 109 toggleChild(data, index); 110 } 111 // use custom event; #index does not exist, if field is radio or multicheckbox, which is ok, because the cannot be used in calculations 112 $('#' + index).trigger('recalculate'); 113 return false; 114 } 115 } else { 116 // data-disabled: Unique values validation: Use to mark options, which are already used up 117 if (($(controls).is(':enabled') || (($(controls).is(':disabled')) && ($(controls).attr('data-disabled') != undefined))) || ((ctagname == 'hr') && $(controls).hasClass('ignore') == false)) { 118 // disable controls, set class ignore, hide div conditional 119 hideControls(controls); 120 // check if control is displayChanger 121 if (controls.hasClass('displayChanger')) { 122 // check if depending fields must be hidden too 123 toggleChild(data, index); 124 } 125 // use custom event; #index does not exist, if field is radio or multicheckbox, which is ok, because the cannot be used in calculations 126 $('#' + index).trigger('recalculate'); 127 return false;
128 } 129 } 130 } 131 }); 132 133 // additional protected class variables can be declared here. 134 135 // protected helper functions for toggleDisplay 136 137 /** 138 * Methode to enable controls, remove class ignore and disabled, show div conditional 139 * @param {jQuery selection} controls 140 * @returns {Boolean} 141 */ 142 function showControls (controls, settings) { 143 if (controls.length < 1) { 144 // no controls found, do nothing 145 return false; 146 } 147 $.each(controls, function (cindex, control) { 148 let elid; 149 // only enable multicheckbox option that have no data-disabled attribute 150 // data-disabled: Unique values validation: Use to mark options, which are already used up 151 // for some like disabled adding or removing the content attribute disabled="disabled" always toggles the js property 152 // so using removeAttr('disabled'); is completely okay! 153 if ($(control).attr('data-disabled') == undefined) { 154 $(control).removeAttr('disabled'); 155 $(control).removeClass('ignore'); 156 elid = $(control).get(0).id; 157 } 158 // no radio or checkbox group 159 if (cindex === 0) { 160 $.each(userinputs, function (i, obj) { 161 // set to user input values 162 switch (obj.type) { 163 case "select": 164 case "selectsql": 165 if (obj.label === elid) { 166 if ($.isPlainObject(obj.value)) { 167 const seloptions = $(control).find('option'); 168 $.each(seloptions, function (i, el) { 169 $.each(obj.value, function (i, val) { 170 if ($(el).attr('value') === val) { 171 $(el).prop('selected', true); 172 // you have to return false to break from an each loop 173 return false; 174 } 175 $(el).prop('selected', false); 176 return; 177 }); 178 }); 179 } 180 } 181 break; 182 case "multicheckbox": 183 case "multicheckboxsql": 184 // control is a single input. It's id (elid) has a additional counter _1....) 185 // 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 186 if ($(control).parents("div.conditional").hasClass(obj.label)) { 187 if ($.isPlainObject(obj.value)) { 188 const boxes = $(control).parents("div.conditional").find('input'); 189 $.each(boxes, function (i, el) { 190 $.each(obj.value, function (ix, val) { 191 if ($(el).attr('value') === val) { 192 $(el).prop('checked', true); 193 // you have to return false to break from an each loop 194 return false;
195 } 196 $(el).prop('checked', false); 197 return; 198 }); 199 }); 200 } 201 } 202 break; 203 case "radio": 204 case "radiosql": 205 // control is a single input. It's id (elid) has a additional counter _1....) 206 // 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 207 if ($(control).parents("div.conditional").hasClass(obj.label)) { 208 const radios = $(control).parents("div.conditional").find('input'); 209 $.each(radios, function (i, el) { 210 if ($(el).attr('value') === obj.value) { 211 $(el).prop('checked', true); 212 } 213 else { 214 $(el).prop('checked', false); 215 } 216 }); 217 } 218 break; 219 case "checkbox": 220 if (obj.label === elid) { 221 $("#" + obj.label).prop("checked", obj.value); 222 return; 223 } 224 break; 225 case "signature" : 226 // value in text-input was already reset to defaultValue in hideControls 227 // the canvas content is only replaced when the field is displayed again (showControls) 228 if (obj.label === elid){ 229 let canvasContainer = document.getElementById(obj.label + "_sig"); 230 const {api} = canvasContainer.dataset; 231 // jSignature 232 if (!api) { 233 $("#" + obj.label).val(obj.value); 234 if (obj.value === "") { 235 $("#" + obj.label + "_sig").jSignature("reset"); 236 } 237 else { 238 $("#" + obj.label + "_sig").jSignature("setData", "data:" + obj.value); 239 } 240 } 241 // signaturePad 242 else if (api === "1") { 243 // create custom event and trigger it on the canvas element 244 let canvas = document.querySelector("#" + obj.label + "_sig canvas") 245 const event = new CustomEvent('replaceCanvasImage', {bubbles: true, detail : obj.value}); 246 canvas.dispatchEvent(event); 247 } 248 } 249 break; 250 default: 251 if (obj.label === elid) { 252 // used to prevent email cloaking in form used in content (plg or module) 253 $("#" + obj.label).val(obj.value.replace(/@/g, '@')); 254 return ; 255 } 256 break; 257 } 258 }); 259 if ($(control).is('[readonly]') == false) { 260 $(control).parents("div.conditional").find("button").removeClass('vishidden'); 261 } 262 $(control).parents("div.conditional").removeClass('vishidden'); 263 // fix bug in google maps: Map in hidden field not displayed properly. 264 $(control).parents("div.conditional").trigger('reloadVfMap'); 265 } 266 }); 267 } 268 269 /** 270 * Methode to disable controls, set class ignore, hide div conditional 271 * @param {jquery selection} controls 272 * @returns {Boolean} 273 */
274 function hideControls (controls) { 275 if (controls.length < 1) { 276 // no controls found, do nothing 277 return false; 278 } 279 $.each(controls, function (cindex, control) { 280 $(control).attr('disabled', 'disabled'); 281 $(control).addClass('ignore'); 282 const isCal = $(control).hasClass('isCal'); 283 const isLocation = $(control).hasClass('locationinput'); 284 const isSearchSelect = $(control).hasClass('select2-hidden-accessible'); 285 // do not empty location field value and cal field value 286 if (!(isCal === true) && !(isLocation === true)) { 287 $(control).val(function () { 288 return this.defaultValue; 289 }); 290 } 291 $(control).prop('checked', function() { 292 return this.defaultChecked; 293 }); 294 const elid = $(control).get(0).id; 295 $('#' + elid + ' option').prop('selected', function() { 296 return this.defaultSelected; 297 }); 298 if (isSearchSelect) { 299 const seloptions = $(control).find('option'); 300 $.each(seloptions, function (i, el) { 301 if ($(el).prop('selected') === true) { 302 $('#select2-' + elid + '-container').html($(el).html()); 303 return false; 304 } 305 }); 306 } 307 // no radio or checkbox group 308 if (cindex === 0) { 309 // if it is a file upload field we reset the delete file checkbox to unchecked 310 $(control).parents("div.conditional").addClass('vishidden'); 311 $(control).parents("div.conditional").find(".deleteFile").prop("checked", false); 312 } 313 }); 314 } 315 316 /** 317 * 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. 318 * We then find the parent html element with class=conditional for each conditional field and trigger the checkConditionalState event on it 319 * The toggleDisplay function is then performed once again for the conditional field 320 * @param {string} restricts list of all conditionla fields and the field__values that trigger there display 321 * @param {string} id id/class name of parent control 322 * @returns {undefined} 323 */ 324 function toggleChild (restricts, id) { 325 $.each(restricts, function (index, list) { 326 // split the restriction string 327 const showWhens = list.split(', '); 328 $.each(showWhens, function (i, v) { 329 // split the restriction into a field id and a value that, if selected , will command the field to be visible 330 const showWhen = v.split('__'); 331 if (showWhen.length >= 2) { 332 // we have a depending child 333 if (showWhen[0] == id) { 334 // find parent element with class=conditional 335 const conditional = $('.' + index); 336 // check the child 337 conditional.trigger('checkConditionalState'); 338 } 339 } 340 }); 341 }); 342 } 343 } 344 }); 345}(jQuery)); 346 347// mend missing placeholder support in some browsers 348(function ($) { 349 $.support.placeholder = ('placeholder' in document.createElement('input')); 350})(jQuery); 351 352const visForm = { 353 version : '5.4.0', 354 // used for both 355 // reload option list (listbox) 356 // reload field value (input) 357 reloadOptionList : function (event) { 358 event.preventDefault(); 359 const formElement = jQuery(this).closest('form'); 360 const fid = jQuery(formElement).attr('id'); 361 const data = event.data;
362 const formData = jQuery(formElement).serializeArray(); 363 const reloadId = data.reloadId; 364 const baseurl = data.baseurl; 365 const cid = data.cid; 366 const phpTask = data.task; 367 formData.push({name: 'reloadId', value: data.reloadId}); 368 jQuery.ajax({ 369 type: 'POST', 370 url: baseurl + '/index.php?option=com_visforms&task=visforms.'+phpTask+'&id=' + fid + '&cid=' + cid, 371 data: formData, 372 success: function(data, textStatus, jqXHR) { 373 let elId = '#' + fid + ' #field' + reloadId; 374 if (phpTask === 'reloadOptionList') { 375 jQuery(elId).empty().append(data); 376 // set any possible default values given by user inputs (edit value or url parameter) in reloaded options 377 visForm.setDefaultValues(fid, reloadId); 378 // perform preSelection of a solitary option if set in field configuration 379 visForm.preSelectSolitaryOption(reloadId); 380 jQuery(elId).trigger('change'); 381 visForm.hideSqlOptionList(fid); 382 visForm.hideSqlDataList(fid); 383 } 384 else if (phpTask === 'reloadValue') { 385 // Set value in reloaded field 386 jQuery(elId).val(data.replace(/@/g, '@')); 387 // Fieldtype date needs special treatment 388 // all Calender fields are accessible through JoomlaCalendar.getCalObject(element)._joomlaCalendar; 389 // where element is the HTML Input field 390 // the _joomlaCalendar is attached to the parent ('.field-calender') element and can be called directly 391 // if there is no _joomlaCalendar for a field, this getCalObject() returns false 392 // change the data-alt-value 393 // call checkInputs() on the _joomlaCalendar to properly set the value in a date field 394 if (jQuery(elId)[0].hasAttribute('data-alt-value')) { 395 jQuery(elId).attr('data-alt-value', data.replace(/@/g, '@')); 396 let calObj = JoomlaCalendar.getCalObject(jQuery(elId)[0]); 397 if (calObj) { 398 let calendar = calObj._joomlaCalendar; 399 calendar.checkInputs(); 400 } 401 } 402 jQuery(elId).trigger("recalculate"); 403 jQuery(elId).trigger('change'); 404 } 405 }, 406 error: function(jqXHR, textStatus, errorThrown) { 407 // give error feedback 408 visForm.showAjaxError(jqXHR.responseText, errorThrown); 409 }, 410 dataType: 'html', 411 async: true 412 }); 413 }, 414 setDefaultValues : function (formId, fieldId) { 415 const userinputs = jQuery.data(jQuery("#" + formId).get(0), 'userinputs'); 416 if ((!userinputs) || typeof userinputs === "undefined") { 417 return; 418 } 419 jQuery.each(userinputs, function (i, obj) { 420 if (obj.label !== "field" + fieldId) { 421 return; 422 } 423 if (obj.value === "undefined") { 424 return; 425 } 426 if (obj.isDisabled === true && obj.isForbidden !== true) { 427 // these fields stay with there configuration default 428 return; 429 } 430 if (jQuery.isPlainObject(obj.value)) { 431 const seloptions = jQuery("#" + obj.label).find('option'); 432 jQuery.each(seloptions, function (i, el) { 433 jQuery.each(obj.value, function (i, val) { 434 if (jQuery(el).attr('value') === val) { 435 jQuery(el).prop('selected', true); 436 // you have to return false to break from an each loop 437 return false; 438 } 439 jQuery(el).prop('selected', false); 440 return; 441 }); 442 }); 443 } 444 return false;
445 }); 446 }, 447 preSelectSolitaryOption : function (fieldId) { 448 const field = jQuery('#field' + fieldId); 449 if (field.hasClass('preSelectedSolitaryOption')) { 450 let options = field.find('option'); 451 let optionsCount = options.length; 452 if (optionsCount === 1 && jQuery(options[0]).prop('disabled') === false) { 453 jQuery(options[0]).prop('selected', true); 454 } 455 else if (optionsCount === 2) { 456 if (options[0].value === '' && jQuery(options[1]).prop('disabled') === false) { 457 jQuery(options[0]).prop('selected', false); 458 jQuery(options[1]).prop('selected', true); 459 } 460 } 461 } 462 }, 463 hideSqlOptionList : function (formId) { 464 let hideEmpty = 1, hidePrselect = 2, hideBoth = 3, state = 0; 465 jQuery('#' + formId + ' select.hideOnEmptyOptionList, #' + formId + ' select.hideOnPreSelectedSolitaryOption').each(function() { 466 // Do not hide field with php Errors 467 if (jQuery(this).hasClass('error')) { 468 return true; 469 } 470 let fieldId = jQuery(this).attr('id'); 471 let controlGroup = jQuery(this).parents('.' + fieldId); 472 let optionsCount = jQuery(this).find('option').length; 473 if (jQuery(this).hasClass('hideOnEmptyOptionList') && jQuery(this).hasClass('hideOnPreSelectedSolitaryOption')) { 474 state = hideBoth; 475 } 476 else if (jQuery(this).hasClass('hideOnEmptyOptionList')) { 477 state = hideEmpty; 478 } 479 else if (jQuery(this).hasClass('hideOnPreSelectedSolitaryOption')) { 480 state = hidePrselect; 481 } 482 if (optionsCount === 0) { 483 // hide if not option is given and hideEmpty is set 484 if (state === hideEmpty) { 485 jQuery(controlGroup).addClass('vishidden'); 486 } 487 else { 488 jQuery(controlGroup).removeClass('vishidden'); 489 } 490 } 491 else if (optionsCount === 1) { 492 // if only one option is given and it's value is '' this is the 'select a value' default option 493 if (this.options[0].value === '') { 494 if (state === hideEmpty || state === hideBoth) { 495 jQuery(controlGroup).addClass('vishidden'); 496 } 497 else { 498 jQuery(controlGroup).removeClass('vishidden'); 499 } 500 } 501 // 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) 502 else { 503 if (this.selectedIndex === 0 && (state === hideBoth || state === hidePrselect)) { 504 jQuery(controlGroup).addClass('vishidden'); 505 } 506 else { 507 jQuery(controlGroup).removeClass('vishidden'); 508 } 509 } 510 } 511 // select an option plus one real option 512 else if (optionsCount === 2) { 513 if (this.selectedIndex === 1 && (state === hideBoth || state === hidePrselect)) { 514 jQuery(controlGroup).addClass('vishidden'); 515 } 516 else { 517 jQuery(controlGroup).removeClass('vishidden'); 518 } 519 } 520 else { 521 jQuery(controlGroup).removeClass('vishidden'); 522 } 523 }); 524 }, 525 hideSqlDataList : function (formId) { 526 const hideEmpty = 1; 527 let state = 0; 528 jQuery('#' + formId + ' table.hideOnEmptyOptionList, #' + formId).each(function() { 529 const fieldId = jQuery(this).attr('id'); 530 const controlGroup = jQuery(this).parents('.' + fieldId); 531 const rowCount = jQuery(this).find('tr').length; 532 if (jQuery(this).hasClass('hideOnEmptyOptionList')) { 533 state = hideEmpty; 534 } 535 if (rowCount === 0) { 536 // hide if not option is given and hideEmpty is set 537 if (state === hideEmpty) { 538 jQuery(controlGroup).addClass('vishidden'); 539 } 540 else { 541 jQuery(controlGroup).removeClass('vishidden'); 542 } 543 } 544 else { 545 jQuery(controlGroup).removeClass('vishidden'); 546 } 547 }); 548 }, 549 showAjaxError : function (responseText, errorThrown) { 550 if (responseText === undefined || responseText === null) { 551 return; 552 } 553 if (responseText.startsWith('<!DOC')) { 554 alert(errorThrown); 555 } 556 else { 557 alert(responseText); 558 } 559 }, 560 showProcessFormMsg : function (parentFormId) { 561 // show form processing message 562 // div _processform does only exist, if showformprocessmessage is enabled! 563 let div = jQuery("#"+parentFormId+"_processform"); 564 if (div.length) { 565 jQuery("#vispoweredby").addClass('vishidden'); 566 jQuery("#"+parentFormId).addClass('vishidden'); 567 div.removeClass('vishidden'); 568 this.focusjQueryElementTop(div); 569 } 570 }, 571 // expects div to be a jQuery object of an HTML element 572 focusjQueryElementTop : function (div) { 573 let elOffset = div.offset().top; 574 let elHeight = div.height(); 575 let windowHeight = jQuery(window).height(); 576 let offset; 577 // focus div with form processing message in the middle of the view port 578 if (elHeight < windowHeight) { 579 offset = elOffset - ((windowHeight / 2) - (elHeight / 2)); 580 } 581 else { 582 offset = elOffset; 583 } 584 let speed = 700; 585 jQuery('html, body').animate({scrollTop: offset}, speed); 586 }, 587 onNextButtonClick: function (button) { 588 const visform = jQuery(button).closest('form'); 589 const isValid = jQuery(visform).valid(); 590 const isPending = jQuery(visform).validate().pendingRequest !== 0; 591 if (isPending) { 592 if (typeof validPendingTimeout !== "undefined") { 593 clearTimeout(validPendingTimeout); 594 } 595 validPendingTimeout = setTimeout(function () { 596 visForm.onNextButtonClick(button); 597 }, 200); 598 } 599 if (jQuery(visform).valid() && !isPending) { 600 let fieldset = jQuery(button).closest('[class^="fieldset-"]').next(); 601 jQuery(button).closest('[class^="fieldset-"]').next().removeClass("vishidden").addClass("active"); 602 jQuery(button).closest('[class^="fieldset-"]').next().trigger("reloadVfMap"); 603 jQuery(button).closest('[class^="fieldset-"]').addClass("vishidden").removeClass("active"); 604 // trigger after active was removed from current fieldset 605 jQuery(button).closest('[class^="fieldset-"]').next().trigger('setFocusOnClassChange',["
605next"]); 606 const activeBadge = jQuery(visform).find('.visprogress .badge.badge-important'); 607 jQuery(activeBadge).removeClass("badge-important").addClass("badge-success"); 608 jQuery(activeBadge).closest('.stepCont').next().find('.badge').addClass("badge-important"); 609 // we have to remove class ignore from controls on next page, because the submit button could be on that page 610 // and we cannot remove class ignore when click event is on submit button (due to validator) 611 jQuery(button).closest('[class^="fieldset-"]').next().find('input, textarea, select').not(":disabled").removeClass("ignore"); 612 } 613 }, 614 onSummaryButtonClick: function (button) { 615 const formid = jQuery(button).closest("form").get(0).id; 616 const isValid = jQuery("#" + formid).valid(); 617 const isPending = jQuery("#" + formid).validate().pendingRequest !== 0; 618 if (isPending) { 619 if (typeof validPendingTimeout !== "undefined") { 620 clearTimeout(validPendingTimeout); 621 } 622 validPendingTimeout = setTimeout(function () { 623 visForm.onSummaryButtonClick(button); 624 }, 200); 625 } 626 if (jQuery("#" + formid).valid() && !isPending) { 627 visForm.createSummaryHtml(formid); 628 jQuery(button).closest('[class^="fieldset-"]').next().removeClass("vishidden").addClass("active"); 629 jQuery(button).closest('[class^="fieldset-"]').addClass("vishidden").removeClass("active"); 630 // trigger after active was removed from current fieldset 631 jQuery(button).closest('[class^="fieldset-"]').next().trigger('setFocusOnClassChange',["next"]); 632 const activeBadge = jQuery('#' + formid + ' .visprogress .badge.badge-important'); 633 jQuery(activeBadge).removeClass("badge-important").addClass("badge-success"); 634 jQuery(activeBadge).closest('.stepCont').next().find('.badge').addClass("badge-important"); 635 // force validation of captcha input on summary page 636 jQuery(button).closest('[class^="fieldset-"]').next().find('input').not(":disabled").removeClass("ignore"); 637 // force validation of hCaptcha input on summary page 638 jQuery(button).closest('[class^="fieldset-"]').next().find('textarea').not(":disabled").removeClass("ignore"); 639 } 640 // recaptcha must be reloaded in order to be checked for required properly 641 if (typeof grecaptcha !== "undefined") { 642 grecaptcha.reset(); 643 } 644 }, 645 createSummaryHtml : function(formid) { 646 let htmlsummary; 647 const visform = jQuery.data(jQuery("#" + formid).get(0), 'visform'); 648 const fields = visform.fields; 649 const summary = []; 650 jQuery.each(fields, function (i, o) { 651 let tmp; 652 let selected; 653 let value; 654 if (jQuery.inArray(o.type, ['image', 'submit', 'reset', 'fieldsep', 'hidden', 'pagebreak']) > -1) { 655 return true; 656 } 657 const label = visform.oSummaryFirstElementLayout + '' + o.label + ': ' + visform.cSummaryFirstElementLayout; 658 switch (o.type) { 659 case "select": 660 case "selectsql": 661 if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { 662 value = []; 663 selected = jQuery("#" + formid + " .field" + o.id + " :selected"); 664 if (selected.length > 0) { 665 selected.each(function () { 666 if (jQuery(this).val() != "") { 667 value.push(jQuery(this).text()); 668 } 669 }); 670 } 671 tmp = value.join(", "); 672 if ((!visform.hideemptyfieldsinsummary) || (tmp != "")) { 673 summary.push(label + visform.oSummarySecondElementLayout + tmp + visform.cSummarySecondElementLayout); 674 } 675 } 676 return;
677 case 'multicheckbox' : 678 case 'radio' : 679 case 'multicheckboxsql' : 680 case 'radiosql' : 681 // Unique Values property sets used inputs to disabled and adds attribute data-disabled=disabled. 682 // Therefore prop disabled cannot be used as only means to detect, whether a multicheckbox or radio is conditianal or simply disabled as because it is used 683 // Search for a disabled input which does not have a data-disabled attribute 684 const senabled = jQuery("#" + formid + " .field" + o.id + " :input:disabled:not([data-disabled])"); 685 if (!(senabled.length > 0)) { 686 value = []; 687 selected = (jQuery("#" + formid + " .field" + o.id + " :input:checked")); 688 689 if (selected.length > 0) { 690 selected.each(function (i) { 691 sid = jQuery(this).attr("id"); 692 value.push(jQuery(this).closest(".field" + o.id).find("label[for=\'" + sid + "\']").text()); 693 }); 694 } 695 tmp = value.join(", "); 696 if ((!visform.hideemptyfieldsinsummary) || (tmp != "")) { 697 summary.push(label + visform.oSummarySecondElementLayout + tmp + visform.cSummarySecondElementLayout); 698 } 699 } 700 return; 701 case 'checkbox' : 702 if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { 703 value = ""; 704 if ((jQuery("#" + formid + " #field" + o.id).prop("checked"))) { 705 value = jQuery("#" + formid + " #field" + o.id).val(); 706 707 } 708 if ((!visform.hideemptyfieldsinsummary) || (value != "")) { 709 summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); 710 } 711 } 712 return; 713 case 'calculation' : 714 if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { 715 value = jQuery("#" + formid + " #field" + o.id).val(); 716 if ((!visform.hideemptyfieldsinsummary) || (value != "")) { 717 if ((!visform.summaryemptycaliszero) || (!(0 == value.replace(",", ".")))) { 718 summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); 719 } 720 } 721 } 722 return; 723 case 'location' : 724 const lat = jQuery("#" + formid + " #field" + o.id + "_lat"); 725 const lng = jQuery("#" + formid + " #field" + o.id + "_lng"); 726 if (!lat.prop("disabled") && !lng.prop("disabled")) { 727 const value_lat = lat.val(); 728 const value_lng = lng.val(); 729 if ((!visform.hideemptyfieldsinsummary) || ((value_lat != "") && (value_lng != ""))) { 730 value = ((value_lat != "") && (value_lng != "")) ? value_lat + ", " + value_lng: ""; 731 summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); 732 } 733 } 734 return; 735 case 'signature' : 736 if (!jQuery("#" + formid + " #field" + o.id).prop("disabled") && !jQuery("#" + formid + " #field" + o.id).hasClass('noSummary')) { 737 const imgData = visForm.getVfSignatureImgFromCanvas({sigFieldId: "#" + formid + " #field" + o.id + "_sig", formid: formid}); 738 if ((!visform.hideemptyfieldsinsummary) || (imgData != "")) { 739 if (imgData.substring(0, 4) !== "data") { 740 value = imgData; 741 } 742 else { 743 value = '<img src="' + imgData + '" />'; 744 } 745 summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); 746 } 747 } 748 return; 749 case 'file' : 750 if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { 751 value = jQuery("#" + formid + " #field" + o.id).val(); 752 // input element of upload field is not always displayed in form 753 if (typeof value === "undefined") { 754 return; 755 } 756 if ((!visform.hideemptyfieldsinsummary) || (value != "")) { 757 value = value.replace(/^.+(?=[\\\/])\\/, ""); 758 summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); 759 } 760 } 761 return; 762 default : 763 if (!jQuery("#" + formid + " #field" + o.id).prop("disabled")) { 764 value = jQuery("#" + formid + " #field" + o.id).val(); 765 if ((!visform.hideemptyfieldsinsummary) || (value != "")) { 766 summary.push(label + visform.oSummarySecondElementLayout + value + visform.cSummarySecondElementLayout); 767 } 768 } 769 return; 770 } 771 }) 772 if (visform.summaryRowLayout) { 773 htmlsummary = summary.join("</"+ visform.summaryRowLayout+"><"+ visform.summaryRowLayout +">"); 774 htmlsummary = "<"+ visform.summaryRowLayout +">" + htmlsummary + "</"+ visform.summaryRowLayout+">"; 775 } 776 else { 777 htmlsummary = summary.join(""); 778 } 779 if (htmlsummary !== "") { 780 htmlsummary = '<' + visform.summaryLayout + ' id="'+formid+'_summary" class="'+ visform.summaryLayoutClass+' visforms_summary">' + htmlsummary + '</' + visform.summaryLayout + '>'; 781 jQuery("#" + formid + "_summarypage").prepend(htmlsummary); 782 } 783 }, 784 verifyMail: function (event) { 785 let item = event.target; 786 if (item.nodeName === 'SPAN' && ['A', 'BUTTON'].includes(item.parentNode.nodeName)) { 787 item = item.parentNode; 788 } 789 if (item.nodeName === 'A') { 790 event.preventDefault(); 791 } 792 if (item.hasAttribute('disabled') || !item.hasAttribute('data-fieldid') || !item.hasAttribute('data-fid') || !item.hasAttribute('data-token')|| !item.hasAttribute('data-baseurl')) { 793 return; 794 } 795 const { 796 fieldid 797 } = item.dataset; 798 const { 799 fid 800 } = item.dataset; 801 const { 802 token 803 } = item.dataset; 804 const { 805 baseurl 806 } = item.dataset; 807 const adr = jQuery("#" + fieldid).val(); 808 const pData = {}; 809 pData[token] = 1; 810 pData['verificationAddr'] = adr; 811 pData['fid'] = fid; 812 jQuery.ajax({ 813 type: 'POST', 814 url: baseurl + '/index.php?option=com_visforms&task=visforms.sendVerficationMail', 815 data : pData, 816 dataType: 'text', 817 success: function (data, textStatus, jqXHR) { 818 alert(data); 819 }, 820 error: function(jqXHR, textStatus, errorThrown) { 821 visForm.showAjaxError(jqXHR.responseText, errorThrown); 822 } 823 }); 824 }, 825 validateDateOnUpdate : function (event) { 826 let input = event.target; 827 jQuery(input).valid(); 828 jQuery(".isCal").trigger("update"); 829 return true; 830 }, 831 getVfSignatureImgFromCanvas : function (sigFieldOptions) { 832 sigFieldOptions = sigFieldOptions || {}; 833 if (!sigFieldOptions.sigFieldId) { 834 return false;
835 } 836 if (!sigFieldOptions.formid) { 837 return false; 838 } 839 let canvasContainer = document.querySelector(sigFieldOptions.sigFieldId); 840 const {api} = canvasContainer.dataset; 841 const settings = jQuery.data(jQuery("#" + sigFieldOptions.formid).get(0), 'visformsettings'); 842 let errorMsg = settings.texts.txtCantCreateSigImage; 843 let data, src; 844 // jSignature 845 if (!api) { 846 // translated texts can be found in the visformsetting data of the form 847 // always returns true in modern browsers. Returns true or false as expected in old browsers 848 let supportsSvg = document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Shape", "1.0"); 849 let dataFormat = (supportsSvg) ? "image/svg+xml;base64" : "image"; 850 let imgFormat = (supportsSvg) ? "image/svg+xml;base64" : "image/png;base64"; 851 try { 852 data = jQuery(sigFieldOptions.sigFieldId).jSignature("getData", dataFormat); 853 src = "data:" + imgFormat + "," + data[1]; 854 return src; 855 } catch (ex) { 856 return errorMsg; 857 } 858 } 859 // signaturePad 860 else if (api === "1") { 861 let canvas = document.querySelector(sigFieldOptions.sigFieldId + " canvas"); 862 const { 863 fieldid 864 } = canvas.dataset; 865 return jQuery("#field" + fieldid).val(); 866 } 867 return errorMsg; 868 }, 869 initVfSignature : function (fieldOptions) { 870 fieldOptions = fieldOptions || {} ; 871 if (!fieldOptions.fieldId) {return false;} 872 let resetText = fieldOptions.texts.resetText; 873 let unlockText = fieldOptions.texts.unlockText; 874 let lockText = fieldOptions.texts.lockText; 875 this.fieldId = fieldOptions.fieldId; // ToDo sets field id in visForm object; not really necessary because it is only used inside the initVfSignature; could use let fieldId instead as a shorthand or simply fieldOptions.fieldId 876 jQuery("#field" + this.fieldId + "_sig").jSignature({cssclass:"visCanvas",width:fieldOptions.width, height:fieldOptions.height}); 877 jQuery('<input type="button" value="'+resetText+'" class="vfSigReset">').on("click", {field: this.fieldId}, function(e){ 878 jQuery("#field" + e.data.field + "_sig").jSignature("reset"); 879 jQuery("#field" + e.data.field).val(""); 880 }).appendTo(jQuery("#field" + this.fieldId + "_sigtools")); 881 jQuery("#field" + this.fieldId + "_sig canvas").attr('tabindex', '0'); 882 jQuery("#field" + this.fieldId + "_sig canvas").attr('aria-label', fieldOptions.label); 883 jQuery("#field" + this.fieldId + "_sig canvas").attr('aria-labelledby', fieldOptions.fieldName + "_siglbl"); 884 // prevent unintentional writing on mobiles on scroll 885 if (("ontouchstart" in window) || navigator.maxTouchPoints || (fieldOptions.lockCanvas && !(jQuery("#field" + this.fieldId + "_sig").hasClass("isForbidden")))) { 886 jQuery('<input type="button" value="'+unlockText+'" class="vfUnlockC">').on("click", {field: this.fieldId}, function(e){ 887 visForm.enableJSignature({field: e.data.field}); 888 }).appendTo(jQuery("#field" + this.fieldId + "_sigtools")); 889 jQuery('<input type="button" value="'+lockText+'" class="vfLockC">').on("click", {field: this.fieldId}, function(e){ 890 visForm.disableJSignature({field: e.data.field}); 891 }).appendTo(jQuery("#field" + this.fieldId + "_sigtools")); 892 this.disableJSignature({field: this.fieldId}); 893 } 894 jQuery("#field" + this.fieldId + "_sig").on("change", {field: this.fieldId}, function (e) { 895 var data = jQuery("#field" + e.data.field + "_sig").jSignature("getData", "base30"); 896 if(jQuery.isArray(data) && (data.length === 2) && (data[1] !== "")){ 897 jQuery("#field" + e.data.field).val(data.join(",")) 898 } 899 else { 900 jQuery("#field" + e.data.field).val(""); 901 } 902 jQuery("#field" + e.data.field).valid(); 903 });
904 jQuery("#field" + this.fieldId + "_sig").closest("form").on("reset", {field: this.fieldId}, function(e) { 905 // jSignature("reset") empties the canvas and existing data and triggers a change event, which results in setting the value in the text-input to '' 906 jQuery("#field" + e.data.field + "_sig").jSignature("reset"); 907 }); 908 if (jQuery("#field" + this.fieldId + "_sig").hasClass("isForbidden")) { 909 visForm.disableJSignature({field: this.fieldId}); 910 } 911 }, 912 // lock canvas, used in edit view and on mobile 913 disableJSignature : function(data){ 914 jQuery("#field" + data.field + "_sig").jSignature("disable"); 915 visForm.hideDisableSignatureControls(data); 916 }, 917 // unlock canvas, used in edit view and on mobile 918 enableJSignature : function(data){ 919 jQuery("#field" + data.field + "_sig").jSignature("enable"); 920 visForm.hideEnableSignatureControls(data); 921 }, 922 initVfSignaturePad : function (fieldOptions) { 923 fieldOptions = fieldOptions || {} ; 924 if (!fieldOptions.fieldId) {return false;} 925 const resetText = fieldOptions.texts.resetText; 926 const unlockText = fieldOptions.texts.unlockText; 927 const lockText = fieldOptions.texts.lockText; 928 const fieldId = fieldOptions.fieldId; 929 const canvasContainer = document.getElementById("field" + fieldId + "_sig"); 930 const canvas = document.querySelector("#field" + fieldId + "_sig canvas"); 931 const signaturePad = new SignaturePad(canvas); 932 jQuery('<input type="button" value="' + resetText + '" class="vfSigReset">').on("click", {field: fieldId}, function (e) { 933 signaturePad.clear(); 934 jQuery("#field" + e.data.field).val(""); 935 }).appendTo(jQuery("#field" + fieldId + "_sigtools")); 936 // Add lock/unlock buttons if lockCanvas is set or we are on a device with ontouchstart and canvas container has not isForbidden 937 // isForbidden set on control group div = user has no permission to edit the data 938 // isForbidden set on canvas container div = field is set to 'readonly' in field configuration 939 // we look for isForbidden on canvas container div here! 940 if (("ontouchstart" in window) || navigator.maxTouchPoints || (fieldOptions.lockCanvas && !(jQuery("#field" + fieldId + "_sig").hasClass("isForbidden")))) { 941 jQuery('<input type="button" value="' + unlockText + '" class="vfUnlockC">').on("click", {field: fieldId}, function (e) { 942 signaturePad.on(); 943 visForm.hideEnableSignatureControls({field: e.data.field}); 944 }).appendTo(jQuery("#field" + fieldId + "_sigtools")); 945 jQuery('<input type="button" value="' + lockText + '" class="vfLockC">').on("click", {field: fieldId}, function (e) { 946 signaturePad.off(); 947 visForm.hideDisableSignatureControls({field: e.data.field}); 948 }).appendTo(jQuery("#field" + fieldId + "_sigtools")); 949 signaturePad.off(); 950 visForm.hideDisableSignatureControls({field: fieldId}); 951 } 952 // store stroke data in text-input 953 signaturePad.addEventListener("endStroke", () => { 954 const data = signaturePad.toDataURL(); 955 const { 956 fieldid 957 } = signaturePad.canvas.dataset; 958 jQuery("#field" + fieldid).val(data); 959 }); 960 // handle reset on form (form view only) 961 jQuery("#field" + fieldId + "_sig").closest("form").on("reset", function (e) { 962 visForm.resetSignaturePadCanvasDataToEmpty(fieldId, signaturePad); 963 }); 964 // after value is set in text-input, make sure, that the correct data are displayed on canvas 965 // add event handler for visforms event visfieldInitialised 966 jQuery("#field" + fieldId + "_sig").closest("form").on('visfieldInitialized', function() { 967 // get value in text-input 968 let data = jQuery("#field" + fieldId).val(); 969 visForm.setImageOnCanvas(signaturePad, data); 970 });
971 // handle data displayed on canvas for conditional signature field 972 // custom event displatched in showControl() 973 // add event handler 974 canvasContainer.addEventListener('replaceCanvasImage', (event) => { 975 event.stopPropagation(); 976 visForm.setImageOnCanvas(signaturePad, event.detail); 977 }); 978 if (jQuery("#field" + fieldId + "_sig").hasClass("isForbidden")) { 979 signaturePad.off(); 980 } 981 }, 982 resetSignaturePadCanvasDataToEmpty : function(fieldId, canvas) { 983 // set text-input to empty 984 jQuery("#field" + fieldId).val(''); 985 // clear canvas 986 canvas.clear(); 987 }, 988 setImageOnCanvas: function (signaturePad, data) { 989 if (data) { 990 signaturePad.clear(); 991 signaturePad.fromDataURL(data); 992 } 993 else { 994 signaturePad.clear(); 995 } 996 }, 997 // toggle visibility of signature controls 998 hideDisableSignatureControls : function (data) { 999 jQuery("#field" + data.field + "_sigtools .vfSigReset").addClass('vishidden'); 1000 jQuery("#field" + data.field + "_sigtools .vfLockC").addClass('vishidden'); 1001 jQuery("#field" + data.field + "_sigtools .vfUnlockC").removeClass('vishidden'); 1002 }, 1003 hideEnableSignatureControls: function(data) { 1004 jQuery("#field" + data.field + "_sigtools .vfSigReset").removeClass('vishidden'); 1005 jQuery("#field" + data.field + "_sigtools .vfLockC").removeClass('vishidden'); 1006 jQuery("#field" + data.field + "_sigtools .vfUnlockC").addClass('vishidden'); 1007 } 1008}; 1009(function($) { 1010 $.extend($.fn, { 1011 initVisform : function (options) { 1012 const defaults = { 1013 // adding translated texts 1014 texts: { 1015 txtCantCreateSigImage: "Problems to display signature as image." 1016 } 1017 }; 1018 const settings = jQuery.extend(true, {}, defaults, options); 1019 // store form information with the form object in javascript 1020 const visform = $.data(this[0], "visform"); 1021 if ((!visform) || typeof visform === "undefined") { 1022 $.data( this[0], "visform", options.visform ); 1023 $.data( this[0], "visformsettings", settings ); 1024 } 1025 // store user input information with the form object in javascript 1026 const userinputs = $.data(this[0], "userinputs"); 1027 if ((!userinputs) || typeof userinputs === "undefined") { 1028 if (!options.visform.useSession && options.userInputs) { 1029 $.data( this[0], "userinputs", options.userInputs ); 1030 } 1031 else { 1032 this.setUserInputs(options.visform); 1033 } 1034 } 1035 1036 jQuery("#" + options.visform.parentFormId + "_processform").addClass('vishidden'); 1037 this.initFields(options.visform); 1038 jQuery(".conditional").on("checkConditionalState", { 1039 restricts: options.restrictData, 1040 userInputs: options.userInputs 1041 }, function (e) { 1042 jQuery(this).toggleDisplay(e.data); 1043 }); 1044 // uikit 1045 jQuery(this).closest("form").on("shown", function () { 1046 jQuery(this).trigger('reloadVfMap'); 1047 jQuery(this).children("fieldset.active").trigger('setFocusOnClassChange'); 1048 }); 1049 // Bootstrap 5 1050 jQuery(this).closest("form").on("shown.bs.collapse", function () { 1051 jQuery(this).trigger('reloadVfMap'); 1052 jQuery(this).children("fieldset.active").trigger('setFocusOnClassChange'); 1053 }); 1054 jQuery("#" + options.visform.parentFormId).trigger('visformsInitialised'); 1055 this.focusInvalidForm(options.visform); 1056 }, 1057 initFields : function (visform) { 1058 const userinputs = $.data($("#" + visform.parentFormId).get(0), 'userinputs'); 1059 if ((!userinputs) || typeof userinputs === "undefined") { 1060 return; 1061 } 1062 jQuery.each(userinputs, function (i, obj) { 1063 if (obj.value === "undefined") { 1064 return; 1065 } 1066 if (obj.isDisabled === true && obj.isForbidden !== true) { 1067 // these fields stay with there configuration default 1068 return; 1069 } 1070 // HTML Controls are set with attributes value, selected and checked according to the settings in the field configuration 1071 // This ensures that the js properties defaultValue, defaultSelected and defaultChecked are true the default values 1072 // So that we can use these js properties to reset disabled fields 1073 // And so that 'reset'-Button will have the expected result (even if we are in a redisplay task after php validation of user inputs failed) 1074 // if we have a Redisplay Task (php validation of user inputs failed) we want to set the valid user inputs, which were already made, in the form 1075 // set to user input values 1076 // if a field is readonly the configuration default and the user input are the same (except, when the field value was set with an url param) 1077 switch (obj.type) { 1078 case "select": 1079 case "selectsql": 1080 if (jQuery.isPlainObject(obj.value)) { 1081 jQuery(this).setSelectedOptions(obj); 1082 } 1083 break;
1084 case "multicheckbox": 1085 case "multicheckboxsql": 1086 if (jQuery.isPlainObject(obj.value)) { 1087 const boxes = jQuery("#" + visform.parentFormId + " ." + obj.label).find('input'); 1088 jQuery.each(boxes, function (i, el) { 1089 jQuery.each(obj.value, function (ix, val) { 1090 if (jQuery(el).attr('value') === val) { 1091 jQuery(el).prop('checked', true); 1092 // you have to return false to break from an each loop 1093 return false; 1094 } 1095 jQuery(el).prop('checked', false); 1096 return; 1097 }); 1098 }); 1099 } 1100 break; 1101 case "radio": 1102 case "radiosql": 1103 const radios = jQuery("#" + visform.parentFormId + " ." + obj.label).find('input'); 1104 jQuery.each(radios, function (i, el) { 1105 if (jQuery(el).attr('value') === obj.value) { 1106 jQuery(el).prop('checked', true); 1107 } 1108 else { 1109 jQuery(el).prop('checked', false); 1110 } 1111 }); 1112 break; 1113 case "checkbox": 1114 jQuery("#" + obj.label).prop("checked", obj.value); 1115 jQuery("#" + obj.label).trigger("recalculate"); 1116 break; 1117 case "signature" : 1118 jQuery("#" + obj.label).val(obj.value); 1119 if (obj.value) { 1120 if (obj.value.startsWith('image/jsignature;base30')) { 1121 jQuery("#" + obj.label + "_sig").jSignature("setData", "data:" + obj.value); 1122 } 1123 else { 1124 // signaturePad can only be populated by using an event handler, which is implemented on initialisation 1125 } 1126 } 1127 break; 1128 case "date" : 1129 // Calendar field needs special treatment 1130 // all Calender fields are accessible through JoomlaCalendar.getCalObject(element)._joomlaCalendar; 1131 // where element is the HTML Input field 1132 // the _joomlaCalendar is attached to the parent ('.field-calender') element and can be called directly 1133 // if there is no _joomlaCalendar for a field, this getCalObject() returns false 1134 // change the data-alt-value 1135 // call checkInputs() on the _joomlaCalendar to properly set the value in a date field 1136 jQuery("#" + obj.label).attr('value', obj.value); 1137 jQuery("#" + obj.label).attr('data-alt-value', obj.value); 1138 // calObj = jQuery("#" + obj.label).closest('.field-calendar')[0]._joomlaCalendar; 1139 let calObj = JoomlaCalendar.getCalObject(jQuery("#" + obj.label)[0]); 1140 if (calObj) { 1141 let calendar = calObj._joomlaCalendar; 1142 calendar.checkInputs(); 1143 } 1144 jQuery("#" + obj.label).trigger("recalculate"); 1145 break; 1146 case "textarea" : 1147 // set value in textarea 1148 let text = obj.value.replace(/@/g, '@'); 1149 jQuery("#" + obj.label).val(text); 1150 // console.log('textarea ' + obj.label + ': content set = ' + text); 1151 break; 1152 default: 1153 // replace used to prevent email cloaking in form used in content (plg or module) 1154 if (typeof obj.value.replace !== 'undefined') { 1155 jQuery("#" + obj.label).val(obj.value.replace(/@/g, '@')); 1156 jQuery("#" + obj.label).trigger("recalculate"); 1157 } 1158 break; 1159 } 1160 }); 1161 // ToDo consider if yo want to do this 1162 // enable the buttons only if there is no javascript error on the page 1163 jQuery("#" + visform.parentFormId + ' input[type="submit"]').prop('disabled', false); 1164 jQuery("#" + visform.parentFormId + ' input[type="image"]').prop('disabled', false); 1165 jQuery("#" + visform.parentFormId + ' input[type="reset"]').prop('disabled', false); 1166 // Select a solitary option in selectSQL Field, if option is enabled 1167 jQuery("#" + visform.parentFormId + ' .preSelectedSolitaryOption').each( function (i, el) { 1168 visForm.preSelectSolitaryOption(jQuery(el).attr('id')) 1169 }); 1170 // necessary in edit view 1171 jQuery("#" + visform.parentFormId + ' input[type="radio"]').trigger('reloadsqloptions'); 1172 jQuery("#" + visform.parentFormId + ' input[type="checkbox"]').trigger('reloadsqloptions'); 1173 jQuery("#" + visform.parentFormId + ' select').trigger('reloadsqloptions'); 1174 jQuery("#" + visform.parentFormId + ' input[type="text"]').trigger('reloadsqloptions'); 1175 jQuery("#" + visform.parentFormId + ' input[type="email"]').trigger('reloadsqloptions'); 1176 jQuery("#" + visform.parentFormId + ' input[type="date"]').trigger('reloadsqloptions'); 1177 jQuery("#" + visform.parentFormId + ' input[type="url"]').trigger('reloadsqloptions'); 1178 jQuery("#" + visform.parentFormId + ' input[type="number"]').trigger('reloadsqloptions'); 1179 jQuery("#" + visform.parentFormId + ' input[type="hidden"]').trigger('reloadsqloptions'); 1180 jQuery("#" + visform.parentFormId + ' input[type="tel"]').trigger('reloadsqloptions'); 1181 jQuery("#" + visform.parentFormId + ' input[type="password"]').trigger('reloadsqloptions'); 1182 visForm.hideSqlOptionList(visform.parentFormId); 1183 visForm.hideSqlDataList(visform.parentFormId); 1184 jQuery("#" + visform.parentFormId).trigger('visfieldInitialized'); 1185 // initialize tinymce editor if present and function is defined: must be done after the text was set to the textarea 1186 if (typeof tinyMCE !== 'undefined' && typeof initTinyMCE
1186=== 'function') { 1187 initTinyMCE(); 1188 } 1189 // pseudo readonly for non mutable html controls 1190 // native JS implementation using data-Attribute 1191 let pseudoReadonlyElments = document.querySelectorAll("#" + visform.parentFormId + ' [data-readonly="1"]'); 1192 pseudoReadonlyElments.forEach(readonlyElement => { 1193 readonlyElement.style.pointerEvents = 'none'; 1194 if (readonlyElement.type === "checkbox" || readonlyElement.type === "radio") { 1195 readonlyElement.addEventListener('click', e => { 1196 e.preventDefault(); 1197 return false; 1198 }); 1199 return true; 1200 } 1201 if (readonlyElement.nodeName === "SELECT") { 1202 readonlyElement.addEventListener('change', e => { 1203 jQuery.each(userinputs, function (i, obj) { 1204 if (obj.label === readonlyElement.id) { 1205 if (obj.value === "undefined") { 1206 return; 1207 } 1208 if (obj.isDisabled === true && obj.isForbidden !== true) { 1209 // these fields stay with there configuration default 1210 return; 1211 } 1212 jQuery(this).setSelectedOptions(obj); 1213 } 1214 }) 1215 jQuery('#'+readonlyElement.id).trigger("change"); 1216 }); 1217 return true; 1218 } 1219 }); 1220 }, 1221 setSelectedOptions : function (obj) { 1222 if (jQuery.isPlainObject(obj.value)) { 1223 const seloptions = jQuery("#" + obj.label).find('option'); 1224 const isSearchSelect = $("#" + obj.label).hasClass('select2-hidden-accessible'); 1225 jQuery.each(seloptions, function (i, el) { 1226 jQuery.each(obj.value, function (i, val) { 1227 if (jQuery(el).attr('value') === val) { 1228 jQuery(el).prop('selected', true); 1229 if (isSearchSelect) { 1230 $('#select2-' + obj.label + '-container').html($(el).html()); 1231 $("#" + obj.label).trigger('change'); 1232 } 1233 // you have to return false to break from an each loop 1234 return false; 1235 } 1236 jQuery(el).prop('selected', false); 1237 return; 1238 }); 1239 }); 1240 // preselect solitary option and hide option list according to field configuration 1241 if (obj.type === "selectsql") { 1242 visForm.preSelectSolitaryOption(obj.label.replace('field','')); 1243 } 1244 if (obj.type === "select") { 1245 jQuery("#" + obj.label).trigger("recalculate"); 1246 } 1247 } 1248 }, 1249 focusInvalidForm : function (visform) { 1250 if (jQuery('#' + visform.parentFormId).parent('.visforms.php-error')) { 1251 // php error 1252 // focus message error message block 1253 let errorContainer = jQuery('#' + visform.parentFormId).parent('.visforms.php-error').find('#form-error-container'); 1254 if (errorContainer.length) { 1255 visForm.focusjQueryElementTop(errorContainer); 1256 } 1257 } 1258 }, 1259 setUserInputs: function (visform) { 1260 const formElement = jQuery('#' + visform.parentFormId) 1261 const formData = jQuery(formElement).serializeArray(); 1262 let baseUrl = visform.baseUrl; 1263 jQuery.ajax({ 1264 type: 'POST', 1265 url: baseUrl + '/index.php?option=com_visforms&task=visforms.getUserInputs&id=' + visform.fid, 1266 data: formData, 1267 success: function(data, textStatus, jqXHR) { 1268 $.data($("#" + visform.parentFormId).get(0), "userinputs", data) 1269 }, 1270 error: function(jqXHR, textStatus, errorThrown) { 1271 // give error feedback 1272 visForm.showAjaxError(jqXHR.responseText, errorThrown); 1273 }, 1274 dataType: 'json', 1275 async: false 1276 }); 1277 } 1278 }); 1279}(jQuery)); 1280 1281jQuery(document).ready(function () { 1282 // "static" scripts which should only be included once 1283 1284 // fix placeholder for IE7, IE8, IE9 1285 if (!jQuery.support.placeholder) { 1286 jQuery("[placeholder]").focus(function () { 1287 if (jQuery(this).val() == jQuery(this).attr("placeholder")) jQuery(this).val(""); 1288 }).blur(function () { 1289 if (jQuery(this).val() == "") jQuery(this).val(jQuery(this).attr("placeholder")); 1290 }).blur(); 1291 1292 jQuery("[placeholder]").parents("form").submit(function () { 1293 jQuery(this).find('[placeholder]').each(function () { 1294 if (jQuery(this).val() == jQuery(this).attr("placeholder")) { 1295 jQuery(this).val(""); 1296 } 1297 }); 1298 }); 1299 } 1300 1301 // keyup event triggers validation if element is marked as invali
1301d 1302 jQuery("input[type='number']").on("input mouseup", function () { 1303 jQuery(this).trigger("keyup"); 1304 }); 1305 jQuery("input[type='file']").on("change", function () { 1306 jQuery(this).trigger("keyup").trigger("focus"); 1307 }) 1308 jQuery("input[type='file']").on("blur", function () { 1309 if (!(jQuery(this).get(0).files.length == 0)) { 1310 const id = jQuery(this).attr('id'); 1311 jQuery('[data-clear-target="'+id+'"]').removeClass("vishidden").addClass("active"); 1312 } 1313 }); 1314 jQuery("a.clear-selection").addClass('vishidden'); 1315 jQuery("a.clear-selection").on("click", function(e) { 1316 const uploadid = jQuery(this).attr('data-clear-target'); 1317 let el = jQuery('#' + uploadid); 1318 el.replaceWith(el.val('').clone(true)); 1319 // get the new jQuery object of el 1320 el = jQuery('#' + uploadid); 1321 el.trigger('keyup'); 1322 jQuery(this).removeClass("active").addClass('vishidden'); 1323 e.preventDefault(); 1324 return false; 1325 }); 1326 const successMessageClose = jQuery(".visforms-form joomla-alert[type=\"success\"] .joomla-alert--close"); 1327 if (successMessageClose) { 1328 const form = successMessageClose.parents(".visforms-form").find("form"); 1329 const description = successMessageClose.parents(".visforms-form").find(".category-desc"); 1330 form.addClass('vishidden'); 1331 description.addClass('vishidden'); 1332 successMessageClose.on("click", function() { 1333 jQuery(this).parents(".visforms-form").find("form").removeClass('vishidden'); 1334 jQuery(this).parents(".visforms-form").find(".category-desc").removeClass('vishidden'); 1335 jQuery(this).parents(".visforms-form").find("form").trigger("reloadVfMap"); 1336 }); 1337 } 1338 1339 // multi step forms 1340 jQuery('.visform .next_btn').on('click', function (){ 1341 jQuery(this).closest('[class^="fieldset-"]').find('input, textarea, select').not(":disabled").removeClass("ignore"); 1342 jQuery(this).closest('[class^="fieldset-"]').siblings('[class^="fieldset-"]').find('input, textarea, select').not(":disabled, .btn, .uk-button").addClass("ignore"); 1343 visForm.onNextButtonClick(this); 1344 }); 1345 1346 jQuery('.visform .summary_btn').on('click', function (){ 1347 jQuery(this).closest('[class^="fieldset-"]').find('input, textarea, select').not(":disabled").removeClass("ignore"); 1348 jQuery(this).closest('[class^="fieldset-"]').siblings('[class^="fieldset-"]').find('input, textarea, select').not(":disabled, .btn, .uk-button").addClass("ignore"); 1349 visForm.onSummaryButtonClick(this) 1350 }); 1351 1352 jQuery(".visform .back_btn").on('click', function () { 1353 const visform = jQuery(this).closest('form'); 1354 jQuery(this).closest("[class^='fieldset-']").prev().removeClass("vishidden").addClass('active'); 1355 jQuery(this).closest("[class^='fieldset-']").prev().trigger("reloadVfMap"); 1356 jQuery(this).closest("[class^='fieldset-']").addClass("vishidden").removeClass('active'); 1357 // trigger after active was removed from current fieldset 1358 jQuery(this).closest("[class^='fieldset-']").prev().trigger('setFocusOnClassChange',["back"]); 1359 const activeBadge = jQuery(visform).find('.visprogress .badge.badge-important'); 1360 jQuery(activeBadge).removeClass("badge-important"); 1361 jQuery(activeBadge).closest('.stepCont').prev().find('.badge').removeClass("badge-success").addClass("badge-important"); 1362 }); 1363 1364 jQuery(".visform .fieldset-1 :reset").on('click', function (e) { 1365 e.preventDefault(); 1366 jQuery(this).closest('form').get(0).reset(); 1367 // set userinputs of this form to empty array; We can do this, because the edit views do not have a reset button. 1368 // So the stored user inputs which are in the userinputs array and which must not get lost, are not deleted by this call 1369 jQuery.data(jQuery((this).closest('form')).get(0), 'userinputs', []); 1370 jQuery(".conditional").trigger("checkConditionalState"); 1371 // Trigger change event is needed to recalculate 1372 jQuery(this).closest('form').find('input').trigger("change"); 1373 jQuery(this).closest('form').find('select').trigger("change"); 1374 }); 1375 1376 // action for reset button on summary page 1377 jQuery(".visform fieldset:not(.fieldset-1) :reset").on('click', function (e) { 1378 e.preventDefault(); 1379 jQuery(this).closest('form').get(0).reset(); 1380 // set userinputs of this form to empty array;
1380 We can do this, because the edit views do not have a reset button. 1381 // So the stored user inputs which are in the userinputs array and which must not get lost, are not deleted by this call 1382 jQuery.data(jQuery((this).closest('form')).get(0), 'userinputs', []); 1383 jQuery(".conditional").trigger("checkConditionalState"); 1384 // Trigger change event is needed to recalculate 1385 jQuery(this).closest('form').find('input').trigger("change"); 1386 jQuery(this).closest('form').find('select').trigger("change"); 1387 const formid = jQuery(this).closest("form").get(0).id; 1388 jQuery("#" + formid + "_summary").remove(); 1389 jQuery(this).closest("[class^='fieldset-']").parent().find("[class^='fieldset-']").first().removeClass("vishidden").addClass('active'); 1390 jQuery(this).closest("[class^='fieldset-']").parent().find("[class^='fieldset-']").first().trigger("reloadVfMap"); 1391 jQuery(this).closest("[class^='fieldset-']").addClass("vishidden").removeClass('active'); 1392 jQuery("#" + formid + " .visprogress .stepCont .badge").removeClass("badge-important badge-success"); 1393 jQuery("#" + formid + " .visprogress .stepCont:first .badge").addClass("badge-important"); 1394 }); 1395 1396 // correct button on summary page 1397 jQuery(".visform .correct_btn").on('click',function () { 1398 const formid = jQuery(this).closest("form").get(0).id; 1399 jQuery("#" + formid + "_summary").remove(); 1400 jQuery(this).closest("[class^='fieldset-']").prev().removeClass("vishidden").addClass('active'); 1401 jQuery(this).closest("[class^='fieldset-']").prev().trigger("reloadVfMap"); 1402 jQuery(this).closest("[class^='fieldset-']").addClass("vishidden").removeClass('active'); 1403 jQuery(this).closest("[class^='fieldset-']").prev().trigger('setFocusOnClassChange',["back"]); 1404 jQuery("#" + formid + " .visprogress .stepCont:last .badge").removeClass("badge-important"); 1405 jQuery("#" + formid + " .visprogress .stepCont:nth-last-child(2) .badge").removeClass("badge-success").addClass("badge-important"); 1406 }); 1407 1408 jQuery(".noEnterSubmit").keypress(function (event) { 1409 const key = event.keyCode; 1410 if (key === 13) { 1411 event.preventDefault(); 1412 event.stopPropagation(); 1413 return false; 1414 } 1415 }); 1416 1417 jQuery("fieldset.mpfocustop, fieldset.mpfocusbottom").on('setFocusOnClassChange', function (e, direction) { 1418 // direction not used yet, could be used to have different focus settings on backwards and forwards 1419 let fieldset = e.target; 1420 if (fieldset) { 1421 fieldset = jQuery(fieldset); 1422 let windowHeight = jQuery(window).height(); 1423 // top of fieldset relative to document 1424 let elOffset = fieldset.offset().top; 1425 // height of fieldset 1426 let elHeight = fieldset.prop("scrollHeight");// fieldset.height(); 1427 let focusTarget = (jQuery(fieldset).hasClass('mpfocusbottom')) ? 'bottom' : 'top'; 1428 let offset; 1429 if (focusTarget === 'top') { 1430 offset = elOffset; 1431 } 1432 else { 1433 offset = elOffset + elHeight - (windowHeight / 4); 1434 } 1435 let speed = 700; 1436 jQuery('html, body').animate({scrollTop: offset}, speed); 1437 } 1438 }); 1439 setupVfFormViewTasks({ 1440 target: document 1441 }); 1442 1443 jQuery(document).displayChanger(); 1444 1445}); 1446 1447const setupVfFormViewTasks = ({target}) => { 1448 target.querySelectorAll('.verifyMailBtn').forEach( 1449 element => element.addEventListener('click', visForm.verifyMail) 1450 ); 1451 target.querySelectorAll('.viscalendar-input').forEach( 1452 element => element.addEventListener('click', visForm.validateDateOnUpdate) 1453 ); 1454};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.