: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( '' !== $parallax_background ) {
$this->add_classname( 'et_pb_section_parallax' );
return $parallax_background;
* Check our shortcode arguments for CSS `filter` properties. If found, set the style rules for this block. (This
* function reads options set by the 'Filters' and 'Image Filters' builder menu fields.)
* @since 3.23 Add responsive setting styling processing here.
* @param string $function_name Builder module's function name (keeps the CSS rules straight)
* @param string $prefix Optional string prepended to the field name (i.e., `filter_saturate` -> `child_filter_saturate`)
* @param mixed $selectors Array or string containing all target DOM element(s), ID(s), and/or class(es)
* @return string Any additional CSS classes (added if filters were applied).
function generate_css_filters( $function_name = '', $prefix = '', $selectors = array('%%order_class%%') ) {
if ( '' === $function_name ) {
ET_Core_Logger::error( '$function_name is required.' );
// If `$selectors` is a string, convert to an array before we continue
$selectors_prepared = $selectors;
if ( ! is_array( $selectors ) ) {
$selectors_prepared = explode( ',', et_core_intentionally_unescaped( $selectors, 'fixed_string' ) );
$responsive_selectors = $selectors_prepared;
$additional_classes = '';
// If we don't have a target selector, get out now
if ( ! $selectors_prepared ) {
return $additional_classes;
$hover_suffix = et_pb_hover_options()->get_suffix();
$field_suffixes = array( '', 'tablet', 'phone', $hover_suffix );
$filters_default = array();
$filters_default_fb = array();
foreach ( $field_suffixes as $suffix ) {
if ( $hover_suffix === $suffix ) {
$selectors_prepared = array_map( array( $this, 'add_hover_to_selectors' ), $selectors_prepared );
// Mobile parameters. Update suffix and add media query argument for styles declaration.
$is_mobile = in_array( $suffix, array( 'tablet', 'phone' ) );
$breakpoint = 'tablet' === $suffix ? 'max_width_980' : 'max_width_767';
$media_query = array( 'media_query' => ET_Builder_Element::get_media_query( $breakpoint ) );
// For mobile, we need to reset $suffix and use $devie_suffix instead. Later on with
// empty suffix, the filter will only return desktop value and will be used as default
// and will be merged with filter mobile values.
$device_suffix = "_{$suffix}";
// Some web browser glitches with filters and blend modes can be improved this way
// see https://bugs.chromium.org/p/chromium/issues/detail?id=157218 for more info
$backfaceVisibility = 'backface-visibility:hidden;';
$backfaceVisibilityAdded = array();
$additional_classes = '';
$mix_blend_mode = self::$data_utils->array_get( $this->props, "{$prefix}mix_blend_mode", '' );
// Assign filter values and names.
foreach( $filter_keys as $filter_key ) {
$filter_name = "{$prefix}filter_{$filter_key}";
$filter_names[] = $filter_name;
$filter[ $filter_key ] = self::$data_utils->array_get( $this->props, "{$filter_name}{$suffix}", '');
$is_any_filter_responsive = et_pb_responsive_options()->is_any_responsive_enabled( $this->props, $filter_names );
$is_any_filter_hover_enabled = et_pb_hover_options()->is_any_hover_enabled( $this->props, $filter_names );
// For mobile, it should return any value exist if current device value is empty.
$is_blend_responsive = et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$prefix}mix_blend_mode" );
$mix_blend_mode = $is_blend_responsive ? et_pb_responsive_options()->get_any_value( $this->props, "{$prefix}mix_blend_mode{$suffix}", '', true ) : '';
$filters_mobile = array();
foreach( $filter as $filter_key => $filter_value ) {
if ( ! et_pb_responsive_options()->is_responsive_enabled( $this->props, "{$prefix}filter_{$filter_key}" ) ) {
$filters_mobile[ $filter_key ] = et_pb_responsive_options()->get_any_value( $this->props, "{$prefix}filter_{$filter_key}{$device_suffix}", '', true );
// If any responsive settings active on filter settings, set desktop value as default.
if ( $is_any_filter_responsive ) {
$filters_mobile = array_merge( $filter, $filters_mobile );
// Replace current filter values with mobile filter values.
$filter = $filters_mobile;
// Remove any filters with null or default values
$filter = array_filter( $filter, 'strlen' );
// Optional: CSS `mix-blend-mode` rule
$mix_blend_mode_default = ET_Global_Settings::get_value( 'all_mix_blend_mode', 'default' );
if ( $mix_blend_mode && $mix_blend_mode !== $mix_blend_mode_default ) {
foreach ( $selectors_prepared as $selector ) {
ET_Builder_Element::set_style( $function_name, array_merge( array(
'declaration' => sprintf(
esc_html( $mix_blend_mode )
$backfaceVisibilityAdded[] = $selector;
$additional_classes .= ' et_pb_css_mix_blend_mode';
} else if ( 'et_pb_column' === $function_name ) {
// Columns need to pass through
$additional_classes .= ' et_pb_css_mix_blend_mode_passthrough';
// Optional: CSS `filter` rule
if ( empty( $filter ) ) {
// Since we added responsive settings, the process should not be stopped here.
// It should continue until tablet and phone are checked completely. Replace
$css_value_fb_hover = array();
foreach ( $filter as $label => $value ) {
// Check against our default settings, and only append the rule if it differs
// (only for default state since hover and mobile might be equal to default,
// ie. no filter on hover only)
if ( ET_Global_Settings::get_value( 'all_filter_' . $label, 'default' ) === $value && $hover_suffix !== $suffix && ! $is_mobile && ! ( $is_any_filter_responsive && $is_any_filter_hover_enabled ) ) {
// Don't apply hover filter if it is not enabled
if ( $hover_suffix === $suffix && ! et_pb_hover_options()->is_enabled( "{$prefix}filter_{$label}{$suffix}", $this->props ) ) {
$value = et_sanitize_input_unit( $value, false, 'deg' );
$label_css_format = str_replace( '_', '-', $label );
// Construct string of all CSS Filter values
$css_value[$label] = esc_html( "${label_css_format}(${value})" );
// Construct Visual Builder hover rules
if ( ! in_array( $label, array( 'opacity', 'blur' ) ) ) {
// Skip those, because they mess with VB controls
$css_value_fb_hover[$label] = esc_html( "${label_css_format}(${value})" );
// Append our new CSS rules
// Store the default (non-hover) filters
$filters_default = $css_value;
// Merge the hover filters onto the default filters so that filters that
// have no hover option set are not removed from the CSS declaration
if ( $hover_suffix === $suffix ) {
$css_value = array_merge( $filters_default, $css_value );
foreach ( $selectors_prepared as $selector ) {
$backfaceVisibilityDeclaration = in_array( $selector, $backfaceVisibilityAdded ) ? '' : $backfaceVisibility;
// Allow custom child filter target hover selector
if ( 'child_' == $prefix && $hover_suffix === $suffix ){
$selector = self::$_->array_get( $this->advanced_fields, 'filters.child_filters_target.css.hover', $selector );
ET_Builder_Element::set_style( $function_name, array_merge( array(
'declaration' => sprintf(
implode( ' ', $css_value )
) . $backfaceVisibilityDeclaration,
$additional_classes .= ' et_pb_css_filters';
// If we have VB hover-friendly CSS rules, we'll gather those and append them here
if ( $css_value_fb_hover ) {
// Store the default (non-hover) filters
$filters_default_fb = $css_value_fb_hover;
// Merge the hover filters onto the default filters so that filters that
// have no hover option set are not removed from the CSS declaration
if ( $hover_suffix === $suffix ) {
$css_value_fb_hover = array_merge( $filters_default_fb, $css_value_fb_hover );
foreach ( $selectors_prepared as $selector ) {
$selector_hover = str_replace(
'html:not(.et_fb_edit_enabled) #et-fb-app %%order_class%%:hover',
ET_Builder_Element::set_style( $function_name, array(
'selector' => $selector_hover,
'declaration' => esc_html( sprintf(
implode( ' ', $css_value_fb_hover )
$additional_classes .= ' et_pb_css_filters_hover';
return $additional_classes;
* Convert classes array to a string. Also removes any duplicate classes
* @param array $classes A list of CSS classnames
function stringify_css_filter_classes( $classes ) {
// Remove repeating classes
$classes = array_unique( $classes );
// Transform classes to a string
$classes = ' ' . implode( ' ', $classes );
* Adds a suffix at the end of the selector
* E.g: add_suffix_to_selectors(':hover', '%%order_class%%% .image') >>> '%%order_class%%% .image:hover'
* @param string $selector
public function add_suffix_to_selectors( $suffix, $selector ) {
$selectors = explode( ',', $selector );
$selectors = array_map( 'trim', $selectors );
foreach ( $selectors as &$selector ) {
return implode( ', ', $selectors );
* Adds `:hover` in selector at the end of the selector
* E.g: add_hover_to_selectors('%%order_class%%% .image') >>> '%%order_class%%% .image:hover'
* @param string $selector
* @deprecated Use et_pb_hover_options()->add_hover_to_selectors( $selector );
public function add_hover_to_selectors( $selector ) {
return et_pb_hover_options()->add_hover_to_selectors( $selector );
* Adds `:hover` in selector at the end of the selector if $add_hover is true
* otherwise returns the original selector
* @param string $selector
protected function _maybe_add_hover_to_selectors( $selector, $add_hover = false ) {
return $add_hover ? et_pb_hover_options()->add_hover_to_selectors( $selector ) : $selector;
* Adds `:hover` in selector after `%%order_class%%`
* E.g: add_hover_to_order_class('%%order_class%%% .image') >>> '%%order_class%%%:hover .image'
* @param string $selector
* @deprecated Use et_pb_hover_options()->add_hover_to_order_class( $selector );
public function add_hover_to_order_class( $selector ) {
return et_pb_hover_options()->add_hover_to_order_class( $selector );
* Adds `:hover` to order class only if is specified, in other cse returns original selector
* otherwise returns the original selector
* @param string $selector
protected function _maybe_add_hover_to_order_class( $selector, $add_hover = false ) {
return $add_hover ? et_pb_hover_options()->add_hover_to_order_class( $selector ) : $selector;
* Convert smart quotes and & entity to their applicable characters
* @param string $text Input text
static function convert_smart_quotes_and_amp( $text ) {
if ( 'fr_FR' === get_locale() ) {
$french_smart_quotes = array(
$french_replacements = array(
$smart_quotes = array_merge( $smart_quotes, $french_smart_quotes );
$replacements = array_merge( $replacements, $french_replacements );
$text = str_replace( $smart_quotes, $replacements, $text );
public function process_multiple_checkboxes_field_value( $value_map, $value ) {
foreach ( explode( '|', $value ) as $checkbox_value ) {
if ( 'on' === $checkbox_value ) {
$result[] = $value_map[ $index ];
return implode( '|', $result );
* Adds one or more CSS classes to the module on the frontend.
* @param string|array $to_add classname(s) to be added
* @param number|bool $position position of added classname (0-based). Some class need to be placed
* at exact position. i.e. .et_pb_column_{$type} on column inner
function add_classname( $to_add, $position = false ) {
if ( empty( $to_add ) ) {
$classname = is_array( $to_add ) ? $to_add : array( $to_add );
if ( is_numeric( $position ) ) {
array_splice($this->classname, intval( $position ), 0, $classname );
$this->classname = array_merge( $this->classname, $classname );
* Removes one ore more CSS classes to the module on the frontend
* @param string|array $to_remove classname(s) to be removed
function remove_classname( $to_remove ) {
$this->classname = array_filter( $this->classname );
if ( is_string( $to_remove ) && '' !== $to_remove ) {
$this->classname = array_diff( $this->classname, array( $to_remove ) );
} elseif ( is_array( $to_remove ) ) {
$to_remove = array_filter( $to_remove );
$this->classname = array_diff( $this->classname, $to_remove );
* @return string escaped class
function module_classname( $function_name = '' ) {
if ( ! in_array( $function_name, ET_Builder_Element::$uses_module_classname ) ) {
// Add module slug to array of modules where `module_classname()` used
ET_Builder_Element::$uses_module_classname[] = $function_name;
$module_name = str_replace( 'et_pb_', '', $this->slug );
* Filters module classes.
* @param array $classname Array of classnames.
* @param int $render_count Number of times render function has been executed
$classname = (array) array_unique( apply_filters( "et_builder_{$module_name}_classes", $this->classname, $this->render_count() ) );
return implode( ' ', array_map( 'esc_attr', $classname ) );
* @param bool $include_attribute wrap module id with id attribute name or not (to be used directly on module div)
* @return string module id / module id wrapped by id attribute
function module_id( $include_attribute = true ) {
$module_id = esc_attr( $this->props['module_id'] );
$output = $include_attribute ? sprintf( ' id="%1$s"', $module_id ) : $module_id;
return '' !== $module_id ? $output : '';
* Helper method for rendering button markup which works compatible with advanced options' button
* @param array $args button settings
* @return string rendered button HTML
function render_button( $args = array() ) {