1"use strict"; 2 3// disable client side validation introduced in CF7 5.6 for now 4if (typeof wpcf7 !== 'undefined') { 5 wpcf7.validate = (a,b) => null; 6} 7 8let cf7signature_resized = 0; // for compatibility with contact-form-7-signature-addon 9 10let wpcf7cf_timeout; 11let wpcf7cf_change_time_ms = 100; // the timeout after a change in the form is detected 12 13if (window.wpcf7 && !wpcf7.setStatus) { 14 wpcf7.setStatus = ( form, status ) => { 15 form = form.length ? form[0] : form; // if form is a jQuery object, only grab te html-element 16 const defaultStatuses = new Map( [ 17 // 0: Status in API response, 1: Status in HTML class 18 [ 'init', 'init' ], 19 [ 'validation_failed', 'invalid' ], 20 [ 'acceptance_missing', 'unaccepted' ], 21 [ 'spam', 'spam' ], 22 [ 'aborted', 'aborted' ], 23 [ 'mail_sent', 'sent' ], 24 [ 'mail_failed', 'failed' ], 25 [ 'submitting', 'submitting' ], 26 [ 'resetting', 'resetting' ], 27 ] ); 28 29 if ( defaultStatuses.has( status ) ) { 30 status = defaultStatuses.get( status ); 31 } 32 33 if ( ! Array.from( defaultStatuses.values() ).includes( status ) ) { 34 status = status.replace( /[^0-9a-z]+/i, ' ' ).trim(); 35 status = status.replace( /\s+/, '-' ); 36 status = `custom-${ status }`; 37 } 38 39 const prevStatus = form.getAttribute( 'data-status' ); 40 41 form.wpcf7.status = status; 42 form.setAttribute( 'data-status', status ); 43 form.classList.add( status ); 44 45 if ( prevStatus && prevStatus !== status ) { 46 form.classList.remove( prevStatus ); 47 } 48 49 return status; 50 }; 51} 52 53if (window.wpcf7cf_running_tests) { 54 jQuery('input[name="_wpcf7cf_options"]').each(function(e) { 55 const $input = jQuery(this); 56 const opt = JSON.parse($input.val()); 57 opt.settings.animation_intime = 0; 58 opt.settings.animation_outtime = 0; 59 $input.val(JSON.stringify(opt)); 60 }); 61 wpcf7cf_change_time_ms = 0; 62} 63 64const wpcf7cf_show_animation = { "height": "show", "marginTop": "show", "marginBottom": "show", "paddingTop": "show", "paddingBottom": "show" }; 65const wpcf7cf_hide_animation = { "height": "hide", "marginTop": "hide", "marginBottom": "hide", "paddingTop": "hide", "paddingBottom": "hide" }; 66 67const wpcf7cf_show_step_animation = { "opacity": "show" }; 68const wpcf7cf_hide_step_animation = { "opacity": "hide" }; 69 70const wpcf7cf_change_events = 'input.wpcf7cf paste.wpcf7cf change.wpcf7cf click.wpcf7cf propertychange.wpcf7cf changedisabledprop.wpcf7cf'; 71 72const wpcf7cf_forms = []; 73 74const Wpcf7cfForm = function($form) { 75 76 const options_element = $form.find('input[name="_wpcf7cf_options"]').eq(0); 77 if (!options_element.length || !options_element.val()) { 78 // doesn't look like a CF7 form created with conditional fields plugin enabled. 79 return false; 80 } 81 82 const form = this; 83 84 const form_options = JSON.parse(options_element.val()); 85 86 form.$form = $form; 87 form.$input_hidden_group_fields = $form.find('[name="_wpcf7cf_hidden_group_fields"]'); 88 form.$input_hidden_groups = $form.find('[name="_wpcf7cf_hidden_groups"]'); 89 form.$input_visible_groups = $form.find('[name="_wpcf7cf_visible_groups"]'); 90 form.$input_repeaters = $form.find('[name="_wpcf7cf_repeaters"]'); 91 form.$input_steps = $form.find('[name="_wpcf7cf_steps"]'); 92 93 form.unit_tag = $form.closest('.wpcf7').attr('id'); 94 form.conditions = form_options['conditions']; 95 96 form.simpleDom = null; 97 98 form.reloadSimpleDom = function() { 99 form.simpleDom = wpcf7cf.get_simplified_dom_model(form.$form[0]); 100 } 101 102 // quicker than reloading the simpleDom completely with reloadSimpleDom 103 form.updateSimpleDom = function() { 104 if (!form.simpleDom) { 105 form.reloadSimpleDom(); 106 } 107 const inputs = Object.values(form.simpleDom).filter(item => item.type === 'input'); 108 const formdata = new FormData(form.$form[0]); 109
110 let formdataEntries = [... formdata.entries()].map(entry => [ entry[0], entry[1].name ?? entry[1] ]); 111 const buttonEntries = [ ... jQuery('button', form.$form) ].map(entry => [entry.name, entry.value]); 112 formdataEntries = formdataEntries.concat(buttonEntries); 113 114 inputs.forEach(simpleDomItem => { 115 const newValue = form.getNewDomValueIfChanged(simpleDomItem, formdataEntries); 116 if (newValue !== null) { 117 form.simpleDom[simpleDomItem.name].val = newValue; 118 } 119 }); 120 121 } 122 123 form.isDomMatch = function(simpleDomItem, formDataEntries) { 124 const simpleDomItemName = simpleDomItem.name; 125 const simpleDomItemValues = simpleDomItem.val; 126 const currentValues = formDataEntries.filter(entry => entry[0] === simpleDomItemName).map(entry => entry[1]); 127 return currentValues.join('|') === simpleDomItemValues.join('|'); 128 } 129 130 /** 131 * 132 * @param {*} simpleDomItem 133 * @param {*} formDataEntries 134 * @returns the new value, or NULL if no change 135 */ 136 form.getNewDomValueIfChanged = function(simpleDomItem, formDataEntries) { 137 const simpleDomItemName = simpleDomItem.name; 138 const simpleDomItemValues = simpleDomItem.val; 139 const currentValues = formDataEntries.filter(entry => entry[0] === simpleDomItemName).map(entry => entry[1]); 140 return currentValues.join('|') === simpleDomItemValues.join('|') ? null : currentValues; 141 } 142 143 // Wrapper around jQuery(selector, form.$form) 144 form.get = function (selector) { 145 // TODO: implement some caching here. 146 return jQuery(selector, form.$form); 147 } 148 149 form.getFieldByName = function(name) { 150 return form.simpleDom[name] || form.simpleDom[name+'[]']; 151 } 152 153 // compatibility with conditional forms created with older versions of the plugin ( < 1.4 ) 154 for (let i=0; i < form.conditions.length; i++) { 155 const condition = form.conditions[i]; 156 if (!('and_rules' in condition)) { 157 condition.and_rules = [{'if_field':condition.if_field,'if_value':condition.if_value,'operator':condition.operator}]; 158 } 159 } 160 161 form.initial_conditions = form.conditions; 162 form.settings = form_options['settings']; 163 164 form.$groups = jQuery(); // empty jQuery set 165 form.repeaters = []; 166 form.multistep = null; 167 form.fields = []; 168 169 form.settings.animation_intime = parseInt(form.settings.animation_intime); 170 form.settings.animation_outtime = parseInt(form.settings.animation_outtime); 171 172 if (form.settings.animation === 'no') { 173 form.settings.animation_intime = 0; 174 form.settings.animation_outtime = 0; 175 } 176 177 form.updateGroups(); 178 form.updateEventListeners(); 179 form.displayFields(); 180 181 // bring form in initial state if the reset event is fired on it. 182 // (CF7 triggers the 'reset' event by default on each successfully submitted form) 183 form.$form.on('reset.wpcf7cf', form, function(e) { 184 const form = e.data; 185 setTimeout(function(){ 186 form.reloadSimpleDom(); 187 form.displayFields(); 188 form.resetRepeaters(); 189 if (form.multistep != null) { 190 form.multistep.moveToStep(1, false); 191 } 192 setTimeout(function(){ 193 if (form.$form.hasClass('sent')) { 194 jQuery('.wpcf7-response-output', form.$form)[0].scrollIntoView({behavior: "smooth", block:"nearest", inline:"nearest"}); 195 } 196 }, 400); 197 },200); 198 }); 199 200 // PRO ONLY 201 202 form.get('.wpcf7cf_repeater:not(.wpcf7cf_repeater .wpcf7cf_repeater)').each(function(){ 203 form.repeaters.push(new Wpcf7cfRepeater(jQuery(this),form)); 204 }); 205 206 form.$input_repeaters.val(JSON.stringify(form.repeaters.map((item)=>item.params.$repeater.id))); 207 208 const $multistep = form.get('.wpcf7cf_multistep'); 209 210 if ($multistep.length) { 211 form.multistep = new Wpcf7cfMultistep($multistep, form); 212 // window.wpcf7cf.updateMultistepState(form.multistep); 213 } 214 215 // END PRO ONLY 216 217} 218 219/** 220 * reset initial number of subs for each repeater. 221 * (does not clear values) 222 */ 223Wpcf7cfForm.prototype.resetRepeaters = function() { 224 const form = this;
225 form.repeaters.forEach(repeater => { 226 repeater.updateSubs( repeater.params.$repeater.initial_subs ); 227 }); 228} 229 230Wpcf7cfForm.prototype.displayFields = function() { 231 232 const form = this; 233 234 const wpcf7cf_conditions = this.conditions; 235 const wpcf7cf_settings = this.settings; 236 237 //for compatibility with contact-form-7-signature-addon 238 if (cf7signature_resized === 0 && typeof signatures !== 'undefined' && signatures.constructor === Array && signatures.length > 0 ) { 239 for (let i = 0; i < signatures.length; i++) { 240 if (signatures[i].canvas.width === 0) { 241 242 const $sig_canvas = jQuery(".wpcf7-form-control-signature-body>canvas"); 243 const $sig_wrap = jQuery(".wpcf7-form-control-signature-wrap"); 244 $sig_canvas.eq(i).attr('width', $sig_wrap.width()); 245 $sig_canvas.eq(i).attr('height', $sig_wrap.height()); 246 247 cf7signature_resized = 1; 248 } 249 } 250 } 251 252 form.$groups.addClass('wpcf7cf-hidden'); 253 254 for (let i=0; i < wpcf7cf_conditions.length; i++) { 255 256 const condition = wpcf7cf_conditions[i]; 257 258 const show_group = window.wpcf7cf.should_group_be_shown(condition, form); 259 260 if (show_group) { 261 form.get('[data-id="'+condition.then_field+'"]').removeClass('wpcf7cf-hidden'); 262 } 263 } 264 265 266 const animation_intime = wpcf7cf_settings.animation_intime; 267 const animation_outtime = wpcf7cf_settings.animation_outtime; 268 269 form.$groups.each(function (index) { 270 const $group = jQuery(this); 271 if ($group.is(':animated')) { 272 $group.finish(); // stop any current animations on the group 273 } 274 if ($group.css('display') === 'none' && !$group.hasClass('wpcf7cf-hidden')) { 275 if ($group.prop('tagName') === 'SPAN') { 276 $group.show().trigger('wpcf7cf_show_group'); // show instantly 277 } else { 278 $group.animate(wpcf7cf_show_animation, animation_intime).trigger('wpcf7cf_show_group'); // show with animation 279 } 280 281 if($group.attr('data-disable_on_hide') !== undefined) { 282 $group.find(':input').prop('disabled', false).trigger('changedisabledprop.wpcf7cf'); 283 $group.find('.wpcf7-form-control-wrap').removeClass('wpcf7cf-disabled'); 284 } 285 286 } else if ($group.css('display') !== 'none' && $group.hasClass('wpcf7cf-hidden')) { 287 288 if ($group.attr('data-clear_on_hide') !== undefined) { 289 const $inputs = jQuery(':input', $group).not(':button, :submit, :reset, :hidden'); 290 291 $inputs.each(function(){ 292 const $this = jQuery(this); 293 $this.val(this.defaultValue); 294 $this.prop('checked', this.defaultChecked); 295 }); 296 297 jQuery('option', $group).each(function() { 298 this.selected = this.defaultSelected; 299 }); 300 301 jQuery('select', $group).each(function() { 302 const $select = jQuery(this); 303 if ($select.val() === null) { 304 $select.val(jQuery("option:first",$select).val()); 305 } 306 }); 307 308 $inputs.each(function(){this.dispatchEvent(new Event("change",{"bubbles":true}))}); 309 } 310 311 if ($group.prop('tagName') === 'SPAN') { 312 $group.hide().trigger('wpcf7cf_hide_group'); 313 } else { 314 $group.animate(wpcf7cf_hide_animation, animation_outtime).trigger('wpcf7cf_hide_group'); // hide 315 } 316 } 317 }); 318 319 form.updateHiddenFields(); 320 form.updateSummaryFields(); 321}; 322 323Wpcf7cfForm.prototype.updateSummaryFields = function() { 324 const form = this; 325 const $summary = form.get('.wpcf7cf-summary'); 326 327 if ($summary.length == 0 || !$summary.is(':visible')) { 328 return; 329 } 330 331 const fd = new FormData(); 332 333 const formdata = form.$form.serializeArray(); 334 jQuery.each(formdata,function(key, input){ 335 fd.append(input.name, input.value); 336 }); 337 338 // Make sure to add file fields to FormData 339 jQuery.each(form.$form.find('input[type="file"]'), function(index, el) { 340 if (! el.files.length) return true; // continue 341 const fieldName = el.name; 342 fd.append(fieldName, new Blob() , Array.from(el.files).map(file => file.name).join(', ')); 343 }); 344 345 // add file fields to form-data 346 347 jQuery.ajax({ 348 url: wpcf7cf_global_settings.ajaxurl + '?action=wpcf7cf_get_summary', 349 type: 'POST', 350 data: fd, 351 processData: false, 352 contentType: false, 353 dataType: 'json', 354 success: function(json) { 355 $summary.html(json.summaryHtml); 356 } 357 }); 358}; 359 360Wpcf7cfForm.prototype.updateHiddenFields = function() { 361 362 const form = this; 363 364 const hidden_fields = []; 365 const hidden_groups = []; 366 const visible_groups = []; 367 368 form.$groups.each(function () { 369 const $group = jQuery(this); 370 if ($group.hasClass('wpcf7cf-hidden')) { 371 hidden_groups.push($group.attr('data-id')); 372 if($group.attr('data-disable_on_hide') !== undefined) { 373 // fields inside hidden disable_on_hide group 374 $group.find('input,select,textarea').each(function(){ 375 const $this = jQuery(this); 376 if (!$this.prop('disabled')) { 377 $this.prop('disabled', true).trigger('changedisabledprop.wpcf7cf'); 378 } 379 380 // if there's no other field with the same name visible in the form 381 // then push this field to hidden_fields 382 if (form.$form.find(`[data-class="wpcf7cf_group"]:not(.wpcf7cf-hidden) [name='${$this.attr('name')}']`).length === 0) { 383 hidden_fields.push($this.attr('name')); 384 } 385 }) 386 $group.find('.wpcf7-form-control-wrap').addClass('wpcf7cf-disabled'); 387 } else { 388 // fields inside regular hidden group are all pushed to hidden_fields 389 $group.find('input,select,textarea').each(function () { 390 hidden_fields.push(jQuery(this).attr('name')); 391 }); 392 } 393 } else { 394 visible_groups.push($group.attr('data-id')); 395 } 396 }); 397 398 form.hidden_fields = hidden_fields; 399 form.hidden_groups = hidden_groups; 400 form.visible_groups = visible_groups; 401 402 form.$input_hidden_group_fields.val(JSON.stringify(hidden_fields)); 403 form.$input_hidden_groups.val(JSON.stringify(hidden_groups)); 404 form.$input_visible_groups.val(JSON.stringify(visible_groups)); 405 406 return true; 407}; 408Wpcf7cfForm.prototype.updateGroups = function() { 409 const form = this; 410 form.$groups = form.$form.find('[data-class="wpcf7cf_group"]'); 411 form.$groups.height('auto'); 412 form.conditions = window.wpcf7cf.get_nested_conditions(form); 413 414}; 415Wpcf7cfForm.prototype.updateEventListeners = function() { 416 417 const form = this; 418 419 // monitor input changes, and call displayFields() if something has changed 420 form.get('input, select, textarea, button').not('.wpcf7cf_add, .wpcf7cf_remove').off(wpcf7cf_change_events).on(wpcf7cf_change_events,form, function(e) { 421 const form = e.data; 422 clearTimeout(wpcf7cf_timeout); 423 wpcf7cf_timeout = setTimeout(function() { 424 window.wpcf7cf.updateMultistepState(form.multistep); 425 form.updateSimpleDom(); 426 form.displayFields(); 427 }, wpcf7cf_change_time_ms); 428 }); 429 430 // PRO ONLY 431 form.get('.wpcf7cf-togglebutton').off('click.toggle_wpcf7cf').on('click.toggle_wpcf7cf',function() { 432 const $this = jQuery(this); 433 if ($this.text() === $this.attr('data-val-1')) { 434 $this.text($this.attr('data-val-2')); 435 $this.val($this.attr('data-val-2')); 436 } else { 437 $this.text($this.attr('data-val-1')); 438 $this.val($this.attr('data-val-1')); 439 } 440 }); 441 // END PRO ONLY 442}; 443 444// PRO ONLY 445function Wpcf7cfRepeater($repeater, form) { 446 const $ = jQuery; 447 448 let thisRepeater = this; 449 450 const wpcf7cf_settings = form.settings; 451 452 thisRepeater.form = form; 453 454 $repeater.parentRepeaters = Array.from($repeater.parents('.wpcf7cf_repeater').map(function() { 455 return this.getAttribute('data-id'); 456 } )).reverse(); 457 458 $repeater.num_subs = 0; 459 $repeater.id = $repeater.attr('data-id'); 460 $repeater.orig_id = $repeater.attr('data-orig_data_id'); 461 $repeater.min = typeof( $repeater.attr('data-min')) !== 'undefined' ? parseInt($repeater.attr('data-min')) : 1; 462 $repeater.max = typeof( $repeater.attr('data-max')) !== 'undefined' ? parseInt($repeater.attr('data-max')) : 200; 463 $repeater.initial_subs = typeof( $repeater.attr('data-initial')) !== 'undefined' ? parseInt($repeater.attr('data-initial')) : $repeater.min; 464 if ($repeater.initial_subs > $repeater.max) { 465 $repeater.initial_subs = $repeater.max; 466 } 467 const $repeater_sub = $repeater.children('.wpcf7cf_repeater_sub').eq(0); 468 const $repeater_controls = $repeater.children('.wpcf7cf_repeater_controls').eq(0); 469 470 const $repeater_sub_clone = $repeater_sub.clone(); 471 472 $repeater_sub_clone.find('.wpcf7cf_repeater_sub').addBack('.wpcf7cf_repeater_sub').each(function() { 473 const $this = jQuery(this); 474 const prev_suffix = $this.attr('data-repeater_sub_suffix'); 475 const new_suffix = prev_suffix+'__{{repeater_sub_suffix}}'; 476 $this.attr('data-repeater_sub_suffix', new_suffix); 477 }); 478 479 $repeater_sub_clone.find('[name]').each(function() { 480 const $this = jQuery(this); 481 const prev_name = $this.attr('name'); 482 const new_name = thisRepeater.getNewName(prev_name); 483 484 const orig_name = $this.attr('data-orig_name') != null ? $this.attr('data-orig_name') : prev_name; 485 486 $this.attr('name', new_name); 487 $this.attr('data-orig_name', orig_name); 488 $this.closest('.wpcf7-form-control-wrap').attr('data-name', new_name.replace('[]','')); 489 }); 490 491 $repeater_sub_clone.find('.wpcf7cf_repeater,[data-class="wpcf7cf_group"]').each(function() { 492 const $this = jQuery(this); 493 const prev_data_id = $this.attr('data-id'); 494 const orig_data_id = $this.attr('data-orig_data_id') != null ? $this.attr('data-orig_data_id') : prev_data_id; 495 let new_data_id = thisRepeater.getNewName(prev_data_id); 496 497 if(prev_data_id.endsWith('_count')) { 498 new_data_id = prev_data_id.replace('_count','__{{repeater_sub_suffix}}_count'); 499 } 500 501 $this.attr('data-id', new_data_id); 502 $this.attr('data-orig_data_id', orig_data_id); 503 }); 504 505 $repeater_sub_clone.find('[id]').each(function() { 506 const $this = jQuery(this); 507 const prev_id = $this.attr('id'); 508 const orig_id = $this.attr('data-orig_id') != null ? $this.attr('data-orig_id') : prev_id; 509 const new_id = thisRepeater.getNewName(prev_id); 510 511 $this.attr('id', new_id); 512 $this.attr('data-orig_id', orig_id); 513 }); 514 515 $repeater_sub_clone.find('[for]').each(function() { 516 const $this = jQuery(this); 517 const prev_for = $this.attr('for'); 518 const orig_for = $this.attr('data-orig_for') != null ? $this.attr('data-orig_for') : prev_for; 519 const new_for = thisRepeater.getNewName(prev_for); 520 521 $this.attr('for', new_for); 522 $this.attr('data-orig_for', orig_for); 523 }); 524 525 const repeater_sub_html = $repeater_sub_clone[0].outerHTML; 526 527 const $repeater_count_field = $repeater.find('[name='+$repeater.id+'_count]').eq(0); 528 const $button_add = $repeater_controls.find('.wpcf7cf_add').eq(0); 529 const $button_remove = $repeater_controls.find('.wpcf7cf_remove').eq(0); 530 531 const params = { 532 $repeater: $repeater, 533 $repeater_count_field: $repeater_count_field, 534 repeater_sub_html: repeater_sub_html, 535 $repeater_controls: $repeater_controls, 536 $button_add: $button_add, 537 $button_remove: $button_remove, 538 wpcf7cf_settings: wpcf7cf_settings 539 }; 540 541 thisRepeater.params = params; 542 543 $button_add.on('click', null, thisRepeater, function(e) { 544 thisRepeater = e.data; 545 thisRepeater.updateSubs(params.$repeater.num_subs+1); 546 }); 547 548 $button_remove.on('click', null, thisRepeater,function(e) { 549 thisRepeater = e.data; 550 thisRepeater.updateSubs(params.$repeater.num_subs-1); 551 }); 552 553 jQuery('> .wpcf7cf_repeater_sub',params.$repeater).eq(0).remove(); // remove the first sub, it's just a template. 554 555 thisRepeater.updateSubs($repeater.initial_subs); 556 thisRepeater.updateButtons(); 557 558} 559 560Wpcf7cfRepeater.prototype.getNewName = function(previousName) { 561 const prev_parts = previousName.split('['); 562 previousName = prev_parts[0]; 563 const prev_suff = prev_parts.length > 1 ? '['+prev_parts.splice(1).join('[') : ''; 564 let newName = previousName+'__{{repeater_sub_suffix}}'+prev_suff; 565 566 if(previousName.endsWith('_count')) { 567 newName = previousName.replace('_count','__{{repeater_sub_suffix}}_count'); 568 } 569 570 return newName; 571} 572 573Wpcf7cfRepeater.prototype.updateButtons = function() { 574 const repeater = this; 575 const params = repeater.params; 576 const num_subs = params.$repeater.num_subs; 577 578 let showButtonRemove = false;
579 let showButtonAdd = false; 580 581 if (params.$repeater.num_subs < params.$repeater.max) { 582 showButtonAdd = true; 583 } 584 if (params.$repeater.num_subs > params.$repeater.min) { 585 showButtonRemove = true; 586 } 587 588 if (showButtonAdd) { 589 params.$button_add.show(); 590 } else { 591 params.$button_add.hide(); 592 593 } 594 595 if (showButtonRemove) { 596 params.$button_remove.show(); 597 } else { 598 params.$button_remove.hide(); 599 } 600 601 params.$repeater_count_field.val(num_subs); 602} 603 604Wpcf7cfRepeater.prototype.updateSubs = function(subs_to_show) { 605 const repeater = this; 606 const params = repeater.params; 607 608 // make sure subs_to_show is a valid number 609 subs_to_show = subs_to_show < params.$repeater.min ? params.$repeater.min : subs_to_show 610 subs_to_show = subs_to_show > params.$repeater.max ? params.$repeater.max : subs_to_show 611 612 const subs_to_add = subs_to_show - params.$repeater.num_subs; 613 614 if (subs_to_add < 0) { 615 repeater.removeSubs(-subs_to_add); 616 } else if (subs_to_add > 0) { 617 repeater.addSubs(subs_to_add); 618 } 619}; 620/** 621 * add Subs to repeater 622 * @param {Number} subs_to_add 623 * @param {Number} index - zero-based. leave blank (or null) to append at the end 624 */ 625Wpcf7cfRepeater.prototype.addSubs = function(subs_to_add, index=null) { 626 627 const $ = jQuery; 628 const params = this.params; 629 const repeater = this; 630 const form = repeater.form; 631 632 const $repeater = params.$repeater; 633 const $repeater_controls = params.$repeater_controls; 634 635 if (subs_to_add + $repeater.num_subs > $repeater.max) { 636 subs_to_add = $repeater.max - $repeater.num_subs; 637 } 638 639 let html_str = ''; 640 641 for(let i=1; i<=subs_to_add; i++) { 642 const sub_suffix = $repeater.num_subs+i; 643 html_str += params.repeater_sub_html.replace(/\{\{repeater_sub_suffix\}\}
643/g,sub_suffix) 644 .replace(new RegExp('\{\{'+$repeater.orig_id+'_index\}\}','g'),'<span class="wpcf7cf-index wpcf7cf__'+$repeater.orig_id+'">'+sub_suffix+'</span>'); 645 } 646 647 648 const $html = $(html_str); 649 650 $('> .wpcf7cf_repeater_sub',$repeater).finish(); // finish any currently running animations immediately. 651 652 // Add the newly created fields to the form 653 if (index === null) { 654 $html.hide().insertBefore($repeater_controls).animate(wpcf7cf_show_animation, params.wpcf7cf_settings.animation_intime).trigger('wpcf7cf_repeater_added'); 655 } else { 656 $html.hide().insertBefore($('> .wpcf7cf_repeater_sub', $repeater).eq(index)).animate(wpcf7cf_show_animation, params.wpcf7cf_settings.animation_intime).trigger('wpcf7cf_repeater_added'); 657 } 658 659 // enable all new fields 660 $html.find('.wpcf7cf-disabled :input').prop('disabled', false).trigger('changedisabledprop.wpcf7cf'); 661 $html.find('.wpcf7-form-control-wrap').removeClass('wpcf7cf-disabled'); 662 663 $('.wpcf7cf_repeater', $html).each(function(){ 664 form.repeaters.push(new Wpcf7cfRepeater($(this),form)); 665 }); 666 667 form.$input_repeaters.val(JSON.stringify(form.repeaters.map((item)=>item.params.$repeater.id))); 668 669 $repeater.num_subs+= subs_to_add; 670 671 if (index !== null) { 672 repeater.updateSuffixes(); 673 } 674 675 window.wpcf7cf.updateMultistepState(form.multistep); 676 form.updateGroups(); 677 form.updateEventListeners(); 678 form.displayFields(); 679 680 repeater.updateButtons(); 681 682 // Exclusive Checkbox 683 $html.on( 'click', '.wpcf7-exclusive-checkbox input:checkbox', function() { 684 const name = $( this ).attr( 'name' ); 685 $html.find( 'input:checkbox[name="' + name + '"]' ).not( this ).prop( 'checked', false ); 686 } ); 687 688 //basic compatibility with material-design-for-contact-form-7 689 if (typeof window.cf7mdInit === "function") { 690 window.cf7mdInit(); 691 } 692 693 return false; 694}; 695 696Wpcf7cfRepeater.prototype.updateSuffixes = function() { 697 698 // Loop trough all subs 699 // -- 1. update all fields, groups and repeaters names, id's, for's, ... 700 // -- 2. loop trough all repeaters 701 // -- update sub_html template for nested repeater 702 // -- call updateSuffixes() for nested repeater 703 704 const $repeater = this.params.$repeater; 705 const num_subs = this.params.$repeater.num_subs; 706 const form = this.form; 707 const orig_id = $repeater.attr('data-orig_data_id'); 708 const repeater_id = $repeater.attr('data-id'); 709 const repeater_suffix = repeater_id.replace(orig_id,''); 710 711 let simplifiedDomArray = Object.values(wpcf7cf.get_simplified_dom_model(form.$form[0])); 712 713 for (let i = 0; i < num_subs; i++) { 714 715 const $sub = jQuery('> .wpcf7cf_repeater_sub', $repeater).eq(i); 716 717 const newIndex = i+1; 718 const currentSuffix = $sub.attr('data-repeater_sub_suffix'); 719 const newSuffix = repeater_suffix+'__'+newIndex; 720 721 $sub.attr('data-repeater_sub_suffix', newSuffix); // update sub attr 722 $sub.find('.wpcf7cf__'+orig_id).html(newIndex); // update {{r_index}} parts 723 724 simplifiedDomArray.forEach(function(el) { 725 726 if (el.suffix !== currentSuffix) return; 727 728 // TODO: may need an extra check to verify that the element is inside the current repeater 729 // (orig_id) . Otherwise problems may occur if there are repeaters on the same level. 730 731 const newName = el.name.replace(currentSuffix, newSuffix); 732 733 const pureElName = el.name.replace('[]',''); 734 const pureNewName = newName.replace('[]',''); 735 736 jQuery('[name="'+el.name+'"]', $sub).attr('name', newName); 737 jQuery('[id="'+el.name+'"]', $sub).attr('id', newName); 738 jQuery('label[for="'+el.name+'"]', $sub).attr('for', newName); 739 const $nested_repeater = jQuery('[data-id="'+el.name+'"]', $sub); 740 $nested_repeater.attr('data-id', newName); 741 jQuery(`.wpcf7-form-control-wrap[data-name="${pureElName}"]`,$sub).attr('data-name', pureNewName); 742 743 if (el.type === 'repeater') { 744 const nested_repeater = form.repeaters.find( function(repeater) { 745 return repeater.params.$repeater.get(0) === $nested_repeater.get(0); 746 }); 747 748 if (!nested_repeater) return; 749 750 nested_repeater.params.repeater_sub_html = wpcf7cf.updateRepeaterSubHTML( 751 nested_repeater.params.repeater_sub_html, 752 currentSuffix, 753 newSuffix, 754 nested_repeater.params.$repeater.parentRepeaters 755 ); 756 757 nested_repeater.updateSuffixes(); 758 759 } 760 761 }); 762 } 763 764}; 765 766/** 767 * Return the parent repeaters, order is not guaranteed. 768 */ 769Wpcf7cfRepeater.prototype.getParentRepeaters = function() { 770 const simplifiedDomArray = Object.values(wpcf7cf.get_simplified_dom_model(form.$form[0])); 771 form.repeaters.map(repeater => { 772 773 }); 774}; 775 776Wpcf7cfRepeater.prototype.removeSubs = function(subs_to_remove, index=null) { 777 const $ = jQuery; 778 const repeater = this; 779 const params = repeater.params; 780 const form = repeater.form; 781 const $repeater = params.$repeater; 782 783 if ($repeater.num_subs - subs_to_remove < $repeater.min) { 784 subs_to_remove = $repeater.num_subs - $repeater.min; 785 } 786 787 if (index===null) { 788 index = $repeater.num_subs-subs_to_remove; 789 } 790 $repeater.num_subs-= subs_to_remove; 791 792 jQuery('> .wpcf7cf_repeater_sub',$repeater).finish(); // finish any currently running animations immediately. 793 794 jQuery('> .wpcf7cf_repeater_sub',$repeater).slice(index,index+subs_to_remove).animate(wpcf7cf_hide_animation, {duration:params.wpcf7cf_settings.animation_intime, done:function() { 795 const $this = jQuery(this); 796 //remove the actual fields from the form 797 $this.remove(); 798 params.$repeater.trigger('wpcf7cf_repeater_removed'); 799 window.wpcf7cf.updateMultistepState(form.multistep); 800 form.updateGroups(); 801 form.updateEventListeners(); 802 form.displayFields(); 803 804 repeater.updateButtons(); 805 806 if (index !== null) { 807 repeater.updateSuffixes(); 808 } 809 }}); 810 811 return false;
812}; 813 814function Wpcf7cfMultistep($multistep, form) { 815 const multistep = this; 816 multistep.$multistep = $multistep; 817 multistep.form = form; 818 multistep.$steps = $multistep.find('.wpcf7cf_step'); 819 multistep.$btn_next = $multistep.find('.wpcf7cf_next'); 820 multistep.$btn_prev = $multistep.find('.wpcf7cf_prev'); 821 multistep.$dots = $multistep.find('.wpcf7cf_steps-dots'); 822 multistep.currentStep = 0; 823 multistep.numSteps = multistep.$steps.length; 824 825 826 multistep.$dots.html(''); 827 for (let i = 1; i <= multistep.numSteps; i++) { 828 multistep.$dots.append(` 829 <div class="dot" data-step="${i}"> 830 <div class="step-index">${i}</div> 831 <div class="step-title">${multistep.$steps.eq(i-1).attr('data-title')}</div> 832 </div> 833 `); 834 } 835 836 multistep.$btn_next.on('click.wpcf7cf_step', async function() { 837 838 multistep.$btn_next.addClass('disabled').attr('disabled', true); 839 multistep.form.$form.addClass('submitting'); 840 const result = await multistep.validateStep(multistep.currentStep); 841 multistep.form.$form.removeClass('submitting'); 842 843 if (result === 'success') { 844 multistep.moveToStep(multistep.currentStep+1); 845 } 846 847 }); 848 849 // If form is submitted (by pressing Enter for example), and if we are not on the last step, 850 // then trigger click event on the $btn_next button instead. 851 multistep.form.$form.on('submit.wpcf7cf_step', function(e) { 852 853 if (multistep.form.$form.hasClass('submitting')) { // prevent double submit 854 e.stopImmediatePropagation(); 855 return false; 856 } 857 858 if (multistep.currentStep !== multistep.numSteps) { 859 multistep.$btn_next.trigger('click.wpcf7cf_step'); 860 861 e.stopImmediatePropagation(); 862 return false; 863 } 864 }); 865 866 multistep.$btn_prev.on( 'click', function() { 867 multistep.moveToStep(multistep.currentStep-1); 868 }); 869 870 multistep.moveToStep(1); 871} 872 873Wpcf7cfMultistep.prototype.validateStep = function(step_index) { 874 875 const multistep = this; 876 const $multistep = multistep.$multistep; 877 const $form = multistep.form.$form; 878 const form = multistep.form; 879 880 $form.find('.wpcf7-response-output').addClass('wpcf7-display-none'); 881 882 return new Promise(resolve => { 883 884 const fd = new FormData(); 885 886 // Make sure to add file fields to FormData 887 jQuery.each($form.find('[data-id="step-'+step_index+'"] input[type="file"]'), function(index, el) { 888 if (! el.files.length) return true; // = continue 889 const file = el.files[0]; 890 const fieldName = el.name; 891 fd.append(fieldName, file); 892 }); 893 894 const formdata = $form.serializeArray(); 895 jQuery.each(formdata,function(key, input){ 896 fd.append(input.name, input.value); 897 }); 898 899 jQuery.ajax({ 900 url: wpcf7cf_global_settings.ajaxurl + '?action=wpcf7cf_validate_step', 901 type: 'POST', 902 data: fd, 903 processData: false, 904 contentType: false, 905 dataType: 'json', 906 }).done(function(json) { 907 908 $multistep.find('.wpcf7-form-control-wrap .wpcf7-not-valid-tip').remove(); 909 $multistep.find('.wpcf7-not-valid').removeClass('wpcf7-not-valid'); 910 $multistep.find('.wpcf7-response-output').remove(); 911 $multistep.find('.wpcf7-response-output.wpcf7-validation-errors').removeClass('wpcf7-validation-errors'); 912 913 multistep.$btn_next.removeClass('disabled').attr('disabled', false); 914 915 if (!json.success) { 916 let checkError = 0; 917 918 jQuery.each(json.invalid_fields, function(index, el) { 919 if ($multistep.find('input[name="'+index+'"]').length || 920 $multistep.find('input[name="'+index+'[]"]').length || 921 $multistep.find('select[name="'+index+'"]').length || 922 $multistep.find('select[name="'+index+'[]"]').length || 923 $multistep.find('textarea[name="'+index+'"]').length || 924 $multistep.find('textarea[name="'+index+'[]"]').length 925 ) { 926 checkError = checkError + 1; 927 928 const controlWrap = form.get(`.wpcf7-form-control-wrap[data-name="${index}"]`); 929 controlWrap.find('.wpcf7-form-control').addClass('wpcf7-not-valid'); 930 controlWrap.find('span.wpcf7-not-valid-tip').remove(); 931 controlWrap.append('<span role="alert" class="wpcf7-not-valid-tip">' + el.reason + '</span>'); 932 933 } 934 }); 935 936 resolve('failed'); 937 938 $multistep.parent().find('.wpcf7-response-output').removeClass('wpcf7-display-none').html(json.message); 939 940 wpcf7.setStatus( $form, 'invalid' );
941 multistep.$steps.trigger('wpcf7cf_step_invalid'); 942 943 // wpcf7.triggerEvent( data.into, 'invalid', detail ); 944 945 } else if (json.success) { 946 947 wpcf7.setStatus( $form, 'init' ); 948 949 resolve('success'); 950 return false; 951 } 952 953 }).fail(function() { 954 resolve('error'); 955 }).always(function() { 956 // do nothing 957 }); 958 }); 959 960}; 961Wpcf7cfMultistep.prototype.moveToStep = function(step_index, scrollToTop = true) { 962 const multistep = this; 963 const previousStep = multistep.currentStep; 964 965 multistep.currentStep = step_index > multistep.numSteps ? multistep.numSteps 966 : step_index < 1 ? 1 967 : step_index; 968 969 // ANIMATION DISABLED FOR NOW cause it's ugly 970 // multistep.$steps.animate(wpcf7cf_hide_step_animation, multistep.form.settings.animation_outtime); 971 // multistep.$steps.eq(multistep.currentStep-1).animate(wpcf7cf_show_step_animation, multistep.form.settings.animation_intime); 972 973 multistep.$multistep.attr('data-current_step', multistep.currentStep); 974 multistep.$steps.hide(); 975 multistep.$steps 976 .eq(multistep.currentStep-1) 977 .show() 978 .trigger('wpcf7cf_change_step', [previousStep, multistep.currentStep]); 979 980 if (scrollToTop) { 981 const formEl = multistep.form.$form[0]; 982 const topOffset = formEl.getBoundingClientRect().top; 983 if (topOffset < 0 && previousStep > 0) { 984 formEl.scrollIntoView({behavior: "smooth"}); 985 } 986 } 987 988 multistep.form.updateSummaryFields(); 989 990 window.wpcf7cf.updateMultistepState(multistep); 991}; 992 993Wpcf7cfMultistep.prototype.getFieldsInStep = function(step_index) { 994 this.form.reloadSimpleDom(); 995 let inStep = false; 996 return Object.values(this.form.simpleDom).filter(function(item, i) { 997 if(item.type == 'step') { 998 inStep = item.val == step_index+''; 999 } 1000 return inStep && item.type == 'input'; 1001 }).map(function(item) { 1002 return item.name; 1003 }); 1004}; 1005 1006// END PRO ONLY 1007 1008/** 1009 * @global 1010 * @namespace wpcf7cf 1011 */ 1012window.wpcf7cf = { 1013 1014 hideGroup : function($group, animate) { 1015 1016 }, 1017 1018 showGroup : function($group, animate) { 1019 1020 }, 1021 1022 updateRepeaterSubHTML : function(html, oldSuffix, newSuffix, parentRepeaters) { 1023 const oldIndexes = oldSuffix.split('__'); 1024 oldIndexes.shift(); // remove first empty element 1025 const newIndexes = newSuffix.split('__'); 1026 newIndexes.shift(); // remove first empty element 1027 1028 let returnHtml = html; 1029 1030 if ( 1031 oldIndexes && newIndexes && 1032 oldIndexes.length === parentRepeaters.length && 1033 newIndexes.length === parentRepeaters.length 1034 ) { 1035 1036 const parentRepeatersInfo = parentRepeaters.map((repeaterId, i) => { 1037 return {[repeaterId.split('__')[0]]: [oldIndexes[i], newIndexes[i]]}; 1038 }); 1039 1040 const length = parentRepeatersInfo.length; 1041 1042 let replacements = oldIndexes.map( (oldIndex, i) => { 1043 return [ 1044 '__'+oldIndexes.slice(0,length-i).join('__'), 1045 '__'+newIndexes.slice(0,length-i).join('__'), 1046 ]; 1047 }); 1048 1049 1050 for (let i=0; i<length ; i++) { 1051 const id = Object.keys(parentRepeatersInfo[i])[0]; 1052 const find = parentRepeatersInfo[i][id][0]; 1053 const repl = parentRepeatersInfo[i][id][1]; 1054 replacements.push([ 1055 `<span class="wpcf7cf-index wpcf7cf__${id}">${find}<\\/span>`, 1056 `<span class="wpcf7cf-index wpcf7cf__${id}">${repl}</span>` 1057 ]); 1058 } 1059 1060 replacements.forEach( ([oldSuffix, newSuffix]) => { 1061 returnHtml = returnHtml.replace(new RegExp(oldSuffix,'g'), newSuffix); 1062 }); 1063 1064 } 1065 1066 return returnHtml ; 1067 }, 1068 1069 // keep this for backwards compatibility 1070 initForm : function($forms) { 1071 $forms.each(function(){ 1072 const $form = jQuery(this); 1073 // only add form is its class is "wpcf7-form" and if the form was not previously added 1074 if ( 1075 $form.hasClass('wpcf7-form') && 1076 !wpcf7cf_forms.some((form)=>{ return form.$form.get(0) === $form.get(0); }) 1077 ) { 1078 wpcf7cf_forms.push(new Wpcf7cfForm($form)); 1079 } 1080 }); 1081 }, 1082 1083 getWpcf7cfForm : function ($form) { 1084 const matched_forms = wpcf7cf_forms.filter((form)=>{ 1085 return form.$form.get(0) === $form.get(0); 1086 }); 1087 if (matched_forms.length) { 1088 return matched_forms[0]; 1089 } 1090 return false;
1091 }, 1092 1093 get_nested_conditions : function(form) { 1094 const conditions = form.initial_conditions; 1095 //loop trough conditions. Then loop trough the dom, and each repeater we pass we should update all sub_values we encounter with __index 1096 form.reloadSimpleDom(); 1097 const groups = Object.values(form.simpleDom).filter(function(item, i) { 1098 return item.type==='group'; 1099 }); 1100 1101 let sub_conditions = []; 1102 1103 for(let i = 0; i < groups.length; i++) { 1104 const g = groups[i]; 1105 let relevant_conditions = conditions.filter(function(condition, i) { 1106 return condition.then_field === g.original_name; 1107 }); 1108 1109 relevant_conditions = relevant_conditions.map(function(item,i) { 1110 return { 1111 then_field : g.name, 1112 and_rules : item.and_rules.map(function(and_rule, i) { 1113 return { 1114 if_field : and_rule.if_field+g.suffix, 1115 if_value : and_rule.if_value, 1116 operator : and_rule.operator 1117 }; 1118 }) 1119 } 1120 }); 1121 1122 sub_conditions = sub_conditions.concat(relevant_conditions); 1123 } 1124 return sub_conditions; 1125 }, 1126 1127 get_simplified_dom_model : function(currentNode, simplified_dom = {}, parentGroups = [], parentRepeaters = []) { 1128 1129 const type = currentNode.classList && currentNode.classList.contains('wpcf7cf_repeater') ? 'repeater' : 1130 currentNode.dataset.class == 'wpcf7cf_group' ? 'group' : 1131 currentNode.className == 'wpcf7cf_step' ? 'step' : 1132 currentNode.hasAttribute('name') ? 'input' : false; 1133 1134 let newParentRepeaters = [...parentRepeaters]; 1135 let newParentGroups = [...parentGroups]; 1136 1137 if (type) { 1138 1139 const name = type === 'input' ? currentNode.getAttribute('name') : currentNode.dataset.id; 1140 1141 if (type === 'repeater') { 1142 newParentRepeaters.push(name); 1143 } 1144 if (type === 'group') { 1145 newParentGroups.push(name); 1146 } 1147 1148 // skip _wpcf7 hidden fields 1149 if (name.substring(0,6) === '_wpcf7') return {}; 1150 1151 const original_name = type === 'repeater' || type === 'group' ? currentNode.dataset.orig_data_id 1152 : type === 'input' ? (currentNode.getAttribute('data-orig_name') || name) 1153 : name; 1154 1155 const nameWithoutBrackets = name.replace('[]',''); 1156 const originalNameWithoutBrackets = original_name.replace('[]',''); 1157 1158 const val = type === 'step' ? [currentNode.dataset.id.substring(5)] : []; 1159 1160 const suffix = nameWithoutBrackets.replace(originalNameWithoutBrackets, ''); 1161 1162 if (!simplified_dom[name]) { 1163 // init entry 1164 simplified_dom[name] = {name, type, original_name, suffix, val, parentGroups, parentRepeaters} 1165 } 1166 1167 if (type === 'input') { 1168 1169 // skip unchecked checkboxes and radiobuttons 1170 if ( (currentNode.type === 'checkbox' || currentNode.type === 'radio') && !currentNode.checked ) return {}; 1171 1172 // if multiselect, make sure to add all the values 1173 if ( currentNode.multiple && currentNode.options ) { 1174 simplified_dom[name].val = Object.values(currentNode.options).filter(o => o.selected).map(o => o.value) 1175 } else { 1176 simplified_dom[name].val.push(currentNode.value); 1177 } 1178 } 1179 } 1180 1181 // can't use currentNode.children (because then field name cannot be "children") 1182 const getter = Object.getOwnPropertyDescriptor(Element.prototype, "children").get; 1183 const children = getter.call(currentNode); 1184 1185 Array.from(children).forEach(childNode => { 1186 const dom = wpcf7cf.get_simplified_dom_model(childNode, simplified_dom, newParentGroups, newParentRepeaters); 1187 simplified_dom = {...dom, ...simplified_dom} ; 1188 }); 1189 1190 return simplified_dom; 1191 }, 1192 1193 updateMultistepState: function (multistep) { 1194 if (multistep == null) return; 1195 if (multistep.form.$form.hasClass('submitting')) return; 1196 1197 // update hidden input field 1198 1199 const stepsData = { 1200 currentStep : multistep.currentStep, 1201 numSteps : multistep.numSteps, 1202 fieldsInCurrentStep : multistep.getFieldsInStep(multistep.currentStep) 1203 }; 1204 multistep.form.$input_steps.val(JSON.stringify(stepsData)); 1205 1206 // update Buttons 1207 multistep.$btn_prev.removeClass('disabled').attr('disabled', false); 1208 multistep.$btn_next.removeClass('disabled').attr('disabled', false); 1209 if (multistep.currentStep == multistep.numSteps) { 1210 multistep.$btn_next.addClass('disabled').attr('disabled', true); 1211 } 1212 if (multistep.currentStep == 1) { 1213 multistep.$btn_prev.addClass('disabled').attr('disabled', true); 1214 } 1215 1216 // replace next button with submit button on last step.
1217 // TODO: make this depend on a setting 1218 const $submit_button = multistep.form.$form.find('input[type="submit"]:last').eq(0); 1219 const $ajax_loader = multistep.form.$form.find('.wpcf7-spinner').eq(0); 1220 1221 $submit_button.detach().prependTo(multistep.$btn_next.parent()); 1222 $ajax_loader.detach().prependTo(multistep.$btn_next.parent()); 1223 1224 if (multistep.currentStep == multistep.numSteps) { 1225 multistep.$btn_next.hide(); 1226 $submit_button.show(); 1227 } else { 1228 $submit_button.hide(); 1229 multistep.$btn_next.show(); 1230 } 1231 1232 // update dots 1233 const $dots = multistep.$dots.find('.dot'); 1234 $dots.removeClass('active').removeClass('completed'); 1235 for(let step = 1; step <= multistep.numSteps; step++) { 1236 if (step < multistep.currentStep) { 1237 $dots.eq(step-1).addClass('completed'); 1238 } else if (step == multistep.currentStep) { 1239 $dots.eq(step-1).addClass('active'); 1240 } 1241 } 1242 1243 }, 1244 1245 should_group_be_shown : function(condition, form) { 1246 1247 let show_group = true; 1248 let atLeastOneFieldFound = false; 1249 1250 for (let and_rule_i = 0; and_rule_i < condition.and_rules.length; and_rule_i++) { 1251 1252 let condition_ok = false; 1253 1254 const condition_and_rule = condition.and_rules[and_rule_i]; 1255 1256 const inputField = form.getFieldByName(condition_and_rule.if_field); 1257 1258 if (!inputField) continue; // field not found 1259 1260 atLeastOneFieldFound = true; 1261 1262 const if_val = condition_and_rule.if_value; 1263 let operator = condition_and_rule.operator; 1264 1265 //backwards compat 1266 operator = operator === 'â¤' ? 'less than or equals' : operator; 1267 operator = operator === 'â¥' ? 'greater than or equals' : operator; 1268 operator = operator === '>' ? 'greater than' : operator; 1269 operator = operator === '<' ? 'less than' : operator; 1270 1271 const $field = operator === 'function' && jQuery(`[name="${inputField.name}"]`).eq(0); 1272 1273 condition_ok = this.isConditionTrue(inputField.val,operator,if_val, $field); 1274 1275 show_group = show_group && condition_ok; 1276 } 1277 1278 return show_group && atLeastOneFieldFound; 1279 1280 }, 1281 1282 isConditionTrue(values, operator, testValue='', $field=jQuery()) { 1283 1284 if (!Array.isArray(values)) { 1285 values = [values]; 1286 } 1287 1288 let condition_ok = false; // start by assuming that the condition is not met 1289 1290 // Considered EMPTY: [] [''] [null] ['',null] [,,''] 1291 // Considered NOT EMPTY: [0] ['ab','c'] ['',0,null] 1292 const valuesAreEmpty = values.length === 0 || values.every((v) => !v&&v!==0); // 0 is not considered empty 1293 1294 // special cases: [] equals '' => TRUE; [] not equals '' => FALSE 1295 if (operator === 'equals' && testValue === '' && valuesAreEmpty) { 1296 return true; 1297 } 1298 if (operator === 'not equals' && testValue === '' && valuesAreEmpty) { 1299 return false; 1300 } 1301 1302 if (valuesAreEmpty) { 1303 if (operator === 'is empty') { 1304 condition_ok = true; 1305 } 1306 } else { 1307 if (operator === 'not empty') { 1308 condition_ok = true; 1309 } 1310 } 1311 1312 const testValueNumber = isFinite(parseFloat(testValue)) ? parseFloat(testValue) : NaN; 1313 1314 1315 if (operator === 'not equals' || operator === 'not equals (regex)') { 1316 // start by assuming that the condition is met 1317 condition_ok = true; 1318 } 1319 1320 if ( 1321 operator === 'function' 1322 && typeof window[testValue] == 'function' 1323 && window[testValue]($field) // here we call the actual user defined function 1324 ) { 1325 condition_ok = true; 1326 } 1327 1328 let regex_patt = /.*/i; // fallback regex pattern 1329 let isValidRegex = true; 1330 if (operator === 'equals (regex)' || operator === 'not equals (regex)') { 1331 try { 1332 regex_patt = new RegExp(testValue, 'i'); 1333 } catch(e) { 1334 isValidRegex = false;
1335 } 1336 } 1337 1338 1339 for(let i = 0; i < values.length; i++) { 1340 1341 const value = values[i]; 1342 1343 const valueNumber = isFinite(parseFloat(value)) ? parseFloat(value) : NaN; 1344 const valsAreNumbers = !isNaN(valueNumber) && !isNaN(testValueNumber); 1345 1346 if ( 1347 1348 operator === 'equals' && value === testValue || 1349 operator === 'equals (regex)' && regex_patt.test(value) || 1350 operator === 'greater than' && valsAreNumbers && valueNumber > testValueNumber || 1351 operator === 'less than' && valsAreNumbers && valueNumber < testValueNumber || 1352 operator === 'greater than or equals' && valsAreNumbers && valueNumber >= testValueNumber || 1353 operator === 'less than or equals' && valsAreNumbers && valueNumber <= testValueNumber 1354 1355 ) { 1356 1357 condition_ok = true; 1358 break; 1359 1360 } else if ( 1361 1362 operator === 'not equals' && value === testValue || 1363 operator === 'not equals (regex)' && regex_patt.test(value) 1364 1365 ) { 1366 1367 condition_ok = false; 1368 break; 1369 1370 } 1371 } 1372 1373 return condition_ok; 1374 1375 }, 1376 1377 getFormObj($form) { 1378 if (typeof $form === 'string') { 1379 $form = jQuery($form).eq(0); 1380 } 1381 return wpcf7cf.getWpcf7cfForm($form); 1382 }, 1383 1384 getRepeaterObj($form, repeaterDataId) { 1385 const form = wpcf7cf.getFormObj($form); 1386 const repeater = form.repeaters.find( repeater => repeater.params.$repeater.attr('data-id') === repeaterDataId ); 1387 1388 return repeater; 1389 1390 }, 1391 1392 getMultiStepObj($form){ 1393 const form = wpcf7cf.getFormObj($form); 1394 return form.multistep; 1395 }, 1396 1397 /** 1398 * Append a new sub-entry to the repeater with the name `repeaterDataId` inside the form `$form` 1399 * @memberof wpcf7cf 1400 * @function wpcf7cf.repeaterAddSub 1401 * @link 1402 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1403 * @param {String} repeaterDataId - *data-id* attribute of the repeater. Normally this is simply the name of the repeater. However, in case of a nested repeater you need to append the name with the correct suffix. For example `my-nested-repeater__1__3`. Hint (check the `data-id` attribute in the HTML code to find the correct suffix) 1404 */ 1405 repeaterAddSub($form,repeaterDataId) { 1406 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1407 repeater.updateSubs(repeater.params.$repeater.num_subs+1); 1408 }, 1409 1410 /** 1411 * Insert a new sub-entry at the given `index` of the repeater with the name `repeaterDataId` inside the form `$form` 1412 * @memberof wpcf7cf 1413 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1414 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1415 * @param {Number} index - position where to insert the new sub-entry within the repeater 1416 */ 1417 repeaterAddSubAtIndex($form,repeaterDataId,index) { 1418 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1419 repeater.addSubs(1, index); 1420 }, 1421 1422 /** 1423 * Remove the sub-entry at the given `index` of the repeater with the *data-id* attribute of `repeaterDataId` inside the form `$form` 1424 * @memberof wpcf7cf 1425 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1426 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1427 * @param {Number} index - position where to insert the new sub-entry within the repeater 1428 */ 1429 repeaterRemoveSubAtIndex($form,repeaterDataId,index) { 1430 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1431 repeater.removeSubs(1, index); 1432 }, 1433 1434 /** 1435 * Remove the last sub-entry from the repeater with the *data-id* attribute of `repeaterDataId` inside the form `$form` 1436 * @memberof wpcf7cf 1437 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1438 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1439 * @param {Number} index - position where to insert the new sub-entry within the repeater 1440 */ 1441 repeaterRemoveSub($form,repeaterDataId) { 1442 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1443 repeater.updateSubs(repeater.params.$repeater.num_subs-1); 1444 }, 1445 1446 /** 1447 * Set the number of subs for the repeater with the *data-id* attribute of `repeaterDataId` inside the form `$form`. 1448 * Subs are either appended to or removed from the end of the repeater. 1449 * @memberof wpcf7cf 1450 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1451 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1452 * @param {Number} numberOfSubs - position where to insert the new sub-entry within the repeater 1453 */ 1454 repeaterSetNumberOfSubs($form, repeaterDataId, numberOfSubs) { 1455 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1456 repeater.updateSubs(numberOfSubs); 1457 }, 1458 1459 /** 1460 * Move to step number `step`, ignoring any validation. 1461 * @memberof wpcf7cf 1462 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1463 * @param {*} step 1464 */ 1465 multistepMoveToStep($form, step) { 1466 const multistep = wpcf7cf.getMultiStepObj($form); 1467 multistep.moveToStep(step); 1468 }, 1469 1470 /** 1471 * Validate the current step, and move to step number `step` if val
1471idation passes. 1472 * @memberof wpcf7cf 1473 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1474 * @param {Number} step 1475 */ 1476 async multistepMoveToStepWithValidation($form, step) { 1477 const multistep = wpcf7cf.getMultiStepObj($form); 1478 1479 const result = await multistep.validateStep(multistep.currentStep); 1480 if (result === 'success') { 1481 multistep.moveToStep(step); 1482 } 1483 }, 1484 1485 1486}; 1487 1488jQuery('.wpcf7-form').each(function(){ 1489 wpcf7cf_forms.push(new Wpcf7cfForm(jQuery(this))); 1490}); 1491 1492// Call displayFields again on all forms 1493// Necessary in case some theme or plugin changed a form value by the time the entire page is fully loaded. 1494jQuery('document').ready( function() { 1495 wpcf7cf_forms.forEach(function(f){ 1496 f.displayFields(); 1497 }); 1498}); 1499 1500// fix for exclusive checkboxes in IE (this will call the change-event again after all other checkboxes are unchecked, triggering the display_fields() function) 1501const old_wpcf7ExclusiveCheckbox = jQuery.fn.wpcf7ExclusiveCheckbox; 1502jQuery.fn.wpcf7ExclusiveCheckbox = function() { 1503 return this.find('input:checkbox').on('click', function() { 1504 const name = jQuery(this).attr('name'); 1505 jQuery(this).closest('form').find('input:checkbox[name="' + name + '"]').not(this).prop('checked', false).eq(0).change(); 1506 }); 1507};
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.