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

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