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

1,961 lines 53.9 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 ( 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 return {
1513 init: function() {
1514 maybeAddPolyfills();
1515
1516 jQuery( document ).off( 'submit.formidable', '.frm-show-form' );
1517 jQuery( document ).on( 'submit.formidable', '.frm-show-form', frmFrontForm.submitForm );
1518
1519 jQuery( '.frm-show-form input[onblur], .frm-show-form textarea[onblur]' ).each( function() {
1520 if ( jQuery( this ).val() === '' ) {
1521 jQuery( this ).trigger( 'blur' );
1522 }
1523 });
1524
1525 jQuery( document ).on( 'focus', '.frm_toggle_default', clearDefault );
1526 jQuery( document ).on( 'blur', '.frm_toggle_default', replaceDefault );
1527 jQuery( '.frm_toggle_default' ).trigger( 'blur' );
1528
1529 jQuery( document.getElementById( 'frm_resend_email' ) ).on( 'click', resendEmail );
1530
1531 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 );
1532
1533 jQuery( document ).on( 'change', '[id^=frm_email_]', onHoneypotFieldChange );
1534 maybeMakeHoneypotFieldsUntabbable();
1535
1536 jQuery( document ).on( 'click', 'a[data-frmconfirm]', confirmClick );
1537 jQuery( 'a[data-frmtoggle]' ).on( 'click', toggleDiv );
1538
1539 checkForErrorsAndMaybeSetFocus();
1540
1541 // Focus on the first sub field when clicking to the primary label of combo field.
1542 changeFocusWhenClickComboFieldLabel();
1543
1544 // Add fallbacks for IE.
1545 addTrimFallbackForIE(); // Trim only works in IE10+.
1546 addFilterFallbackForIE(); // Filter is not supported in any version of IE.
1547
1548 initFloatingLabels();
1549 maybeShowNewTabFallbackMessage();
1550
1551 jQuery( document ).on( 'frmAfterAddRow', setCustomValidityMessage );
1552 setCustomValidityMessage();
1553 jQuery( document ).on( 'frmFieldChanged', maybeClearCustomValidityMessage );
1554
1555 setSelectPlaceholderColor();
1556
1557 // Elementor popup show event. Fix Elementor Popup && FF Captcha field conflicts
1558 jQuery( document ).on( 'elementor/popup/show', frmRecaptcha );
1559 },
1560
1561 getFieldId: function( field, fullID ) {
1562 return getFieldId( field, fullID );
1563 },
1564
1565 renderRecaptcha: function( captcha ) {
1566 var formID, recaptchaID,
1567 size = captcha.getAttribute( 'data-size' ),
1568 rendered = captcha.getAttribute( 'data-rid' ) !== null,
1569 params = {
1570 'sitekey': captcha.getAttribute( 'data-sitekey' ),
1571 'size': size,
1572 'theme': captcha.getAttribute( 'data-theme' )
1573 };
1574
1575 if ( rendered ) {
1576 return;
1577 }
1578
1579 if ( size === 'invisible' ) {
1580 formID = jQuery( captcha ).closest( 'form' ).find( 'input[name="form_id"]' ).val();
1581 jQuery( captcha ).closest( '.frm_form_field .frm_primary_label' ).hide();
1582 params.callback = function( token ) {
1583 frmFrontForm.afterRecaptcha( token, formID );
1584 };
1585 }
1586
1587 recaptchaID = grecaptcha.render( captcha.id, params );
1588
1589 captcha.setAttribute( 'data-rid', recaptchaID );
1590 },
1591
1592 afterSingleRecaptcha: function() {
1593 var object = jQuery( '.frm-show-form .g-recaptcha' ).closest( 'form' )[0];
1594 frmFrontForm.submitFormNow( object );
1595 },
1596
1597 afterRecaptcha: function( token, formID ) {
1598 var object = jQuery( '#frm_form_' + formID + '_container form' )[0];
1599 frmFrontForm.submitFormNow( object );
1600 },
1601
1602 submitForm: function( e ) {
1603 frmFrontForm.submitFormManual( e, this );
1604 },
1605
1606 submitFormManual: function( e, object ) {
1607 var isPro, errors,
1608 invisibleRecaptcha = hasInvisibleRecaptcha( object ),
1609 classList = object.className.trim().split( /\s+/gi );
1610
1611 if ( classList && invisibleRecaptcha.length < 1 ) {
1612 isPro = classList.indexOf( 'frm_pro_form' ) > -1;
1613 if ( ! isPro ) {
1614 return;
1615 }
1616 }
1617
1618 if ( jQuery( 'body' ).hasClass( 'wp-admin' ) && jQuery( object ).closest( '.frmapi-form' ).length < 1 ) {
1619 return;
1620 }
1621
1622 e.preventDefault();
1623
1624 if ( typeof frmProForm !== 'undefined' && typeof frmProForm.submitAllowed === 'function' ) {
1625 if ( ! frmProForm.submitAllowed( object ) ) {
1626 return;
1627 }
1628 }
1629
1630 if ( invisibleRecaptcha.length ) {
1631 showLoadingIndicator( jQuery( object ) );
1632 executeInvisibleRecaptcha( invisibleRecaptcha );
1633 } else {
1634
1635 errors = frmFrontForm.validateFormSubmit( object );
1636
1637 if ( Object.keys( errors ).length === 0 ) {
1638 showSubmitLoading( jQuery( object ) );
1639
1640 frmFrontForm.submitFormNow( object, classList );
1641 }
1642 }
1643 },
1644
1645 submitFormNow: function( object ) {
1646 var hasFileFields, antispamInput,
1647 classList = object.className.trim().split( /\s+/gi );
1648
1649 if ( object.hasAttribute( 'data-token' ) && null === object.querySelector( '[name="antispam_token"]' ) ) {
1650 // include the antispam token on form submit.
1651 antispamInput = document.createElement( 'input' );
1652 antispamInput.type = 'hidden';
1653 antispamInput.name = 'antispam_token';
1654 antispamInput.value = object.getAttribute( 'data-token' );
1655 object.appendChild( antispamInput );
1656 }
1657
1658 if ( classList.indexOf( 'frm_ajax_submit' ) > -1 ) {
1659 hasFileFields = jQuery( object ).find( 'input[type="file"]' ).filter( function() {
1660 return !! this.value;
1661 }).length;
1662 if ( hasFileFields < 1 ) {
1663 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1664 frmFrontForm.checkFormErrors( object, action );
1665 } else {
1666 object.submit();
1667 }
1668 } else {
1669 object.submit();
1670 }
1671 },
1672
1673 validateFormSubmit: function( object ) {
1674 if ( typeof tinyMCE !== 'undefined' && jQuery( object ).find( '.wp-editor-wrap' ).length ) {
1675 tinyMCE.triggerSave();
1676 }
1677
1678 jsErrors = [];
1679
1680 if ( shouldJSValidate( object ) ) {
1681 frmFrontForm.getAjaxFormErrors( object );
1682
1683 if ( Object.keys( jsErrors ).length ) {
1684 frmFrontForm.addAjaxFormErrors( object );
1685 }
1686 }
1687
1688 return jsErrors;
1689 },
1690
1691 getAjaxFormErrors: function( object ) {
1692 var customErrors, key;
1693
1694 jsErrors = validateForm( object );
1695 if ( typeof frmThemeOverride_jsErrors === 'function' ) { // eslint-disable-line camelcase
1696 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1697 customErrors = frmThemeOverride_jsErrors( action, object );
1698 if ( Object.keys( customErrors ).length ) {
1699 for ( key in customErrors ) {
1700 jsErrors[ key ] = customErrors[ key ];
1701 }
1702 }
1703 }
1704
1705 return jsErrors;
1706 },
1707
1708 addAjaxFormErrors: function( object ) {
1709 var key, $fieldCont;
1710 removeAllErrors();
1711
1712 for ( key in jsErrors ) {
1713 $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
1714
1715 if ( $fieldCont.length ) {
1716 addFieldError( $fieldCont, key, jsErrors );
1717 } else {
1718 // we are unable to show the error, so remove it
1719 delete jsErrors[ key ];
1720 }
1721 }
1722
1723 scrollToFirstField( object );
1724 checkForErrorsAndMaybeSetFocus();
1725 },
1726
1727 checkFormErrors: function( object, action ) {
1728 getFormErrors( object, action );
1729 },
1730
1731 checkRequiredField: function( field, errors ) {
1732 return checkRequiredField( field, errors );
1733 },
1734
1735 showSubmitLoading: function( $object ) {
1736 showSubmitLoading( $object );
1737 },
1738
1739 removeSubmitLoading: function( $object, enable, processesRunning ) {
1740 removeSubmitLoading( $object, enable, processesRunning );
1741 },
1742
1743 scrollToID: function( id ) {
1744 var object = jQuery( document.getElementById( id ) );
1745 frmFrontForm.scrollMsg( object, false );
1746 },
1747
1748 scrollMsg: function( id, object, animate ) {
1749 var newPos, m, b, screenTop, screenBottom,
1750 scrollObj = '';
1751 if ( typeof object === 'undefined' ) {
1752 scrollObj = jQuery( document.getElementById( 'frm_form_' + id + '_container' ) );
1753 if ( scrollObj.length < 1 ) {
1754 return;
1755 }
1756 } else if ( typeof id === 'string' ) {
1757 scrollObj = jQuery( object ).find( '#frm_field_' + id + '_container' );
1758 } else {
1759 scrollObj = id;
1760 }
1761
1762 jQuery( scrollObj ).trigger( 'focus' );
1763 newPos = scrollObj.offset().top;
1764 if ( ! newPos || frm_js.offset === '-1' ) { // eslint-disable-line camelcase
1765 return;
1766 }
1767 newPos = newPos - frm_js.offset; // eslint-disable-line camelcase
1768
1769 m = jQuery( 'html' ).css( 'margin-top' );
1770 b = jQuery( 'body' ).css( 'margin-top' );
1771 if ( m || b ) {
1772 newPos = newPos - parseInt( m ) - parseInt( b );
1773 }
1774
1775 if ( newPos && window.innerHeight ) {
1776 screenTop = document.documentElement.scrollTop || document.body.scrollTop;
1777 screenBottom = screenTop + window.innerHeight;
1778
1779 if ( newPos > screenBottom || newPos < screenTop ) {
1780 // Not in view
1781 if ( typeof animate === 'undefined' ) {
1782 jQuery( window ).scrollTop( newPos );
1783 } else {
1784 jQuery( 'html,body' ).animate({ scrollTop: newPos }, 500 );
1785 }
1786 return false;
1787 }
1788 }
1789 },
1790
1791 fieldValueChanged: function( e ) {
1792 /*jshint validthis:true */
1793
1794 var fieldId = frmFrontForm.getFieldId( this, false );
1795 if ( ! fieldId || typeof fieldId === 'undefined' ) {
1796 return;
1797 }
1798
1799 if ( e.frmTriggered && e.frmTriggered == fieldId ) {
1800 return;
1801 }
1802
1803 jQuery( document ).trigger( 'frmFieldChanged', [ this, fieldId, e ]);
1804
1805 if ( e.selfTriggered !== true ) {
1806 maybeValidateChange( this );
1807 }
1808 },
1809
1810 savingDraft: function( object ) {
1811 console.warn( 'DEPRECATED: function frmFrontForm.savingDraft in v3.0 use frmProForm.savingDraft' );
1812 if ( typeof frmProForm !== 'undefined' ) {
1813 return frmProForm.savingDraft( object );
1814 }
1815 },
1816
1817 goingToPreviousPage: function( object ) {
1818 console.warn( 'DEPRECATED: function frmFrontForm.goingToPreviousPage in v3.0 use frmProForm.goingToPreviousPage' );
1819 if ( typeof frmProForm !== 'undefined' ) {
1820 return frmProForm.goingToPreviousPage( object );
1821 }
1822 },
1823
1824 hideOrShowFields: function() {
1825 console.warn( 'DEPRECATED: function frmFrontForm.hideOrShowFields in v3.0 use frmProForm.hideOrShowFields' );
1826 if ( typeof frmProForm !== 'undefined' ) {
1827 frmProForm.hideOrShowFields();
1828 }
1829 },
1830
1831 hidePreviouslyHiddenFields: function() {
1832 console.warn( 'DEPRECATED: function frmFrontForm.hidePreviouslyHiddenFields in v3.0 use frmProForm.hidePreviouslyHiddenFields' );
1833 if ( typeof frmProForm !== 'undefined' ) {
1834 frmProForm.hidePreviouslyHiddenFields();
1835 }
1836 },
1837
1838 checkDependentDynamicFields: function( ids ) {
1839 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentDynamicFields in v3.0 use frmProForm.checkDependentDynamicFields' );
1840 if ( typeof frmProForm !== 'undefined' ) {
1841 frmProForm.checkDependentDynamicFields( ids );
1842 }
1843 },
1844
1845 checkDependentLookupFields: function( ids ) {
1846 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentLookupFields in v3.0 use frmProForm.checkDependentLookupFields' );
1847 if ( typeof frmProForm !== 'undefined' ) {
1848 frmProForm.checkDependentLookupFields( ids );
1849 }
1850 },
1851
1852 loadGoogle: function() {
1853 console.warn( 'DEPRECATED: function frmFrontForm.loadGoogle in v3.0 use frmProForm.loadGoogle' );
1854 frmProForm.loadGoogle();
1855 },
1856
1857 escapeHtml: function( text ) {
1858 return text
1859 .replace( /&/g, '&amp;' )
1860 .replace( /</g, '&lt;' )
1861 .replace( />/g, '&gt;' )
1862 .replace( /"/g, '&quot;' )
1863 .replace( /'/g, '&#039;' );
1864 },
1865
1866 invisible: function( classes ) {
1867 jQuery( classes ).css( 'visibility', 'hidden' );
1868 },
1869
1870 visible: function( classes ) {
1871 jQuery( classes ).css( 'visibility', 'visible' );
1872 },
1873
1874 triggerCustomEvent: triggerCustomEvent
1875 };
1876 }
1877 frmFrontForm = frmFrontFormJS();
1878
1879 jQuery( document ).ready( function() {
1880 frmFrontForm.init();
1881 });
1882
1883 function frmRecaptcha() {
1884 var c, cl,
1885 captchas = jQuery( '.frm-g-recaptcha' );
1886 for ( c = 0, cl = captchas.length; c < cl; c++ ) {
1887 frmFrontForm.renderRecaptcha( captchas[c]);
1888 }
1889 }
1890
1891 function frmAfterRecaptcha( token ) {
1892 frmFrontForm.afterSingleRecaptcha( token );
1893 }
1894
1895 function frmUpdateField( entryId, fieldId, value, message, num ) {
1896 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).html( '<span class="frm-loading-img"></span>' );
1897 jQuery.ajax({
1898 type: 'POST',
1899 url: frm_js.ajax_url, // eslint-disable-line camelcase
1900 data: {
1901 action: 'frm_entries_update_field_ajax',
1902 entry_id: entryId,
1903 field_id: fieldId,
1904 value: value,
1905 nonce: frm_js.nonce // eslint-disable-line camelcase
1906 },
1907 success: function() {
1908 if ( message.replace( /^\s+|\s+$/g, '' ) === '' ) {
1909 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).fadeOut( 'slow' );
1910 } else {
1911 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).replaceWith( message );
1912 }
1913 }
1914 });
1915 }
1916
1917 function frmDeleteEntry( entryId, prefix ) {
1918 console.warn( 'DEPRECATED: function frmDeleteEntry in v2.0.13 use frmFrontForm.deleteEntry' );
1919 jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( '<span class="frm-loading-img" id="frm_delete_' + entryId + '"></span>' );
1920 jQuery.ajax({
1921 type: 'POST',
1922 url: frm_js.ajax_url, // eslint-disable-line camelcase
1923 data: {
1924 action: 'frm_entries_destroy',
1925 entry: entryId,
1926 nonce: frm_js.nonce // eslint-disable-line camelcase
1927 },
1928 success: function( html ) {
1929 if ( html.replace( /^\s+|\s+$/g, '' ) === 'success' ) {
1930 jQuery( document.getElementById( prefix + entryId ) ).fadeOut( 'slow' );
1931 } else {
1932 jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( html );
1933 }
1934 }
1935 });
1936 }
1937
1938 function frmOnSubmit( e ) {
1939 console.warn( 'DEPRECATED: function frmOnSubmit in v2.0 use frmFrontForm.submitForm' );
1940 frmFrontForm.submitForm( e, this );
1941 }
1942
1943 function frm_resend_email( entryId, formId ) { // eslint-disable-line camelcase
1944 var $link = jQuery( document.getElementById( 'frm_resend_email' ) );
1945 console.warn( 'DEPRECATED: function frm_resend_email in v2.0' );
1946 $link.append( '<span class="spinner" style="display:inline"></span>' );
1947 jQuery.ajax({
1948 type: 'POST',
1949 url: frm_js.ajax_url, // eslint-disable-line camelcase
1950 data: {
1951 action: 'frm_entries_send_email',
1952 entry_id: entryId,
1953 form_id: formId,
1954 nonce: frm_js.nonce // eslint-disable-line camelcase
1955 },
1956 success: function( msg ) {
1957 $link.replaceWith( msg );
1958 }
1959 });
1960 }
1961