PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 2.1.5
JetFormBuilder — Dynamic Blocks Form Builder v2.1.5
3.6.5.2 3.6.5.1 3.6.5 3.6.4.2 3.6.4.1 3.6.4 3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / assets / js / frontend-forms.js
jetformbuilder / assets / js Last commit date
admin 4 years ago action-localize-helper.js 4 years ago admin-package.js 4 years ago admin-package.js.LICENSE.txt 4 years ago admin-package.js.map 4 years ago admin-vuex-package.js 4 years ago admin-vuex-package.js.map 4 years ago editor.js 4 years ago editor.js.map 4 years ago file-upload.js 4 years ago form-block.js 4 years ago form-block.js.map 4 years ago frontend-forms.js 4 years ago import-form.js 4 years ago map-field.js 4 years ago package.js 4 years ago package.js.map 4 years ago re-captcha-v3.js 4 years ago
frontend-forms.js
1806 lines
1 ( function( $ ) {
2
3 'use strict';
4
5 const { addAction, doAction, applyFilters } = wp.hooks;
6
7 const JetFormBuilderDev = {
8 isActive: function() {
9 return Boolean( window.JetFormBuilderSettings.devmode );
10 },
11 log: function( label = '', params = {} ) {
12 if ( ! this.isActive() || ! params ) {
13 return;
14 }
15 console.group( label );
16 for ( const key in params ) {
17 console.log( `${ key }: ${ params[ key ] }` );
18 }
19 console.groupEnd();
20 },
21 hardLog: function( label = '', params = {} ) {
22 this.log( label, params );
23 if ( this.isActive() ) {
24 debugger;
25 }
26 },
27 }
28
29 window.JetFormBuilderMain = {
30
31 filters: ( function() {
32
33 var callbacks = {};
34
35 return {
36
37 addFilter: function( name, callback ) {
38
39 if ( ! callbacks.hasOwnProperty( name ) ) {
40 callbacks[ name ] = [];
41 }
42
43 callbacks[ name ].push( callback );
44
45 },
46
47 applyFilters: function( name, value, args ) {
48
49 if ( ! callbacks.hasOwnProperty( name ) ) {
50 return value;
51 }
52
53 if ( args === undefined ) {
54 args = [];
55 }
56
57 var container = callbacks[ name ];
58 var cbLen = container.length;
59
60 for ( var i = 0; i < cbLen; i++ ) {
61 if ( typeof container[ i ] === 'function' ) {
62 value = container[ i ]( value, args );
63 }
64 }
65
66 return value;
67 },
68
69 };
70
71 } )(),
72
73 };
74
75 $.fn.jetFormBuilderConditional = function( options ) {
76
77 this.JFBroot = this.closest( '.jet-form-builder-repeater__row-fields' );
78
79 if ( ! this.JFBroot.length ) {
80 this.JFBroot = this.closest( 'form.jet-form-builder' );
81 } else {
82 this.JFBparent = this.closest( 'form.jet-form-builder' );
83 }
84
85 const current = this;
86
87 var settings = $.extend( {
88 hideJS: true,
89 }, options );
90
91 var checkValue = function( $listenTo, listenFor, operator ) {
92
93 var val = '';
94 var checkResult = false;
95 var controlType = 'plain';
96
97 operator = operator || 'equal';
98
99 if ( $listenTo.is( 'input[type=checkbox]' ) ) {
100 controlType = 'checkbox';
101 } else if ( $listenTo.is( 'input[type=radio]' ) ) {
102 controlType = 'radio';
103 }
104
105 if ( 'checkbox' === controlType ) {
106 val = [];
107 }
108
109 if ( 'plain' === controlType ) {
110 val = $listenTo.val();
111 } else {
112
113 $listenTo.each( function() {
114
115 var $control = $( this );
116
117 if ( $control.is( ':checked' ) ) {
118 if ( 'checkbox' === controlType ) {
119 val.push( $control.val() );
120 } else {
121 val = $control.val();
122 }
123 }
124
125 } );
126 }
127
128 switch ( operator ) {
129 case 'equal':
130 if ( val && val.constructor === Array ) {
131 checkResult = false;
132 } else {
133 checkResult = ( val == listenFor );
134 }
135 break;
136
137 case 'greater':
138 if ( val && val.constructor === Array ) {
139 checkResult = false;
140 } else {
141 checkResult = ( parseFloat( val ) > parseFloat( listenFor ) );
142 }
143 break;
144
145 case 'less':
146 if ( val && val.constructor === Array ) {
147 checkResult = false;
148 } else {
149 checkResult = ( parseFloat( val ) < parseFloat( listenFor ) );
150 }
151 break;
152
153 case 'between':
154
155 if ( val && val.constructor === Array ) {
156 checkResult = false;
157 } else {
158 if ( 2 <= listenFor.length ) {
159 let from = parseFloat( listenFor[ 0 ] );
160 let to = parseFloat( listenFor[ 1 ] );
161 val = parseFloat( val );
162 checkResult = ( from <= val && val <= to );
163 } else {
164 checkResult = false;
165 }
166 }
167
168 break;
169
170 case 'one_of':
171 if ( val && val.constructor === Array ) {
172 checkResult = false;
173
174 for ( const valElement of val ) {
175 if ( listenFor.length && 0 <= listenFor.indexOf( valElement ) ) {
176 checkResult = true;
177 }
178 }
179
180 } else if ( ! val ) {
181 checkResult = false;
182 } else {
183 if ( listenFor.length ) {
184 checkResult = 0 <= listenFor.indexOf( val );
185 } else {
186 checkResult = false;
187 }
188 }
189
190 break;
191
192 case 'contain':
193 if ( val && val.constructor === Array ) {
194
195 var intersect = val.filter( function( n ) {
196 return n.indexOf( listenFor ) !== -1;
197 } );
198
199 checkResult = 0 < intersect.length;
200
201 } else if ( ! val ) {
202 checkResult = false;
203 } else {
204 checkResult = 0 <= val.indexOf( listenFor );
205 }
206
207 break;
208 }
209
210 return checkResult;
211 };
212
213 var checkVisibilityCond = ( listenTo, listenFor, $section, operator, type ) => {
214
215 let $listenTo = $( listenTo, current.JFBroot );
216
217 if ( ! $listenTo.length && current?.JFBparent?.length ) {
218 $listenTo = $( listenTo, current.JFBparent );
219 }
220
221 var checkResult = checkValue( $listenTo, listenFor, operator );
222
223 type = type || 'show';
224
225 $section.JFBchecked = $section.JFBchecked ?? {};
226
227 if ( 'show' === type ) {
228 $section.JFBchecked[ listenTo ] = checkResult;
229 } else {
230 $section.JFBchecked[ listenTo ] = ! checkResult;
231 }
232 };
233
234 var checkSetValueCond = function( listenTo, listenFor, $section, operator, value, type ) {
235
236 var currentVal = $section.data( 'result_' + type );
237 var $listenTo = $( listenTo );
238 var checkResult = checkValue( $listenTo, listenFor, operator );
239
240 if ( checkResult ) {
241 currentVal = value;
242 }
243
244 $section.data( 'result_' + type, currentVal );
245
246 };
247
248 var setValue = function( $section ) {
249
250 var setVal = false;
251 var setCalcVal = false;
252 var $field;
253 var triggered = false;
254
255 if ( $section.data( 'result_set_value' ) ) {
256 setVal = $section.data( 'result_set_value' );
257 }
258
259 if ( $section.data( 'result_set_calculated_value' ) ) {
260 setCalcVal = $section.data( 'result_set_calculated_value' );
261 }
262
263 if ( ! setVal && ! setCalcVal ) {
264 return;
265 }
266
267 $field = $section.find( '.jet-form-builder__field' );
268
269 if ( ! $field.length ) {
270 return;
271 }
272
273 if ( $field.is( 'select' ) ) {
274
275 $field.find( ':selected' ).removeAttr( 'selected' );
276
277 if ( setVal ) {
278 $field.find( 'option[value="' + setVal + '"]' ).attr( 'selected', 'selected' ).trigger( 'change.JetFormBuilderMain' );
279 triggered = true;
280 }
281
282 if ( setCalcVal ) {
283 $field.find( 'option[data-calculate="' + setCalcVal + '"]' ).attr( 'selected', 'selected' );
284 if ( ! triggered ) {
285 $field.trigger( 'change.JetFormBuilderMain' );
286 triggered = true;
287 }
288 }
289
290 } else if ( $field.is( ':not( input[type=checkbox], input[type=radio] )' ) ) {
291
292 if ( setVal ) {
293 $field.val( setVal ).trigger( 'change.JetFormBuilderMain' );
294 triggered = true;
295 }
296
297 if ( setCalcVal ) {
298 $field.data( 'calculate', setCalcVal );
299 if ( ! triggered ) {
300 $field.trigger( 'change.JetFormBuilderMain' );
301 triggered = true;
302 }
303 }
304
305 } else {
306
307 $field.each( function() {
308
309 var $this = $( this );
310
311 if ( $this.is( ':checked' ) ) {
312 $this.removeAttr( 'checked' );
313 }
314
315 if ( setVal && setVal == $this.val() ) {
316 $this.attr( 'checked', 'checked' ).trigger( 'change.JetFormBuilderMain' );
317 triggered = true;
318 }
319
320 if ( setCalcVal && setCalcVal == $this.data( 'calculate' ) ) {
321 $this.attr( 'checked', 'checked' );
322 if ( ! triggered ) {
323 $this.trigger( 'change.JetFormBuilderMain' );
324 triggered = true;
325 }
326 }
327
328 } );
329
330 }
331
332 };
333
334 var setVisibility = $section => {
335 //var $row = $section.closest( '.jet-form-builder-row' );
336 let res = true;
337
338 if ( ! Object.keys( $section?.JFBchecked ?? {} )?.length ) {
339 return;
340 }
341
342 for ( let check in $section.JFBchecked ) {
343 if ( ! $section.JFBchecked[ check ] ) {
344 res = false;
345 }
346 }
347
348 if ( res ) {
349
350 $section.show();
351 //$row.show();
352
353 $section.find( '*[data-initial-type]' ).each( function() {
354 var $this = $( this );
355
356 $this.attr( 'type', $this.data( 'initial-type' ) );
357 } );
358
359 $section.find( 'select option[data-is-hidden="1"]' ).remove();
360
361 $section
362 .find( '*[data-required="1"]' )
363 .attr( 'required', 'required' );
364
365 } else {
366
367 $section.hide();
368
369 $section.find( '*[type="date"],*[type="time"],*[type="email"],*[type="url"]' ).each( function() {
370 var $this = $( this ),
371 type = $this.attr( 'type' );
372
373 $this.attr( 'data-initial-type', type );
374 $this.attr( 'type', 'text' );
375 } );
376
377 var $select = $section.find( 'select' );
378
379 if ( $select.length ) {
380 $select.append( '<option value="" data-is-hidden="1"></option>' );
381 }
382
383 $section.find( '*[required]' )
384 .removeAttr( 'required' )
385 .attr( 'data-required', 1 );
386
387 /*var $hiddenItems = $row.find( '>*' ).filter( function() {
388 return $( this ).css( 'display' ) === 'none';
389 } );
390
391 if ( $row.find( '>*' ).length === $hiddenItems.length ) {
392 $row.hide();
393 }*/
394 }
395
396 };
397
398 return this.each( function() {
399
400 var $section = $( this );
401 var conditions = $section.data( 'conditional' );
402
403 if ( ! conditions || ! conditions.length ) {
404 return;
405 }
406
407 for ( var i = 0; i < conditions.length; i++ ) {
408
409 let condition = conditions[ i ];
410
411 if ( ! condition.field ) {
412 continue;
413 }
414
415 let listenTo = '[data-field-name=' + condition.field + ']';
416 let listenFor = condition.value;
417 let operator = condition.operator;
418 let type = condition.type;
419 let valueToSet = condition.set_value;
420
421 let field = current.JFBroot.find( listenTo );
422
423 if ( ! field?.length && current?.JFBparent?.length ) {
424 field = current.JFBparent.find( listenTo );
425 }
426
427 field.each( function() {
428 $( this ).on( 'change.JetFormBuilderMain', function() {
429 if ( 'show' === type || 'hide' === type ) {
430 checkVisibilityCond( listenTo, listenFor, $section, operator, type );
431 } else {
432 checkSetValueCond( listenTo, listenFor, $section, operator, valueToSet, type );
433 }
434
435 setValue( $section );
436 setVisibility( $section );
437 });
438 } );
439
440 //If setting was chosen, hide everything first...
441 if ( settings.hideJS && ( 'show' === type || 'hide' === type ) ) {
442 $section.hide();
443 }
444
445 //Show based on current value on page load
446 if ( 'show' === type || 'hide' === type ) {
447 checkVisibilityCond( listenTo, listenFor, $section, operator, type );
448 } else {
449 checkSetValueCond( listenTo, listenFor, $section, operator, valueToSet, type );
450 }
451
452 }
453
454 setValue( $section );
455 setVisibility( $section );
456 } );
457 };
458
459 var JetFormBuilder = {
460
461 pages: {},
462 calcFields: {},
463 repeaterCalcFields: {},
464 childrenCalcFields: {},
465 currentFieldWithError: {
466 length: 0,
467 },
468 initCommon: function( $scope = false ) {
469 let wrappers = $( '.jet-fb-form-block', $scope );
470
471 wrappers.each( function( index, value ) {
472 JetFormBuilder.widgetBookingForm( $( value ) );
473 } );
474 },
475
476 initElementor: function() {
477 const widgets = {
478 'jet-engine-booking-form.default': JetFormBuilder.widgetBookingForm,
479 'jet-form-builder-form.default': JetFormBuilder.widgetBookingForm,
480 };
481
482 $.each( widgets, function( widget, callback ) {
483 window.elementorFrontend.hooks.addAction( 'frontend/element_ready/' + widget, callback );
484 } );
485 },
486
487 addHandlersInit: function() {
488 var self = JetFormBuilder;
489
490 $( document )
491 .on( 'submit.JetFormBuilderMain', 'form.jet-form-builder.submit-type-ajax', self.ajaxSubmitForm )
492 .on( 'submit.JetFormBuilderMain', 'form.jet-form-builder.submit-type-reload', self.reloadSubmitForm )
493 .on( 'click.JetFormBuilderMain', '.jet-form-builder__next-page', self.nextFormPage )
494 .on( 'click.JetFormBuilderMain', '.jet-form-builder__prev-page', self.prevFormPage )
495 .on( 'focus.JetFormBuilderMain', '.jet-form-builder__field', self.clearFieldErrors )
496 .on( 'click.JetFormBuilderMain', '.jet-form-builder__field-template', self.simLabelClick )
497 .on( 'change.JetFormBuilderMain', '.jet-form-builder__field', self.recalcFields )
498 .on( 'change.JetFormBuilderMain', '.jet-form-builder__field', self.maybeInitSinglePage )
499 .on( 'jet-form-builder/repeater-changed', '.jet-form-builder-repeater', self.recalcFields )
500 .on( 'change.JetFormBuilderMain', '.jet-form-builder__field.checkboxes-group-required', self.requiredCheckboxGroup )
501 .on( 'change.JetFormBuilderMain', '.checkradio-field', self.changeActiveTemplateClass )
502 .on( 'input.JetFormBuilderMain/range', '.jet-form-builder__field.range-field', self.updateRangeField )
503 .on( 'click.JetFormBuilderMain', '.jet-form-builder-repeater__new', self.newRepeaterItem )
504 .on( 'click.JetFormBuilderMain', '.jet-form-builder-repeater__remove', self.removeRepeaterItem )
505 .on( 'jet-form-builder/page/field-changed', self.maybeSwitchPage )
506 .on( 'jet-form-builder/switch-page', self.updateProgress )
507 .on( 'input.JetFormBuilderMain', '.jet-form-builder__field.text-field, .jet-form-builder__field.textarea-field', self.inputTextFields )
508 },
509
510 maybeInitSinglePage: function () {
511 const self = $( this ),
512 $page = self.closest( '.jet-form-builder-page' );
513
514 if ( ! $page.length ) {
515 return
516 }
517 const $form = self.closest( 'form.jet-form-builder' );
518
519 JetFormBuilder.initSingleFormPage( $page, $form, self );
520 },
521
522 inputTextFields: function() {
523 $( this ).trigger( 'change.JetFormBuilderMain' );
524 },
525
526 updateProgress: function( event, $fromPage, $toPage, $progress ) {
527 if ( ! $progress || 'default' !== $progress.data( 'type' ) ) {
528 return;
529 }
530 const [ from, to ] = [ $fromPage.data( 'page' ), $toPage.data( 'page' ) ];
531
532 const prevItem = $progress.find( `.jet-form-builder-progress-pages__item--wrapper[data-page="${ from }"]` );
533 const currentItem = $progress.find( `.jet-form-builder-progress-pages__item--wrapper[data-page="${ to }"]` );
534
535 prevItem.removeClass( 'active-page' );
536 currentItem.addClass( 'active-page' );
537 currentItem.removeClass( 'passed-page' );
538
539 if ( from < to ) {
540 prevItem.addClass( 'passed-page' );
541 } else {
542 prevItem.removeClass( 'passed-page' );
543 }
544 },
545
546 removeRepeaterItem: function() {
547
548 let $this = $( this ),
549 $repeater = $this.closest( '.jet-form-builder-repeater' ),
550 $repeaterItem = $this.closest( '.jet-form-builder-repeater__row' ),
551 $editor = $repeaterItem.find( '.wp-editor-area' );
552
553 $this.trigger( 'jet-form-builder/on-remove-repeater-item' );
554
555 if ( $editor.length && window.wp && window.wp.editor ) {
556 window.wp.editor.remove( $editor.attr( 'id' ) );
557 }
558
559 $repeaterItem.remove();
560 $repeater.trigger( 'jet-form-builder/repeater-changed' );
561
562 },
563
564 newRepeaterItem: function() {
565 var $this = $( this ),
566 $repeater = $this.closest( '.jet-form-builder-repeater' ),
567 $initial = $repeater.find( '.jet-form-builder-repeater__initial' ),
568 $items = $repeater.find( '.jet-form-builder-repeater__items' ),
569 $newVal = $initial.html(),
570 index = 0;
571
572 if ( $items.find( '.jet-form-builder-repeater__row' ).length ) {
573 $items.find( '.jet-form-builder-repeater__row' ).each( function() {
574 var $this = $( this ),
575 currentIndex = parseInt( $this.data( 'index' ), 10 );
576
577 if ( currentIndex > index ) {
578 index = currentIndex;
579 }
580 } );
581 index++;
582 }
583
584 $newVal = $newVal.replace( /__i__/g, index );
585 $newVal = $( $newVal );
586 $newVal.data( 'index', index );
587 $newVal.attr( 'data-index', index );
588
589 JetFormBuilder.initRangeFields( $newVal );
590
591 $items.append( $newVal );
592
593 var $editors = $newVal.find( '.wp-editor-area' );
594
595 if ( $editors.length && window.wp && window.wp.editor ) {
596 $editors.each( function() {
597 JetFormBuilder.wysiwygInitWithTriggers( this, true );
598 } );
599 }
600
601 if ( $.fn.inputmask ) {
602 $newVal.find( '.jet-form-builder__masked-field' ).inputmask();
603 }
604
605 $repeater.trigger( 'jet-form-builder/repeater-changed' );
606 $this.trigger( 'jet-form-builder/repeater-add-new', [ index ] );
607
608 JetFormBuilder.calculateRowValue( $newVal );
609 JetFormBuilder.initConditions( $newVal );
610
611 },
612
613 updateRepeaterItems: function( $repeater, $field ) {
614
615 var val = JetFormBuilder.getFieldValue( $field );
616
617 if ( ! val ) {
618 return;
619 }
620
621 for ( var i = 0; i < val; i++ ) {
622
623 var $item = $repeater.find( '.jet-form-builder-repeater__row[data-index="' + i + '"]' );
624
625 if ( ! $item.length ) {
626 JetFormBuilder.newRepeaterItem.call( $repeater );
627 }
628
629 }
630
631 var $rows = $repeater.find( '.jet-form-builder-repeater__row' );
632
633 if ( $rows.length ) {
634 $rows.each( function() {
635 var $row = $( this ),
636 index = parseInt( $row.data( 'index' ), 10 );
637
638 index++;
639
640 if ( index > val ) {
641 $row.remove();
642 $repeater.trigger( 'jet-form-builder/repeater-changed' );
643 }
644
645 } );
646 }
647
648 $repeater.trigger( 'change' );
649
650 },
651
652 calculateRowValue: function( $row ) {
653
654 var val = JetFormBuilder.calculateValue( $row );
655
656 $row.data( 'value', val );
657 JetFormBuilder.calculateFieldsInRow( $row );
658
659 },
660
661 calculateFieldsInRow: function( $row ) {
662
663 $row.find( '.jet-form-builder__calculated-field--child' ).each( function() {
664
665 var $childCalculatedField = $( this ),
666 val = JetFormBuilder.calculateValue( $childCalculatedField )
667
668 if ( ! val ) {
669 val = 0;
670 }
671
672 JetFormBuilder.setCalculatedValue( val, $childCalculatedField );
673 } );
674
675 },
676
677 initRepeaterListener: function( $scope ) {
678
679 var $repeater = $scope.find( '.jet-form-builder-repeater' );
680
681 if ( ! $repeater.length ) {
682 return;
683 }
684
685 $repeater.each( function() {
686
687 var $this = $( this ),
688 settings = $this.data( 'settings' );
689
690 if ( 'dynamically' === settings.manageItems && settings.itemsField ) {
691 var $itemsField = $scope.find( '[data-field-name="' + settings.itemsField + '"]' );
692
693 JetFormBuilder.updateRepeaterItems( $this, $itemsField );
694
695 $itemsField.on( 'change', function() {
696 JetFormBuilder.updateRepeaterItems( $this, $itemsField );
697 } );
698 }
699
700 if ( 'custom' === settings.calcType ) {
701
702 var calculated = null;
703
704 JetFormBuilder.repeaterCalcFields[ $this.data( 'field-name' ) ] = {
705 'el': $this,
706 'listenTo': $this.data( 'listen_fields' ),
707 };
708
709 calculated = JetFormBuilder.calculateValue( $this );
710
711 $this.data( 'value', calculated.toFixed( 0 ) );
712
713 }
714
715 var $initial = $this.find( '.jet-form-builder-repeater__initial' );
716 $initial = $( $initial.html() );
717
718 var $calcFields = $initial.find( '.jet-form-builder__calculated-field--child' );
719
720 if ( $calcFields.length ) {
721
722 $calcFields.each( function() {
723
724 var $childField = $( this );
725
726 JetFormBuilder.childrenCalcFields[ $childField.data( 'name' ) ] = {
727 'el': $childField,
728 'parentEl': $this,
729 'listenTo': $childField.data( 'listen_to' ),
730 };
731
732 $this.find( '.jet-form-builder-repeater__row' ).each( function() {
733 JetFormBuilder.calculateRowValue( $( this ), $childField.data( 'precision' ) );
734 } );
735
736 } );
737 }
738
739 } );
740
741 },
742
743 simLabelClick: function( event ) {
744 $( this ).next( '.jet-form-builder__field-label' ).trigger( 'click' );
745 },
746
747 maybeSwitchPage: function( event, $field, $page, disabled ) {
748
749 var $item = $field[ 0 ],
750 isSwitch = $field.data( 'switch' ),
751 value = null,
752 $toPage = null;
753
754 if ( ! isSwitch ) {
755 return;
756 }
757
758 if ( disabled ) {
759 return;
760 }
761
762 value = $item.value;
763
764 if ( ! value ) {
765 return;
766 }
767
768 $toPage = $page.next();
769
770 if ( ! $page || ! $page.length ) {
771 return;
772 }
773
774 if ( ! $toPage || ! $toPage.length ) {
775 return;
776 }
777
778 JetFormBuilder.switchFormPage( $page, $toPage );
779
780 },
781
782 changeActiveTemplateClass: function( event ) {
783
784 var $this = $( this ),
785 $template = $this.closest( '.jet-form-builder__field-wrap' ).find( '.jet-form-builder__field-template' );
786
787 if ( ! $template.length ) {
788 return;
789 }
790
791 if ( 'radio' === $this[ 0 ].type ) {
792 $template
793 .closest( '.jet-form-builder__fields-group' )
794 .find( '.jet-form-builder__field-template--checked' )
795 .removeClass( 'jet-form-builder__field-template--checked' );
796 }
797
798 $template.toggleClass( 'jet-form-builder__field-template--checked', $this[ 0 ].checked );
799
800 },
801
802 initConditions: function( $scope ) {
803 $scope.find( '.jet-form-builder__conditional' ).jetFormBuilderConditional();
804 },
805 widgetBookingForm: function( $scope ) {
806 var $editors = $scope.find( '.jet-form-builder__field .wp-editor-area' );
807
808 if ( $editors.length && window.wp && window.wp.editor ) {
809 $editors.each( function() {
810 JetFormBuilder.wysiwygInitWithTriggers( this, true );
811 } );
812 }
813 JetFormBuilder.initRequiredCheckboxGroup( $scope );
814
815 $( document ).trigger( 'jet-form-builder/init', [ $scope ] );
816
817 JetFormBuilder.initFormPager( $scope );
818 JetFormBuilder.initRangeFields( $scope );
819 JetFormBuilder.initRepeaterListener( $scope );
820 JetFormBuilder.initConditions( $scope );
821
822 if ( $.fn.inputmask ) {
823 $scope.find( '.jet-form-builder__masked-field' ).inputmask();
824 }
825 JetFormBuilder.initCalcFields( $scope );
826 JetFormBuilder.initReplaceValues( $scope );
827
828 $( document ).trigger( 'jet-form-builder/after-init', [ $scope ] );
829 },
830
831 initCalcFields: function( $scope ) {
832 const $calcFields = $scope.find( '.jet-form-builder__calculated-field' );
833
834 if ( ! $calcFields.length ) {
835 return;
836 }
837
838 $( $calcFields ).each( function() {
839
840 var $this = $( this ),
841 calculated = null;
842
843 JetFormBuilder.calcFields[ $this.data( 'name' ) ] = {
844 'el': $this,
845 'listenTo': $this.data( 'listen_to' ),
846 };
847
848 calculated = JetFormBuilder.calculateValue( $this );
849
850 JetFormBuilder.setCalculatedValue( calculated, $this );
851 } );
852 },
853
854 initReplaceValues: function( $scope ) {
855 const $form = $scope.find( 'form.jet-form-builder' );
856
857 const macrosPrefix = ( suffix = '' ) => 'JFB_FIELD::' + suffix;
858
859 const replaceAttrs = window.JetFormBuilderSettings.replaceAttrs || [];
860 JetFormBuilder.replaceStack = JetFormBuilder.replaceStack || {};
861
862 const getAllComments = rootElem => {
863 const comments = [];
864 // Fourth argument, which is actually obsolete according to the DOM4 standard, is required in IE 11
865 const iterator = document.createNodeIterator( rootElem, NodeFilter.SHOW_COMMENT, () => NodeFilter.FILTER_ACCEPT, false );
866 let curNode;
867
868 const timestamp = Date.now();
869 let counter = 0;
870
871 const uniqId = () => {
872 return ++counter + '_' + timestamp;
873 }
874
875 while ( curNode = iterator.nextNode() ) {
876 if ( curNode.textContent.includes( macrosPrefix() ) ) {
877 curNode.nodeValue = curNode.nodeValue.trim();
878
879 comments.push( { id: uniqId(), node: curNode } );
880 }
881 }
882 const querySelector = [];
883
884 for ( let i = 0; i < replaceAttrs.length; i++ ) {
885 querySelector.push( `[${ replaceAttrs[ i ] }*="${ macrosPrefix() }"]` );
886 }
887
888 const elements = Array.from( rootElem.querySelectorAll( querySelector.join( ', ' ) ) );
889
890 elements.forEach( elem => {
891 for ( let i = 0; i < replaceAttrs.length; i++ ) {
892 const attr = elem[ replaceAttrs[ i ] ] || "";
893
894 if ( ! attr.toLowerCase().includes( macrosPrefix().toLowerCase() ) ) {
895 continue;
896 }
897
898 comments.push( {
899 id: uniqId(),
900 attr: replaceAttrs[ i ],
901 value: decodeURIComponent( attr ),
902 node: elem,
903 } );
904 }
905 } )
906
907 return comments;
908 }
909
910 const isRadio = function( $field ) {
911 if ( 'INPUT' !== $field.prop( 'tagName' ) ) {
912 return false;
913 }
914
915 return [ 'checkbox', 'radio' ].includes( $field.attr( 'type' ) );
916 }
917
918 const getFieldNamesWithBrackets = macrosValue => {
919 const matches = macrosValue.match( /(?:<!\-{2}\s*JFB_FIELD::)([\w\-]+)\s*(?:\-{2}>)/gi );
920
921 if ( ! matches ) {
922 return [];
923 }
924
925 return matches.map(
926 match => match.replace( /<!\-{2}\s*JFB_FIELD::/gi, '' ).replace( /\-{2}>/gi, '' )
927 )
928 };
929
930 const getFieldNamesWithOutBrackets = macrosValue => {
931 const matches = macrosValue.match( /(?:\s*JFB_FIELD::)([\w\-]+)\s*/gi );
932
933 if ( ! matches ) {
934 return [];
935 }
936
937 return matches.map(
938 match => match.replace( /\s*JFB_FIELD::/gi, '' )
939 )
940 };
941
942 const getFieldNames = ( macrosValue, withBrackets = true ) => {
943 return withBrackets
944 ? getFieldNamesWithBrackets( macrosValue )
945 : getFieldNamesWithOutBrackets( macrosValue );
946 };
947
948 const replaceMacros = ( replaceFrom, fieldName, fieldValue, withBrackets = true ) => {
949 const regExp = withBrackets
950 ? new RegExp( `<!--\\s*JFB_FIELD::${ fieldName }\\s*-->`, 'gi' )
951 : new RegExp( `\\s*JFB_FIELD::${ fieldName }\\s*`, 'gi' );
952
953 return replaceFrom.replace( regExp, fieldValue );
954 };
955
956 const getValueFromField = field => {
957 const is_radio = isRadio( field );
958
959 if ( ! is_radio ) {
960 return field.val();
961 }
962
963 const checked = [];
964 for ( const radioInput of Array.from( field ) ) {
965 if ( radioInput.checked ) {
966 checked.push( radioInput.value );
967 }
968 }
969
970 return checked.join( ', ' );
971 }
972
973 const prepareValueFromMacros = function( scope, initialValue, withBrackets = true ) {
974 const fieldNames = getFieldNames( initialValue, withBrackets );
975
976 fieldNames.forEach( name => {
977 const fieldElement = scope.find( `[data-field-name="${ name }"]` );
978
979 let fieldValue = applyFilters(
980 'jet.fb.macro.field.value',
981 false,
982 fieldElement,
983 scope,
984 )
985
986 if ( false === fieldValue ) {
987 fieldValue = getValueFromField( fieldElement );
988 }
989
990 initialValue = replaceMacros( initialValue, name, fieldValue, withBrackets );
991 } );
992
993 return initialValue;
994 }
995
996 const replaceAttr = ( macros, scope ) => {
997 const replacement = prepareValueFromMacros( scope, macros.value );
998
999 if ( replacement !== macros.node[ macros.attr ] ) {
1000 macros.node[ macros.attr ] = replacement;
1001 }
1002 };
1003
1004 const insertText = ( macros, scope ) => {
1005 let currentValue = prepareValueFromMacros( scope, macros.node.nodeValue, false );
1006
1007 let prevSibling = Array.from( macros.node.parentNode.childNodes ).find(
1008 node => node.JFB_macros_id === macros.id,
1009 );
1010
1011 if ( ! prevSibling ) {
1012 let wrapper = document.createElement( 'span' );
1013 wrapper.innerHTML = currentValue;
1014
1015 prevSibling = macros.node.parentNode.insertBefore( wrapper, macros.node );
1016 prevSibling.JFB_macros_id = macros.id;
1017
1018 return;
1019 }
1020
1021 if ( prevSibling.innerHTML === currentValue ) {
1022 return;
1023 }
1024
1025 prevSibling.innerHTML = currentValue;
1026 };
1027
1028 const replaceFieldValues = scope => {
1029 const currentFormId = scope.data( 'form-id' );
1030
1031 for ( const formID in JetFormBuilder.replaceStack ) {
1032 if ( +formID !== currentFormId ) {
1033 continue;
1034 }
1035 const multiMacros = JetFormBuilder.replaceStack[ currentFormId ].macros;
1036
1037 multiMacros.forEach( macros => {
1038 if ( macros.attr ) {
1039 replaceAttr( macros, scope );
1040 } else if ( macros.node.nodeName === '#comment' ) {
1041 insertText( macros, scope );
1042 }
1043 } );
1044 }
1045 };
1046
1047 const initRepeater = function( e ) {
1048 const repeater = $( e.target ).closest( '.jet-form-builder-repeater' );
1049 const $form = $( e.target ).closest( 'form.jet-form-builder' );
1050
1051 const formID = $form.data( 'form-id' );
1052 const repeaterName = repeater.data( 'field-name' );
1053
1054 let repeaterRowsList = JetFormBuilder.replaceStack[ formID ].repeaters[ repeaterName ] || [];
1055 let [ scope ] = $( '.jet-form-builder-repeater__row:last', repeater );
1056
1057 if ( ! repeaterRowsList.includes( +scope.dataset.index ) ) {
1058 pushRepeaterRowIndex( formID, repeaterName, +scope.dataset.index );
1059 pushStack( formID, getAllComments( scope ) );
1060 }
1061
1062 replaceFieldValues( $form );
1063 }
1064
1065 const pushStack = function( formId, stack ) {
1066 JetFormBuilder.replaceStack[ formId ].macros.push( ...stack );
1067 };
1068
1069 const pushRepeaterRowIndex = function( formId, repeaterName, index ) {
1070 const repeater = JetFormBuilder.replaceStack[ formId ].repeaters[ repeaterName ] || [];
1071 repeater.push( index );
1072
1073 JetFormBuilder.replaceStack[ formId ].repeaters[ repeaterName ] = repeater;
1074 }
1075
1076 $( document ).on( 'change.JetFormBuilderMain', 'form.jet-form-builder .jet-form-builder__field', function() {
1077 replaceFieldValues( $( this ).closest( 'form.jet-form-builder' ) );
1078 } );
1079
1080 if ( $form.length ) {
1081 JetFormBuilder.replaceStack[ $form.data( 'form-id' ) ] = {
1082 macros: [],
1083 repeaters: {},
1084 };
1085 pushStack( $form.data( 'form-id' ), getAllComments( $form[ 0 ] ) );
1086 replaceFieldValues( $form );
1087 }
1088
1089 $( document ).on(
1090 'jet-form-builder/repeater-add-new',
1091 '.jet-form-builder-repeater__new',
1092 e => initRepeater( e ),
1093 );
1094 },
1095
1096 initFormPager: function( $scope ) {
1097 var $pages = $scope.find( '.jet-form-builder-page' ),
1098 $form = $scope.find( '.jet-form-builder' );
1099
1100 if ( ! $pages.length ) {
1101 return;
1102 }
1103
1104 $pages.each( function() {
1105
1106 var $page = $( this );
1107
1108 if ( ! $page.hasClass( '.jet-form-builder-page--hidden' ) ) {
1109 JetFormBuilder.initSingleFormPage( $page, $form, false );
1110 }
1111
1112 } );
1113
1114 },
1115
1116 initSingleFormPage: function( $page, $form, $changedField ) {
1117
1118 var $button = $page.find( '.jet-form-builder__next-page' ),
1119 $msg = $page.find( '.jet-form-builder__next-page-msg' ),
1120 requiredFields = $page[ 0 ].querySelectorAll( '.jet-form-builder__field[required]' ),
1121 pageNum = parseInt( $page.data( 'page' ), 10 ),
1122 disabled = false,
1123 radioFields = {};
1124
1125 $changedField = $changedField || false;
1126
1127 if ( requiredFields.length ) {
1128 for ( var i = 0; i < requiredFields.length; i++ ) {
1129
1130 var $field = $( requiredFields[ i ] );
1131 var val = null;
1132 var isRadio = false;
1133
1134 if ( 'INPUT' === $field[ 0 ].nodeName ) {
1135
1136 if ( $field.length > 1 ) {
1137 for ( var j = 0; j < $field.length; j++ ) {
1138 if ( $field[ j ].checked ) {
1139 val = $field[ j ].value;
1140 }
1141 }
1142 } else if ( 'radio' === $field[ 0 ].type ) {
1143
1144 isRadio = true;
1145
1146 if ( $field[ 0 ].checked ) {
1147 radioFields[ $field[ 0 ].name ] = $field[ 0 ].value;
1148 }
1149
1150 } else {
1151 val = $field.val();
1152 }
1153 }
1154
1155 if ( 'TEXTAREA' === $field[ 0 ].nodeName ) {
1156 val = $field.val();
1157 }
1158
1159 if ( 'SELECT' === $field[ 0 ].nodeName ) {
1160 val = $field.find( 'option:selected' ).val();
1161 }
1162
1163 if ( ! val ) {
1164 disabled = true;
1165 }
1166
1167 if ( isRadio && radioFields[ $field[ 0 ].name ] ) {
1168 disabled = false;
1169 }
1170
1171 }
1172 }
1173
1174 if ( disabled ) {
1175
1176 if ( $msg.length ) {
1177 $msg.addClass( 'jet-form-builder__next-page-msg--visible' );
1178 }
1179
1180 $button.attr( 'disabled', true );
1181 } else {
1182
1183 if ( $msg.length ) {
1184 $msg.removeClass( 'jet-form-builder__next-page-msg--visible' );
1185 }
1186
1187 $button.attr( 'disabled', false );
1188 }
1189
1190 if ( ! JetFormBuilder.pages[ pageNum ] ) {
1191 JetFormBuilder.pages[ pageNum ] = {
1192 page: $page,
1193 disabled: disabled,
1194 };
1195 } else {
1196 JetFormBuilder.pages[ pageNum ].disabled = disabled;
1197 }
1198
1199 if ( $changedField ) {
1200 $( document ).trigger( 'jet-form-builder/page/field-changed', [ $changedField, $page, disabled ] );
1201 }
1202 },
1203
1204 nextFormPage: function() {
1205
1206 var $button = $( this ),
1207 $fromPage = $button.closest( '.jet-form-builder-page' ),
1208 $pageFields = $fromPage.find( '.jet-form-builder__field' ).filter( ':input' ),
1209 $toPage = $fromPage.next();
1210
1211 if ( ! JetFormBuilder.isFieldsValid( $pageFields ) ) {
1212 return;
1213 }
1214
1215 JetFormBuilder.switchFormPage( $fromPage, $toPage );
1216
1217 },
1218
1219 prevFormPage: function() {
1220
1221 var $button = $( this ),
1222 $fromPage = $button.closest( '.jet-form-builder-page' ),
1223 $toPage = $fromPage.prev();
1224
1225 JetFormBuilder.switchFormPage( $fromPage, $toPage );
1226 },
1227
1228 isFieldsValid: function( $fields ) {
1229 var isValid = true;
1230
1231 $fields.each( function( ind, field ) {
1232 if ( ! field.checkValidity() ) {
1233 isValid = false;
1234 } else {
1235 return isValid;
1236 }
1237 if ( ! field.classList.contains( 'jet-form-builder-file-upload__input' ) ) {
1238 field.reportValidity();
1239 return isValid;
1240 }
1241 const preset = field.closest( '.jet-form-builder-file-upload' ).querySelector( '.jet-form-builder-file-upload__value' );
1242
1243 isValid = !! preset?.value;
1244
1245 if ( ! isValid ) {
1246 field.reportValidity();
1247 return isValid;
1248 }
1249 } );
1250
1251 return isValid;
1252 },
1253
1254 switchFormPage: function( $fromPage, $toPage ) {
1255 const $form = $fromPage.closest( '.jet-form-builder' );
1256 const $conditional = $fromPage.closest( '.jet-form-builder__conditional' );
1257 let $progress = null;
1258
1259 if ( ! $conditional.length ) {
1260 $progress = $form.find( '.jet-form-builder-progress-pages--global' );
1261 }
1262
1263 $fromPage.addClass( 'jet-form-builder-page--hidden' );
1264 $toPage.removeClass( 'jet-form-builder-page--hidden' );
1265
1266 window.scrollTo( 0, $toPage.offset().top + ( +JetFormBuilderSettings.scrollOffset ) );
1267
1268 JetFormBuilder.initSingleFormPage( $toPage, $form, false );
1269
1270 $( '.jet-form-builder-messages-wrap[data-form-id="' + $form.data( 'form-id' ) + '"]' ).html( '' );
1271 $( document ).trigger( 'jet-form-builder/switch-page', [ $fromPage, $toPage, $progress ] );
1272 },
1273
1274 getFieldValue: function( $field ) {
1275
1276 var val = 0;
1277
1278 if ( $field.length ) {
1279
1280 if ( 'INPUT' === $field[ 0 ].nodeName ) {
1281 if ( $field.length > 1 ) {
1282
1283 for ( var i = 0; i < $field.length; i++ ) {
1284 if ( $field[ i ].checked ) {
1285
1286 var itemVal = 0;
1287
1288 if ( undefined !== $field[ i ].dataset.calculate ) {
1289 itemVal = $field[ i ].dataset.calculate;
1290 } else {
1291 itemVal = $field[ i ].value;
1292 }
1293
1294 if ( 'checkbox' === $field[ i ].type ) {
1295 val += parseFloat( itemVal );
1296 } else {
1297 val = itemVal;
1298 }
1299
1300 }
1301 }
1302
1303 } else {
1304 if ( 'checkbox' === $field[ 0 ].type ) {
1305 if ( $field[ 0 ].checked ) {
1306 if ( undefined !== $field[ 0 ].dataset.calculate ) {
1307 val = $field[ 0 ].dataset.calculate;
1308 } else {
1309 val = $field[ 0 ].value;
1310 }
1311 }
1312 } else {
1313 const rawValue = $field.val();
1314
1315 val = Number.isNaN( Number( rawValue ) )
1316 ? rawValue
1317 : parseFloat( rawValue );
1318 }
1319 }
1320 }
1321
1322 if ( 'SELECT' === $field[ 0 ].nodeName ) {
1323
1324 var selectedOption = $field.find( 'option:selected' ),
1325 calcValue = selectedOption.data( 'calculate' );
1326
1327 if ( undefined !== calcValue ) {
1328 val = calcValue;
1329 } else {
1330 val = $field.find( 'option:selected' ).val();
1331 }
1332
1333 }
1334
1335 if ( 'DIV' === $field[ 0 ].nodeName ) {
1336
1337 if ( $field.hasClass( 'jet-form-builder-repeater' ) ) {
1338 var repeaterSettings = $field.data( 'settings' );
1339 if ( repeaterSettings && 'custom' === repeaterSettings.calcType ) {
1340 $field.find( '.jet-form-builder-repeater__row' ).each( function() {
1341 var $row = $( this ),
1342 rowVal = JetFormBuilder.calculateValue( $row );
1343
1344 $row.data( 'value', rowVal );
1345
1346 val += rowVal;
1347 } );
1348
1349 } else {
1350 val = $field.find( '.jet-form-builder-repeater__row' ).length;
1351 }
1352 }
1353
1354 }
1355
1356 }
1357
1358 if ( ! val ) {
1359 val = '0';
1360 }
1361
1362 val = JetFormBuilderMain.filters.applyFilters( 'forms/calculated-field-value', val, $field );
1363
1364 return val;
1365
1366 },
1367
1368 calculateValue: function( $scope ) {
1369
1370 var formula = String( $scope.data( 'formula' ) ),
1371 //listenTo = $( '[name^="' + $scope.data( 'listen_to' ) + '"]', $scope.closest( 'form' ) ),
1372 regexp = /%([a-zA-Z0-9-_]+)%/g,
1373 func = null,
1374 $form = $scope.closest( 'form' );
1375
1376 if ( typeof formula === 'undefined' || ! $form.length ) {
1377 return 0;
1378 }
1379
1380 const getGlobalObj = name => {
1381 return $form.find( '[name="' + name + '"], [name="' + name + '[]"]' )
1382 };
1383
1384 formula = JetFormBuilderMain.filters.applyFilters( 'forms/calculated-formula-before-value', formula, $scope );
1385
1386 formula = formula.replace( regexp, function( match1, match2 ) {
1387
1388 var object = null;
1389
1390 if ( $scope.data( 'repeater' ) ) {
1391 object = $scope;
1392 } else if ( $scope.hasClass( 'jet-form-builder__calculated-field--child' ) ) {
1393 object = $scope.closest( '.jet-form-builder-repeater__row' ).find( '[data-field-name="' + match2 + '"]' );
1394
1395 if ( ! object.length ) {
1396 object = getGlobalObj( match2 );
1397 }
1398
1399 } else if ( $scope.data( 'repeater-row' ) ) {
1400 object = $scope.find( '[data-field-name="' + match2 + '"]' );
1401 } else {
1402 object = getGlobalObj( match2 );
1403 }
1404
1405 return JetFormBuilder.getFieldValue( object );
1406
1407 } );
1408
1409 formula = JetFormBuilderMain.filters.applyFilters( 'forms/calculated-formula-after-value', formula, $scope );
1410
1411 func = new Function( 'return ' + formula );
1412
1413 return func();
1414
1415 },
1416
1417 convertCalcValue: function( value, sepDecimal, sepThousands ) {
1418 const parts = value.toString().split( '.' );
1419 parts[ 0 ] = parts[ 0 ].replace( /\B(?=(\d{3})+(?!\d))/g, sepThousands );
1420
1421 return parts.join( sepDecimal );
1422 },
1423
1424 setCalculatedValue: function( calculatedValue, calcField ) {
1425 const fieldPrecision = calcField.data( 'precision' );
1426 const number = calculatedValue.toFixed( fieldPrecision );
1427
1428 const visibleNumber = JetFormBuilder.convertCalcValue(
1429 number,
1430 calcField.data( 'sep-decimal' ) || '.',
1431 calcField.data( 'sep-thousands' ) || '',
1432 );
1433
1434 calcField.find( '.jet-form-builder__calculated-field-val' ).text( visibleNumber );
1435 calcField.find( '.jet-form-builder__calculated-field-input' ).val( number ).trigger( 'change.JetFormBuilderMain' );
1436 },
1437
1438 recalcFields: function( event ) {
1439
1440 var $this = $( this ),
1441 fieldName = $this.attr( 'name' ),
1442 calculated = null;
1443
1444 if ( $this.data( 'field-name' ) ) {
1445 fieldName = $this.data( 'field-name' );
1446 }
1447
1448 if ( ! fieldName ) {
1449 return;
1450 }
1451 fieldName = fieldName.replace( '[]', '' );
1452
1453 $.each( JetFormBuilder.calcFields, function( calcFieldName, field ) {
1454
1455 if ( 0 <= $.inArray( fieldName, field.listenTo ) ) {
1456 calculated = JetFormBuilder.calculateValue( $( field.el ) );
1457
1458 JetFormBuilder.setCalculatedValue( calculated, $( field.el ) )
1459 }
1460 } );
1461
1462 if ( 'jet-form-builder/repeater-changed' !== event.type ) {
1463 $.each( JetFormBuilder.repeaterCalcFields, function( calcFieldName, field ) {
1464 if ( 0 <= $.inArray( fieldName, field.listenTo ) ) {
1465 field.el.trigger( 'jet-form-builder/repeater-changed' );
1466 }
1467 } );
1468 }
1469
1470 $.each( JetFormBuilder.childrenCalcFields, function( calcFieldName, field ) {
1471
1472 if ( 0 <= $.inArray( fieldName, field.listenTo ) ) {
1473 field.parentEl.find( '.jet-form-builder-repeater__row' ).each( function() {
1474 const $row = $( this );
1475
1476 JetFormBuilder.calculateFieldsInRow( $row )
1477 } );
1478 }
1479 } );
1480
1481 },
1482
1483 initRequiredCheckboxGroup: function( $scope ) {
1484 var $group = $scope.find( '.jet-form-builder__fields-group' );
1485
1486 $group.each( function() {
1487 var $this = $( this ),
1488 $checkboxes = $( '.checkboxes-group-required', $this );
1489
1490 if ( $checkboxes.length ) {
1491 var isChecked = $checkboxes.is( ':checked' );
1492
1493 $checkboxes.prop( 'required', ! isChecked );
1494 }
1495 } );
1496 },
1497
1498 requiredCheckboxGroup: function( event ) {
1499 var $this = $( event.target ),
1500 $group = $this.closest( '.jet-form-builder__fields-group' ),
1501 $checkboxes = $( '.checkboxes-field', $group );
1502
1503 if ( $checkboxes.length < 2 ) {
1504 return;
1505 }
1506
1507 var isChecked = $checkboxes.is( ':checked' );
1508
1509 $checkboxes.prop( 'required', ! isChecked );
1510 },
1511
1512 initRangeFields: function( $scope ) {
1513 var $rangeFields = $scope.find( '.jet-form-builder__field.range-field' );
1514
1515 if ( ! $rangeFields.length ) {
1516 return;
1517 }
1518
1519 $rangeFields.each( function() {
1520 JetFormBuilder.updateRangeField( { target: $( this ), firstInit: true } );
1521 } );
1522 },
1523
1524 updateRangeField: function( event ) {
1525 var $target = $( event.target ),
1526 $wrap = $target.closest( '.jet-form-builder__field-wrap' ),
1527 $number = $wrap.find( '.jet-form-builder__field-value-number' ),
1528 max = $target.attr( 'max' ) || 100,
1529 val = $target.val();
1530
1531 if ( event.firstInit ) {
1532 $number.text( max );
1533 }
1534
1535 $number.text( val );
1536 },
1537 getRecalculateFields: function ( $form ) {
1538 $form.find(
1539 '.jet-form-builder__calculated-field, .jet-form-builder__calculated-field--child'
1540 ).each( function() {
1541
1542 let $childCalculatedField = $( this ),
1543 val = JetFormBuilder.calculateValue( $childCalculatedField )
1544
1545 if ( ! val ) {
1546 val = 0;
1547 }
1548
1549 JetFormBuilder.setCalculatedValue( val, $childCalculatedField );
1550 } );
1551
1552 return true;
1553 },
1554
1555 reloadSubmitForm: function( event ) {
1556 event.preventDefault();
1557 const $target = $( event.target );
1558 const $maskedFields = $target.find( '.jet-form-builder__masked-field' );
1559
1560 if ( event.target?.checkValidity && event.target?.reportValidity && ! event.target.checkValidity() ) {
1561 event.target.reportValidity();
1562
1563 return;
1564 }
1565
1566 if ( $maskedFields && $maskedFields.length ) {
1567 $maskedFields.each( function() {
1568 const $maskedField = $( this );
1569
1570 // Remove mask if empty value
1571 if ( ! $maskedField.val() && $maskedField.inputmask ) {
1572 $maskedField.inputmask( 'remove' );
1573 }
1574 } );
1575 }
1576
1577 $target.addClass( 'is-loading' );
1578 $target.find( '.jet-form-builder__submit' ).attr( 'disabled', true );
1579
1580 Promise.all(
1581 applyFilters(
1582 'jet.fb.submit.reload.promises',
1583 [
1584 JetFormBuilder.getRecalculateFields( $target )
1585 ],
1586 event
1587 )
1588 ).then( () => event.target.submit() ).catch( () => {
1589 $target.removeClass( 'is-loading' );
1590 $target.find( '.jet-form-builder__submit' ).attr( 'disabled', false );
1591
1592 doAction( 'jet.fb.on.prevented.submit.reload', event );
1593 } );
1594 },
1595
1596 ajaxSubmitForm: function( event ) {
1597 event.preventDefault();
1598
1599 var $this = $( this ),
1600 $form = $this.closest( '.jet-form-builder' ),
1601 formID = $form.data( 'form-id' ),
1602 data = {};
1603
1604 if ( $form[ 0 ]?.checkValidity && $form[ 0 ]?.reportValidity && ! $form[ 0 ].checkValidity() ) {
1605 $form[ 0 ].reportValidity();
1606
1607 return;
1608 }
1609
1610 if ( window.tinyMCE ) {
1611 window.tinyMCE.triggerSave();
1612 }
1613
1614 JetFormBuilder.clearFieldErrors( formID );
1615
1616 const onSuccess = function( response ) {
1617
1618 $form.removeClass( 'is-loading' );
1619 $this.attr( 'disabled', false );
1620
1621 switch ( response.status ) {
1622
1623 case 'validation_failed':
1624
1625 Object.entries( response.fields ).forEach( function( [ fieldName, fieldData ] ) {
1626 var $field = JetFormBuilder.findFieldByName( $form, fieldName );
1627
1628 const afterMessage = `<div class="error-message">${ fieldData.message }</div>`;
1629
1630 $field.addClass( 'field-has-error' );
1631
1632 if ( $field.hasClass( 'checkradio-field' ) ) {
1633 $field.closest( '.jet-form-builder__field-wrap' ).after( afterMessage );
1634 } else {
1635 $field.after( afterMessage );
1636 }
1637
1638 JetFormBuilder.currentFieldWithError = {
1639 length: 0,
1640 };
1641 } );
1642
1643 break;
1644
1645 case 'success':
1646 $( document ).trigger( 'jet-form-builder/ajax/on-success', [ response, $form, data ] );
1647 break;
1648 }
1649
1650 if ( response.redirect ) {
1651 window.location = response.redirect;
1652 } else if ( response.reload ) {
1653 window.location.reload();
1654 }
1655
1656 $( '.jet-form-builder-messages-wrap[data-form-id="' + formID + '"]' ).html( response.message );
1657
1658 };
1659
1660 const onError = function ( jqXHR, textStatus, errorThrown ) {
1661 console.error( jqXHR.responseText, errorThrown );
1662
1663 removeLoading();
1664 }
1665
1666 const removeLoading = () => {
1667 $form.removeClass( 'is-loading' );
1668 $this.attr( 'disabled', false );
1669 };
1670
1671 const runAjaxForm = callbacks => {
1672 //data.values = $form.serializeArray();
1673 //data._jet_engine_booking_form_id = formID;
1674 const formData = new FormData( $form[ 0 ] );
1675 formData.append( '_jet_engine_booking_form_id', formID );
1676
1677 $.ajax( {
1678 url: JetFormBuilderSettings.ajaxurl,
1679 type: 'POST',
1680 dataType: 'json',
1681 data: formData,
1682 cache : false,
1683 contentType: false,
1684 processData: false,
1685 } ).done( response => {
1686 onSuccess( response );
1687 callbacks.forEach( cb => {
1688 if ( 'function' === typeof cb ) {
1689 cb.call( $form, response )
1690 }
1691 } );
1692
1693 } ).fail( onError );
1694 };
1695
1696 $form.addClass( 'is-loading' );
1697 $this.attr( 'disabled', true );
1698
1699 Promise.all(
1700 applyFilters(
1701 'jet.fb.submit.ajax.promises',
1702 [
1703 JetFormBuilder.getRecalculateFields( $form )
1704 ],
1705 $form,
1706 $this,
1707 data
1708 )
1709 ).then( runAjaxForm ).catch( () => {
1710 removeLoading();
1711 doAction( 'jet.fb.on.prevented.submit.ajax', $this, $form, data );
1712 } );
1713 },
1714
1715 clearFieldErrors: function( formID ) {
1716 var $this = $( this );
1717
1718 $this.closest( '.jet-form-builder-col' ).find( '.jet-form-builder__field-error' ).remove();
1719
1720 $( '.jet-form-builder__field.field-has-error' ).each( ( index, elem ) => {
1721 $( elem ).removeClass( 'field-has-error' );
1722 $( elem ).siblings( '.error-message' ).remove();
1723 } );
1724
1725 $( '.jet-form-builder-messages-wrap[data-form-id="' + formID + '"]' ).html( '' );
1726
1727 },
1728
1729 findFieldByName: function( form, fieldName ) {
1730 const callbackFinders = [
1731 'findInputDefault',
1732 'findWysiwyg',
1733 ];
1734
1735 callbackFinders.forEach( function( callback ) {
1736 if ( ! JetFormBuilder.currentFieldWithError || ! JetFormBuilder.currentFieldWithError.length ) {
1737 JetFormBuilder.currentFieldWithError = JetFormBuilder[ callback ]( form, fieldName );
1738 }
1739 } );
1740
1741 return JetFormBuilder.currentFieldWithError;
1742 },
1743
1744 findInputDefault: function( form, fieldName ) {
1745 return form.find( `.jet-form-builder-row [data-field-name="${ fieldName }"]:last` );
1746 },
1747
1748 findWysiwyg: function( form, fieldName ) {
1749 let field;
1750
1751 form.find( '.jet-form-builder__field[data-editor]' ).each( function( index, editor ) {
1752
1753 if ( fieldName === $( editor ).data( 'editor' ).textarea_name ) {
1754 field = $( editor );
1755 }
1756 } );
1757
1758 return field;
1759 },
1760
1761 addTriggersWysiwyg: function( field, editorId ) {
1762 const callable = function( e ) {
1763 field.trigger( 'change.JetFormBuilderMain', [ this ] );
1764 };
1765
1766 const editor = tinymce.get( editorId );
1767
1768 field.trigger( 'init.JetFormBuilderMain', [ editor ] );
1769
1770 editor
1771 .on( 'input', callable )
1772 .on( 'change', callable );
1773 },
1774 wysiwygInit: function( closure, replace = false ) {
1775 const self = $( closure ),
1776 editorID = self.attr( 'id' ),
1777 field = self.closest( '.jet-form-builder__field' );
1778
1779 if ( replace && window.tinymce && window.tinymce.get( editorID ) ) {
1780 window.tinymce.get( editorID ).remove();
1781 }
1782
1783 window.wp.editor.initialize(
1784 editorID,
1785 field.data( 'editor' ),
1786 );
1787
1788 return { editorID, field };
1789 },
1790 wysiwygInitWithTriggers: function( closure, replace = false ) {
1791 const { editorID, field } = JetFormBuilder.wysiwygInit( closure, replace );
1792
1793 JetFormBuilder.addTriggersWysiwyg( field, editorID );
1794 },
1795
1796 };
1797
1798 window.JetFormBuilderDev = JetFormBuilderDev;
1799 window.JetFormBuilder = JetFormBuilder;
1800
1801 $( JetFormBuilder.initCommon );
1802 $( window ).on( 'elementor/frontend/init', JetFormBuilder.initElementor );
1803
1804 JetFormBuilder.addHandlersInit();
1805 } )( jQuery );
1806