: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
public function get_transition_height_fields_css_props( $prefix = '' ) {
$options = self::$_->array_get( $this->advanced_fields, 'height' );
if ( ! is_array( $options ) ) {
$height = et_pb_height_options( $prefix );
$max_height = et_pb_max_height_options( $prefix );
$selector = self::$_->array_get( $options, 'css.main', '%%order_class%%' );
$height->get_field() => array( 'height' => $selector ),
$max_height->get_field() => array( 'max-height' => $selector ),
public function get_transition_image_fields_css_props() {
$fields = array_merge( $this->get_transition_filters_fields_css_props( 'image' ), $fields );
$fields = array_merge( $this->get_transition_borders_fields_css_props( 'image' ), $fields );
$fields = array_merge( $this->get_transition_box_shadow_fields_css_props( 'image' ), $fields );
public function get_transition_button_fields_css_props() {
$buttons = self::$_->array_get( $this->advanced_fields, 'button', array() );
if ( empty( $buttons ) ) {
foreach ( $buttons as $key => $button ) {
$selector = self::$_->array_get( $button, 'css.main', '%%order_class%%' );
"{$key}_text_color" => array( 'color' => $selector, ),
"{$key}_text_size" => array(
'font-size' => $selector,
'line-height' => $selector,
"{$key}_bg_color" => array( 'background-color' => $selector, ),
"{$key}_border_width" => array( 'border' => $selector, ),
"{$key}_border_color" => array( 'border' => $selector, ),
"{$key}_border_radius" => array( 'border-radius' => $selector, ),
"{$key}_letter_spacing" => array( 'letter-spacing' => $selector, ),
"{$key}text_shadow_horizontal_length" => array( 'text-shadow' => $selector, ),
"{$key}text_shadow_vertical_length" => array( 'text-shadow' => $selector, ),
"{$key}text_shadow_blur_strength" => array( 'text-shadow' => $selector, ),
"{$key}text_shadow_color" => array( 'text-shadow' => $selector, ),
"box_shadow_style_$key" => array(
'box-shadow' => implode( ', ',
$this->add_suffix_to_selectors( ' > .box-shadow-overlay', $selector ),
$fields = array_merge( $fields, $field );
* Get transition form field CSS props.
* @return array Selector for each fields.
public function get_transition_form_field_fields_css_props() {
$fields_input = self::$_->array_get( $this->advanced_fields, 'form_field', array() );
// Ensure fields input is exist.
if ( empty( $fields_input ) ) {
foreach ( $fields_input as $key => $form_field ) {
$selector = self::$_->array_get( $form_field, 'css.main', '%%order_class%% input' );
$placeholders = "$selector::placeholder, $selector::-webkit-input-placeholder, $selector::-moz-placeholder, $selector::-ms-input-placeholder";
// Set all individual fields that need transition during hover event.
$fields = array_merge( $fields, array(
"{$key}_background_color" => array( 'background-color' => $selector ),
"{$key}_text_color" => array( 'color' => implode( ', ', array( $placeholders, $selector ) ) ),
"{$key}_focus_background_color" => array( 'background-color' => $selector ),
"{$key}_focus_text_color" => array( 'color' => implode( ', ', array( $placeholders, $selector ) ) ),
"{$key}_custom_margin" => array( 'margin' => $selector ),
"{$key}_custom_padding" => array( 'padding' => $selector ),
// Merge group fields such as borders, box shadow, and text shadow.
$this->get_transition_borders_fields_css_props( $key ),
$this->get_transition_borders_fields_css_props( "{$key}_focus" ),
$this->get_transition_box_shadow_fields_css_props( $key )
public function get_transition_gutter_fields_css_props() {
$gutter_selector = 'et_pb_section' === $this->slug ? '%%order_class%% .et_pb_gutter_hover *' : '%%order_class%%.et_pb_gutter_hover *';
// animate width, padding and margin if gutter width has hover options
'width' => $gutter_selector,
'margin' => $gutter_selector,
'padding' => $gutter_selector,
* Get CSS fields transition.
* @since 3.23 Add form field options group and background image on the fields list.
public function get_transition_fields_css_props() {
$default = $this->main_css_element;
$text_main = self::$_->array_get( $this->advanced_fields, 'text.css.main', $default );
'background_layout' => array( 'color' => $text_main, ),
'background-color' => self::$_->array_get( $this->advanced_fields,
'background-image' => self::$_->array_get( $this->advanced_fields,
'max_width' => array( 'max-width' => $default, ),
'width' => array( 'width' => $default, ),
'color' => self::$_->array_get( $this->advanced_fields,
$fields = array_merge( $this->get_transition_filters_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_box_shadow_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_text_shadow_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_image_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_borders_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_margin_padding_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_button_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_form_field_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_font_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_gutter_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_height_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_transform_css_props(), $fields );
$fields = array_merge( $this->get_transition_position_css_props(), $fields );
return apply_filters( 'et_builder_hover_transitions_map', $fields );
* Add link options fields to all modules
protected function _add_link_options_fields() {
// Link Options are added by default if module has partial or full VB support
if ( $this->has_vb_support() ) {
$this->advanced_fields['link_options'] = self::$_->array_get( $this->advanced_fields, 'link_options', array() );
} else if ( ! $this->has_advanced_fields ) {
// Disable if module doesn't set advanced_fields property and has no VB support
// Set link_options to false to disable Link options.
if ( false === self::$_->array_get( $this->advanced_fields, 'link_options' ) ) {
// Link options settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'link_options' ) ) ) {
$this->settings_modal_toggles['general']['toggles']['link_options'] = array(
'title' => et_builder_i18n( 'Link' ),
$additional_options = array();
if ( ! isset( $i18n['link'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
'label' => esc_html__( 'Module Link URL', 'et_builder' ),
'description' => esc_html__( 'When clicked the module will link to this URL.', 'et_builder' ),
'label' => esc_html__( 'Module Link Target', 'et_builder' ),
'description' => esc_html__( 'Here you can choose whether or not your link opens in a new window', 'et_builder' ),
'off' => esc_html__( 'In The Same Window', 'et_builder' ),
'on' => esc_html__( 'In The New Tab', 'et_builder' ),
// Translate the whole label as a phrase instead of replacing placeholder with section / row / module translation
// Less error prone for translator and the translation. Phrase might be structured differently in some language
$url_label = esc_html__( 'Section Link URL', 'et_builder' );
$target_label = esc_html__( 'Section Link Target', 'et_builder' );
$url_label = esc_html__( 'Row Link URL', 'et_builder' );
$target_label = esc_html__( 'Row Link Target', 'et_builder' );
case 'et_pb_column_inner':
$url_label = esc_html__( 'Column Link URL', 'et_builder' );
$target_label = esc_html__( 'Column Link Target', 'et_builder' );
$url_label = $i18n['link']['url']['label'];
$target_label = $i18n['link']['target']['label'];
$additional_options['link_option_url'] = array(
'description' => $i18n['link']['url']['description'],
'option_category' => 'configuration',
'toggle_slug' => 'link_options',
'dynamic_content' => 'url',
$additional_options['link_option_url_new_window'] = array(
'label' => $target_label,
'description' => $i18n['link']['target']['description'],
'option_category' => 'configuration',
'off' => $i18n['link']['target']['options']['off'],
'on' => $i18n['link']['target']['options']['on'],
'toggle_slug' => 'link_options',
'default_on_front' => 'off',
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
* @since 3.23 Add $device parameter to support responsive settings.
public function get_transition_style( array $props = array(), $device = 'desktop' ) {
$duration = et_pb_transition_options()->get_duration( $this->props, $device );
$easing = et_pb_transition_options()->get_easing( $this->props, $device );
$delay = et_pb_transition_options()->get_delay( $this->props, $device );
$transition_css = array();
foreach ( $props as $prop ) {
$transition_css[] = sprintf(
return 'transition: ' . implode( ', ', $transition_css ) . ';';
function setup_hover_transitions( $function_name ) {
// List of all property names and their respective CSS property names
$transitions_map = $this->get_transition_fields_css_props();
$hover = et_pb_hover_options();
$hover_suffix = $hover->get_suffix();
$enabled_suffix = $hover->get_enabled_suffix();
// We need to loop transitions array so cases of prefixed prop names can also be caught
foreach ( $transitions_map as $prop_name => $css_props ) {
$key_hover = "{$prop_name}{$hover_suffix}";
$key_enabled = "{$prop_name}{$enabled_suffix}";
// Background is a special case because it also contains the "background_color" property
if ( 'background' === $prop_name ) {
// We can continue if hover background color is not set because it is the only animatable property
$hover_background_color_field = $hover->get_hover_field( "background_color" );
if ( empty( $this->props[$hover_background_color_field] ) ) {
} else if ( empty( $this->props[ $key_hover ] ) ) {
// Continue if {property_name}__hover is empty (ie. no hover value is set)
// Continue if {property_name}__hover_enabled is not defined/"on"
if ( empty( $this->props[ $key_enabled ] ) || 0 !== strpos( $this->props[ $key_enabled ], 'on' ) ) {
// Add the CSS property for the transition
$transitions = array_merge( $transitions, array_keys( $css_props ) );
foreach ( $css_props as $selector ) {
$selector = is_array( $selector ) ? $selector : array( $selector );
$selectors = array_merge( $selectors, $selector );
// Don't apply transitions if none are needed
if ( empty( $transitions ) ) {
$transition_style = $this->get_transition_style( $transitions );
self::set_style( $function_name, array(
'selector' => implode( ', ', array_unique( $selectors ) ),
'declaration' => esc_html( $transition_style )
$transition_style_tablet = $this->get_transition_style( $transitions, 'tablet' );
if ( $transition_style_tablet !== $transition_style ) {
self::set_style( $function_name, array(
'selector' => implode( ', ', array_unique( $selectors ) ),
'declaration' => esc_html( $transition_style_tablet ),
'media_query' => ET_Builder_Element::get_media_query( 'max_width_980' ),
$transition_style_phone = $this->get_transition_style( $transitions, 'phone' );
if ( $transition_style_phone !== $transition_style || $transition_style_phone !== $transition_style_tablet ) {
self::set_style( $function_name, array(
'selector' => implode( ', ', array_unique( $selectors ) ),
'declaration' => esc_html( $transition_style_phone ),
'media_query' => ET_Builder_Element::get_media_query( 'max_width_767' ),
protected function _add_custom_css_fields() {
if ( isset( $this->custom_css_tab ) && ! $this->custom_css_tab ) {
$custom_css_fields_processed = array();
$current_module_unique_class = '.' . $this->slug . '_' . "<%= typeof( module_order ) !== 'undefined' ? module_order : '<span class=\"et_pb_module_order_placeholder\"></span>' %>";
$main_css_element_output = isset( $this->main_css_element ) ? $this->main_css_element : '%%order_class%%';
$main_css_element_output = str_replace( '%%order_class%%', $current_module_unique_class, $main_css_element_output );
$custom_css_default_options = array(
'label' => et_builder_i18n( 'Before' ),
'no_space_before_selector' => true,
'label' => et_builder_i18n( 'Main Element' ),
'label' => et_builder_i18n( 'After' ),
'no_space_before_selector' => true,
$custom_css_fields = apply_filters( 'et_default_custom_css_fields', $custom_css_default_options );
if ( $this->custom_css_fields = $this->get_custom_css_fields_config() ) {
$custom_css_fields = array_merge( $custom_css_fields, $this->custom_css_fields );
$this->custom_css_fields = apply_filters( 'et_custom_css_fields_' . $this->slug, $custom_css_fields );
// optional settings names in custom css options
$additional_option_slugs = array( 'description', 'priority' );
foreach ( $custom_css_fields as $slug => $option ) {
$selector_value = isset( $option['selector'] ) ? $option['selector'] : '';
$selector_contains_module_class = false !== strpos( $selector_value, '%%order_class%%' ) ? true : false;
$selector_output = '' !== $selector_value ? str_replace( '%%order_class%%', $current_module_unique_class, $option['selector'] ) : '';
$custom_css_fields_processed[ "custom_css_{$slug}" ] = array(
'%1$s:<span>%2$s%3$s%4$s</span>',
! $selector_contains_module_class ? $main_css_element_output : '',
! isset( $option['no_space_before_selector'] ) && isset( $option['selector'] ) ? ' ' : '',
'tab_slug' => 'custom_css',
'toggle_slug' => 'custom_css',
'option_category' => 'layout',
// update toggle slug and option category for $this->custom_css_fields
$this->custom_css_fields[ $slug ]['toggle_slug'] = 'custom_css';
$this->custom_css_fields[ $slug ]['option_category'] = 'layout';
// add optional settings if needed
foreach ( $additional_option_slugs as $option_slug ) {
if ( isset( $option[ $option_slug ] ) ) {
$custom_css_fields_processed[ "custom_css_{$slug}" ][ $option_slug ] = $option[ $option_slug ];
if ( ! empty( $custom_css_fields_processed ) ) {
$this->fields_unprocessed = array_merge( $this->fields_unprocessed, $custom_css_fields_processed );
if ( ! isset( $i18n['css'] ) ) {
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
'classes' => esc_html__( 'CSS ID & Classes', 'et_builder' ),
$default_custom_css_toggles = array(
'classes' => $i18n['css']['classes'],
'custom_css' => et_builder_i18n( 'Custom CSS' ),
$this->_add_settings_modal_toggles( 'custom_css', $default_custom_css_toggles );
protected function _add_settings_modal_toggles( $tab_slug, $toggles_array ) {
if ( ! isset( $this->settings_modal_toggles[ $tab_slug ] ) ) {
$this->settings_modal_toggles[ $tab_slug ] = array();
if ( ! isset( $this->settings_modal_toggles[ $tab_slug ]['toggles'] ) ) {
$this->settings_modal_toggles[ $tab_slug ]['toggles'] = array();
// get the only toggles which do not exist.
$processed_toggles = array_diff_key( $toggles_array, $this->settings_modal_toggles[ $tab_slug ]['toggles'] );
$this->settings_modal_toggles[ $tab_slug ]['toggles'] = array_merge( $this->settings_modal_toggles[ $tab_slug ]['toggles'], $processed_toggles );
* Add settings under sub toggles.
* @since 3.26.7 Add support to set custom icons on sub toggles.
* @param string $tab_slug Current tab slug.
* @param string $toggle_slug Current toggle slug.
* @param array $sub_toggle_items Sub toggles settings need to be added.
* @param boolean $tabbed_subtoggles Tabbed sub toggle status.
* @param boolean $bb_icons_support BB icons support status.
protected function _add_settings_modal_sub_toggles( $tab_slug, $toggle_slug, $sub_toggle_items, $tabbed_subtoggles = false, $bb_icons_support = false ) {
// Ensure tab slug is exist.
if ( ! isset( $this->settings_modal_toggles[ $tab_slug ] ) ) {
$this->settings_modal_toggles[ $tab_slug ] = array();