PluginProbe ʕ •ᴥ•ʔ
Conditional Fields for Contact Form 7 / 1.9.6
Conditional Fields for Contact Form 7 v1.9.6
2.7.10 2.7.9 2.7.8 2.7.7 2.7.6 2.7.5 2.7.4 2.7.3 2.7.2 0.2.4 0.2.5 0.2.6 0.2.7 0.2.8 0.2.9 1.0 1.1 1.2 1.2.1 1.2.2 1.2.3 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.4 1.4.1 1.4.2 1.4.3 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6.1 1.6.2 1.6.3 1.6.5 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.8 1.7.9 1.8 1.8.1 1.8.2 1.8.3 1.8.5 1.8.6 1.8.7 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2 2.2.1 2.2.10 2.2.11 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3 2.3.1 2.3.10 2.3.11 2.3.12 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4 2.4.1 2.4.10 2.4.11 2.4.12 2.4.13 2.4.14 2.4.15 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.5 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.14 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7 2.6.8 2.7 2.7.1 trunk 0.1 0.1.1 0.1.2 0.1.3 0.1.4 0.1.5 0.1.6 0.1.7 0.2 0.2.1 0.2.2 0.2.3
cf7-conditional-fields / js / scripts_es6.js
cf7-conditional-fields / js Last commit date
scripts.js 6 years ago scripts.js.map 6 years ago scripts_admin.js 6 years ago scripts_es6.js 6 years ago
scripts_es6.js
1063 lines
1 "use strict";
2
3 var cf7signature_resized = 0; // for compatibility with contact-form-7-signature-addon
4
5 var wpcf7cf_timeout;
6 var wpcf7cf_change_time_ms = 100;
7
8 if (window.wpcf7cf_running_tests) {
9 jQuery('input[name="_wpcf7cf_options"]').each(function(e) {
10 var $input = jQuery(this);
11 var opt = JSON.parse($input.val());
12 opt.settings.animation_intime = 0;
13 opt.settings.animation_outtime = 0;
14 $input.val(JSON.stringify(opt));
15 });
16 wpcf7cf_change_time_ms = 0;
17 }
18
19 var wpcf7cf_show_animation = { "height": "show", "marginTop": "show", "marginBottom": "show", "paddingTop": "show", "paddingBottom": "show" };
20 var wpcf7cf_hide_animation = { "height": "hide", "marginTop": "hide", "marginBottom": "hide", "paddingTop": "hide", "paddingBottom": "hide" };
21
22 var wpcf7cf_show_step_animation = { "opacity": "show" };
23 var wpcf7cf_hide_step_animation = { "opacity": "hide" };
24
25 var wpcf7cf_change_events = 'input.wpcf7cf paste.wpcf7cf change.wpcf7cf click.wpcf7cf propertychange.wpcf7cf';
26
27 var wpcf7cf_forms = [];
28
29 window.wpcf7cf_dom = {};
30
31 const wpcf7cf_reload_dom = function($form) {
32 wpcf7cf_dom = wpcf7cf.get_simplified_dom_model($form);
33 }
34
35 const wpcf7cf_getFieldsByOriginalName = function(originalName) {
36 return Object.values(wpcf7cf_dom).filter(function (inputField) {
37 return inputField.original_name === originalName || inputField.original_name === originalName+'[]';
38 });
39 }
40
41 const wpcf7cf_getFieldByName = function(name) {
42 return wpcf7cf_dom[name] || wpcf7cf_dom[name+'[]'];
43 }
44
45 // endsWith polyfill
46 if (!String.prototype.endsWith) {
47 String.prototype.endsWith = function(search, this_len) {
48 if (this_len === undefined || this_len > this.length) {
49 this_len = this.length;
50 }
51 return this.substring(this_len - search.length, this_len) === search;
52 };
53 }
54
55 var Wpcf7cfForm = function($form) {
56
57 var options_element = $form.find('input[name="_wpcf7cf_options"]').eq(0);
58 if (!options_element.length || !options_element.val()) {
59 // doesn't look like a CF7 form created with conditional fields plugin enabled.
60 return false;
61 }
62
63 var form = this;
64
65 var form_options = JSON.parse(options_element.val());
66
67 form.$form = $form;
68 form.$input_hidden_group_fields = $form.find('[name="_wpcf7cf_hidden_group_fields"]');
69 form.$input_hidden_groups = $form.find('[name="_wpcf7cf_hidden_groups"]');
70 form.$input_visible_groups = $form.find('[name="_wpcf7cf_visible_groups"]');
71 form.$input_repeaters = $form.find('[name="_wpcf7cf_repeaters"]');
72 form.$input_steps = $form.find('[name="_wpcf7cf_steps"]');
73
74 form.unit_tag = $form.closest('.wpcf7').attr('id');
75 form.conditions = form_options['conditions'];
76
77 // Wrapper around jQuery(selector, form.$form)
78 form.get = function (selector) {
79 // TODO: implement some caching here.
80 return jQuery(selector, form.$form);
81 }
82
83 // compatibility with conditional forms created with older versions of the plugin ( < 1.4 )
84 for (var i=0; i < form.conditions.length; i++) {
85 var condition = form.conditions[i];
86 if (!('and_rules' in condition)) {
87 condition.and_rules = [{'if_field':condition.if_field,'if_value':condition.if_value,'operator':condition.operator}];
88 }
89 }
90
91 form.initial_conditions = form.conditions;
92 form.settings = form_options['settings'];
93
94 form.$groups = jQuery(); // empty jQuery set
95 form.repeaters = [];
96 form.multistep = null;
97 form.fields = [];
98
99 form.settings.animation_intime = parseInt(form.settings.animation_intime);
100 form.settings.animation_outtime = parseInt(form.settings.animation_outtime);
101
102 if (form.settings.animation === 'no') {
103 form.settings.animation_intime = 0;
104 form.settings.animation_outtime = 0;
105 }
106
107 form.updateGroups();
108 form.updateEventListeners();
109 form.displayFields();
110
111 // bring form in initial state if the reset event is fired on it.
112 form.$form.on('reset.wpcf7cf', form, function(e) {
113 var form = e.data;
114 setTimeout(function(){
115 form.displayFields();
116 form.resetRepeaters();
117 if (form.multistep != null) {
118 form.multistep.moveToStep(1);
119 }
120 },200);
121 });
122
123 // PRO ONLY
124
125 form.get('.wpcf7cf_repeater:not(.wpcf7cf_repeater .wpcf7cf_repeater)').each(function(){
126 form.repeaters.push(new Wpcf7cfRepeater(jQuery(this),form));
127 });
128
129 form.$input_repeaters.val(JSON.stringify(form.repeaters.map((item)=>item.params.$repeater.id)));
130
131 var $multistep = form.get('.wpcf7cf_multistep');
132
133 if ($multistep.length) {
134 form.multistep = new Wpcf7cfMultistep($multistep, form);
135 // window.wpcf7cf.updateMultistepState(form.multistep);
136 }
137
138 // END PRO ONLY
139
140 }
141
142 /**
143 * reset initial number of subs for each repeater.
144 * (does not clear values)
145 */
146 Wpcf7cfForm.prototype.resetRepeaters = function() {
147 var form = this;
148 form.repeaters.forEach(repeater => {
149 repeater.updateSubs( repeater.params.$repeater.initial_subs );
150 });
151 }
152
153 Wpcf7cfForm.prototype.displayFields = function() {
154
155 var form = this;
156
157 var wpcf7cf_conditions = this.conditions;
158 var wpcf7cf_settings = this.settings;
159
160 //for compatibility with contact-form-7-signature-addon
161 if (cf7signature_resized === 0 && typeof signatures !== 'undefined' && signatures.constructor === Array && signatures.length > 0 ) {
162 for (var i = 0; i < signatures.length; i++) {
163 if (signatures[i].canvas.width === 0) {
164
165 var $sig_canvas = jQuery(".wpcf7-form-control-signature-body>canvas");
166 var $sig_wrap = jQuery(".wpcf7-form-control-signature-wrap");
167 $sig_canvas.eq(i).attr('width', $sig_wrap.width());
168 $sig_canvas.eq(i).attr('height', $sig_wrap.height());
169
170 cf7signature_resized = 1;
171 }
172 }
173 }
174
175 form.$groups.addClass('wpcf7cf-hidden');
176
177 wpcf7cf_reload_dom(form.$form);
178
179 for (var i=0; i < wpcf7cf_conditions.length; i++) {
180
181 var condition = wpcf7cf_conditions[i];
182
183 var show_group = window.wpcf7cf.should_group_be_shown(condition, form);
184
185 if (show_group) {
186 form.get('[data-id="'+condition.then_field+'"]').removeClass('wpcf7cf-hidden');
187 }
188 }
189
190
191 var animation_intime = wpcf7cf_settings.animation_intime;
192 var animation_outtime = wpcf7cf_settings.animation_outtime;
193
194 form.$groups.each(function (index) {
195 var $group = jQuery(this);
196 if ($group.is(':animated')) $group.finish(); // stop any current animations on the group
197 if ($group.css('display') === 'none' && !$group.hasClass('wpcf7cf-hidden')) {
198 if ($group.prop('tagName') === 'SPAN') {
199 $group.show().trigger('wpcf7cf_show_group');
200 } else {
201 $group.animate(wpcf7cf_show_animation, animation_intime).trigger('wpcf7cf_show_group'); // show
202 }
203 } else if ($group.css('display') !== 'none' && $group.hasClass('wpcf7cf-hidden')) {
204
205 if ($group.attr('data-clear_on_hide') !== undefined) {
206 var $inputs = jQuery(':input', $group).not(':button, :submit, :reset, :hidden');
207
208 $inputs.each(function(){
209 var $this = jQuery(this);
210 $this.val(this.defaultValue);
211 $this.prop('checked', this.defaultChecked);
212 });
213
214 jQuery('option', $group).each(function() {
215 this.selected = this.defaultSelected;
216 });
217
218 jQuery('select', $group).each(function() {
219 const $select = jQuery(this);
220 if ($select.val() === null) {
221 $select.val(jQuery("option:first",$select).val());
222 }
223 });
224
225 $inputs.change();
226 //display_fields();
227 }
228
229 if ($group.prop('tagName') === 'SPAN') {
230 $group.hide().trigger('wpcf7cf_hide_group');
231 } else {
232 $group.animate(wpcf7cf_hide_animation, animation_outtime).trigger('wpcf7cf_hide_group'); // hide
233 }
234
235 }
236 });
237
238 form.updateHiddenFields();
239 form.updateSummaryFields();
240 };
241
242 Wpcf7cfForm.prototype.updateSummaryFields = function() {
243 const form = this;
244 var $summary = form.get('.wpcf7cf-summary');
245
246 if ($summary.length == 0 || !$summary.is(':visible')) return;
247
248 var fd = new FormData();
249
250 var formdata = form.$form.serializeArray();
251 jQuery.each(formdata,function(key, input){
252 fd.append(input.name, input.value);
253 });
254
255 // Make sure to add file fields to FormData
256 jQuery.each(form.$form.find('input[type="file"]'), function(index, el) {
257 if (! el.files.length) return false;
258 const file = el.files[0];
259 const fieldName = el.name;
260 fd.append(fieldName, new Blob() ,file.name);
261 });
262
263 // add file fields to form-data
264
265 jQuery.ajax({
266 url: wpcf7cf_global_settings.ajaxurl + '?action=wpcf7cf_get_summary',
267 type: 'POST',
268 data: fd,
269 processData: false,
270 contentType: false,
271 dataType: 'json',
272 success: function(json) {
273 $summary.html(json.summaryHtml);
274 }
275 });
276 };
277
278 Wpcf7cfForm.prototype.updateHiddenFields = function() {
279
280 var form = this;
281
282 var hidden_fields = [];
283 var hidden_groups = [];
284 var visible_groups = [];
285
286 form.$groups.each(function () {
287 var $this = jQuery(this);
288 if ($this.hasClass('wpcf7cf-hidden')) {
289 hidden_groups.push($this.data('id'));
290 $this.find('input,select,textarea').each(function () {
291 hidden_fields.push(jQuery(this).attr('name'));
292 });
293 } else {
294 visible_groups.push($this.data('id'));
295 }
296 });
297
298 form.hidden_fields = hidden_fields;
299 form.hidden_groups = hidden_groups;
300 form.visible_groups = visible_groups;
301
302 form.$input_hidden_group_fields.val(JSON.stringify(hidden_fields));
303 form.$input_hidden_groups.val(JSON.stringify(hidden_groups));
304 form.$input_visible_groups.val(JSON.stringify(visible_groups));
305
306 return true;
307 };
308 Wpcf7cfForm.prototype.updateGroups = function() {
309 var form = this;
310 form.$groups = form.$form.find('[data-class="wpcf7cf_group"]');
311
312 form.conditions = window.wpcf7cf.get_nested_conditions(form.initial_conditions, form.$form);
313
314 };
315 Wpcf7cfForm.prototype.updateEventListeners = function() {
316
317 var form = this;
318
319 // monitor input changes, and call displayFields() if something has changed
320 form.get('input, select, textarea, button').not('.wpcf7cf_add, .wpcf7cf_remove').off(wpcf7cf_change_events).on(wpcf7cf_change_events,form, function(e) {
321 var form = e.data;
322 clearTimeout(wpcf7cf_timeout);
323 wpcf7cf_timeout = setTimeout(function() {
324 form.displayFields();
325 }, wpcf7cf_change_time_ms);
326 });
327
328 // PRO ONLY
329 form.get('.wpcf7cf-togglebutton').off('click.toggle_wpcf7cf').on('click.toggle_wpcf7cf',function() {
330 var $this = jQuery(this);
331 if ($this.text() === $this.data('val-1')) {
332 $this.text($this.data('val-2'));
333 $this.val($this.data('val-2'));
334 } else {
335 $this.text($this.data('val-1'));
336 $this.val($this.data('val-1'));
337 }
338 });
339 // END PRO ONLY
340 };
341
342 // PRO ONLY
343 function Wpcf7cfRepeater($repeater, form) {
344 var $ = jQuery;
345
346 var repeater = this;
347
348 var wpcf7cf_settings = form.settings;
349
350 repeater.form = form;
351
352 $repeater.num_subs = 0;
353 $repeater.id = $repeater.data('id');
354 $repeater.orig_id = $repeater.data('orig_data_id');
355 $repeater.min = typeof( $repeater.data('min')) !== 'undefined' ? parseInt($repeater.data('min')) : 1;
356 $repeater.max = typeof( $repeater.data('max')) !== 'undefined' ? parseInt($repeater.data('max')) : 200;
357 $repeater.initial_subs = typeof( $repeater.data('initial')) !== 'undefined' ? parseInt($repeater.data('initial')) : $repeater.min;
358 if ($repeater.initial_subs > $repeater.max) $repeater.initial_subs = $repeater.max;
359 var $repeater_sub = $repeater.children('.wpcf7cf_repeater_sub').eq(0);
360 var $repeater_controls = $repeater.children('.wpcf7cf_repeater_controls').eq(0);
361
362 var $repeater_sub_clone = $repeater_sub.clone();
363
364 $repeater_sub_clone.find('.wpcf7cf_repeater_sub').addBack('.wpcf7cf_repeater_sub').each(function() {
365 var $this = jQuery(this);
366 var prev_suffix = $this.attr('data-repeater_sub_suffix');
367 var new_suffix = prev_suffix+'__{{repeater_sub_suffix}}';
368 $this.attr('data-repeater_sub_suffix', new_suffix);
369 });
370
371 $repeater_sub_clone.find('[name]').each(function() {
372 var $this = jQuery(this);
373 var prev_name = $this.attr('name');
374 var new_name = repeater.getNewName(prev_name);
375
376 var orig_name = $this.attr('data-orig_name') != null ? $this.attr('data-orig_name') : prev_name;
377
378 $this.attr('name', new_name);
379 $this.attr('data-orig_name', orig_name);
380 $this.closest('.wpcf7-form-control-wrap').addClass(new_name.replace('[]',''));
381 });
382
383 $repeater_sub_clone.find('.wpcf7cf_repeater,[data-class="wpcf7cf_group"]').each(function() {
384 var $this = jQuery(this);
385 var prev_data_id = $this.attr('data-id');
386 var orig_data_id = $this.attr('data-orig_data_id') != null ? $this.attr('data-orig_data_id') : prev_data_id;
387 var new_data_id = repeater.getNewName(prev_data_id);
388
389 if(prev_data_id.endsWith('_count')) {
390 new_data_id = prev_data_id.replace('_count','__{{repeater_sub_suffix}}_count');
391 }
392
393 $this.attr('data-id', new_data_id);
394 $this.attr('data-orig_data_id', orig_data_id);
395 });
396
397 $repeater_sub_clone.find('[id]').each(function() {
398 var $this = jQuery(this);
399 var prev_id = $this.attr('id');
400 var orig_id = $this.attr('data-orig_id') != null ? $this.attr('data-orig_id') : prev_id;
401 var new_id = repeater.getNewName(prev_id);
402
403 $this.attr('id', new_id);
404 $this.attr('data-orig_id', orig_id);
405 });
406
407 $repeater_sub_clone.find('[for]').each(function() {
408 var $this = jQuery(this);
409 var prev_for = $this.attr('for');
410 var orig_for = $this.attr('data-orig_for') != null ? $this.attr('data-orig_for') : prev_for;
411 var new_for = repeater.getNewName(prev_for);
412
413 $this.attr('for', new_for);
414 $this.attr('data-orig_for', orig_for);
415 });
416
417 var repeater_sub_html = $repeater_sub_clone[0].outerHTML;
418
419 var $repeater_count_field = $repeater.find('[name='+$repeater.id+'_count]').eq(0);
420 var $button_add = $repeater_controls.find('.wpcf7cf_add').eq(0);
421 var $button_remove = $repeater_controls.find('.wpcf7cf_remove').eq(0);
422
423 var params = {
424 $repeater: $repeater,
425 $repeater_count_field: $repeater_count_field,
426 repeater_sub_html: repeater_sub_html,
427 $repeater_controls: $repeater_controls,
428 $button_add: $button_add,
429 $button_remove: $button_remove,
430 wpcf7cf_settings: wpcf7cf_settings
431 };
432
433 this.params = params;
434
435 $button_add.on('click', null, repeater, function(e) {
436 var repeater = e.data;
437 repeater.updateSubs(params.$repeater.num_subs+1);
438 });
439
440 $button_remove.on('click', null, repeater,function(e) {
441 var repeater = e.data;
442 repeater.updateSubs(params.$repeater.num_subs-1);
443 });
444
445 jQuery('> .wpcf7cf_repeater_sub',params.$repeater).eq(0).remove(); // remove the first sub, it's just a template.
446
447 repeater.updateSubs($repeater.initial_subs);
448
449 }
450
451 Wpcf7cfRepeater.prototype.getNewName = function(previousName) {
452 var prev_parts = previousName.split('[');
453 previousName = prev_parts[0];
454 var prev_suff = prev_parts.length > 1 ? '['+prev_parts.splice(1).join('[') : '';
455 var newName = previousName+'__{{repeater_sub_suffix}}'+prev_suff;
456
457 if(previousName.endsWith('_count')) {
458 newName = previousName.replace('_count','__{{repeater_sub_suffix}}_count');
459 }
460
461 return newName;
462 }
463
464
465 Wpcf7cfRepeater.prototype.updateSubs = function(subs_to_show) {
466 var repeater = this;
467 var params = repeater.params;
468 var subs_to_add = subs_to_show - params.$repeater.num_subs;
469
470 if (subs_to_add < 0) {
471 repeater.removeSubs(-subs_to_add);
472 } else if (subs_to_add > 0) {
473 repeater.addSubs(subs_to_add);
474 }
475
476 var showButtonRemove = false;
477 var showButtonAdd = false;
478
479 if (params.$repeater.num_subs < params.$repeater.max) {
480 showButtonAdd = true;
481 }
482 if (params.$repeater.num_subs > params.$repeater.min) {
483 showButtonRemove = true;
484 }
485
486 if (showButtonAdd) {
487 params.$button_add.show();
488 } else {
489 params.$button_add.hide();
490
491 }
492
493 if (showButtonRemove) {
494 params.$button_remove.show();
495 } else {
496 params.$button_remove.hide();
497 }
498
499 params.$repeater_count_field.val(subs_to_show);
500
501 };
502 Wpcf7cfRepeater.prototype.addSubs = function(subs_to_add) {
503 var $ = jQuery;
504 var params = this.params;
505 var repeater = this;
506 var form = repeater.form;
507
508
509 var $repeater = params.$repeater;
510 var $repeater_controls = params.$repeater_controls;
511
512 //jQuery(params.repeater_sub_html.replace(/name="(.*?)"/g,'name="wpcf7cf_repeater['+$repeater.id+']['+$repeater.num_subs+'][$1]" data-original-name="$1"')).hide().insertBefore($repeater_controls).animate(wpcf7cf_show_animation, params.wpcf7cf_settings.animation_intime);
513
514 var html_str = '';
515
516 for(var i=1; i<=subs_to_add; i++) {
517 var sub_suffix = $repeater.num_subs+i;
518 html_str += params.repeater_sub_html.replace(/\{\{repeater_sub_suffix\}\}/g,sub_suffix)
519 .replace(new RegExp('\{\{'+$repeater.orig_id+'_index\}\}','g'),sub_suffix);
520 }
521
522
523 var $html = jQuery(html_str);
524
525 // Add the newly created fields to the form
526 $html.hide().insertBefore($repeater_controls).animate(wpcf7cf_show_animation, params.wpcf7cf_settings.animation_intime).trigger('wpcf7cf_repeater_added');
527
528 jQuery('.wpcf7cf_repeater', $html).each(function(){
529 form.repeaters.push(new Wpcf7cfRepeater(jQuery(this),form));
530 });
531 form.$input_repeaters.val(JSON.stringify(form.repeaters.map((item)=>item.params.$repeater.id)));
532
533 $repeater.num_subs+= subs_to_add;
534
535 window.wpcf7cf.updateMultistepState(form.multistep);
536 form.updateGroups();
537 form.updateEventListeners();
538 form.displayFields();
539
540 // Exclusive Checkbox
541 $html.on( 'click', '.wpcf7-exclusive-checkbox input:checkbox', function() {
542 var name = $( this ).attr( 'name' );
543 $html.find( 'input:checkbox[name="' + name + '"]' ).not( this ).prop( 'checked', false );
544 } );
545
546 //basic compatibility with material-design-for-contact-form-7
547 if (typeof window.cf7mdInit === "function") {
548 window.cf7mdInit();
549 }
550
551 return false;
552 };
553 Wpcf7cfRepeater.prototype.removeSubs = function(num_subs) {
554 var $ = jQuery;
555 var params = this.params;
556 var form = this.form;
557
558 params.$repeater.num_subs-= num_subs;
559
560 jQuery('> .wpcf7cf_repeater_sub',params.$repeater).slice(-num_subs).animate(wpcf7cf_hide_animation, {duration:params.wpcf7cf_settings.animation_intime, done:function() {
561 var $this = jQuery(this);
562 //remove the actual fields from the form
563 $this.remove();
564 params.$repeater.trigger('wpcf7cf_repeater_removed');
565 window.wpcf7cf.updateMultistepState(form.multistep);
566 form.updateGroups();
567 form.updateEventListeners();
568 form.displayFields();
569 }});
570
571 return false;
572 };
573
574 function Wpcf7cfMultistep($multistep, form) {
575 var multistep = this;
576 multistep.$multistep = $multistep;
577 multistep.form = form;
578 multistep.$steps = $multistep.find('.wpcf7cf_step');
579 multistep.$btn_next = $multistep.find('.wpcf7cf_next');
580 multistep.$btn_prev = $multistep.find('.wpcf7cf_prev');
581 multistep.$dots = $multistep.find('.wpcf7cf_steps-dots');
582 multistep.currentStep = 0;
583 multistep.numSteps = multistep.$steps.length;
584
585
586 multistep.$dots.html('');
587 for (var i = 1; i <= multistep.numSteps; i++) {
588 multistep.$dots.append(`
589 <div class="dot" data-step="${i}">
590 <div class="step-index">${i}</div>
591 <div class="step-title">${multistep.$steps.eq(i-1).data('title')}</div>
592 </div>
593 `);
594 }
595
596 multistep.$btn_next.on('click.wpcf7cf_step', async function() {
597
598 var result = await multistep.validateStep(multistep.currentStep);
599 if (result === 'success') {
600 multistep.moveToStep(multistep.currentStep+1);
601 }
602
603 });
604
605 // If form is submitted (by pressing Enter for example), and if we are not on the last step,
606 // then trigger click event on the $btn_next button instead.
607 multistep.form.$form.on('submit.wpcf7cf_step', function(e) {
608
609 if (multistep.currentStep !== multistep.numSteps) {
610 multistep.$btn_next.trigger('click.wpcf7cf_step');
611
612 e.stopImmediatePropagation();
613 return false;
614 }
615 });
616
617 multistep.$btn_prev.on( 'click', function() {
618 multistep.moveToStep(multistep.currentStep-1);
619 });
620
621 multistep.moveToStep(1);
622 }
623
624 jQuery(document).ajaxComplete(function(e, xhr, settings){
625 if (
626 xhr.hasOwnProperty('responseJSON') &&
627 xhr.responseJSON != null &&
628 xhr.responseJSON.hasOwnProperty('status') &&
629 xhr.responseJSON.hasOwnProperty('into') &&
630 xhr.responseJSON.status === "mail_success"
631 ) {
632 jQuery( xhr.responseJSON.into ).trigger('reset.wpcf7cf');
633 }
634 });
635
636 Wpcf7cfMultistep.prototype.validateStep = function(step_index) {
637
638 var multistep = this;
639 var $multistep = multistep.$multistep;
640 var $form = multistep.form.$form;
641 var form = multistep.form;
642
643 $form.find('.wpcf7-response-output').addClass('wpcf7-display-none');
644
645 return new Promise(resolve => {
646
647 var fd = new FormData();
648
649 // Make sure to add file fields to FormData
650 jQuery.each($form.find('[data-id="step-'+step_index+'"] input[type="file"]'), function(index, el) {
651 if (! el.files.length) return false;
652 const file = el.files[0];
653 const fieldName = el.name;
654 fd.append(fieldName, file);
655 });
656
657 var formdata = $form.serializeArray();
658 jQuery.each(formdata,function(key, input){
659 fd.append(input.name, input.value);
660 });
661
662 jQuery.ajax({
663 url: wpcf7cf_global_settings.ajaxurl + '?action=wpcf7cf_validate_step',
664 type: 'POST',
665 data: fd,
666 processData: false,
667 contentType: false,
668 dataType: 'json',
669 }).done(function(json) {
670
671 $multistep.find('.wpcf7-form-control-wrap .wpcf7-not-valid-tip').remove();
672 $multistep.find('.wpcf7-not-valid').removeClass('wpcf7-not-valid');
673 $multistep.find('.wpcf7-response-output').remove();
674 $multistep.find('.wpcf7-response-output.wpcf7-validation-errors').removeClass('wpcf7-validation-errors');
675
676 if (!json.success) {
677 var checkError = 0;
678
679 jQuery.each(json.invalid_fields, function(index, el) {
680 if ($multistep.find('input[name="'+index+'"]').length ||
681 $multistep.find('input[name="'+index+'[]"]').length ||
682 $multistep.find('select[name="'+index+'"]').length ||
683 $multistep.find('select[name="'+index+'[]"]').length ||
684 $multistep.find('textarea[name="'+index+'"]').length ||
685 $multistep.find('textarea[name="'+index+'[]"]').length
686 ) {
687 checkError = checkError + 1;
688
689 var controlWrap = form.get('.wpcf7-form-control-wrap.' + index);
690 controlWrap.find('.wpcf7-form-control').addClass('wpcf7-not-valid');
691 controlWrap.find('span.wpcf7-not-valid-tip').remove();
692 controlWrap.append('<span role="alert" class="wpcf7-not-valid-tip">' + el.reason + '</span>');
693
694 }
695 });
696
697 resolve('failed');
698
699 $multistep.parent().find('.wpcf7-response-output').removeClass('wpcf7-display-none').html(json.message);
700
701 } else if (json.success) {
702 resolve('success');
703 return false;
704 }
705
706 }).fail(function() {
707 resolve('error');
708 }).always(function() {
709 // do nothing
710 });
711 });
712
713 };
714 Wpcf7cfMultistep.prototype.moveToStep = function(step_index) {
715 var multistep = this;
716 var previousStep = multistep.currentStep;
717
718 multistep.currentStep = step_index > multistep.numSteps ? multistep.numSteps
719 : step_index < 1 ? 1
720 : step_index;
721
722 // ANIMATION DISABLED FOR NOW cause it's ugly
723 // multistep.$steps.animate(wpcf7cf_hide_step_animation, multistep.form.settings.animation_outtime);
724 // multistep.$steps.eq(multistep.currentStep-1).animate(wpcf7cf_show_step_animation, multistep.form.settings.animation_intime);
725
726 multistep.$multistep.attr('data-current_step', multistep.currentStep);
727 multistep.$steps.hide();
728 multistep.$steps
729 .eq(multistep.currentStep-1)
730 .show()
731 .trigger('wpcf7cf_change_step', [previousStep, multistep.currentStep]);
732
733 const formEl = multistep.form.$form[0];
734 const topOffset = formEl.getBoundingClientRect().top;
735 if (topOffset < 0 && previousStep > 0) {
736 formEl.scrollIntoView({behavior: "smooth"});
737 }
738
739 multistep.form.updateSummaryFields();
740
741 window.wpcf7cf.updateMultistepState(multistep);
742 };
743
744 Wpcf7cfMultistep.prototype.getFieldsInStep = function(step_index) {
745 wpcf7cf_reload_dom(this.form.$form);
746 var inStep = false;
747 return Object.values(wpcf7cf_dom).filter(function(item, i) {
748 if(item.type == 'step') {
749 inStep = item.val == step_index+'';
750 }
751 return inStep && item.type == 'input';
752 }).map(function(item) {
753 return item.name;
754 });
755 };
756
757 // END PRO ONLY
758
759 window.wpcf7cf = {
760
761 // keep this for backwards compatibility
762 initForm : function($forms) {
763 $forms.each(function(){
764 const $form = jQuery(this);
765 // only add form is its class is "wpcf7-form" and if the form was not previously added
766 if (
767 $form.hasClass('wpcf7-form') &&
768 !wpcf7cf_forms.some((form)=>{ return form.$form.get(0) === $form.get(0); })
769 ) {
770 wpcf7cf_forms.push(new Wpcf7cfForm($form));
771 }
772 });
773 },
774
775 get_nested_conditions : function(conditions, $current_form) {
776 //loop trough conditions. Then loop trough the dom, and each repeater we pass we should update all sub_values we encounter with __index
777 wpcf7cf_reload_dom($current_form);
778 var groups = Object.values(wpcf7cf_dom).filter(function(item, i) {
779 return item.type==='group';
780 });
781
782 var sub_conditions = [];
783
784 for(var i = 0; i < groups.length; i++) {
785 var g = groups[i];
786 var relevant_conditions = conditions.filter(function(condition, i) {
787 return condition.then_field === g.original_name;
788 });
789
790 var relevant_conditions = relevant_conditions.map(function(item,i) {
791 return {
792 then_field : g.name,
793 and_rules : item.and_rules.map(function(and_rule, i) {
794 return {
795 if_field : and_rule.if_field+g.suffix,
796 if_value : and_rule.if_value,
797 operator : and_rule.operator
798 };
799 })
800 }
801 });
802
803 sub_conditions = sub_conditions.concat(relevant_conditions);
804 }
805 return sub_conditions;
806 },
807
808 get_simplified_dom_model : function($current_form) {
809
810 var currentNode;
811 var ni = document.createNodeIterator($current_form[0], NodeFilter.SHOW_ELEMENT, null, false); //, NodeFilter.SHOW_ELEMENT, function(){ return NodeFilter.FILTER_ACCEPT; }
812
813 var simplified_dom = {};
814
815 while(currentNode = ni.nextNode()) {
816
817 const type = currentNode.classList.contains('wpcf7cf_repeater') ? 'repeater' :
818 currentNode.dataset.class == 'wpcf7cf_group' ? 'group' :
819 currentNode.className == 'wpcf7cf_step' ? 'step' :
820 currentNode.hasAttribute('name') ? 'input' : false;
821
822 if (!type) {
823 continue;
824 }
825
826 const name = type === 'input' ? currentNode.getAttribute('name') : currentNode.dataset.id;
827
828 // skip _wpcf7 hidden fields
829 if (name.substring(0,6) === '_wpcf7') continue;
830
831 const original_name = type === 'repeater' || type === 'group' ? currentNode.dataset.orig_data_id
832 : type === 'input' ? (currentNode.getAttribute('data-orig_name') || name)
833 : name;
834
835 const val = type === 'step' ? [currentNode.dataset.id.substring(5)] : [];
836
837 const original_name_length = original_name == null ? name.length : original_name.length;
838 const suffix = name.substring(original_name_length);
839
840 if (!simplified_dom[name]) {
841 // init entry
842 simplified_dom[name] = {name, type, original_name, suffix, val}
843 }
844
845 if (type === 'input') {
846
847 // skip unchecked checkboxes and radiobuttons
848 if ( (currentNode.type === 'checkbox' || currentNode.type === 'radio') && !currentNode.checked ) continue;
849
850 // if multiselect, make sure to add all the values
851 if ( currentNode.multiple && currentNode.options ) {
852 simplified_dom[name].val = Object.values(currentNode.options).filter(o => o.selected).map(o => o.value)
853 } else {
854 simplified_dom[name].val.push(currentNode.value);
855 }
856 }
857
858 }
859
860 return simplified_dom;
861 },
862
863 updateMultistepState: function (multistep) {
864 if (multistep == null) return;
865
866 // update hidden input field
867
868 var stepsData = {
869 currentStep : multistep.currentStep,
870 numSteps : multistep.numSteps,
871 fieldsInCurrentStep : multistep.getFieldsInStep(multistep.currentStep)
872 };
873 multistep.form.$input_steps.val(JSON.stringify(stepsData));
874
875 // update Buttons
876 multistep.$btn_prev.removeClass('disabled').attr('disabled', false);
877 multistep.$btn_next.removeClass('disabled').attr('disabled', false);
878 if (multistep.currentStep == multistep.numSteps) {
879 multistep.$btn_next.addClass('disabled').attr('disabled', true);
880 }
881 if (multistep.currentStep == 1) {
882 multistep.$btn_prev.addClass('disabled').attr('disabled', true);
883 }
884
885 // replace next button with submit button on last step.
886 // TODO: make this depend on a setting
887 var $submit_button = multistep.form.$form.find('input[type="submit"]').eq(0);
888 var $ajax_loader = multistep.form.$form.find('.ajax-loader').eq(0);
889 if (multistep.currentStep == multistep.numSteps) {
890 multistep.$btn_next.hide();
891 $ajax_loader.detach().appendTo(multistep.$btn_next.parent());
892 $submit_button.detach().appendTo(multistep.$btn_next.parent());
893 $submit_button.show();
894 } else {
895 $submit_button.hide();
896 multistep.$btn_next.show();
897 }
898
899 // update dots
900 var $dots = multistep.$dots.find('.dot');
901 $dots.removeClass('active').removeClass('completed');
902 for(var step = 1; step <= multistep.numSteps; step++) {
903 if (step < multistep.currentStep) {
904 $dots.eq(step-1).addClass('completed');
905 } else if (step == multistep.currentStep) {
906 $dots.eq(step-1).addClass('active');
907 }
908 }
909
910 },
911
912 should_group_be_shown : function(condition) {
913
914 var show_group = true;
915
916 for (var and_rule_i = 0; and_rule_i < condition.and_rules.length; and_rule_i++) {
917
918 var condition_ok = false;
919
920 var condition_and_rule = condition.and_rules[and_rule_i];
921
922 var inputField = wpcf7cf_getFieldByName(condition_and_rule.if_field);
923
924 if (!inputField) continue; // field not found
925
926 var if_val = condition_and_rule.if_value;
927 var operator = condition_and_rule.operator;
928
929 //backwards compat
930 operator = operator === '' ? 'less than or equals' : operator;
931 operator = operator === '' ? 'greater than or equals' : operator;
932 operator = operator === '>' ? 'greater than' : operator;
933 operator = operator === '<' ? 'less than' : operator;
934
935 const $field = operator === 'function' && jQuery(`[name="${inputField.name}"]`).eq(0);
936
937 condition_ok = this.isConditionTrue(inputField.val,operator,if_val, $field);
938
939 show_group = show_group && condition_ok;
940 }
941
942 return show_group;
943
944 },
945 isConditionTrue(values, operator, testValue='', $field=jQuery()) {
946
947 if (!Array.isArray(values)) {
948 values = [values];
949 }
950
951 let condition_ok = false; // start by assuming that the condition is not met
952
953 // Considered EMPTY: [] [''] [null] ['',null] [,,'']
954 // Considered NOT EMPTY: [0] ['ab','c'] ['',0,null]
955 const valuesAreEmpty = values.length === 0 || values.every((v) => !v&&v!==0); // 0 is not considered empty
956
957 // special cases: [] equals '' => TRUE; [] not equals '' => FALSE
958 if (operator === 'equals' && testValue === '' && valuesAreEmpty) {
959 return true;
960 }
961 if (operator === 'not equals' && testValue === '' && valuesAreEmpty) {
962 return false;
963 }
964
965 if (valuesAreEmpty) {
966 if (operator === 'is empty') {
967 condition_ok = true;
968 }
969 } else {
970 if (operator === 'not empty') {
971 condition_ok = true;
972 }
973 }
974
975 const testValueNumber = isFinite(parseFloat(testValue)) ? parseFloat(testValue) : NaN;
976
977
978 if (operator === 'not equals' || operator === 'not equals (regex)') {
979 // start by assuming that the condition is met
980 condition_ok = true;
981 }
982
983 if (
984 operator === 'function'
985 && typeof window[testValue] == 'function'
986 && window[testValue]($field) // here we call the actual user defined function
987 ) {
988 condition_ok = true;
989 }
990
991 let regex_patt = /.*/i; // fallback regex pattern
992 let isValidRegex = true;
993 if (operator === 'equals (regex)' || operator === 'not equals (regex)') {
994 try {
995 regex_patt = new RegExp(testValue, 'i');
996 } catch(e) {
997 isValidRegex = false;
998 }
999 }
1000
1001
1002 for(let i = 0; i < values.length; i++) {
1003
1004 const value = values[i];
1005
1006 const valueNumber = isFinite(parseFloat(value)) ? parseFloat(value) : NaN;
1007 const valsAreNumbers = !isNaN(valueNumber) && !isNaN(testValueNumber);
1008
1009 if (
1010
1011 operator === 'equals' && value === testValue ||
1012 operator === 'equals (regex)' && regex_patt.test(value) ||
1013 operator === 'greater than' && valsAreNumbers && valueNumber > testValueNumber ||
1014 operator === 'less than' && valsAreNumbers && valueNumber < testValueNumber ||
1015 operator === 'greater than or equals' && valsAreNumbers && valueNumber >= testValueNumber ||
1016 operator === 'less than or equals' && valsAreNumbers && valueNumber <= testValueNumber
1017
1018 ) {
1019
1020 condition_ok = true;
1021 break;
1022
1023 } else if (
1024
1025 operator === 'not equals' && value === testValue ||
1026 operator === 'not equals (regex)' && regex_patt.test(value)
1027
1028 ) {
1029
1030 condition_ok = false;
1031 break;
1032
1033 }
1034 }
1035
1036 return condition_ok;
1037
1038 }
1039
1040 };
1041
1042 jQuery('.wpcf7-form').each(function(){
1043 wpcf7cf_forms.push(new Wpcf7cfForm(jQuery(this)));
1044 });
1045
1046 // Call displayFields again on all forms
1047 // Necessary in case some theme or plugin changed a form value by the time the entire page is fully loaded.
1048 jQuery('document').ready(function() {
1049 wpcf7cf_forms.forEach(function(f){
1050 f.displayFields();
1051 });
1052 });
1053
1054 // fix for exclusive checkboxes in IE (this will call the change-event again after all other checkboxes are unchecked, triggering the display_fields() function)
1055 var old_wpcf7ExclusiveCheckbox = jQuery.fn.wpcf7ExclusiveCheckbox;
1056 jQuery.fn.wpcf7ExclusiveCheckbox = function() {
1057 return this.find('input:checkbox').on('click', function() {
1058 var name = jQuery(this).attr('name');
1059 jQuery(this).closest('form').find('input:checkbox[name="' + name + '"]').not(this).prop('checked', false).eq(0).change();
1060 });
1061 };
1062
1063