PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.9
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.9
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
formidable / js / formidable.js

formidable.js in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.9, at js/formidable.js

1,985 lines 54.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* exported frmRecaptcha, frmAfterRecaptcha, frmUpdateField */
2
3 var frmFrontForm;
4
5 function frmFrontFormJS() {
6 'use strict';
7
8 /*global jQuery:false, frm_js, grecaptcha, hcaptcha, turnstile, frmProForm, tinyMCE */
9 /*global frmThemeOverride_jsErrors, frmThemeOverride_frmPlaceError, frmThemeOverride_frmAfterSubmit */
10
11 var action = '';
12 var jsErrors = [];
13
14 /**
15 * Maybe add polyfills.
16 *
17 * @since 5.4
18 */
19 function maybeAddPolyfills() {
20 var i;
21 if ( ! Element.prototype.matches ) {
22 // IE9 supports matches but as msMatchesSelector instead.
23 Element.prototype.matches = Element.prototype.msMatchesSelector;
24 }
25
26 if ( ! Element.prototype.closest ) {
27 Element.prototype.closest = function( s ) {
28 var el = this;
29
30 do {
31 if ( el.matches( s ) ) {
32 return el;
33 }
34 el = el.parentElement || el.parentNode;
35 } while ( el !== null && el.nodeType === 1 );
36
37 return null;
38 };
39 }
40
41 // NodeList.forEach().
42 if ( window.NodeList && ! NodeList.prototype.forEach ) {
43 NodeList.prototype.forEach = function( callback, thisArg ) {
44 thisArg = thisArg || window;
45 for ( i = 0; i < this.length; i++ ) {
46 callback.call( thisArg, this[ i ], i, this );
47 }
48 };
49 }
50 }
51
52 /**
53 * Triggers custom JS event.
54 *
55 * @since 5.5.3
56 *
57 * @param {HTMLElement} el The HTML element.
58 * @param {String} eventName Event name.
59 * @param {mixed} data The passed data.
60 */
61 function triggerCustomEvent( el, eventName, data ) {
62 var event;
63
64 if ( typeof window.CustomEvent === 'function' ) {
65 event = new CustomEvent( eventName );
66 } else if ( document.createEvent ) {
67 event = document.createEvent( 'HTMLEvents' );
68 event.initEvent( eventName, false, true );
69 } else {
70 return;
71 }
72
73 event.frmData = data;
74
75 el.dispatchEvent( event );
76 }
77
78 /* Get the ID of the field that changed*/
79 function getFieldId( field, fullID ) {
80 var nameParts, fieldId,
81 isRepeating = false,
82 fieldName = '';
83 if ( field instanceof jQuery ) {
84 fieldName = field.attr( 'name' );
85 } else {
86 fieldName = field.name;
87 }
88
89 if ( typeof fieldName === 'undefined' ) {
90 fieldName = '';
91 }
92
93 if ( fieldName === '' ) {
94 if ( field instanceof jQuery ) {
95 fieldName = field.data( 'name' );
96 } else {
97 fieldName = field.getAttribute( 'data-name' );
98 }
99
100 if ( typeof fieldName === 'undefined' ) {
101 fieldName = '';
102 }
103
104 if ( fieldName !== '' && fieldName ) {
105 return fieldName;
106 }
107 return 0;
108 }
109
110 nameParts = fieldName.replace( 'item_meta[', '' ).replace( '[]', '' ).split( ']' );
111 //TODO: Fix this for checkboxes and address fields
112 if ( nameParts.length < 1 ) {
113 return 0;
114 }
115 nameParts = nameParts.filter( function( n ) {
116 return n !== '';
117 });
118
119 fieldId = nameParts[0];
120
121 if ( nameParts.length === 1 ) {
122 return fieldId;
123 }
124
125 if ( nameParts[1] === '[form' || nameParts[1] === '[row_ids' ) {
126 return 0;
127 }
128
129 // Check if 'this' is in a repeating section
130 if ( jQuery( 'input[name="item_meta[' + fieldId + '][form]"]' ).length ) {
131
132 // this is a repeatable section with name: item_meta[repeating-section-id][row-id][field-id]
133 fieldId = nameParts[2].replace( '[', '' );
134 isRepeating = true;
135 }
136
137 // Check if 'this' is an other text field and get field ID for it
138 if ( 'other' === fieldId ) {
139 if ( isRepeating ) {
140 // name for other fields: item_meta[370][0][other][414]
141 fieldId = nameParts[3].replace( '[', '' );
142 } else {
143 // Other field name: item_meta[other][370]
144 fieldId = nameParts[1].replace( '[', '' );
145 }
146 }
147
148 if ( fullID === true ) {
149 // For use in the container div id
150 if ( fieldId === nameParts[0]) {
151 fieldId = fieldId + '-' + nameParts[1].replace( '[', '' );
152 } else {
153 fieldId = fieldId + '-' + nameParts[0] + '-' + nameParts[1].replace( '[', '' );
154 }
155 }
156
157 return fieldId;
158 }
159
160 /**
161 * Disable the submit button for a given jQuery form object
162 *
163 * @since 2.03.02
164 *
165 * @param {object} $form
166 */
167 function disableSubmitButton( $form ) {
168 $form.find( 'input[type="submit"], input[type="button"], button[type="submit"], button.frm_save_draft' ).attr( 'disabled', 'disabled' );
169 }
170
171 /**
172 * Enable the submit button for a given jQuery form object
173 *
174 * @since 2.03.02
175 *
176 * @param {object} $form
177 */
178 function enableSubmitButton( $form ) {
179 $form.find( 'input[type="submit"], input[type="button"], button[type="submit"]' ).prop( 'disabled', false );
180 }
181
182 /**
183 * Disable the save draft link for a given jQuery form object
184 *
185 * @since 4.04.03
186 *
187 * @param {object} $form
188 */
189 function disableSaveDraft( $form ) {
190 $form.find( 'a.frm_save_draft' ).css( 'pointer-events', 'none' );
191 }
192
193 /**
194 * Enable the save draft link for a given jQuery form object
195 *
196 * @since 4.04.03
197 *
198 * @param {object} $form
199 */
200 function enableSaveDraft( $form ) {
201 $form.find( 'a.frm_save_draft' ).css( 'pointer-events', '' );
202 }
203
204 function validateForm( object ) {
205 var errors, r, rl, n, nl, fields, field, requiredFields;
206
207 errors = [];
208
209 // Make sure required text field is filled in
210 requiredFields = jQuery( object ).find(
211 '.frm_required_field:visible input, .frm_required_field:visible select, .frm_required_field:visible textarea'
212 ).filter( ':not(.frm_optional)' );
213 if ( requiredFields.length ) {
214 for ( r = 0, rl = requiredFields.length; r < rl; r++ ) {
215 if ( hasClass( requiredFields[r], 'ed_button' ) ) {
216 // skip rich text field buttons.
217 continue;
218 }
219 errors = checkRequiredField( requiredFields[r], errors );
220 }
221 }
222
223 fields = jQuery( object ).find( 'input,select,textarea' );
224 if ( fields.length ) {
225 for ( n = 0, nl = fields.length; n < nl; n++ ) {
226 field = fields[n];
227 if ( '' === field.value ) {
228 if ( 'number' === field.type ) {
229 // A number field will return an empty string when it is invalid.
230 checkValidity( field, errors );
231 }
232 continue;
233 }
234
235 validateFieldValue( field, errors );
236 checkValidity( field, errors );
237 }
238 }
239
240 errors = validateRecaptcha( object, errors );
241
242 return errors;
243 }
244
245 /**
246 * Check the ValidityState interface for the field.
247 * If it is invalid, show an error for it.
248 *
249 * @param {HTMLElement} field
250 * @param {Array} errors
251 * @returns
252 */
253 function checkValidity( field, errors ) {
254 var fieldID;
255 if ( 'object' !== typeof field.validity || false !== field.validity.valid ) {
256 return;
257 }
258
259 fieldID = getFieldId( field, true );
260 if ( 'undefined' === typeof errors[ fieldID ]) {
261 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
262 }
263
264 if ( 'function' === typeof field.reportValidity ) {
265 // This triggers an error pop up.
266 field.reportValidity();
267 }
268 }
269
270 /**
271 * @since 5.0.10
272 *
273 * @param {object} element
274 * @param {string} targetClass
275 * @returns {boolean}
276 */
277 function hasClass( element, targetClass ) {
278 var className = ' ' + element.className + ' ';
279 return -1 !== className.indexOf( ' ' + targetClass + ' ' );
280 }
281
282 function maybeValidateChange( field ) {
283 if ( field.type === 'url' ) {
284 maybeAddHttpToUrl( field );
285 }
286 if ( jQuery( field ).closest( 'form' ).hasClass( 'frm_js_validate' ) ) {
287 validateField( field );
288 }
289 }
290
291 function maybeAddHttpToUrl( field ) {
292 var url = field.value;
293 var matches = url.match( /^(https?|ftps?|mailto|news|feed|telnet):/ );
294 if ( field.value !== '' && matches === null ) {
295 field.value = 'http://' + url;
296 }
297 }
298
299 function validateField( field ) {
300 var key,
301 errors = [],
302 $fieldCont = jQuery( field ).closest( '.frm_form_field' );
303
304 if ( $fieldCont.hasClass( 'frm_required_field' ) && ! jQuery( field ).hasClass( 'frm_optional' ) ) {
305 errors = checkRequiredField( field, errors );
306 }
307
308 if ( errors.length < 1 ) {
309 validateFieldValue( field, errors );
310 }
311
312 removeFieldError( $fieldCont );
313 if ( Object.keys( errors ).length > 0 ) {
314 for ( key in errors ) {
315 addFieldError( $fieldCont, key, errors );
316 }
317 }
318 }
319
320 function validateFieldValue( field, errors ) {
321 if ( field.type === 'hidden' ) {
322 // don't validate
323 } else if ( field.type === 'number' ) {
324 checkNumberField( field, errors );
325 } else if ( field.type === 'email' ) {
326 checkEmailField( field, errors );
327 } else if ( field.type === 'password' ) {
328 checkPasswordField( field, errors );
329 } else if ( field.type === 'url' ) {
330 checkUrlField( field, errors );
331 } else if ( field.pattern !== null ) {
332 checkPatternField( field, errors );
333 }
334
335 triggerCustomEvent( document, 'frm_validate_field_value', {
336 field: field,
337 errors: errors
338 });
339 }
340
341 function checkRequiredField( field, errors ) {
342 var checkGroup, tempVal, i, placeholder,
343 val = '',
344 fieldID = '',
345 fileID = field.getAttribute( 'data-frmfile' );
346
347 if ( field.type === 'hidden' && fileID === null && ! isAppointmentField( field ) && ! isInlineDatepickerField( field ) ) {
348 return errors;
349 }
350
351 if ( field.type === 'checkbox' || field.type === 'radio' ) {
352 checkGroup = jQuery( 'input[name="' + field.name + '"]' ).closest( '.frm_required_field' ).find( 'input:checked' );
353 jQuery( checkGroup ).each( function() {
354 val = this.value;
355 });
356 } else if ( field.type === 'file' || fileID ) {
357 if ( typeof fileID === 'undefined' ) {
358 fileID = getFieldId( field, true );
359 fileID = fileID.replace( 'file', '' );
360 }
361
362 if ( typeof errors[ fileID ] === 'undefined' ) {
363 val = getFileVals( fileID );
364 }
365 fieldID = fileID;
366 } else {
367 if ( hasClass( field, 'frm_pos_none' ) ) {
368 // skip hidden other fields
369 return errors;
370 }
371
372 val = jQuery( field ).val();
373 if ( val === null ) {
374 val = '';
375 } else if ( typeof val !== 'string' ) {
376 tempVal = val;
377 val = '';
378 for ( i = 0; i < tempVal.length; i++ ) {
379 if ( tempVal[i] !== '' ) {
380 val = tempVal[i];
381 }
382 }
383 }
384
385 if ( hasClass( field, 'frm_other_input' ) ) {
386 fieldID = getFieldId( field, false );
387
388 if ( val === '' ) {
389 field = document.getElementById( field.id.replace( '-otext', '' ) );
390 }
391 } else {
392 fieldID = getFieldId( field, true );
393 }
394
395 if ( hasClass( field, 'frm_time_select' ) ) {
396 // set id for time field
397 fieldID = fieldID.replace( '-H', '' ).replace( '-m', '' );
398 } else if ( isSignatureField( field ) ) {
399 if ( val === '' ) {
400 val = jQuery( field ).closest( '.frm_form_field' ).find( '[name="' + field.getAttribute( 'name' ).replace( '[typed]', '[output]' ) + '"]' ).val();
401 }
402 fieldID = fieldID.replace( '-typed', '' );
403 }
404
405 placeholder = field.getAttribute( 'data-frmplaceholder' );
406 if ( placeholder !== null && val === placeholder ) {
407 val = '';
408 }
409 }
410
411 if ( val === '' ) {
412 if ( fieldID === '' ) {
413 fieldID = getFieldId( field, true );
414 }
415 if ( ! ( fieldID in errors ) ) {
416 errors[ fieldID ] = getFieldValidationMessage( field, 'data-reqmsg' );
417 }
418 }
419
420 return errors;
421 }
422
423 function isSignatureField( field ) {
424 var name = field.getAttribute( 'name' );
425 return 'string' === typeof name && '[typed]' === name.substr( -7 );
426 }
427
428 function isAppointmentField( field ) {
429 return hasClass( field, 'ssa_appointment_form_field_appointment_id' );
430 }
431
432 function isInlineDatepickerField( field ) {
433 return 'hidden' === field.type && '_alt' === field.id.substr( -4 ) && hasClass( field.nextElementSibling, 'frm_date_inline' );
434 }
435
436 function getFileVals( fileID ) {
437 var val = '',
438 fileFields = jQuery( 'input[name="file' + fileID + '"], input[name="file' + fileID + '[]"], input[name^="item_meta[' + fileID + ']"]' );
439
440 fileFields.each( function() {
441 if ( val === '' ) {
442 val = this.value;
443 }
444 });
445 return val;
446 }
447
448 function checkUrlField( field, errors ) {
449 var fieldID,
450 url = field.value;
451
452 if ( url !== '' && ! /^http(s)?:\/\/(?:localhost|(?:[\da-z\.-]+\.[\da-z\.-]+))/i.test( url ) ) {
453 fieldID = getFieldId( field, true );
454 if ( ! ( fieldID in errors ) ) {
455 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
456 }
457 }
458 }
459
460 function checkEmailField( field, errors ) {
461 var fieldID = getFieldId( field, true ),
462 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;
463
464 // validate the current field we're editing first
465 if ( '' !== field.value && pattern.test( field.value ) === false ) {
466 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
467 }
468
469 confirmField( field, errors );
470 }
471
472 function checkPasswordField( field, errors ) {
473 confirmField( field, errors );
474 }
475
476 function confirmField( field, errors ) {
477 var value, confirmValue, firstField,
478 fieldID = getFieldId( field, true ),
479 strippedId = field.id.replace( 'conf_', '' ),
480 strippedFieldID = fieldID.replace( 'conf_', '' ),
481 confirmField = document.getElementById( strippedId.replace( 'field_', 'field_conf_' ) );
482
483 if ( confirmField === null || typeof errors[ 'conf_' + strippedFieldID ] !== 'undefined' ) {
484 return;
485 }
486
487 if ( fieldID !== strippedFieldID ) {
488 firstField = document.getElementById( strippedId );
489 value = firstField.value;
490 confirmValue = confirmField.value;
491 if ( value !== confirmValue ) {
492 errors[ 'conf_' + strippedFieldID ] = getFieldValidationMessage( confirmField, 'data-confmsg' );
493 }
494 } else {
495 validateField( confirmField );
496 }
497 }
498
499 function checkNumberField( field, errors ) {
500 var fieldID,
501 number = field.value;
502
503 if ( number !== '' && isNaN( number / 1 ) !== false ) {
504 fieldID = getFieldId( field, true );
505 if ( ! ( fieldID in errors ) ) {
506 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
507 }
508 }
509 }
510
511 function checkPatternField( field, errors ) {
512 var fieldID,
513 text = field.value,
514 format = getFieldValidationMessage( field, 'pattern' );
515
516 if ( format !== '' && text !== '' ) {
517 fieldID = getFieldId( field, true );
518 if ( ! ( fieldID in errors ) ) {
519 format = new RegExp( '^' + format + '$', 'i' );
520 if ( format.test( text ) === false ) {
521 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
522 }
523 }
524 }
525 }
526
527 /**
528 * Set color for select placeholders.
529 *
530 * @since 6.5.1
531 */
532 function setSelectPlaceholderColor() {
533 var selects = document.querySelectorAll( '.form-field select' ),
534 styleElement = document.querySelector( '.with_frm_style' ),
535 textColorDisabled = styleElement ? getComputedStyle( styleElement ).getPropertyValue( '--text-color-disabled' ).trim() : '',
536 changeSelectColor;
537
538 // Exit if there are no select elements or the textColorDisabled property is missing
539 if ( ! selects.length || ! textColorDisabled ) {
540 return;
541 }
542
543 // Function to change the color of a select element
544 changeSelectColor = function( select ) {
545 if ( select.options[select.selectedIndex] && hasClass( select.options[select.selectedIndex], 'frm-select-placeholder' ) ) {
546 select.style.setProperty( 'color', textColorDisabled, 'important' );
547 } else {
548 select.style.color = '';
549 }
550 };
551
552 // Use a loop to iterate through each select element
553 Array.prototype.forEach.call( selects, function( select ) {
554 // Apply the color change to each select element
555 changeSelectColor( select );
556
557 // Add an event listener for future changes
558 select.addEventListener( 'change', function() {
559 changeSelectColor( select );
560 });
561 });
562 }
563
564 function hasInvisibleRecaptcha( object ) {
565 var recaptcha, recaptchaID, alreadyChecked;
566
567 if ( isGoingToPrevPage( object ) ) {
568 return false;
569 }
570
571 recaptcha = jQuery( object ).find( '.frm-g-recaptcha[data-size="invisible"], .g-recaptcha[data-size="invisible"]' );
572 if ( recaptcha.length ) {
573 recaptchaID = recaptcha.data( 'rid' );
574 alreadyChecked = grecaptcha.getResponse( recaptchaID );
575 if ( alreadyChecked.length === 0 ) {
576 return recaptcha;
577 } else {
578 return false;
579 }
580 } else {
581 return false;
582 }
583 }
584
585 function executeInvisibleRecaptcha( invisibleRecaptcha ) {
586 var recaptchaID = invisibleRecaptcha.data( 'rid' );
587 grecaptcha.reset( recaptchaID );
588 grecaptcha.execute( recaptchaID );
589 }
590
591 function validateRecaptcha( form, errors ) {
592 var recaptchaID, response, fieldContainer, fieldID,
593 $recaptcha = jQuery( form ).find( '.frm-g-recaptcha' );
594 if ( $recaptcha.length ) {
595 recaptchaID = $recaptcha.data( 'rid' );
596
597 try {
598 response = grecaptcha.getResponse( recaptchaID );
599 } catch ( e ) {
600 if ( jQuery( form ).find( 'input[name="recaptcha_checked"]' ).length ) {
601 return errors;
602 } else {
603 response = '';
604 }
605 }
606
607 if ( response.length === 0 ) {
608 fieldContainer = $recaptcha.closest( '.frm_form_field' );
609 fieldID = fieldContainer.attr( 'id' ).replace( 'frm_field_', '' ).replace( '_container', '' );
610 errors[ fieldID ] = '';
611 }
612 }
613 return errors;
614 }
615
616 function getFieldValidationMessage( field, messageType ) {
617 var msg, errorHtml;
618
619 msg = field.getAttribute( messageType );
620 if ( null === msg ) {
621 msg = '';
622 }
623
624 if ( '' !== msg && shouldWrapErrorHtmlAroundMessageType( messageType ) ) {
625 errorHtml = field.getAttribute( 'data-error-html' );
626 if ( null !== errorHtml ) {
627 errorHtml = errorHtml.replace( /\+/g, '%20' );
628 msg = decodeURIComponent( errorHtml ).replace( '[error]', msg );
629 msg = msg.replace( '[key]', getFieldId( field, false ) );
630 }
631 }
632
633 return msg;
634 }
635
636 function shouldWrapErrorHtmlAroundMessageType( type ) {
637 return 'pattern' !== type;
638 }
639
640 function shouldJSValidate( object ) {
641 var validate = jQuery( object ).hasClass( 'frm_js_validate' );
642 if ( validate && typeof frmProForm !== 'undefined' && ( frmProForm.savingDraft( object ) || frmProForm.goingToPreviousPage( object ) ) ) {
643 validate = false;
644 }
645
646 return validate;
647 }
648
649 function getFormErrors( object, action ) {
650 var fieldset, data, success, error, shouldTriggerEvent;
651
652 if ( typeof action === 'undefined' ) {
653 jQuery( object ).find( 'input[name="frm_action"]' ).val();
654 }
655
656 fieldset = jQuery( object ).find( '.frm_form_field' );
657 fieldset.addClass( 'frm_doing_ajax' );
658
659 data = jQuery( object ).serialize() + '&action=frm_entries_' + action + '&nonce=' + frm_js.nonce; // eslint-disable-line camelcase
660 shouldTriggerEvent = object.classList.contains( 'frm_trigger_event_on_submit' );
661
662 success = function( response ) {
663 var defaultResponse, formID, replaceContent, pageOrder, formReturned, contSubmit, delay,
664 $fieldCont, key, inCollapsedSection, frmTrigger, newTab;
665
666 defaultResponse = {
667 content: '',
668 errors: {},
669 pass: false
670 };
671
672 if ( response === null ) {
673 response = defaultResponse;
674 }
675
676 response = response.replace( /^\s+|\s+$/g, '' );
677 if ( response.indexOf( '{' ) === 0 ) {
678 response = JSON.parse( response );
679 } else {
680 response = defaultResponse;
681 }
682
683 if ( typeof response.redirect !== 'undefined' ) {
684 if ( shouldTriggerEvent ) {
685 triggerCustomEvent( object, 'frmSubmitEvent' );
686 return;
687 }
688
689 jQuery( document ).trigger( 'frmBeforeFormRedirect', [ object, response ]);
690
691 if ( ! response.openInNewTab ) {
692 // We return here because we're redirecting there is no need to update content.
693 window.location = response.redirect;
694 return;
695 }
696
697 // We don't return here because we're opening in a new tab, the old tab will still update.
698 newTab = window.open( response.redirect, '_blank' );
699 if ( ! newTab && response.fallbackMsg && response.content ) {
700 response.content = response.content.trim().replace( /(<\/div><\/div>)$/, ' ' + response.fallbackMsg + '</div></div>' );
701 }
702 }
703
704 if ( response.content !== '' ) {
705 // the form or success message was returned
706
707 if ( shouldTriggerEvent ) {
708 triggerCustomEvent( object, 'frmSubmitEvent', { content: response.content });
709 return;
710 }
711
712 removeSubmitLoading( jQuery( object ) );
713 if ( frm_js.offset != -1 ) { // eslint-disable-line camelcase
714 frmFrontForm.scrollMsg( jQuery( object ), false );
715 }
716
717 formID = jQuery( object ).find( 'input[name="form_id"]' ).val();
718 response.content = response.content.replace( / frm_pro_form /g, ' frm_pro_form frm_no_hide ' );
719 replaceContent = jQuery( object ).closest( '.frm_forms' );
720 removeAddedScripts( replaceContent, formID );
721 delay = maybeSlideOut( replaceContent, response.content );
722
723 setTimeout(
724 function() {
725 var container, input, previousInput;
726
727 afterFormSubmittedBeforeReplace( object, response );
728
729 replaceContent.replaceWith( response.content );
730
731 addUrlParam( response );
732
733 if ( typeof frmThemeOverride_frmAfterSubmit === 'function' ) { // eslint-disable-line camelcase
734 pageOrder = jQuery( 'input[name="frm_page_order_' + formID + '"]' ).val();
735 formReturned = jQuery( response.content ).find( 'input[name="form_id"]' ).val();
736 frmThemeOverride_frmAfterSubmit( formReturned, pageOrder, response.content, object );
737 }
738
739 if ( typeof response.recaptcha !== 'undefined' ) {
740 container = jQuery( '#frm_form_' + formID + '_container' ).find( '.frm_fields_container' );
741 input = '<input type="hidden" name="recaptcha_checked" value="' + response.recaptcha + '">';
742 previousInput = container.find( 'input[name="recaptcha_checked"]' );
743
744 if ( previousInput.length ) {
745 previousInput.replaceWith( input );
746 } else {
747 container.append( input );
748 }
749 }
750
751 afterFormSubmitted( object, response );
752 },
753 delay
754 );
755 } else if ( Object.keys( response.errors ).length ) {
756 // errors were returned
757 removeSubmitLoading( jQuery( object ), 'enable' );
758
759 //show errors
760 contSubmit = true;
761 removeAllErrors();
762
763 $fieldCont = null;
764
765 for ( key in response.errors ) {
766 $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
767
768 if ( $fieldCont.length ) {
769 if ( ! $fieldCont.is( ':visible' ) ) {
770 inCollapsedSection = $fieldCont.closest( '.frm_toggle_container' );
771 if ( inCollapsedSection.length ) {
772 frmTrigger = inCollapsedSection.prev();
773 if ( ! frmTrigger.hasClass( 'frm_trigger' ) ) {
774 // If the frmTrigger object is the section description, check to see if the previous element is the trigger
775 frmTrigger = frmTrigger.prev( '.frm_trigger' );
776 }
777 frmTrigger.trigger( 'click' );
778 }
779 }
780
781 if ( $fieldCont.is( ':visible' ) ) {
782 addFieldError( $fieldCont, key, response.errors );
783 contSubmit = false;
784 }
785 }
786 }
787
788 jQuery( object ).find( '.frm-g-recaptcha, .g-recaptcha, .h-captcha' ).each( function() {
789 var $recaptcha = jQuery( this ),
790 recaptchaID = $recaptcha.data( 'rid' );
791
792 if ( typeof grecaptcha !== 'undefined' && grecaptcha ) {
793 if ( recaptchaID ) {
794 grecaptcha.reset( recaptchaID );
795 } else {
796 grecaptcha.reset();
797 }
798 }
799 if ( typeof hcaptcha !== 'undefined' && hcaptcha ) {
800 hcaptcha.reset();
801 }
802 });
803
804 jQuery( document ).trigger( 'frmFormErrors', [ object, response ]);
805
806 fieldset.removeClass( 'frm_doing_ajax' );
807 scrollToFirstField( object );
808
809 if ( contSubmit ) {
810 object.submit();
811 } else {
812 jQuery( object ).prepend( response.error_message );
813 checkForErrorsAndMaybeSetFocus();
814 }
815 } else {
816 // there may have been a plugin conflict, or the form is not set to submit with ajax
817
818 showFileLoading( object );
819
820 object.submit();
821 }
822 };
823
824 error = function() {
825 jQuery( object ).find( 'input[type="submit"], input[type="button"]' ).prop( 'disabled', false );
826 object.submit();
827 };
828
829 postToAjaxUrl( object, data, success, error );
830 }
831
832 function postToAjaxUrl( form, data, success, error ) {
833 var ajaxUrl, action, ajaxParams;
834
835 ajaxUrl = frm_js.ajax_url; // eslint-disable-line camelcase
836 action = form.getAttribute( 'action' );
837
838 if ( 'string' === typeof action && -1 !== action.indexOf( '?action=frm_forms_preview' ) ) {
839 ajaxUrl = action.split( '?action=frm_forms_preview' )[0];
840 }
841
842 ajaxParams = {
843 type: 'POST',
844 url: ajaxUrl,
845 data: data,
846 success: success
847 };
848
849 if ( 'function' === typeof error ) {
850 ajaxParams.error = error;
851 }
852
853 jQuery.ajax( ajaxParams );
854 }
855
856 function afterFormSubmitted( object, response ) {
857 var formCompleted = jQuery( response.content ).find( '.frm_message' );
858 if ( formCompleted.length ) {
859 jQuery( document ).trigger( 'frmFormComplete', [ object, response ]);
860 } else {
861 jQuery( document ).trigger( 'frmPageChanged', [ object, response ]);
862 }
863 }
864
865 /**
866 * Trigger an event before the form is replaced with a success message.
867 *
868 * @since 6.9
869 *
870 * @param {HTMLElement} object The form.
871 * @param {object} response The response from submitting the form with AJAX.
872 * @return {void}
873 */
874 function afterFormSubmittedBeforeReplace( object, response ) {
875 var formCompleted = jQuery( response.content ).find( '.frm_message' );
876 if ( formCompleted.length ) {
877 triggerCustomEvent( document, 'frmFormCompleteBeforeReplace', { object, response });
878 }
879 }
880
881 function removeAddedScripts( formContainer, formID ) {
882 var endReplace = jQuery( '.frm_end_ajax_' + formID );
883 if ( endReplace.length ) {
884 formContainer.nextUntil( '.frm_end_ajax_' + formID ).remove();
885 endReplace.remove();
886 }
887 }
888
889 function maybeSlideOut( oldContent, newContent ) {
890 var c,
891 newClass = 'frm_slideout';
892 if ( newContent.indexOf( ' frm_slide' ) !== -1 ) {
893 c = oldContent.children();
894 if ( newContent.indexOf( ' frm_going_back' ) !== -1 ) {
895 newClass += ' frm_going_back';
896 }
897 c.removeClass( 'frm_going_back' );
898 c.addClass( newClass );
899 return 300;
900 }
901 return 0;
902 }
903
904 function addUrlParam( response ) {
905 var url;
906 if ( history.pushState && typeof response.page !== 'undefined' ) {
907 url = addQueryVar( 'frm_page', response.page );
908 window.history.pushState({ 'html': response.html }, '', '?' + url );
909 }
910 }
911
912 function addQueryVar( key, value ) {
913 var kvp, i, x;
914
915 key = encodeURI( key );
916 value = encodeURI( value );
917
918 kvp = document.location.search.substr( 1 ).split( '&' );
919
920 i = kvp.length;
921 while ( i-- ) {
922 x = kvp[i].split( '=' );
923
924 if ( x[0] == key ) {
925 x[1] = value;
926 kvp[i] = x.join( '=' );
927 break;
928 }
929 }
930
931 if ( i < 0 ) {
932 kvp[ kvp.length ] = [ key, value ].join( '=' );
933 }
934
935 return kvp.join( '&' );
936 }
937
938 function addFieldError( $fieldCont, key, jsErrors ) {
939 var input, id, describedBy, roleString;
940 if ( $fieldCont.length && $fieldCont.is( ':visible' ) ) {
941 $fieldCont.addClass( 'frm_blank_field' );
942 input = $fieldCont.find( 'input, select, textarea' );
943 id = 'frm_error_field_' + key;
944 describedBy = input.attr( 'aria-describedby' );
945
946 if ( typeof frmThemeOverride_frmPlaceError === 'function' ) { // eslint-disable-line camelcase
947 frmThemeOverride_frmPlaceError( key, jsErrors );
948 } else {
949 if ( -1 !== jsErrors[key].indexOf( '<div' ) ) {
950 $fieldCont.append(
951 jsErrors[key]
952 );
953 } else {
954 roleString = frm_js.include_alert_role ? 'role="alert"' : ''; // eslint-disable-line camelcase
955 $fieldCont.append( '<div class="frm_error" ' + roleString + ' id="' + id + '">' + jsErrors[key] + '</div>' );
956 }
957
958 if ( typeof describedBy === 'undefined' ) {
959 describedBy = id;
960 } else if ( describedBy.indexOf( id ) === -1 && describedBy.indexOf( 'frm_error_field_' ) === -1 ) {
961 if ( input.data( 'error-first' ) === 0 ) {
962 describedBy = describedBy + ' ' + id;
963 } else {
964 describedBy = id + ' ' + describedBy;
965 }
966 }
967
968 input.attr( 'aria-describedby', describedBy );
969 }
970 input.attr( 'aria-invalid', true );
971
972 jQuery( document ).trigger( 'frmAddFieldError', [ $fieldCont, key, jsErrors ]);
973 }
974 }
975
976 function removeFieldError( $fieldCont ) {
977 var errorMessage = $fieldCont.find( '.frm_error' ),
978 errorId = errorMessage.attr( 'id' ),
979 input = $fieldCont.find( 'input, select, textarea' ),
980 describedBy = input.attr( 'aria-describedby' );
981
982 $fieldCont.removeClass( 'frm_blank_field has-error' );
983 errorMessage.remove();
984 input.attr( 'aria-invalid', false );
985 input.removeAttr( 'aria-describedby' );
986
987 if ( typeof describedBy !== 'undefined' ) {
988 describedBy = describedBy.replace( errorId, '' );
989 input.attr( 'aria-describedby', describedBy );
990 }
991 }
992
993 function removeAllErrors() {
994 jQuery( '.form-field' ).removeClass( 'frm_blank_field has-error' );
995 jQuery( '.form-field .frm_error' ).replaceWith( '' );
996 jQuery( '.frm_error_style' ).remove();
997 }
998
999 function scrollToFirstField( object ) {
1000 var field = jQuery( object ).find( '.frm_blank_field' ).first();
1001 if ( field.length ) {
1002 frmFrontForm.scrollMsg( field, object, true );
1003 }
1004 }
1005
1006 function showSubmitLoading( $object ) {
1007 showLoadingIndicator( $object );
1008 disableSubmitButton( $object );
1009 disableSaveDraft( $object );
1010 }
1011
1012 function showLoadingIndicator( $object ) {
1013 if ( ! $object.hasClass( 'frm_loading_form' ) && ! $object.hasClass( 'frm_loading_prev' ) ) {
1014 addLoadingClass( $object );
1015 $object.trigger( 'frmStartFormLoading' );
1016 }
1017 }
1018
1019 function addLoadingClass( $object ) {
1020 var loadingClass = isGoingToPrevPage( $object ) ? 'frm_loading_prev' : 'frm_loading_form';
1021
1022 $object.addClass( loadingClass );
1023 }
1024
1025 function isGoingToPrevPage( $object ) {
1026 return ( typeof frmProForm !== 'undefined' && frmProForm.goingToPreviousPage( $object ) );
1027 }
1028
1029 function removeSubmitLoading( $object, enable, processesRunning ) {
1030 var loadingForm;
1031
1032 if ( processesRunning > 0 ) {
1033 return;
1034 }
1035
1036 loadingForm = jQuery( '.frm_loading_form' );
1037 loadingForm.removeClass( 'frm_loading_form' );
1038 loadingForm.removeClass( 'frm_loading_prev' );
1039
1040 loadingForm.trigger( 'frmEndFormLoading' );
1041
1042 if ( enable === 'enable' ) {
1043 enableSubmitButton( loadingForm );
1044 enableSaveDraft( loadingForm );
1045 }
1046 }
1047
1048 function showFileLoading( object ) {
1049 var fileval,
1050 loading = document.getElementById( 'frm_loading' );
1051 if ( loading !== null ) {
1052 fileval = jQuery( object ).find( 'input[type=file]' ).val();
1053 if ( typeof fileval !== 'undefined' && fileval !== '' ) {
1054 setTimeout( function() {
1055 jQuery( loading ).fadeIn( 'slow' );
1056 }, 2000 );
1057 }
1058 }
1059 }
1060
1061 function clearDefault() {
1062 /*jshint validthis:true */
1063 toggleDefault( jQuery( this ), 'clear' );
1064 }
1065
1066 function replaceDefault() {
1067 /*jshint validthis:true */
1068 toggleDefault( jQuery( this ), 'replace' );
1069 }
1070
1071 function toggleDefault( $thisField, e ) {
1072 // TODO: Fix this for a default value that is a number or array
1073 var thisVal,
1074 v = $thisField.data( 'frmval' ).replace( /(\n|\r\n)/g, '\r' );
1075 if ( v === '' || typeof v === 'undefined' ) {
1076 return false;
1077 }
1078 thisVal = $thisField.val().replace( /(\n|\r\n)/g, '\r' );
1079
1080 if ( 'replace' === e ) {
1081 if ( thisVal === '' ) {
1082 $thisField.addClass( 'frm_default' ).val( v );
1083 }
1084 } else if ( thisVal == v ) {
1085 $thisField.removeClass( 'frm_default' ).val( '' );
1086 }
1087 }
1088
1089 function resendEmail() {
1090 /*jshint validthis:true */
1091 var $link = jQuery( this ),
1092 entryId = this.getAttribute( 'data-eid' ),
1093 formId = this.getAttribute( 'data-fid' ),
1094 label = $link.find( '.frm_link_label' );
1095 if ( label.length < 1 ) {
1096 label = $link;
1097 }
1098 label.append( '<span class="frm-wait"></span>' );
1099
1100 jQuery.ajax({
1101 type: 'POST',
1102 url: frm_js.ajax_url, // eslint-disable-line camelcase
1103 data: {
1104 action: 'frm_entries_send_email',
1105 entry_id: entryId,
1106 form_id: formId,
1107 nonce: frm_js.nonce // eslint-disable-line camelcase
1108 },
1109 success: function( msg ) {
1110 var admin = document.getElementById( 'wpbody' );
1111 if ( admin === null ) {
1112 label.html( msg );
1113 } else {
1114 label.html( '' );
1115 $link.after( msg );
1116 }
1117 }
1118 });
1119 return false;
1120 }
1121
1122 /**********************************************
1123 * General Helpers
1124 *********************************************/
1125
1126 function confirmClick() {
1127 /*jshint validthis:true */
1128 var message = jQuery( this ).data( 'frmconfirm' );
1129 return confirm( message );
1130 }
1131
1132 function toggleDiv() {
1133 /*jshint validthis:true */
1134 var div = jQuery( this ).data( 'frmtoggle' );
1135 if ( jQuery( div ).is( ':visible' ) ) {
1136 jQuery( div ).slideUp( 'fast' );
1137 } else {
1138 jQuery( div ).slideDown( 'fast' );
1139 }
1140 return false;
1141 }
1142
1143 /**********************************************
1144 * Fallback functions
1145 *********************************************/
1146
1147 function addTrimFallbackForIE() {
1148 if ( typeof String.prototype.trim !== 'function' ) {
1149 String.prototype.trim = function() {
1150 return this.replace( /^\s+|\s+$/g, '' );
1151 };
1152 }
1153 }
1154
1155 function addFilterFallbackForIE() {
1156 var t, len, res, thisp, i, val;
1157
1158 if ( ! Array.prototype.filter ) {
1159
1160 Array.prototype.filter = function( fun /*, thisp */ ) {
1161
1162 if ( this === void 0 || this === null ) {
1163 throw new TypeError();
1164 }
1165
1166 t = Object( this );
1167 len = t.length >>> 0;
1168 if ( typeof fun !== 'function' ) {
1169 throw new TypeError();
1170 }
1171
1172 res = [];
1173 thisp = arguments[1];
1174 for ( i = 0; i < len; i++ ) {
1175 if ( i in t ) {
1176 val = t[i]; // in case fun mutates this
1177 if ( fun.call( thisp, val, i, t ) ) {
1178 res.push( val );
1179 }
1180 }
1181 }
1182
1183 return res;
1184 };
1185 }
1186 }
1187
1188 /**
1189 * Check for -webkit-box-shadow css value for input:-webkit-autofill selector.
1190 * If this is a match, the User is autofilling the input on a Webkit browser.
1191 * We want to delete the Honeypot field, otherwise it will get triggered as spam on autocomplete.
1192 */
1193 function onHoneypotFieldChange() {
1194 var css = jQuery( this ).css( 'box-shadow' );
1195 if ( css.match( /inset/ ) ) {
1196 this.parentNode.removeChild( this );
1197 }
1198 }
1199
1200 function maybeMakeHoneypotFieldsUntabbable() {
1201 document.addEventListener( 'keydown', handleKeyUp );
1202
1203 function handleKeyUp( event ) {
1204 var code;
1205
1206 if ( 'undefined' !== typeof event.key ) {
1207 code = event.key;
1208 } else if ( 'undefined' !== typeof event.keyCode && 9 === event.keyCode ) {
1209 code = 'Tab';
1210 }
1211
1212 if ( 'Tab' === code ) {
1213 makeHoneypotFieldsUntabbable();
1214 document.removeEventListener( 'keydown', handleKeyUp );
1215 }
1216 }
1217
1218 function makeHoneypotFieldsUntabbable() {
1219 document.querySelectorAll( '.frm_verify' ).forEach(
1220 function( input ) {
1221 if ( input.id && 0 === input.id.indexOf( 'frm_email_' ) ) {
1222 input.setAttribute( 'tabindex', -1 );
1223 }
1224 }
1225 );
1226 }
1227 }
1228
1229 /**
1230 * Focus on the first sub field when clicking to the primary label of combo field.
1231 *
1232 * @since 4.10.02
1233 */
1234 function changeFocusWhenClickComboFieldLabel() {
1235 var label;
1236
1237 var comboInputsContainer = document.querySelectorAll( '.frm_combo_inputs_container' );
1238 comboInputsContainer.forEach( function( inputsContainer ) {
1239 if ( ! inputsContainer.closest( '.frm_form_field' ) ) {
1240 return;
1241 }
1242
1243 label = inputsContainer.closest( '.frm_form_field' ).querySelector( '.frm_primary_label' );
1244 if ( ! label ) {
1245 return;
1246 }
1247
1248 label.addEventListener( 'click', function( e ) {
1249 inputsContainer.querySelector( '.frm_form_field:first-child input, .frm_form_field:first-child select, .frm_form_field:first-child textarea' ).focus();
1250 });
1251 });
1252 }
1253
1254 function checkForErrorsAndMaybeSetFocus() {
1255 var errors, element, timeoutCallback;
1256
1257 if ( ! frm_js.focus_first_error ) { // eslint-disable-line camelcase
1258 return;
1259 }
1260
1261 errors = document.querySelectorAll( '.frm_form_field .frm_error' );
1262 if ( ! errors.length ) {
1263 return;
1264 }
1265
1266 element = errors[0];
1267 do {
1268 element = element.previousSibling;
1269 if ( -1 !== [ 'input', 'select', 'textarea' ].indexOf( element.nodeName.toLowerCase() ) ) {
1270 element.focus();
1271 break;
1272 }
1273
1274 if ( 'undefined' !== typeof element.classList ) {
1275 if ( element.classList.contains( 'html-active' ) ) {
1276 timeoutCallback = function() {
1277 var textarea = element.querySelector( 'textarea' );
1278 if ( null !== textarea ) {
1279 textarea.focus();
1280 }
1281 };
1282 } else if ( element.classList.contains( 'tmce-active' ) ) {
1283 timeoutCallback = function() {
1284 tinyMCE.activeEditor.focus();
1285 };
1286 }
1287
1288 if ( 'function' === typeof timeoutCallback ) {
1289 setTimeout( timeoutCallback, 0 );
1290 break;
1291 }
1292 }
1293 } while ( element.previousSibling );
1294 }
1295
1296 /**
1297 * Checks if is on IE browser.
1298 *
1299 * @since 5.4
1300 *
1301 * @return {Boolean}
1302 */
1303 function isIE() {
1304 return navigator.userAgent.indexOf( 'MSIE' ) > -1 || navigator.userAgent.indexOf( 'Trident' ) > -1;
1305 }
1306
1307 /**
1308 * Does the same as jQuery( document ).on( 'event', 'selector', handler ).
1309 *
1310 * @since 5.4
1311 *
1312 * @param {String} event Event name.
1313 * @param {String} selector Selector.
1314 * @param {Function} handler Handler.
1315 * @param {Boolean|Object} options Options to be added to `addEventListener()` method. Default is `false`.
1316 */
1317 function documentOn( event, selector, handler, options ) {
1318 if ( 'undefined' === typeof options ) {
1319 options = false;
1320 }
1321
1322 document.addEventListener( event, function( e ) {
1323 var target;
1324
1325 // loop parent nodes from the target to the delegation node.
1326 for ( target = e.target; target && target != this; target = target.parentNode ) {
1327 if ( target && target.matches && target.matches( selector ) ) {
1328 handler.call( target, e );
1329 break;
1330 }
1331 }
1332 }, options );
1333 }
1334
1335 function initFloatingLabels() {
1336 var checkFloatLabel, checkDropdownLabel, checkPlaceholderIE, runOnLoad, selector, floatClass;
1337
1338 selector = '.frm-show-form .frm_inside_container input, .frm-show-form .frm_inside_container select, .frm-show-form .frm_inside_container textarea';
1339 floatClass = 'frm_label_float_top';
1340
1341 checkFloatLabel = function( input ) {
1342 var container, shouldFloatTop, firstOpt;
1343
1344 container = input.closest( '.frm_inside_container' );
1345 if ( ! container ) {
1346 return;
1347 }
1348
1349 shouldFloatTop = input.value || document.activeElement === input;
1350
1351 container.classList.toggle( floatClass, shouldFloatTop );
1352
1353 if ( 'SELECT' === input.tagName ) {
1354 firstOpt = input.querySelector( 'option:first-child' );
1355
1356 if ( shouldFloatTop ) {
1357 if ( firstOpt.hasAttribute( 'data-label' ) ) {
1358 firstOpt.textContent = firstOpt.getAttribute( 'data-label' );
1359 firstOpt.removeAttribute( 'data-label' );
1360 }
1361 } else {
1362 if ( firstOpt.textContent ) {
1363 firstOpt.setAttribute( 'data-label', firstOpt.textContent );
1364 firstOpt.textContent = '';
1365 }
1366 }
1367 } else if ( isIE() ) {
1368 checkPlaceholderIE( input );
1369 }
1370 };
1371
1372 checkDropdownLabel = function() {
1373 document.querySelectorAll( '.frm-show-form .frm_inside_container:not(.' + floatClass + ') select' ).forEach( function( input ) {
1374 var firstOpt = input.querySelector( 'option:first-child' );
1375
1376 if ( firstOpt.textContent ) {
1377 firstOpt.setAttribute( 'data-label', firstOpt.textContent );
1378 firstOpt.textContent = '';
1379 }
1380 });
1381 };
1382
1383 checkPlaceholderIE = function( input ) {
1384 if ( input.value ) {
1385 // Don't need to handle this case because placeholder isn't shown.
1386 return;
1387 }
1388
1389 if ( document.activeElement === input ) {
1390 if ( input.hasAttribute( 'data-placeholder' ) ) {
1391 input.placeholder = input.getAttribute( 'data-placeholder' );
1392 input.removeAttribute( 'data-placeholder' );
1393 }
1394 } else {
1395 if ( input.placeholder ) {
1396 input.setAttribute( 'data-placeholder', input.placeholder );
1397 input.placeholder = '';
1398 }
1399 }
1400 };
1401
1402 [ 'focus', 'blur', 'change' ].forEach( function( eventName ) {
1403 documentOn(
1404 eventName,
1405 selector,
1406 function( event ) {
1407 checkFloatLabel( event.target );
1408 },
1409 true
1410 );
1411 });
1412
1413 jQuery( document ).on( 'change', selector, function( event ) {
1414 checkFloatLabel( event.target );
1415 });
1416
1417 runOnLoad = function( firstLoad ) {
1418 if ( firstLoad && document.activeElement && -1 !== [ 'INPUT', 'SELECT', 'TEXTAREA' ].indexOf( document.activeElement.tagName ) ) {
1419 checkFloatLabel( document.activeElement );
1420 } else if ( firstLoad ) {
1421 document.querySelectorAll( '.frm_inside_container' ).forEach(
1422 function( container ) {
1423 var input = container.querySelector( 'input, select, textarea' );
1424 if ( input && '' !== input.value ) {
1425 checkFloatLabel( input );
1426 }
1427 }
1428 );
1429 }
1430
1431 checkDropdownLabel();
1432
1433 if ( isIE() ) {
1434 document.querySelectorAll( selector ).forEach( function( input ) {
1435 checkPlaceholderIE( input );
1436 });
1437 }
1438 };
1439
1440 runOnLoad( true );
1441
1442 jQuery( document ).on( 'frmPageChanged', function( event ) {
1443 runOnLoad();
1444 });
1445
1446 document.addEventListener( 'frm_after_start_over', function( event ) {
1447 runOnLoad();
1448 });
1449 }
1450
1451 function shouldUpdateValidityMessage( target ) {
1452 if ( 'INPUT' !== target.nodeName ) {
1453 return false;
1454 }
1455
1456 if ( ! target.dataset.invmsg ) {
1457 return false;
1458 }
1459
1460 if ( 'text' !== target.getAttribute( 'type' ) ) {
1461 return false;
1462 }
1463
1464 if ( target.classList.contains( 'frm_verify' ) ) {
1465 return false;
1466 }
1467
1468 return true;
1469 }
1470
1471 function maybeClearCustomValidityMessage( event, field ) {
1472 var key,
1473 isInvalid = false;
1474
1475 if ( ! shouldUpdateValidityMessage( field ) ) {
1476 return;
1477 }
1478
1479 for ( key in field.validity ) {
1480 if ( 'customError' === key ) {
1481 continue;
1482 }
1483 if ( 'valid' !== key && field.validity[ key ] === true ) {
1484 isInvalid = true;
1485 break;
1486 }
1487 };
1488
1489 if ( ! isInvalid ) {
1490 field.setCustomValidity( '' );
1491 }
1492 }
1493
1494 function maybeShowNewTabFallbackMessage() {
1495 var messageEl;
1496
1497 if ( ! window.frmShowNewTabFallback ) {
1498 return;
1499 }
1500
1501 messageEl = document.querySelector( '#frm_form_' + frmShowNewTabFallback.formId + '_container .frm_message' );
1502 if ( ! messageEl ) {
1503 return;
1504 }
1505
1506 messageEl.insertAdjacentHTML( 'beforeend', ' ' + frmShowNewTabFallback.message );
1507 }
1508
1509 function setCustomValidityMessage() {
1510 var forms, length, index;
1511
1512 forms = document.getElementsByClassName( 'frm-show-form' );
1513 length = forms.length;
1514
1515 for ( index = 0; index < length; ++index ) {
1516 forms[ index ].addEventListener(
1517 'invalid',
1518 function( event ) {
1519 var target = event.target;
1520
1521 if ( shouldUpdateValidityMessage( target ) ) {
1522 target.setCustomValidity( target.dataset.invmsg );
1523 }
1524 },
1525 true
1526 );
1527 }
1528 }
1529
1530 function enableSubmitButtonOnBackButtonPress() {
1531 window.addEventListener( 'pageshow', function( event ) {
1532 if ( event.persisted ) {
1533 document.querySelectorAll( '.frm_loading_form' ).forEach(
1534 function( form ) {
1535 enableSubmitButton( jQuery( form ) );
1536 }
1537 );
1538 removeSubmitLoading();
1539 }
1540 });
1541 }
1542
1543 /**
1544 * Destroys the formidable generated global hcaptcha object since it wouldn't otherwise render.
1545 */
1546 function destroyhCaptcha() {
1547 if ( ! window.hasOwnProperty( 'hcaptcha' ) || ! document.querySelector( '.frm-show-form .h-captcha' ) ) {
1548 return;
1549 }
1550 window.hcaptcha = null;
1551 }
1552
1553 return {
1554 init: function() {
1555 maybeAddPolyfills();
1556
1557 jQuery( document ).off( 'submit.formidable', '.frm-show-form' );
1558 jQuery( document ).on( 'submit.formidable', '.frm-show-form', frmFrontForm.submitForm );
1559
1560 jQuery( '.frm-show-form input[onblur], .frm-show-form textarea[onblur]' ).each( function() {
1561 if ( jQuery( this ).val() === '' ) {
1562 jQuery( this ).trigger( 'blur' );
1563 }
1564 });
1565
1566 jQuery( document ).on( 'focus', '.frm_toggle_default', clearDefault );
1567 jQuery( document ).on( 'blur', '.frm_toggle_default', replaceDefault );
1568 jQuery( '.frm_toggle_default' ).trigger( 'blur' );
1569
1570 jQuery( document.getElementById( 'frm_resend_email' ) ).on( 'click', resendEmail );
1571
1572 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 );
1573
1574 jQuery( document ).on( 'change', '[id^=frm_email_]', onHoneypotFieldChange );
1575 maybeMakeHoneypotFieldsUntabbable();
1576
1577 jQuery( document ).on( 'click', 'a[data-frmconfirm]', confirmClick );
1578 jQuery( 'a[data-frmtoggle]' ).on( 'click', toggleDiv );
1579
1580 checkForErrorsAndMaybeSetFocus();
1581
1582 // Focus on the first sub field when clicking to the primary label of combo field.
1583 changeFocusWhenClickComboFieldLabel();
1584
1585 // Add fallbacks for IE.
1586 addTrimFallbackForIE(); // Trim only works in IE10+.
1587 addFilterFallbackForIE(); // Filter is not supported in any version of IE.
1588
1589 initFloatingLabels();
1590 maybeShowNewTabFallbackMessage();
1591
1592 jQuery( document ).on( 'frmAfterAddRow', setCustomValidityMessage );
1593 setCustomValidityMessage();
1594 jQuery( document ).on( 'frmFieldChanged', maybeClearCustomValidityMessage );
1595
1596 setSelectPlaceholderColor();
1597
1598 // Elementor popup show event. Fix Elementor Popup && FF Captcha field conflicts
1599 jQuery( document ).on( 'elementor/popup/show', frmRecaptcha );
1600
1601 enableSubmitButtonOnBackButtonPress();
1602 jQuery( document ).on(
1603 'frmPageChanged',
1604 destroyhCaptcha
1605 );
1606 },
1607
1608 getFieldId: function( field, fullID ) {
1609 return getFieldId( field, fullID );
1610 },
1611
1612 renderCaptcha: function( captcha, captchaSelector ) {
1613 var formID, captchaID,
1614 size = captcha.getAttribute( 'data-size' ),
1615 rendered = captcha.getAttribute( 'data-rid' ) !== null,
1616 params = {
1617 'sitekey': captcha.getAttribute( 'data-sitekey' ),
1618 'size': size,
1619 'theme': captcha.getAttribute( 'data-theme' )
1620 },
1621 activeCaptcha = getSelectedCaptcha( captchaSelector ),
1622 captchaContainer = typeof turnstile !== 'undefined' && turnstile === activeCaptcha ? '#' + captcha.id : captcha.id;
1623
1624 if ( rendered ) {
1625 return;
1626 }
1627
1628 if ( size === 'invisible' ) {
1629 formID = jQuery( captcha ).closest( 'form' ).find( 'input[name="form_id"]' ).val();
1630 jQuery( captcha ).closest( '.frm_form_field .frm_primary_label' ).hide();
1631 params.callback = function( token ) {
1632 frmFrontForm.afterRecaptcha( token, formID );
1633 };
1634 }
1635
1636
1637 captchaID = activeCaptcha.render( captchaContainer, params );
1638
1639 captcha.setAttribute( 'data-rid', captchaID );
1640 },
1641
1642 afterSingleRecaptcha: function() {
1643 var object = jQuery( '.frm-show-form .g-recaptcha' ).closest( 'form' )[0];
1644 frmFrontForm.submitFormNow( object );
1645 },
1646
1647 afterRecaptcha: function( token, formID ) {
1648 var object = jQuery( '#frm_form_' + formID + '_container form' )[0];
1649 frmFrontForm.submitFormNow( object );
1650 },
1651
1652 submitForm: function( e ) {
1653 frmFrontForm.submitFormManual( e, this );
1654 },
1655
1656 submitFormManual: function( e, object ) {
1657 var isPro, errors,
1658 invisibleRecaptcha = hasInvisibleRecaptcha( object ),
1659 classList = object.className.trim().split( /\s+/gi );
1660
1661 if ( classList && invisibleRecaptcha.length < 1 ) {
1662 isPro = classList.indexOf( 'frm_pro_form' ) > -1;
1663 if ( ! isPro ) {
1664 return;
1665 }
1666 }
1667
1668 if ( jQuery( 'body' ).hasClass( 'wp-admin' ) && jQuery( object ).closest( '.frmapi-form' ).length < 1 ) {
1669 return;
1670 }
1671
1672 e.preventDefault();
1673
1674 if ( typeof frmProForm !== 'undefined' && typeof frmProForm.submitAllowed === 'function' ) {
1675 if ( ! frmProForm.submitAllowed( object ) ) {
1676 return;
1677 }
1678 }
1679
1680 if ( invisibleRecaptcha.length ) {
1681 showLoadingIndicator( jQuery( object ) );
1682 executeInvisibleRecaptcha( invisibleRecaptcha );
1683 } else {
1684
1685 errors = frmFrontForm.validateFormSubmit( object );
1686
1687 if ( Object.keys( errors ).length === 0 ) {
1688 showSubmitLoading( jQuery( object ) );
1689
1690 frmFrontForm.submitFormNow( object, classList );
1691 }
1692 }
1693 },
1694
1695 submitFormNow: function( object ) {
1696 var hasFileFields, antispamInput,
1697 classList = object.className.trim().split( /\s+/gi );
1698
1699 if ( object.hasAttribute( 'data-token' ) && null === object.querySelector( '[name="antispam_token"]' ) ) {
1700 // include the antispam token on form submit.
1701 antispamInput = document.createElement( 'input' );
1702 antispamInput.type = 'hidden';
1703 antispamInput.name = 'antispam_token';
1704 antispamInput.value = object.getAttribute( 'data-token' );
1705 object.appendChild( antispamInput );
1706 }
1707
1708 if ( classList.indexOf( 'frm_ajax_submit' ) > -1 ) {
1709 hasFileFields = jQuery( object ).find( 'input[type="file"]' ).filter( function() {
1710 return !! this.value;
1711 }).length;
1712 if ( hasFileFields < 1 ) {
1713 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1714 frmFrontForm.checkFormErrors( object, action );
1715 } else {
1716 object.submit();
1717 }
1718 } else {
1719 object.submit();
1720 }
1721 },
1722
1723 validateFormSubmit: function( object ) {
1724 if ( typeof tinyMCE !== 'undefined' && jQuery( object ).find( '.wp-editor-wrap' ).length ) {
1725 tinyMCE.triggerSave();
1726 }
1727
1728 jsErrors = [];
1729
1730 if ( shouldJSValidate( object ) ) {
1731 frmFrontForm.getAjaxFormErrors( object );
1732
1733 if ( Object.keys( jsErrors ).length ) {
1734 frmFrontForm.addAjaxFormErrors( object );
1735 }
1736 }
1737
1738 return jsErrors;
1739 },
1740
1741 getAjaxFormErrors: function( object ) {
1742 var customErrors, key;
1743
1744 jsErrors = validateForm( object );
1745 if ( typeof frmThemeOverride_jsErrors === 'function' ) { // eslint-disable-line camelcase
1746 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1747 customErrors = frmThemeOverride_jsErrors( action, object );
1748 if ( Object.keys( customErrors ).length ) {
1749 for ( key in customErrors ) {
1750 jsErrors[ key ] = customErrors[ key ];
1751 }
1752 }
1753 }
1754
1755 return jsErrors;
1756 },
1757
1758 addAjaxFormErrors: function( object ) {
1759 var key, $fieldCont;
1760 removeAllErrors();
1761
1762 for ( key in jsErrors ) {
1763 $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
1764
1765 if ( $fieldCont.length ) {
1766 addFieldError( $fieldCont, key, jsErrors );
1767 } else {
1768 // we are unable to show the error, so remove it
1769 delete jsErrors[ key ];
1770 }
1771 }
1772
1773 scrollToFirstField( object );
1774 checkForErrorsAndMaybeSetFocus();
1775 },
1776
1777 checkFormErrors: function( object, action ) {
1778 getFormErrors( object, action );
1779 },
1780
1781 checkRequiredField: function( field, errors ) {
1782 return checkRequiredField( field, errors );
1783 },
1784
1785 showSubmitLoading: function( $object ) {
1786 showSubmitLoading( $object );
1787 },
1788
1789 removeSubmitLoading: function( $object, enable, processesRunning ) {
1790 removeSubmitLoading( $object, enable, processesRunning );
1791 },
1792
1793 scrollToID: function( id ) {
1794 var object = jQuery( document.getElementById( id ) );
1795 frmFrontForm.scrollMsg( object, false );
1796 },
1797
1798 scrollMsg: function( id, object, animate ) {
1799 var newPos, m, b, screenTop, screenBottom,
1800 scrollObj = '';
1801 if ( typeof object === 'undefined' ) {
1802 scrollObj = jQuery( document.getElementById( 'frm_form_' + id + '_container' ) );
1803 if ( scrollObj.length < 1 ) {
1804 return;
1805 }
1806 } else if ( typeof id === 'string' ) {
1807 scrollObj = jQuery( object ).find( '#frm_field_' + id + '_container' );
1808 } else {
1809 scrollObj = id;
1810 }
1811
1812 jQuery( scrollObj ).trigger( 'focus' );
1813 newPos = scrollObj.offset().top;
1814 if ( ! newPos || frm_js.offset === '-1' ) { // eslint-disable-line camelcase
1815 return;
1816 }
1817 newPos = newPos - frm_js.offset; // eslint-disable-line camelcase
1818
1819 m = jQuery( 'html' ).css( 'margin-top' );
1820 b = jQuery( 'body' ).css( 'margin-top' );
1821 if ( m || b ) {
1822 newPos = newPos - parseInt( m ) - parseInt( b );
1823 }
1824
1825 if ( newPos && window.innerHeight ) {
1826 screenTop = document.documentElement.scrollTop || document.body.scrollTop;
1827 screenBottom = screenTop + window.innerHeight;
1828
1829 if ( newPos > screenBottom || newPos < screenTop ) {
1830 // Not in view
1831 if ( typeof animate === 'undefined' ) {
1832 jQuery( window ).scrollTop( newPos );
1833 } else {
1834 jQuery( 'html,body' ).animate({ scrollTop: newPos }, 500 );
1835 }
1836 return false;
1837 }
1838 }
1839 },
1840
1841 fieldValueChanged: function( e ) {
1842 /*jshint validthis:true */
1843
1844 var fieldId = frmFrontForm.getFieldId( this, false );
1845 if ( ! fieldId || typeof fieldId === 'undefined' ) {
1846 return;
1847 }
1848
1849 if ( e.frmTriggered && e.frmTriggered == fieldId ) {
1850 return;
1851 }
1852
1853 jQuery( document ).trigger( 'frmFieldChanged', [ this, fieldId, e ]);
1854
1855 if ( e.selfTriggered !== true ) {
1856 maybeValidateChange( this );
1857 }
1858 },
1859
1860 savingDraft: function( object ) {
1861 console.warn( 'DEPRECATED: function frmFrontForm.savingDraft in v3.0 use frmProForm.savingDraft' );
1862 if ( typeof frmProForm !== 'undefined' ) {
1863 return frmProForm.savingDraft( object );
1864 }
1865 },
1866
1867 goingToPreviousPage: function( object ) {
1868 console.warn( 'DEPRECATED: function frmFrontForm.goingToPreviousPage in v3.0 use frmProForm.goingToPreviousPage' );
1869 if ( typeof frmProForm !== 'undefined' ) {
1870 return frmProForm.goingToPreviousPage( object );
1871 }
1872 },
1873
1874 hideOrShowFields: function() {
1875 console.warn( 'DEPRECATED: function frmFrontForm.hideOrShowFields in v3.0 use frmProForm.hideOrShowFields' );
1876 if ( typeof frmProForm !== 'undefined' ) {
1877 frmProForm.hideOrShowFields();
1878 }
1879 },
1880
1881 hidePreviouslyHiddenFields: function() {
1882 console.warn( 'DEPRECATED: function frmFrontForm.hidePreviouslyHiddenFields in v3.0 use frmProForm.hidePreviouslyHiddenFields' );
1883 if ( typeof frmProForm !== 'undefined' ) {
1884 frmProForm.hidePreviouslyHiddenFields();
1885 }
1886 },
1887
1888 checkDependentDynamicFields: function( ids ) {
1889 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentDynamicFields in v3.0 use frmProForm.checkDependentDynamicFields' );
1890 if ( typeof frmProForm !== 'undefined' ) {
1891 frmProForm.checkDependentDynamicFields( ids );
1892 }
1893 },
1894
1895 checkDependentLookupFields: function( ids ) {
1896 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentLookupFields in v3.0 use frmProForm.checkDependentLookupFields' );
1897 if ( typeof frmProForm !== 'undefined' ) {
1898 frmProForm.checkDependentLookupFields( ids );
1899 }
1900 },
1901
1902 loadGoogle: function() {
1903 console.warn( 'DEPRECATED: function frmFrontForm.loadGoogle in v3.0 use frmProForm.loadGoogle' );
1904 frmProForm.loadGoogle();
1905 },
1906
1907 escapeHtml: function( text ) {
1908 return text
1909 .replace( /&/g, '&amp;' )
1910 .replace( /</g, '&lt;' )
1911 .replace( />/g, '&gt;' )
1912 .replace( /"/g, '&quot;' )
1913 .replace( /'/g, '&#039;' );
1914 },
1915
1916 invisible: function( classes ) {
1917 jQuery( classes ).css( 'visibility', 'hidden' );
1918 },
1919
1920 visible: function( classes ) {
1921 jQuery( classes ).css( 'visibility', 'visible' );
1922 },
1923
1924 triggerCustomEvent: triggerCustomEvent,
1925 documentOn
1926 };
1927 }
1928 frmFrontForm = frmFrontFormJS();
1929
1930 jQuery( document ).ready( function() {
1931 frmFrontForm.init();
1932 });
1933
1934 function frmRecaptcha() {
1935 frmCaptcha( '.frm-g-recaptcha' );
1936 }
1937
1938 function frmTurnstile() {
1939 frmCaptcha( '.cf-turnstile' );
1940 }
1941
1942 function frmCaptcha( captchaSelector ) {
1943 var c, cl,
1944 captchas = document.querySelectorAll( captchaSelector );
1945 for ( c = 0, cl = captchas.length; c < cl; c++ ) {
1946 frmFrontForm.renderCaptcha( captchas[c], captchaSelector );
1947 }
1948 }
1949
1950 function getSelectedCaptcha( captchaSelector ) {
1951 if ( captchaSelector === '.frm-g-recaptcha' ) {
1952 return grecaptcha;
1953 }
1954 if ( document.querySelector( '.cf-turnstile' ) ) {
1955 return turnstile;
1956 }
1957 return hcaptcha;
1958 }
1959
1960 function frmAfterRecaptcha( token ) {
1961 frmFrontForm.afterSingleRecaptcha( token );
1962 }
1963
1964 function frmUpdateField( entryId, fieldId, value, message, num ) {
1965 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).html( '<span class="frm-loading-img"></span>' );
1966 jQuery.ajax({
1967 type: 'POST',
1968 url: frm_js.ajax_url, // eslint-disable-line camelcase
1969 data: {
1970 action: 'frm_entries_update_field_ajax',
1971 entry_id: entryId,
1972 field_id: fieldId,
1973 value: value,
1974 nonce: frm_js.nonce // eslint-disable-line camelcase
1975 },
1976 success: function() {
1977 if ( message.replace( /^\s+|\s+$/g, '' ) === '' ) {
1978 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).fadeOut( 'slow' );
1979 } else {
1980 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).replaceWith( message );
1981 }
1982 }
1983 });
1984 }
1985