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