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

1,976 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, frmDeleteEntry, frmOnSubmit, frm_resend_email */
2
3 var frmFrontForm;
4
5 function frmFrontFormJS() {
6 'use strict';
7
8 /*global jQuery:false, frm_js, grecaptcha, 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"]' ).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 && 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' );
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 replaceContent.replaceWith( response.content );
728
729 addUrlParam( response );
730
731 if ( typeof frmThemeOverride_frmAfterSubmit === 'function' ) { // eslint-disable-line camelcase
732 pageOrder = jQuery( 'input[name="frm_page_order_' + formID + '"]' ).val();
733 formReturned = jQuery( response.content ).find( 'input[name="form_id"]' ).val();
734 frmThemeOverride_frmAfterSubmit( formReturned, pageOrder, response.content, object );
735 }
736
737 if ( typeof response.recaptcha !== 'undefined' ) {
738 container = jQuery( '#frm_form_' + formID + '_container' ).find( '.frm_fields_container' );
739 input = '<input type="hidden" name="recaptcha_checked" value="' + response.recaptcha + '">';
740 previousInput = container.find( 'input[name="recaptcha_checked"]' );
741
742 if ( previousInput.length ) {
743 previousInput.replaceWith( input );
744 } else {
745 container.append( input );
746 }
747 }
748
749 afterFormSubmitted( object, response );
750 },
751 delay
752 );
753 } else if ( Object.keys( response.errors ).length ) {
754 // errors were returned
755 removeSubmitLoading( jQuery( object ), 'enable' );
756
757 //show errors
758 contSubmit = true;
759 removeAllErrors();
760
761 $fieldCont = null;
762
763 for ( key in response.errors ) {
764 $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
765
766 if ( $fieldCont.length ) {
767 if ( ! $fieldCont.is( ':visible' ) ) {
768 inCollapsedSection = $fieldCont.closest( '.frm_toggle_container' );
769 if ( inCollapsedSection.length ) {
770 frmTrigger = inCollapsedSection.prev();
771 if ( ! frmTrigger.hasClass( 'frm_trigger' ) ) {
772 // If the frmTrigger object is the section description, check to see if the previous element is the trigger
773 frmTrigger = frmTrigger.prev( '.frm_trigger' );
774 }
775 frmTrigger.trigger( 'click' );
776 }
777 }
778
779 if ( $fieldCont.is( ':visible' ) ) {
780 addFieldError( $fieldCont, key, response.errors );
781 contSubmit = false;
782 }
783 }
784 }
785
786 jQuery( object ).find( '.frm-g-recaptcha, .g-recaptcha, .h-captcha' ).each( function() {
787 var $recaptcha = jQuery( this ),
788 recaptchaID = $recaptcha.data( 'rid' );
789
790 if ( typeof grecaptcha !== 'undefined' && grecaptcha ) {
791 if ( recaptchaID ) {
792 grecaptcha.reset( recaptchaID );
793 } else {
794 grecaptcha.reset();
795 }
796 }
797 if ( typeof hcaptcha !== 'undefined' && hcaptcha ) {
798 hcaptcha.reset();
799 }
800 });
801
802 jQuery( document ).trigger( 'frmFormErrors', [ object, response ]);
803
804 fieldset.removeClass( 'frm_doing_ajax' );
805 scrollToFirstField( object );
806
807 if ( contSubmit ) {
808 object.submit();
809 } else {
810 jQuery( object ).prepend( response.error_message );
811 checkForErrorsAndMaybeSetFocus();
812 }
813 } else {
814 // there may have been a plugin conflict, or the form is not set to submit with ajax
815
816 showFileLoading( object );
817
818 object.submit();
819 }
820 };
821
822 error = function() {
823 jQuery( object ).find( 'input[type="submit"], input[type="button"]' ).prop( 'disabled', false );
824 object.submit();
825 };
826
827 postToAjaxUrl( object, data, success, error );
828 }
829
830 function postToAjaxUrl( form, data, success, error ) {
831 var ajaxUrl, action, ajaxParams;
832
833 ajaxUrl = frm_js.ajax_url; // eslint-disable-line camelcase
834 action = form.getAttribute( 'action' );
835
836 if ( 'string' === typeof action && -1 !== action.indexOf( '?action=frm_forms_preview' ) ) {
837 ajaxUrl = action.split( '?action=frm_forms_preview' )[0];
838 }
839
840 ajaxParams = {
841 type: 'POST',
842 url: ajaxUrl,
843 data: data,
844 success: success
845 };
846
847 if ( 'function' === typeof error ) {
848 ajaxParams.error = error;
849 }
850
851 jQuery.ajax( ajaxParams );
852 }
853
854 function afterFormSubmitted( object, response ) {
855 var formCompleted = jQuery( response.content ).find( '.frm_message' );
856 if ( formCompleted.length ) {
857 jQuery( document ).trigger( 'frmFormComplete', [ object, response ]);
858 } else {
859 jQuery( document ).trigger( 'frmPageChanged', [ object, response ]);
860 }
861 }
862
863 function removeAddedScripts( formContainer, formID ) {
864 var endReplace = jQuery( '.frm_end_ajax_' + formID );
865 if ( endReplace.length ) {
866 formContainer.nextUntil( '.frm_end_ajax_' + formID ).remove();
867 endReplace.remove();
868 }
869 }
870
871 function maybeSlideOut( oldContent, newContent ) {
872 var c,
873 newClass = 'frm_slideout';
874 if ( newContent.indexOf( ' frm_slide' ) !== -1 ) {
875 c = oldContent.children();
876 if ( newContent.indexOf( ' frm_going_back' ) !== -1 ) {
877 newClass += ' frm_going_back';
878 }
879 c.removeClass( 'frm_going_back' );
880 c.addClass( newClass );
881 return 300;
882 }
883 return 0;
884 }
885
886 function addUrlParam( response ) {
887 var url;
888 if ( history.pushState && typeof response.page !== 'undefined' ) {
889 url = addQueryVar( 'frm_page', response.page );
890 window.history.pushState({ 'html': response.html }, '', '?' + url );
891 }
892 }
893
894 function addQueryVar( key, value ) {
895 var kvp, i, x;
896
897 key = encodeURI( key );
898 value = encodeURI( value );
899
900 kvp = document.location.search.substr( 1 ).split( '&' );
901
902 i = kvp.length;
903 while ( i-- ) {
904 x = kvp[i].split( '=' );
905
906 if ( x[0] == key ) {
907 x[1] = value;
908 kvp[i] = x.join( '=' );
909 break;
910 }
911 }
912
913 if ( i < 0 ) {
914 kvp[ kvp.length ] = [ key, value ].join( '=' );
915 }
916
917 return kvp.join( '&' );
918 }
919
920 function addFieldError( $fieldCont, key, jsErrors ) {
921 var input, id, describedBy, roleString;
922 if ( $fieldCont.length && $fieldCont.is( ':visible' ) ) {
923 $fieldCont.addClass( 'frm_blank_field' );
924 input = $fieldCont.find( 'input, select, textarea' );
925 id = 'frm_error_field_' + key;
926 describedBy = input.attr( 'aria-describedby' );
927
928 if ( typeof frmThemeOverride_frmPlaceError === 'function' ) { // eslint-disable-line camelcase
929 frmThemeOverride_frmPlaceError( key, jsErrors );
930 } else {
931 if ( -1 !== jsErrors[key].indexOf( '<div' ) ) {
932 $fieldCont.append(
933 jsErrors[key]
934 );
935 } else {
936 roleString = frm_js.include_alert_role ? 'role="alert"' : ''; // eslint-disable-line camelcase
937 $fieldCont.append( '<div class="frm_error" ' + roleString + ' id="' + id + '">' + jsErrors[key] + '</div>' );
938 }
939
940 if ( typeof describedBy === 'undefined' ) {
941 describedBy = id;
942 } else if ( describedBy.indexOf( id ) === -1 && describedBy.indexOf( 'frm_error_field_' ) === -1 ) {
943 if ( input.data( 'error-first' ) === 0 ) {
944 describedBy = describedBy + ' ' + id;
945 } else {
946 describedBy = id + ' ' + describedBy;
947 }
948 }
949
950 input.attr( 'aria-describedby', describedBy );
951 }
952 input.attr( 'aria-invalid', true );
953
954 jQuery( document ).trigger( 'frmAddFieldError', [ $fieldCont, key, jsErrors ]);
955 }
956 }
957
958 function removeFieldError( $fieldCont ) {
959 var errorMessage = $fieldCont.find( '.frm_error' ),
960 errorId = errorMessage.attr( 'id' ),
961 input = $fieldCont.find( 'input, select, textarea' ),
962 describedBy = input.attr( 'aria-describedby' );
963
964 $fieldCont.removeClass( 'frm_blank_field has-error' );
965 errorMessage.remove();
966 input.attr( 'aria-invalid', false );
967 input.removeAttr( 'aria-describedby' );
968
969 if ( typeof describedBy !== 'undefined' ) {
970 describedBy = describedBy.replace( errorId, '' );
971 input.attr( 'aria-describedby', describedBy );
972 }
973 }
974
975 function removeAllErrors() {
976 jQuery( '.form-field' ).removeClass( 'frm_blank_field has-error' );
977 jQuery( '.form-field .frm_error' ).replaceWith( '' );
978 jQuery( '.frm_error_style' ).remove();
979 }
980
981 function scrollToFirstField( object ) {
982 var field = jQuery( object ).find( '.frm_blank_field' ).first();
983 if ( field.length ) {
984 frmFrontForm.scrollMsg( field, object, true );
985 }
986 }
987
988 function showSubmitLoading( $object ) {
989 showLoadingIndicator( $object );
990 disableSubmitButton( $object );
991 disableSaveDraft( $object );
992 }
993
994 function showLoadingIndicator( $object ) {
995 if ( ! $object.hasClass( 'frm_loading_form' ) && ! $object.hasClass( 'frm_loading_prev' ) ) {
996 addLoadingClass( $object );
997 $object.trigger( 'frmStartFormLoading' );
998 }
999 }
1000
1001 function addLoadingClass( $object ) {
1002 var loadingClass = isGoingToPrevPage( $object ) ? 'frm_loading_prev' : 'frm_loading_form';
1003
1004 $object.addClass( loadingClass );
1005 }
1006
1007 function isGoingToPrevPage( $object ) {
1008 return ( typeof frmProForm !== 'undefined' && frmProForm.goingToPreviousPage( $object ) );
1009 }
1010
1011 function removeSubmitLoading( $object, enable, processesRunning ) {
1012 var loadingForm;
1013
1014 if ( processesRunning > 0 ) {
1015 return;
1016 }
1017
1018 loadingForm = jQuery( '.frm_loading_form' );
1019 loadingForm.removeClass( 'frm_loading_form' );
1020 loadingForm.removeClass( 'frm_loading_prev' );
1021
1022 loadingForm.trigger( 'frmEndFormLoading' );
1023
1024 if ( enable === 'enable' ) {
1025 enableSubmitButton( loadingForm );
1026 enableSaveDraft( loadingForm );
1027 }
1028 }
1029
1030 function showFileLoading( object ) {
1031 var fileval,
1032 loading = document.getElementById( 'frm_loading' );
1033 if ( loading !== null ) {
1034 fileval = jQuery( object ).find( 'input[type=file]' ).val();
1035 if ( typeof fileval !== 'undefined' && fileval !== '' ) {
1036 setTimeout( function() {
1037 jQuery( loading ).fadeIn( 'slow' );
1038 }, 2000 );
1039 }
1040 }
1041 }
1042
1043 function clearDefault() {
1044 /*jshint validthis:true */
1045 toggleDefault( jQuery( this ), 'clear' );
1046 }
1047
1048 function replaceDefault() {
1049 /*jshint validthis:true */
1050 toggleDefault( jQuery( this ), 'replace' );
1051 }
1052
1053 function toggleDefault( $thisField, e ) {
1054 // TODO: Fix this for a default value that is a number or array
1055 var thisVal,
1056 v = $thisField.data( 'frmval' ).replace( /(\n|\r\n)/g, '\r' );
1057 if ( v === '' || typeof v === 'undefined' ) {
1058 return false;
1059 }
1060 thisVal = $thisField.val().replace( /(\n|\r\n)/g, '\r' );
1061
1062 if ( 'replace' === e ) {
1063 if ( thisVal === '' ) {
1064 $thisField.addClass( 'frm_default' ).val( v );
1065 }
1066 } else if ( thisVal == v ) {
1067 $thisField.removeClass( 'frm_default' ).val( '' );
1068 }
1069 }
1070
1071 function resendEmail() {
1072 /*jshint validthis:true */
1073 var $link = jQuery( this ),
1074 entryId = this.getAttribute( 'data-eid' ),
1075 formId = this.getAttribute( 'data-fid' ),
1076 label = $link.find( '.frm_link_label' );
1077 if ( label.length < 1 ) {
1078 label = $link;
1079 }
1080 label.append( '<span class="frm-wait"></span>' );
1081
1082 jQuery.ajax({
1083 type: 'POST',
1084 url: frm_js.ajax_url, // eslint-disable-line camelcase
1085 data: {
1086 action: 'frm_entries_send_email',
1087 entry_id: entryId,
1088 form_id: formId,
1089 nonce: frm_js.nonce // eslint-disable-line camelcase
1090 },
1091 success: function( msg ) {
1092 var admin = document.getElementById( 'wpbody' );
1093 if ( admin === null ) {
1094 label.html( msg );
1095 } else {
1096 label.html( '' );
1097 $link.after( msg );
1098 }
1099 }
1100 });
1101 return false;
1102 }
1103
1104 /**********************************************
1105 * General Helpers
1106 *********************************************/
1107
1108 function confirmClick() {
1109 /*jshint validthis:true */
1110 var message = jQuery( this ).data( 'frmconfirm' );
1111 return confirm( message );
1112 }
1113
1114 function toggleDiv() {
1115 /*jshint validthis:true */
1116 var div = jQuery( this ).data( 'frmtoggle' );
1117 if ( jQuery( div ).is( ':visible' ) ) {
1118 jQuery( div ).slideUp( 'fast' );
1119 } else {
1120 jQuery( div ).slideDown( 'fast' );
1121 }
1122 return false;
1123 }
1124
1125 /**********************************************
1126 * Fallback functions
1127 *********************************************/
1128
1129 function addTrimFallbackForIE() {
1130 if ( typeof String.prototype.trim !== 'function' ) {
1131 String.prototype.trim = function() {
1132 return this.replace( /^\s+|\s+$/g, '' );
1133 };
1134 }
1135 }
1136
1137 function addFilterFallbackForIE() {
1138 var t, len, res, thisp, i, val;
1139
1140 if ( ! Array.prototype.filter ) {
1141
1142 Array.prototype.filter = function( fun /*, thisp */ ) {
1143
1144 if ( this === void 0 || this === null ) {
1145 throw new TypeError();
1146 }
1147
1148 t = Object( this );
1149 len = t.length >>> 0;
1150 if ( typeof fun !== 'function' ) {
1151 throw new TypeError();
1152 }
1153
1154 res = [];
1155 thisp = arguments[1];
1156 for ( i = 0; i < len; i++ ) {
1157 if ( i in t ) {
1158 val = t[i]; // in case fun mutates this
1159 if ( fun.call( thisp, val, i, t ) ) {
1160 res.push( val );
1161 }
1162 }
1163 }
1164
1165 return res;
1166 };
1167 }
1168 }
1169
1170 /**
1171 * Check for -webkit-box-shadow css value for input:-webkit-autofill selector.
1172 * If this is a match, the User is autofilling the input on a Webkit browser.
1173 * We want to delete the Honeypot field, otherwise it will get triggered as spam on autocomplete.
1174 */
1175 function onHoneypotFieldChange() {
1176 var css = jQuery( this ).css( 'box-shadow' );
1177 if ( css.match( /inset/ ) ) {
1178 this.parentNode.removeChild( this );
1179 }
1180 }
1181
1182 function maybeMakeHoneypotFieldsUntabbable() {
1183 document.addEventListener( 'keydown', handleKeyUp );
1184
1185 function handleKeyUp( event ) {
1186 var code;
1187
1188 if ( 'undefined' !== typeof event.key ) {
1189 code = event.key;
1190 } else if ( 'undefined' !== typeof event.keyCode && 9 === event.keyCode ) {
1191 code = 'Tab';
1192 }
1193
1194 if ( 'Tab' === code ) {
1195 makeHoneypotFieldsUntabbable();
1196 document.removeEventListener( 'keydown', handleKeyUp );
1197 }
1198 }
1199
1200 function makeHoneypotFieldsUntabbable() {
1201 document.querySelectorAll( '.frm_verify' ).forEach(
1202 function( input ) {
1203 if ( input.id && 0 === input.id.indexOf( 'frm_email_' ) ) {
1204 input.setAttribute( 'tabindex', -1 );
1205 }
1206 }
1207 );
1208 }
1209 }
1210
1211 /**
1212 * Focus on the first sub field when clicking to the primary label of combo field.
1213 *
1214 * @since 4.10.02
1215 */
1216 function changeFocusWhenClickComboFieldLabel() {
1217 var label;
1218
1219 var comboInputsContainer = document.querySelectorAll( '.frm_combo_inputs_container' );
1220 comboInputsContainer.forEach( function( inputsContainer ) {
1221 if ( ! inputsContainer.closest( '.frm_form_field' ) ) {
1222 return;
1223 }
1224
1225 label = inputsContainer.closest( '.frm_form_field' ).querySelector( '.frm_primary_label' );
1226 if ( ! label ) {
1227 return;
1228 }
1229
1230 label.addEventListener( 'click', function( e ) {
1231 inputsContainer.querySelector( '.frm_form_field:first-child input, .frm_form_field:first-child select, .frm_form_field:first-child textarea' ).focus();
1232 });
1233 });
1234 }
1235
1236 function checkForErrorsAndMaybeSetFocus() {
1237 var errors, element, timeoutCallback;
1238
1239 if ( ! frm_js.focus_first_error ) { // eslint-disable-line camelcase
1240 return;
1241 }
1242
1243 errors = document.querySelectorAll( '.frm_form_field .frm_error' );
1244 if ( ! errors.length ) {
1245 return;
1246 }
1247
1248 element = errors[0];
1249 do {
1250 element = element.previousSibling;
1251 if ( -1 !== [ 'input', 'select', 'textarea' ].indexOf( element.nodeName.toLowerCase() ) ) {
1252 element.focus();
1253 break;
1254 }
1255
1256 if ( 'undefined' !== typeof element.classList ) {
1257 if ( element.classList.contains( 'html-active' ) ) {
1258 timeoutCallback = function() {
1259 var textarea = element.querySelector( 'textarea' );
1260 if ( null !== textarea ) {
1261 textarea.focus();
1262 }
1263 };
1264 } else if ( element.classList.contains( 'tmce-active' ) ) {
1265 timeoutCallback = function() {
1266 tinyMCE.activeEditor.focus();
1267 };
1268 }
1269
1270 if ( 'function' === typeof timeoutCallback ) {
1271 setTimeout( timeoutCallback, 0 );
1272 break;
1273 }
1274 }
1275 } while ( element.previousSibling );
1276 }
1277
1278 /**
1279 * Checks if is on IE browser.
1280 *
1281 * @since 5.4
1282 *
1283 * @return {Boolean}
1284 */
1285 function isIE() {
1286 return navigator.userAgent.indexOf( 'MSIE' ) > -1 || navigator.userAgent.indexOf( 'Trident' ) > -1;
1287 }
1288
1289 /**
1290 * Does the same as jQuery( document ).on( 'event', 'selector', handler ).
1291 *
1292 * @since 5.4
1293 *
1294 * @param {String} event Event name.
1295 * @param {String} selector Selector.
1296 * @param {Function} handler Handler.
1297 * @param {Boolean|Object} options Options to be added to `addEventListener()` method. Default is `false`.
1298 */
1299 function documentOn( event, selector, handler, options ) {
1300 if ( 'undefined' === typeof options ) {
1301 options = false;
1302 }
1303
1304 document.addEventListener( event, function( e ) {
1305 var target;
1306
1307 // loop parent nodes from the target to the delegation node.
1308 for ( target = e.target; target && target != this; target = target.parentNode ) {
1309 if ( target && target.matches && target.matches( selector ) ) {
1310 handler.call( target, e );
1311 break;
1312 }
1313 }
1314 }, options );
1315 }
1316
1317 function initFloatingLabels() {
1318 var checkFloatLabel, checkDropdownLabel, checkPlaceholderIE, runOnLoad, selector, floatClass;
1319
1320 selector = '.frm-show-form .frm_inside_container input, .frm-show-form .frm_inside_container select, .frm-show-form .frm_inside_container textarea';
1321 floatClass = 'frm_label_float_top';
1322
1323 checkFloatLabel = function( input ) {
1324 var container, shouldFloatTop, firstOpt;
1325
1326 container = input.closest( '.frm_inside_container' );
1327 if ( ! container ) {
1328 return;
1329 }
1330
1331 shouldFloatTop = input.value || document.activeElement === input;
1332
1333 container.classList.toggle( floatClass, shouldFloatTop );
1334
1335 if ( 'SELECT' === input.tagName ) {
1336 firstOpt = input.querySelector( 'option:first-child' );
1337
1338 if ( shouldFloatTop ) {
1339 if ( firstOpt.hasAttribute( 'data-label' ) ) {
1340 firstOpt.textContent = firstOpt.getAttribute( 'data-label' );
1341 firstOpt.removeAttribute( 'data-label' );
1342 }
1343 } else {
1344 if ( firstOpt.textContent ) {
1345 firstOpt.setAttribute( 'data-label', firstOpt.textContent );
1346 firstOpt.textContent = '';
1347 }
1348 }
1349 } else if ( isIE() ) {
1350 checkPlaceholderIE( input );
1351 }
1352 };
1353
1354 checkDropdownLabel = function() {
1355 document.querySelectorAll( '.frm-show-form .frm_inside_container:not(.' + floatClass + ') select' ).forEach( function( input ) {
1356 var firstOpt = input.querySelector( 'option:first-child' );
1357
1358 if ( firstOpt.textContent ) {
1359 firstOpt.setAttribute( 'data-label', firstOpt.textContent );
1360 firstOpt.textContent = '';
1361 }
1362 });
1363 };
1364
1365 checkPlaceholderIE = function( input ) {
1366 if ( input.value ) {
1367 // Don't need to handle this case because placeholder isn't shown.
1368 return;
1369 }
1370
1371 if ( document.activeElement === input ) {
1372 if ( input.hasAttribute( 'data-placeholder' ) ) {
1373 input.placeholder = input.getAttribute( 'data-placeholder' );
1374 input.removeAttribute( 'data-placeholder' );
1375 }
1376 } else {
1377 if ( input.placeholder ) {
1378 input.setAttribute( 'data-placeholder', input.placeholder );
1379 input.placeholder = '';
1380 }
1381 }
1382 };
1383
1384 [ 'focus', 'blur', 'change' ].forEach( function( eventName ) {
1385 documentOn(
1386 eventName,
1387 selector,
1388 function( event ) {
1389 checkFloatLabel( event.target );
1390 },
1391 true
1392 );
1393 });
1394
1395 jQuery( document ).on( 'change', selector, function( event ) {
1396 checkFloatLabel( event.target );
1397 });
1398
1399 runOnLoad = function( firstLoad ) {
1400 if ( firstLoad && document.activeElement && -1 !== [ 'INPUT', 'SELECT', 'TEXTAREA' ].indexOf( document.activeElement.tagName ) ) {
1401 checkFloatLabel( document.activeElement );
1402 } else if ( firstLoad ) {
1403 document.querySelectorAll( '.frm_inside_container' ).forEach(
1404 function( container ) {
1405 var input = container.querySelector( 'input, select, textarea' );
1406 if ( input && '' !== input.value ) {
1407 checkFloatLabel( input );
1408 }
1409 }
1410 );
1411 }
1412
1413 checkDropdownLabel();
1414
1415 if ( isIE() ) {
1416 document.querySelectorAll( selector ).forEach( function( input ) {
1417 checkPlaceholderIE( input );
1418 });
1419 }
1420 };
1421
1422 runOnLoad( true );
1423
1424 jQuery( document ).on( 'frmPageChanged', function( event ) {
1425 runOnLoad();
1426 });
1427
1428 document.addEventListener( 'frm_after_start_over', function( event ) {
1429 runOnLoad();
1430 });
1431 }
1432
1433 function shouldUpdateValidityMessage( target ) {
1434 if ( 'INPUT' !== target.nodeName ) {
1435 return false;
1436 }
1437
1438 if ( ! target.dataset.invmsg ) {
1439 return false;
1440 }
1441
1442 if ( 'text' !== target.getAttribute( 'type' ) ) {
1443 return false;
1444 }
1445
1446 if ( target.classList.contains( 'frm_verify' ) ) {
1447 return false;
1448 }
1449
1450 return true;
1451 }
1452
1453 function maybeClearCustomValidityMessage( event, field ) {
1454 var key,
1455 isInvalid = false;
1456
1457 if ( ! shouldUpdateValidityMessage( field ) ) {
1458 return;
1459 }
1460
1461 for ( key in field.validity ) {
1462 if ( 'customError' === key ) {
1463 continue;
1464 }
1465 if ( 'valid' !== key && field.validity[ key ] === true ) {
1466 isInvalid = true;
1467 break;
1468 }
1469 };
1470
1471 if ( ! isInvalid ) {
1472 field.setCustomValidity( '' );
1473 }
1474 }
1475
1476 function maybeShowNewTabFallbackMessage() {
1477 var messageEl;
1478
1479 if ( ! window.frmShowNewTabFallback ) {
1480 return;
1481 }
1482
1483 messageEl = document.querySelector( '#frm_form_' + frmShowNewTabFallback.formId + '_container .frm_message' );
1484 if ( ! messageEl ) {
1485 return;
1486 }
1487
1488 messageEl.insertAdjacentHTML( 'beforeend', ' ' + frmShowNewTabFallback.message );
1489 }
1490
1491 function setCustomValidityMessage() {
1492 var forms, length, index;
1493
1494 forms = document.getElementsByClassName( 'frm-show-form' );
1495 length = forms.length;
1496
1497 for ( index = 0; index < length; ++index ) {
1498 forms[ index ].addEventListener(
1499 'invalid',
1500 function( event ) {
1501 var target = event.target;
1502
1503 if ( shouldUpdateValidityMessage( target ) ) {
1504 target.setCustomValidity( target.dataset.invmsg );
1505 }
1506 },
1507 true
1508 );
1509 }
1510 }
1511
1512 function enableSubmitButtonOnBackButtonPress() {
1513 window.addEventListener( 'pageshow', function( event ) {
1514 if ( event.persisted ) {
1515 document.querySelectorAll( '.frm_loading_form' ).forEach(
1516 function( form ) {
1517 enableSubmitButton( jQuery( form ) );
1518 }
1519 );
1520 removeSubmitLoading();
1521 }
1522 });
1523 }
1524
1525 return {
1526 init: function() {
1527 maybeAddPolyfills();
1528
1529 jQuery( document ).off( 'submit.formidable', '.frm-show-form' );
1530 jQuery( document ).on( 'submit.formidable', '.frm-show-form', frmFrontForm.submitForm );
1531
1532 jQuery( '.frm-show-form input[onblur], .frm-show-form textarea[onblur]' ).each( function() {
1533 if ( jQuery( this ).val() === '' ) {
1534 jQuery( this ).trigger( 'blur' );
1535 }
1536 });
1537
1538 jQuery( document ).on( 'focus', '.frm_toggle_default', clearDefault );
1539 jQuery( document ).on( 'blur', '.frm_toggle_default', replaceDefault );
1540 jQuery( '.frm_toggle_default' ).trigger( 'blur' );
1541
1542 jQuery( document.getElementById( 'frm_resend_email' ) ).on( 'click', resendEmail );
1543
1544 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 );
1545
1546 jQuery( document ).on( 'change', '[id^=frm_email_]', onHoneypotFieldChange );
1547 maybeMakeHoneypotFieldsUntabbable();
1548
1549 jQuery( document ).on( 'click', 'a[data-frmconfirm]', confirmClick );
1550 jQuery( 'a[data-frmtoggle]' ).on( 'click', toggleDiv );
1551
1552 checkForErrorsAndMaybeSetFocus();
1553
1554 // Focus on the first sub field when clicking to the primary label of combo field.
1555 changeFocusWhenClickComboFieldLabel();
1556
1557 // Add fallbacks for IE.
1558 addTrimFallbackForIE(); // Trim only works in IE10+.
1559 addFilterFallbackForIE(); // Filter is not supported in any version of IE.
1560
1561 initFloatingLabels();
1562 maybeShowNewTabFallbackMessage();
1563
1564 jQuery( document ).on( 'frmAfterAddRow', setCustomValidityMessage );
1565 setCustomValidityMessage();
1566 jQuery( document ).on( 'frmFieldChanged', maybeClearCustomValidityMessage );
1567
1568 setSelectPlaceholderColor();
1569
1570 // Elementor popup show event. Fix Elementor Popup && FF Captcha field conflicts
1571 jQuery( document ).on( 'elementor/popup/show', frmRecaptcha );
1572
1573 enableSubmitButtonOnBackButtonPress();
1574 },
1575
1576 getFieldId: function( field, fullID ) {
1577 return getFieldId( field, fullID );
1578 },
1579
1580 renderRecaptcha: function( captcha ) {
1581 var formID, recaptchaID,
1582 size = captcha.getAttribute( 'data-size' ),
1583 rendered = captcha.getAttribute( 'data-rid' ) !== null,
1584 params = {
1585 'sitekey': captcha.getAttribute( 'data-sitekey' ),
1586 'size': size,
1587 'theme': captcha.getAttribute( 'data-theme' )
1588 };
1589
1590 if ( rendered ) {
1591 return;
1592 }
1593
1594 if ( size === 'invisible' ) {
1595 formID = jQuery( captcha ).closest( 'form' ).find( 'input[name="form_id"]' ).val();
1596 jQuery( captcha ).closest( '.frm_form_field .frm_primary_label' ).hide();
1597 params.callback = function( token ) {
1598 frmFrontForm.afterRecaptcha( token, formID );
1599 };
1600 }
1601
1602 recaptchaID = grecaptcha.render( captcha.id, params );
1603
1604 captcha.setAttribute( 'data-rid', recaptchaID );
1605 },
1606
1607 afterSingleRecaptcha: function() {
1608 var object = jQuery( '.frm-show-form .g-recaptcha' ).closest( 'form' )[0];
1609 frmFrontForm.submitFormNow( object );
1610 },
1611
1612 afterRecaptcha: function( token, formID ) {
1613 var object = jQuery( '#frm_form_' + formID + '_container form' )[0];
1614 frmFrontForm.submitFormNow( object );
1615 },
1616
1617 submitForm: function( e ) {
1618 frmFrontForm.submitFormManual( e, this );
1619 },
1620
1621 submitFormManual: function( e, object ) {
1622 var isPro, errors,
1623 invisibleRecaptcha = hasInvisibleRecaptcha( object ),
1624 classList = object.className.trim().split( /\s+/gi );
1625
1626 if ( classList && invisibleRecaptcha.length < 1 ) {
1627 isPro = classList.indexOf( 'frm_pro_form' ) > -1;
1628 if ( ! isPro ) {
1629 return;
1630 }
1631 }
1632
1633 if ( jQuery( 'body' ).hasClass( 'wp-admin' ) && jQuery( object ).closest( '.frmapi-form' ).length < 1 ) {
1634 return;
1635 }
1636
1637 e.preventDefault();
1638
1639 if ( typeof frmProForm !== 'undefined' && typeof frmProForm.submitAllowed === 'function' ) {
1640 if ( ! frmProForm.submitAllowed( object ) ) {
1641 return;
1642 }
1643 }
1644
1645 if ( invisibleRecaptcha.length ) {
1646 showLoadingIndicator( jQuery( object ) );
1647 executeInvisibleRecaptcha( invisibleRecaptcha );
1648 } else {
1649
1650 errors = frmFrontForm.validateFormSubmit( object );
1651
1652 if ( Object.keys( errors ).length === 0 ) {
1653 showSubmitLoading( jQuery( object ) );
1654
1655 frmFrontForm.submitFormNow( object, classList );
1656 }
1657 }
1658 },
1659
1660 submitFormNow: function( object ) {
1661 var hasFileFields, antispamInput,
1662 classList = object.className.trim().split( /\s+/gi );
1663
1664 if ( object.hasAttribute( 'data-token' ) && null === object.querySelector( '[name="antispam_token"]' ) ) {
1665 // include the antispam token on form submit.
1666 antispamInput = document.createElement( 'input' );
1667 antispamInput.type = 'hidden';
1668 antispamInput.name = 'antispam_token';
1669 antispamInput.value = object.getAttribute( 'data-token' );
1670 object.appendChild( antispamInput );
1671 }
1672
1673 if ( classList.indexOf( 'frm_ajax_submit' ) > -1 ) {
1674 hasFileFields = jQuery( object ).find( 'input[type="file"]' ).filter( function() {
1675 return !! this.value;
1676 }).length;
1677 if ( hasFileFields < 1 ) {
1678 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1679 frmFrontForm.checkFormErrors( object, action );
1680 } else {
1681 object.submit();
1682 }
1683 } else {
1684 object.submit();
1685 }
1686 },
1687
1688 validateFormSubmit: function( object ) {
1689 if ( typeof tinyMCE !== 'undefined' && jQuery( object ).find( '.wp-editor-wrap' ).length ) {
1690 tinyMCE.triggerSave();
1691 }
1692
1693 jsErrors = [];
1694
1695 if ( shouldJSValidate( object ) ) {
1696 frmFrontForm.getAjaxFormErrors( object );
1697
1698 if ( Object.keys( jsErrors ).length ) {
1699 frmFrontForm.addAjaxFormErrors( object );
1700 }
1701 }
1702
1703 return jsErrors;
1704 },
1705
1706 getAjaxFormErrors: function( object ) {
1707 var customErrors, key;
1708
1709 jsErrors = validateForm( object );
1710 if ( typeof frmThemeOverride_jsErrors === 'function' ) { // eslint-disable-line camelcase
1711 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1712 customErrors = frmThemeOverride_jsErrors( action, object );
1713 if ( Object.keys( customErrors ).length ) {
1714 for ( key in customErrors ) {
1715 jsErrors[ key ] = customErrors[ key ];
1716 }
1717 }
1718 }
1719
1720 return jsErrors;
1721 },
1722
1723 addAjaxFormErrors: function( object ) {
1724 var key, $fieldCont;
1725 removeAllErrors();
1726
1727 for ( key in jsErrors ) {
1728 $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
1729
1730 if ( $fieldCont.length ) {
1731 addFieldError( $fieldCont, key, jsErrors );
1732 } else {
1733 // we are unable to show the error, so remove it
1734 delete jsErrors[ key ];
1735 }
1736 }
1737
1738 scrollToFirstField( object );
1739 checkForErrorsAndMaybeSetFocus();
1740 },
1741
1742 checkFormErrors: function( object, action ) {
1743 getFormErrors( object, action );
1744 },
1745
1746 checkRequiredField: function( field, errors ) {
1747 return checkRequiredField( field, errors );
1748 },
1749
1750 showSubmitLoading: function( $object ) {
1751 showSubmitLoading( $object );
1752 },
1753
1754 removeSubmitLoading: function( $object, enable, processesRunning ) {
1755 removeSubmitLoading( $object, enable, processesRunning );
1756 },
1757
1758 scrollToID: function( id ) {
1759 var object = jQuery( document.getElementById( id ) );
1760 frmFrontForm.scrollMsg( object, false );
1761 },
1762
1763 scrollMsg: function( id, object, animate ) {
1764 var newPos, m, b, screenTop, screenBottom,
1765 scrollObj = '';
1766 if ( typeof object === 'undefined' ) {
1767 scrollObj = jQuery( document.getElementById( 'frm_form_' + id + '_container' ) );
1768 if ( scrollObj.length < 1 ) {
1769 return;
1770 }
1771 } else if ( typeof id === 'string' ) {
1772 scrollObj = jQuery( object ).find( '#frm_field_' + id + '_container' );
1773 } else {
1774 scrollObj = id;
1775 }
1776
1777 jQuery( scrollObj ).trigger( 'focus' );
1778 newPos = scrollObj.offset().top;
1779 if ( ! newPos || frm_js.offset === '-1' ) { // eslint-disable-line camelcase
1780 return;
1781 }
1782 newPos = newPos - frm_js.offset; // eslint-disable-line camelcase
1783
1784 m = jQuery( 'html' ).css( 'margin-top' );
1785 b = jQuery( 'body' ).css( 'margin-top' );
1786 if ( m || b ) {
1787 newPos = newPos - parseInt( m ) - parseInt( b );
1788 }
1789
1790 if ( newPos && window.innerHeight ) {
1791 screenTop = document.documentElement.scrollTop || document.body.scrollTop;
1792 screenBottom = screenTop + window.innerHeight;
1793
1794 if ( newPos > screenBottom || newPos < screenTop ) {
1795 // Not in view
1796 if ( typeof animate === 'undefined' ) {
1797 jQuery( window ).scrollTop( newPos );
1798 } else {
1799 jQuery( 'html,body' ).animate({ scrollTop: newPos }, 500 );
1800 }
1801 return false;
1802 }
1803 }
1804 },
1805
1806 fieldValueChanged: function( e ) {
1807 /*jshint validthis:true */
1808
1809 var fieldId = frmFrontForm.getFieldId( this, false );
1810 if ( ! fieldId || typeof fieldId === 'undefined' ) {
1811 return;
1812 }
1813
1814 if ( e.frmTriggered && e.frmTriggered == fieldId ) {
1815 return;
1816 }
1817
1818 jQuery( document ).trigger( 'frmFieldChanged', [ this, fieldId, e ]);
1819
1820 if ( e.selfTriggered !== true ) {
1821 maybeValidateChange( this );
1822 }
1823 },
1824
1825 savingDraft: function( object ) {
1826 console.warn( 'DEPRECATED: function frmFrontForm.savingDraft in v3.0 use frmProForm.savingDraft' );
1827 if ( typeof frmProForm !== 'undefined' ) {
1828 return frmProForm.savingDraft( object );
1829 }
1830 },
1831
1832 goingToPreviousPage: function( object ) {
1833 console.warn( 'DEPRECATED: function frmFrontForm.goingToPreviousPage in v3.0 use frmProForm.goingToPreviousPage' );
1834 if ( typeof frmProForm !== 'undefined' ) {
1835 return frmProForm.goingToPreviousPage( object );
1836 }
1837 },
1838
1839 hideOrShowFields: function() {
1840 console.warn( 'DEPRECATED: function frmFrontForm.hideOrShowFields in v3.0 use frmProForm.hideOrShowFields' );
1841 if ( typeof frmProForm !== 'undefined' ) {
1842 frmProForm.hideOrShowFields();
1843 }
1844 },
1845
1846 hidePreviouslyHiddenFields: function() {
1847 console.warn( 'DEPRECATED: function frmFrontForm.hidePreviouslyHiddenFields in v3.0 use frmProForm.hidePreviouslyHiddenFields' );
1848 if ( typeof frmProForm !== 'undefined' ) {
1849 frmProForm.hidePreviouslyHiddenFields();
1850 }
1851 },
1852
1853 checkDependentDynamicFields: function( ids ) {
1854 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentDynamicFields in v3.0 use frmProForm.checkDependentDynamicFields' );
1855 if ( typeof frmProForm !== 'undefined' ) {
1856 frmProForm.checkDependentDynamicFields( ids );
1857 }
1858 },
1859
1860 checkDependentLookupFields: function( ids ) {
1861 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentLookupFields in v3.0 use frmProForm.checkDependentLookupFields' );
1862 if ( typeof frmProForm !== 'undefined' ) {
1863 frmProForm.checkDependentLookupFields( ids );
1864 }
1865 },
1866
1867 loadGoogle: function() {
1868 console.warn( 'DEPRECATED: function frmFrontForm.loadGoogle in v3.0 use frmProForm.loadGoogle' );
1869 frmProForm.loadGoogle();
1870 },
1871
1872 escapeHtml: function( text ) {
1873 return text
1874 .replace( /&/g, '&amp;' )
1875 .replace( /</g, '&lt;' )
1876 .replace( />/g, '&gt;' )
1877 .replace( /"/g, '&quot;' )
1878 .replace( /'/g, '&#039;' );
1879 },
1880
1881 invisible: function( classes ) {
1882 jQuery( classes ).css( 'visibility', 'hidden' );
1883 },
1884
1885 visible: function( classes ) {
1886 jQuery( classes ).css( 'visibility', 'visible' );
1887 },
1888
1889 triggerCustomEvent: triggerCustomEvent
1890 };
1891 }
1892 frmFrontForm = frmFrontFormJS();
1893
1894 jQuery( document ).ready( function() {
1895 frmFrontForm.init();
1896 });
1897
1898 function frmRecaptcha() {
1899 var c, cl,
1900 captchas = jQuery( '.frm-g-recaptcha' );
1901 for ( c = 0, cl = captchas.length; c < cl; c++ ) {
1902 frmFrontForm.renderRecaptcha( captchas[c]);
1903 }
1904 }
1905
1906 function frmAfterRecaptcha( token ) {
1907 frmFrontForm.afterSingleRecaptcha( token );
1908 }
1909
1910 function frmUpdateField( entryId, fieldId, value, message, num ) {
1911 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).html( '<span class="frm-loading-img"></span>' );
1912 jQuery.ajax({
1913 type: 'POST',
1914 url: frm_js.ajax_url, // eslint-disable-line camelcase
1915 data: {
1916 action: 'frm_entries_update_field_ajax',
1917 entry_id: entryId,
1918 field_id: fieldId,
1919 value: value,
1920 nonce: frm_js.nonce // eslint-disable-line camelcase
1921 },
1922 success: function() {
1923 if ( message.replace( /^\s+|\s+$/g, '' ) === '' ) {
1924 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).fadeOut( 'slow' );
1925 } else {
1926 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).replaceWith( message );
1927 }
1928 }
1929 });
1930 }
1931
1932 function frmDeleteEntry( entryId, prefix ) {
1933 console.warn( 'DEPRECATED: function frmDeleteEntry in v2.0.13 use frmFrontForm.deleteEntry' );
1934 jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( '<span class="frm-loading-img" id="frm_delete_' + entryId + '"></span>' );
1935 jQuery.ajax({
1936 type: 'POST',
1937 url: frm_js.ajax_url, // eslint-disable-line camelcase
1938 data: {
1939 action: 'frm_entries_destroy',
1940 entry: entryId,
1941 nonce: frm_js.nonce // eslint-disable-line camelcase
1942 },
1943 success: function( html ) {
1944 if ( html.replace( /^\s+|\s+$/g, '' ) === 'success' ) {
1945 jQuery( document.getElementById( prefix + entryId ) ).fadeOut( 'slow' );
1946 } else {
1947 jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( html );
1948 }
1949 }
1950 });
1951 }
1952
1953 function frmOnSubmit( e ) {
1954 console.warn( 'DEPRECATED: function frmOnSubmit in v2.0 use frmFrontForm.submitForm' );
1955 frmFrontForm.submitForm( e, this );
1956 }
1957
1958 function frm_resend_email( entryId, formId ) { // eslint-disable-line camelcase
1959 var $link = jQuery( document.getElementById( 'frm_resend_email' ) );
1960 console.warn( 'DEPRECATED: function frm_resend_email in v2.0' );
1961 $link.append( '<span class="spinner" style="display:inline"></span>' );
1962 jQuery.ajax({
1963 type: 'POST',
1964 url: frm_js.ajax_url, // eslint-disable-line camelcase
1965 data: {
1966 action: 'frm_entries_send_email',
1967 entry_id: entryId,
1968 form_id: formId,
1969 nonce: frm_js.nonce // eslint-disable-line camelcase
1970 },
1971 success: function( msg ) {
1972 $link.replaceWith( msg );
1973 }
1974 });
1975 }
1976