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.currentStep !== multistep.numSteps) { 854 multistep.$btn_next.trigger('click.wpcf7cf_step'); 855 856 e.stopImmediatePropagation(); 857 return false; 858 } 859 }); 860 861 multistep.$btn_prev.on( 'click', function() { 862 multistep.moveToStep(multistep.currentStep-1); 863 }); 864 865 multistep.moveToStep(1); 866} 867 868Wpcf7cfMultistep.prototype.validateStep = function(step_index) { 869 870 const multistep = this; 871 const $multistep = multistep.$multistep; 872 const $form = multistep.form.$form; 873 const form = multistep.form; 874 875 $form.find('.wpcf7-response-output').addClass('wpcf7-display-none'); 876 877 return new Promise(resolve => { 878 879 const fd = new FormData(); 880 881 // Make sure to add file fields to FormData 882 jQuery.each($form.find('[data-id="step-'+step_index+'"] input[type="file"]'), function(index, el) { 883 if (! el.files.length) return true; // = continue 884 const file = el.files[0]; 885 const fieldName = el.name; 886 fd.append(fieldName, file); 887 }); 888 889 const formdata = $form.serializeArray(); 890 jQuery.each(formdata,function(key, input){ 891 fd.append(input.name, input.value); 892 }); 893 894 jQuery.ajax({ 895 url: wpcf7cf_global_settings.ajaxurl + '?action=wpcf7cf_validate_step', 896 type: 'POST', 897 data: fd, 898 processData: false, 899 contentType: false, 900 dataType: 'json', 901 }).done(function(json) { 902 903 $multistep.find('.wpcf7-form-control-wrap .wpcf7-not-valid-tip').remove(); 904 $multistep.find('.wpcf7-not-valid').removeClass('wpcf7-not-valid'); 905 $multistep.find('.wpcf7-response-output').remove(); 906 $multistep.find('.wpcf7-response-output.wpcf7-validation-errors').removeClass('wpcf7-validation-errors'); 907 908 multistep.$btn_next.removeClass('disabled').attr('disabled', false); 909 910 if (!json.success) { 911 let checkError = 0; 912 913 jQuery.each(json.invalid_fields, function(index, el) { 914 if ($multistep.find('input[name="'+index+'"]').length || 915 $multistep.find('input[name="'+index+'[]"]').length || 916 $multistep.find('select[name="'+index+'"]').length || 917 $multistep.find('select[name="'+index+'[]"]').length || 918 $multistep.find('textarea[name="'+index+'"]').length || 919 $multistep.find('textarea[name="'+index+'[]"]').length 920 ) { 921 checkError = checkError + 1; 922 923 const controlWrap = form.get(`.wpcf7-form-control-wrap[data-name="${index}"]`); 924 controlWrap.find('.wpcf7-form-control').addClass('wpcf7-not-valid'); 925 controlWrap.find('span.wpcf7-not-valid-tip').remove(); 926 controlWrap.append('<span role="alert" class="wpcf7-not-valid-tip">' + el.reason + '</span>'); 927 928 } 929 }); 930 931 resolve('failed'); 932 933 $multistep.parent().find('.wpcf7-response-output').removeClass('wpcf7-display-none').html(json.message); 934 935 wpcf7.setStatus( $form, 'invalid' );
936 multistep.$steps.trigger('wpcf7cf_step_invalid'); 937 938 // wpcf7.triggerEvent( data.into, 'invalid', detail ); 939 940 } else if (json.success) { 941 942 wpcf7.setStatus( $form, 'init' ); 943 944 resolve('success'); 945 return false; 946 } 947 948 }).fail(function() { 949 resolve('error'); 950 }).always(function() { 951 // do nothing 952 }); 953 }); 954 955}; 956Wpcf7cfMultistep.prototype.moveToStep = function(step_index, scrollToTop = true) { 957 const multistep = this; 958 const previousStep = multistep.currentStep; 959 960 multistep.currentStep = step_index > multistep.numSteps ? multistep.numSteps 961 : step_index < 1 ? 1 962 : step_index; 963 964 // ANIMATION DISABLED FOR NOW cause it's ugly 965 // multistep.$steps.animate(wpcf7cf_hide_step_animation, multistep.form.settings.animation_outtime); 966 // multistep.$steps.eq(multistep.currentStep-1).animate(wpcf7cf_show_step_animation, multistep.form.settings.animation_intime); 967 968 multistep.$multistep.attr('data-current_step', multistep.currentStep); 969 multistep.$steps.hide(); 970 multistep.$steps 971 .eq(multistep.currentStep-1) 972 .show() 973 .trigger('wpcf7cf_change_step', [previousStep, multistep.currentStep]); 974 975 if (scrollToTop) { 976 const formEl = multistep.form.$form[0]; 977 const topOffset = formEl.getBoundingClientRect().top; 978 if (topOffset < 0 && previousStep > 0) { 979 formEl.scrollIntoView({behavior: "smooth"}); 980 } 981 } 982 983 multistep.form.updateSummaryFields(); 984 985 window.wpcf7cf.updateMultistepState(multistep); 986}; 987 988Wpcf7cfMultistep.prototype.getFieldsInStep = function(step_index) { 989 this.form.reloadSimpleDom(); 990 let inStep = false; 991 return Object.values(this.form.simpleDom).filter(function(item, i) { 992 if(item.type == 'step') { 993 inStep = item.val == step_index+''; 994 } 995 return inStep && item.type == 'input'; 996 }).map(function(item) { 997 return item.name; 998 }); 999}; 1000 1001// END PRO ONLY 1002 1003/** 1004 * @global 1005 * @namespace wpcf7cf 1006 */ 1007window.wpcf7cf = { 1008 1009 hideGroup : function($group, animate) { 1010 1011 }, 1012 1013 showGroup : function($group, animate) { 1014 1015 }, 1016 1017 updateRepeaterSubHTML : function(html, oldSuffix, newSuffix, parentRepeaters) { 1018 const oldIndexes = oldSuffix.split('__'); 1019 oldIndexes.shift(); // remove first empty element 1020 const newIndexes = newSuffix.split('__'); 1021 newIndexes.shift(); // remove first empty element 1022 1023 let returnHtml = html; 1024 1025 if ( 1026 oldIndexes && newIndexes && 1027 oldIndexes.length === parentRepeaters.length && 1028 newIndexes.length === parentRepeaters.length 1029 ) { 1030 1031 const parentRepeatersInfo = parentRepeaters.map((repeaterId, i) => { 1032 return {[repeaterId.split('__')[0]]: [oldIndexes[i], newIndexes[i]]}; 1033 }); 1034 1035 const length = parentRepeatersInfo.length; 1036 1037 let replacements = oldIndexes.map( (oldIndex, i) => { 1038 return [ 1039 '__'+oldIndexes.slice(0,length-i).join('__'), 1040 '__'+newIndexes.slice(0,length-i).join('__'), 1041 ]; 1042 }); 1043 1044 1045 for (let i=0; i<length ; i++) { 1046 const id = Object.keys(parentRepeatersInfo[i])[0]; 1047 const find = parentRepeatersInfo[i][id][0]; 1048 const repl = parentRepeatersInfo[i][id][1]; 1049 replacements.push([ 1050 `<span class="wpcf7cf-index wpcf7cf__${id}">${find}<\\/span>`, 1051 `<span class="wpcf7cf-index wpcf7cf__${id}">${repl}</span>` 1052 ]); 1053 } 1054 1055 replacements.forEach( ([oldSuffix, newSuffix]) => { 1056 returnHtml = returnHtml.replace(new RegExp(oldSuffix,'g'), newSuffix); 1057 }); 1058 1059 } 1060 1061 return returnHtml ; 1062 }, 1063 1064 // keep this for backwards compatibility 1065 initForm : function($forms) { 1066 $forms.each(function(){ 1067 const $form = jQuery(this); 1068 // only add form is its class is "wpcf7-form" and if the form was not previously added 1069 if ( 1070 $form.hasClass('wpcf7-form') && 1071 !wpcf7cf_forms.some((form)=>{ return form.$form.get(0) === $form.get(0); }) 1072 ) { 1073 wpcf7cf_forms.push(new Wpcf7cfForm($form)); 1074 } 1075 }); 1076 }, 1077 1078 getWpcf7cfForm : function ($form) { 1079 const matched_forms = wpcf7cf_forms.filter((form)=>{ 1080 return form.$form.get(0) === $form.get(0); 1081 }); 1082 if (matched_forms.length) { 1083 return matched_forms[0]; 1084 } 1085 return false;
1086 }, 1087 1088 get_nested_conditions : function(form) { 1089 const conditions = form.initial_conditions; 1090 //loop trough conditions. Then loop trough the dom, and each repeater we pass we should update all sub_values we encounter with __index 1091 form.reloadSimpleDom(); 1092 const groups = Object.values(form.simpleDom).filter(function(item, i) { 1093 return item.type==='group'; 1094 }); 1095 1096 let sub_conditions = []; 1097 1098 for(let i = 0; i < groups.length; i++) { 1099 const g = groups[i]; 1100 let relevant_conditions = conditions.filter(function(condition, i) { 1101 return condition.then_field === g.original_name; 1102 }); 1103 1104 relevant_conditions = relevant_conditions.map(function(item,i) { 1105 return { 1106 then_field : g.name, 1107 and_rules : item.and_rules.map(function(and_rule, i) { 1108 return { 1109 if_field : and_rule.if_field+g.suffix, 1110 if_value : and_rule.if_value, 1111 operator : and_rule.operator 1112 }; 1113 }) 1114 } 1115 }); 1116 1117 sub_conditions = sub_conditions.concat(relevant_conditions); 1118 } 1119 return sub_conditions; 1120 }, 1121 1122 get_simplified_dom_model : function(currentNode, simplified_dom = {}, parentGroups = [], parentRepeaters = []) { 1123 1124 const type = currentNode.classList && currentNode.classList.contains('wpcf7cf_repeater') ? 'repeater' : 1125 currentNode.dataset.class == 'wpcf7cf_group' ? 'group' : 1126 currentNode.className == 'wpcf7cf_step' ? 'step' : 1127 currentNode.hasAttribute('name') ? 'input' : false; 1128 1129 let newParentRepeaters = [...parentRepeaters]; 1130 let newParentGroups = [...parentGroups]; 1131 1132 if (type) { 1133 1134 const name = type === 'input' ? currentNode.getAttribute('name') : currentNode.dataset.id; 1135 1136 if (type === 'repeater') { 1137 newParentRepeaters.push(name); 1138 } 1139 if (type === 'group') { 1140 newParentGroups.push(name); 1141 } 1142 1143 // skip _wpcf7 hidden fields 1144 if (name.substring(0,6) === '_wpcf7') return {}; 1145 1146 const original_name = type === 'repeater' || type === 'group' ? currentNode.dataset.orig_data_id 1147 : type === 'input' ? (currentNode.getAttribute('data-orig_name') || name) 1148 : name; 1149 1150 const nameWithoutBrackets = name.replace('[]',''); 1151 const originalNameWithoutBrackets = original_name.replace('[]',''); 1152 1153 const val = type === 'step' ? [currentNode.dataset.id.substring(5)] : []; 1154 1155 const suffix = nameWithoutBrackets.replace(originalNameWithoutBrackets, ''); 1156 1157 if (!simplified_dom[name]) { 1158 // init entry 1159 simplified_dom[name] = {name, type, original_name, suffix, val, parentGroups, parentRepeaters} 1160 } 1161 1162 if (type === 'input') { 1163 1164 // skip unchecked checkboxes and radiobuttons 1165 if ( (currentNode.type === 'checkbox' || currentNode.type === 'radio') && !currentNode.checked ) return {}; 1166 1167 // if multiselect, make sure to add all the values 1168 if ( currentNode.multiple && currentNode.options ) { 1169 simplified_dom[name].val = Object.values(currentNode.options).filter(o => o.selected).map(o => o.value) 1170 } else { 1171 simplified_dom[name].val.push(currentNode.value); 1172 } 1173 } 1174 } 1175 1176 // can't use currentNode.children (because then field name cannot be "children") 1177 const getter = Object.getOwnPropertyDescriptor(Element.prototype, "children").get; 1178 const children = getter.call(currentNode); 1179 1180 Array.from(children).forEach(childNode => { 1181 const dom = wpcf7cf.get_simplified_dom_model(childNode, simplified_dom, newParentGroups, newParentRepeaters); 1182 simplified_dom = {...dom, ...simplified_dom} ; 1183 }); 1184 1185 return simplified_dom; 1186 }, 1187 1188 updateMultistepState: function (multistep) { 1189 if (multistep == null) return; 1190 1191 // update hidden input field 1192 1193 const stepsData = { 1194 currentStep : multistep.currentStep, 1195 numSteps : multistep.numSteps, 1196 fieldsInCurrentStep : multistep.getFieldsInStep(multistep.currentStep) 1197 }; 1198 multistep.form.$input_steps.val(JSON.stringify(stepsData)); 1199 1200 // update Buttons 1201 multistep.$btn_prev.removeClass('disabled').attr('disabled', false); 1202 multistep.$btn_next.removeClass('disabled').attr('disabled', false); 1203 if (multistep.currentStep == multistep.numSteps) { 1204 multistep.$btn_next.addClass('disabled').attr('disabled', true); 1205 } 1206 if (multistep.currentStep == 1) { 1207 multistep.$btn_prev.addClass('disabled').attr('disabled', true); 1208 } 1209 1210 // replace next button with submit button on last step.
1211 // TODO: make this depend on a setting 1212 const $submit_button = multistep.form.$form.find('input[type="submit"]:last').eq(0); 1213 const $ajax_loader = multistep.form.$form.find('.wpcf7-spinner').eq(0); 1214 1215 $submit_button.detach().prependTo(multistep.$btn_next.parent()); 1216 $ajax_loader.detach().prependTo(multistep.$btn_next.parent()); 1217 1218 if (multistep.currentStep == multistep.numSteps) { 1219 multistep.$btn_next.hide(); 1220 $submit_button.show(); 1221 } else { 1222 $submit_button.hide(); 1223 multistep.$btn_next.show(); 1224 } 1225 1226 // update dots 1227 const $dots = multistep.$dots.find('.dot'); 1228 $dots.removeClass('active').removeClass('completed'); 1229 for(let step = 1; step <= multistep.numSteps; step++) { 1230 if (step < multistep.currentStep) { 1231 $dots.eq(step-1).addClass('completed'); 1232 } else if (step == multistep.currentStep) { 1233 $dots.eq(step-1).addClass('active'); 1234 } 1235 } 1236 1237 }, 1238 1239 should_group_be_shown : function(condition, form) { 1240 1241 let show_group = true; 1242 let atLeastOneFieldFound = false; 1243 1244 for (let and_rule_i = 0; and_rule_i < condition.and_rules.length; and_rule_i++) { 1245 1246 let condition_ok = false; 1247 1248 const condition_and_rule = condition.and_rules[and_rule_i]; 1249 1250 const inputField = form.getFieldByName(condition_and_rule.if_field); 1251 1252 if (!inputField) continue; // field not found 1253 1254 atLeastOneFieldFound = true; 1255 1256 const if_val = condition_and_rule.if_value; 1257 let operator = condition_and_rule.operator; 1258 1259 //backwards compat 1260 operator = operator === 'â¤' ? 'less than or equals' : operator; 1261 operator = operator === 'â¥' ? 'greater than or equals' : operator; 1262 operator = operator === '>' ? 'greater than' : operator; 1263 operator = operator === '<' ? 'less than' : operator; 1264 1265 const $field = operator === 'function' && jQuery(`[name="${inputField.name}"]`).eq(0); 1266 1267 condition_ok = this.isConditionTrue(inputField.val,operator,if_val, $field); 1268 1269 show_group = show_group && condition_ok; 1270 } 1271 1272 return show_group && atLeastOneFieldFound; 1273 1274 }, 1275 1276 isConditionTrue(values, operator, testValue='', $field=jQuery()) { 1277 1278 if (!Array.isArray(values)) { 1279 values = [values]; 1280 } 1281 1282 let condition_ok = false; // start by assuming that the condition is not met 1283 1284 // Considered EMPTY: [] [''] [null] ['',null] [,,''] 1285 // Considered NOT EMPTY: [0] ['ab','c'] ['',0,null] 1286 const valuesAreEmpty = values.length === 0 || values.every((v) => !v&&v!==0); // 0 is not considered empty 1287 1288 // special cases: [] equals '' => TRUE; [] not equals '' => FALSE 1289 if (operator === 'equals' && testValue === '' && valuesAreEmpty) { 1290 return true; 1291 } 1292 if (operator === 'not equals' && testValue === '' && valuesAreEmpty) { 1293 return false; 1294 } 1295 1296 if (valuesAreEmpty) { 1297 if (operator === 'is empty') { 1298 condition_ok = true; 1299 } 1300 } else { 1301 if (operator === 'not empty') { 1302 condition_ok = true; 1303 } 1304 } 1305 1306 const testValueNumber = isFinite(parseFloat(testValue)) ? parseFloat(testValue) : NaN; 1307 1308 1309 if (operator === 'not equals' || operator === 'not equals (regex)') { 1310 // start by assuming that the condition is met 1311 condition_ok = true; 1312 } 1313 1314 if ( 1315 operator === 'function' 1316 && typeof window[testValue] == 'function' 1317 && window[testValue]($field) // here we call the actual user defined function 1318 ) { 1319 condition_ok = true; 1320 } 1321 1322 let regex_patt = /.*/i; // fallback regex pattern 1323 let isValidRegex = true; 1324 if (operator === 'equals (regex)' || operator === 'not equals (regex)') { 1325 try { 1326 regex_patt = new RegExp(testValue, 'i'); 1327 } catch(e) { 1328 isValidRegex = false;
1329 } 1330 } 1331 1332 1333 for(let i = 0; i < values.length; i++) { 1334 1335 const value = values[i]; 1336 1337 const valueNumber = isFinite(parseFloat(value)) ? parseFloat(value) : NaN; 1338 const valsAreNumbers = !isNaN(valueNumber) && !isNaN(testValueNumber); 1339 1340 if ( 1341 1342 operator === 'equals' && value === testValue || 1343 operator === 'equals (regex)' && regex_patt.test(value) || 1344 operator === 'greater than' && valsAreNumbers && valueNumber > testValueNumber || 1345 operator === 'less than' && valsAreNumbers && valueNumber < testValueNumber || 1346 operator === 'greater than or equals' && valsAreNumbers && valueNumber >= testValueNumber || 1347 operator === 'less than or equals' && valsAreNumbers && valueNumber <= testValueNumber 1348 1349 ) { 1350 1351 condition_ok = true; 1352 break; 1353 1354 } else if ( 1355 1356 operator === 'not equals' && value === testValue || 1357 operator === 'not equals (regex)' && regex_patt.test(value) 1358 1359 ) { 1360 1361 condition_ok = false; 1362 break; 1363 1364 } 1365 } 1366 1367 return condition_ok; 1368 1369 }, 1370 1371 getFormObj($form) { 1372 if (typeof $form === 'string') { 1373 $form = jQuery($form).eq(0); 1374 } 1375 return wpcf7cf.getWpcf7cfForm($form); 1376 }, 1377 1378 getRepeaterObj($form, repeaterDataId) { 1379 const form = wpcf7cf.getFormObj($form); 1380 const repeater = form.repeaters.find( repeater => repeater.params.$repeater.attr('data-id') === repeaterDataId ); 1381 1382 return repeater; 1383 1384 }, 1385 1386 getMultiStepObj($form){ 1387 const form = wpcf7cf.getFormObj($form); 1388 return form.multistep; 1389 }, 1390 1391 /** 1392 * Append a new sub-entry to the repeater with the name `repeaterDataId` inside the form `$form` 1393 * @memberof wpcf7cf 1394 * @function wpcf7cf.repeaterAddSub 1395 * @link 1396 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1397 * @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) 1398 */ 1399 repeaterAddSub($form,repeaterDataId) { 1400 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1401 repeater.updateSubs(repeater.params.$repeater.num_subs+1); 1402 }, 1403 1404 /** 1405 * Insert a new sub-entry at the given `index` of the repeater with the name `repeaterDataId` inside the form `$form` 1406 * @memberof wpcf7cf 1407 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1408 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1409 * @param {Number} index - position where to insert the new sub-entry within the repeater 1410 */ 1411 repeaterAddSubAtIndex($form,repeaterDataId,index) { 1412 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1413 repeater.addSubs(1, index); 1414 }, 1415 1416 /** 1417 * Remove the sub-entry at the given `index` of the repeater with the *data-id* attribute of `repeaterDataId` inside the form `$form` 1418 * @memberof wpcf7cf 1419 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1420 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1421 * @param {Number} index - position where to insert the new sub-entry within the repeater 1422 */ 1423 repeaterRemoveSubAtIndex($form,repeaterDataId,index) { 1424 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1425 repeater.removeSubs(1, index); 1426 }, 1427 1428 /** 1429 * Remove the last sub-entry from the repeater with the *data-id* attribute of `repeaterDataId` inside the form `$form` 1430 * @memberof wpcf7cf 1431 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1432 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1433 * @param {Number} index - position where to insert the new sub-entry within the repeater 1434 */ 1435 repeaterRemoveSub($form,repeaterDataId) { 1436 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1437 repeater.updateSubs(repeater.params.$repeater.num_subs-1); 1438 }, 1439 1440 /** 1441 * Set the number of subs for the repeater with the *data-id* attribute of `repeaterDataId` inside the form `$form`. 1442 * Subs are either appended to or removed from the end of the repeater. 1443 * @memberof wpcf7cf 1444 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1445 * @param {String} repeaterDataId - *data-id* attribute of the repeater. 1446 * @param {Number} numberOfSubs - position where to insert the new sub-entry within the repeater 1447 */ 1448 repeaterSetNumberOfSubs($form, repeaterDataId, numberOfSubs) { 1449 const repeater = wpcf7cf.getRepeaterObj($form, repeaterDataId); 1450 repeater.updateSubs(numberOfSubs); 1451 }, 1452 1453 /** 1454 * Move to step number `step`, ignoring any validation. 1455 * @memberof wpcf7cf 1456 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1457 * @param {*} step 1458 */ 1459 multistepMoveToStep($form, step) { 1460 const multistep = wpcf7cf.getMultiStepObj($form); 1461 multistep.moveToStep(step); 1462 }, 1463 1464 /** 1465 * Validate the current step, and move to step number `step` if val
1465idation passes. 1466 * @memberof wpcf7cf 1467 * @param {String|JQuery} $form - JQuery object or css-selector representing the form 1468 * @param {Number} step 1469 */ 1470 async multistepMoveToStepWithValidation($form, step) { 1471 const multistep = wpcf7cf.getMultiStepObj($form); 1472 1473 const result = await multistep.validateStep(multistep.currentStep); 1474 if (result === 'success') { 1475 multistep.moveToStep(step); 1476 } 1477 }, 1478 1479 1480}; 1481 1482jQuery('.wpcf7-form').each(function(){ 1483 wpcf7cf_forms.push(new Wpcf7cfForm(jQuery(this))); 1484}); 1485 1486// Call displayFields again on all forms 1487// Necessary in case some theme or plugin changed a form value by the time the entire page is fully loaded. 1488jQuery('document').ready( function() { 1489 wpcf7cf_forms.forEach(function(f){ 1490 f.displayFields(); 1491 }); 1492}); 1493 1494// fix for exclusive checkboxes in IE (this will call the change-event again after all other checkboxes are unchecked, triggering the display_fields() function) 1495const old_wpcf7ExclusiveCheckbox = jQuery.fn.wpcf7ExclusiveCheckbox; 1496jQuery.fn.wpcf7ExclusiveCheckbox = function() { 1497 return this.find('input:checkbox').on('click', function() { 1498 const name = jQuery(this).attr('name'); 1499 jQuery(this).closest('form').find('input:checkbox[name="' + name + '"]').not(this).prop('checked', false).eq(0).change(); 1500 }); 1501};
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.