PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.5.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.5.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / js / formidable.js

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

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