| @@ -201,11 +201,12 @@ | ||
| 201 | 201 | $form.find( 'a.frm_save_draft' ).css( 'pointer-events', '' ); |
| 202 | 202 | } |
| 203 | 203 | |
| 204 | 204 | function validateForm( object ) { |
| 205 | - var r, rl, n, nl, fields, field, value, requiredFields, | |
| 206 | - errors = []; | |
| 205 | + var errors, r, rl, n, nl, fields, field, requiredFields; | |
| 207 | 206 | |
| 207 | + errors = []; | |
| 208 | + | |
| 208 | 209 | // Make sure required text field is filled in |
| 209 | 210 | requiredFields = jQuery( object ).find( |
| 210 | 211 | '.frm_required_field:visible input, .frm_required_field:visible select, .frm_required_field:visible textarea' |
| 211 | 212 | ).filter( ':not(.frm_optional)' ); |
| @@ -222,11 +223,18 @@ | ||
| 222 | 223 | fields = jQuery( object ).find( 'input,select,textarea' ); |
| 223 | 224 | if ( fields.length ) { |
| 224 | 225 | for ( n = 0, nl = fields.length; n < nl; n++ ) { |
| 225 | 226 | field = fields[n]; |
| 226 | - if ( '' !== field.value ) { | |
| 227 | - validateFieldValue( field, errors ); | |
| 227 | + if ( '' === field.value ) { | |
| 228 | + if ( 'number' === field.type ) { | |
| 229 | + // A number field will return an empty string when it is invalid. | |
| 230 | + checkValidity( field, errors ); | |
| 231 | + } | |
| 232 | + continue; | |
| 228 | 233 | } |
| 234 | + | |
| 235 | + validateFieldValue( field, errors ); | |
| 236 | + checkValidity( field, errors ); | |
| 229 | 237 | } |
| 230 | 238 | } |
| 231 | 239 | |
| 232 | 240 | errors = validateRecaptcha( object, errors ); |
| @@ -234,8 +242,33 @@ | ||
| 234 | 242 | return errors; |
| 235 | 243 | } |
| 236 | 244 | |
| 237 | 245 | /** |
| 246 | + * Check the ValidityState interface for the field. | |
| 247 | + * If it is invalid, show an error for it. | |
| 248 | + * | |
| 249 | + * @param {HTMLElement} field | |
| 250 | + * @param {Array} errors | |
| 251 | + * @returns | |
| 252 | + */ | |
| 253 | + function checkValidity( field, errors ) { | |
| 254 | + var fieldID; | |
| 255 | + if ( 'object' !== typeof field.validity || false !== field.validity.valid ) { | |
| 256 | + return; | |
| 257 | + } | |
| 258 | + | |
| 259 | + fieldID = getFieldId( field, true ); | |
| 260 | + if ( 'undefined' === typeof errors[ fieldID ]) { | |
| 261 | + errors[ fieldID ] = getFieldValidationMessage( field, 'data-invmsg' ); | |
| 262 | + } | |
| 263 | + | |
| 264 | + if ( 'function' === typeof field.reportValidity ) { | |
| 265 | + // This triggers an error pop up. | |
| 266 | + field.reportValidity(); | |
| 267 | + } | |
| 268 | + } | |
| 269 | + | |
| 270 | + /** | |
| 238 | 271 | * @since 5.0.10 |
| 239 | 272 | * |
| 240 | 273 | * @param {object} element |
| 241 | 274 | * @param {string} targetClass |
| @@ -490,8 +523,45 @@ | ||
| 490 | 523 | } |
| 491 | 524 | } |
| 492 | 525 | } |
| 493 | 526 | |
| 527 | + /** | |
| 528 | + * Set color for select placeholders. | |
| 529 | + * | |
| 530 | + * @since 6.5.1 | |
| 531 | + */ | |
| 532 | + function setSelectPlaceholderColor() { | |
| 533 | + var selects = document.querySelectorAll( '.form-field select' ), | |
| 534 | + styleElement = document.querySelector( '.with_frm_style' ), | |
| 535 | + textColorDisabled = styleElement ? getComputedStyle( styleElement ).getPropertyValue( '--text-color-disabled' ).trim() : '', | |
| 536 | + changeSelectColor; | |
| 537 | + | |
| 538 | + // Exit if there are no select elements or the textColorDisabled property is missing | |
| 539 | + if ( ! selects.length || ! textColorDisabled ) { | |
| 540 | + return; | |
| 541 | + } | |
| 542 | + | |
| 543 | + // Function to change the color of a select element | |
| 544 | + changeSelectColor = function( select ) { | |
| 545 | + if ( select.options[select.selectedIndex] && hasClass( select.options[select.selectedIndex], 'frm-select-placeholder' ) ) { | |
| 546 | + select.style.setProperty( 'color', textColorDisabled, 'important' ); | |
| 547 | + } else { | |
| 548 | + select.style.color = ''; | |
| 549 | + } | |
| 550 | + }; | |
| 551 | + | |
| 552 | + // Use a loop to iterate through each select element | |
| 553 | + Array.prototype.forEach.call( selects, function( select ) { | |
| 554 | + // Apply the color change to each select element | |
| 555 | + changeSelectColor( select ); | |
| 556 | + | |
| 557 | + // Add an event listener for future changes | |
| 558 | + select.addEventListener( 'change', function() { | |
| 559 | + changeSelectColor( select ); | |
| 560 | + }); | |
| 561 | + }); | |
| 562 | + } | |
| 563 | + | |
| 494 | 564 | function hasInvisibleRecaptcha( object ) { |
| 495 | 565 | var recaptcha, recaptchaID, alreadyChecked; |
| 496 | 566 | |
| 497 | 567 | if ( isGoingToPrevPage( object ) ) { |
| @@ -590,9 +660,9 @@ | ||
| 590 | 660 | shouldTriggerEvent = object.classList.contains( 'frm_trigger_event_on_submit' ); |
| 591 | 661 | |
| 592 | 662 | success = function( response ) { |
| 593 | 663 | var defaultResponse, formID, replaceContent, pageOrder, formReturned, contSubmit, delay, |
| 594 | - $fieldCont, key, inCollapsedSection, frmTrigger; | |
| 664 | + $fieldCont, key, inCollapsedSection, frmTrigger, newTab; | |
| 595 | 665 | |
| 596 | 666 | defaultResponse = { |
| 597 | 667 | content: '', |
| 598 | 668 | errors: {}, |
| @@ -616,10 +686,23 @@ | ||
| 616 | 686 | return; |
| 617 | 687 | } |
| 618 | 688 | |
| 619 | 689 | jQuery( document ).trigger( 'frmBeforeFormRedirect', [ object, response ]); |
| 620 | - window.location = response.redirect; | |
| 621 | - } else if ( response.content !== '' ) { | |
| 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; | |
| 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 | + } | |
| 702 | + } | |
| 703 | + | |
| 704 | + if ( response.content !== '' ) { | |
| 622 | 705 | // the form or success message was returned |
| 623 | 706 | |
| 624 | 707 | if ( shouldTriggerEvent ) { |
| 625 | 708 | triggerCustomEvent( object, 'frmSubmitEvent' ); |
| @@ -1115,11 +1198,10 @@ | ||
| 1115 | 1198 | } |
| 1116 | 1199 | |
| 1117 | 1200 | function makeHoneypotFieldsUntabbable() { |
| 1118 | 1201 | document.querySelectorAll( '.frm_verify' ).forEach( |
| 1119 | - function( wrapper ) { | |
| 1120 | - var input = wrapper.querySelector( 'input[id^=frm_email]' ); | |
| 1121 | - if ( input ) { | |
| 1202 | + function( input ) { | |
| 1203 | + if ( input.id && 0 === input.id.indexOf( 'frm_email_' ) ) { | |
| 1122 | 1204 | input.setAttribute( 'tabindex', -1 ); |
| 1123 | 1205 | } |
| 1124 | 1206 | } |
| 1125 | 1207 | ); |
| @@ -1347,8 +1429,66 @@ | ||
| 1347 | 1429 | runOnLoad(); |
| 1348 | 1430 | }); |
| 1349 | 1431 | } |
| 1350 | 1432 | |
| 1433 | + function shouldUpdateValidityMessage( target ) { | |
| 1434 | + if ( 'INPUT' !== target.nodeName ) { | |
| 1435 | + return false; | |
| 1436 | + } | |
| 1437 | + | |
| 1438 | + if ( ! target.dataset.invmsg ) { | |
| 1439 | + return false; | |
| 1440 | + } | |
| 1441 | + | |
| 1442 | + if ( 'text' !== target.getAttribute( 'type' ) ) { | |
| 1443 | + return false; | |
| 1444 | + } | |
| 1445 | + | |
| 1446 | + if ( target.classList.contains( 'frm_verify' ) ) { | |
| 1447 | + return false; | |
| 1448 | + } | |
| 1449 | + | |
| 1450 | + return true; | |
| 1451 | + } | |
| 1452 | + | |
| 1453 | + function maybeClearCustomValidityMessage( event, field ) { | |
| 1454 | + var key, | |
| 1455 | + isInvalid = false; | |
| 1456 | + | |
| 1457 | + if ( ! shouldUpdateValidityMessage( field ) ) { | |
| 1458 | + return; | |
| 1459 | + } | |
| 1460 | + | |
| 1461 | + for ( key in field.validity ) { | |
| 1462 | + if ( 'customError' === key ) { | |
| 1463 | + continue; | |
| 1464 | + } | |
| 1465 | + if ( 'valid' !== key && field.validity[ key ] === true ) { | |
| 1466 | + isInvalid = true; | |
| 1467 | + break; | |
| 1468 | + } | |
| 1469 | + }; | |
| 1470 | + | |
| 1471 | + if ( ! isInvalid ) { | |
| 1472 | + field.setCustomValidity( '' ); | |
| 1473 | + } | |
| 1474 | + } | |
| 1475 | + | |
| 1476 | + function maybeShowNewTabFallbackMessage() { | |
| 1477 | + var messageEl; | |
| 1478 | + | |
| 1479 | + if ( ! window.frmShowNewTabFallback ) { | |
| 1480 | + return; | |
| 1481 | + } | |
| 1482 | + | |
| 1483 | + messageEl = document.querySelector( '#frm_form_' + frmShowNewTabFallback.formId + '_container .frm_message' ); | |
| 1484 | + if ( ! messageEl ) { | |
| 1485 | + return; | |
| 1486 | + } | |
| 1487 | + | |
| 1488 | + messageEl.insertAdjacentHTML( 'beforeend', ' ' + frmShowNewTabFallback.message ); | |
| 1489 | + } | |
| 1490 | + | |
| 1351 | 1491 | function setCustomValidityMessage() { |
| 1352 | 1492 | var forms, length, index; |
| 1353 | 1493 | |
| 1354 | 1494 | forms = document.getElementsByClassName( 'frm-show-form' ); |
| @@ -1359,25 +1499,11 @@ | ||
| 1359 | 1499 | 'invalid', |
| 1360 | 1500 | function( event ) { |
| 1361 | 1501 | var target = event.target; |
| 1362 | 1502 | |
| 1363 | - if ( 'INPUT' !== target.nodeName ) { | |
| 1364 | - return; | |
| 1503 | + if ( shouldUpdateValidityMessage( target ) ) { | |
| 1504 | + target.setCustomValidity( target.dataset.invmsg ); | |
| 1365 | 1505 | } |
| 1366 | - | |
| 1367 | - if ( ! target.dataset.invmsg ) { | |
| 1368 | - return; | |
| 1369 | - } | |
| 1370 | - | |
| 1371 | - if ( 'text' !== target.getAttribute( 'type' ) ) { | |
| 1372 | - return; | |
| 1373 | - } | |
| 1374 | - | |
| 1375 | - if ( target.classList.contains( 'frm_verify' ) ) { | |
| 1376 | - return; | |
| 1377 | - } | |
| 1378 | - | |
| 1379 | - target.setCustomValidity( target.dataset.invmsg ); | |
| 1380 | 1506 | }, |
| 1381 | 1507 | true |
| 1382 | 1508 | ); |
| 1383 | 1509 | } |
| @@ -1382,8 +1508,21 @@ | ||
| 1382 | 1508 | ); |
| 1383 | 1509 | } |
| 1384 | 1510 | } |
| 1385 | 1511 | |
| 1512 | + function enableSubmitButtonOnBackButtonPress() { | |
| 1513 | + window.addEventListener( 'pageshow', function( event ) { | |
| 1514 | + if ( event.persisted ) { | |
| 1515 | + document.querySelectorAll( '.frm_loading_form' ).forEach( | |
| 1516 | + function( form ) { | |
| 1517 | + enableSubmitButton( jQuery( form ) ); | |
| 1518 | + } | |
| 1519 | + ); | |
| 1520 | + removeSubmitLoading(); | |
| 1521 | + } | |
| 1522 | + }); | |
| 1523 | + } | |
| 1524 | + | |
| 1386 | 1525 | return { |
| 1387 | 1526 | init: function() { |
| 1388 | 1527 | maybeAddPolyfills(); |
| 1389 | 1528 | |
| @@ -1419,11 +1558,20 @@ | ||
| 1419 | 1558 | addTrimFallbackForIE(); // Trim only works in IE10+. |
| 1420 | 1559 | addFilterFallbackForIE(); // Filter is not supported in any version of IE. |
| 1421 | 1560 | |
| 1422 | 1561 | initFloatingLabels(); |
| 1562 | + maybeShowNewTabFallbackMessage(); | |
| 1423 | 1563 | |
| 1424 | 1564 | jQuery( document ).on( 'frmAfterAddRow', setCustomValidityMessage ); |
| 1425 | 1565 | setCustomValidityMessage(); |
| 1566 | + jQuery( document ).on( 'frmFieldChanged', maybeClearCustomValidityMessage ); | |
| 1567 | + | |
| 1568 | + setSelectPlaceholderColor(); | |
| 1569 | + | |
| 1570 | + // Elementor popup show event. Fix Elementor Popup && FF Captcha field conflicts | |
| 1571 | + jQuery( document ).on( 'elementor/popup/show', frmRecaptcha ); | |
| 1572 | + | |
| 1573 | + enableSubmitButtonOnBackButtonPress(); | |
| 1426 | 1574 | }, |
| 1427 | 1575 | |
| 1428 | 1576 | getFieldId: function( field, fullID ) { |
| 1429 | 1577 | return getFieldId( field, fullID ); |
| @@ -1735,9 +1883,11 @@ | ||
| 1735 | 1883 | }, |
| 1736 | 1884 | |
| 1737 | 1885 | visible: function( classes ) { |
| 1738 | 1886 | jQuery( classes ).css( 'visibility', 'visible' ); |
| 1739 | - } | |
| 1887 | + }, | |
| 1888 | + | |
| 1889 | + triggerCustomEvent: triggerCustomEvent | |
| 1740 | 1890 | }; |
| 1741 | 1891 | } |
| 1742 | 1892 | frmFrontForm = frmFrontFormJS(); |
| 1743 | 1893 | |