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

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