PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 2.1.1
JetFormBuilder — Dynamic Blocks Form Builder v2.1.1
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 admin.js 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 package.js 4 years ago package.js.map 4 years ago re-captcha-v3.js 4 years ago
frontend-forms.js
1808 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 ( ! val && $field[ 0 ].classList.contains( 'jet-form-builder-file-upload__input' ) ) {
1156 const hiddenMediaPreset = $page[ 0 ].querySelector( '.jet-form-builder-file-upload__value' );
1157
1158 val = !! hiddenMediaPreset?.value;
1159 }
1160
1161 if ( 'TEXTAREA' === $field[ 0 ].nodeName ) {
1162 val = $field.val();
1163 }
1164
1165 if ( 'SELECT' === $field[ 0 ].nodeName ) {
1166 val = $field.find( 'option:selected' ).val();
1167 }
1168
1169 if ( ! val ) {
1170 disabled = true;
1171 }
1172
1173 if ( isRadio && radioFields[ $field[ 0 ].name ] ) {
1174 disabled = false;
1175 }
1176
1177 }
1178 }
1179
1180 if ( disabled ) {
1181
1182 if ( $msg.length ) {
1183 $msg.addClass( 'jet-form-builder__next-page-msg--visible' );
1184 }
1185
1186 $button.attr( 'disabled', true );
1187 } else {
1188
1189 if ( $msg.length ) {
1190 $msg.removeClass( 'jet-form-builder__next-page-msg--visible' );
1191 }
1192
1193 $button.attr( 'disabled', false );
1194 }
1195
1196 if ( ! JetFormBuilder.pages[ pageNum ] ) {
1197 JetFormBuilder.pages[ pageNum ] = {
1198 page: $page,
1199 disabled: disabled,
1200 };
1201 } else {
1202 JetFormBuilder.pages[ pageNum ].disabled = disabled;
1203 }
1204
1205 if ( $changedField ) {
1206 $( document ).trigger( 'jet-form-builder/page/field-changed', [ $changedField, $page, disabled ] );
1207 }
1208 },
1209
1210 nextFormPage: function() {
1211
1212 var $button = $( this ),
1213 $fromPage = $button.closest( '.jet-form-builder-page' ),
1214 $pageFields = $fromPage.find( '.jet-form-builder__field' ).filter( ':input' ),
1215 $toPage = $fromPage.next();
1216
1217 if ( ! JetFormBuilder.isFieldsValid( $pageFields ) ) {
1218 return;
1219 }
1220
1221 JetFormBuilder.switchFormPage( $fromPage, $toPage );
1222
1223 },
1224
1225 prevFormPage: function() {
1226
1227 var $button = $( this ),
1228 $fromPage = $button.closest( '.jet-form-builder-page' ),
1229 $toPage = $fromPage.prev();
1230
1231 JetFormBuilder.switchFormPage( $fromPage, $toPage );
1232 },
1233
1234 isFieldsValid: function( $fields ) {
1235 var isValid = true;
1236
1237 $fields.each( function( ind, field ) {
1238 if ( ! field.checkValidity() ) {
1239 isValid = false;
1240 } else {
1241 return isValid;
1242 }
1243 if ( ! field.classList.contains( 'jet-form-builder-file-upload__input' ) ) {
1244 field.reportValidity();
1245 return isValid;
1246 }
1247 const preset = field.closest( '.jet-form-builder-file-upload' ).querySelector( '.jet-form-builder-file-upload__value' );
1248
1249 isValid = !! preset?.value;
1250
1251 if ( ! isValid ) {
1252 field.reportValidity();
1253 return isValid;
1254 }
1255 } );
1256
1257 return isValid;
1258 },
1259
1260 switchFormPage: function( $fromPage, $toPage ) {
1261 const $form = $fromPage.closest( '.jet-form-builder' );
1262 const $conditional = $fromPage.closest( '.jet-form-builder__conditional' );
1263 let $progress = null;
1264
1265 if ( ! $conditional.length ) {
1266 $progress = $form.find( '.jet-form-builder-progress-pages--global' );
1267 }
1268
1269 $fromPage.addClass( 'jet-form-builder-page--hidden' );
1270 $toPage.removeClass( 'jet-form-builder-page--hidden' );
1271
1272 window.scrollTo( 0, $toPage.offset().top + ( +JetFormBuilderSettings.scrollOffset ) );
1273
1274 JetFormBuilder.initSingleFormPage( $toPage, $form, false );
1275
1276 $( '.jet-form-builder-messages-wrap[data-form-id="' + $form.data( 'form-id' ) + '"]' ).html( '' );
1277 $( document ).trigger( 'jet-form-builder/switch-page', [ $fromPage, $toPage, $progress ] );
1278 },
1279
1280 getFieldValue: function( $field ) {
1281
1282 var val = 0;
1283
1284 if ( $field.length ) {
1285
1286 if ( 'INPUT' === $field[ 0 ].nodeName ) {
1287 if ( $field.length > 1 ) {
1288
1289 for ( var i = 0; i < $field.length; i++ ) {
1290 if ( $field[ i ].checked ) {
1291
1292 var itemVal = 0;
1293
1294 if ( undefined !== $field[ i ].dataset.calculate ) {
1295 itemVal = $field[ i ].dataset.calculate;
1296 } else {
1297 itemVal = $field[ i ].value;
1298 }
1299
1300 if ( 'checkbox' === $field[ i ].type ) {
1301 val += parseFloat( itemVal );
1302 } else {
1303 val = itemVal;
1304 }
1305
1306 }
1307 }
1308
1309 } else {
1310 if ( 'checkbox' === $field[ 0 ].type ) {
1311 if ( $field[ 0 ].checked ) {
1312 if ( undefined !== $field[ 0 ].dataset.calculate ) {
1313 val = $field[ 0 ].dataset.calculate;
1314 } else {
1315 val = $field[ 0 ].value;
1316 }
1317 }
1318 } else {
1319 val = $field.val();
1320 }
1321 }
1322 }
1323
1324 if ( 'SELECT' === $field[ 0 ].nodeName ) {
1325
1326 var selectedOption = $field.find( 'option:selected' ),
1327 calcValue = selectedOption.data( 'calculate' );
1328
1329 if ( undefined !== calcValue ) {
1330 val = calcValue;
1331 } else {
1332 val = $field.find( 'option:selected' ).val();
1333 }
1334
1335 }
1336
1337 if ( 'DIV' === $field[ 0 ].nodeName ) {
1338
1339 if ( $field.hasClass( 'jet-form-builder-repeater' ) ) {
1340 var repeaterSettings = $field.data( 'settings' );
1341 if ( repeaterSettings && 'custom' === repeaterSettings.calcType ) {
1342 $field.find( '.jet-form-builder-repeater__row' ).each( function() {
1343 var $row = $( this ),
1344 rowVal = JetFormBuilder.calculateValue( $row );
1345
1346 $row.data( 'value', rowVal );
1347
1348 val += rowVal;
1349 } );
1350
1351 } else {
1352 val = $field.find( '.jet-form-builder-repeater__row' ).length;
1353 }
1354 }
1355
1356 }
1357
1358 }
1359
1360 if ( ! val ) {
1361 val = '0';
1362 }
1363
1364 val = JetFormBuilderMain.filters.applyFilters( 'forms/calculated-field-value', val, $field );
1365
1366 return val;
1367
1368 },
1369
1370 calculateValue: function( $scope ) {
1371
1372 var formula = String( $scope.data( 'formula' ) ),
1373 //listenTo = $( '[name^="' + $scope.data( 'listen_to' ) + '"]', $scope.closest( 'form' ) ),
1374 regexp = /%([a-zA-Z0-9-_]+)%/g,
1375 func = null,
1376 $form = $scope.closest( 'form' );
1377
1378 if ( typeof formula === 'undefined' || ! $form.length ) {
1379 return 0;
1380 }
1381
1382 const getGlobalObj = name => {
1383 return $form.find( '[name="' + name + '"], [name="' + name + '[]"]' )
1384 };
1385
1386 formula = JetFormBuilderMain.filters.applyFilters( 'forms/calculated-formula-before-value', formula, $scope );
1387
1388 formula = formula.replace( regexp, function( match1, match2 ) {
1389
1390 var object = null;
1391
1392 if ( $scope.data( 'repeater' ) ) {
1393 object = $scope;
1394 } else if ( $scope.hasClass( 'jet-form-builder__calculated-field--child' ) ) {
1395 object = $scope.closest( '.jet-form-builder-repeater__row' ).find( '[data-field-name="' + match2 + '"]' );
1396
1397 if ( ! object.length ) {
1398 object = getGlobalObj( match2 );
1399 }
1400
1401 } else if ( $scope.data( 'repeater-row' ) ) {
1402 object = $scope.find( '[data-field-name="' + match2 + '"]' );
1403 } else {
1404 object = getGlobalObj( match2 );
1405 }
1406
1407 return JetFormBuilder.getFieldValue( object );
1408
1409 } );
1410
1411 formula = JetFormBuilderMain.filters.applyFilters( 'forms/calculated-formula-after-value', formula, $scope );
1412
1413 func = new Function( 'return ' + formula );
1414
1415 return func();
1416
1417 },
1418
1419 convertCalcValue: function( value, sepDecimal, sepThousands ) {
1420 const parts = value.toString().split( '.' );
1421 parts[ 0 ] = parts[ 0 ].replace( /\B(?=(\d{3})+(?!\d))/g, sepThousands );
1422
1423 return parts.join( sepDecimal );
1424 },
1425
1426 setCalculatedValue: function( calculatedValue, calcField ) {
1427 const fieldPrecision = calcField.data( 'precision' );
1428 const number = calculatedValue.toFixed( fieldPrecision );
1429
1430 const visibleNumber = JetFormBuilder.convertCalcValue(
1431 number,
1432 calcField.data( 'sep-decimal' ) || '.',
1433 calcField.data( 'sep-thousands' ) || '',
1434 );
1435
1436 calcField.find( '.jet-form-builder__calculated-field-val' ).text( visibleNumber );
1437 calcField.find( '.jet-form-builder__calculated-field-input' ).val( number ).trigger( 'change.JetFormBuilderMain' );
1438 },
1439
1440 recalcFields: function( event ) {
1441
1442 var $this = $( this ),
1443 fieldName = $this.attr( 'name' ),
1444 calculated = null;
1445
1446 if ( $this.data( 'field-name' ) ) {
1447 fieldName = $this.data( 'field-name' );
1448 }
1449
1450 if ( ! fieldName ) {
1451 return;
1452 }
1453 fieldName = fieldName.replace( '[]', '' );
1454
1455 $.each( JetFormBuilder.calcFields, function( calcFieldName, field ) {
1456
1457 if ( 0 <= $.inArray( fieldName, field.listenTo ) ) {
1458 calculated = JetFormBuilder.calculateValue( $( field.el ) );
1459
1460 JetFormBuilder.setCalculatedValue( calculated, $( field.el ) )
1461 }
1462 } );
1463
1464 if ( 'jet-form-builder/repeater-changed' !== event.type ) {
1465 $.each( JetFormBuilder.repeaterCalcFields, function( calcFieldName, field ) {
1466 if ( 0 <= $.inArray( fieldName, field.listenTo ) ) {
1467 field.el.trigger( 'jet-form-builder/repeater-changed' );
1468 }
1469 } );
1470 }
1471
1472 $.each( JetFormBuilder.childrenCalcFields, function( calcFieldName, field ) {
1473
1474 if ( 0 <= $.inArray( fieldName, field.listenTo ) ) {
1475 field.parentEl.find( '.jet-form-builder-repeater__row' ).each( function() {
1476 const $row = $( this );
1477
1478 JetFormBuilder.calculateFieldsInRow( $row )
1479 } );
1480 }
1481 } );
1482
1483 },
1484
1485 initRequiredCheckboxGroup: function( $scope ) {
1486 var $group = $scope.find( '.jet-form-builder__fields-group' );
1487
1488 $group.each( function() {
1489 var $this = $( this ),
1490 $checkboxes = $( '.checkboxes-group-required', $this );
1491
1492 if ( $checkboxes.length ) {
1493 var isChecked = $checkboxes.is( ':checked' );
1494
1495 $checkboxes.prop( 'required', ! isChecked );
1496 }
1497 } );
1498 },
1499
1500 requiredCheckboxGroup: function( event ) {
1501 var $this = $( event.target ),
1502 $group = $this.closest( '.jet-form-builder__fields-group' ),
1503 $checkboxes = $( '.checkboxes-field', $group );
1504
1505 if ( $checkboxes.length < 2 ) {
1506 return;
1507 }
1508
1509 var isChecked = $checkboxes.is( ':checked' );
1510
1511 $checkboxes.prop( 'required', ! isChecked );
1512 },
1513
1514 initRangeFields: function( $scope ) {
1515 var $rangeFields = $scope.find( '.jet-form-builder__field.range-field' );
1516
1517 if ( ! $rangeFields.length ) {
1518 return;
1519 }
1520
1521 $rangeFields.each( function() {
1522 JetFormBuilder.updateRangeField( { target: $( this ), firstInit: true } );
1523 } );
1524 },
1525
1526 updateRangeField: function( event ) {
1527 var $target = $( event.target ),
1528 $wrap = $target.closest( '.jet-form-builder__field-wrap' ),
1529 $number = $wrap.find( '.jet-form-builder__field-value-number' ),
1530 max = $target.attr( 'max' ) || 100,
1531 val = $target.val();
1532
1533 if ( event.firstInit ) {
1534 $number.text( max );
1535 }
1536
1537 $number.text( val );
1538 },
1539 getRecalculateFields: function ( $form ) {
1540 $form.find(
1541 '.jet-form-builder__calculated-field, .jet-form-builder__calculated-field--child'
1542 ).each( function() {
1543
1544 let $childCalculatedField = $( this ),
1545 val = JetFormBuilder.calculateValue( $childCalculatedField )
1546
1547 if ( ! val ) {
1548 val = 0;
1549 }
1550
1551 JetFormBuilder.setCalculatedValue( val, $childCalculatedField );
1552 } );
1553
1554 return true;
1555 },
1556
1557 reloadSubmitForm: function( event ) {
1558 event.preventDefault();
1559 const $target = $( event.target );
1560 const $maskedFields = $target.find( '.jet-form-builder__masked-field' );
1561
1562 if ( event.target?.checkValidity && event.target?.reportValidity && ! event.target.checkValidity() ) {
1563 event.target.reportValidity();
1564
1565 return;
1566 }
1567
1568 if ( $maskedFields && $maskedFields.length ) {
1569 $maskedFields.each( function() {
1570 const $maskedField = $( this );
1571
1572 // Remove mask if empty value
1573 if ( ! $maskedField.val() && $maskedField.inputmask ) {
1574 $maskedField.inputmask( 'remove' );
1575 }
1576 } );
1577 }
1578
1579 $target.addClass( 'is-loading' );
1580 $target.find( '.jet-form-builder__submit' ).attr( 'disabled', true );
1581
1582 Promise.all(
1583 applyFilters(
1584 'jet.fb.submit.reload.promises',
1585 [
1586 JetFormBuilder.getRecalculateFields( $target )
1587 ],
1588 event
1589 )
1590 ).then( () => event.target.submit() ).catch( () => {
1591 $target.removeClass( 'is-loading' );
1592 $target.find( '.jet-form-builder__submit' ).attr( 'disabled', false );
1593
1594 doAction( 'jet.fb.on.prevented.submit.reload', event );
1595 } );
1596 },
1597
1598 ajaxSubmitForm: function( event ) {
1599 event.preventDefault();
1600
1601 var $this = $( this ),
1602 $form = $this.closest( '.jet-form-builder' ),
1603 formID = $form.data( 'form-id' ),
1604 data = {};
1605
1606 if ( $form[ 0 ]?.checkValidity && $form[ 0 ]?.reportValidity && ! $form[ 0 ].checkValidity() ) {
1607 $form[ 0 ].reportValidity();
1608
1609 return;
1610 }
1611
1612 if ( window.tinyMCE ) {
1613 window.tinyMCE.triggerSave();
1614 }
1615
1616 JetFormBuilder.clearFieldErrors( formID );
1617
1618 const onSuccess = function( response ) {
1619
1620 $form.removeClass( 'is-loading' );
1621 $this.attr( 'disabled', false );
1622
1623 switch ( response.status ) {
1624
1625 case 'validation_failed':
1626
1627 Object.entries( response.fields ).forEach( function( [ fieldName, fieldData ] ) {
1628 var $field = JetFormBuilder.findFieldByName( $form, fieldName );
1629
1630 const afterMessage = `<div class="error-message">${ fieldData.message }</div>`;
1631
1632 $field.addClass( 'field-has-error' );
1633
1634 if ( $field.hasClass( 'checkradio-field' ) ) {
1635 $field.closest( '.jet-form-builder__field-wrap' ).after( afterMessage );
1636 } else {
1637 $field.after( afterMessage );
1638 }
1639
1640 JetFormBuilder.currentFieldWithError = {
1641 length: 0,
1642 };
1643 } );
1644
1645 break;
1646
1647 case 'success':
1648 $( document ).trigger( 'jet-form-builder/ajax/on-success', [ response, $form, data ] );
1649 break;
1650 }
1651
1652 if ( response.redirect ) {
1653 window.location = response.redirect;
1654 } else if ( response.reload ) {
1655 window.location.reload();
1656 }
1657
1658 $( '.jet-form-builder-messages-wrap[data-form-id="' + formID + '"]' ).html( response.message );
1659
1660 };
1661
1662 const onError = function ( jqXHR, textStatus, errorThrown ) {
1663 console.error( jqXHR.responseText, errorThrown );
1664
1665 removeLoading();
1666 }
1667
1668 const removeLoading = () => {
1669 $form.removeClass( 'is-loading' );
1670 $this.attr( 'disabled', false );
1671 };
1672
1673 const runAjaxForm = callbacks => {
1674 //data.values = $form.serializeArray();
1675 //data._jet_engine_booking_form_id = formID;
1676 const formData = new FormData( $form[ 0 ] );
1677 formData.append( '_jet_engine_booking_form_id', formID );
1678
1679 $.ajax( {
1680 url: JetFormBuilderSettings.ajaxurl,
1681 type: 'POST',
1682 dataType: 'json',
1683 data: formData,
1684 cache : false,
1685 contentType: false,
1686 processData: false,
1687 } ).done( response => {
1688 onSuccess( response );
1689 callbacks.forEach( cb => {
1690 if ( 'function' === typeof cb ) {
1691 cb.call( $form, response )
1692 }
1693 } );
1694
1695 } ).fail( onError );
1696 };
1697
1698 $form.addClass( 'is-loading' );
1699 $this.attr( 'disabled', true );
1700
1701 Promise.all(
1702 applyFilters(
1703 'jet.fb.submit.ajax.promises',
1704 [
1705 JetFormBuilder.getRecalculateFields( $form )
1706 ],
1707 $form,
1708 $this,
1709 data
1710 )
1711 ).then( runAjaxForm ).catch( () => {
1712 removeLoading();
1713 doAction( 'jet.fb.on.prevented.submit.ajax', $this, $form, data );
1714 } );
1715 },
1716
1717 clearFieldErrors: function( formID ) {
1718 var $this = $( this );
1719
1720 $this.closest( '.jet-form-builder-col' ).find( '.jet-form-builder__field-error' ).remove();
1721
1722 $( '.jet-form-builder__field.field-has-error' ).each( ( index, elem ) => {
1723 $( elem ).removeClass( 'field-has-error' );
1724 $( elem ).siblings( '.error-message' ).remove();
1725 } );
1726
1727 $( '.jet-form-builder-messages-wrap[data-form-id="' + formID + '"]' ).html( '' );
1728
1729 },
1730
1731 findFieldByName: function( form, fieldName ) {
1732 const callbackFinders = [
1733 'findInputDefault',
1734 'findWysiwyg',
1735 ];
1736
1737 callbackFinders.forEach( function( callback ) {
1738 if ( ! JetFormBuilder.currentFieldWithError || ! JetFormBuilder.currentFieldWithError.length ) {
1739 JetFormBuilder.currentFieldWithError = JetFormBuilder[ callback ]( form, fieldName );
1740 }
1741 } );
1742
1743 return JetFormBuilder.currentFieldWithError;
1744 },
1745
1746 findInputDefault: function( form, fieldName ) {
1747 return form.find( `.jet-form-builder-row [data-field-name="${ fieldName }"]:last` );
1748 },
1749
1750 findWysiwyg: function( form, fieldName ) {
1751 let field;
1752
1753 form.find( '.jet-form-builder__field[data-editor]' ).each( function( index, editor ) {
1754
1755 if ( fieldName === $( editor ).data( 'editor' ).textarea_name ) {
1756 field = $( editor );
1757 }
1758 } );
1759
1760 return field;
1761 },
1762
1763 addTriggersWysiwyg: function( field, editorId ) {
1764 const callable = function( e ) {
1765 field.trigger( 'change.JetFormBuilderMain', [ this ] );
1766 };
1767
1768 const editor = tinymce.get( editorId );
1769
1770 field.trigger( 'init.JetFormBuilderMain', [ editor ] );
1771
1772 editor
1773 .on( 'input', callable )
1774 .on( 'change', callable );
1775 },
1776 wysiwygInit: function( closure, replace = false ) {
1777 const self = $( closure ),
1778 editorID = self.attr( 'id' ),
1779 field = self.closest( '.jet-form-builder__field' );
1780
1781 if ( replace && window.tinymce && window.tinymce.get( editorID ) ) {
1782 window.tinymce.get( editorID ).remove();
1783 }
1784
1785 window.wp.editor.initialize(
1786 editorID,
1787 field.data( 'editor' ),
1788 );
1789
1790 return { editorID, field };
1791 },
1792 wysiwygInitWithTriggers: function( closure, replace = false ) {
1793 const { editorID, field } = JetFormBuilder.wysiwygInit( closure, replace );
1794
1795 JetFormBuilder.addTriggersWysiwyg( field, editorID );
1796 },
1797
1798 };
1799
1800 window.JetFormBuilderDev = JetFormBuilderDev;
1801 window.JetFormBuilder = JetFormBuilder;
1802
1803 $( JetFormBuilder.initCommon );
1804 $( window ).on( 'elementor/frontend/init', JetFormBuilder.initElementor );
1805
1806 JetFormBuilder.addHandlersInit();
1807 } )( jQuery );
1808