: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( typeof $.fn.timepicker === 'undefined' ) {
$context = $context?.length ? $context : $( document );
$context.find( '.wpforms-timepicker' ).each( function() { // eslint-disable-line complexity
const element = $( this ),
form = element.closest( '.wpforms-form' ),
formID = form.data( 'formid' ),
fieldID = element.closest( '.wpforms-field' ).data( 'field-id' );
typeof window[ 'wpforms_' + formID + '_' + fieldID ] !== 'undefined' &&
window[ 'wpforms_' + formID + '_' + fieldID ].hasOwnProperty( 'timepicker' )
properties = window[ 'wpforms_' + formID + '_' + fieldID ].timepicker;
typeof window[ 'wpforms_' + formID ] !== 'undefined' &&
window[ 'wpforms_' + formID ].hasOwnProperty( 'timepicker' )
properties = window[ 'wpforms_' + formID ].timepicker;
} else if ( typeof wpforms_timepicker !== 'undefined' ) {
properties = wpforms_timepicker;
// Retrieve the value from the input element.
const inputValue = element.val();
element.timepicker( properties );
// Check if a value is available.
// Set the input element's value to the retrieved value.
element.val( inputValue );
// Trigger the 'changeTime' event to update the timepicker after programmatically setting the value.
element.trigger( 'changeTime' );
* Load jQuery input masks.
* @since 1.8.9 Added the `$context` parameter.
* @param {jQuery} $context Container to search for datepicker elements.
loadInputMask( $context ) {
// Only load if jQuery input mask library exists.
if ( typeof $.fn.inputmask === 'undefined' ) {
$context = $context?.length ? $context : $( document );
// This setting has no effect when switching to the "RTL" mode.
$context.find( '.wpforms-masked-input' ).inputmask( { rightAlign: false } );
* Fix the Phone field snippets.
* @param {jQuery} $field Phone field element.
fixPhoneFieldSnippets( $field ) {
$field.siblings( 'input[type="hidden"]' ).each( function() {
if ( ! $( this ).attr( 'name' ).includes( 'function' ) ) {
const data = $field.data( 'plugin_intlTelInput' );
const options = data.d || data.options;
$field.intlTelInput( 'destroy' );
options.initialCountry = options.initialCountry.toLowerCase();
options.onlyCountries = options.onlyCountries.map( ( v ) => v.toLowerCase() );
options.preferredCountries = options.preferredCountries.map( ( v ) => v.toLowerCase() );
$field.intlTelInput( options );
$field.siblings( 'input[type="hidden"]' ).each( function() {
const $hiddenInput = $( this );
$hiddenInput.attr( 'name', $hiddenInput.attr( 'name' ).replace( 'wpf-temp-', '' ) );
* @since 1.8.9 Added the `$context` parameter.
* @param {jQuery} $context Context to search for smartphone elements.
loadSmartPhoneField( $context ) { // eslint-disable-line complexity
if ( typeof $.fn.intlTelInput === 'undefined' ) {
// Only load if a library exists.
preferredCountries: [ 'us', 'gb' ],
// Determine the country by IP if no GDPR restrictions enabled.
if ( ! wpforms_settings.gdpr ) {
inputOptions.geoIpLookup = app.currentIpToCountry;
// Try to kick in an alternative solution if GDPR restrictions are enabled.
if ( wpforms_settings.gdpr ) {
const lang = this.getFirstBrowserLanguage();
countryCode = lang.indexOf( '-' ) > -1 ? lang.split( '-' ).pop() : '';
// Make sure the library recognizes browser country code to avoid console error.
let countryData = window.intlTelInputGlobals.getCountryData();
countryData = countryData.filter( function( country ) {
return country.iso2 === countryCode.toLowerCase();
countryCode = countryData.length ? countryCode : '';
inputOptions.initialCountry = wpforms_settings.gdpr && countryCode ? countryCode.toLowerCase() : 'auto';
$context = $context?.length ? $context : $( document );
$context.find( '.wpforms-smart-phone-field' ).each( function( i, el ) {
// Prevent initialization if the popup is hidden.
if ( $el.parents( '.elementor-location-popup' ).is( ':hidden' ) ) {
// Hidden input allows to include country code into submitted data.
inputOptions.hiddenInput = function( telInputName ) {
inputOptions.utilsScript = wpforms_settings.wpforms_plugin_url + 'assets/pro/lib/intl-tel-input/jquery.intl-tel-input-utils.min.js';
$el.intlTelInput( inputOptions );
// For proper validation, we should preserve the name attribute of the input field.
// But we need to modify the original input name not to interfere with a hidden input.
$el.attr( 'name', 'wpf-temp-' + $el.attr( 'name' ) );
// Add special class to remove name attribute before submitting.
// So, only the hidden input value will be submitted.
$el.addClass( 'wpforms-input-temp-name' );
// Instantly update a hidden form input with correct data.
// Previously "blur" only was used, which is broken in case Enter was used to submit the form.
$el.on( 'blur input', function() {
if ( $el.intlTelInput( 'isValidNumber' ) || ! app.empty( window.WPFormsEditEntry ) ) {
$el.siblings( 'input[type="hidden"]' ).val( $el.intlTelInput( 'getNumber' ) );
* Bind Smartphone field event.
// Update hidden input of the `Smart` phone field to be sure the latest value will be submitted.
$( '.wpforms-form' ).on( 'wpformsBeforeFormSubmit', function() {
$( this ).find( '.wpforms-smart-phone-field' ).trigger( 'input' );
* Payments: Do various payment-related tasks on a load.
// Update Total field(s) with the latest calculation.
$( '.wpforms-payment-total' ).each( function( index, el ) {
// Credit card validation.
if ( typeof $.fn.payment !== 'undefined' ) {
$( '.wpforms-field-credit-card-cardnumber' ).payment( 'formatCardNumber' );
$( '.wpforms-field-credit-card-cardcvc' ).payment( 'formatCardCVC' );
loadMailcheck() { // eslint-disable-line max-lines-per-function
// Skip loading if `wpforms_mailcheck_enabled` filter return false.
if ( ! wpforms_settings.mailcheck_enabled ) {
// Only load if a library exists.
if ( typeof $.fn.mailcheck === 'undefined' ) {
if ( wpforms_settings.mailcheck_domains.length > 0 ) {
Mailcheck.defaultDomains = Mailcheck.defaultDomains.concat( wpforms_settings.mailcheck_domains );
if ( wpforms_settings.mailcheck_toplevel_domains.length > 0 ) {
Mailcheck.defaultTopLevelDomains = Mailcheck.defaultTopLevelDomains.concat( wpforms_settings.mailcheck_toplevel_domains );
$( document ).on( 'blur', '.wpforms-field-email input', function() {
const $input = $( this ),
id = $input.attr( 'id' );
suggested( $el, suggestion ) {
// decodeURI() will throw an error if the percent sign is not followed by two hexadecimal digits.
suggestion.full = suggestion.full.replace( /%(?![0-9][0-9a-fA-F]+)/g, '%25' );
suggestion.address = suggestion.address.replace( /%(?![0-9][0-9a-fA-F]+)/g, '%25' );
suggestion.domain = suggestion.domain.replace( /%(?![0-9][0-9a-fA-F]+)/g, '%25' );
if ( suggestion.address.match( /^xn--/ ) ) {
suggestion.full = punycode.toUnicode( decodeURI( suggestion.full ) );
const parts = suggestion.full.split( '@' );
suggestion.address = parts[ 0 ];
suggestion.domain = parts[ 1 ];
if ( suggestion.domain.match( /^xn--/ ) ) {
suggestion.domain = punycode.toUnicode( decodeURI( suggestion.domain ) );
const address = decodeURI( suggestion.address ).replaceAll( /[<>'"()/\\|:;=@%&\s]/ig, '' ).substr( 0, 64 ),
domain = decodeURI( suggestion.domain ).replaceAll( /[<>'"()/\\|:;=@%&+_\s]/ig, '' );
suggestion = '<a href="#" class="mailcheck-suggestion" data-id="' + id + '" title="' + wpforms_settings.val_email_suggestion_title + '">' + address + '@' + domain + '</a>';
suggestion = wpforms_settings.val_email_suggestion.replace( '{suggestion}', suggestion );
$el.closest( '.wpforms-field' ).find( '#' + id + '_suggestion' ).remove();
$el.parent().append( '<label class="wpforms-error mailcheck-error" id="' + id + '_suggestion">' + suggestion + '</label>' );
$( '#' + id + '_suggestion' ).remove();
// Apply a Mailcheck suggestion.
$( document ).on( 'click', '.wpforms-field-email .mailcheck-suggestion', function( e ) {
const $suggestion = $( this ),
$field = $suggestion.closest( '.wpforms-field' ),
id = $suggestion.data( 'id' );
$field.find( '#' + id ).val( $suggestion.text() );
$suggestion.parent().remove();
* Load Choices.js library for all Modern style Dropdown fields (<select>).
* @since 1.8.9 Added the `$context` parameter.
* @param {jQuery} $context Container to search for ChoicesJS elements.
loadChoicesJS( $context ) { // eslint-disable-line max-lines-per-function
// Loads if function exists.
if ( typeof window.Choices !== 'function' ) {
$context = $context?.length ? $context : $( document );
// eslint-disable-next-line max-lines-per-function
$context.find( '.wpforms-field-select-style-modern .choicesjs-select, .wpforms-field-payment-select .choicesjs-select' ).each( function( idx, el ) {
if ( $( el ).data( 'choicesjs' ) ) {
const args = window.wpforms_choicesjs_config || {},
searchEnabled = $( el ).data( 'search-enabled' ),
removeItems = $( el ).data( 'remove-items-enabled' );
args.searchEnabled = 'undefined' !== typeof searchEnabled ? searchEnabled : true;
args.removeItems = 'undefined' !== typeof removeItems ? removeItems : true;
args.removeItemButton = args.removeItems;
args.searchEnabled = 'undefined' !== typeof searchEnabled ? searchEnabled : true;
args.allowHTML = false; // TODO: Remove after next Choices.js release.
args.callbackOnInit = function() {
$element = $( self.passedElement.element ),
$input = $( self.input.element ),
sizeClass = $element.data( 'size-class' );
// Remove hidden attribute and hide `<select>` like a screen-reader text.
// It's important for field validation.
.addClass( self.config.classNames.input + '--hidden' );
// Add CSS-class for size.
$( self.containerOuter.element ).addClass( sizeClass );
* If a multiple select has selected choices - hide a placeholder text.
* In case if select is empty - we return placeholder text.
if ( $element.prop( 'multiple' ) ) {
$input.data( 'placeholder', $input.attr( 'placeholder' ) ).css( 'width', 'auto' );
if ( self.getValue( true ).length ) {
$input.removeAttr( 'placeholder' );
$element.on( 'change', function() {
// Listen if multiple select has choices.
if ( $element.prop( 'multiple' ) ) {
// eslint-disable-next-line no-unused-expressions
self.getValue( true ).length
? $input.removeAttr( 'placeholder' )
: $input.attr( 'placeholder', $input.data( 'placeholder' ) ).css( 'width', 'auto' );
const validator = $element.closest( 'form' ).data( 'validator' );
validator.element( $element );
args.callbackOnCreateTemplates = function() {
$element = $( self.passedElement.element );
// Change default template for option.
const opt = Choices.defaults.templates.option.call( this, item );
// Add a `.placeholder` class for placeholder option - it needs for WPForm CL.
if ( 'undefined' !== typeof item.placeholder && true === item.placeholder ) {
opt.classList.add( 'placeholder' );
// Add a `data-amount` attribute for payment dropdown.
// It will be a copy from a Choices.js `data-custom-properties` attribute.
if ( $element.hasClass( 'wpforms-payment-price' ) && 'undefined' !== typeof item.customProperties && null !== item.customProperties ) {
opt.dataset.amount = item.customProperties;
// Save choicesjs instance for future access.
$( el ).data( 'choicesjs', new Choices( el, args ) );
* Bind ChoicesJS' events.
// Add the ability to close the drop-down menu on the frontend.
$( document ).on( 'click', '.choices', function( e ) {
const $choices = $( this ),
choicesObj = $choices.find( 'select' ).data( 'choicesjs' );
$choices.hasClass( 'is-open' ) &&
e.target.classList.contains( 'choices__inner' ) ||
e.target.classList.contains( 'choices__arrow' )
choicesObj.hideDropdown();
//--------------------------------------------------------------------//
//--------------------------------------------------------------------//
bindUIActions() { // eslint-disable-line max-lines-per-function
const $document = $( document );
$document.on( 'click', '.wpforms-page-button', function( event ) {
app.pagebreakNav( this );
// Payments: Update Total field(s) when latest calculation.
$document.on( 'change input', '.wpforms-payment-price', function() {
app.amountTotal( this, true );
// Payments: Update Total field(s) when changing quantity.
$document.on( 'change input', 'select.wpforms-payment-quantity', function() {
app.amountTotal( this, true );
app.updateOrderSummaryItemQuantity( $( this ) );
// Payments: Restrict user input payment fields.
$document.on( 'input', '.wpforms-payment-user-input', function() {
$this.val( amount.replace( /[^0-9.,]/g, '' ) );
// Payments: Sanitize/format user input amounts.
$document.on( 'focusout', '.wpforms-payment-user-input', function() {
const sanitized = app.amountSanitize( amount ),
formatted = app.amountFormat( sanitized );
// Payments: Update Total field(s) when conditionals are processed.
$document.on( 'wpformsProcessConditionals', function( e, el ) {
app.amountTotal( el, true );
// Order Summary: Update field when conditionals are processed.
$document.on( 'wpformsProcessConditionalsField', function( e, formID, fieldID ) {
app.updateOrderSummaryItems( $( `#wpforms-form-${ formID }` ), $( `#wpforms-${ formID }-field_${ fieldID }` ), '' );
// Rating field: hover effect.
$document.on( 'mouseenter', '.wpforms-field-rating-item', function() {
$( this ).parent().find( '.wpforms-field-rating-item' ).removeClass( 'selected hover' );
$( this ).prevAll().addBack().addClass( 'hover' );
} ).on( 'mouseleave', '.wpforms-field-rating-item', function() {
$( this ).parent().find( '.wpforms-field-rating-item' ).removeClass( 'selected hover' );
$( this ).parent().find( 'input:checked' ).parent().prevAll().addBack().addClass( 'selected' );
// Rating field: toggle selected state.
$( document ).on( 'change', '.wpforms-field-rating-item input', function() {
$wrap = $this.closest( '.wpforms-field-rating-items' ),