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

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