PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.0.07
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.0.07
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
← All changes | js/formidable.js +805 -1440 6.295.0.07 View file →
@@ -1,58 +1,53 @@
1 -/* exported frmRecaptcha, frmAfterRecaptcha */
1 +/* exported frmRecaptcha, frmAfterRecaptcha, frmUpdateField, frmDeleteEntry, frmOnSubmit, frm_resend_email */
2 2
3 +var frmFrontForm;
4 +
3 5 function frmFrontFormJS() {
4 6 'use strict';
5 7
6 - let jsErrors = [];
8 + /*global jQuery:false, frm_js, grecaptcha, frmProForm, tinyMCE */
9 + /*global frmThemeOverride_jsErrors, frmThemeOverride_frmPlaceError, frmThemeOverride_frmAfterSubmit */
7 10
8 - /**
9 - * Triggers custom JS event.
10 - *
11 - * @since 5.5.3
12 - *
13 - * @param {HTMLElement} el The HTML element.
14 - * @param {string} eventName Event name.
15 - * @param {*} data The passed data.
16 - */
17 - function triggerCustomEvent( el, eventName, data ) {
18 - if ( typeof window.CustomEvent !== 'function' ) {
19 - return;
20 - }
11 + var action = '';
12 + var jsErrors = [];
21 13
22 - const event = new CustomEvent( eventName );
23 - event.frmData = data;
14 + function maybeShowLabel() {
15 + /*jshint validthis:true */
16 + var $field = jQuery( this ),
17 + $label = $field.closest( '.frm_inside_container' ).find( '.frm_primary_label' ),
18 + val = $field.val();
24 19
25 - el.dispatchEvent( event );
20 + if ( val !== null && val.length > 0 ) {
21 + $label.addClass( 'frm_visible' );
22 + } else {
23 + $label.removeClass( 'frm_visible' );
24 + }
26 25 }
27 26
28 - /**
29 - * Get the ID of the field that changed.
30 - *
31 - * @param {HTMLElement|jQuery} field
32 - * @param {boolean} fullID
33 - * @return {string|number} Field ID.
34 - */
27 + /* Get the ID of the field that changed*/
35 28 function getFieldId( field, fullID ) {
36 - let nameParts;
37 - let fieldId;
38 - let isRepeating = false;
39 - let fieldName = '';
40 -
29 + var nameParts, fieldId,
30 + isRepeating = false,
31 + fieldName = '';
41 32 if ( field instanceof jQuery ) {
42 - field = field.get( 0 );
33 + fieldName = field.attr( 'name' );
34 + } else {
35 + fieldName = field.name;
43 36 }
44 37
45 - fieldName = field.name;
46 -
47 - if ( fieldName === undefined ) {
38 + if ( typeof fieldName === 'undefined' ) {
48 39 fieldName = '';
49 40 }
50 41
51 42 if ( fieldName === '' ) {
52 - fieldName = field.getAttribute( 'data-name' );
43 + if ( field instanceof jQuery ) {
44 + fieldName = field.data( 'name' );
45 + } else {
46 + fieldName = field.getAttribute( 'data-name' );
47 + }
53 48
54 - if ( fieldName === undefined ) {
49 + if ( typeof fieldName === 'undefined' ) {
55 50 fieldName = '';
56 51 }
57 52
58 53 if ( fieldName !== '' && fieldName ) {
@@ -67,24 +62,25 @@
67 62 return 0;
68 63 }
69 64 nameParts = nameParts.filter( function( n ) {
70 65 return n !== '';
71 - } );
66 + });
72 67
73 - fieldId = nameParts[ 0 ];
68 + fieldId = nameParts[0];
74 69
75 70 if ( nameParts.length === 1 ) {
76 71 return fieldId;
77 72 }
78 73
79 - if ( nameParts[ 1 ] === '[form' || nameParts[ 1 ] === '[row_ids' ) {
74 + if ( nameParts[1] === '[form' || nameParts[1] === '[row_ids' ) {
80 75 return 0;
81 76 }
82 77
83 78 // Check if 'this' is in a repeating section
84 - if ( document.querySelector( `input[name="item_meta[${ fieldId }][form]"]` ) ) {
79 + if ( jQuery( 'input[name="item_meta[' + fieldId + '][form]"]' ).length ) {
80 +
85 81 // this is a repeatable section with name: item_meta[repeating-section-id][row-id][field-id]
86 - fieldId = nameParts[ 2 ].replace( '[', '' );
82 + fieldId = nameParts[2].replace( '[', '' );
87 83 isRepeating = true;
88 84 }
89 85
90 86 // Check if 'this' is an other text field and get field ID for it
@@ -90,21 +86,21 @@
90 86 // Check if 'this' is an other text field and get field ID for it
91 87 if ( 'other' === fieldId ) {
92 88 if ( isRepeating ) {
93 89 // name for other fields: item_meta[370][0][other][414]
94 - fieldId = nameParts[ 3 ].replace( '[', '' );
90 + fieldId = nameParts[3].replace( '[', '' );
95 91 } else {
96 92 // Other field name: item_meta[other][370]
97 - fieldId = nameParts[ 1 ].replace( '[', '' );
93 + fieldId = nameParts[1].replace( '[', '' );
98 94 }
99 95 }
100 96
101 97 if ( fullID === true ) {
102 98 // For use in the container div id
103 - if ( fieldId === nameParts[ 0 ] ) {
104 - fieldId = `${ fieldId }-${ nameParts[ 1 ].replace( '[', '' ) }`;
99 + if ( fieldId === nameParts[0]) {
100 + fieldId = fieldId + '-' + nameParts[1].replace( '[', '' );
105 101 } else {
106 - fieldId = `${ fieldId }-${ nameParts[ 0 ] }-${ nameParts[ 1 ].replace( '[', '' ) }`;
102 + fieldId = fieldId + '-' + nameParts[0] + '-' + nameParts[1].replace( '[', '' );
107 103 }
108 104 }
109 105
110 106 return fieldId;
@@ -114,18 +110,12 @@
114 110 * Disable the submit button for a given jQuery form object
115 111 *
116 112 * @since 2.03.02
117 113 *
118 - * @param {Object} $form
119 - */
114 + * @param {object} $form
115 + */
120 116 function disableSubmitButton( $form ) {
121 - const form = $form instanceof jQuery ? $form.get( 0 ) : $form;
122 - if ( ! form ) {
123 - return;
124 - }
125 - form.querySelectorAll( 'input[type="submit"], input[type="button"], button[type="submit"], button.frm_save_draft' ).forEach(
126 - button => button.disabled = true
127 - );
117 + $form.find( 'input[type="submit"], input[type="button"], button[type="submit"]' ).attr( 'disabled', 'disabled' );
128 118 }
129 119
130 120 /**
131 121 * Enable the submit button for a given jQuery form object
@@ -131,16 +121,12 @@
131 121 * Enable the submit button for a given jQuery form object
132 122 *
133 123 * @since 2.03.02
134 124 *
135 - * @param {HTMLElement} form
136 - *
137 - * @return {void}
138 - */
139 - function enableSubmitButton( form ) {
140 - form.querySelectorAll( 'input[type="submit"], input[type="button"], button[type="submit"]' ).forEach(
141 - button => button.disabled = false
142 - );
125 + * @param {object} $form
126 + */
127 + function enableSubmitButton( $form ) {
128 + $form.find( 'input[type="submit"], input[type="button"], button[type="submit"]' ).prop( 'disabled', false );
143 129 }
144 130
145 131 /**
146 132 * Disable the save draft link for a given jQuery form object
@@ -146,279 +132,148 @@
146 132 * Disable the save draft link for a given jQuery form object
147 133 *
148 134 * @since 4.04.03
149 135 *
150 - * @param {Object} $form
136 + * @param {object} $form
151 137 */
152 138 function disableSaveDraft( $form ) {
153 - const form = $form instanceof jQuery ? $form.get( 0 ) : $form;
154 - if ( ! form ) {
155 - return;
156 - }
157 - form.querySelectorAll( 'a.frm_save_draft' ).forEach(
158 - link => link.style.pointerEvents = 'none'
159 - );
139 + $form.find( 'a.frm_save_draft' ).css( 'pointer-events', 'none' );
160 140 }
161 141
162 142 /**
163 - * Enable the save draft link for a given form object.
143 + * Enable the save draft link for a given jQuery form object
164 144 *
165 145 * @since 4.04.03
166 146 *
167 - * @param {jQuery|HTMLElement} $form
147 + * @param {object} $form
168 148 */
169 149 function enableSaveDraft( $form ) {
170 - const form = $form instanceof jQuery ? $form.get( 0 ) : $form;
171 - if ( ! form ) {
172 - return;
173 - }
174 - form.querySelectorAll( '.frm_save_draft' ).forEach( saveDraftButton => {
175 - saveDraftButton.disabled = false;
176 - saveDraftButton.style.pointerEvents = '';
177 - } );
150 + $form.find( 'a.frm_save_draft' ).css( 'pointer-events', '' );
178 151 }
179 152
180 - /**
181 - * Validate form with JS.
182 - *
183 - * @param {HTMLElement|jQuery} object
184 - * @return {Array} Errors.
185 - */
186 153 function validateForm( object ) {
187 - let errors = [];
154 + var r, rl, n, nl, fields, field, value, requiredFields,
155 + errors = [];
188 156
189 - const vanillaJsObject = 'function' === typeof object.get ? object.get( 0 ) : object;
190 -
191 - // Required field validation.
192 - vanillaJsObject?.querySelectorAll( '.frm_required_field' ).forEach(
193 - requiredField => {
194 - const isVisible = requiredField.offsetParent !== null;
195 - if ( ! isVisible ) {
196 - return;
197 - }
198 -
199 - requiredField.querySelectorAll( 'input, select, textarea' ).forEach(
200 - requiredInput => {
201 - if ( hasClass( requiredInput, 'frm_optional' ) || hasClass( requiredInput, 'ed_button' ) ) {
202 - // skip rich text field buttons.
203 - return;
204 - }
205 -
206 - errors = checkRequiredField( requiredInput, errors );
207 - }
208 - );
157 + // Make sure required text field is filled in
158 + requiredFields = jQuery( object ).find(
159 + '.frm_required_field:visible input, .frm_required_field:visible select, .frm_required_field:visible textarea'
160 + ).filter( ':not(.frm_optional)' );
161 + if ( requiredFields.length ) {
162 + for ( r = 0, rl = requiredFields.length; r < rl; r++ ) {
163 + errors = checkRequiredField( requiredFields[r], errors );
209 164 }
210 - );
165 + }
211 166
212 - vanillaJsObject?.querySelectorAll( 'input,select,textarea' ).forEach(
213 - field => {
214 - if ( '' === field.value ) {
215 - if ( 'number' === field.type ) {
216 - // A number field will return an empty string when it is invalid.
217 - checkValidity( field, errors );
167 + fields = jQuery( object ).find( 'input,select,textarea' );
168 + if ( fields.length ) {
169 + for ( n = 0, nl = fields.length; n < nl; n++ ) {
170 + field = fields[n];
171 + value = field.value;
172 + if ( value !== '' ) {
173 + if ( field.type === 'hidden' ) {
174 + // don't validate
175 + } else if ( field.type === 'number' ) {
176 + errors = checkNumberField( field, errors );
177 + } else if ( field.type === 'email' ) {
178 + errors = checkEmailField( field, errors );
179 + } else if ( field.type === 'password' ) {
180 + errors = checkPasswordField( field, errors );
181 + } else if ( field.type === 'url' ) {
182 + errors = checkUrlField( field, errors );
183 + } else if ( field.pattern !== null ) {
184 + errors = checkPatternField( field, errors );
218 185 }
219 -
220 - const isConfirmationField = field.name && 0 === field.name.indexOf( 'item_meta[conf_' );
221 - if ( ! isConfirmationField ) {
222 - // Allow a blank confirmation field to still call validateFieldValue.
223 - // If we continue for a confirmation field there are issues with forms submitting with a blank confirmation field.
224 - return;
225 - }
226 186 }
227 -
228 - validateFieldValue( field, errors, true );
229 - checkValidity( field, errors );
230 187 }
231 - );
188 + }
232 189
233 - // Invisible captchas are processed after validation.
234 - // We only want to validate a visible captcha on submit.
235 - if ( ! hasInvisibleRecaptcha( object ) ) {
236 - errors = validateRecaptcha( object, errors );
237 - }
190 + errors = validateRecaptcha( object, errors );
238 191
239 192 return errors;
240 193 }
241 194
242 - /**
243 - * Check the ValidityState interface for the field.
244 - * If it is invalid, show an error for it.
245 - *
246 - * @param {HTMLElement} field
247 - * @param {Array} errors
248 - * @return {void}
249 - */
250 - function checkValidity( field, errors ) {
251 - if ( 'object' !== typeof field.validity || false !== field.validity.valid ) {
252 - return;
253 - }
254 -
255 - const fieldID = getFieldId( field, true );
256 - if ( errors[ fieldID ] === undefined ) {
257 - errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
258 - }
259 -
260 - if ( 'function' === typeof field.reportValidity ) {
261 - // This triggers an error pop up.
262 - field.reportValidity();
263 - }
264 - }
265 -
266 - /**
267 - * @since 5.0.10
268 - *
269 - * @param {Object} element
270 - * @param {string} targetClass
271 - * @return {boolean} True if the element has the target class.
272 - */
273 - function hasClass( element, targetClass ) {
274 - return element.classList && element.classList.contains( targetClass );
275 - }
276 -
277 - /**
278 - * @param {HTMLElement} field
279 - */
280 - function maybeValidateChange( field ) {
195 + function maybeValidateChange( fieldId, field ) {
281 196 if ( field.type === 'url' ) {
282 - maybeAddHttpsToUrl( field );
197 + maybeAddHttpToUrl( field );
283 198 }
284 - const form = field.closest( 'form' );
285 - if ( form && hasClass( form, 'frm_js_validate' ) ) {
286 - validateField( field );
199 + if ( jQuery( field ).closest( 'form' ).hasClass( 'frm_js_validate' ) ) {
200 + validateField( fieldId, field );
287 201 }
288 202 }
289 203
290 - /**
291 - * @param {HTMLElement} field
292 - */
293 - function maybeAddHttpsToUrl( field ) {
294 - const url = field.value;
295 - const matches = url.match( /^(https?|ftps?|mailto|news|feed|telnet):/ );
204 + function maybeAddHttpToUrl( field ) {
205 + var url = field.value;
206 + var matches = url.match( /^(https?|ftps?|mailto|news|feed|telnet):/ );
296 207 if ( field.value !== '' && matches === null ) {
297 - field.value = `https://${ url }`;
208 + field.value = 'http://' + url;
298 209 }
299 210 }
300 211
301 - /**
302 - * Validate a field with JS.
303 - *
304 - * @param {HTMLElement} field
305 - *
306 - * @return {void}
307 - */
308 - function validateField( field ) {
309 - let errors;
310 - let key;
212 + function validateField( fieldId, field ) {
213 + var key,
214 + errors = [];
311 215
312 - errors = [];
313 - const fieldContainer = field.closest( '.frm_form_field' );
314 -
315 - if ( ! fieldContainer ) {
316 - // Hidden fields do not have a field container and do not require JS validation.
317 - return;
318 - }
319 -
320 - if ( hasClass( fieldContainer, 'frm_required_field' ) && ! hasClass( field, 'frm_optional' ) ) {
216 + var $fieldCont = jQuery( field ).closest( '.frm_form_field' );
217 + if ( $fieldCont.hasClass( 'frm_required_field' ) && ! jQuery( field ).hasClass( 'frm_optional' ) ) {
321 218 errors = checkRequiredField( field, errors );
322 219 }
323 220
324 221 if ( errors.length < 1 ) {
325 - validateFieldValue( field, errors, false );
222 + if ( field.type === 'email' ) {
223 + errors = checkEmailField( field, errors );
224 + } else if ( field.type === 'password' ) {
225 + errors = checkPasswordField( field, errors );
226 + } else if ( field.type === 'number' ) {
227 + errors = checkNumberField( field, errors );
228 + } else if ( field.type === 'url' ) {
229 + errors = checkUrlField( field, errors );
230 + } else if ( field.pattern !== null ) {
231 + errors = checkPatternField( field, errors );
232 + }
326 233 }
327 234
328 - removeFieldError( fieldContainer );
329 - if ( Object.keys( errors ).length > 0 ) {
235 + removeFieldError( $fieldCont );
236 + if ( Object.keys( errors ).length > 0 ) {
330 237 for ( key in errors ) {
331 - addFieldError( fieldContainer, key, errors );
238 + addFieldError( $fieldCont, key, errors );
332 239 }
333 240 }
334 241 }
335 242
336 - /**
337 - * Validates a field value.
338 - *
339 - * @since 6.15 Added `onSubmit` parameter.
340 - *
341 - * @param {HTMLElement} field Field input.
342 - * @param {Object} errors Errors data.
343 - * @param {boolean} onSubmit Is `true` if the form is being submitted.
344 - */
345 - function validateFieldValue( field, errors, onSubmit ) {
346 - if ( field.type === 'hidden' ) {
347 - // don't validate
348 - } else if ( field.type === 'number' ) {
349 - checkNumberField( field, errors );
350 - } else if ( field.type === 'email' ) {
351 - checkEmailField( field, errors, onSubmit );
352 - } else if ( field.type === 'password' ) {
353 - checkPasswordField( field, errors, onSubmit );
354 - } else if ( field.type === 'url' ) {
355 - checkUrlField( field, errors );
356 - } else if ( field.pattern !== null ) {
357 - checkPatternField( field, errors );
358 - }
359 -
360 - if ( 'tel' === field.type && shouldCheckConfirmField( field, onSubmit ) ) {
361 - confirmField( field, errors );
362 - }
363 -
364 - /**
365 - * @since 6.15 Added `onSubmit` to the data.
366 - */
367 - triggerCustomEvent( document, 'frm_validate_field_value', {
368 - field,
369 - errors,
370 - onSubmit
371 - } );
372 - }
373 -
374 - /**
375 - * @param {HTMLElement} field
376 - * @param {Array} errors
377 - * @return {Array} Errors
378 - */
379 243 function checkRequiredField( field, errors ) {
380 - let tempVal;
381 - let i;
382 - let placeholder;
383 - let val = '';
384 - let fieldID = '';
385 - let fileID = field.getAttribute( 'data-frmfile' );
244 + var checkGroup, fieldClasses, tempVal, i, placeholder,
245 + val = '',
246 + fieldID = '',
247 + fileID = field.getAttribute( 'data-frmfile' );
386 248
387 - if ( field.type === 'hidden' && fileID === null && ! isAppointmentField( field ) && ! isInlineDatepickerField( field ) ) {
249 + if ( field.type === 'hidden' && fileID === null ) {
388 250 return errors;
389 251 }
390 252
391 253 if ( field.type === 'checkbox' || field.type === 'radio' ) {
392 - document.querySelectorAll( `input[name="${ field.name }"]` ).forEach( function( input ) {
393 - const requiredField = input.closest( '.frm_required_field' );
394 - if ( ! requiredField ) {
395 - return;
396 - }
397 -
398 - const checkedInputs = requiredField.querySelectorAll( 'input:checked' );
399 - checkedInputs.forEach( function( checkedInput ) {
400 - val = checkedInput.value;
401 - } );
402 - } );
254 + checkGroup = jQuery( 'input[name="' + field.name + '"]' ).closest( '.frm_required_field' ).find( 'input:checked' );
255 + jQuery( checkGroup ).each( function() {
256 + val = this.value;
257 + });
403 258 } else if ( field.type === 'file' || fileID ) {
404 - if ( fileID === undefined ) {
259 + if ( typeof fileID === 'undefined' ) {
405 260 fileID = getFieldId( field, true );
406 261 fileID = fileID.replace( 'file', '' );
407 262 }
408 263
409 - if ( errors[ fileID ] === undefined ) {
264 + if ( typeof errors[ fileID ] === 'undefined' ) {
410 265 val = getFileVals( fileID );
411 266 }
412 267 fieldID = fileID;
413 268 } else {
414 - if ( hasClass( field, 'frm_pos_none' ) ) {
269 + fieldClasses = field.className;
270 + if ( fieldClasses.indexOf( 'frm_pos_none' ) !== -1 ) {
415 271 // skip hidden other fields
416 272 return errors;
417 273 }
418 274
419 - val = jQuery( field ).val(); // eslint-disable-line no-jquery/no-val
420 -
275 + val = jQuery( field ).val();
421 276 if ( val === null ) {
422 277 val = '';
423 278 } else if ( typeof val !== 'string' ) {
424 279 tempVal = val;
@@ -423,40 +278,23 @@
423 278 } else if ( typeof val !== 'string' ) {
424 279 tempVal = val;
425 280 val = '';
426 281 for ( i = 0; i < tempVal.length; i++ ) {
427 - if ( tempVal[ i ] !== '' ) {
428 - val = tempVal[ i ];
282 + if ( tempVal[i] !== '' ) {
283 + val = tempVal[i];
429 284 }
430 285 }
431 286 }
432 287
433 - if ( hasClass( field, 'frm_other_input' ) ) {
288 + if ( fieldClasses.indexOf( 'frm_other_input' ) === -1 ) {
289 + fieldID = getFieldId( field, true );
290 + } else {
434 291 fieldID = getFieldId( field, false );
435 -
436 - if ( val === '' ) {
437 - field = document.getElementById( field.id.replace( '-otext', '' ) );
438 - }
439 - } else {
440 - fieldID = getFieldId( field, true );
441 292 }
442 293
443 - // Make sure fieldID is a string.
444 - // fieldID may be a number which doesn't include a .replace function.
445 - if ( 'function' !== typeof fieldID.replace ) {
446 - fieldID = fieldID.toString();
447 - }
448 -
449 - if ( hasClass( field, 'frm_time_select' ) ) {
294 + if ( fieldClasses.indexOf( 'frm_time_select' ) !== -1 ) {
450 295 // set id for time field
451 296 fieldID = fieldID.replace( '-H', '' ).replace( '-m', '' );
452 - } else if ( isSignatureField( field ) ) {
453 - if ( val === '' ) {
454 - const fieldContainer = field.closest( '.frm_form_field' );
455 - const outputField = fieldContainer ? fieldContainer.querySelector( `[name="${ field.getAttribute( 'name' ).replace( '[typed]', '[output]' ) }"]` ) : null;
456 - val = outputField ? outputField.value : '';
457 - }
458 - fieldID = fieldID.replace( '-typed', '' );
459 297 }
460 298
461 299 placeholder = field.getAttribute( 'data-frmplaceholder' );
462 300 if ( placeholder !== null && val === placeholder ) {
@@ -475,57 +313,23 @@
475 313
476 314 return errors;
477 315 }
478 316
479 - /**
480 - * @param {HTMLElement} field
481 - * @return {boolean} True if the input is a typed signature input.
482 - */
483 - function isSignatureField( field ) {
484 - const name = field.getAttribute( 'name' );
485 - return 'string' === typeof name && '[typed]' === name.substr( -7 );
486 - }
487 -
488 - /**
489 - * @param {HTMLElement} field
490 - * @return {boolean} True if the field is a SSA appointment field.
491 - */
492 - function isAppointmentField( field ) {
493 - return hasClass( field, 'ssa_appointment_form_field_appointment_id' );
494 - }
495 -
496 - /**
497 - * @param {HTMLElement} field
498 - * @return {boolean} True if the field is inline datepicker field.
499 - */
500 - function isInlineDatepickerField( field ) {
501 - return 'hidden' === field.type && '_alt' === field.id.substr( -4 ) && hasClass( field.nextElementSibling, 'frm_date_inline' );
502 - }
503 -
504 - /**
505 - * @param {string|number} fileID
506 - * @return {string} File input value.
507 - */
508 317 function getFileVals( fileID ) {
509 - let val = '';
510 - const fileFields = document.querySelectorAll( `input[name="file${ fileID }"], input[name="file${ fileID }[]"], input[name^="item_meta[${ fileID }]"]` );
318 + var val = '',
319 + fileFields = jQuery( 'input[name="file' + fileID + '"], input[name="file' + fileID + '[]"], input[name^="item_meta[' + fileID + ']"]' );
511 320
512 - fileFields.forEach( function( field ) {
321 + fileFields.each( function() {
513 322 if ( val === '' ) {
514 - val = field.value;
323 + val = this.value;
515 324 }
516 - } );
325 + });
517 326 return val;
518 327 }
519 328
520 - /**
521 - * @param {HTMLElement} field
522 - * @param {Array} errors
523 - * @return {void}
524 - */
525 329 function checkUrlField( field, errors ) {
526 - let fieldID;
527 - const url = field.value;
330 + var fieldID,
331 + url = field.value;
528 332
529 333 if ( url !== '' && ! /^http(s)?:\/\/(?:localhost|(?:[\da-z\.-]+\.[\da-z\.-]+))/i.test( url ) ) {
530 334 fieldID = getFieldId( field, true );
531 335 if ( ! ( fieldID in errors ) ) {
@@ -531,106 +335,55 @@
531 335 if ( ! ( fieldID in errors ) ) {
532 336 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
533 337 }
534 338 }
339 + return errors;
535 340 }
536 341
537 - /**
538 - * Checks if the confirm field should be checked.
539 - *
540 - * @since 6.15
541 - *
542 - * @param {HTMLElement} field Field input.
543 - * @param {boolean} onSubmit Is `true` if the form is being submitted.
544 - * @return {boolean} True if we should confirm the field.
545 - */
546 - function shouldCheckConfirmField( field, onSubmit ) {
547 - if ( onSubmit ) {
548 - // Always check on submitting.
549 - return true;
550 - }
342 + function checkEmailField( field, errors ) {
343 + var fieldID = getFieldId( field, true ),
344 + 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;
551 345
552 - if ( 0 === field.id.indexOf( 'field_conf_' ) ) {
553 - // Always check if it's the confirm field.
554 - return true;
555 - }
556 -
557 - return false;
558 - }
559 -
560 - /**
561 - * Check the email field for errors.
562 - *
563 - * @since 6.15 Added `onSubmit` parameter.
564 - *
565 - * @param {HTMLElement} field Field input.
566 - * @param {Object} errors Errors data.
567 - * @param {boolean} onSubmit Is `true` if the form is being submitted.
568 - */
569 - function checkEmailField( field, errors, onSubmit ) {
570 - const fieldID = getFieldId( field, true );
571 - const 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;
572 -
573 346 // validate the current field we're editing first
574 347 if ( '' !== field.value && pattern.test( field.value ) === false ) {
575 348 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
576 349 }
577 350
578 - if ( shouldCheckConfirmField( field, onSubmit ) ) {
579 - confirmField( field, errors );
580 - }
351 + confirmField( field, errors );
352 + return errors;
581 353 }
582 354
583 - /**
584 - * Check the password field for errors.
585 - *
586 - * @since 6.15 Added `onSubmit` parameter.
587 - *
588 - * @param {HTMLElement} field Field input.
589 - * @param {Object} errors Errors data.
590 - * @param {boolean} onSubmit Is `true` if the form is being submitted.
591 - */
592 - function checkPasswordField( field, errors, onSubmit ) {
593 - if ( shouldCheckConfirmField( field, onSubmit ) ) {
594 - confirmField( field, errors );
595 - }
355 + function checkPasswordField( field, errors ) {
356 + confirmField( field, errors );
357 + return errors;
596 358 }
597 359
598 - /**
599 - * @param {HTMLElement} field
600 - * @param {Array} errors
601 - * @return {void}
602 - */
603 360 function confirmField( field, errors ) {
604 - const fieldID = getFieldId( field, true );
605 - const strippedId = field.id.replace( 'conf_', '' );
606 - const strippedFieldID = fieldID.replace( 'conf_', '' );
607 - const confirmField = document.getElementById( strippedId.replace( 'field_', 'field_conf_' ) );
361 + var value, confirmValue, firstField,
362 + fieldID = getFieldId( field, true ),
363 + strippedId = field.id.replace( 'conf_', '' ),
364 + strippedFieldID = fieldID.replace( 'conf_', '' ),
365 + confirmField = document.getElementById( strippedId.replace( 'field_', 'field_conf_' ) );
608 366
609 - if ( confirmField === null || errors[ `conf_${ strippedFieldID }` ] !== undefined ) {
367 + if ( confirmField === null || typeof errors[ 'conf_' + strippedFieldID ] !== 'undefined' ) {
610 368 return;
611 369 }
612 370
613 371 if ( fieldID !== strippedFieldID ) {
614 - const firstField = document.getElementById( strippedId );
615 - const { value } = firstField;
616 - const confirmValue = confirmField.value;
617 - if ( value !== confirmValue ) {
618 - errors[ `conf_${ strippedFieldID }` ] = getFieldValidationMessage( confirmField, 'data-confmsg' );
372 + firstField = document.getElementById( strippedId );
373 + value = firstField.value;
374 + confirmValue = confirmField.value;
375 + if ( '' !== value && '' !== confirmValue && value !== confirmValue ) {
376 + errors[ 'conf_' + strippedFieldID ] = getFieldValidationMessage( confirmField, 'data-confmsg' );
619 377 }
620 378 } else {
621 - validateField( confirmField );
379 + validateField( 'conf_' + strippedFieldID, confirmField );
622 380 }
623 381 }
624 382
625 - /**
626 - * @param {HTMLElement} field
627 - * @param {Array} errors
628 - * @return {void}
629 - */
630 383 function checkNumberField( field, errors ) {
631 - let fieldID;
632 - const number = field.value;
384 + var fieldID,
385 + number = field.value;
633 386
634 387 if ( number !== '' && isNaN( number / 1 ) !== false ) {
635 388 fieldID = getFieldId( field, true );
636 389 if ( ! ( fieldID in errors ) ) {
@@ -636,200 +389,106 @@
636 389 if ( ! ( fieldID in errors ) ) {
637 390 errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
638 391 }
639 392 }
393 + return errors;
640 394 }
641 395
642 - /**
643 - * @param {HTMLElement} field
644 - * @param {Array} errors
645 - * @return {void}
646 - */
647 396 function checkPatternField( field, errors ) {
648 - let fieldID;
649 - const text = field.value;
650 - let format = getFieldValidationMessage( field, 'pattern' );
397 + var fieldID,
398 + text = field.value,
399 + format = getFieldValidationMessage( field, 'pattern' );
651 400
652 401 if ( format !== '' && text !== '' ) {
653 402 fieldID = getFieldId( field, true );
654 403 if ( ! ( fieldID in errors ) ) {
655 - if ( 'object' === typeof window.frmProForm && 'function' === typeof window.frmProForm.isIntlPhoneInput && window.frmProForm.isIntlPhoneInput( field ) ) {
656 - if ( ! window.frmProForm.validateIntlPhoneInput( field ) ) {
657 - errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
658 - }
659 - } else {
660 - format = new RegExp( `^${ format }$`, 'i' );
661 - if ( format.test( text ) === false ) {
662 - errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
663 - }
404 + format = new RegExp( '^' + format + '$', 'i' );
405 + if ( format.test( text ) === false ) {
406 + errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' );
664 407 }
665 408 }
666 409 }
410 + return errors;
667 411 }
668 412
669 - /**
670 - * Set color for select placeholders.
671 - *
672 - * @since 6.5.1
673 - */
674 - function setSelectPlaceholderColor() {
675 - const selects = document.querySelectorAll( '.form-field select' );
676 - const styleElement = document.querySelector( '.with_frm_style' );
677 - const textColorDisabled = styleElement ? getComputedStyle( styleElement ).getPropertyValue( '--text-color-disabled' ).trim() : '';
678 - // Exit if there are no select elements or the textColorDisabled property is missing
679 - if ( ! selects.length || ! textColorDisabled ) {
680 - return;
681 - }
413 + function hasInvisibleRecaptcha( object ) {
414 + var recaptcha, recaptchaID, alreadyChecked;
682 415
683 - // Function to change the color of a select element
684 - const changeSelectColor = function( select ) {
685 - if ( select.options[ select.selectedIndex ] && hasClass( select.options[ select.selectedIndex ], 'frm-select-placeholder' ) ) {
686 - select.style.setProperty( 'color', textColorDisabled, 'important' );
687 - } else {
688 - select.style.color = '';
689 - }
690 - };
691 -
692 - // Use a loop to iterate through each select element
693 - Array.prototype.forEach.call( selects, function( select ) {
694 - // Apply the color change to each select element
695 - changeSelectColor( select );
696 -
697 - // Add an event listener for future changes
698 - select.addEventListener( 'change', function() {
699 - changeSelectColor( select );
700 - } );
701 - } );
702 - }
703 -
704 - /**
705 - * @param {HTMLElement|jQuery} object
706 - *
707 - * @return {HTMLElement|false} Captcha element if there is an invisible recaptcha.
708 - */
709 - function hasInvisibleRecaptcha( object ) {
710 416 if ( isGoingToPrevPage( object ) ) {
711 417 return false;
712 418 }
713 419
714 - const form = object instanceof jQuery ? object.get( 0 ) : object;
715 - if ( ! form ) {
716 - return false;
717 - }
718 -
719 - const recaptcha = form.querySelector( '.frm-g-recaptcha[data-size="invisible"], .g-recaptcha[data-size="invisible"]' );
720 - if ( recaptcha ) {
721 - const recaptchaID = recaptcha.dataset.rid;
722 - const alreadyChecked = grecaptcha.getResponse( recaptchaID );
420 + recaptcha = jQuery( object ).find( '.frm-g-recaptcha[data-size="invisible"], .g-recaptcha[data-size="invisible"]' );
421 + if ( recaptcha.length ) {
422 + recaptchaID = recaptcha.data( 'rid' );
423 + alreadyChecked = grecaptcha.getResponse( recaptchaID );
723 424 if ( alreadyChecked.length === 0 ) {
724 425 return recaptcha;
426 + } else {
427 + return false;
725 428 }
429 + } else {
430 + return false;
726 431 }
727 -
728 - return false;
729 432 }
730 433
731 - /**
732 - * @param {HTMLElement} invisibleRecaptcha
733 - *
734 - * @return {void}
735 - */
736 434 function executeInvisibleRecaptcha( invisibleRecaptcha ) {
737 - const recaptchaID = invisibleRecaptcha.dataset.rid;
435 + var recaptchaID = invisibleRecaptcha.data( 'rid' );
738 436 grecaptcha.reset( recaptchaID );
739 437 grecaptcha.execute( recaptchaID );
740 438 }
741 439
742 440 function validateRecaptcha( form, errors ) {
743 - const formEl = form instanceof jQuery ? form.get( 0 ) : form;
744 - if ( ! formEl ) {
745 - return errors;
746 - }
441 + var recaptchaID, response, fieldContainer, fieldID,
442 + $recaptcha = jQuery( form ).find( '.frm-g-recaptcha' );
443 + if ( $recaptcha.length ) {
444 + recaptchaID = $recaptcha.data( 'rid' );
747 445
748 - const recaptcha = formEl.querySelector( '.frm-g-recaptcha' );
749 - if ( ! recaptcha ) {
750 - return errors;
751 - }
752 -
753 - const recaptchaID = recaptcha.dataset.rid;
754 - let response;
755 -
756 - try {
757 - response = grecaptcha.getResponse( recaptchaID );
758 - } catch ( e ) {
759 - if ( formEl.querySelector( 'input[name="recaptcha_checked"]' ) ) {
760 - return errors;
446 + try {
447 + response = grecaptcha.getResponse( recaptchaID );
448 + } catch ( e ) {
449 + if ( jQuery( form ).find( 'input[name="recaptcha_checked"]' ).length ) {
450 + return errors;
451 + } else {
452 + response = '';
453 + }
761 454 }
762 - response = '';
763 - }
764 455
765 - if ( response.length === 0 ) {
766 - const fieldContainer = recaptcha.closest( '.frm_form_field' );
767 - if ( fieldContainer && fieldContainer.id ) {
768 - const fieldID = fieldContainer.id.replace( 'frm_field_', '' ).replace( '_container', '' );
456 + if ( response.length === 0 ) {
457 + fieldContainer = $recaptcha.closest( '.frm_form_field' );
458 + fieldID = fieldContainer.attr( 'id' ).replace( 'frm_field_', '' ).replace( '_container', '' );
769 459 errors[ fieldID ] = '';
770 460 }
771 461 }
772 -
773 462 return errors;
774 463 }
775 464
776 - /**
777 - * @param {HTMLElement} field
778 - * @param {string} messageType
779 - * @return {string} The error message to display.
780 - */
781 465 function getFieldValidationMessage( field, messageType ) {
782 - let msg = field.getAttribute( messageType );
466 + var msg, errorHtml;
467 +
468 + msg = field.getAttribute( messageType );
783 469 if ( null === msg ) {
784 470 msg = '';
785 471 }
786 472
787 473 if ( '' !== msg && shouldWrapErrorHtmlAroundMessageType( messageType ) ) {
788 - msg = wrapErrorHtml( msg, field );
474 + errorHtml = field.getAttribute( 'data-error-html' );
475 + if ( null !== errorHtml ) {
476 + errorHtml = errorHtml.replace( /\+/g, '%20' );
477 + msg = decodeURIComponent( errorHtml ).replace( '[error]', msg );
478 + msg = msg.replace( '[key]', getFieldId( field, false ) );
479 + }
789 480 }
790 481
791 482 return msg;
792 483 }
793 484
794 - /**
795 - * @param {string} msg
796 - * @param {HTMLElement} field
797 - * @return {string} The error HTML to use.
798 - */
799 - function wrapErrorHtml( msg, field ) {
800 - let errorHtml = field.getAttribute( 'data-error-html' );
801 - if ( null === errorHtml ) {
802 - return msg;
803 - }
804 -
805 - errorHtml = errorHtml.replace( /\+/g, '%20' );
806 - msg = decodeURIComponent( errorHtml ).replace( '[error]', msg );
807 - const fieldId = getFieldId( field, false );
808 - const split = fieldId.split( '-' );
809 - const fieldIdParts = field.id.split( '_' );
810 - fieldIdParts.shift(); // Drop the "field" value from the front.
811 - split[ 0 ] = fieldIdParts.join( '_' );
812 - const errorKey = split.join( '-' );
813 - return msg.replace( '[key]', errorKey );
814 - }
815 -
816 485 function shouldWrapErrorHtmlAroundMessageType( type ) {
817 486 return 'pattern' !== type;
818 487 }
819 488
820 - /**
821 - * Check if JS validation should happen.
822 - *
823 - * @param {HTMLElement|Object} object Form object.
824 - * @return {boolean} True if validation is enabled and we are not saving a draft or going to a previous page.
825 - */
826 489 function shouldJSValidate( object ) {
827 - if ( 'function' === typeof object.get ) {
828 - // Get the HTMLElement from a jQuery object.
829 - object = object.get( 0 );
830 - }
831 - let validate = hasClass( object, 'frm_js_validate' );
490 + var validate = jQuery( object ).hasClass( 'frm_js_validate' );
832 491 if ( validate && typeof frmProForm !== 'undefined' && ( frmProForm.savingDraft( object ) || frmProForm.goingToPreviousPage( object ) ) ) {
833 492 validate = false;
834 493 }
835 494
@@ -835,47 +494,28 @@
835 494
836 495 return validate;
837 496 }
838 497
839 - /**
840 - * @param {HTMLElement} object
841 - * @param {string} action
842 - * @return {void}
843 - */
844 498 function getFormErrors( object, action ) {
845 - const fieldsets = object.querySelectorAll( '.frm_form_field' );
846 - fieldsets.forEach( field => field.classList.add( 'frm_doing_ajax' ) );
499 + var fieldset;
847 500
848 - const data = `${ jQuery( object ).serialize() }&action=frm_entries_${ action }&nonce=${ frm_js.nonce }`; // eslint-disable-line no-jquery/no-serialize
849 - const shouldTriggerEvent = object.classList.contains( 'frm_trigger_event_on_submit' );
501 + if ( typeof action === 'undefined' ) {
502 + jQuery( object ).find( 'input[name="frm_action"]' ).val();
503 + }
850 504
851 - const doRedirect = response => {
852 - jQuery( document ).trigger( 'frmBeforeFormRedirect', [ object, response ] );
505 + fieldset = jQuery( object ).find( '.frm_form_field' );
506 + fieldset.addClass( 'frm_doing_ajax' );
507 + jQuery.ajax({
508 + type: 'POST', url: frm_js.ajax_url,
509 + data: jQuery( object ).serialize() + '&action=frm_entries_' + action + '&nonce=' + frm_js.nonce,
510 + success: function( response ) {
511 + var formID, replaceContent, pageOrder, formReturned, contSubmit, delay,
512 + $fieldCont, key, inCollapsedSection, frmTrigger,
513 + defaultResponse = { 'content': '', 'errors': {}, 'pass': false };
514 + if ( response === null ) {
515 + response = defaultResponse;
516 + }
853 517
854 - if ( ! response.openInNewTab ) {
855 - // We return here because we're redirecting there is no need to update content.
856 - window.location = response.redirect;
857 - return;
858 - }
859 -
860 - // We don't return here because we're opening in a new tab, the old tab will still update.
861 - const newTab = window.open( response.redirect, '_blank' );
862 - if ( ! newTab && response.fallbackMsg && response.content ) {
863 - response.content = response.content.trim().replace( /(<\/div><\/div>)$/, ` ${ response.fallbackMsg }</div></div>` );
864 - }
865 - };
866 -
867 - const success = function( response ) {
868 - const defaultResponse = {
869 - content: '',
870 - errors: {},
871 - pass: false
872 - };
873 -
874 - if ( response === null ) {
875 - response = defaultResponse;
876 - } else {
877 - // Response is a string. Convert it to an object.
878 518 response = response.replace( /^\s+|\s+$/g, '' );
879 519 if ( response.indexOf( '{' ) === 0 ) {
880 520 response = JSON.parse( response );
881 521 } else {
@@ -880,218 +520,151 @@
880 520 response = JSON.parse( response );
881 521 } else {
882 522 response = defaultResponse;
883 523 }
884 - }
885 524
886 - if ( response.redirect !== undefined ) {
887 - if ( shouldTriggerEvent ) {
888 - triggerCustomEvent( object, 'frmSubmitEvent' );
889 - return;
890 - }
525 + if ( typeof response.redirect !== 'undefined' ) {
526 + jQuery( document ).trigger( 'frmBeforeFormRedirect', [ object, response ]);
527 + window.location = response.redirect;
528 + } else if ( response.content !== '' ) {
529 + // the form or success message was returned
891 530
892 - if ( response.delay ) {
893 - setTimeout( function() {
894 - doRedirect( response );
895 - }, 1000 * response.delay );
896 - } else {
897 - doRedirect( response );
898 - }
899 - }
531 + removeSubmitLoading( jQuery( object ) );
532 + if ( frm_js.offset != -1 ) {
533 + frmFrontForm.scrollMsg( jQuery( object ), false );
534 + }
535 + formID = jQuery( object ).find( 'input[name="form_id"]' ).val();
536 + response.content = response.content.replace( / frm_pro_form /g, ' frm_pro_form frm_no_hide ' );
537 + replaceContent = jQuery( object ).closest( '.frm_forms' );
538 + removeAddedScripts( replaceContent, formID );
539 + delay = maybeSlideOut( replaceContent, response.content );
900 540
901 - if ( 'string' === typeof response.content && response.content !== '' ) {
902 - // the form or success message was returned
541 + setTimeout(
542 + function() {
543 + var container, input, previousInput;
903 544
904 - if ( shouldTriggerEvent ) {
905 - triggerCustomEvent( object, 'frmSubmitEvent', { content: response.content } );
906 - return;
907 - }
545 + replaceContent.replaceWith( response.content );
908 546
909 - removeSubmitLoading( jQuery( object ) );
910 - if ( frm_js.offset != -1 ) {
911 - frmFrontForm.scrollMsg( jQuery( object ), false );
912 - }
547 + addUrlParam( response );
913 548
914 - const formIdInput = object.querySelector( 'input[name="form_id"]' );
915 - const formID = formIdInput ? formIdInput.value : '';
916 - response.content = response.content.replace( / frm_pro_form /g, ' frm_pro_form frm_no_hide ' );
917 - const replaceContent = jQuery( object ).closest( '.frm_forms' ); // eslint-disable-line no-jquery/no-closest
918 - removeAddedScripts( replaceContent, formID );
919 - const delay = maybeSlideOut( replaceContent, response.content );
549 + if ( typeof frmThemeOverride_frmAfterSubmit === 'function' ) { // eslint-disable-line camelcase
550 + pageOrder = jQuery( 'input[name="frm_page_order_' + formID + '"]' ).val();
551 + formReturned = jQuery( response.content ).find( 'input[name="form_id"]' ).val();
552 + frmThemeOverride_frmAfterSubmit( formReturned, pageOrder, response.content, object );
553 + }
920 554
921 - setTimeout(
922 - function() {
923 - afterFormSubmittedBeforeReplace( object, response );
555 + if ( typeof response.recaptcha !== 'undefined' ) {
556 + container = jQuery( '#frm_form_' + formID + '_container' ).find( '.frm_fields_container' );
557 + input = '<input type="hidden" name="recaptcha_checked" value="' + response.recaptcha + '">';
558 + previousInput = container.find( 'input[name="recaptcha_checked"]' );
924 559
925 - replaceContent.replaceWith( response.content );
560 + if ( previousInput.length ) {
561 + previousInput.replaceWith( input );
562 + } else {
563 + container.append( input );
564 + }
565 + }
926 566
927 - addUrlParam( response );
567 + afterFormSubmitted( object, response );
568 + },
569 + delay
570 + );
571 + } else if ( Object.keys( response.errors ).length ) {
572 + // errors were returned
928 573
929 - if ( typeof frmThemeOverride_frmAfterSubmit === 'function' ) { // eslint-disable-line camelcase
930 - const pageOrderInput = document.querySelector( `input[name="frm_page_order_${ formID }"]` );
931 - const pageOrder = pageOrderInput ? pageOrderInput.value : '';
932 - const tempDiv = document.createElement( 'div' );
933 - tempDiv.innerHTML = response.content;
934 - const formReturnedInput = tempDiv.querySelector( 'input[name="form_id"]' );
935 - const formReturned = formReturnedInput ? formReturnedInput.value : '';
936 - frmThemeOverride_frmAfterSubmit( formReturned, pageOrder, response.content, object );
937 - }
574 + removeSubmitLoading( jQuery( object ), 'enable' );
938 575
939 - afterFormSubmitted( object, response );
940 - },
941 - delay
942 - );
943 - } else if ( Object.keys( response.errors ).length ) {
944 - // errors were returned
945 - removeSubmitLoading( jQuery( object ), 'enable' );
576 + //show errors
577 + contSubmit = true;
578 + removeAllErrors();
946 579
947 - //show errors
948 - let contSubmit = true;
949 - removeAllErrors();
580 + $fieldCont = null;
950 581
951 - let $fieldCont = null;
582 + for ( key in response.errors ) {
583 + $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
952 584
953 - for ( const key in response.errors ) {
954 - const fieldContEl = object.querySelector( `#frm_field_${ key }_container` );
955 - $fieldCont = fieldContEl ? jQuery( fieldContEl ) : jQuery();
956 -
957 - if ( $fieldCont.length ) {
958 - if ( ! $fieldCont.is( ':visible' ) ) { // eslint-disable-line no-jquery/no-is
959 - const inCollapsedSection = $fieldCont.closest( '.frm_toggle_container' ); // eslint-disable-line no-jquery/no-closest
960 - if ( inCollapsedSection.length ) {
961 - let frmTrigger = inCollapsedSection.prev();
962 - if ( ! frmTrigger.hasClass( 'frm_trigger' ) ) {
963 - // If the frmTrigger object is the section description, check to see if the previous element is the trigger
964 - frmTrigger = frmTrigger.prev( '.frm_trigger' );
585 + if ( $fieldCont.length ) {
586 + if ( ! $fieldCont.is( ':visible' ) ) {
587 + inCollapsedSection = $fieldCont.closest( '.frm_toggle_container' );
588 + if ( inCollapsedSection.length ) {
589 + frmTrigger = inCollapsedSection.prev();
590 + if ( ! frmTrigger.hasClass( 'frm_trigger' ) ) {
591 + // If the frmTrigger object is the section description, check to see if the previous element is the trigger
592 + frmTrigger = frmTrigger.prev( '.frm_trigger' );
593 + }
594 + frmTrigger.trigger( 'click' );
965 595 }
966 - frmTrigger.trigger( 'click' );
967 596 }
968 - }
969 597
970 - if ( $fieldCont.is( ':visible' ) ) { // eslint-disable-line no-jquery/no-is
971 - addFieldError( $fieldCont, key, response.errors );
972 - contSubmit = false;
598 + if ( $fieldCont.is( ':visible' ) ) {
599 + addFieldError( $fieldCont, key, response.errors );
600 + contSubmit = false;
601 + }
973 602 }
974 603 }
975 - }
976 604
977 - object.querySelectorAll( '.frm-g-recaptcha, .g-recaptcha, .h-captcha' ).forEach( function( captchaEl ) {
978 - const recaptchaID = captchaEl.dataset.rid;
605 + jQuery( object ).find( '.frm-g-recaptcha, .g-recaptcha' ).each( function() {
606 + var $recaptcha = jQuery( this ),
607 + recaptchaID = $recaptcha.data( 'rid' );
979 608
980 - if ( typeof grecaptcha !== 'undefined' && grecaptcha ) {
981 - if ( recaptchaID ) {
982 - grecaptcha.reset( recaptchaID );
983 - } else {
984 - grecaptcha.reset();
609 + if ( typeof grecaptcha !== 'undefined' && grecaptcha ) {
610 + if ( recaptchaID ) {
611 + grecaptcha.reset( recaptchaID );
612 + } else {
613 + grecaptcha.reset();
614 + }
985 615 }
986 - }
616 + });
987 617
988 - if ( typeof hcaptcha !== 'undefined' && hcaptcha ) {
989 - hcaptcha.reset();
990 - }
991 - } );
618 + jQuery( document ).trigger( 'frmFormErrors', [ object, response ]);
992 619
993 - if ( window.turnstile ) {
994 - object.querySelectorAll( '.frm-cf-turnstile' ).forEach(
995 - turnstileField => turnstileField.dataset.rid && turnstile.reset( turnstileField.dataset.rid )
996 - );
997 - }
620 + fieldset.removeClass( 'frm_doing_ajax' );
621 + scrollToFirstField( object );
998 622
999 - jQuery( document ).trigger( 'frmFormErrors', [ object, response ] );
623 + if ( contSubmit ) {
624 + object.submit();
625 + } else {
626 + jQuery( object ).prepend( response.error_message );
627 + checkForErrorsAndMaybeSetFocus();
628 + }
629 + } else {
630 + // there may have been a plugin conflict, or the form is not set to submit with ajax
1000 631
1001 - fieldsets.forEach( field => field.classList.remove( 'frm_doing_ajax' ) );
1002 - scrollToFirstField( object );
632 + showFileLoading( object );
1003 633
1004 - if ( contSubmit ) {
1005 634 object.submit();
1006 - } else {
1007 - object.insertAdjacentHTML( 'afterbegin', response.error_message );
1008 - checkForErrorsAndMaybeSetFocus();
1009 635 }
1010 - } else {
1011 - // there may have been a plugin conflict, or the form is not set to submit with ajax
1012 -
1013 - showFileLoading( object );
1014 -
636 + },
637 + error: function() {
638 + jQuery( object ).find( 'input[type="submit"], input[type="button"]' ).prop( 'disabled', false );
1015 639 object.submit();
1016 640 }
1017 - };
1018 -
1019 - const error = function() {
1020 - object.querySelectorAll( 'input[type="submit"], input[type="button"]' ).forEach(
1021 - button => button.disabled = false
1022 - );
1023 - object.submit();
1024 - };
1025 -
1026 - postToAjaxUrl( object, data, success, error );
641 + });
1027 642 }
1028 643
1029 - function postToAjaxUrl( form, data, success, error ) {
1030 - let ajaxUrl = frm_js.ajax_url;
1031 - const action = form.getAttribute( 'action' );
1032 -
1033 - if ( 'string' === typeof action && action.includes( '?action=frm_forms_preview' ) ) {
1034 - ajaxUrl = action.split( '?action=frm_forms_preview' )[ 0 ];
1035 - }
1036 -
1037 - const ajaxParams = {
1038 - type: 'POST',
1039 - url: ajaxUrl,
1040 - data,
1041 - success
1042 - };
1043 -
1044 - if ( 'function' === typeof error ) {
1045 - ajaxParams.error = error;
1046 - }
1047 -
1048 - jQuery.ajax( ajaxParams ); // eslint-disable-line no-jquery/no-ajax
1049 - }
1050 -
1051 644 function afterFormSubmitted( object, response ) {
1052 - const tempDiv = document.createElement( 'div' );
1053 - tempDiv.innerHTML = response.content;
1054 - const formCompleted = tempDiv.querySelector( '.frm_message' );
1055 - if ( formCompleted ) {
1056 - jQuery( document ).trigger( 'frmFormComplete', [ object, response ] );
645 + var formCompleted = jQuery( response.content ).find( '.frm_message' );
646 + if ( formCompleted.length ) {
647 + jQuery( document ).trigger( 'frmFormComplete', [ object, response ]);
1057 648 } else {
1058 - jQuery( document ).trigger( 'frmPageChanged', [ object, response ] );
649 + jQuery( document ).trigger( 'frmPageChanged', [ object, response ]);
1059 650 }
1060 651 }
1061 652
1062 - /**
1063 - * Trigger an event before the form is replaced with a success message.
1064 - *
1065 - * @since 6.9
1066 - *
1067 - * @param {HTMLElement} object The form.
1068 - * @param {Object} response The response from submitting the form with AJAX.
1069 - * @return {void}
1070 - */
1071 - function afterFormSubmittedBeforeReplace( object, response ) {
1072 - const tempDiv = document.createElement( 'div' );
1073 - tempDiv.innerHTML = response.content;
1074 - const formCompleted = tempDiv.querySelector( '.frm_message' );
1075 - if ( formCompleted ) {
1076 - triggerCustomEvent( document, 'frmFormCompleteBeforeReplace', { object, response } );
1077 - }
1078 - }
1079 -
1080 653 function removeAddedScripts( formContainer, formID ) {
1081 - const endReplace = document.querySelectorAll( `.frm_end_ajax_${ formID }` );
654 + var endReplace = jQuery( '.frm_end_ajax_' + formID );
1082 655 if ( endReplace.length ) {
1083 - formContainer.nextUntil( `.frm_end_ajax_${ formID }` ).remove();
1084 - endReplace.forEach( el => el.remove() );
656 + formContainer.nextUntil( '.frm_end_ajax_' + formID ).remove();
657 + endReplace.remove();
1085 658 }
1086 659 }
1087 660
1088 661 function maybeSlideOut( oldContent, newContent ) {
1089 - let c;
1090 - let newClass = 'frm_slideout';
1091 - if ( newContent.includes( ' frm_slide' ) ) {
662 + var c,
663 + newClass = 'frm_slideout';
664 + if ( newContent.indexOf( ' frm_slide' ) !== -1 ) {
1092 665 c = oldContent.children();
1093 - if ( newContent.includes( ' frm_going_back' ) ) {
666 + if ( newContent.indexOf( ' frm_going_back' ) !== -1 ) {
1094 667 newClass += ' frm_going_back';
1095 668 }
1096 669 c.removeClass( 'frm_going_back' );
1097 670 c.addClass( newClass );
@@ -1100,28 +673,30 @@
1100 673 return 0;
1101 674 }
1102 675
1103 676 function addUrlParam( response ) {
1104 - let url;
1105 - if ( history.pushState && response.page !== undefined ) {
677 + var url;
678 + if ( history.pushState && typeof response.page !== 'undefined' ) {
1106 679 url = addQueryVar( 'frm_page', response.page );
1107 - window.history.pushState( { html: response.html }, '', `?${ url }` );
680 + window.history.pushState({ 'html': response.html }, '', '?' + url );
1108 681 }
1109 682 }
1110 683
1111 684 function addQueryVar( key, value ) {
685 + var kvp, i, x;
686 +
1112 687 key = encodeURI( key );
1113 688 value = encodeURI( value );
1114 689
1115 - const kvp = document.location.search.substr( 1 ).split( '&' );
690 + kvp = document.location.search.substr( 1 ).split( '&' );
1116 691
1117 - let i = kvp.length;
692 + i = kvp.length;
1118 693 while ( i-- ) {
1119 - const x = kvp[ i ].split( '=' );
694 + x = kvp[i].split( '=' );
1120 695
1121 - if ( x[ 0 ] == key ) {
1122 - x[ 1 ] = value;
1123 - kvp[ i ] = x.join( '=' );
696 + if ( x[0] == key ) {
697 + x[1] = value;
698 + kvp[i] = x.join( '=' );
1124 699 break;
1125 700 }
1126 701 }
1127 702
@@ -1132,143 +707,66 @@
1132 707 return kvp.join( '&' );
1133 708 }
1134 709
1135 710 function addFieldError( $fieldCont, key, jsErrors ) {
1136 - const container = $fieldCont instanceof jQuery ? $fieldCont.get( 0 ) : $fieldCont;
711 + var input, id, describedBy;
712 + if ( $fieldCont.length && $fieldCont.is( ':visible' ) ) {
713 + $fieldCont.addClass( 'frm_blank_field' );
714 + input = $fieldCont.find( 'input, select, textarea' );
715 + id = 'frm_error_field_' + key;
716 + describedBy = input.attr( 'aria-describedby' );
1137 717
1138 - if ( ! container || container.offsetParent === null ) {
1139 - return;
1140 - }
1141 -
1142 - container.classList.add( 'frm_blank_field' );
1143 - const input = container.querySelector( 'input, select, textarea' );
1144 - const id = getErrorElementId( key, input );
1145 -
1146 - let describedBy = input ? input.getAttribute( 'aria-describedby' ) : null;
1147 -
1148 - if ( typeof frmThemeOverride_frmPlaceError === 'function' ) { // eslint-disable-line camelcase
1149 - frmThemeOverride_frmPlaceError( key, jsErrors );
1150 - } else {
1151 - let errorHtml;
1152 - if ( jsErrors[ key ].includes( '<div' ) ) {
1153 - errorHtml = jsErrors[ key ];
718 + if ( typeof frmThemeOverride_frmPlaceError === 'function' ) { // eslint-disable-line camelcase
719 + frmThemeOverride_frmPlaceError( key, jsErrors );
1154 720 } else {
1155 - const roleString = frm_js.include_alert_role ? 'role="alert"' : '';
1156 - errorHtml = `<div class="frm_error" ${ roleString } id="${ id }">${ jsErrors[ key ] }</div>`;
1157 - }
1158 - container.insertAdjacentHTML( 'beforeend', errorHtml );
721 + if ( -1 !== jsErrors[key].indexOf( '<div' ) ) {
722 + $fieldCont.append(
723 + jsErrors[key]
724 + );
725 + } else {
726 + $fieldCont.append( '<div class="frm_error" id="' + id + '">' + jsErrors[key] + '</div>' );
727 + }
1159 728
1160 - if ( input ) {
1161 - if ( ! describedBy ) {
729 + if ( typeof describedBy === 'undefined' ) {
1162 730 describedBy = id;
1163 - } else if ( ! describedBy.includes( id ) && ! describedBy.includes( 'frm_error_field_' ) ) {
1164 - const { errorFirst } = input.dataset;
1165 - if ( errorFirst === '0' ) {
1166 - describedBy = `${ describedBy } ${ id }`;
1167 - } else {
1168 - describedBy = `${ id } ${ describedBy }`;
1169 - }
731 + } else if ( describedBy.indexOf( id ) === -1 ) {
732 + describedBy = describedBy + ' ' + id;
1170 733 }
1171 - input.setAttribute( 'aria-describedby', describedBy );
734 + input.attr( 'aria-describedby', describedBy );
1172 735 }
1173 - }
736 + input.attr( 'aria-invalid', true );
1174 737
1175 - if ( input ) {
1176 - if ( [ 'radio', 'checkbox' ].includes( input.type ) ) {
1177 - const group = input.closest( '[role="radiogroup"], [role="group"]' );
1178 - if ( group ) {
1179 - group.setAttribute( 'aria-invalid', 'true' );
1180 - }
1181 - } else {
1182 - input.setAttribute( 'aria-invalid', 'true' );
1183 - }
738 + jQuery( document ).trigger( 'frmAddFieldError', [ $fieldCont, key, jsErrors ]);
1184 739 }
1185 -
1186 - jQuery( document ).trigger( 'frmAddFieldError', [ jQuery( container ), key, jsErrors ] );
1187 740 }
1188 741
1189 - /**
1190 - * Get the ID to use for an error element added when submitting with AJAX.
1191 - *
1192 - * @param {string} key
1193 - * @param {HTMLElement} input
1194 - * @return {string} The ID to use for the error element.
1195 - */
1196 - function getErrorElementId( key, input ) {
1197 - if ( isNaN( key ) || ! input || ! input.id ) {
1198 - // If key isn't a number, assume it's already in the right format.
1199 - return `frm_error_field_${ key }`;
1200 - }
1201 - return `frm_error_${ input.id }`;
1202 - }
742 + function removeFieldError( $fieldCont ) {
743 + var errorMessage = $fieldCont.find( '.frm_error' ),
744 + errorId = errorMessage.attr( 'id' ),
745 + input = $fieldCont.find( 'input, select, textarea' ),
746 + describedBy = input.attr( 'aria-describedby' );
1203 747
1204 - /**
1205 - * Removes errors before validating with JS.
1206 - * This prevents issues with stale errors that has since been fixed.
1207 - *
1208 - * @param {HTMLElement|jQuery} fieldCont Field container element.
1209 - * @return {void}
1210 - */
1211 - function removeFieldError( fieldCont ) {
1212 - const container = fieldCont instanceof jQuery ? fieldCont.get( 0 ) : fieldCont;
1213 - if ( ! container ) {
1214 - return;
1215 - }
748 + $fieldCont.removeClass( 'frm_blank_field has-error' );
749 + errorMessage.remove();
750 + input.attr( 'aria-invalid', false );
1216 751
1217 - const errorMessage = container.querySelector( '.frm_error' );
1218 - const errorId = errorMessage ? errorMessage.id : '';
1219 - const input = container.querySelector( 'input, select, textarea' );
1220 - let describedBy = input ? input.getAttribute( 'aria-describedby' ) : null;
1221 -
1222 - container.classList.remove( 'frm_blank_field', 'has-error' );
1223 -
1224 - if ( input ) {
1225 - if ( 'true' === input.getAttribute( 'aria-invalid' ) ) {
1226 - input.setAttribute( 'aria-invalid', 'false' );
1227 - } else if ( [ 'radio', 'checkbox' ].includes( input.type ) ) {
1228 - const group = input.closest( '[role="radiogroup"], [role="group"]' );
1229 - if ( group ) {
1230 - group.setAttribute( 'aria-invalid', 'false' );
1231 - }
1232 - }
752 + if ( typeof describedBy !== 'undefined' ) {
753 + describedBy = describedBy.replace( errorId, '' );
754 + input.attr( 'aria-describedby', describedBy );
1233 755 }
1234 -
1235 - if ( errorMessage ) {
1236 - errorMessage.remove();
1237 - }
1238 -
1239 - if ( input ) {
1240 - input.removeAttribute( 'aria-describedby' );
1241 - if ( describedBy ) {
1242 - describedBy = describedBy.replace( errorId, '' ).trim();
1243 - if ( describedBy ) {
1244 - input.setAttribute( 'aria-describedby', describedBy );
1245 - }
1246 - }
1247 - }
1248 756 }
1249 757
1250 758 function removeAllErrors() {
1251 - document.querySelectorAll( '.form-field' ).forEach( field => {
1252 - field.classList.remove( 'frm_blank_field', 'has-error' );
1253 - } );
1254 - document.querySelectorAll( '.form-field .frm_error' ).forEach( error => error.remove() );
1255 - document.querySelectorAll( '.frm_error_style' ).forEach( error => error.remove() );
759 + jQuery( '.form-field' ).removeClass( 'frm_blank_field has-error' );
760 + jQuery( '.form-field .frm_error' ).replaceWith( '' );
761 + jQuery( '.frm_error_style' ).remove();
1256 762 }
1257 763
1258 - /**
1259 - * @param {HTMLElement|Object} object Form object.
1260 - * @return {void}
1261 - */
1262 764 function scrollToFirstField( object ) {
1263 - if ( 'function' === typeof object.get ) {
1264 - // Get the HTMLElement from a jQuery object.
1265 - object = object.get( 0 );
765 + var field = jQuery( object ).find( '.frm_blank_field' ).first();
766 + if ( field.length ) {
767 + frmFrontForm.scrollMsg( field, object, true );
1266 768 }
1267 - const field = object.querySelector( '.frm_blank_field' );
1268 - if ( field ) {
1269 - frmFrontForm.scrollMsg( jQuery( field ), object, true );
1270 - }
1271 769 }
1272 770
1273 771 function showSubmitLoading( $object ) {
1274 772 showLoadingIndicator( $object );
@@ -1276,9 +774,9 @@
1276 774 disableSaveDraft( $object );
1277 775 }
1278 776
1279 777 function showLoadingIndicator( $object ) {
1280 - if ( ! $object.hasClass( 'frm_loading_form' ) && ! $object.hasClass( 'frm_loading_prev' ) ) { // eslint-disable-line no-jquery/no-class
778 + if ( ! $object.hasClass( 'frm_loading_form' ) && ! $object.hasClass( 'frm_loading_prev' ) ) {
1281 779 addLoadingClass( $object );
1282 780 $object.trigger( 'frmStartFormLoading' );
1283 781 }
1284 782 }
@@ -1283,11 +781,11 @@
1283 781 }
1284 782 }
1285 783
1286 784 function addLoadingClass( $object ) {
1287 - const loadingClass = isGoingToPrevPage( $object ) ? 'frm_loading_prev' : 'frm_loading_form';
785 + var loadingClass = isGoingToPrevPage( $object ) ? 'frm_loading_prev' : 'frm_loading_form';
1288 786
1289 - $object.addClass( loadingClass ); // eslint-disable-line no-jquery/no-class
787 + $object.addClass( loadingClass );
1290 788 }
1291 789
1292 790 function isGoingToPrevPage( $object ) {
1293 791 return ( typeof frmProForm !== 'undefined' && frmProForm.goingToPreviousPage( $object ) );
@@ -1292,39 +790,101 @@
1292 790 function isGoingToPrevPage( $object ) {
1293 791 return ( typeof frmProForm !== 'undefined' && frmProForm.goingToPreviousPage( $object ) );
1294 792 }
1295 793
1296 - function removeSubmitLoading( _, enable, processesRunning ) {
794 + function removeSubmitLoading( $object, enable, processesRunning ) {
795 + var loadingForm;
796 +
1297 797 if ( processesRunning > 0 ) {
1298 798 return;
1299 799 }
1300 800
1301 - document.querySelectorAll( '.frm_loading_form' ).forEach( function( form ) {
1302 - form.classList.remove( 'frm_loading_form', 'frm_loading_prev' );
1303 - jQuery( form ).trigger( 'frmEndFormLoading' );
801 + loadingForm = jQuery( '.frm_loading_form' );
802 + loadingForm.removeClass( 'frm_loading_form' );
803 + loadingForm.removeClass( 'frm_loading_prev' );
1304 804
1305 - if ( enable === 'enable' ) {
1306 - enableSubmitButton( form );
1307 - enableSaveDraft( form );
805 + loadingForm.trigger( 'frmEndFormLoading' );
806 +
807 + if ( enable === 'enable' ) {
808 + enableSubmitButton( loadingForm );
809 + enableSaveDraft( loadingForm );
810 + }
811 + }
812 +
813 + function showFileLoading( object ) {
814 + var fileval,
815 + loading = document.getElementById( 'frm_loading' );
816 + if ( loading !== null ) {
817 + fileval = jQuery( object ).find( 'input[type=file]' ).val();
818 + if ( typeof fileval !== 'undefined' && fileval !== '' ) {
819 + setTimeout( function() {
820 + jQuery( loading ).fadeIn( 'slow' );
821 + }, 2000 );
1308 822 }
1309 - } );
823 + }
1310 824 }
1311 825
1312 - function showFileLoading( object ) {
1313 - const loading = document.getElementById( 'frm_loading' );
1314 - if ( loading === null ) {
1315 - return;
826 + function clearDefault() {
827 + /*jshint validthis:true */
828 + toggleDefault( jQuery( this ), 'clear' );
829 + }
830 +
831 + function replaceDefault() {
832 + /*jshint validthis:true */
833 + toggleDefault( jQuery( this ), 'replace' );
834 + }
835 +
836 + function toggleDefault( $thisField, e ) {
837 + // TODO: Fix this for a default value that is a number or array
838 + var thisVal,
839 + v = $thisField.data( 'frmval' ).replace( /(\n|\r\n)/g, '\r' );
840 + if ( v === '' || typeof v === 'undefined' ) {
841 + return false;
1316 842 }
843 + thisVal = $thisField.val().replace( /(\n|\r\n)/g, '\r' );
1317 844
1318 - const fileInput = object.querySelector( 'input[type=file]' );
1319 - const fileval = fileInput ? fileInput.value : '';
1320 - if ( fileval !== '' ) {
1321 - setTimeout( function() {
1322 - jQuery( loading ).fadeIn( 'slow' ); // eslint-disable-line no-jquery/no-fade
1323 - }, 2000 );
845 + if ( 'replace' === e ) {
846 + if ( thisVal === '' ) {
847 + $thisField.addClass( 'frm_default' ).val( v );
848 + }
849 + } else if ( thisVal == v ) {
850 + $thisField.removeClass( 'frm_default' ).val( '' );
1324 851 }
1325 852 }
1326 853
854 + function resendEmail() {
855 + /*jshint validthis:true */
856 + var $link = jQuery( this ),
857 + entryId = this.getAttribute( 'data-eid' ),
858 + formId = this.getAttribute( 'data-fid' ),
859 + label = $link.find( '.frm_link_label' );
860 + if ( label.length < 1 ) {
861 + label = $link;
862 + }
863 + label.append( '<span class="frm-wait"></span>' );
864 +
865 + jQuery.ajax({
866 + type: 'POST',
867 + url: frm_js.ajax_url,
868 + data: {
869 + action: 'frm_entries_send_email',
870 + entry_id: entryId,
871 + form_id: formId,
872 + nonce: frm_js.nonce
873 + },
874 + success: function( msg ) {
875 + var admin = document.getElementById( 'wpbody' );
876 + if ( admin === null ) {
877 + label.html( msg );
878 + } else {
879 + label.html( '' );
880 + $link.after( msg );
881 + }
882 + }
883 + });
884 + return false;
885 + }
886 +
1327 887 /**********************************************
1328 888 * General Helpers
1329 889 *********************************************/
1330 890
@@ -1329,12 +889,109 @@
1329 889 *********************************************/
1330 890
1331 891 function confirmClick() {
1332 892 /*jshint validthis:true */
1333 - const message = this.dataset.frmconfirm;
893 + var message = jQuery( this ).data( 'frmconfirm' );
1334 894 return confirm( message );
1335 895 }
1336 896
897 + function toggleDiv() {
898 + /*jshint validthis:true */
899 + var div = jQuery( this ).data( 'frmtoggle' );
900 + if ( jQuery( div ).is( ':visible' ) ) {
901 + jQuery( div ).slideUp( 'fast' );
902 + } else {
903 + jQuery( div ).slideDown( 'fast' );
904 + }
905 + return false;
906 + }
907 +
908 + /**********************************************
909 + * Fallback functions
910 + *********************************************/
911 +
912 + function addIndexOfFallbackForIE8() {
913 + var len, from;
914 +
915 + if ( ! Array.prototype.indexOf ) {
916 + Array.prototype.indexOf = function( elt /*, from*/ ) {
917 + len = this.length >>> 0;
918 +
919 + from = Number( arguments[1]) || 0;
920 + from = ( from < 0 ) ? Math.ceil( from ) : Math.floor( from );
921 + if ( from < 0 ) {
922 + from += len;
923 + }
924 +
925 + for ( ; from < len; from++ ) {
926 + if ( from in this && this[from] === elt ) {
927 + return from;
928 + }
929 + }
930 + return -1;
931 + };
932 + }
933 + }
934 +
935 + function addTrimFallbackForIE8() {
936 + if ( typeof String.prototype.trim !== 'function' ) {
937 + String.prototype.trim = function() {
938 + return this.replace( /^\s+|\s+$/g, '' );
939 + };
940 + }
941 + }
942 +
943 + function addFilterFallbackForIE8() {
944 + var t, len, res, thisp, i, val;
945 +
946 + if ( ! Array.prototype.filter ) {
947 +
948 + Array.prototype.filter = function( fun /*, thisp */ ) {
949 +
950 + if ( this === void 0 || this === null ) {
951 + throw new TypeError();
952 + }
953 +
954 + t = Object( this );
955 + len = t.length >>> 0;
956 + if ( typeof fun !== 'function' ) {
957 + throw new TypeError();
958 + }
959 +
960 + res = [];
961 + thisp = arguments[1];
962 + for ( i = 0; i < len; i++ ) {
963 + if ( i in t ) {
964 + val = t[i]; // in case fun mutates this
965 + if ( fun.call( thisp, val, i, t ) ) {
966 + res.push( val );
967 + }
968 + }
969 + }
970 +
971 + return res;
972 + };
973 + }
974 + }
975 +
976 + function addKeysFallbackForIE8() {
977 + var keys, i;
978 +
979 + if ( ! Object.keys ) {
980 + Object.keys = function( obj ) {
981 + keys = [];
982 +
983 + for ( i in obj ) {
984 + if ( obj.hasOwnProperty( i ) ) {
985 + keys.push( i );
986 + }
987 + }
988 +
989 + return keys;
990 + };
991 + }
992 + }
993 +
1337 994 /**
1338 995 * Check for -webkit-box-shadow css value for input:-webkit-autofill selector.
1339 996 * If this is a match, the User is autofilling the input on a Webkit browser.
1340 997 * We want to delete the Honeypot field, otherwise it will get triggered as spam on autocomplete.
@@ -1339,12 +996,11 @@
1339 996 * If this is a match, the User is autofilling the input on a Webkit browser.
1340 997 * We want to delete the Honeypot field, otherwise it will get triggered as spam on autocomplete.
1341 998 */
1342 999 function onHoneypotFieldChange() {
1343 - /*jshint validthis:true */
1344 - const css = window.getComputedStyle( this ).boxShadow;
1345 - if ( css && css.match( /inset/ ) ) {
1346 - this.remove();
1000 + var css = jQuery( this ).css( 'box-shadow' );
1001 + if ( css.match( /inset/ ) ) {
1002 + this.parentNode.removeChild( this );
1347 1003 }
1348 1004 }
1349 1005
1350 1006 /**
@@ -1365,64 +1021,34 @@
1365 1021 if ( ! label ) {
1366 1022 return;
1367 1023 }
1368 1024
1369 - label.addEventListener( 'click', function() {
1025 + label.addEventListener( 'click', function( e ) {
1370 1026 inputsContainer.querySelector( '.frm_form_field:first-child input, .frm_form_field:first-child select, .frm_form_field:first-child textarea' ).focus();
1371 - } );
1372 - } );
1027 + });
1028 + });
1373 1029 }
1374 1030
1375 - /**
1376 - * Sets focus on a the first subfield of a combo field that has an error.
1377 - *
1378 - * @since 6.16.3
1379 - *
1380 - * @param {HTMLElement} element
1381 - * @return {boolean} True if the focus was set on a combo field.
1382 - */
1383 - function maybeFocusOnComboSubField( element ) {
1384 - if ( 'FIELDSET' !== element.nodeName ) {
1385 - return false;
1386 - }
1387 - if ( ! element.querySelector( '.frm_combo_inputs_container' ) ) {
1388 - return false;
1389 - }
1390 - const comboSubfield = element.querySelector( '[aria-invalid="true"]' );
1391 - if ( comboSubfield ) {
1392 - focusInput( comboSubfield );
1393 - return true;
1394 - }
1395 - return false;
1396 - }
1397 -
1398 1031 function checkForErrorsAndMaybeSetFocus() {
1399 - if ( ! frm_js.focus_first_error ) {
1400 - return;
1401 - }
1032 + var errors, element, timeoutCallback;
1402 1033
1403 - const errors = document.querySelectorAll( '.frm_form_field .frm_error' );
1034 + errors = document.querySelectorAll( '.frm_form_field .frm_error' );
1404 1035 if ( ! errors.length ) {
1405 1036 return;
1406 1037 }
1407 1038
1408 - let element = errors[ 0 ];
1409 - let timeoutCallback;
1039 + element = errors[0];
1410 1040 do {
1411 1041 element = element.previousSibling;
1412 - if ( [ 'input', 'select', 'textarea' ].includes( element.nodeName.toLowerCase() ) ) {
1413 - focusInput( element );
1042 + if ( -1 !== [ 'input', 'select', 'textarea' ].indexOf( element.nodeName.toLowerCase() ) ) {
1043 + element.focus();
1414 1044 break;
1415 1045 }
1416 1046
1417 - if ( maybeFocusOnComboSubField( element ) ) {
1418 - break;
1419 - }
1420 -
1421 - if ( element.classList !== undefined ) {
1047 + if ( 'undefined' !== typeof element.classList ) {
1422 1048 if ( element.classList.contains( 'html-active' ) ) {
1423 1049 timeoutCallback = function() {
1424 - const textarea = element.querySelector( 'textarea' );
1050 + var textarea = element.querySelector( 'textarea' );
1425 1051 if ( null !== textarea ) {
1426 1052 textarea.focus();
1427 1053 }
1428 1054 };
@@ -1429,14 +1055,8 @@
1429 1055 } else if ( element.classList.contains( 'tmce-active' ) ) {
1430 1056 timeoutCallback = function() {
1431 1057 tinyMCE.activeEditor.focus();
1432 1058 };
1433 - } else if ( element.classList.contains( 'frm_opt_container' ) ) {
1434 - const firstInput = element.querySelector( 'input' );
1435 - if ( firstInput ) {
1436 - focusInput( firstInput );
1437 - break;
1438 - }
1439 1059 }
1440 1060
1441 1061 if ( 'function' === typeof timeoutCallback ) {
1442 1062 setTimeout( timeoutCallback, 0 );
@@ -1445,303 +1065,32 @@
1445 1065 }
1446 1066 } while ( element.previousSibling );
1447 1067 }
1448 1068
1449 - /**
1450 - * Focus a visible input, or possibly delay the focus event until the form has faded in.
1451 - *
1452 - * @since 6.16.3
1453 - *
1454 - * @param {HTMLElement} input
1455 - * @return {void}
1456 - */
1457 - function focusInput( input ) {
1458 - if ( input.offsetParent !== null ) {
1459 - input.focus();
1460 - } else {
1461 - triggerCustomEvent( document, 'frmMaybeDelayFocus', { input } );
1462 - }
1463 - }
1069 + return {
1070 + init: function() {
1071 + jQuery( document ).off( 'submit.formidable', '.frm-show-form' );
1072 + jQuery( document ).on( 'submit.formidable', '.frm-show-form', frmFrontForm.submitForm );
1464 1073
1465 - /**
1466 - * Does the same as jQuery( document ).on( 'event', 'selector', handler ).
1467 - *
1468 - * @since 5.4
1469 - *
1470 - * @param {string} event Event name.
1471 - * @param {string} selector Selector.
1472 - * @param {Function} handler Handler.
1473 - * @param {boolean | Object} options Options to be added to `addEventListener()` method. Default is `false`.
1474 - */
1475 - function documentOn( event, selector, handler, options ) {
1476 - if ( options === undefined ) {
1477 - options = false;
1478 - }
1479 -
1480 - document.addEventListener( event, function( e ) {
1481 - let target;
1482 -
1483 - // loop parent nodes from the target to the delegation node.
1484 - for ( target = e.target; target && target != this; target = target.parentNode ) {
1485 - if ( target.matches && target.matches( selector ) ) {
1486 - handler.call( target, e );
1487 - break;
1074 + jQuery( '.frm-show-form input[onblur], .frm-show-form textarea[onblur]' ).each( function() {
1075 + if ( jQuery( this ).val() === '' ) {
1076 + jQuery( this ).trigger( 'blur' );
1488 1077 }
1489 - }
1490 - }, options );
1491 - }
1078 + });
1492 1079
1493 - function initFloatingLabels() {
1494 - const selector = '.frm-show-form .frm_inside_container input, .frm-show-form .frm_inside_container select, .frm-show-form .frm_inside_container textarea';
1495 - const floatClass = 'frm_label_float_top';
1080 + jQuery( document ).on( 'focus', '.frm_toggle_default', clearDefault );
1081 + jQuery( document ).on( 'blur', '.frm_toggle_default', replaceDefault );
1082 + jQuery( '.frm_toggle_default' ).trigger( 'blur' );
1496 1083
1497 - const checkFloatLabel = function( input ) {
1498 - const container = input.closest( '.frm_inside_container' );
1499 - if ( ! container ) {
1500 - return;
1501 - }
1084 + jQuery( document.getElementById( 'frm_resend_email' ) ).on( 'click', resendEmail );
1502 1085
1503 - const shouldFloatTop = input.value || document.activeElement === input;
1504 -
1505 - container.classList.toggle( floatClass, shouldFloatTop );
1506 -
1507 - if ( 'SELECT' === input.tagName ) {
1508 - const firstOpt = input.querySelector( 'option:first-child' );
1509 -
1510 - if ( shouldFloatTop ) {
1511 - if ( firstOpt.hasAttribute( 'data-label' ) ) {
1512 - firstOpt.textContent = firstOpt.getAttribute( 'data-label' );
1513 - firstOpt.removeAttribute( 'data-label' );
1514 - }
1515 - } else if ( firstOpt.textContent ) {
1516 - firstOpt.setAttribute( 'data-label', firstOpt.textContent );
1517 - firstOpt.textContent = '';
1518 - }
1519 - }
1520 - };
1521 -
1522 - const checkDropdownLabel = function() {
1523 - document.querySelectorAll( `.frm-show-form .frm_inside_container:not(.${ floatClass }) select` ).forEach( function( input ) {
1524 - const firstOpt = input.querySelector( 'option:first-child' );
1525 -
1526 - if ( firstOpt.textContent ) {
1527 - firstOpt.setAttribute( 'data-label', firstOpt.textContent );
1528 - firstOpt.textContent = '';
1529 - }
1530 - } );
1531 - };
1532 -
1533 - [ 'focus', 'blur', 'change' ].forEach( function( eventName ) {
1534 - documentOn(
1535 - eventName,
1536 - selector,
1537 - function( event ) {
1538 - checkFloatLabel( event.target );
1539 - },
1540 - true
1541 - );
1542 - } );
1543 -
1544 - const runOnLoad = function( firstLoad ) {
1545 - if ( firstLoad && document.activeElement && [ 'INPUT', 'SELECT', 'TEXTAREA' ].includes( document.activeElement.tagName ) ) {
1546 - checkFloatLabel( document.activeElement );
1547 - } else if ( firstLoad ) {
1548 - document.querySelectorAll( '.frm_inside_container' ).forEach(
1549 - function( container ) {
1550 - const input = container.querySelector( 'input, select, textarea' );
1551 - if ( input && '' !== input.value ) {
1552 - checkFloatLabel( input );
1553 - }
1554 - }
1555 - );
1556 - }
1557 -
1558 - checkDropdownLabel();
1559 - };
1560 -
1561 - runOnLoad( true );
1562 -
1563 - jQuery( document ).on( 'frmPageChanged', function( event ) {
1564 - runOnLoad();
1565 - } );
1566 -
1567 - document.addEventListener( 'frm_after_start_over', function( event ) {
1568 - runOnLoad();
1569 - } );
1570 - }
1571 -
1572 - function shouldUpdateValidityMessage( target ) {
1573 - if ( 'INPUT' !== target.nodeName ) {
1574 - return false;
1575 - }
1576 -
1577 - if ( ! target.dataset.invmsg ) {
1578 - return false;
1579 - }
1580 -
1581 - if ( 'text' !== target.getAttribute( 'type' ) ) {
1582 - return false;
1583 - }
1584 -
1585 - if ( target.classList.contains( 'frm_verify' ) ) {
1586 - return false;
1587 - }
1588 -
1589 - return true;
1590 - }
1591 -
1592 - function maybeClearCustomValidityMessage( event, field ) {
1593 - let key;
1594 - let isInvalid = false;
1595 -
1596 - if ( ! shouldUpdateValidityMessage( field ) ) {
1597 - return;
1598 - }
1599 -
1600 - for ( key in field.validity ) {
1601 - if ( 'customError' === key ) {
1602 - continue;
1603 - }
1604 - if ( 'valid' !== key && field.validity[ key ] === true ) {
1605 - isInvalid = true;
1606 - break;
1607 - }
1608 - }
1609 -
1610 - if ( ! isInvalid ) {
1611 - field.setCustomValidity( '' );
1612 - }
1613 - }
1614 -
1615 - function maybeShowNewTabFallbackMessage() {
1616 - if ( ! window.frmShowNewTabFallback ) {
1617 - return;
1618 - }
1619 -
1620 - const messageEl = document.querySelector( `#frm_form_${ frmShowNewTabFallback.formId }_container .frm_message` );
1621 - if ( ! messageEl ) {
1622 - return;
1623 - }
1624 -
1625 - messageEl.insertAdjacentHTML( 'beforeend', ` ${ frmShowNewTabFallback.message }` );
1626 - }
1627 -
1628 - function setCustomValidityMessage() {
1629 - const forms = document.getElementsByClassName( 'frm-show-form' );
1630 - const { length } = forms;
1631 -
1632 - for ( let index = 0; index < length; ++index ) {
1633 - forms[ index ].addEventListener(
1634 - 'invalid',
1635 - function( event ) {
1636 - const { target } = event;
1637 -
1638 - if ( shouldUpdateValidityMessage( target ) ) {
1639 - target.setCustomValidity( target.dataset.invmsg );
1640 - }
1641 - },
1642 - true
1643 - );
1644 - }
1645 - }
1646 -
1647 - function enableSubmitButtonOnBackButtonPress() {
1648 - window.addEventListener( 'pageshow', function( event ) {
1649 - if ( event.persisted ) {
1650 - document.querySelectorAll( '.frm_loading_form' ).forEach(
1651 - function( form ) {
1652 - enableSubmitButton( form );
1653 - }
1654 - );
1655 - removeSubmitLoading();
1656 - }
1657 - } );
1658 - }
1659 -
1660 - /**
1661 - * Destroys the formidable generated global hcaptcha object since it wouldn't otherwise render.
1662 - */
1663 - function destroyhCaptcha() {
1664 - if ( ! window.hasOwnProperty( 'hcaptcha' ) || ! document.querySelector( '.frm-show-form .h-captcha' ) ) {
1665 - return;
1666 - }
1667 - window.hcaptcha = null;
1668 - }
1669 -
1670 - /**
1671 - * @since 6.16.3
1672 - *
1673 - * @return {string} Unique key, used for duplicate checks.
1674 - */
1675 - function getUniqueKey() {
1676 - const uniqueKey = Array.from( window.crypto.getRandomValues( new Uint8Array( 8 ) ) )
1677 - .map( b => b.toString( 16 ).padStart( 2, '0' ) )
1678 - .join( '' );
1679 - const timestamp = Date.now().toString( 16 );
1680 - return `${ uniqueKey }-${ timestamp }`;
1681 - }
1682 -
1683 - /**
1684 - * Animates the scroll position of the document.
1685 - *
1686 - * @since 6.20
1687 - *
1688 - * @param {number} start
1689 - * @param {number} end
1690 - * @param {number} duration
1691 - * @return {void}
1692 - */
1693 - function animateScroll( start, end, duration ) {
1694 - if ( ! window.hasOwnProperty( 'performance' ) || ! window.hasOwnProperty( 'requestAnimationFrame' ) ) {
1695 - document.documentElement.scrollTop = end;
1696 - return;
1697 - }
1698 -
1699 - const startTime = performance.now();
1700 - const step = currentTime => {
1701 - const progress = Math.min( ( currentTime - startTime ) / duration, 1 );
1702 - document.documentElement.scrollTop = start + ( ( end - start ) * progress );
1703 - if ( progress < 1 ) {
1704 - requestAnimationFrame( step );
1705 - }
1706 - };
1707 - requestAnimationFrame( step );
1708 - }
1709 -
1710 - /**
1711 - * Make sure that the captcha label for a reCAPTCHA or Turnstile field matches the response input ID.
1712 - * This is determined dynamically, so we check for the ID after the input is rendered.
1713 - * hCaptcha is handled separately, in the frmCaptcha function as it is not rendered explicitly.
1714 - *
1715 - * @since 6.25.1
1716 - *
1717 - * @param {HTMLElement} captcha
1718 - * @return {void}
1719 - */
1720 - function maybeFixCaptchaLabel( captcha ) {
1721 - const form = captcha.closest( 'form' );
1722 - if ( ! form ) {
1723 - return;
1724 - }
1725 -
1726 - const label = form.querySelector( 'label[for="g-recaptcha-response"], label[for="cf-turnstile-response"]' );
1727 - const captchaResponse = form.querySelector( '[name="g-recaptcha-response"], [name="cf-turnstile-response"]' );
1728 -
1729 - if ( label && captchaResponse ) {
1730 - label.htmlFor = captchaResponse.id;
1731 - }
1732 - }
1733 -
1734 - return {
1735 - init() {
1736 - jQuery( document ).off( 'submit.formidable', '.frm-show-form' );
1737 - jQuery( document ).on( 'submit.formidable', '.frm-show-form', frmFrontForm.submitForm );
1738 -
1739 1086 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 );
1087 + jQuery( document ).on( 'change keyup', '.frm-show-form .frm_inside_container input, .frm-show-form .frm_inside_container select, .frm-show-form .frm_inside_container textarea', maybeShowLabel );
1740 1088
1741 - jQuery( document ).on( 'change', '.frm_verify[id^=field_]', onHoneypotFieldChange );
1089 + jQuery( document ).on( 'change', '[id^=frm_email_]', onHoneypotFieldChange );
1742 1090
1743 1091 jQuery( document ).on( 'click', 'a[data-frmconfirm]', confirmClick );
1092 + jQuery( 'a[data-frmtoggle]' ).on( 'click', toggleDiv );
1744 1093
1745 1094 checkForErrorsAndMaybeSetFocus();
1746 1095
1747 1096 // Focus on the first sub field when clicking to the primary label of combo field.
@@ -1746,123 +1095,102 @@
1746 1095
1747 1096 // Focus on the first sub field when clicking to the primary label of combo field.
1748 1097 changeFocusWhenClickComboFieldLabel();
1749 1098
1750 - initFloatingLabels();
1751 - maybeShowNewTabFallbackMessage();
1099 + // Add fallbacks for the beloved IE8
1100 + addIndexOfFallbackForIE8();
1101 + addTrimFallbackForIE8();
1102 + addFilterFallbackForIE8();
1103 + addKeysFallbackForIE8();
1104 + },
1752 1105
1753 - jQuery( document ).on( 'frmAfterAddRow', setCustomValidityMessage );
1754 - setCustomValidityMessage();
1755 - jQuery( document ).on( 'frmFieldChanged', maybeClearCustomValidityMessage );
1756 -
1757 - setSelectPlaceholderColor();
1758 -
1759 - // Elementor popup show event. Fix Elementor Popup && FF Captcha field conflicts
1760 - jQuery( document ).on( 'elementor/popup/show', frmRecaptcha );
1761 -
1762 - enableSubmitButtonOnBackButtonPress();
1763 - jQuery( document ).on(
1764 - 'frmPageChanged',
1765 - destroyhCaptcha
1766 - );
1106 + getFieldId: function( field, fullID ) {
1107 + return getFieldId( field, fullID );
1767 1108 },
1768 1109
1769 - getFieldId,
1110 + renderRecaptcha: function( captcha ) {
1111 + var formID, recaptchaID,
1112 + size = captcha.getAttribute( 'data-size' ),
1113 + rendered = captcha.getAttribute( 'data-rid' ) !== null,
1114 + params = {
1115 + 'sitekey': captcha.getAttribute( 'data-sitekey' ),
1116 + 'size': size,
1117 + 'theme': captcha.getAttribute( 'data-theme' )
1118 + };
1770 1119
1771 - /**
1772 - * Render a captcha field.
1773 - *
1774 - * @param {HTMLElement} captcha
1775 - * @param {string} captchaSelector
1776 - * @return {void}
1777 - */
1778 - renderCaptcha( captcha, captchaSelector ) {
1779 - const rendered = captcha.getAttribute( 'data-rid' ) !== null;
1780 1120 if ( rendered ) {
1781 1121 return;
1782 1122 }
1783 1123
1784 - const size = captcha.getAttribute( 'data-size' );
1785 - const params = {
1786 - sitekey: captcha.getAttribute( 'data-sitekey' ),
1787 - size,
1788 - theme: captcha.getAttribute( 'data-theme' )
1789 - };
1790 -
1791 1124 if ( size === 'invisible' ) {
1792 - const formID = captcha.closest( 'form' )?.querySelector( 'input[name="form_id"]' )?.value;
1793 -
1794 - const captchaLabel = captcha.closest( '.frm_form_field' )?.querySelector( '.frm_primary_label' );
1795 - if ( captchaLabel ) {
1796 - captchaLabel.style.display = 'none';
1797 - }
1798 -
1125 + formID = jQuery( captcha ).closest( 'form' ).find( 'input[name="form_id"]' ).val();
1126 + jQuery( captcha ).closest( '.frm_form_field .frm_primary_label' ).hide();
1799 1127 params.callback = function( token ) {
1800 1128 frmFrontForm.afterRecaptcha( token, formID );
1801 1129 };
1802 1130 }
1803 1131
1804 - const activeCaptcha = getSelectedCaptcha( captchaSelector );
1805 - const captchaContainer = typeof turnstile !== 'undefined' && turnstile === activeCaptcha ? `#${ captcha.id }` : captcha.id;
1806 - const captchaID = activeCaptcha.render( captchaContainer, params );
1132 + recaptchaID = grecaptcha.render( captcha.id, params );
1807 1133
1808 - captcha.setAttribute( 'data-rid', captchaID );
1809 -
1810 - maybeFixCaptchaLabel( captcha );
1134 + captcha.setAttribute( 'data-rid', recaptchaID );
1811 1135 },
1812 1136
1813 - afterSingleRecaptcha() {
1814 - const recaptcha = document.querySelector( '.frm-show-form .g-recaptcha' );
1815 - const object = recaptcha ? recaptcha.closest( 'form' ) : null;
1137 + afterSingleRecaptcha: function() {
1138 + var object = jQuery( '.frm-show-form .g-recaptcha' ).closest( 'form' )[0];
1816 1139 frmFrontForm.submitFormNow( object );
1817 1140 },
1818 1141
1819 - afterRecaptcha( _, formID ) {
1820 - const object = document.querySelector( `#frm_form_${ formID }_container form` );
1142 + afterRecaptcha: function( token, formID ) {
1143 + var object = jQuery( '#frm_form_' + formID + '_container form' )[0];
1821 1144 frmFrontForm.submitFormNow( object );
1822 1145 },
1823 1146
1824 - submitForm( e ) {
1147 + submitForm: function( e ) {
1825 1148 frmFrontForm.submitFormManual( e, this );
1826 1149 },
1827 1150
1828 - /**
1829 - * @param {Event} e
1830 - * @param {HTMLElement} object The form object that is being submitted.
1831 - * @return {void}
1832 - */
1833 - submitFormManual( e, object ) {
1834 - if ( document.body.classList.contains( 'wp-admin' ) && ! object.closest( '.frmapi-form' ) ) {
1151 + submitFormManual: function( e, object ) {
1152 + var isPro, errors,
1153 + invisibleRecaptcha = hasInvisibleRecaptcha( object ),
1154 + classList = object.className.trim().split( /\s+/gi );
1155 +
1156 + if ( classList && invisibleRecaptcha.length < 1 ) {
1157 + isPro = classList.indexOf( 'frm_pro_form' ) > -1;
1158 + if ( ! isPro ) {
1159 + return;
1160 + }
1161 + }
1162 +
1163 + if ( jQuery( 'body' ).hasClass( 'wp-admin' ) && jQuery( object ).closest( '.frmapi-form' ).length < 1 ) {
1835 1164 return;
1836 1165 }
1837 1166
1838 1167 e.preventDefault();
1839 1168
1840 - if ( typeof frmProForm !== 'undefined' && typeof frmProForm.submitAllowed === 'function' && ! frmProForm.submitAllowed( object ) ) {
1841 - return;
1169 + if ( typeof frmProForm !== 'undefined' && typeof frmProForm.submitAllowed === 'function' ) {
1170 + if ( ! frmProForm.submitAllowed( object ) ) {
1171 + return;
1172 + }
1842 1173 }
1843 1174
1844 - const errors = frmFrontForm.validateFormSubmit( object );
1845 - if ( Object.keys( errors ).length !== 0 ) {
1846 - return;
1847 - }
1848 -
1849 - const invisibleRecaptcha = hasInvisibleRecaptcha( object );
1850 -
1851 - if ( invisibleRecaptcha ) {
1175 + if ( invisibleRecaptcha.length ) {
1852 1176 showLoadingIndicator( jQuery( object ) );
1853 1177 executeInvisibleRecaptcha( invisibleRecaptcha );
1854 1178 } else {
1855 - showSubmitLoading( jQuery( object ) );
1856 1179
1857 - frmFrontForm.submitFormNow( object );
1180 + errors = frmFrontForm.validateFormSubmit( object );
1181 +
1182 + if ( Object.keys( errors ).length === 0 ) {
1183 + showSubmitLoading( jQuery( object ) );
1184 +
1185 + frmFrontForm.submitFormNow( object, classList );
1186 + }
1858 1187 }
1859 1188 },
1860 1189
1861 - submitFormNow( object ) {
1862 - let hasFileFields;
1863 - let antispamInput;
1864 - const classList = object.className.trim().split( /\s+/gi );
1190 + submitFormNow: function( object ) {
1191 + var hasFileFields, antispamInput,
1192 + classList = object.className.trim().split( /\s+/gi );
1865 1193
1866 1194 if ( object.hasAttribute( 'data-token' ) && null === object.querySelector( '[name="antispam_token"]' ) ) {
1867 1195 // include the antispam token on form submit.
1868 1196 antispamInput = document.createElement( 'input' );
@@ -1868,24 +1196,17 @@
1868 1196 antispamInput = document.createElement( 'input' );
1869 1197 antispamInput.type = 'hidden';
1870 1198 antispamInput.name = 'antispam_token';
1871 1199 antispamInput.value = object.getAttribute( 'data-token' );
1872 - object.append( antispamInput );
1200 + object.appendChild( antispamInput );
1873 1201 }
1874 1202
1875 - // Add a unique ID, used for duplicate checks.
1876 - const uniqueIDInput = document.createElement( 'input' );
1877 - uniqueIDInput.type = 'hidden';
1878 - uniqueIDInput.name = 'unique_id';
1879 - uniqueIDInput.value = getUniqueKey();
1880 - object.append( uniqueIDInput );
1881 -
1882 - if ( classList.includes( 'frm_ajax_submit' ) ) {
1883 - const fileInputs = object.querySelectorAll( 'input[type="file"]' );
1884 - hasFileFields = Array.from( fileInputs ).filter( input => !! input.value ).length;
1203 + if ( classList.indexOf( 'frm_ajax_submit' ) > -1 ) {
1204 + hasFileFields = jQuery( object ).find( 'input[type="file"]' ).filter( function() {
1205 + return !! this.value;
1206 + }).length;
1885 1207 if ( hasFileFields < 1 ) {
1886 - const actionInput = object.querySelector( 'input[name="frm_action"]' );
1887 - const action = actionInput ? actionInput.value : '';
1208 + action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1888 1209 frmFrontForm.checkFormErrors( object, action );
1889 1210 } else {
1890 1211 object.submit();
1891 1212 }
@@ -1893,16 +1214,10 @@
1893 1214 object.submit();
1894 1215 }
1895 1216 },
1896 1217
1897 - /**
1898 - * @param {HTMLElement|Object} object Form object. This might be a jQuery object.
1899 - *
1900 - * @return {Array} List of errors.
1901 - */
1902 - validateFormSubmit( object ) {
1903 - const form = object instanceof jQuery ? object.get( 0 ) : object;
1904 - if ( typeof tinyMCE !== 'undefined' && form && form.querySelector( '.wp-editor-wrap' ) ) {
1218 + validateFormSubmit: function( object ) {
1219 + if ( typeof tinyMCE !== 'undefined' && jQuery( object ).find( '.wp-editor-wrap' ).length ) {
1905 1220 tinyMCE.triggerSave();
1906 1221 }
1907 1222
1908 1223 jsErrors = [];
@@ -1917,23 +1232,16 @@
1917 1232
1918 1233 return jsErrors;
1919 1234 },
1920 1235
1921 - /**
1922 - * @param {HTMLElement|Object} object Form object. This might be a jQuery object.
1923 - * @return {Array} List of errors.
1924 - */
1925 - getAjaxFormErrors( object ) {
1926 - let customErrors;
1927 - let key;
1928 - const form = object instanceof jQuery ? object.get( 0 ) : object;
1236 + getAjaxFormErrors: function( object ) {
1237 + var customErrors, key;
1929 1238
1930 1239 jsErrors = validateForm( object );
1931 1240 if ( typeof frmThemeOverride_jsErrors === 'function' ) { // eslint-disable-line camelcase
1932 - const actionInput = form ? form.querySelector( 'input[name="frm_action"]' ) : null;
1933 - const action = actionInput ? actionInput.value : '';
1241 + action = jQuery( object ).find( 'input[name="frm_action"]' ).val();
1934 1242 customErrors = frmThemeOverride_jsErrors( action, object );
1935 - if ( Object.keys( customErrors ).length ) {
1243 + if ( Object.keys( customErrors ).length ) {
1936 1244 for ( key in customErrors ) {
1937 1245 jsErrors[ key ] = customErrors[ key ];
1938 1246 }
1939 1247 }
@@ -1938,30 +1246,20 @@
1938 1246 }
1939 1247 }
1940 1248 }
1941 1249
1942 - triggerCustomEvent( document, 'frm_get_ajax_form_errors', {
1943 - formEl: object,
1944 - errors: jsErrors
1945 - } );
1946 -
1947 1250 return jsErrors;
1948 1251 },
1949 1252
1950 - /**
1951 - * @param {HTMLElement|Object} object Form object. This might be a jQuery object.
1952 - * @return {void}
1953 - */
1954 - addAjaxFormErrors( object ) {
1955 - let key;
1956 - const form = object instanceof jQuery ? object.get( 0 ) : object;
1253 + addAjaxFormErrors: function( object ) {
1254 + var key, $fieldCont;
1957 1255 removeAllErrors();
1958 1256
1959 1257 for ( key in jsErrors ) {
1960 - const fieldCont = form ? form.querySelector( `#frm_field_${ key }_container` ) : null;
1258 + $fieldCont = jQuery( object ).find( '#frm_field_' + key + '_container' );
1961 1259
1962 - if ( fieldCont ) {
1963 - addFieldError( fieldCont, key, jsErrors );
1260 + if ( $fieldCont.length ) {
1261 + addFieldError( $fieldCont, key, jsErrors );
1964 1262 } else {
1965 1263 // we are unable to show the error, so remove it
1966 1264 delete jsErrors[ key ];
1967 1265 }
@@ -1970,33 +1268,39 @@
1970 1268 scrollToFirstField( object );
1971 1269 checkForErrorsAndMaybeSetFocus();
1972 1270 },
1973 1271
1974 - checkFormErrors: getFormErrors,
1975 - checkRequiredField,
1976 - showSubmitLoading,
1977 - removeSubmitLoading,
1272 + checkFormErrors: function( object, action ) {
1273 + getFormErrors( object, action );
1274 + },
1978 1275
1979 - scrollToID( id ) {
1980 - const object = jQuery( document.getElementById( id ) );
1981 - frmFrontForm.scrollMsg( object, false );
1276 + checkRequiredField: function( field, errors ) {
1277 + return checkRequiredField( field, errors );
1982 1278 },
1983 1279
1984 - scrollMsg( id, object, animate ) {
1985 - let newPos;
1986 - let screenTop;
1987 - let screenBottom;
1988 - let scrollObj = '';
1280 + showSubmitLoading: function( $object ) {
1281 + showSubmitLoading( $object );
1282 + },
1989 1283
1990 - if ( object === undefined ) {
1991 - scrollObj = jQuery( document.getElementById( `frm_form_${ id }_container` ) );
1284 + removeSubmitLoading: function( $object, enable, processesRunning ) {
1285 + removeSubmitLoading( $object, enable, processesRunning );
1286 + },
1287 +
1288 + scrollToID: function( id ) {
1289 + var object = jQuery( document.getElementById( id ) );
1290 + frmFrontForm.scrollMsg( object, false );
1291 + },
1292 +
1293 + scrollMsg: function( id, object, animate ) {
1294 + var newPos, m, b, screenTop, screenBottom,
1295 + scrollObj = '';
1296 + if ( typeof object === 'undefined' ) {
1297 + scrollObj = jQuery( document.getElementById( 'frm_form_' + id + '_container' ) );
1992 1298 if ( scrollObj.length < 1 ) {
1993 1299 return;
1994 1300 }
1995 1301 } else if ( typeof id === 'string' ) {
1996 - const formEl = object instanceof jQuery ? object.get( 0 ) : object;
1997 - const fieldEl = formEl ? formEl.querySelector( `#frm_field_${ id }_container` ) : null;
1998 - scrollObj = fieldEl ? jQuery( fieldEl ) : jQuery();
1302 + scrollObj = jQuery( object ).find( '#frm_field_' + id + '_container' );
1999 1303 } else {
2000 1304 scrollObj = id;
2001 1305 }
2002 1306
@@ -2006,12 +1310,12 @@
2006 1310 return;
2007 1311 }
2008 1312 newPos = newPos - frm_js.offset;
2009 1313
2010 - const docMarginTop = getComputedStyle( document.documentElement ).marginTop;
2011 - const bodyMarginTop = getComputedStyle( document.body ).marginTop;
2012 - if ( docMarginTop || bodyMarginTop ) {
2013 - newPos = newPos - parseInt( docMarginTop ) - parseInt( bodyMarginTop );
1314 + m = jQuery( 'html' ).css( 'margin-top' );
1315 + b = jQuery( 'body' ).css( 'margin-top' );
1316 + if ( m || b ) {
1317 + newPos = newPos - parseInt( m ) - parseInt( b );
2014 1318 }
2015 1319
2016 1320 if ( newPos && window.innerHeight ) {
2017 1321 screenTop = document.documentElement.scrollTop || document.body.scrollTop;
@@ -2018,12 +1322,12 @@
2018 1322 screenBottom = screenTop + window.innerHeight;
2019 1323
2020 1324 if ( newPos > screenBottom || newPos < screenTop ) {
2021 1325 // Not in view
2022 - if ( animate === undefined ) {
2023 - document.documentElement.scrollTop = newPos;
1326 + if ( typeof animate === 'undefined' ) {
1327 + jQuery( window ).scrollTop( newPos );
2024 1328 } else {
2025 - animateScroll( screenTop, newPos, 500 );
1329 + jQuery( 'html,body' ).animate({ scrollTop: newPos }, 500 );
2026 1330 }
2027 1331 return false;
2028 1332 }
2029 1333 }
@@ -2028,13 +1332,13 @@
2028 1332 }
2029 1333 }
2030 1334 },
2031 1335
2032 - fieldValueChanged( e ) {
1336 + fieldValueChanged: function( e ) {
2033 1337 /*jshint validthis:true */
2034 1338
2035 - const fieldId = frmFrontForm.getFieldId( this, false );
2036 - if ( ! fieldId ) {
1339 + var fieldId = frmFrontForm.getFieldId( this, false );
1340 + if ( ! fieldId || typeof fieldId === 'undefined' ) {
2037 1341 return;
2038 1342 }
2039 1343
2040 1344 if ( e.frmTriggered && e.frmTriggered == fieldId ) {
@@ -2040,17 +1344,63 @@
2040 1344 if ( e.frmTriggered && e.frmTriggered == fieldId ) {
2041 1345 return;
2042 1346 }
2043 1347
2044 - jQuery( document ).trigger( 'frmFieldChanged', [ this, fieldId, e ] );
1348 + jQuery( document ).trigger( 'frmFieldChanged', [ this, fieldId, e ]);
2045 1349
2046 1350 if ( e.selfTriggered !== true ) {
2047 - maybeValidateChange( this );
1351 + maybeValidateChange( fieldId, this );
2048 1352 }
2049 1353 },
2050 1354
2051 - escapeHtml( text ) {
2052 - console.warn( 'DEPRECATED: function frmFrontForm.escapeHtml in v6.17' );
1355 + savingDraft: function( object ) {
1356 + console.warn( 'DEPRECATED: function frmFrontForm.savingDraft in v3.0 use frmProForm.savingDraft' );
1357 + if ( typeof frmProForm !== 'undefined' ) {
1358 + return frmProForm.savingDraft( object );
1359 + }
1360 + },
1361 +
1362 + goingToPreviousPage: function( object ) {
1363 + console.warn( 'DEPRECATED: function frmFrontForm.goingToPreviousPage in v3.0 use frmProForm.goingToPreviousPage' );
1364 + if ( typeof frmProForm !== 'undefined' ) {
1365 + return frmProForm.goingToPreviousPage( object );
1366 + }
1367 + },
1368 +
1369 + hideOrShowFields: function() {
1370 + console.warn( 'DEPRECATED: function frmFrontForm.hideOrShowFields in v3.0 use frmProForm.hideOrShowFields' );
1371 + if ( typeof frmProForm !== 'undefined' ) {
1372 + frmProForm.hideOrShowFields();
1373 + }
1374 + },
1375 +
1376 + hidePreviouslyHiddenFields: function() {
1377 + console.warn( 'DEPRECATED: function frmFrontForm.hidePreviouslyHiddenFields in v3.0 use frmProForm.hidePreviouslyHiddenFields' );
1378 + if ( typeof frmProForm !== 'undefined' ) {
1379 + frmProForm.hidePreviouslyHiddenFields();
1380 + }
1381 + },
1382 +
1383 + checkDependentDynamicFields: function( ids ) {
1384 + console.warn( 'DEPRECATED: function frmFrontForm.checkDependentDynamicFields in v3.0 use frmProForm.checkDependentDynamicFields' );
1385 + if ( typeof frmProForm !== 'undefined' ) {
1386 + frmProForm.checkDependentDynamicFields( ids );
1387 + }
1388 + },
1389 +
1390 + checkDependentLookupFields: function( ids ) {
1391 + console.warn( 'DEPRECATED: function frmFrontForm.checkDependentLookupFields in v3.0 use frmProForm.checkDependentLookupFields' );
1392 + if ( typeof frmProForm !== 'undefined' ) {
1393 + frmProForm.checkDependentLookupFields( ids );
1394 + }
1395 + },
1396 +
1397 + loadGoogle: function() {
1398 + console.warn( 'DEPRECATED: function frmFrontForm.loadGoogle in v3.0 use frmProForm.loadGoogle' );
1399 + frmProForm.loadGoogle();
1400 + },
1401 +
1402 + escapeHtml: function( text ) {
2053 1403 return text
2054 1404 .replace( /&/g, '&amp;' )
2055 1405 .replace( /</g, '&lt;' )
2056 1406 .replace( />/g, '&gt;' )
@@ -2057,82 +1407,97 @@
2057 1407 .replace( /"/g, '&quot;' )
2058 1408 .replace( /'/g, '&#039;' );
2059 1409 },
2060 1410
2061 - triggerCustomEvent,
2062 - documentOn
1411 + invisible: function( classes ) {
1412 + jQuery( classes ).css( 'visibility', 'hidden' );
1413 + },
1414 +
1415 + visible: function( classes ) {
1416 + jQuery( classes ).css( 'visibility', 'visible' );
1417 + }
2063 1418 };
2064 1419 }
1420 +frmFrontForm = frmFrontFormJS();
2065 1421
2066 -window.frmFrontForm = frmFrontFormJS();
2067 -
2068 1422 jQuery( document ).ready( function() {
2069 1423 frmFrontForm.init();
2070 -} );
1424 +});
2071 1425
2072 1426 function frmRecaptcha() {
2073 - frmCaptcha( '.frm-g-recaptcha' );
1427 + var c, cl,
1428 + captchas = jQuery( '.frm-g-recaptcha' );
1429 + for ( c = 0, cl = captchas.length; c < cl; c++ ) {
1430 + frmFrontForm.renderRecaptcha( captchas[c]);
1431 + }
2074 1432 }
2075 1433
2076 -function frmHcaptcha() {
2077 - frmCaptcha( '.h-captcha' );
1434 +function frmAfterRecaptcha( token ) {
1435 + frmFrontForm.afterSingleRecaptcha( token );
2078 1436 }
2079 1437
2080 -function frmTurnstile() {
2081 - frmCaptcha( '.frm-cf-turnstile' );
1438 +function frmUpdateField( entryId, fieldId, value, message, num ) {
1439 + jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).html( '<span class="frm-loading-img"></span>' );
1440 + jQuery.ajax({
1441 + type: 'POST',
1442 + url: frm_js.ajax_url,
1443 + data: {
1444 + action: 'frm_entries_update_field_ajax',
1445 + entry_id: entryId,
1446 + field_id: fieldId,
1447 + value: value,
1448 + nonce: frm_js.nonce
1449 + },
1450 + success: function() {
1451 + if ( message.replace( /^\s+|\s+$/g, '' ) === '' ) {
1452 + jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).fadeOut( 'slow' );
1453 + } else {
1454 + jQuery( document.getElementById( 'frm_update_field_' + entryId + '_' + fieldId + '_' + num ) ).replaceWith( message );
1455 + }
1456 + }
1457 + });
2082 1458 }
2083 1459
2084 -function frmCaptcha( captchaSelector ) {
2085 - if ( '.h-captcha' === captchaSelector ) {
2086 - // hCaptcha is still rendered implicitly, so we only want to handle the label and exit early.
2087 - // Match the hcaptcha labels to the hcaptcha response fields.
2088 - const captchaLabels = document.querySelectorAll( 'label[for="h-captcha-response"]' );
2089 - if ( captchaLabels.length ) {
2090 - captchaLabels.forEach( label => {
2091 - const captchaResponse = label.closest( 'form' )?.querySelector( '[name="h-captcha-response"]' );
2092 - if ( captchaResponse ) {
2093 - label.htmlFor = captchaResponse.id;
2094 - }
2095 - } );
1460 +function frmDeleteEntry( entryId, prefix ) {
1461 + console.warn( 'DEPRECATED: function frmDeleteEntry in v2.0.13 use frmFrontForm.deleteEntry' );
1462 + jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( '<span class="frm-loading-img" id="frm_delete_' + entryId + '"></span>' );
1463 + jQuery.ajax({
1464 + type: 'POST',
1465 + url: frm_js.ajax_url,
1466 + data: {
1467 + action: 'frm_entries_destroy',
1468 + entry: entryId,
1469 + nonce: frm_js.nonce
1470 + },
1471 + success: function( html ) {
1472 + if ( html.replace( /^\s+|\s+$/g, '' ) === 'success' ) {
1473 + jQuery( document.getElementById( prefix + entryId ) ).fadeOut( 'slow' );
1474 + } else {
1475 + jQuery( document.getElementById( 'frm_delete_' + entryId ) ).replaceWith( html );
1476 + }
2096 1477 }
2097 - return;
2098 - }
2099 -
2100 - let c;
2101 - const captchas = document.querySelectorAll( captchaSelector );
2102 - const cl = captchas.length;
2103 - for ( c = 0; c < cl; c++ ) {
2104 - const closestForm = captchas[ c ].closest( 'form' );
2105 - const formIsVisible = closestForm && closestForm.offsetParent !== null;
2106 - const captcha = captchas[ c ];
2107 - if ( ! formIsVisible ) {
2108 - // If the form is not visible, try again later in 400ms.
2109 - // This fixes issues where the form fades visible on page load.
2110 - // Or whne the form is inside of a modal.
2111 - const interval = setInterval(
2112 - function() {
2113 - if ( closestForm && closestForm.offsetParent !== null ) {
2114 - frmFrontForm.renderCaptcha( captcha, captchaSelector );
2115 - clearInterval( interval );
2116 - }
2117 - },
2118 - 400
2119 - );
2120 - continue;
2121 - }
2122 - frmFrontForm.renderCaptcha( captcha, captchaSelector );
2123 - }
1478 + });
2124 1479 }
2125 1480
2126 -function getSelectedCaptcha( captchaSelector ) {
2127 - if ( captchaSelector === '.frm-g-recaptcha' ) {
2128 - return grecaptcha;
2129 - }
2130 - if ( document.querySelector( '.frm-cf-turnstile' ) ) {
2131 - return turnstile;
2132 - }
2133 - return hcaptcha;
1481 +function frmOnSubmit( e ) {
1482 + console.warn( 'DEPRECATED: function frmOnSubmit in v2.0 use frmFrontForm.submitForm' );
1483 + frmFrontForm.submitForm( e, this );
2134 1484 }
2135 1485
2136 -function frmAfterRecaptcha( token ) {
2137 - frmFrontForm.afterSingleRecaptcha( token );
1486 +function frm_resend_email( entryId, formId ) { // eslint-disable-line camelcase
1487 + var $link = jQuery( document.getElementById( 'frm_resend_email' ) );
1488 + console.warn( 'DEPRECATED: function frm_resend_email in v2.0' );
1489 + $link.append( '<span class="spinner" style="display:inline"></span>' );
1490 + jQuery.ajax({
1491 + type: 'POST',
1492 + url: frm_js.ajax_url,
1493 + data: {
1494 + action: 'frm_entries_send_email',
1495 + entry_id: entryId,
1496 + form_id: formId,
1497 + nonce: frm_js.nonce
1498 + },
1499 + success: function( msg ) {
1500 + $link.replaceWith( msg );
1501 + }
1502 + });
2138 1503 }