: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
/* global wpforms_builder, WPFormsUtils */
* @param window.DOMPurify
* @param wpforms_builder.currency_decimal
* @param wpforms_builder.currency_decimals
* @param wpforms_builder.currency_symbol
* @param wpforms_builder.currency_symbol_pos
* @param wpforms_builder.currency_thousands
// This file contains a collection of utility functions.
// Init Radio Group for Checkboxes.
wpf.initRadioGroupForCheckboxes();
// Load initial form saved state.
wpf.savedState = wpf.getFormState( '#wpforms-builder-form' );
// Save field and choice order for sorting later.
// The following items should all trigger the fieldUpdate trigger.
jQuery( document ).on( 'wpformsFieldAdd', wpf.setFieldOrders );
jQuery( document ).on( 'wpformsFieldDelete', wpf.setFieldOrders );
jQuery( document ).on( 'wpformsFieldMove', wpf.setFieldOrders );
jQuery( document ).on( 'wpformsFieldAdd', wpf.setChoicesOrders );
jQuery( document ).on( 'wpformsFieldChoiceAdd', wpf.setChoicesOrders );
jQuery( document ).on( 'wpformsFieldChoiceDelete', wpf.setChoicesOrders );
jQuery( document ).on( 'wpformsFieldChoiceMove', wpf.setChoicesOrders );
jQuery( document ).on( 'wpformsFieldAdd', wpf.fieldUpdate );
jQuery( document ).on( 'wpformsFieldDelete', wpf.fieldUpdate );
jQuery( document ).on( 'wpformsFieldMove', wpf.fieldUpdate );
jQuery( document ).on( 'focusout', '.wpforms-field-option-row-label input', wpf.fieldUpdate );
jQuery( document ).on( 'wpformsFieldChoiceAdd', wpf.fieldUpdate );
jQuery( document ).on( 'wpformsFieldChoiceDelete', wpf.fieldUpdate );
jQuery( document ).on( 'wpformsFieldChoiceMove', wpf.fieldUpdate );
jQuery( document ).on( 'wpformsFieldDynamicChoiceToggle', wpf.fieldUpdate );
jQuery( document ).on( 'focusout', '.wpforms-field-option-row-choices input.label', wpf.fieldUpdate );
* Store the order of the fields.
jQuery( '.wpforms-field-option' ).each( function() {
wpf.orders.fields.push( jQuery( this ).data( 'field-id' ) );
* Store the order of the choices for each field.
jQuery( '.choices-list' ).each( function() {
const fieldID = jQuery( this ).data( 'field-id' );
wpf.orders.choices[ 'field_' + fieldID ] = [];
jQuery( this ).find( 'li' ).each( function() {
wpf.orders.choices[ 'field_' + fieldID ].push( jQuery( this ).data( 'key' ) );
* Return the order of choices for a specific field.
* @param {number|string} id Field ID.
* @return {Array} Choices.
jQuery( '#wpforms-field-option-' + id ).find( '.choices-list li' ).each( function() {
choices.push( jQuery( this ).data( 'key' ) );
* Maintain multiselect dropdown with search.
* If a multiple select has selected choices - hide a placeholder text.
* In case if select is empty - we return placeholder text.
* @param {Object} self Current object.
initMultipleSelectWithSearch( self ) {
const $element = jQuery( self.passedElement.element ),
$input = jQuery( self.input.element );
if ( $element.prop( 'multiple' ) ) {
$input.data( 'placeholder', $input.attr( 'placeholder' ) );
// We need to save the style attribute to restore it later and make placeholder visible.
$input.data( 'style', $input.attr( 'style' ) );
if ( self.getValue( true ).length ) {
$input.removeAttr( 'placeholder' );
$element.on( 'change', function() {
// eslint-disable-next-line no-unused-expressions
self.getValue( true ).length
? $input.removeAttr( 'placeholder' )
: $input.attr( 'placeholder', $input.data( 'placeholder' ) ).attr( 'style', $input.data( 'style' ) );
* Display/hide show more icon inside multiselect dropdown.
* @param {string} container Container element.
showMoreButtonForChoices( container ) {
const first = jQuery( container ).find( '.choices__list--multiple .choices__item' ).first(),
last = jQuery( container ).find( '.choices__list--multiple .choices__item' ).last();
jQuery( container ).removeClass( 'choices__show-more' );
if ( first.length > 0 && last.length > 0 && first.position().top !== last.position().top ) {
jQuery( container ).addClass( 'choices__show-more' );
* Trigger fired for all field update related actions.
const fields = wpf.getFields();
jQuery( document ).trigger( 'wpformsFieldUpdate', [ fields ] );
wpf.debug( 'fieldUpdate triggered' );
* Dynamically get the fields from the current form state.
* @since 1.8.9 Added `allowedFields` parameter.
* @param {Array|boolean|undefined} allowedFields Allowed fields.
* @param {boolean|undefined} useCache Use cache.
* @param {boolean|undefined} isAllowedRepeaterFields Is repeater fields allowed?
* @return {Object} Fields.
getFields( allowedFields, useCache, isAllowedRepeaterFields ) { // eslint-disable-line complexity
useCache = useCache || false;
if ( useCache && ! jQuery.isEmptyObject( wpf.cachedFields ) ) {
// Use cache if told and cache is primed.
fields = jQuery.extend( {}, wpf.cachedFields );
wpf.debug( 'getFields triggered (cached)' );
// Normal processing, get fields from builder and prime cache.
const formData = wpf.formObject( '#wpforms-field-options' );
fields = formData.fields;
for ( const key in fields ) {
if ( ! fields[ key ].type || jQuery.inArray( fields[ key ].type, fieldBlockList ) > -1 ) {
if ( fields[ key ]?.type === 'repeater' ) {
Object.values( fields[ key ][ 'columns-json' ] ?? {} ).forEach( ( column ) => {
Object.values( column?.fields ?? [] ).forEach( ( field ) => {
if ( ! fields[ field ] ) {
// Update field label for a repeater child fields.
fields[ field ].label += ' (' + fields[ key ].label + ')';
fields[ field ].isRepeater = true;
// Cache the all the fields now that they have been ordered and initially processed.
wpf.cachedFields = jQuery.extend( {}, fields );
wpf.debug( 'getFields triggered' );
if ( ! isAllowedRepeaterFields ) {
for ( const key in fields ) {
if ( fields[ key ]?.isRepeater ) {
// If we should only return specific field types, remove the others.
if ( allowedFields && allowedFields.constructor === Array ) {
for ( const key in fields ) {
if ( jQuery.inArray( fields[ key ].type, allowedFields ) === -1 ) {
if ( Object.keys( fields ).length === 0 ) {
* Get a field settings object.
* @param {number|string} id Field ID.
* @return {Object} Field settings.
const field = wpf.formObject( '#wpforms-field-option-' + id );
return field.fields[ Object.keys( field.fields )[ 0 ] ];
* Toggle the loading state/indicator of a field option.
* @param {string|Element} option jQuery object, or DOM element selector.
* @param {boolean} unload True if you need to unload spinner, and vice versa.
fieldOptionLoading( option, unload ) {
const $option = jQuery( option ),
$label = $option.find( 'label' ),
spinner = '<i class="wpforms-loading-spinner wpforms-loading-inline"></i>';
unload = typeof unload !== 'undefined';
$label.find( '.wpforms-loading-spinner' ).remove();
$label.find( '.wpforms-help-tooltip' ).show();
$option.find( 'input,select,textarea' ).prop( 'disabled', false );
$label.append( spinner );
$label.find( '.wpforms-help-tooltip' ).hide();
$option.find( 'input,select,textarea' ).prop( 'disabled', true );
* @param {Object} el Element.
* @return {string} Form state.
// Serialize tested the most performant string we can use for comparisons.
return jQuery( el ).serialize();
* Remove items from an array.
* @param {Array} array An array.
* @param {any} item Array item.
* @return {number} Count of removed items.
removeArrayItem( array, item ) {
for ( let index = 0; index < array.length; index++ ) {
if ( array[ index ] === item ) {
array.splice( index, 1 );
* @param {string} str String to sanitize.
* @return {string} String after sanitization.
if ( typeof str === 'string' || str instanceof String ) {
* Update query string in URL.
* @since 1.8.7 Refactored using URL API.
* @param {string} key Query string param.
* @param {string|null} value Query string value.
* @param {string|null} url URL. If not defined, the current URL will be used.
* @return {string} Updated URL.
updateQueryString( key, value, url = null ) {
url = window.location.href;
const urlObj = new URL( url );
if ( typeof value !== 'undefined' && value !== null ) {
urlObj.searchParams.set( key, value );
// Remove param from the URL.
urlObj.searchParams.delete( key );
return urlObj.toString();
* Get query string in a URL.
* @param {string} name Query string param.
* @return {string} Query string value.
const match = new RegExp( '[?&]' + name + '=([^&]*)' ).exec( window.location.search );
return match && decodeURIComponent( match[ 1 ].replace( /\+/g, ' ' ) );
* Remove defined query parameter in the current URL.
* @see https://gist.github.com/simonw/9445b8c24ddfcbb856ec#gistcomment-3117674
* @param {string} name The name of the parameter to be removed.
removeQueryParam( name ) {
if ( wpf.getQueryString( name ) ) {
const replace = '[\\?&]' + name + '=[^&]+',
re = new RegExp( replace );
// eslint-disable-next-line no-unused-expressions
history.replaceState && history.replaceState(
null, '', location.pathname + location.search.replace( re, '' ).replace( /^&/, '?' ) + location.hash
* @param {number|string} n Number to check.
* @return {boolean} Whether this is a number.
return ! isNaN( parseFloat( n ) ) && isFinite( n );
* Sanitize amount and convert to standard format for calculations.
* @param {string} amount Price amount to sanitize.
* @return {string} Sanitized amount.
amountSanitize( amount ) { // eslint-disable-line complexity
// Convert to string and allow only numbers, dots, and commas.
amount = String( amount ).replace( /[^0-9.,]/g, '' );
if ( wpforms_builder.currency_decimal === ',' ) {
if ( wpforms_builder.currency_thousands === '.' && amount.indexOf( wpforms_builder.currency_thousands ) !== -1 ) {
amount = amount.replace( new RegExp( '\\' + wpforms_builder.currency_thousands, 'g' ), '' );
} else if ( wpforms_builder.currency_thousands === '' && amount.indexOf( '.' ) !== -1 ) {
amount = amount.replace( /\./g, '' );
amount = amount.replace( wpforms_builder.currency_decimal, '.' );
} else if ( wpforms_builder.currency_thousands === ',' && ( amount.indexOf( wpforms_builder.currency_thousands ) !== -1 ) ) {
amount = amount.replace( new RegExp( '\\' + wpforms_builder.currency_thousands, 'g' ), '' );
return wpf.numberFormat( amount, wpforms_builder.currency_decimals, '.', '' );
* @param {string} amount Price amount to format.
* @return {string} Formatted amount.
amount = String( amount );