PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.24.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.24.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
← All changes | js/formidable.js +383 -874 6.31 → 6.24.1 View file →
@@ -1,9 +1,13 @@
1 1 /* exported frmRecaptcha, frmAfterRecaptcha */
2 +/* eslint-disable prefer-const */
2 3
3 4 function frmFrontFormJS() {
4 5 'use strict';
5 6
7 + /*global jQuery:false, frm_js, grecaptcha, hcaptcha, turnstile, frmProForm, tinyMCE */
8 + /*global frmThemeOverride_jsErrors, frmThemeOverride_frmPlaceError, frmThemeOverride_frmAfterSubmit */
9 +
6 10 let jsErrors = [];
7 11
8 12 /**
9 13 * Triggers custom JS event.
@@ -32,27 +36,29 @@
32 36 * @param {boolean} fullID
33 37 * @return {string|number} Field ID.
34 38 */
35 39 function getFieldId( field, fullID ) {
36 - let nameParts;
37 - let fieldId;
38 - let isRepeating = false;
39 - let fieldName = '';
40 -
40 + let nameParts, fieldId,
41 + isRepeating = false,
42 + fieldName = '';
41 43 if ( field instanceof jQuery ) {
42 - field = field.get( 0 );
44 + fieldName = field.attr( 'name' );
45 + } else {
46 + fieldName = field.name;
43 47 }
44 48
45 - fieldName = field.name;
46 -
47 - if ( fieldName === undefined ) {
49 + if ( typeof fieldName === 'undefined' ) {
48 50 fieldName = '';
49 51 }
50 52
51 53 if ( fieldName === '' ) {
52 - fieldName = field.getAttribute( 'data-name' );
54 + if ( field instanceof jQuery ) {
55 + fieldName = field.data( 'name' );
56 + } else {
57 + fieldName = field.getAttribute( 'data-name' );
58 + }
53 59
54 - if ( fieldName === undefined ) {
60 + if ( typeof fieldName === 'undefined' ) {
55 61 fieldName = '';
56 62 }
57 63
58 64 if ( fieldName !== '' && fieldName ) {
@@ -80,9 +86,9 @@
80 86 return 0;
81 87 }
82 88
83 89 // Check if 'this' is in a repeating section
84 - if ( document.querySelector( `input[name="item_meta[${ fieldId }][form]"]` ) ) {
90 + if ( document.querySelector( 'input[name="item_meta[' + fieldId + '][form]"]' ) ) {
85 91 // this is a repeatable section with name: item_meta[repeating-section-id][row-id][field-id]
86 92 fieldId = nameParts[ 2 ].replace( '[', '' );
87 93 isRepeating = true;
88 94 }
@@ -100,11 +106,11 @@
100 106
101 107 if ( fullID === true ) {
102 108 // For use in the container div id
103 109 if ( fieldId === nameParts[ 0 ] ) {
104 - fieldId = `${ fieldId }-${ nameParts[ 1 ].replace( '[', '' ) }`;
110 + fieldId = fieldId + '-' + nameParts[ 1 ].replace( '[', '' );
105 111 } else {
106 - fieldId = `${ fieldId }-${ nameParts[ 0 ] }-${ nameParts[ 1 ].replace( '[', '' ) }`;
112 + fieldId = fieldId + '-' + nameParts[ 0 ] + '-' + nameParts[ 1 ].replace( '[', '' );
107 113 }
108 114 }
109 115
110 116 return fieldId;
@@ -117,15 +123,9 @@
117 123 *
118 124 * @param {Object} $form
119 125 */
120 126 function disableSubmitButton( $form ) {
121 - const form = $form instanceof jQuery ? $form.get( 0 ) : $form;
122 - if ( ! form ) {
123 - return;
124 - }
125 - form.querySelectorAll( 'input[type="submit"], input[type="button"], button[type="submit"], button.frm_save_draft' ).forEach(
126 - button => button.disabled = true
127 - );
127 + $form.find( 'input[type="submit"], input[type="button"], button[type="submit"], button.frm_save_draft' ).attr( 'disabled', 'disabled' );
128 128 }
129 129
130 130 /**
131 131 * Enable the submit button for a given jQuery form object
@@ -131,16 +131,12 @@
131 131 * Enable the submit button for a given jQuery form object
132 132 *
133 133 * @since 2.03.02
134 134 *
135 - * @param {HTMLElement} form
136 - *
137 - * @return {void}
135 + * @param {Object} $form
138 136 */
139 - function enableSubmitButton( form ) {
140 - form.querySelectorAll( 'input[type="submit"], input[type="button"], button[type="submit"]' ).forEach(
141 - button => button.disabled = false
142 - );
137 + function enableSubmitButton( $form ) {
138 + $form.find( 'input[type="submit"], input[type="button"], button[type="submit"]' ).prop( 'disabled', false );
143 139 }
144 140
145 141 /**
146 142 * Disable the save draft link for a given jQuery form object
@@ -149,30 +145,23 @@
149 145 *
150 146 * @param {Object} $form
151 147 */
152 148 function disableSaveDraft( $form ) {
153 - const form = $form instanceof jQuery ? $form.get( 0 ) : $form;
154 - if ( ! form ) {
155 - return;
156 - }
157 - form.querySelectorAll( 'a.frm_save_draft' ).forEach(
158 - link => link.style.pointerEvents = 'none'
159 - );
149 + $form.find( 'a.frm_save_draft' ).css( 'pointer-events', 'none' );
160 150 }
161 151
162 152 /**
163 - * Enable the save draft link for a given form object.
153 + * Enable the save draft link for a given jQuery form object
164 154 *
165 155 * @since 4.04.03
166 156 *
167 - * @param {jQuery|HTMLElement} $form
157 + * @param {jQuery} $form
168 158 */
169 159 function enableSaveDraft( $form ) {
170 - const form = $form instanceof jQuery ? $form.get( 0 ) : $form;
171 - if ( ! form ) {
160 + if ( ! $form.length ) {
172 161 return;
173 162 }
174 - form.querySelectorAll( '.frm_save_draft' ).forEach( saveDraftButton => {
163 + $form[ 0 ].querySelectorAll( '.frm_save_draft' ).forEach( saveDraftButton => {
175 164 saveDraftButton.disabled = false;
176 165 saveDraftButton.style.pointerEvents = '';
177 166 } );
178 167 }
@@ -247,14 +236,15 @@
247 236 * @param {Array} errors
248 237 * @return {void}
249 238 */
250 239 function checkValidity( field, errors ) {
240 + let fieldID;
251 241 if ( 'object' !== typeof field.validity || false !== field.validity.valid ) {
252 242 return;
253 243 }
254 244
255 - const fieldID = getFieldId( field, true );
256 - if ( errors[ fieldID ] === undefined ) {
245 + fieldID = getFieldId( field, true );
246 + if ( 'undefined' === typeof errors[ fieldID ] ) {
257 247 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
258 248 }
259 249
260 250 if ( 'function' === typeof field.reportValidity ) {
@@ -293,9 +283,9 @@
293 283 function maybeAddHttpsToUrl( field ) {
294 284 const url = field.value;
295 285 const matches = url.match( /^(https?|ftps?|mailto|news|feed|telnet):/ );
296 286 if ( field.value !== '' && matches === null ) {
297 - field.value = `https://${ url }`;
287 + field.value = 'https://' + url;
298 288 }
299 289 }
300 290
301 291 /**
@@ -305,10 +295,9 @@
305 295 *
306 296 * @return {void}
307 297 */
308 298 function validateField( field ) {
309 - let errors;
310 - let key;
299 + let errors, key;
311 300
312 301 errors = [];
313 302 const fieldContainer = field.closest( '.frm_form_field' );
314 303
@@ -324,12 +313,13 @@
324 313 if ( errors.length < 1 ) {
325 314 validateFieldValue( field, errors, false );
326 315 }
327 316
328 - removeFieldError( fieldContainer );
317 + const $fieldCont = jQuery( fieldContainer );
318 + removeFieldError( $fieldCont );
329 319 if ( Object.keys( errors ).length > 0 ) {
330 320 for ( key in errors ) {
331 - addFieldError( fieldContainer, key, errors );
321 + addFieldError( $fieldCont, key, errors );
332 322 }
333 323 }
334 324 }
335 325
@@ -356,19 +346,15 @@
356 346 } else if ( field.pattern !== null ) {
357 347 checkPatternField( field, errors );
358 348 }
359 349
360 - if ( 'tel' === field.type && shouldCheckConfirmField( field, onSubmit ) ) {
361 - confirmField( field, errors );
362 - }
363 -
364 350 /**
365 351 * @since 6.15 Added `onSubmit` to the data.
366 352 */
367 353 triggerCustomEvent( document, 'frm_validate_field_value', {
368 - field,
369 - errors,
370 - onSubmit
354 + field: field,
355 + errors: errors,
356 + onSubmit: onSubmit
371 357 } );
372 358 }
373 359
374 360 /**
@@ -376,14 +362,12 @@
376 362 * @param {Array} errors
377 363 * @return {Array} Errors
378 364 */
379 365 function checkRequiredField( field, errors ) {
380 - let tempVal;
381 - let i;
382 - let placeholder;
383 - let val = '';
384 - let fieldID = '';
385 - let fileID = field.getAttribute( 'data-frmfile' );
366 + let tempVal, i, placeholder,
367 + val = '',
368 + fieldID = '',
369 + fileID = field.getAttribute( 'data-frmfile' );
386 370
387 371 if ( field.type === 'hidden' && fileID === null && ! isAppointmentField( field ) && ! isInlineDatepickerField( field ) ) {
388 372 return errors;
389 373 }
@@ -388,9 +372,9 @@
388 372 return errors;
389 373 }
390 374
391 375 if ( field.type === 'checkbox' || field.type === 'radio' ) {
392 - document.querySelectorAll( `input[name="${ field.name }"]` ).forEach( function( input ) {
376 + document.querySelectorAll( 'input[name="' + field.name + '"]' ).forEach( function( input ) {
393 377 const requiredField = input.closest( '.frm_required_field' );
394 378 if ( ! requiredField ) {
395 379 return;
396 380 }
@@ -400,14 +384,14 @@
400 384 val = checkedInput.value;
401 385 } );
402 386 } );
403 387 } else if ( field.type === 'file' || fileID ) {
404 - if ( fileID === undefined ) {
388 + if ( typeof fileID === 'undefined' ) {
405 389 fileID = getFieldId( field, true );
406 390 fileID = fileID.replace( 'file', '' );
407 391 }
408 392
409 - if ( errors[ fileID ] === undefined ) {
393 + if ( typeof errors[ fileID ] === 'undefined' ) {
410 394 val = getFileVals( fileID );
411 395 }
412 396 fieldID = fileID;
413 397 } else {
@@ -415,10 +399,9 @@
415 399 // skip hidden other fields
416 400 return errors;
417 401 }
418 402
419 - val = jQuery( field ).val(); // eslint-disable-line no-jquery/no-val
420 -
403 + val = jQuery( field ).val();
421 404 if ( val === null ) {
422 405 val = '';
423 406 } else if ( typeof val !== 'string' ) {
424 407 tempVal = val;
@@ -450,11 +433,9 @@
450 433 // set id for time field
451 434 fieldID = fieldID.replace( '-H', '' ).replace( '-m', '' );
452 435 } else if ( isSignatureField( field ) ) {
453 436 if ( val === '' ) {
454 - const fieldContainer = field.closest( '.frm_form_field' );
455 - const outputField = fieldContainer ? fieldContainer.querySelector( `[name="${ field.getAttribute( 'name' ).replace( '[typed]', '[output]' ) }"]` ) : null;
456 - val = outputField ? outputField.value : '';
437 + val = jQuery( field ).closest( '.frm_form_field' ).find( '[name="' + field.getAttribute( 'name' ).replace( '[typed]', '[output]' ) + '"]' ).val();
457 438 }
458 439 fieldID = fieldID.replace( '-typed', '' );
459 440 }
460 441
@@ -505,14 +486,14 @@
505 486 * @param {string|number} fileID
506 487 * @return {string} File input value.
507 488 */
508 489 function getFileVals( fileID ) {
509 - let val = '';
510 - const fileFields = document.querySelectorAll( `input[name="file${ fileID }"], input[name="file${ fileID }[]"], input[name^="item_meta[${ fileID }]"]` );
490 + let val = '',
491 + fileFields = jQuery( 'input[name="file' + fileID + '"], input[name="file' + fileID + '[]"], input[name^="item_meta[' + fileID + ']"]' );
511 492
512 - fileFields.forEach( function( field ) {
493 + fileFields.each( function() {
513 494 if ( val === '' ) {
514 - val = field.value;
495 + val = this.value;
515 496 }
516 497 } );
517 498 return val;
518 499 }
@@ -522,10 +503,10 @@
522 503 * @param {Array} errors
523 504 * @return {void}
524 505 */
525 506 function checkUrlField( field, errors ) {
526 - let fieldID;
527 - const url = field.value;
507 + let fieldID,
508 + url = field.value;
528 509
529 510 if ( url !== '' && ! /^http(s)?:\/\/(?:localhost|(?:[\da-z\.-]+\.[\da-z\.-]+))/i.test( url ) ) {
530 511 fieldID = getFieldId( field, true );
531 512 if ( ! ( fieldID in errors ) ) {
@@ -566,10 +547,10 @@
566 547 * @param {Object} errors Errors data.
567 548 * @param {boolean} onSubmit Is `true` if the form is being submitted.
568 549 */
569 550 function checkEmailField( field, errors, onSubmit ) {
570 - const fieldID = getFieldId( field, true );
571 - const pattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
551 + const fieldID = getFieldId( field, true ),
552 + pattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
572 553
573 554 // validate the current field we're editing first
574 555 if ( '' !== field.value && pattern.test( field.value ) === false ) {
575 556 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
@@ -600,23 +581,24 @@
600 581 * @param {Array} errors
601 582 * @return {void}
602 583 */
603 584 function confirmField( field, errors ) {
604 - const fieldID = getFieldId( field, true );
605 - const strippedId = field.id.replace( 'conf_', '' );
606 - const strippedFieldID = fieldID.replace( 'conf_', '' );
607 - const confirmField = document.getElementById( strippedId.replace( 'field_', 'field_conf_' ) );
585 + let value, confirmValue, firstField,
586 + fieldID = getFieldId( field, true ),
587 + strippedId = field.id.replace( 'conf_', '' ),
588 + strippedFieldID = fieldID.replace( 'conf_', '' ),
589 + confirmField = document.getElementById( strippedId.replace( 'field_', 'field_conf_' ) );
608 590
609 - if ( ! confirmField || errors[ `conf_${ strippedFieldID }` ] !== undefined ) {
591 + if ( confirmField === null || typeof errors[ 'conf_' + strippedFieldID ] !== 'undefined' ) {
610 592 return;
611 593 }
612 594
613 595 if ( fieldID !== strippedFieldID ) {
614 - const firstField = document.getElementById( strippedId );
615 - const { value } = firstField;
616 - const confirmValue = confirmField.value;
596 + firstField = document.getElementById( strippedId );
597 + value = firstField.value;
598 + confirmValue = confirmField.value;
617 599 if ( value !== confirmValue ) {
618 - errors[ `conf_${ strippedFieldID }` ] = getFieldValidationMessage( confirmField, 'data-confmsg' );
600 + errors[ 'conf_' + strippedFieldID ] = getFieldValidationMessage( confirmField, 'data-confmsg' );
619 601 }
620 602 } else {
621 603 validateField( confirmField );
622 604 }
@@ -627,10 +609,10 @@
627 609 * @param {Array} errors
628 610 * @return {void}
629 611 */
630 612 function checkNumberField( field, errors ) {
631 - let fieldID;
632 - const number = field.value;
613 + let fieldID,
614 + number = field.value;
633 615
634 616 if ( number !== '' && isNaN( number / 1 ) !== false ) {
635 617 fieldID = getFieldId( field, true );
636 618 if ( ! ( fieldID in errors ) ) {
@@ -644,11 +626,11 @@
644 626 * @param {Array} errors
645 627 * @return {void}
646 628 */
647 629 function checkPatternField( field, errors ) {
648 - let fieldID;
649 - const text = field.value;
650 - let format = getFieldValidationMessage( field, 'pattern' );
630 + let fieldID,
631 + text = field.value,
632 + format = getFieldValidationMessage( field, 'pattern' );
651 633
652 634 if ( format !== '' && text !== '' ) {
653 635 fieldID = getFieldId( field, true );
654 636 if ( ! ( fieldID in errors ) ) {
@@ -656,9 +638,9 @@
656 638 if ( ! window.frmProForm.validateIntlPhoneInput( field ) ) {
657 639 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
658 640 }
659 641 } else {
660 - format = new RegExp( `^${ format }$`, 'i' );
642 + format = new RegExp( '^' + format + '$', 'i' );
661 643 if ( format.test( text ) === false ) {
662 644 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
663 645 }
664 646 }
@@ -671,11 +653,13 @@
671 653 *
672 654 * @since 6.5.1
673 655 */
674 656 function setSelectPlaceholderColor() {
675 - const selects = document.querySelectorAll( '.form-field select' );
676 - const styleElement = document.querySelector( '.with_frm_style' );
677 - const textColorDisabled = styleElement ? getComputedStyle( styleElement ).getPropertyValue( '--text-color-disabled' ).trim() : '';
657 + let selects = document.querySelectorAll( '.form-field select' ),
658 + styleElement = document.querySelector( '.with_frm_style' ),
659 + textColorDisabled = styleElement ? getComputedStyle( styleElement ).getPropertyValue( '--text-color-disabled' ).trim() : '',
660 + changeSelectColor;
661 +
678 662 // Exit if there are no select elements or the textColorDisabled property is missing
679 663 if ( ! selects.length || ! textColorDisabled ) {
680 664 return;
681 665 }
@@ -680,9 +664,9 @@
680 664 return;
681 665 }
682 666
683 667 // Function to change the color of a select element
684 - const changeSelectColor = function( select ) {
668 + changeSelectColor = function( select ) {
685 669 if ( select.options[ select.selectedIndex ] && hasClass( select.options[ select.selectedIndex ], 'frm-select-placeholder' ) ) {
686 670 select.style.setProperty( 'color', textColorDisabled, 'important' );
687 671 } else {
688 672 select.style.color = '';
@@ -702,62 +686,51 @@
702 686 }
703 687
704 688 /**
705 689 * @param {HTMLElement|jQuery} object
706 - *
707 - * @return {HTMLElement|false} Captcha element if there is an invisible recaptcha.
690 + * @return {boolean} True if there is an invisible recaptcha.
708 691 */
709 692 function hasInvisibleRecaptcha( object ) {
693 + let recaptcha, recaptchaID, alreadyChecked;
694 +
710 695 if ( isGoingToPrevPage( object ) ) {
711 696 return false;
712 697 }
713 698
714 - const form = object instanceof jQuery ? object.get( 0 ) : object;
715 - if ( ! form ) {
716 - return false;
717 - }
718 -
719 - const recaptcha = form.querySelector( '.frm-g-recaptcha[data-size="invisible"], .g-recaptcha[data-size="invisible"]' );
720 - if ( recaptcha ) {
721 - const recaptchaID = recaptcha.dataset.rid;
722 - const alreadyChecked = grecaptcha.getResponse( recaptchaID );
699 + recaptcha = jQuery( object ).find( '.frm-g-recaptcha[data-size="invisible"], .g-recaptcha[data-size="invisible"]' );
700 + if ( recaptcha.length ) {
701 + recaptchaID = recaptcha.data( 'rid' );
702 + alreadyChecked = grecaptcha.getResponse( recaptchaID );
723 703 if ( alreadyChecked.length === 0 ) {
724 704 return recaptcha;
725 705 }
726 706 }
727 -
728 707 return false;
729 708 }
730 709
731 710 /**
732 - * @param {HTMLElement} invisibleRecaptcha
733 - *
734 - * @return {void}
711 + * @param {jQuery} invisibleRecaptcha
735 712 */
736 713 function executeInvisibleRecaptcha( invisibleRecaptcha ) {
737 - const recaptchaID = invisibleRecaptcha.dataset.rid;
714 + const recaptchaID = invisibleRecaptcha.data( 'rid' );
738 715 grecaptcha.reset( recaptchaID );
739 716 grecaptcha.execute( recaptchaID );
740 717 }
741 718
742 719 function validateRecaptcha( form, errors ) {
743 - const formEl = form instanceof jQuery ? form.get( 0 ) : form;
744 - if ( ! formEl ) {
745 - return errors;
746 - }
720 + let response;
747 721
748 - const recaptcha = formEl.querySelector( '.frm-g-recaptcha' );
749 - if ( ! recaptcha ) {
722 + const $recaptcha = jQuery( form ).find( '.frm-g-recaptcha' );
723 + if ( ! $recaptcha.length ) {
750 724 return errors;
751 725 }
752 726
753 - const recaptchaID = recaptcha.dataset.rid;
754 - let response;
727 + const recaptchaID = $recaptcha.data( 'rid' );
755 728
756 729 try {
757 730 response = grecaptcha.getResponse( recaptchaID );
758 731 } catch ( e ) {
759 - if ( formEl.querySelector( 'input[name="recaptcha_checked"]' ) ) {
732 + if ( jQuery( form ).find( 'input[name="recaptcha_checked"]' ).length ) {
760 733 return errors;
761 734 }
762 735 response = '';
763 736 }
@@ -762,13 +735,11 @@
762 735 response = '';
763 736 }
764 737
765 738 if ( response.length === 0 ) {
766 - const fieldContainer = recaptcha.closest( '.frm_form_field' );
767 - if ( fieldContainer?.id ) {
768 - const fieldID = fieldContainer.id.replace( 'frm_field_', '' ).replace( '_container', '' );
769 - errors[ fieldID ] = '';
770 - }
739 + const fieldContainer = $recaptcha.closest( '.frm_form_field' );
740 + const fieldID = fieldContainer.attr( 'id' ).replace( 'frm_field_', '' ).replace( '_container', '' );
741 + errors[ fieldID ] = '';
771 742 }
772 743
773 744 return errors;
774 745 }
@@ -841,14 +812,16 @@
841 812 * @param {string} action
842 813 * @return {void}
843 814 */
844 815 function getFormErrors( object, action ) {
845 - const fieldsets = object.querySelectorAll( '.frm_form_field' );
846 - fieldsets.forEach( field => field.classList.add( 'frm_doing_ajax' ) );
816 + let fieldset, data, success, error, shouldTriggerEvent;
847 817
848 - const data = `${ jQuery( object ).serialize() }&action=frm_entries_${ action }&nonce=${ frm_js.nonce }`; // eslint-disable-line no-jquery/no-serialize
849 - const shouldTriggerEvent = object.classList.contains( 'frm_trigger_event_on_submit' );
818 + fieldset = jQuery( object ).find( '.frm_form_field' );
819 + fieldset.addClass( 'frm_doing_ajax' );
850 820
821 + data = jQuery( object ).serialize() + '&action=frm_entries_' + action + '&nonce=' + frm_js.nonce; // eslint-disable-line camelcase
822 + shouldTriggerEvent = object.classList.contains( 'frm_trigger_event_on_submit' );
823 +
851 824 const doRedirect = response => {
852 825 jQuery( document ).trigger( 'frmBeforeFormRedirect', [ object, response ] );
853 826
854 827 if ( ! response.openInNewTab ) {
@@ -859,14 +832,17 @@
859 832
860 833 // We don't return here because we're opening in a new tab, the old tab will still update.
861 834 const newTab = window.open( response.redirect, '_blank' );
862 835 if ( ! newTab && response.fallbackMsg && response.content ) {
863 - response.content = response.content.trim().replace( /(<\/div><\/div>)$/, ` ${ response.fallbackMsg }</div></div>` );
836 + response.content = response.content.trim().replace( /(<\/div><\/div>)$/, ' ' + response.fallbackMsg + '</div></div>' );
864 837 }
865 838 };
866 839
867 - const success = function( response ) {
868 - const defaultResponse = {
840 + success = function( response ) {
841 + let defaultResponse, formID, replaceContent, pageOrder, formReturned, contSubmit, delay,
842 + $fieldCont, key, inCollapsedSection, frmTrigger;
843 +
844 + defaultResponse = {
869 845 content: '',
870 846 errors: {},
871 847 pass: false
872 848 };
@@ -882,11 +858,9 @@
882 858 response = defaultResponse;
883 859 }
884 860 }
885 861
886 - let willRedirect = false;
887 -
888 - if ( response.redirect !== undefined ) {
862 + if ( typeof response.redirect !== 'undefined' ) {
889 863 if ( shouldTriggerEvent ) {
890 864 triggerCustomEvent( object, 'frmSubmitEvent' );
891 865 return;
892 866 }
@@ -897,10 +871,8 @@
897 871 }, 1000 * response.delay );
898 872 } else {
899 873 doRedirect( response );
900 874 }
901 -
902 - willRedirect = true;
903 875 }
904 876
905 877 if ( 'string' === typeof response.content && response.content !== '' ) {
906 878 // the form or success message was returned
@@ -910,21 +882,22 @@
910 882 return;
911 883 }
912 884
913 885 removeSubmitLoading( jQuery( object ) );
914 - if ( frm_js.offset != -1 ) {
886 + if ( frm_js.offset != -1 ) { // eslint-disable-line camelcase
915 887 frmFrontForm.scrollMsg( jQuery( object ), false );
916 888 }
917 889
918 - const formIdInput = object.querySelector( 'input[name="form_id"]' );
919 - const formID = formIdInput ? formIdInput.value : '';
890 + formID = jQuery( object ).find( 'input[name="form_id"]' ).val();
920 891 response.content = response.content.replace( / frm_pro_form /g, ' frm_pro_form frm_no_hide ' );
921 - const replaceContent = jQuery( object ).closest( '.frm_forms' ); // eslint-disable-line no-jquery/no-closest
892 + replaceContent = jQuery( object ).closest( '.frm_forms' );
922 893 removeAddedScripts( replaceContent, formID );
923 - const delay = maybeSlideOut( replaceContent, response.content );
894 + delay = maybeSlideOut( replaceContent, response.content );
924 895
925 896 setTimeout(
926 897 function() {
898 + let container, input, previousInput;
899 +
927 900 afterFormSubmittedBeforeReplace( object, response );
928 901
929 902 replaceContent.replaceWith( response.content );
930 903
@@ -930,41 +903,48 @@
930 903
931 904 addUrlParam( response );
932 905
933 906 if ( typeof frmThemeOverride_frmAfterSubmit === 'function' ) { // eslint-disable-line camelcase
934 - const pageOrderInput = document.querySelector( `input[name="frm_page_order_${ formID }"]` );
935 - const pageOrder = pageOrderInput ? pageOrderInput.value : '';
936 - const tempDiv = document.createElement( 'div' );
937 - tempDiv.innerHTML = response.content;
938 - const formReturnedInput = tempDiv.querySelector( 'input[name="form_id"]' );
939 - const formReturned = formReturnedInput ? formReturnedInput.value : '';
907 + pageOrder = jQuery( 'input[name="frm_page_order_' + formID + '"]' ).val();
908 + formReturned = jQuery( response.content ).find( 'input[name="form_id"]' ).val();
940 909 frmThemeOverride_frmAfterSubmit( formReturned, pageOrder, response.content, object );
941 910 }
942 911
912 + if ( typeof response.recaptcha !== 'undefined' ) {
913 + container = jQuery( '#frm_form_' + formID + '_container' ).find( '.frm_fields_container' );
914 + input = '<input type="hidden" name="recaptcha_checked" value="' + response.recaptcha + '">';
915 + previousInput = container.find( 'input[name="recaptcha_checked"]' );
916 +
917 + if ( previousInput.length ) {
918 + previousInput.replaceWith( input );
919 + } else {
920 + container.append( input );
921 + }
922 + }
923 +
943 924 afterFormSubmitted( object, response );
944 925 },
945 926 delay
946 927 );
947 - } else if ( response.errors !== undefined && Object.keys( response.errors ).length ) {
928 + } else if ( Object.keys( response.errors ).length ) {
948 929 // errors were returned
949 930 removeSubmitLoading( jQuery( object ), 'enable' );
950 931
951 932 //show errors
952 - let contSubmit = true;
933 + contSubmit = true;
953 934 removeAllErrors();
954 935
955 - let $fieldCont = null;
936 + $fieldCont = null;
956 937
957 - for ( const key in response.errors ) {
958 - const fieldContEl = object.querySelector( `#frm_field_${ key }_container` );
959 - $fieldCont = fieldContEl ? jQuery( fieldContEl ) : jQuery();
938 + for ( key in response.errors ) {
939 + $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
960 940
961 941 if ( $fieldCont.length ) {
962 - if ( ! $fieldCont.is( ':visible' ) ) { // eslint-disable-line no-jquery/no-is
963 - const inCollapsedSection = $fieldCont.closest( '.frm_toggle_container' ); // eslint-disable-line no-jquery/no-closest, formidable/no-jquery-variable-methods
942 + if ( ! $fieldCont.is( ':visible' ) ) {
943 + inCollapsedSection = $fieldCont.closest( '.frm_toggle_container' );
964 944 if ( inCollapsedSection.length ) {
965 - let frmTrigger = inCollapsedSection.prev();
966 - if ( ! frmTrigger.hasClass( 'frm_trigger' ) ) { // eslint-disable-line formidable/no-jquery-variable-methods
945 + frmTrigger = inCollapsedSection.prev();
946 + if ( ! frmTrigger.hasClass( 'frm_trigger' ) ) {
967 947 // If the frmTrigger object is the section description, check to see if the previous element is the trigger
968 948 frmTrigger = frmTrigger.prev( '.frm_trigger' );
969 949 }
970 950 frmTrigger.trigger( 'click' );
@@ -970,9 +950,9 @@
970 950 frmTrigger.trigger( 'click' );
971 951 }
972 952 }
973 953
974 - if ( $fieldCont.is( ':visible' ) ) { // eslint-disable-line no-jquery/no-is
954 + if ( $fieldCont.is( ':visible' ) ) {
975 955 addFieldError( $fieldCont, key, response.errors );
976 956 contSubmit = false;
977 957 }
978 958 }
@@ -977,10 +957,11 @@
977 957 }
978 958 }
979 959 }
980 960
981 - object.querySelectorAll( '.frm-g-recaptcha, .g-recaptcha, .h-captcha' ).forEach( function( captchaEl ) {
982 - const recaptchaID = captchaEl.dataset.rid;
961 + jQuery( object ).find( '.frm-g-recaptcha, .g-recaptcha, .h-captcha' ).each( function() {
962 + const $recaptcha = jQuery( this ),
963 + recaptchaID = $recaptcha.data( 'rid' );
983 964
984 965 if ( typeof grecaptcha !== 'undefined' && grecaptcha ) {
985 966 if ( recaptchaID ) {
986 967 grecaptcha.reset( recaptchaID );
@@ -987,9 +968,8 @@
987 968 } else {
988 969 grecaptcha.reset();
989 970 }
990 971 }
991 -
992 972 if ( typeof hcaptcha !== 'undefined' && hcaptcha ) {
993 973 hcaptcha.reset();
994 974 }
995 975 } );
@@ -994,9 +974,9 @@
994 974 }
995 975 } );
996 976
997 977 if ( window.turnstile ) {
998 - object.querySelectorAll( '.frm-cf-turnstile' ).forEach(
978 + object.querySelectorAll( '.cf-turnstile' ).forEach(
999 979 turnstileField => turnstileField.dataset.rid && turnstile.reset( turnstileField.dataset.rid )
1000 980 );
1001 981 }
1002 982
@@ -1001,9 +981,9 @@
1001 981 }
1002 982
1003 983 jQuery( document ).trigger( 'frmFormErrors', [ object, response ] );
1004 984
1005 - fieldsets.forEach( field => field.classList.remove( 'frm_doing_ajax' ) );
985 + fieldset.removeClass( 'frm_doing_ajax' );
1006 986 scrollToFirstField( object );
1007 987
1008 988 if ( contSubmit ) {
1009 989 object.submit();
@@ -1010,10 +990,10 @@
1010 990 } else {
1011 991 object.insertAdjacentHTML( 'afterbegin', response.error_message );
1012 992 checkForErrorsAndMaybeSetFocus();
1013 993 }
1014 - } else if ( ! willRedirect ) { // Avoid double submission if redirecting to a page.
1015 - // There may have been a plugin conflict, or the form is not set to submit with ajax.
994 + } else {
995 + // there may have been a plugin conflict, or the form is not set to submit with ajax
1016 996
1017 997 showFileLoading( object );
1018 998
1019 999 object.submit();
@@ -1019,12 +999,10 @@
1019 999 object.submit();
1020 1000 }
1021 1001 };
1022 1002
1023 - const error = function() {
1024 - object.querySelectorAll( 'input[type="submit"], input[type="button"]' ).forEach(
1025 - button => button.disabled = false
1026 - );
1003 + error = function() {
1004 + jQuery( object ).find( 'input[type="submit"], input[type="button"]' ).prop( 'disabled', false );
1027 1005 object.submit();
1028 1006 };
1029 1007
1030 1008 postToAjaxUrl( object, data, success, error );
@@ -1030,20 +1008,22 @@
1030 1008 postToAjaxUrl( object, data, success, error );
1031 1009 }
1032 1010
1033 1011 function postToAjaxUrl( form, data, success, error ) {
1034 - let ajaxUrl = frm_js.ajax_url;
1035 - const action = form.getAttribute( 'action' );
1012 + let ajaxUrl, action, ajaxParams;
1036 1013
1037 - if ( 'string' === typeof action && action.includes( '?action=frm_forms_preview' ) ) {
1014 + ajaxUrl = frm_js.ajax_url; // eslint-disable-line camelcase
1015 + action = form.getAttribute( 'action' );
1016 +
1017 + if ( 'string' === typeof action && -1 !== action.indexOf( '?action=frm_forms_preview' ) ) {
1038 1018 ajaxUrl = action.split( '?action=frm_forms_preview' )[ 0 ];
1039 1019 }
1040 1020
1041 - const ajaxParams = {
1021 + ajaxParams = {
1042 1022 type: 'POST',
1043 1023 url: ajaxUrl,
1044 - data,
1045 - success
1024 + data: data,
1025 + success: success
1046 1026 };
1047 1027
1048 1028 if ( 'function' === typeof error ) {
1049 1029 ajaxParams.error = error;
@@ -1048,16 +1028,14 @@
1048 1028 if ( 'function' === typeof error ) {
1049 1029 ajaxParams.error = error;
1050 1030 }
1051 1031
1052 - jQuery.ajax( ajaxParams ); // eslint-disable-line no-jquery/no-ajax
1032 + jQuery.ajax( ajaxParams );
1053 1033 }
1054 1034
1055 1035 function afterFormSubmitted( object, response ) {
1056 - const tempDiv = document.createElement( 'div' );
1057 - tempDiv.innerHTML = response.content;
1058 - const formCompleted = tempDiv.querySelector( '.frm_message' );
1059 - if ( formCompleted ) {
1036 + const formCompleted = jQuery( response.content ).find( '.frm_message' );
1037 + if ( formCompleted.length ) {
1060 1038 jQuery( document ).trigger( 'frmFormComplete', [ object, response ] );
1061 1039 } else {
1062 1040 jQuery( document ).trigger( 'frmPageChanged', [ object, response ] );
1063 1041 }
@@ -1072,30 +1050,28 @@
1072 1050 * @param {Object} response The response from submitting the form with AJAX.
1073 1051 * @return {void}
1074 1052 */
1075 1053 function afterFormSubmittedBeforeReplace( object, response ) {
1076 - const tempDiv = document.createElement( 'div' );
1077 - tempDiv.innerHTML = response.content;
1078 - const formCompleted = tempDiv.querySelector( '.frm_message' );
1079 - if ( formCompleted ) {
1054 + const formCompleted = jQuery( response.content ).find( '.frm_message' );
1055 + if ( formCompleted.length ) {
1080 1056 triggerCustomEvent( document, 'frmFormCompleteBeforeReplace', { object, response } );
1081 1057 }
1082 1058 }
1083 1059
1084 1060 function removeAddedScripts( formContainer, formID ) {
1085 - const endReplace = document.querySelectorAll( `.frm_end_ajax_${ formID }` );
1061 + const endReplace = jQuery( '.frm_end_ajax_' + formID );
1086 1062 if ( endReplace.length ) {
1087 - formContainer.nextUntil( `.frm_end_ajax_${ formID }` ).remove();
1088 - endReplace.forEach( el => el.remove() );
1063 + formContainer.nextUntil( '.frm_end_ajax_' + formID ).remove();
1064 + endReplace.remove();
1089 1065 }
1090 1066 }
1091 1067
1092 1068 function maybeSlideOut( oldContent, newContent ) {
1093 - let c;
1094 - let newClass = 'frm_slideout';
1095 - if ( newContent.includes( ' frm_slide' ) ) {
1069 + let c,
1070 + newClass = 'frm_slideout';
1071 + if ( newContent.indexOf( ' frm_slide' ) !== -1 ) {
1096 1072 c = oldContent.children();
1097 - if ( newContent.includes( ' frm_going_back' ) ) {
1073 + if ( newContent.indexOf( ' frm_going_back' ) !== -1 ) {
1098 1074 newClass += ' frm_going_back';
1099 1075 }
1100 1076 c.removeClass( 'frm_going_back' );
1101 1077 c.addClass( newClass );
@@ -1105,23 +1081,25 @@
1105 1081 }
1106 1082
1107 1083 function addUrlParam( response ) {
1108 1084 let url;
1109 - if ( history.pushState && response.page !== undefined ) {
1085 + if ( history.pushState && typeof response.page !== 'undefined' ) {
1110 1086 url = addQueryVar( 'frm_page', response.page );
1111 - window.history.pushState( { html: response.html }, '', `?${ url }` );
1087 + window.history.pushState( { html: response.html }, '', '?' + url );
1112 1088 }
1113 1089 }
1114 1090
1115 1091 function addQueryVar( key, value ) {
1092 + let kvp, i, x;
1093 +
1116 1094 key = encodeURI( key );
1117 1095 value = encodeURI( value );
1118 1096
1119 - const kvp = document.location.search.substr( 1 ).split( '&' );
1097 + kvp = document.location.search.substr( 1 ).split( '&' );
1120 1098
1121 - let i = kvp.length;
1099 + i = kvp.length;
1122 1100 while ( i-- ) {
1123 - const x = kvp[ i ].split( '=' );
1101 + x = kvp[ i ].split( '=' );
1124 1102
1125 1103 if ( x[ 0 ] == key ) {
1126 1104 x[ 1 ] = value;
1127 1105 kvp[ i ] = x.join( '=' );
@@ -1136,59 +1114,44 @@
1136 1114 return kvp.join( '&' );
1137 1115 }
1138 1116
1139 1117 function addFieldError( $fieldCont, key, jsErrors ) {
1140 - const container = $fieldCont instanceof jQuery ? $fieldCont.get( 0 ) : $fieldCont;
1118 + let input, id, describedBy, roleString;
1119 + if ( $fieldCont.length && $fieldCont.is( ':visible' ) ) {
1120 + $fieldCont.addClass( 'frm_blank_field' );
1121 + input = $fieldCont.find( 'input, select, textarea' );
1122 + id = getErrorElementId( key, input.get( 0 ) );
1141 1123
1142 - if ( ! container || container.offsetParent === null ) {
1143 - return;
1144 - }
1124 + describedBy = input.attr( 'aria-describedby' );
1145 1125
1146 - container.classList.add( 'frm_blank_field' );
1147 - const input = container.querySelector( 'input, select, textarea' );
1148 - const id = getErrorElementId( key, input );
1149 -
1150 - let describedBy = input ? input.getAttribute( 'aria-describedby' ) : null;
1151 -
1152 - if ( typeof frmThemeOverride_frmPlaceError === 'function' ) { // eslint-disable-line camelcase
1153 - frmThemeOverride_frmPlaceError( key, jsErrors );
1154 - } else {
1155 - let errorHtml;
1156 - if ( jsErrors[ key ].includes( '<div' ) ) {
1157 - errorHtml = jsErrors[ key ];
1126 + if ( typeof frmThemeOverride_frmPlaceError === 'function' ) { // eslint-disable-line camelcase
1127 + frmThemeOverride_frmPlaceError( key, jsErrors );
1158 1128 } else {
1159 - const roleString = frm_js.include_alert_role ? 'role="alert"' : '';
1160 - errorHtml = `<div class="frm_error" ${ roleString } id="${ id }">${ jsErrors[ key ] }</div>`;
1161 - }
1162 - container.insertAdjacentHTML( 'beforeend', errorHtml );
1129 + if ( -1 !== jsErrors[ key ].indexOf( '<div' ) ) {
1130 + $fieldCont.append(
1131 + jsErrors[ key ]
1132 + );
1133 + } else {
1134 + roleString = frm_js.include_alert_role ? 'role="alert"' : ''; // eslint-disable-line camelcase
1135 + $fieldCont.append( '<div class="frm_error" ' + roleString + ' id="' + id + '">' + jsErrors[ key ] + '</div>' );
1136 + }
1163 1137
1164 - if ( input ) {
1165 - if ( ! describedBy ) {
1138 + if ( typeof describedBy === 'undefined' ) {
1166 1139 describedBy = id;
1167 - } else if ( ! describedBy.includes( id ) && ! describedBy.includes( 'frm_error_field_' ) ) {
1168 - const { errorFirst } = input.dataset;
1169 - if ( errorFirst === '0' ) {
1170 - describedBy = `${ describedBy } ${ id }`;
1140 + } else if ( describedBy.indexOf( id ) === -1 && describedBy.indexOf( 'frm_error_field_' ) === -1 ) {
1141 + if ( input.data( 'error-first' ) === 0 ) {
1142 + describedBy = describedBy + ' ' + id;
1171 1143 } else {
1172 - describedBy = `${ id } ${ describedBy }`;
1144 + describedBy = id + ' ' + describedBy;
1173 1145 }
1174 1146 }
1175 - input.setAttribute( 'aria-describedby', describedBy );
1147 +
1148 + input.attr( 'aria-describedby', describedBy );
1176 1149 }
1177 - }
1150 + input.attr( 'aria-invalid', true );
1178 1151
1179 - if ( input ) {
1180 - if ( [ 'radio', 'checkbox' ].includes( input.type ) ) {
1181 - const group = input.closest( '[role="radiogroup"], [role="group"]' );
1182 - if ( group ) {
1183 - group.setAttribute( 'aria-invalid', 'true' );
1184 - }
1185 - } else {
1186 - input.setAttribute( 'aria-invalid', 'true' );
1187 - }
1152 + jQuery( document ).trigger( 'frmAddFieldError', [ $fieldCont, key, jsErrors ] );
1188 1153 }
1189 -
1190 - jQuery( document ).trigger( 'frmAddFieldError', [ jQuery( container ), key, jsErrors ] );
1191 1154 }
1192 1155
1193 1156 /**
1194 1157 * Get the ID to use for an error element added when submitting with AJAX.
@@ -1199,11 +1162,11 @@
1199 1162 */
1200 1163 function getErrorElementId( key, input ) {
1201 1164 if ( isNaN( key ) || ! input || ! input.id ) {
1202 1165 // If key isn't a number, assume it's already in the right format.
1203 - return `frm_error_field_${ key }`;
1166 + return 'frm_error_field_' + key;
1204 1167 }
1205 - return `frm_error_${ input.id }`;
1168 + return 'frm_error_' + input.id;
1206 1169 }
1207 1170
1208 1171 /**
1209 1172 * Removes errors before validating with JS.
@@ -1208,56 +1171,36 @@
1208 1171 /**
1209 1172 * Removes errors before validating with JS.
1210 1173 * This prevents issues with stale errors that has since been fixed.
1211 1174 *
1212 - * @param {HTMLElement|jQuery} fieldCont Field container element.
1175 + * @param {Object} $fieldCont jQuery object.
1213 1176 * @return {void}
1214 1177 */
1215 - function removeFieldError( fieldCont ) {
1216 - const container = fieldCont instanceof jQuery ? fieldCont.get( 0 ) : fieldCont;
1217 - if ( ! container ) {
1218 - return;
1219 - }
1178 + function removeFieldError( $fieldCont ) {
1179 + const errorMessage = $fieldCont.find( '.frm_error' );
1180 + const errorId = errorMessage.attr( 'id' );
1181 + const input = $fieldCont.find( 'input, select, textarea' );
1182 + let describedBy = input.attr( 'aria-describedby' );
1220 1183
1221 - const errorMessage = container.querySelector( '.frm_error' );
1222 - const errorId = errorMessage ? errorMessage.id : '';
1223 - const input = container.querySelector( 'input, select, textarea' );
1224 - let describedBy = input ? input.getAttribute( 'aria-describedby' ) : null;
1225 -
1226 - container.classList.remove( 'frm_blank_field', 'has-error' );
1227 -
1228 - if ( input ) {
1229 - if ( 'true' === input.getAttribute( 'aria-invalid' ) ) {
1230 - input.setAttribute( 'aria-invalid', 'false' );
1231 - } else if ( [ 'radio', 'checkbox' ].includes( input.type ) ) {
1232 - const group = input.closest( '[role="radiogroup"], [role="group"]' );
1233 - if ( group ) {
1234 - group.setAttribute( 'aria-invalid', 'false' );
1235 - }
1236 - }
1184 + const fieldContainer = $fieldCont.get( 0 );
1185 + if ( fieldContainer && fieldContainer.classList ) {
1186 + fieldContainer.classList.remove( 'frm_blank_field', 'has-error' );
1237 1187 }
1238 1188
1239 - if ( errorMessage ) {
1240 - errorMessage.remove();
1241 - }
1189 + errorMessage.remove();
1190 + input.attr( 'aria-invalid', false );
1191 + input.removeAttr( 'aria-describedby' );
1242 1192
1243 - if ( input ) {
1244 - input.removeAttribute( 'aria-describedby' );
1245 - if ( describedBy ) {
1246 - describedBy = describedBy.replace( errorId, '' ).trim();
1247 - if ( describedBy ) {
1248 - input.setAttribute( 'aria-describedby', describedBy );
1249 - }
1250 - }
1193 + if ( typeof describedBy !== 'undefined' ) {
1194 + describedBy = describedBy.replace( errorId, '' );
1195 + input.attr( 'aria-describedby', describedBy );
1251 1196 }
1252 1197 }
1253 1198
1254 1199 function removeAllErrors() {
1255 - document.querySelectorAll( '.form-field' ).forEach( field => {
1256 - field.classList.remove( 'frm_blank_field', 'has-error' );
1257 - } );
1258 - document.querySelectorAll( '.form-field .frm_error' ).forEach( error => error.remove() );
1259 - document.querySelectorAll( '.frm_error_style' ).forEach( error => error.remove() );
1200 + jQuery( '.form-field' ).removeClass( 'frm_blank_field has-error' );
1201 + jQuery( '.form-field .frm_error' ).replaceWith( '' );
1202 + jQuery( '.frm_error_style' ).remove();
1260 1203 }
1261 1204
1262 1205 /**
1263 1206 * @param {HTMLElement|Object} object Form object.
@@ -1280,9 +1223,9 @@
1280 1223 disableSaveDraft( $object );
1281 1224 }
1282 1225
1283 1226 function showLoadingIndicator( $object ) {
1284 - if ( ! $object.hasClass( 'frm_loading_form' ) && ! $object.hasClass( 'frm_loading_prev' ) ) { // eslint-disable-line no-jquery/no-class, formidable/no-jquery-variable-methods
1227 + if ( ! $object.hasClass( 'frm_loading_form' ) && ! $object.hasClass( 'frm_loading_prev' ) ) {
1285 1228 addLoadingClass( $object );
1286 1229 $object.trigger( 'frmStartFormLoading' );
1287 1230 }
1288 1231 }
@@ -1289,44 +1232,45 @@
1289 1232
1290 1233 function addLoadingClass( $object ) {
1291 1234 const loadingClass = isGoingToPrevPage( $object ) ? 'frm_loading_prev' : 'frm_loading_form';
1292 1235
1293 - $object.addClass( loadingClass ); // eslint-disable-line no-jquery/no-class, formidable/no-jquery-variable-methods
1236 + $object.addClass( loadingClass );
1294 1237 }
1295 1238
1296 1239 function isGoingToPrevPage( $object ) {
1297 - return typeof frmProForm !== 'undefined' && frmProForm.goingToPreviousPage( $object );
1240 + return ( typeof frmProForm !== 'undefined' && frmProForm.goingToPreviousPage( $object ) );
1298 1241 }
1299 1242
1300 1243 function removeSubmitLoading( _, enable, processesRunning ) {
1244 + let loadingForm;
1245 +
1301 1246 if ( processesRunning > 0 ) {
1302 1247 return;
1303 1248 }
1304 1249
1305 - document.querySelectorAll( '.frm_loading_form' ).forEach( function( form ) {
1306 - form.classList.remove( 'frm_loading_form', 'frm_loading_prev' );
1307 - jQuery( form ).trigger( 'frmEndFormLoading' );
1250 + loadingForm = jQuery( '.frm_loading_form' );
1251 + loadingForm.removeClass( 'frm_loading_form' );
1252 + loadingForm.removeClass( 'frm_loading_prev' );
1308 1253
1309 - if ( enable === 'enable' ) {
1310 - enableSubmitButton( form );
1311 - enableSaveDraft( form );
1312 - }
1313 - } );
1254 + loadingForm.trigger( 'frmEndFormLoading' );
1255 +
1256 + if ( enable === 'enable' ) {
1257 + enableSubmitButton( loadingForm );
1258 + enableSaveDraft( loadingForm );
1259 + }
1314 1260 }
1315 1261
1316 1262 function showFileLoading( object ) {
1317 - const loading = document.getElementById( 'frm_loading' );
1318 - if ( ! loading ) {
1319 - return;
1263 + let fileval,
1264 + loading = document.getElementById( 'frm_loading' );
1265 + if ( loading !== null ) {
1266 + fileval = jQuery( object ).find( 'input[type=file]' ).val();
1267 + if ( typeof fileval !== 'undefined' && fileval !== '' ) {
1268 + setTimeout( function() {
1269 + jQuery( loading ).fadeIn( 'slow' );
1270 + }, 2000 );
1271 + }
1320 1272 }
1321 -
1322 - const fileInput = object.querySelector( 'input[type=file]' );
1323 - const fileval = fileInput ? fileInput.value : '';
1324 - if ( fileval !== '' ) {
1325 - setTimeout( function() {
1326 - jQuery( loading ).fadeIn( 'slow' ); // eslint-disable-line no-jquery/no-fade
1327 - }, 2000 );
1328 - }
1329 1273 }
1330 1274
1331 1275 /**********************************************
1332 1276 * General Helpers
@@ -1333,9 +1277,9 @@
1333 1277 *********************************************/
1334 1278
1335 1279 function confirmClick() {
1336 1280 /*jshint validthis:true */
1337 - const message = this.dataset.frmconfirm;
1281 + const message = jQuery( this ).data( 'frmconfirm' );
1338 1282 return confirm( message );
1339 1283 }
1340 1284
1341 1285 /**
@@ -1343,12 +1287,11 @@
1343 1287 * If this is a match, the User is autofilling the input on a Webkit browser.
1344 1288 * We want to delete the Honeypot field, otherwise it will get triggered as spam on autocomplete.
1345 1289 */
1346 1290 function onHoneypotFieldChange() {
1347 - /*jshint validthis:true */
1348 - const css = window.getComputedStyle( this ).boxShadow;
1349 - if ( css?.match( /inset/ ) ) {
1350 - this.remove();
1291 + const css = jQuery( this ).css( 'box-shadow' );
1292 + if ( css.match( /inset/ ) ) {
1293 + this.parentNode.removeChild( this );
1351 1294 }
1352 1295 }
1353 1296
1354 1297 /**
@@ -1399,22 +1342,23 @@
1399 1342 return false;
1400 1343 }
1401 1344
1402 1345 function checkForErrorsAndMaybeSetFocus() {
1403 - if ( ! frm_js.focus_first_error ) {
1346 + let errors, element, timeoutCallback;
1347 +
1348 + if ( ! frm_js.focus_first_error ) { // eslint-disable-line camelcase
1404 1349 return;
1405 1350 }
1406 1351
1407 - const errors = document.querySelectorAll( '.frm_form_field .frm_error' );
1352 + errors = document.querySelectorAll( '.frm_form_field .frm_error' );
1408 1353 if ( ! errors.length ) {
1409 1354 return;
1410 1355 }
1411 1356
1412 - let element = errors[ 0 ];
1413 - let timeoutCallback;
1357 + element = errors[ 0 ];
1414 1358 do {
1415 1359 element = element.previousSibling;
1416 - if ( [ 'input', 'select', 'textarea' ].includes( element.nodeName.toLowerCase() ) ) {
1360 + if ( -1 !== [ 'input', 'select', 'textarea' ].indexOf( element.nodeName.toLowerCase() ) ) {
1417 1361 focusInput( element );
1418 1362 break;
1419 1363 }
1420 1364
@@ -1421,9 +1365,9 @@
1421 1365 if ( maybeFocusOnComboSubField( element ) ) {
1422 1366 break;
1423 1367 }
1424 1368
1425 - if ( element.classList !== undefined ) {
1369 + if ( 'undefined' !== typeof element.classList ) {
1426 1370 if ( element.classList.contains( 'html-active' ) ) {
1427 1371 timeoutCallback = function() {
1428 1372 const textarea = element.querySelector( 'textarea' );
1429 1373 if ( null !== textarea ) {
@@ -1476,9 +1420,9 @@
1476 1420 * @param {Function} handler Handler.
1477 1421 * @param {boolean | Object} options Options to be added to `addEventListener()` method. Default is `false`.
1478 1422 */
1479 1423 function documentOn( event, selector, handler, options ) {
1480 - if ( options === undefined ) {
1424 + if ( 'undefined' === typeof options ) {
1481 1425 options = false;
1482 1426 }
1483 1427
1484 1428 document.addEventListener( event, function( e ) {
@@ -1485,9 +1429,9 @@
1485 1429 let target;
1486 1430
1487 1431 // loop parent nodes from the target to the delegation node.
1488 1432 for ( target = e.target; target && target != this; target = target.parentNode ) {
1489 - if ( target.matches && target.matches( selector ) ) {
1433 + if ( target && target.matches && target.matches( selector ) ) {
1490 1434 handler.call( target, e );
1491 1435 break;
1492 1436 }
1493 1437 }
@@ -1494,23 +1438,27 @@
1494 1438 }, options );
1495 1439 }
1496 1440
1497 1441 function initFloatingLabels() {
1498 - const selector = '.frm-show-form .frm_inside_container input, .frm-show-form .frm_inside_container select, .frm-show-form .frm_inside_container textarea';
1499 - const floatClass = 'frm_label_float_top';
1442 + let checkFloatLabel, checkDropdownLabel, runOnLoad, selector, floatClass;
1500 1443
1501 - const checkFloatLabel = function( input ) {
1502 - const container = input.closest( '.frm_inside_container' );
1444 + selector = '.frm-show-form .frm_inside_container input, .frm-show-form .frm_inside_container select, .frm-show-form .frm_inside_container textarea';
1445 + floatClass = 'frm_label_float_top';
1446 +
1447 + checkFloatLabel = function( input ) {
1448 + let container, shouldFloatTop, firstOpt;
1449 +
1450 + container = input.closest( '.frm_inside_container' );
1503 1451 if ( ! container ) {
1504 1452 return;
1505 1453 }
1506 1454
1507 - const shouldFloatTop = input.value || document.activeElement === input;
1455 + shouldFloatTop = input.value || document.activeElement === input;
1508 1456
1509 1457 container.classList.toggle( floatClass, shouldFloatTop );
1510 1458
1511 1459 if ( 'SELECT' === input.tagName ) {
1512 - const firstOpt = input.querySelector( 'option:first-child' );
1460 + firstOpt = input.querySelector( 'option:first-child' );
1513 1461
1514 1462 if ( shouldFloatTop ) {
1515 1463 if ( firstOpt.hasAttribute( 'data-label' ) ) {
1516 1464 firstOpt.textContent = firstOpt.getAttribute( 'data-label' );
@@ -1522,10 +1470,10 @@
1522 1470 }
1523 1471 }
1524 1472 };
1525 1473
1526 - const checkDropdownLabel = function() {
1527 - document.querySelectorAll( `.frm-show-form .frm_inside_container:not(.${ floatClass }) select` ).forEach( function( input ) {
1474 + checkDropdownLabel = function() {
1475 + document.querySelectorAll( '.frm-show-form .frm_inside_container:not(.' + floatClass + ') select' ).forEach( function( input ) {
1528 1476 const firstOpt = input.querySelector( 'option:first-child' );
1529 1477
1530 1478 if ( firstOpt.textContent ) {
1531 1479 firstOpt.setAttribute( 'data-label', firstOpt.textContent );
@@ -1544,10 +1492,14 @@
1544 1492 true
1545 1493 );
1546 1494 } );
1547 1495
1548 - const runOnLoad = function( firstLoad ) {
1549 - if ( firstLoad && document.activeElement && [ 'INPUT', 'SELECT', 'TEXTAREA' ].includes( document.activeElement.tagName ) ) {
1496 + jQuery( document ).on( 'change', selector, function( event ) {
1497 + checkFloatLabel( event.target );
1498 + } );
1499 +
1500 + runOnLoad = function( firstLoad ) {
1501 + if ( firstLoad && document.activeElement && -1 !== [ 'INPUT', 'SELECT', 'TEXTAREA' ].indexOf( document.activeElement.tagName ) ) {
1550 1502 checkFloatLabel( document.activeElement );
1551 1503 } else if ( firstLoad ) {
1552 1504 document.querySelectorAll( '.frm_inside_container' ).forEach(
1553 1505 function( container ) {
@@ -1559,9 +1511,8 @@
1559 1511 );
1560 1512 }
1561 1513
1562 1514 checkDropdownLabel();
1563 - calcProductsTotal();
1564 1515 };
1565 1516
1566 1517 runOnLoad( true );
1567 1518
@@ -1594,10 +1545,10 @@
1594 1545 return true;
1595 1546 }
1596 1547
1597 1548 function maybeClearCustomValidityMessage( event, field ) {
1598 - let key;
1599 - let isInvalid = false;
1549 + let key,
1550 + isInvalid = false;
1600 1551
1601 1552 if ( ! shouldUpdateValidityMessage( field ) ) {
1602 1553 return;
1603 1554 }
@@ -1617,29 +1568,33 @@
1617 1568 }
1618 1569 }
1619 1570
1620 1571 function maybeShowNewTabFallbackMessage() {
1572 + let messageEl;
1573 +
1621 1574 if ( ! window.frmShowNewTabFallback ) {
1622 1575 return;
1623 1576 }
1624 1577
1625 - const messageEl = document.querySelector( `#frm_form_${ frmShowNewTabFallback.formId }_container .frm_message` );
1578 + messageEl = document.querySelector( '#frm_form_' + frmShowNewTabFallback.formId + '_container .frm_message' );
1626 1579 if ( ! messageEl ) {
1627 1580 return;
1628 1581 }
1629 1582
1630 - messageEl.insertAdjacentHTML( 'beforeend', ` ${ frmShowNewTabFallback.message }` );
1583 + messageEl.insertAdjacentHTML( 'beforeend', ' ' + frmShowNewTabFallback.message );
1631 1584 }
1632 1585
1633 1586 function setCustomValidityMessage() {
1634 - const forms = document.getElementsByClassName( 'frm-show-form' );
1635 - const { length } = forms;
1587 + let forms, length, index;
1636 1588
1637 - for ( let index = 0; index < length; ++index ) {
1589 + forms = document.getElementsByClassName( 'frm-show-form' );
1590 + length = forms.length;
1591 +
1592 + for ( index = 0; index < length; ++index ) {
1638 1593 forms[ index ].addEventListener(
1639 1594 'invalid',
1640 1595 function( event ) {
1641 - const { target } = event;
1596 + const target = event.target;
1642 1597
1643 1598 if ( shouldUpdateValidityMessage( target ) ) {
1644 1599 target.setCustomValidity( target.dataset.invmsg );
1645 1600 }
@@ -1653,9 +1608,9 @@
1653 1608 window.addEventListener( 'pageshow', function( event ) {
1654 1609 if ( event.persisted ) {
1655 1610 document.querySelectorAll( '.frm_loading_form' ).forEach(
1656 1611 function( form ) {
1657 - enableSubmitButton( form );
1612 + enableSubmitButton( jQuery( form ) );
1658 1613 }
1659 1614 );
1660 1615 removeSubmitLoading();
1661 1616 }
@@ -1681,9 +1636,9 @@
1681 1636 const uniqueKey = Array.from( window.crypto.getRandomValues( new Uint8Array( 8 ) ) )
1682 1637 .map( b => b.toString( 16 ).padStart( 2, '0' ) )
1683 1638 .join( '' );
1684 1639 const timestamp = Date.now().toString( 16 );
1685 - return `${ uniqueKey }-${ timestamp }`;
1640 + return uniqueKey + '-' + timestamp;
1686 1641 }
1687 1642
1688 1643 /**
1689 1644 * Animates the scroll position of the document.
@@ -1700,8 +1655,9 @@
1700 1655 document.documentElement.scrollTop = end;
1701 1656 return;
1702 1657 }
1703 1658
1659 + /* eslint-disable compat/compat */
1704 1660 const startTime = performance.now();
1705 1661 const step = currentTime => {
1706 1662 const progress = Math.min( ( currentTime - startTime ) / duration, 1 );
1707 1663 document.documentElement.scrollTop = start + ( ( end - start ) * progress );
@@ -1709,462 +1665,22 @@
1709 1665 requestAnimationFrame( step );
1710 1666 }
1711 1667 };
1712 1668 requestAnimationFrame( step );
1669 + /* eslint-enable compat/compat */
1713 1670 }
1714 1671
1715 - /**
1716 - * Make sure that the captcha label for a reCAPTCHA or Turnstile field matches the response input ID.
1717 - * This is determined dynamically, so we check for the ID after the input is rendered.
1718 - * hCaptcha is handled separately, in the frmCaptcha function as it is not rendered explicitly.
1719 - *
1720 - * @since 6.25.1
1721 - *
1722 - * @param {HTMLElement} captcha
1723 - * @return {void}
1724 - */
1725 - function maybeFixCaptchaLabel( captcha ) {
1726 - const form = captcha.closest( 'form' );
1727 - if ( ! form ) {
1728 - return;
1729 - }
1730 -
1731 - const label = form.querySelector( 'label[for="g-recaptcha-response"], label[for="cf-turnstile-response"]' );
1732 - const captchaResponse = form.querySelector( '[name="g-recaptcha-response"], [name="cf-turnstile-response"]' );
1733 -
1734 - if ( label && captchaResponse ) {
1735 - label.htmlFor = captchaResponse.id;
1736 - }
1737 - }
1738 -
1739 - /**
1740 - * Check to make sure the quantity field value is within the min and max values.
1741 - *
1742 - * @param {HTMLElement} input
1743 - * @return {number} The quantity value.
1744 - */
1745 - function checkQuantityFieldMinMax( input ) {
1746 - if ( '' === input.value ) {
1747 - // Leave the value if it is empty.
1748 - return 0;
1749 - }
1750 -
1751 - const val = parseFloat( input.value ? input.value.trim() : 0 );
1752 - if ( isNaN( val ) ) {
1753 - return 0;
1754 - }
1755 -
1756 - let max = input.hasAttribute( 'max' ) ? parseFloat( input.getAttribute( 'max' ) ) : 0;
1757 - let min = input.hasAttribute( 'min' ) ? parseFloat( input.getAttribute( 'min' ) ) : 0;
1758 -
1759 - max = isNaN( max ) ? 0 : max;
1760 - min = isNaN( min ) ? 0 : Math.max( 0, min );
1761 -
1762 - if ( val < min ) {
1763 - input.value = min;
1764 - return min;
1765 - }
1766 -
1767 - if ( 0 !== max && val > max ) {
1768 - input.value = max;
1769 - return max;
1770 - }
1771 -
1772 - return val;
1773 - }
1774 -
1775 - function triggerChange( input, fieldKey ) {
1776 - if ( fieldKey === undefined ) {
1777 - fieldKey = 'dependent';
1778 - }
1779 -
1780 - jQuery( input ).trigger( { type: 'change', selfTriggered: true, frmTriggered: fieldKey } );
1781 - }
1782 -
1783 - /**
1784 - * Calculates the total price.
1785 - *
1786 - * @param {Event|undefined} e The event object.
1787 - * @return {void}
1788 - */
1789 - function calcProductsTotal( e ) {
1790 - if ( 'object' === typeof frmProForm ) {
1791 - // Pro is installed, use the Pro JS.
1792 - return;
1793 - }
1794 -
1795 - if ( typeof __FRMCURR === 'undefined' ) {
1796 - return;
1797 - }
1798 -
1799 - const totalFields = document.querySelectorAll( '[data-frmtotal]' );
1800 - if ( ! totalFields.length ) {
1801 - return;
1802 - }
1803 -
1804 - const formTotals = [];
1805 -
1806 - totalFields.forEach( totalField => {
1807 - let total = 0;
1808 - const form = totalField.closest( 'form' );
1809 -
1810 - if ( ! form ) {
1811 - return;
1812 - }
1813 -
1814 - const formId = form.querySelector( 'input[name="form_id"]' ).value;
1815 - const currency = getCurrency( formId );
1816 -
1817 - if ( undefined !== formTotals[ formId ] ) {
1818 - total = formTotals[ formId ];
1819 - } else {
1820 - form.querySelectorAll( 'input[data-frmprice],select:has([data-frmprice])' ).forEach( function( input ) {
1821 - let quantity = 0;
1822 - let price = 0;
1823 - const isSingle = 'hidden' === input.type;
1824 -
1825 - if ( input.tagName === 'SELECT' ) {
1826 - if ( input.selectedIndex !== -1 ) {
1827 - price = input.options[ input.selectedIndex ].getAttribute( 'data-frmprice' );
1828 - }
1829 - } else {
1830 - if ( ! isSingle && ! input.matches( ':checked' ) ) {
1831 - return;
1832 - }
1833 - price = input.getAttribute( 'data-frmprice' );
1834 - }
1835 -
1836 - if ( ! price ) {
1837 - price = 0;
1838 - } else {
1839 - price = preparePrice( price, currency );
1840 - quantity = getQuantity( input );
1841 - price = parseFloat( quantity ) * parseFloat( price );
1842 - }
1843 -
1844 - if ( 'true' === input.getAttribute( 'data-frmdiscount' ) ) {
1845 - price = price * -1;
1846 - }
1847 -
1848 - total += price;
1849 - } );
1850 -
1851 - formTotals[ formId ] = total;
1852 - }
1853 -
1854 - total = isNaN( total ) ? 0 : total;
1855 -
1856 - // Set a decimal separator for currency if no default for it
1857 - currency.decimal_separator = currency.decimal_separator.trim(); // first remove unnecessary space(s)
1858 - if ( ! currency.decimal_separator.length ) {
1859 - currency.decimal_separator = '.';
1860 - }
1861 -
1862 - totalField.value = roundTotal( total, currency );
1863 - total = normalizeTotal( total, currency );
1864 -
1865 - // because of e.g. fields that might be using this field for calculations
1866 - triggerChange( totalField );
1867 -
1868 - total = formatCurrency( total, currency );
1869 - const formatted = totalField.previousElementSibling;
1870 - if ( formatted?.matches( '.frm_total_formatted' ) ) {
1871 - // Use innerHTML so that currency symbols like Euros can render and not their encoded string value.
1872 - formatted.innerHTML = total;
1873 - return;
1874 - }
1875 -
1876 - const formattedEls = totalField.closest( '.frm_form_field' ).querySelectorAll( '.frm_total_formatted' );
1877 - formattedEls.forEach( formattedEl => {
1878 - // Use innerHTML so that currency symbols like Euros can render and not their encoded string value.
1879 - formattedEl.innerHTML = total;
1880 - } );
1881 - } );
1882 - }
1883 -
1884 - /**
1885 - * Round total and maybe add trailing zeros so formatCurrency has a proper format to work with.
1886 - *
1887 - * @param {number} total The total amount to normalize.
1888 - * @param {Object} currency The currency object containing decimal information.
1889 - * @return {string} The normalized total amount.
1890 - */
1891 - function normalizeTotal( total, currency ) {
1892 - const isLargeTotal = total > Number.MAX_SAFE_INTEGER;
1893 -
1894 - total = roundTotal( total, currency );
1895 -
1896 - return maybeAddTrailingZeroToPrice( total, currency, isLargeTotal );
1897 - }
1898 -
1899 - function roundTotal( total, currency ) {
1900 - const isLargeTotal = total > Number.MAX_SAFE_INTEGER;
1901 -
1902 - if ( ! isLargeTotal ) {
1903 - const { decimals } = currency;
1904 - total = decimals > 0 ? round10( total, decimals ) : Math.ceil( total );
1905 - }
1906 -
1907 - return total;
1908 - }
1909 -
1910 - function round10( value, decimals ) {
1911 - return Number( `${ Math.round( `${ value }e${ decimals }` ) }e-${ decimals }` );
1912 - }
1913 -
1914 - /**
1915 - * Format a numeric value according to the specified currency format settings.
1916 - *
1917 - * @param {string} total The numeric value to format.
1918 - * @param {Object} currency The currency formatting configuration.
1919 - * @return {string} The formatted currency string.
1920 - */
1921 - function formatCurrency( total, currency ) {
1922 - total = maybeAddTrailingZeroToPrice( total, currency );
1923 - if ( total.length && ( total[ total.length - 1 ] === '.' || total[ total.length - 1 ] === currency.decimal_separator ) ) {
1924 - total = total.substr( 0, total.length - 1 );
1925 - }
1926 -
1927 - total = maybeRemoveTrailingZerosFromPrice( total, currency );
1928 - total = addThousands( total, currency );
1929 -
1930 - const leftSymbol = currency.symbol_left ? ( currency.symbol_left + currency.symbol_padding ) : '';
1931 - const rightSymbol = currency.symbol_right ? ( currency.symbol_padding + currency.symbol_right ) : '';
1932 -
1933 - return `${ leftSymbol }${ total }${ rightSymbol }`;
1934 - }
1935 -
1936 - /**
1937 - * Gets currency from form id.
1938 - *
1939 - * @param {number} formId Form ID.
1940 - * @return {Object} Currency object.
1941 - */
1942 - function getCurrency( formId ) {
1943 - if ( undefined !== window.__FRMCURR && undefined !== window.__FRMCURR[ formId ] ) {
1944 - return window.__FRMCURR[ formId ];
1945 - }
1946 -
1947 - return {
1948 - symbol_left: '$',
1949 - symbol_right: '',
1950 - symbol_padding: '',
1951 - thousand_separator: ',',
1952 - decimal_separator: '.',
1953 - decimals: 2,
1954 - };
1955 - }
1956 -
1957 - /**
1958 - * Gets quantity.
1959 - *
1960 - * @param {HTMLElement} field The field element.
1961 - * @return {number} The quantity.
1962 - */
1963 - function getQuantity( field ) {
1964 - const fieldID = frmFrontForm.getFieldId( field, false );
1965 - if ( ! fieldID ) {
1966 - return 0;
1967 - }
1968 -
1969 - const quantityField = getQuantityField( field, fieldID );
1970 - if ( ! quantityField ) {
1971 - // If there is no quantity field, assume 1.
1972 - return 1;
1973 - }
1974 -
1975 - return checkQuantityFieldMinMax( quantityField );
1976 - }
1977 -
1978 - /**
1979 - * Gets quantity field.
1980 - *
1981 - * @param {HTMLElement} element The element.
1982 - * @param {number} fieldID The field ID.
1983 - * @return {HTMLElement|null} The quantity field.
1984 - */
1985 - function getQuantityField( element, fieldID ) {
1986 - const quantityFields = element.closest( 'form' ).querySelectorAll( '[data-frmproduct]' );
1987 - if ( ! quantityFields.length ) {
1988 - return null;
1989 - }
1990 -
1991 - fieldID = fieldID.toString();
1992 -
1993 - return Array.from( quantityFields ).find( element => {
1994 - let ids;
1995 -
1996 - ids = JSON.parse( element.getAttribute( 'data-frmproduct' ).trim() );
1997 - if ( '' === ids ) {
1998 - return false;
1999 - }
2000 -
2001 - // Convert to array if necessary because of existing fields that are already using single product fields.
2002 - ids = 'string' === typeof ids ? [ ids ] : ids;
2003 - return ids.includes( fieldID );
2004 - } );
2005 - }
2006 -
2007 - /**
2008 - * Prepare a price for calculation.
2009 - *
2010 - * @param {number|string} price The price to prepare.
2011 - * @param {Object} currency The currency object containing decimal information.
2012 - * @return {string} The prepared price.
2013 - */
2014 - function preparePrice( price, currency ) {
2015 - if ( ! price ) {
2016 - return 0;
2017 - }
2018 - price = `${ price }`; // convert to string just to be sure
2019 -
2020 - const regex = getRegexForPrice( currency );
2021 -
2022 - const matches = price.match( regex );
2023 - if ( null === matches ) {
2024 - return 0;
2025 - }
2026 -
2027 - price = matches.length ? matches[ matches.length - 1 ] : 0;
2028 - price = price.trim();
2029 -
2030 - // Fix issues with parsing Fr.15.00. The regex catches .15.00.
2031 - // This checks for the leading decimal and removes it.
2032 - if ( currency.decimal_separator === '.' && 3 === price.split( '.' ).length && price[ 0 ] === '.' ) {
2033 - price = price.substr( 1 );
2034 - }
2035 -
2036 - if ( price ) {
2037 - price = maybeUseDecimal( price, currency );
2038 - price = price.split( currency.thousand_separator ).join( '' ).replace( currency.decimal_separator, '.' );
2039 - }
2040 -
2041 - return price;
2042 - }
2043 -
2044 - /**
2045 - * @param {Object} currency The currency object.
2046 - * @return {RegExp} The regular expression object.
2047 - */
2048 - function getRegexForPrice( currency ) {
2049 - let regexString = '[0-9,.';
2050 -
2051 - if ( currency.thousand_separator !== '.' && currency.thousand_separator !== ',' ) {
2052 - regexString += currency.thousand_separator;
2053 - }
2054 - if ( currency.decimal_separator !== '.' && currency.decimal_separator !== ',' ) {
2055 - regexString += currency.decimal_separator;
2056 - }
2057 -
2058 - regexString += ']*\\.?\\,?[0-9]+';
2059 -
2060 - return new RegExp( regexString, 'g' );
2061 - }
2062 -
2063 - /**
2064 - * Maybe replace the decimal separator with the currency's decimal separator.
2065 - *
2066 - * @param {string} price The price string.
2067 - * @param {Object} currency The currency object.
2068 - * @return {string} The modified price string.
2069 - */
2070 - function maybeUseDecimal( price, currency ) {
2071 - let usedForDecimal;
2072 - let priceParts;
2073 - if ( '.' === currency.thousand_separator ) {
2074 - priceParts = price.split( '.' );
2075 - usedForDecimal = 2 === priceParts.length && 2 === priceParts[ 1 ].length;
2076 - if ( usedForDecimal ) {
2077 - price = price.replace( '.', currency.decimal_separator );
2078 - }
2079 - }
2080 - return price;
2081 - }
2082 -
2083 - /**
2084 - * Add trailing zeros to a price if necessary and replace the decimal separator.
2085 - *
2086 - * @param {number|string} price The price to format.
2087 - * @param {Object} currency The currency object containing the decimal separator.
2088 - * @param {boolean} [force=false] Whether to force processing even if the price is not a number.
2089 - * @return {string} The formatted price string.
2090 - */
2091 - function maybeAddTrailingZeroToPrice( price, currency, force = false ) {
2092 - if ( 'number' !== typeof price && ! force ) {
2093 - return price;
2094 - }
2095 -
2096 - price = String( price ); // first convert to string
2097 - const pos = price.indexOf( '.' );
2098 -
2099 - if ( pos === -1 ) {
2100 - price = `${ price }.`;
2101 -
2102 - for ( let n = 0; n < currency.decimals; ++n ) {
2103 - price += '0';
2104 - }
2105 - } else {
2106 - const decimalsString = price.substring( pos + 1 );
2107 - if ( decimalsString.length < currency.decimals ) {
2108 - if ( decimalsString.length < 2 ) {
2109 - price += '0';
2110 - }
2111 -
2112 - for ( let n = 2; n < currency.decimals; ++n ) {
2113 - price += '0';
2114 - }
2115 - }
2116 - }
2117 -
2118 - return price.replace( '.', currency.decimal_separator );
2119 - }
2120 -
2121 - /**
2122 - * Format a numeric string by adding thousand separators.
2123 - *
2124 - * @param {string|number} price The numeric value to format.
2125 - * @param {Object} options Formatting options.
2126 - * @param {string} options.decimal_separator Character used as decimal separator.
2127 - * @param {string} options.thousand_separator Character used as thousand separator.
2128 - *
2129 - * @return {string} The price string with thousand separators.
2130 - */
2131 - function addThousands( price, options ) {
2132 - const split = options.decimal_separator === ''
2133 - ? [ price.toString() ]
2134 - : price.split( options.decimal_separator );
2135 -
2136 - if ( options.thousand_separator ) {
2137 - split[ 0 ] = split[ 0 ].replace( /\B(?=(\d{3})+(?!\d))/g, options.thousand_separator );
2138 - }
2139 -
2140 - return split.join( options.decimal_separator );
2141 - }
2142 -
2143 - /**
2144 - * Maybe remove trailing zeros from a price string.
2145 - *
2146 - * @param {string} price The price string.
2147 - * @param {Object} currency The currency data.
2148 - *
2149 - * @return {string} The price string with trailing zeros removed.
2150 - */
2151 - function maybeRemoveTrailingZerosFromPrice( price, currency ) {
2152 - const split = price.split( currency.decimal_separator );
2153 - if ( 2 !== split.length || split[ 1 ].length <= currency.decimals ) {
2154 - return price;
2155 - }
2156 - if ( 0 === currency.decimals ) {
2157 - return split[ 0 ];
2158 - }
2159 - return `${ split[ 0 ] }${ currency.decimal_separator }${ split[ 1 ].substr( 0, currency.decimals ) }`;
2160 - }
2161 -
2162 1672 return {
2163 - init() {
1673 + init: function() {
2164 1674 jQuery( document ).off( 'submit.formidable', '.frm-show-form' );
2165 1675 jQuery( document ).on( 'submit.formidable', '.frm-show-form', frmFrontForm.submitForm );
2166 1676
1677 + jQuery( '.frm-show-form input[onblur], .frm-show-form textarea[onblur]' ).each( function() {
1678 + if ( jQuery( this ).val() === '' ) {
1679 + jQuery( this ).trigger( 'blur' );
1680 + }
1681 + } );
1682 +
2167 1683 jQuery( document ).on( 'change', '.frm-show-form input[name^="item_meta"], .frm-show-form select[name^="item_meta"], .frm-show-form textarea[name^="item_meta"]', frmFrontForm.fieldValueChanged );
2168 1684
2169 1685 jQuery( document ).on( 'change', '.frm_verify[id^=field_]', onHoneypotFieldChange );
2170 1686
@@ -2191,13 +1707,8 @@
2191 1707 jQuery( document ).on(
2192 1708 'frmPageChanged',
2193 1709 destroyhCaptcha
2194 1710 );
2195 -
2196 - jQuery( document ).on( 'frmAfterAddRow frmAfterRemoveRow', calcProductsTotal );
2197 - jQuery( document ).on( 'change', '[type="checkbox"][data-frmprice],[type="radio"][data-frmprice],[type="hidden"][data-frmprice],select:has([data-frmprice])', calcProductsTotal );
2198 - jQuery( document ).on( 'keyup change', '[data-frmproduct],[type="text"][data-frmprice]', calcProductsTotal );
2199 - calcProductsTotal();
2200 1711 },
2201 1712
2202 1713 getFieldId,
2203 1714
@@ -2207,9 +1718,9 @@
2207 1718 * @param {HTMLElement} captcha
2208 1719 * @param {string} captchaSelector
2209 1720 * @return {void}
2210 1721 */
2211 - renderCaptcha( captcha, captchaSelector ) {
1722 + renderCaptcha: function( captcha, captchaSelector ) {
2212 1723 const rendered = captcha.getAttribute( 'data-rid' ) !== null;
2213 1724 if ( rendered ) {
2214 1725 return;
2215 1726 }
@@ -2216,9 +1727,9 @@
2216 1727
2217 1728 const size = captcha.getAttribute( 'data-size' );
2218 1729 const params = {
2219 1730 sitekey: captcha.getAttribute( 'data-sitekey' ),
2220 - size,
1731 + size: size,
2221 1732 theme: captcha.getAttribute( 'data-theme' )
2222 1733 };
2223 1734
2224 1735 if ( size === 'invisible' ) {
@@ -2234,28 +1745,25 @@
2234 1745 };
2235 1746 }
2236 1747
2237 1748 const activeCaptcha = getSelectedCaptcha( captchaSelector );
2238 - const captchaContainer = typeof turnstile !== 'undefined' && turnstile === activeCaptcha ? `#${ captcha.id }` : captcha.id;
1749 + const captchaContainer = typeof turnstile !== 'undefined' && turnstile === activeCaptcha ? '#' + captcha.id : captcha.id;
2239 1750 const captchaID = activeCaptcha.render( captchaContainer, params );
2240 1751
2241 1752 captcha.setAttribute( 'data-rid', captchaID );
2242 -
2243 - maybeFixCaptchaLabel( captcha );
2244 1753 },
2245 1754
2246 - afterSingleRecaptcha() {
2247 - const recaptcha = document.querySelector( '.frm-show-form .g-recaptcha' );
2248 - const object = recaptcha ? recaptcha.closest( 'form' ) : null;
1755 + afterSingleRecaptcha: function() {
1756 + const object = jQuery( '.frm-show-form .g-recaptcha' ).closest( 'form' )[ 0 ];
2249 1757 frmFrontForm.submitFormNow( object );
2250 1758 },
2251 1759
2252 - afterRecaptcha( _, formID ) {
2253 - const object = document.querySelector( `#frm_form_${ formID }_container form` );
1760 + afterRecaptcha: function( _, formID ) {
1761 + const object = jQuery( '#frm_form_' + formID + '_container form' )[ 0 ];
2254 1762 frmFrontForm.submitFormNow( object );
2255 1763 },
2256 1764
2257 - submitForm( e ) {
1765 + submitForm: function( e ) {
2258 1766 frmFrontForm.submitFormManual( e, this );
2259 1767 },
2260 1768
2261 1769 /**
@@ -2262,10 +1770,21 @@
2262 1770 * @param {Event} e
2263 1771 * @param {HTMLElement} object The form object that is being submitted.
2264 1772 * @return {void}
2265 1773 */
2266 - submitFormManual( e, object ) {
2267 - if ( document.body.classList.contains( 'wp-admin' ) && ! object.closest( '.frmapi-form' ) ) {
1774 + submitFormManual: function( e, object ) {
1775 + let isPro, errors,
1776 + invisibleRecaptcha = hasInvisibleRecaptcha( object ),
1777 + classList = object.className.trim().split( /\s+/gi );
1778 +
1779 + if ( classList && invisibleRecaptcha.length < 1 ) {
1780 + isPro = classList.indexOf( 'frm_pro_form' ) > -1;
1781 + if ( ! isPro ) {
1782 + return;
1783 + }
1784 + }
1785 +
1786 + if ( jQuery( 'body' ).hasClass( 'wp-admin' ) && jQuery( object ).closest( '.frmapi-form' ).length < 1 ) {
2268 1787 return;
2269 1788 }
2270 1789
2271 1790 e.preventDefault();
@@ -2273,16 +1792,14 @@
2273 1792 if ( typeof frmProForm !== 'undefined' && typeof frmProForm.submitAllowed === 'function' && ! frmProForm.submitAllowed( object ) ) {
2274 1793 return;
2275 1794 }
2276 1795
2277 - const errors = frmFrontForm.validateFormSubmit( object );
1796 + errors = frmFrontForm.validateFormSubmit( object );
2278 1797 if ( Object.keys( errors ).length !== 0 ) {
2279 1798 return;
2280 1799 }
2281 1800
2282 - const invisibleRecaptcha = hasInvisibleRecaptcha( object );
2283 -
2284 - if ( invisibleRecaptcha ) {
1801 + if ( invisibleRecaptcha.length ) {
2285 1802 showLoadingIndicator( jQuery( object ) );
2286 1803 executeInvisibleRecaptcha( invisibleRecaptcha );
2287 1804 } else {
2288 1805 showSubmitLoading( jQuery( object ) );
@@ -2290,12 +1807,11 @@
2290 1807 frmFrontForm.submitFormNow( object );
2291 1808 }
2292 1809 },
2293 1810
2294 - submitFormNow( object ) {
2295 - let hasFileFields;
2296 - let antispamInput;
2297 - const classList = object.className.trim().split( /\s+/gi );
1811 + submitFormNow: function( object ) {
1812 + let hasFileFields, antispamInput,
1813 + classList = object.className.trim().split( /\s+/gi );
2298 1814
2299 1815 if ( object.hasAttribute( 'data-token' ) && null === object.querySelector( '[name="antispam_token"]' ) ) {
2300 1816 // include the antispam token on form submit.
2301 1817 antispamInput = document.createElement( 'input' );
@@ -2301,9 +1817,9 @@
2301 1817 antispamInput = document.createElement( 'input' );
2302 1818 antispamInput.type = 'hidden';
2303 1819 antispamInput.name = 'antispam_token';
2304 1820 antispamInput.value = object.getAttribute( 'data-token' );
2305 - object.append( antispamInput );
1821 + object.appendChild( antispamInput );
2306 1822 }
2307 1823
2308 1824 // Add a unique ID, used for duplicate checks.
2309 1825 const uniqueIDInput = document.createElement( 'input' );
@@ -2309,16 +1825,16 @@
2309 1825 const uniqueIDInput = document.createElement( 'input' );
2310 1826 uniqueIDInput.type = 'hidden';
2311 1827 uniqueIDInput.name = 'unique_id';
2312 1828 uniqueIDInput.value = getUniqueKey();
2313 - object.append( uniqueIDInput );
1829 + object.appendChild( uniqueIDInput );
2314 1830
2315 - if ( classList.includes( 'frm_ajax_submit' ) ) {
2316 - const fileInputs = object.querySelectorAll( 'input[type="file"]' );
2317 - hasFileFields = Array.from( fileInputs ).filter( input => !! input.value ).length;
1831 + if ( classList.indexOf( 'frm_ajax_submit' ) > -1 ) {
1832 + hasFileFields = jQuery( object ).find( 'input[type="file"]' ).filter( function() {
1833 + return !! this.value;
1834 + } ).length;
2318 1835 if ( hasFileFields < 1 ) {
2319 - const actionInput = object.querySelector( 'input[name="frm_action"]' );
2320 - const action = actionInput ? actionInput.value : '';
1836 + const action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
2321 1837 frmFrontForm.checkFormErrors( object, action );
2322 1838 } else {
2323 1839 object.submit();
2324 1840 }
@@ -2331,11 +1847,10 @@
2331 1847 * @param {HTMLElement|Object} object Form object. This might be a jQuery object.
2332 1848 *
2333 1849 * @return {Array} List of errors.
2334 1850 */
2335 - validateFormSubmit( object ) {
2336 - const form = object instanceof jQuery ? object.get( 0 ) : object;
2337 - if ( typeof tinyMCE !== 'undefined' && form?.querySelector( '.wp-editor-wrap' ) ) {
1851 + validateFormSubmit: function( object ) {
1852 + if ( typeof tinyMCE !== 'undefined' && jQuery( object ).find( '.wp-editor-wrap' ).length ) {
2338 1853 tinyMCE.triggerSave();
2339 1854 }
2340 1855
2341 1856 jsErrors = [];
@@ -2354,17 +1869,14 @@
2354 1869 /**
2355 1870 * @param {HTMLElement|Object} object Form object. This might be a jQuery object.
2356 1871 * @return {Array} List of errors.
2357 1872 */
2358 - getAjaxFormErrors( object ) {
2359 - let customErrors;
2360 - let key;
2361 - const form = object instanceof jQuery ? object.get( 0 ) : object;
1873 + getAjaxFormErrors: function( object ) {
1874 + let customErrors, key;
2362 1875
2363 1876 jsErrors = validateForm( object );
2364 1877 if ( typeof frmThemeOverride_jsErrors === 'function' ) { // eslint-disable-line camelcase
2365 - const actionInput = form ? form.querySelector( 'input[name="frm_action"]' ) : null;
2366 - const action = actionInput ? actionInput.value : '';
1878 + const action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
2367 1879 customErrors = frmThemeOverride_jsErrors( action, object );
2368 1880 if ( Object.keys( customErrors ).length ) {
2369 1881 for ( key in customErrors ) {
2370 1882 jsErrors[ key ] = customErrors[ key ];
@@ -2383,18 +1895,17 @@
2383 1895 /**
2384 1896 * @param {HTMLElement|Object} object Form object. This might be a jQuery object.
2385 1897 * @return {void}
2386 1898 */
2387 - addAjaxFormErrors( object ) {
2388 - let key;
2389 - const form = object instanceof jQuery ? object.get( 0 ) : object;
1899 + addAjaxFormErrors: function( object ) {
1900 + let key, $fieldCont;
2390 1901 removeAllErrors();
2391 1902
2392 1903 for ( key in jsErrors ) {
2393 - const fieldCont = form ? form.querySelector( `#frm_field_${ key }_container` ) : null;
1904 + $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
2394 1905
2395 - if ( fieldCont ) {
2396 - addFieldError( fieldCont, key, jsErrors );
1906 + if ( $fieldCont.length ) {
1907 + addFieldError( $fieldCont, key, jsErrors );
2397 1908 } else {
2398 1909 // we are unable to show the error, so remove it
2399 1910 delete jsErrors[ key ];
2400 1911 }
@@ -2408,28 +1919,23 @@
2408 1919 checkRequiredField,
2409 1920 showSubmitLoading,
2410 1921 removeSubmitLoading,
2411 1922
2412 - scrollToID( id ) {
1923 + scrollToID: function( id ) {
2413 1924 const object = jQuery( document.getElementById( id ) );
2414 1925 frmFrontForm.scrollMsg( object, false );
2415 1926 },
2416 1927
2417 - scrollMsg( id, object, animate ) {
2418 - let newPos;
2419 - let screenTop;
2420 - let screenBottom;
2421 - let scrollObj = '';
2422 -
2423 - if ( object === undefined ) {
2424 - scrollObj = jQuery( document.getElementById( `frm_form_${ id }_container` ) );
1928 + scrollMsg: function( id, object, animate ) {
1929 + let newPos, m, b, screenTop, screenBottom,
1930 + scrollObj = '';
1931 + if ( typeof object === 'undefined' ) {
1932 + scrollObj = jQuery( document.getElementById( 'frm_form_' + id + '_container' ) );
2425 1933 if ( scrollObj.length < 1 ) {
2426 1934 return;
2427 1935 }
2428 1936 } else if ( typeof id === 'string' ) {
2429 - const formEl = object instanceof jQuery ? object.get( 0 ) : object;
2430 - const fieldEl = formEl ? formEl.querySelector( `#frm_field_${ id }_container` ) : null;
2431 - scrollObj = fieldEl ? jQuery( fieldEl ) : jQuery();
1937 + scrollObj = jQuery( object ).find( '#frm_field_' + id + '_container' );
2432 1938 } else {
2433 1939 scrollObj = id;
2434 1940 }
2435 1941
@@ -2434,17 +1940,17 @@
2434 1940 }
2435 1941
2436 1942 jQuery( scrollObj ).trigger( 'focus' );
2437 1943 newPos = scrollObj.offset().top;
2438 - if ( ! newPos || frm_js.offset === '-1' ) {
1944 + if ( ! newPos || frm_js.offset === '-1' ) { // eslint-disable-line camelcase
2439 1945 return;
2440 1946 }
2441 - newPos = newPos - frm_js.offset;
1947 + newPos = newPos - frm_js.offset; // eslint-disable-line camelcase
2442 1948
2443 - const docMarginTop = getComputedStyle( document.documentElement ).marginTop;
2444 - const bodyMarginTop = getComputedStyle( document.body ).marginTop;
2445 - if ( docMarginTop || bodyMarginTop ) {
2446 - newPos = newPos - parseInt( docMarginTop ) - parseInt( bodyMarginTop );
1949 + m = jQuery( 'html' ).css( 'margin-top' );
1950 + b = jQuery( 'body' ).css( 'margin-top' );
1951 + if ( m || b ) {
1952 + newPos = newPos - parseInt( m ) - parseInt( b );
2447 1953 }
2448 1954
2449 1955 if ( newPos && window.innerHeight ) {
2450 1956 screenTop = document.documentElement.scrollTop || document.body.scrollTop;
@@ -2451,9 +1957,9 @@
2451 1957 screenBottom = screenTop + window.innerHeight;
2452 1958
2453 1959 if ( newPos > screenBottom || newPos < screenTop ) {
2454 1960 // Not in view
2455 - if ( animate === undefined ) {
1961 + if ( typeof animate === 'undefined' ) {
2456 1962 document.documentElement.scrollTop = newPos;
2457 1963 } else {
2458 1964 animateScroll( screenTop, newPos, 500 );
2459 1965 }
@@ -2461,13 +1967,13 @@
2461 1967 }
2462 1968 }
2463 1969 },
2464 1970
2465 - fieldValueChanged( e ) {
1971 + fieldValueChanged: function( e ) {
2466 1972 /*jshint validthis:true */
2467 1973
2468 1974 const fieldId = frmFrontForm.getFieldId( this, false );
2469 - if ( ! fieldId ) {
1975 + if ( ! fieldId || typeof fieldId === 'undefined' ) {
2470 1976 return;
2471 1977 }
2472 1978
2473 1979 if ( e.frmTriggered && e.frmTriggered == fieldId ) {
@@ -2480,9 +1986,9 @@
2480 1986 maybeValidateChange( this );
2481 1987 }
2482 1988 },
2483 1989
2484 - escapeHtml( text ) {
1990 + escapeHtml: function( text ) {
2485 1991 console.warn( 'DEPRECATED: function frmFrontForm.escapeHtml in v6.17' );
2486 1992 return text
2487 1993 .replace( /&/g, '&amp;' )
2488 1994 .replace( /</g, '&lt;' )
@@ -2490,9 +1996,31 @@
2490 1996 .replace( /"/g, '&quot;' )
2491 1997 .replace( /'/g, '&#039;' );
2492 1998 },
2493 1999
2494 - triggerCustomEvent,
2000 + /**
2001 + * This function was used in old back end code in v2.0.
2002 + *
2003 + * @param {string} classes
2004 + * @return {void}
2005 + */
2006 + invisible: function( classes ) {
2007 + console.warn( 'DEPRECATED: function frmFrontForm.invisible in v6.16.3' );
2008 + jQuery( classes ).css( 'visibility', 'hidden' );
2009 + },
2010 +
2011 + /**
2012 + * This function was used in old back end code in v2.0.
2013 + *
2014 + * @param {string} classes
2015 + * @return {void}
2016 + */
2017 + visible: function( classes ) {
2018 + console.warn( 'DEPRECATED: function frmFrontForm.visible in v6.16.3' );
2019 + jQuery( classes ).css( 'visibility', 'visible' );
2020 + },
2021 +
2022 + triggerCustomEvent: triggerCustomEvent,
2495 2023 documentOn
2496 2024 };
2497 2025 }
2498 2026
@@ -2505,32 +2033,13 @@
2505 2033 function frmRecaptcha() {
2506 2034 frmCaptcha( '.frm-g-recaptcha' );
2507 2035 }
2508 2036
2509 -function frmHcaptcha() {
2510 - frmCaptcha( '.h-captcha' );
2511 -}
2512 -
2513 2037 function frmTurnstile() {
2514 - frmCaptcha( '.frm-cf-turnstile' );
2038 + frmCaptcha( '.cf-turnstile' );
2515 2039 }
2516 2040
2517 2041 function frmCaptcha( captchaSelector ) {
2518 - if ( '.h-captcha' === captchaSelector ) {
2519 - // hCaptcha is still rendered implicitly, so we only want to handle the label and exit early.
2520 - // Match the hcaptcha labels to the hcaptcha response fields.
2521 - const captchaLabels = document.querySelectorAll( 'label[for="h-captcha-response"]' );
2522 - if ( captchaLabels.length ) {
2523 - captchaLabels.forEach( label => {
2524 - const captchaResponse = label.closest( 'form' )?.querySelector( '[name="h-captcha-response"]' );
2525 - if ( captchaResponse ) {
2526 - label.htmlFor = captchaResponse.id;
2527 - }
2528 - } );
2529 - }
2530 - return;
2531 - }
2532 -
2533 2042 let c;
2534 2043 const captchas = document.querySelectorAll( captchaSelector );
2535 2044 const cl = captchas.length;
2536 2045 for ( c = 0; c < cl; c++ ) {
@@ -2539,9 +2048,9 @@
2539 2048 const captcha = captchas[ c ];
2540 2049 if ( ! formIsVisible ) {
2541 2050 // If the form is not visible, try again later in 400ms.
2542 2051 // This fixes issues where the form fades visible on page load.
2543 - // Or when the form is inside of a modal.
2052 + // Or whne the form is inside of a modal.
2544 2053 const interval = setInterval(
2545 2054 function() {
2546 2055 if ( closestForm && closestForm.offsetParent !== null ) {
2547 2056 frmFrontForm.renderCaptcha( captcha, captchaSelector );
@@ -2559,9 +2068,9 @@
2559 2068 function getSelectedCaptcha( captchaSelector ) {
2560 2069 if ( captchaSelector === '.frm-g-recaptcha' ) {
2561 2070 return grecaptcha;
2562 2071 }
2563 - if ( document.querySelector( '.frm-cf-turnstile' ) ) {
2072 + if ( document.querySelector( '.cf-turnstile' ) ) {
2564 2073 return turnstile;
2565 2074 }
2566 2075 return hcaptcha;
2567 2076 }