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