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

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

1,751 lines 48.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* exported frmRecaptcha, frmAfterRecaptcha, frmUpdateField, frmDeleteEntry, frmOnSubmit, frm_resend_email */
2
3 var frmFrontForm;
4
5 function frmFrontFormJS() {
6 'use strict';
7
8 /*global jQuery:false, frm_js, grecaptcha, frmProForm, tinyMCE */
9 /*global frmThemeOverride_jsErrors, frmThemeOverride_frmPlaceError, frmThemeOverride_frmAfterSubmit */
10
11 var action = '';
12 var jsErrors = [];
13
14 /**
15 * Maybe add polyfills.
16 *
17 * @since 5.4
18 */
19 function maybeAddPolyfills() {
20 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 ) {
820 describedBy = describedBy + ' ' + id;
821 }
822 input.attr( 'aria-describedby', describedBy );
823 }
824 input.attr( 'aria-invalid', true );
825
826 jQuery( document ).trigger( 'frmAddFieldError', [ $fieldCont, key, jsErrors ]);
827 }
828 }
829
830 function removeFieldError( $fieldCont ) {
831 var errorMessage = $fieldCont.find( '.frm_error' ),
832 errorId = errorMessage.attr( 'id' ),
833 input = $fieldCont.find( 'input, select, textarea' ),
834 describedBy = input.attr( 'aria-describedby' );
835
836 $fieldCont.removeClass( 'frm_blank_field has-error' );
837 errorMessage.remove();
838 input.attr( 'aria-invalid', false );
839 input.removeAttr( 'aria-describedby' );
840
841 if ( typeof describedBy !== 'undefined' ) {
842 describedBy = describedBy.replace( errorId, '' );
843 input.attr( 'aria-describedby', describedBy );
844 }
845 }
846
847 function removeAllErrors() {
848 jQuery( '.form-field' ).removeClass( 'frm_blank_field has-error' );
849 jQuery( '.form-field .frm_error' ).replaceWith( '' );
850 jQuery( '.frm_error_style' ).remove();
851 }
852
853 function scrollToFirstField( object ) {
854 var field = jQuery( object ).find( '.frm_blank_field' ).first();
855 if ( field.length ) {
856 frmFrontForm.scrollMsg( field, object, true );
857 }
858 }
859
860 function showSubmitLoading( $object ) {
861 showLoadingIndicator( $object );
862 disableSubmitButton( $object );
863 disableSaveDraft( $object );
864 }
865
866 function showLoadingIndicator( $object ) {
867 if ( ! $object.hasClass( 'frm_loading_form' ) && ! $object.hasClass( 'frm_loading_prev' ) ) {
868 addLoadingClass( $object );
869 $object.trigger( 'frmStartFormLoading' );
870 }
871 }
872
873 function addLoadingClass( $object ) {
874 var loadingClass = isGoingToPrevPage( $object ) ? 'frm_loading_prev' : 'frm_loading_form';
875
876 $object.addClass( loadingClass );
877 }
878
879 function isGoingToPrevPage( $object ) {
880 return ( typeof frmProForm !== 'undefined' && frmProForm.goingToPreviousPage( $object ) );
881 }
882
883 function removeSubmitLoading( $object, enable, processesRunning ) {
884 var loadingForm;
885
886 if ( processesRunning > 0 ) {
887 return;
888 }
889
890 loadingForm = jQuery( '.frm_loading_form' );
891 loadingForm.removeClass( 'frm_loading_form' );
892 loadingForm.removeClass( 'frm_loading_prev' );
893
894 loadingForm.trigger( 'frmEndFormLoading' );
895
896 if ( enable === 'enable' ) {
897 enableSubmitButton( loadingForm );
898 enableSaveDraft( loadingForm );
899 }
900 }
901
902 function showFileLoading( object ) {
903 var fileval,
904 loading = document.getElementById( 'frm_loading' );
905 if ( loading !== null ) {
906 fileval = jQuery( object ).find( 'input[type=file]' ).val();
907 if ( typeof fileval !== 'undefined' && fileval !== '' ) {
908 setTimeout( function() {
909 jQuery( loading ).fadeIn( 'slow' );
910 }, 2000 );
911 }
912 }
913 }
914
915 function clearDefault() {
916 /*jshint validthis:true */
917 toggleDefault( jQuery( this ), 'clear' );
918 }
919
920 function replaceDefault() {
921 /*jshint validthis:true */
922 toggleDefault( jQuery( this ), 'replace' );
923 }
924
925 function toggleDefault( $thisField, e ) {
926 // TODO: Fix this for a default value that is a number or array
927 var thisVal,
928 v = $thisField.data( 'frmval' ).replace( /(\n|\r\n)/g, '\r' );
929 if ( v === '' || typeof v === 'undefined' ) {
930 return false;
931 }
932 thisVal = $thisField.val().replace( /(\n|\r\n)/g, '\r' );
933
934 if ( 'replace' === e ) {
935 if ( thisVal === '' ) {
936 $thisField.addClass( 'frm_default' ).val( v );
937 }
938 } else if ( thisVal == v ) {
939 $thisField.removeClass( 'frm_default' ).val( '' );
940 }
941 }
942
943 function resendEmail() {
944 /*jshint validthis:true */
945 var $link = jQuery( this ),
946 entryId = this.getAttribute( 'data-eid' ),
947 formId = this.getAttribute( 'data-fid' ),
948 label = $link.find( '.frm_link_label' );
949 if ( label.length < 1 ) {
950 label = $link;
951 }
952 label.append( '<span class="frm-wait"></span>' );
953
954 jQuery.ajax({
955 type: 'POST',
956 url: frm_js.ajax_url,
957 data: {
958 action: 'frm_entries_send_email',
959 entry_id: entryId,
960 form_id: formId,
961 nonce: frm_js.nonce
962 },
963 success: function( msg ) {
964 var admin = document.getElementById( 'wpbody' );
965 if ( admin === null ) {
966 label.html( msg );
967 } else {
968 label.html( '' );
969 $link.after( msg );
970 }
971 }
972 });
973 return false;
974 }
975
976 /**********************************************
977 * General Helpers
978 *********************************************/
979
980 function confirmClick() {
981 /*jshint validthis:true */
982 var message = jQuery( this ).data( 'frmconfirm' );
983 return confirm( message );
984 }
985
986 function toggleDiv() {
987 /*jshint validthis:true */
988 var div = jQuery( this ).data( 'frmtoggle' );
989 if ( jQuery( div ).is( ':visible' ) ) {
990 jQuery( div ).slideUp( 'fast' );
991 } else {
992 jQuery( div ).slideDown( 'fast' );
993 }
994 return false;
995 }
996
997 /**********************************************
998 * Fallback functions
999 *********************************************/
1000
1001 function addIndexOfFallbackForIE8() {
1002 var len, from;
1003
1004 if ( ! Array.prototype.indexOf ) {
1005 Array.prototype.indexOf = function( elt /*, from*/ ) {
1006 len = this.length >>> 0;
1007
1008 from = Number( arguments[1]) || 0;
1009 from = ( from < 0 ) ? Math.ceil( from ) : Math.floor( from );
1010 if ( from < 0 ) {
1011 from += len;
1012 }
1013
1014 for ( ; from < len; from++ ) {
1015 if ( from in this && this[from] === elt ) {
1016 return from;
1017 }
1018 }
1019 return -1;
1020 };
1021 }
1022 }
1023
1024 function addTrimFallbackForIE8() {
1025 if ( typeof String.prototype.trim !== 'function' ) {
1026 String.prototype.trim = function() {
1027 return this.replace( /^\s+|\s+$/g, '' );
1028 };
1029 }
1030 }
1031
1032 function addFilterFallbackForIE8() {
1033 var t, len, res, thisp, i, val;
1034
1035 if ( ! Array.prototype.filter ) {
1036
1037 Array.prototype.filter = function( fun /*, thisp */ ) {
1038
1039 if ( this === void 0 || this === null ) {
1040 throw new TypeError();
1041 }
1042
1043 t = Object( this );
1044 len = t.length >>> 0;
1045 if ( typeof fun !== 'function' ) {
1046 throw new TypeError();
1047 }
1048
1049 res = [];
1050 thisp = arguments[1];
1051 for ( i = 0; i < len; i++ ) {
1052 if ( i in t ) {
1053 val = t[i]; // in case fun mutates this
1054 if ( fun.call( thisp, val, i, t ) ) {
1055 res.push( val );
1056 }
1057 }
1058 }
1059
1060 return res;
1061 };
1062 }
1063 }
1064
1065 function addKeysFallbackForIE8() {
1066 var keys, i;
1067
1068 if ( ! Object.keys ) {
1069 Object.keys = function( obj ) {
1070 keys = [];
1071
1072 for ( i in obj ) {
1073 if ( obj.hasOwnProperty( i ) ) {
1074 keys.push( i );
1075 }
1076 }
1077
1078 return keys;
1079 };
1080 }
1081 }
1082
1083 /**
1084 * Check for -webkit-box-shadow css value for input:-webkit-autofill selector.
1085 * If this is a match, the User is autofilling the input on a Webkit browser.
1086 * We want to delete the Honeypot field, otherwise it will get triggered as spam on autocomplete.
1087 */
1088 function onHoneypotFieldChange() {
1089 var css = jQuery( this ).css( 'box-shadow' );
1090 if ( css.match( /inset/ ) ) {
1091 this.parentNode.removeChild( this );
1092 }
1093 }
1094
1095 /**
1096 * Focus on the first sub field when clicking to the primary label of combo field.
1097 *
1098 * @since 4.10.02
1099 */
1100 function changeFocusWhenClickComboFieldLabel() {
1101 let label;
1102
1103 const comboInputsContainer = document.querySelectorAll( '.frm_combo_inputs_container' );
1104 comboInputsContainer.forEach( function( inputsContainer ) {
1105 if ( ! inputsContainer.closest( '.frm_form_field' ) ) {
1106 return;
1107 }
1108
1109 label = inputsContainer.closest( '.frm_form_field' ).querySelector( '.frm_primary_label' );
1110 if ( ! label ) {
1111 return;
1112 }
1113
1114 label.addEventListener( 'click', function( e ) {
1115 inputsContainer.querySelector( '.frm_form_field:first-child input, .frm_form_field:first-child select, .frm_form_field:first-child textarea' ).focus();
1116 });
1117 });
1118 }
1119
1120 function checkForErrorsAndMaybeSetFocus() {
1121 var errors, element, timeoutCallback;
1122
1123 if ( ! frm_js.focus_first_error ) {
1124 return;
1125 }
1126
1127 errors = document.querySelectorAll( '.frm_form_field .frm_error' );
1128 if ( ! errors.length ) {
1129 return;
1130 }
1131
1132 element = errors[0];
1133 do {
1134 element = element.previousSibling;
1135 if ( -1 !== [ 'input', 'select', 'textarea' ].indexOf( element.nodeName.toLowerCase() ) ) {
1136 element.focus();
1137 break;
1138 }
1139
1140 if ( 'undefined' !== typeof element.classList ) {
1141 if ( element.classList.contains( 'html-active' ) ) {
1142 timeoutCallback = function() {
1143 var textarea = element.querySelector( 'textarea' );
1144 if ( null !== textarea ) {
1145 textarea.focus();
1146 }
1147 };
1148 } else if ( element.classList.contains( 'tmce-active' ) ) {
1149 timeoutCallback = function() {
1150 tinyMCE.activeEditor.focus();
1151 };
1152 }
1153
1154 if ( 'function' === typeof timeoutCallback ) {
1155 setTimeout( timeoutCallback, 0 );
1156 break;
1157 }
1158 }
1159 } while ( element.previousSibling );
1160 }
1161
1162 /**
1163 * Checks if is on IE browser.
1164 *
1165 * @since 5.4
1166 *
1167 * @return {Boolean}
1168 */
1169 function isIE() {
1170 return navigator.userAgent.indexOf( 'MSIE' ) > -1 || navigator.userAgent.indexOf( 'Trident' ) > -1;
1171 }
1172
1173 /**
1174 * Does the same as jQuery( document ).on( 'event', 'selector', handler ).
1175 *
1176 * @since 5.4
1177 *
1178 * @param {String} event Event name.
1179 * @param {String} selector Selector.
1180 * @param {Function} handler Handler.
1181 * @param {Boolean|Object} options Options to be added to `addEventListener()` method. Default is `false`.
1182 */
1183 function documentOn( event, selector, handler, options ) {
1184 if ( 'undefined' === typeof options ) {
1185 options = false;
1186 }
1187
1188 document.addEventListener( event, function( e ) {
1189 var target;
1190
1191 // loop parent nodes from the target to the delegation node.
1192 for ( target = e.target; target && target != this; target = target.parentNode ) {
1193 if ( target.matches( selector ) ) {
1194 handler.call( target, e );
1195 break;
1196 }
1197 }
1198 }, options );
1199 }
1200
1201 function initFloatingLabels() {
1202 var checkFloatLabel, checkDropdownLabel, checkPlaceholderIE, runOnLoad, selector, floatClass;
1203
1204 selector = '.frm-show-form .frm_inside_container input, .frm-show-form .frm_inside_container select, .frm-show-form .frm_inside_container textarea';
1205 floatClass = 'frm_label_float_top';
1206
1207 checkFloatLabel = function( input ) {
1208 var container, shouldFloatTop, firstOpt;
1209
1210 container = input.closest( '.frm_inside_container' );
1211 shouldFloatTop = input.value || document.activeElement === input;
1212
1213 container.classList.toggle( floatClass, shouldFloatTop );
1214
1215 if ( 'SELECT' === input.tagName ) {
1216 firstOpt = input.querySelector( 'option:first-child' );
1217
1218 if ( shouldFloatTop ) {
1219 if ( firstOpt.getAttribute( 'data-label' ) ) {
1220 firstOpt.text = firstOpt.getAttribute( 'data-label' );
1221 firstOpt.removeAttribute( 'data-label' );
1222 }
1223 } else {
1224 if ( firstOpt.text ) {
1225 firstOpt.setAttribute( 'data-label', firstOpt.text );
1226 firstOpt.text = '';
1227 }
1228 }
1229 } else if ( isIE() ) {
1230 checkPlaceholderIE( input );
1231 }
1232 };
1233
1234 checkDropdownLabel = function() {
1235 document.querySelectorAll( '.frm-show-form .frm_inside_container:not(.' + floatClass + ') select' ).forEach( function( input ) {
1236 var firstOpt = input.querySelector( 'option:first-child' );
1237
1238 if ( firstOpt.text ) {
1239 firstOpt.setAttribute( 'data-label', firstOpt.text );
1240 firstOpt.text = '';
1241 }
1242 });
1243 };
1244
1245 checkPlaceholderIE = function( input ) {
1246 if ( input.value ) {
1247 // Don't need to handle this case because placeholder isn't shown.
1248 return;
1249 }
1250
1251 if ( document.activeElement === input ) {
1252 if ( input.getAttribute( 'data-placeholder' ) ) {
1253 input.placeholder = input.getAttribute( 'data-placeholder' );
1254 input.removeAttribute( 'data-placeholder' );
1255 }
1256 } else {
1257 if ( input.placeholder ) {
1258 input.setAttribute( 'data-placeholder', input.placeholder );
1259 input.placeholder = '';
1260 }
1261 }
1262 };
1263
1264 [ 'focus', 'blur', 'change' ].forEach( function( eventName ) {
1265 documentOn(
1266 eventName,
1267 selector,
1268 function( event ) {
1269 checkFloatLabel( event.target );
1270 },
1271 true
1272 );
1273 });
1274
1275 jQuery( document ).on( 'change', selector, function( event ) {
1276 checkFloatLabel( event.target );
1277 });
1278
1279 runOnLoad = function( firstLoad ) {
1280 if ( firstLoad && document.activeElement && -1 !== [ 'INPUT', 'SELECT', 'TEXTAREA' ].indexOf( document.activeElement.tagName ) ) {
1281 checkFloatLabel( document.activeElement );
1282 } else if ( firstLoad ) {
1283 document.querySelectorAll( '.frm_inside_container' ).forEach(
1284 function( container ) {
1285 var input = container.querySelector( 'input, select, textarea' );
1286 if ( input && '' !== input.value ) {
1287 checkFloatLabel( input );
1288 }
1289 }
1290 );
1291 }
1292
1293 checkDropdownLabel();
1294
1295 if ( isIE() ) {
1296 document.querySelectorAll( selector ).forEach( function( input ) {
1297 checkPlaceholderIE( input );
1298 });
1299 }
1300 };
1301
1302 runOnLoad( true );
1303
1304 jQuery( document ).on( 'frmPageChanged', function( event ) {
1305 runOnLoad();
1306 });
1307
1308 document.addEventListener( 'frm_after_start_over', function( event ) {
1309 runOnLoad();
1310 });
1311 }
1312
1313 return {
1314 init: function() {
1315 maybeAddPolyfills();
1316
1317 jQuery( document ).off( 'submit.formidable', '.frm-show-form' );
1318 jQuery( document ).on( 'submit.formidable', '.frm-show-form', frmFrontForm.submitForm );
1319
1320 jQuery( '.frm-show-form input[onblur], .frm-show-form textarea[onblur]' ).each( function() {
1321 if ( jQuery( this ).val() === '' ) {
1322 jQuery( this ).trigger( 'blur' );
1323 }
1324 });
1325
1326 jQuery( document ).on( 'focus', '.frm_toggle_default', clearDefault );
1327 jQuery( document ).on( 'blur', '.frm_toggle_default', replaceDefault );
1328 jQuery( '.frm_toggle_default' ).trigger( 'blur' );
1329
1330 jQuery( document.getElementById( 'frm_resend_email' ) ).on( 'click', resendEmail );
1331
1332 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 );
1333
1334 jQuery( document ).on( 'change', '[id^=frm_email_]', onHoneypotFieldChange );
1335
1336 jQuery( document ).on( 'click', 'a[data-frmconfirm]', confirmClick );
1337 jQuery( 'a[data-frmtoggle]' ).on( 'click', toggleDiv );
1338
1339 checkForErrorsAndMaybeSetFocus();
1340
1341 // Focus on the first sub field when clicking to the primary label of combo field.
1342 changeFocusWhenClickComboFieldLabel();
1343
1344 // Add fallbacks for the beloved IE8
1345 addIndexOfFallbackForIE8();
1346 addTrimFallbackForIE8();
1347 addFilterFallbackForIE8();
1348 addKeysFallbackForIE8();
1349
1350 initFloatingLabels();
1351 },
1352
1353 getFieldId: function( field, fullID ) {
1354 return getFieldId( field, fullID );
1355 },
1356
1357 renderRecaptcha: function( captcha ) {
1358 var formID, recaptchaID,
1359 size = captcha.getAttribute( 'data-size' ),
1360 rendered = captcha.getAttribute( 'data-rid' ) !== null,
1361 params = {
1362 'sitekey': captcha.getAttribute( 'data-sitekey' ),
1363 'size': size,
1364 'theme': captcha.getAttribute( 'data-theme' )
1365 };
1366
1367 if ( rendered ) {
1368 return;
1369 }
1370
1371 if ( size === 'invisible' ) {
1372 formID = jQuery( captcha ).closest( 'form' ).find( 'input[name="form_id"]' ).val();
1373 jQuery( captcha ).closest( '.frm_form_field .frm_primary_label' ).hide();
1374 params.callback = function( token ) {
1375 frmFrontForm.afterRecaptcha( token, formID );
1376 };
1377 }
1378
1379 recaptchaID = grecaptcha.render( captcha.id, params );
1380
1381 captcha.setAttribute( 'data-rid', recaptchaID );
1382 },
1383
1384 afterSingleRecaptcha: function() {
1385 var object = jQuery( '.frm-show-form .g-recaptcha' ).closest( 'form' )[0];
1386 frmFrontForm.submitFormNow( object );
1387 },
1388
1389 afterRecaptcha: function( token, formID ) {
1390 var object = jQuery( '#frm_form_' + formID + '_container form' )[0];
1391 frmFrontForm.submitFormNow( object );
1392 },
1393
1394 submitForm: function( e ) {
1395 frmFrontForm.submitFormManual( e, this );
1396 },
1397
1398 submitFormManual: function( e, object ) {
1399 var isPro, errors,
1400 invisibleRecaptcha = hasInvisibleRecaptcha( object ),
1401 classList = object.className.trim().split( /\s+/gi );
1402
1403 if ( classList && invisibleRecaptcha.length < 1 ) {
1404 isPro = classList.indexOf( 'frm_pro_form' ) > -1;
1405 if ( ! isPro ) {
1406 return;
1407 }
1408 }
1409
1410 if ( jQuery( 'body' ).hasClass( 'wp-admin' ) && jQuery( object ).closest( '.frmapi-form' ).length < 1 ) {
1411 return;
1412 }
1413
1414 e.preventDefault();
1415
1416 if ( typeof frmProForm !== 'undefined' && typeof frmProForm.submitAllowed === 'function' ) {
1417 if ( ! frmProForm.submitAllowed( object ) ) {
1418 return;
1419 }
1420 }
1421
1422 if ( invisibleRecaptcha.length ) {
1423 showLoadingIndicator( jQuery( object ) );
1424 executeInvisibleRecaptcha( invisibleRecaptcha );
1425 } else {
1426
1427 errors = frmFrontForm.validateFormSubmit( object );
1428
1429 if ( Object.keys( errors ).length === 0 ) {
1430 showSubmitLoading( jQuery( object ) );
1431
1432 frmFrontForm.submitFormNow( object, classList );
1433 }
1434 }
1435 },
1436
1437 submitFormNow: function( object ) {
1438 var hasFileFields, antispamInput,
1439 classList = object.className.trim().split( /\s+/gi );
1440
1441 if ( object.hasAttribute( 'data-token' ) && null === object.querySelector( '[name="antispam_token"]' ) ) {
1442 // include the antispam token on form submit.
1443 antispamInput = document.createElement( 'input' );
1444 antispamInput.type = 'hidden';
1445 antispamInput.name = 'antispam_token';
1446 antispamInput.value = object.getAttribute( 'data-token' );
1447 object.appendChild( antispamInput );
1448 }
1449
1450 if ( classList.indexOf( 'frm_ajax_submit' ) > -1 ) {
1451 hasFileFields = jQuery( object ).find( 'input[type="file"]' ).filter( function() {
1452 return !! this.value;
1453 }).length;
1454 if ( hasFileFields < 1 ) {
1455 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1456 frmFrontForm.checkFormErrors( object, action );
1457 } else {
1458 object.submit();
1459 }
1460 } else {
1461 object.submit();
1462 }
1463 },
1464
1465 validateFormSubmit: function( object ) {
1466 if ( typeof tinyMCE !== 'undefined' && jQuery( object ).find( '.wp-editor-wrap' ).length ) {
1467 tinyMCE.triggerSave();
1468 }
1469
1470 jsErrors = [];
1471
1472 if ( shouldJSValidate( object ) ) {
1473 frmFrontForm.getAjaxFormErrors( object );
1474
1475 if ( Object.keys( jsErrors ).length ) {
1476 frmFrontForm.addAjaxFormErrors( object );
1477 }
1478 }
1479
1480 return jsErrors;
1481 },
1482
1483 getAjaxFormErrors: function( object ) {
1484 var customErrors, key;
1485
1486 jsErrors = validateForm( object );
1487 if ( typeof frmThemeOverride_jsErrors === 'function' ) { // eslint-disable-line camelcase
1488 action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1489 customErrors = frmThemeOverride_jsErrors( action, object );
1490 if ( Object.keys( customErrors ).length ) {
1491 for ( key in customErrors ) {
1492 jsErrors[ key ] = customErrors[ key ];
1493 }
1494 }
1495 }
1496
1497 return jsErrors;
1498 },
1499
1500 addAjaxFormErrors: function( object ) {
1501 var key, $fieldCont;
1502 removeAllErrors();
1503
1504 for ( key in jsErrors ) {
1505 $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
1506
1507 if ( $fieldCont.length ) {
1508 addFieldError( $fieldCont, key, jsErrors );
1509 } else {
1510 // we are unable to show the error, so remove it
1511 delete jsErrors[ key ];
1512 }
1513 }
1514
1515 scrollToFirstField( object );
1516 checkForErrorsAndMaybeSetFocus();
1517 },
1518
1519 checkFormErrors: function( object, action ) {
1520 getFormErrors( object, action );
1521 },
1522
1523 checkRequiredField: function( field, errors ) {
1524 return checkRequiredField( field, errors );
1525 },
1526
1527 showSubmitLoading: function( $object ) {
1528 showSubmitLoading( $object );
1529 },
1530
1531 removeSubmitLoading: function( $object, enable, processesRunning ) {
1532 removeSubmitLoading( $object, enable, processesRunning );
1533 },
1534
1535 scrollToID: function( id ) {
1536 var object = jQuery( document.getElementById( id ) );
1537 frmFrontForm.scrollMsg( object, false );
1538 },
1539
1540 scrollMsg: function( id, object, animate ) {
1541 var newPos, m, b, screenTop, screenBottom,
1542 scrollObj = '';
1543 if ( typeof object === 'undefined' ) {
1544 scrollObj = jQuery( document.getElementById( 'frm_form_' + id + '_container' ) );
1545 if ( scrollObj.length < 1 ) {
1546 return;
1547 }
1548 } else if ( typeof id === 'string' ) {
1549 scrollObj = jQuery( object ).find( '#frm_field_' + id + '_container' );
1550 } else {
1551 scrollObj = id;
1552 }
1553
1554 jQuery( scrollObj ).trigger( 'focus' );
1555 newPos = scrollObj.offset().top;
1556 if ( ! newPos || frm_js.offset === '-1' ) {
1557 return;
1558 }
1559 newPos = newPos - frm_js.offset;
1560
1561 m = jQuery( 'html' ).css( 'margin-top' );
1562 b = jQuery( 'body' ).css( 'margin-top' );
1563 if ( m || b ) {
1564 newPos = newPos - parseInt( m ) - parseInt( b );
1565 }
1566
1567 if ( newPos && window.innerHeight ) {
1568 screenTop = document.documentElement.scrollTop || document.body.scrollTop;
1569 screenBottom = screenTop + window.innerHeight;
1570
1571 if ( newPos > screenBottom || newPos < screenTop ) {
1572 // Not in view
1573 if ( typeof animate === 'undefined' ) {
1574 jQuery( window ).scrollTop( newPos );
1575 } else {
1576 jQuery( 'html,body' ).animate({ scrollTop: newPos }, 500 );
1577 }
1578 return false;
1579 }
1580 }
1581 },
1582
1583 fieldValueChanged: function( e ) {
1584 /*jshint validthis:true */
1585
1586 var fieldId = frmFrontForm.getFieldId( this, false );
1587 if ( ! fieldId || typeof fieldId === 'undefined' ) {
1588 return;
1589 }
1590
1591 if ( e.frmTriggered && e.frmTriggered == fieldId ) {
1592 return;
1593 }
1594
1595 jQuery( document ).trigger( 'frmFieldChanged', [ this, fieldId, e ]);
1596
1597 if ( e.selfTriggered !== true ) {
1598 maybeValidateChange( this );
1599 }
1600 },
1601
1602 savingDraft: function( object ) {
1603 console.warn( 'DEPRECATED: function frmFrontForm.savingDraft in v3.0 use frmProForm.savingDraft' );
1604 if ( typeof frmProForm !== 'undefined' ) {
1605 return frmProForm.savingDraft( object );
1606 }
1607 },
1608
1609 goingToPreviousPage: function( object ) {
1610 console.warn( 'DEPRECATED: function frmFrontForm.goingToPreviousPage in v3.0 use frmProForm.goingToPreviousPage' );
1611 if ( typeof frmProForm !== 'undefined' ) {
1612 return frmProForm.goingToPreviousPage( object );
1613 }
1614 },
1615
1616 hideOrShowFields: function() {
1617 console.warn( 'DEPRECATED: function frmFrontForm.hideOrShowFields in v3.0 use frmProForm.hideOrShowFields' );
1618 if ( typeof frmProForm !== 'undefined' ) {
1619 frmProForm.hideOrShowFields();
1620 }
1621 },
1622
1623 hidePreviouslyHiddenFields: function() {
1624 console.warn( 'DEPRECATED: function frmFrontForm.hidePreviouslyHiddenFields in v3.0 use frmProForm.hidePreviouslyHiddenFields' );
1625 if ( typeof frmProForm !== 'undefined' ) {
1626 frmProForm.hidePreviouslyHiddenFields();
1627 }
1628 },
1629
1630 checkDependentDynamicFields: function( ids ) {
1631 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentDynamicFields in v3.0 use frmProForm.checkDependentDynamicFields' );
1632 if ( typeof frmProForm !== 'undefined' ) {
1633 frmProForm.checkDependentDynamicFields( ids );
1634 }
1635 },
1636
1637 checkDependentLookupFields: function( ids ) {
1638 console.warn( 'DEPRECATED: function frmFrontForm.checkDependentLookupFields in v3.0 use frmProForm.checkDependentLookupFields' );
1639 if ( typeof frmProForm !== 'undefined' ) {
1640 frmProForm.checkDependentLookupFields( ids );
1641 }
1642 },
1643
1644 loadGoogle: function() {
1645 console.warn( 'DEPRECATED: function frmFrontForm.loadGoogle in v3.0 use frmProForm.loadGoogle' );
1646 frmProForm.loadGoogle();
1647 },
1648
1649 escapeHtml: function( text ) {
1650 return text
1651 .replace( /&/g, '&amp;' )
1652 .replace( /</g, '&lt;' )
1653 .replace( />/g, '&gt;' )
1654 .replace( /"/g, '&quot;' )
1655 .replace( /'/g, '&#039;' );
1656 },
1657
1658 invisible: function( classes ) {
1659 jQuery( classes ).css( 'visibility', 'hidden' );
1660 },
1661
1662 visible: function( classes ) {
1663 jQuery( classes ).css( 'visibility', 'visible' );
1664 }
1665 };
1666 }
1667 frmFrontForm = frmFrontFormJS();
1668
1669 jQuery( document ).ready( function() {
1670 frmFrontForm.init();
1671 });
1672
1673 function frmRecaptcha() {
1674 var c, cl,
1675 captchas = jQuery( '.frm-g-recaptcha' );
1676 for ( c = 0, cl = captchas.length; c < cl; c++ ) {
1677 frmFrontForm.renderRecaptcha( captchas[c]);
1678 }
1679 }
1680
1681 function frmAfterRecaptcha( token ) {
1682 frmFrontForm.afterSingleRecaptcha( token );
1683 }
1684
1685 function frmUpdateField( entryId, fieldId, value, message, num ) {
1686 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).html( '<span class="frm-loading-img"></span>' );
1687 jQuery.ajax({
1688 type: 'POST',
1689 url: frm_js.ajax_url,
1690 data: {
1691 action: 'frm_entries_update_field_ajax',
1692 entry_id: entryId,
1693 field_id: fieldId,
1694 value: value,
1695 nonce: frm_js.nonce
1696 },
1697 success: function() {
1698 if ( message.replace( /^\s+|\s+$/g, '' ) === '' ) {
1699 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).fadeOut( 'slow' );
1700 } else {
1701 jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).replaceWith( message );
1702 }
1703 }
1704 });
1705 }
1706
1707 function frmDeleteEntry( entryId, prefix ) {
1708 console.warn( 'DEPRECATED: function frmDeleteEntry in v2.0.13 use frmFrontForm.deleteEntry' );
1709 jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( '<span class="frm-loading-img" id="frm_delete_' + entryId + '"></span>' );
1710 jQuery.ajax({
1711 type: 'POST',
1712 url: frm_js.ajax_url,
1713 data: {
1714 action: 'frm_entries_destroy',
1715 entry: entryId,
1716 nonce: frm_js.nonce
1717 },
1718 success: function( html ) {
1719 if ( html.replace( /^\s+|\s+$/g, '' ) === 'success' ) {
1720 jQuery( document.getElementById( prefix + entryId ) ).fadeOut( 'slow' );
1721 } else {
1722 jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( html );
1723 }
1724 }
1725 });
1726 }
1727
1728 function frmOnSubmit( e ) {
1729 console.warn( 'DEPRECATED: function frmOnSubmit in v2.0 use frmFrontForm.submitForm' );
1730 frmFrontForm.submitForm( e, this );
1731 }
1732
1733 function frm_resend_email( entryId, formId ) { // eslint-disable-line camelcase
1734 var $link = jQuery( document.getElementById( 'frm_resend_email' ) );
1735 console.warn( 'DEPRECATED: function frm_resend_email in v2.0' );
1736 $link.append( '<span class="spinner" style="display:inline"></span>' );
1737 jQuery.ajax({
1738 type: 'POST',
1739 url: frm_js.ajax_url,
1740 data: {
1741 action: 'frm_entries_send_email',
1742 entry_id: entryId,
1743 form_id: formId,
1744 nonce: frm_js.nonce
1745 },
1746 success: function( msg ) {
1747 $link.replaceWith( msg );
1748 }
1749 });
1750 }
1751