*/
protected $attrs_unprocessed = array();
/**
* Unprocessed content.
*
* @since 3.17.2
*
* @var string
*/
protected $content_unprocessed = '';
/**
* Settings used to render the module's output.
*
* @since 3.1 Renamed from `$shortcode_atts` to `$props`.
* @since 1.0
*
* @var array
*/
public $props = array();
/**
* What appears inside the element. For structure elements, this will contain children
* elements. For parent modules, this will include child modules.
*
* @since 3.1 Renamed from `$shortcode_content` to `$content`.
* @since 1.0
*
* @var string
*/
public $content;
/**
* Configuration for module's wrapper and inner wrapper
*
* @since 3.1
*
* @var array
*/
public $wrapper_settings = array();
public $fields_unprocessed = array();
public $main_css_element;
public $custom_css_fields = array();
public $child_title_var;
public $child_title_fallback_var;
public $post_types = array();
public $main_tabs = array();
public $used_tabs = array();
public $custom_css_tab;
public $vb_support = 'off';
public $dbl_quote_exception_options = array( 'et_pb_font_icon', 'et_pb_button_one_icon', 'et_pb_button_two_icon', 'et_pb_button_icon', 'et_pb_content' );
public $settings_modal_tabs = array();
public $settings_modal_toggles = array();
public $featured_image_background = false;
public $classname = array();
public $help_videos = array();
public static $settings_migrations_initialized = false;
public static $setting_advanced_styles = false;
public static $uses_module_classname = array();
private static $_current_section_index = -1;
private static $_current_row_index = -1;
private static $_current_row_inner_index = -1;
private static $_current_column_index = -1;
private static $_current_column_inner_index = -1;
private static $_current_module_index = -1;
private static $_current_module_item_index = -1;
private static $_unique_bb_keys_map = array();
private static $_unique_bb_keys_values = array();
private static $_unique_bb_strip = array( "\t", "\r", "\n" );
/**
* Number of times {@see self::render()} has been executed.
*
* @var int
*/
private $_render_count;
/**
* Number of times {@see self::render()} has been executed for the shop module.
*
* @var int
*/
private static $_shop_render_count = 0;
/**
* Slug of a module whose render count should also be bumped when this module's is bumped.
*
* @var string
*/
protected $_bumps_render_count;
/**
* Priority number applied to some CSS rules.
*
* @var int
*/
protected $_style_priority;
// only needed for BB + hover
protected $is_background = false;
private $_is_official_module;
/**
* Holds module styles for the current request.
*
* @var array
*/
private static $styles = array();
private static $internal_modules_styles = array();
private static $prepare_internal_styles = false;
private static $internal_modules_counter = 10000;
private static $media_queries = array();
private static $modules_order;
private static $inner_modules_order;
private static $modules = array();
private static $parent_modules = array();
private static $child_modules = array();
private static $current_module_index = 0;
private static $structure_modules = array();
private static $structure_module_slugs = array();
private static $_module_slugs_by_post_type = array();
private static $module_icons = array();
private static $module_help_videos = array();
private static $loading_backbone_templates = false;
/**
* @var ET_Core_Data_Utils
*/
protected static $_ = null;
/**
* @var ET_Core_PageResource
*/
public static $advanced_styles_manager = null;
/**
* @var ET_Core_Data_Utils
*/
public static $data_utils = null;
public static $field_dependencies = array();
public static $can_reset_element_indexes = true;
const DEFAULT_PRIORITY = 10;
const HIDE_ON_MOBILE = 'et-hide-mobile';
protected $module_credits;
function __construct() {
self::$current_module_index++;
if ( ! self::$_deprecations ) {
self::$_deprecations = require ET_BUILDER_DIR . 'deprecations.php';
self::$_deprecations = self::$_deprecations['classes']['\ET_Builder_Module_Blurb'];
}
if ( ! self::$settings_migrations_initialized ) {
self::$settings_migrations_initialized = true;
require_once 'module/settings/Migration.php';
ET_Builder_Module_Settings_Migration::init();
add_filter( 'the_content', array( get_class( $this ), 'reset_element_indexes' ), 9999 );
}
if ( self::$loading_backbone_templates || et_admin_backbone_templates_being_loaded() ) {
if ( ! self::$loading_backbone_templates ) {
self::$loading_backbone_templates = true;
}
$start_from = (int) sanitize_text_field( $_POST['et_templates_start_from'] );
$post_type = sanitize_text_field( $_POST['et_post_type'] );
if ( 'layout' === $post_type ) {
// need - 2 to include the et_pb_section and et_pb_row modules
$start_from = ET_Builder_Element::get_modules_count( 'page' ) - 2;
}
$current_module_index = self::$current_module_index - 1;
if ( ! ( $current_module_index >= $start_from && $current_module_index < ( ET_BUILDER_AJAX_TEMPLATES_AMOUNT + $start_from ) ) ) {
return;
}
}
if ( null === self::$advanced_styles_manager && ! is_admin() && ! et_fb_is_enabled() ) {
self::_setup_advanced_styles_manager();
}
if ( null === self::$data_utils ) {
self::$_ = self::$data_utils = ET_Core_Data_Utils::instance();
}
$this->init();
$this->settings_modal_tabs = $this->get_settings_modal_tabs();
$this->settings_modal_toggles = $this->get_settings_modal_toggles();
$this->custom_css_fields = $this->get_custom_css_fields_config();
$this->_set_advanced_fields_config();
$this->_is_official_module = self::_is_official_module( get_class( $this ) );
$this->make_options_filterable();
$this->set_fields();
$this->set_factory_objects();
$this->_additional_fields_options = array();
$this->_add_additional_fields();
$this->_add_custom_css_fields();
$this->_maybe_add_global_defaults();
$this->_finalize_all_fields();
if ( ! isset( $this->main_css_element ) ) {
$this->main_css_element = '%%order_class%%';
}
$this->_render_count = 0;
$this->type = isset( $this->type ) ? $this->type : '';
$this->_style_priority = (int) self::DEFAULT_PRIORITY;
if ( isset( $this->type ) && 'child' === $this->type ) {
$this->_style_priority = $this->_style_priority + 1;
} else {
// add default toggles
$default_general_toggles = array(
'admin_label' => array(
'title' => esc_html__( 'Admin Label', 'et_builder' ),
'priority' => 99,
),
);
$default_advanced_toggles = array(
'visibility' => array(
'title' => esc_html__( 'Visibility', 'et_builder' ),
'priority' => 99,
),
);
$this->_add_settings_modal_toggles( 'general', $default_general_toggles );
$this->_add_settings_modal_toggles( 'custom_css', $default_advanced_toggles );
}
$this->main_tabs = $this->get_main_tabs();
$this->custom_css_tab = isset( $this->custom_css_tab ) ? $this->custom_css_tab : true;
self::$modules[ $this->slug ] = $this;
$post_types = ! empty( $this->post_types ) ? $this->post_types : et_builder_get_builder_post_types();
// all modules should be assigned for et_pb_layout post type to work in the library
if ( ! in_array( 'et_pb_layout', $post_types ) ) {
$post_types[] = 'et_pb_layout';
}
$this->post_types = apply_filters( 'et_builder_module_post_types', $post_types, $this->slug, $this->post_types );
foreach ( $this->post_types as $post_type ) {
if ( ! in_array( $post_type, $this->post_types ) ) {
$this->register_post_type( $post_type );
}
if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ] = array();
}
if ( ! in_array( $this->slug, self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ][] = $this->slug;
}
if ( isset( $this->additional_shortcode ) && ! in_array( $this->additional_shortcode, self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ][] = $this->additional_shortcode;
}
if ( isset( $this->additional_shortcode_slugs ) ) {
foreach ( $this->additional_shortcode_slugs as $additional_shortcode_slug ) {
if ( ! in_array( $additional_shortcode_slug, self::$_module_slugs_by_post_type[ $post_type ] ) ) {
self::$_module_slugs_by_post_type[ $post_type ][] = $additional_shortcode_slug;
}
}
}
if ( 'child' === $this->type ) {
self::$child_modules[ $post_type ][ $this->slug ] = $this;
} else {
self::$parent_modules[ $post_type ][ $this->slug ] = $this;
}
}
if ( ! isset( $this->no_render ) ) {
$shortcode_slugs = array( $this->slug );
if ( ! empty( $this->additional_shortcode_slugs ) ) {
$shortcode_slugs = array_merge( $shortcode_slugs, $this->additional_shortcode_slugs );
}
foreach ( $shortcode_slugs as $shortcode_slug ) {
add_shortcode( $shortcode_slug, array( $this, '_render' ) );
}
if ( isset( $this->additional_shortcode ) ) {
add_shortcode( $this->additional_shortcode, array( $this, 'additional_render' ) );
}
}
if ( isset( $this->icon ) ) {
self::$_->array_set( self::$module_icons, "{$this->slug}.icon", $this->icon );
}
if ( isset( $this->icon_path ) ) {
self::$_->array_set( self::$module_icons, "{$this->slug}.icon_path", $this->icon_path );
}
// Push module's help videos to all help videos array if there's any
if ( ! empty( $this->help_videos ) ) {
// Automatically add design tab and library tutorial. DRY
if ( 'et_pb_column' !== $this->slug ) {
// Adding next tabs (design & tab) helps
$next_tabs_help = array(
'id' => esc_html__( '1iqjhnHVA9Y', 'et_builder' ),
'name' => esc_html__( 'Design Settings and Advanced Module Settings', 'et_builder' ),
);
// Adjust row name
if ( in_array( $this->slug, array( 'et_pb_row', 'et_pb_row_inner' ) ) ) {
$next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Row Settings', 'et_builder' );
}
// Adjust section name
if ( 'et_pb_section' === $this->slug ) {
$next_tabs_help['name'] = esc_html__( 'Design Settings and Advanced Section Settings', 'et_builder' );
}
$this->help_videos[] = $next_tabs_help;
// Adding Divi Library helps
$this->help_videos[] = array(
'id' => esc_html( 'boNZZ0MYU0E' ),
'name' => esc_html__( 'Saving and loading from the library', 'et_builder' ),
);
}
self::$module_help_videos[ $this->slug ] = $this->help_videos;
}
}
public function __call( $name, $args ) {
$class = get_class( $this );
$message = "You're Doing It Wrong!";
$is_deprecated = array_key_exists( $name, self::$_deprecations['methods'] );
$value = null;
$old_method_exists = method_exists( $this, $name );
if ( $old_method_exists && ! $is_deprecated ) {
// Inaccessible method (protected or private) that isn't deprecated
et_debug( "{$message} Attempted to call {$class}::{$name}() from out of scope.", 4, false );
return $value;
}
$message .= " {$class}::{$name}()";
if ( ! $is_deprecated ) {
$message .= " doesn't exist.";
} else {
$message .= " is deprecated.";
$new_method = self::$_deprecations['methods'][ $name ];
if ( ! is_string( $new_method ) ) {
// Default value for a method that has no replacement.
$value = $new_method;
} else if ( method_exists( $this, $new_method ) && ! $old_method_exists ) {
$message .= " Use {$class}::{$new_method}() instead.";
$value = call_user_func_array( array( $this, $new_method ), $args );
} else if ( $old_method_exists ) {
// Ensure that our current caller is not the same as the method we're about to call.
// as that would cause an infinite recursion situation. It happens when a child class
// method which has been deprecated calls itself on the parent class (using parent::)
$trace = debug_backtrace();
$callers = array(
self::$_->array_get( $trace, '1.function' ),
self::$_->array_get( $trace, '2.function' ),
);
if ( ! in_array( $name, $callers ) ) {
// We've used $this->__call() to call a deprecated method from its replacement
// method so that a deprecation notice will be output.
$message .= " Use {$class}::{$new_method}() instead.";
$value = call_user_func_array( array( $this, $name ), $args );
}
}
}
et_debug( $message, 4, false );
return $value;
}
public function &__get( $name ) {
$class = get_class( $this );
$message = "You're Doing It Wrong!";
$is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] );
$value = null;
if ( property_exists( $this, $name ) && ! $is_deprecated ) {
// Inaccessible property (protected or private) that isn't deprecated
et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false );
return $value;
}
$message .= " {$class}::\${$name}";
if ( ! $is_deprecated ) {
$message .= " doesn't exist.";
$should_set_value = true;
} else {
$message .= " is deprecated.";
$new_prop = self::$_deprecations['properties'][ $name ];
if ( $new_prop && is_string( $new_prop ) && property_exists( $this, $new_prop ) ) {
$message .= " Use {$class}::\${$new_prop} instead.";
$value = &$this->$new_prop;
} else if ( ! is_string( $new_prop ) || ! $new_prop ) {
// Default value
$value = $new_prop;
$should_set_value = true;
}
}
if ( isset( $should_set_value ) ) {
// Create the property so we can return a reference to it which allows it to be
// used like this: $this->name[] = 'something'
$this->$name = $value;
$value = &$this->$name;
}
et_debug( $message, 4, false );
return $value;
}
public function __isset( $name ) {
$prop_name = array_key_exists( $name, self::$_deprecations['properties'] ) ? self::$_deprecations['properties'][ $name ] : $name;
if ( ! $prop_name || ! is_string( $prop_name ) ) {
return false;
}
return property_exists( $this, $prop_name );
}
public function __set( $name, $value ) {
$class = get_class( $this );
$message = "You're Doing It Wrong!";
$is_deprecated = array_key_exists( $name, self::$_deprecations['properties'] );
$property_exists = property_exists( $this, $name );
$has_replacement = $property_exists && is_string( self::$_deprecations['properties'][ $name ] ) && self::$_deprecations['properties'][ $name ];
if ( $property_exists && ! $is_deprecated ) {
// Inaccessible property (protected or private) that isn't deprecated
et_debug( "{$message} Attempted to access {$class}::\${$name} from out of scope.", 4, false );
return;
}
if ( ( ! $property_exists && ! $is_deprecated ) || ! $has_replacement ) {
// Always allow setting values for properties that are undeclared
$this->$name = $value;
}
if ( ! $is_deprecated ) {
return;
}
$message = " {$class}::\${$name} is deprecated.";
$replacement = self::$_deprecations['properties'][ $name ];
if ( $replacement && is_string( $replacement ) ) {
$message .= " Use {$class}::\${$replacement} instead.";
$this->$replacement = $value;
// Unset deprecated property so next time it's updated we process it again
unset( $this->$name );
}
et_debug( $message, 4, false );
}
private static function _is_official_module( $class_name ) {
try {
$reflection = new ReflectionClass( $class_name );
$is_official = self::$_->includes( $reflection->getFileName(), ET_BUILDER_DIR_RESOLVED_PATH );
} catch( Exception $err ) {
$is_official = false;
}
return $is_official;
}
protected function _set_advanced_fields_config() {
$this->advanced_fields = $this->get_advanced_fields_config();
// 3rd-Party Backwards Compatability
if ( isset( $this->advanced_fields['custom_margin_padding'] ) ) {
$this->advanced_fields['margin_padding'] = $this->advanced_fields['custom_margin_padding'];
unset( $this->advanced_fields['custom_margin_padding'] );
}
}
/**
* Retrieve Post ID from 1 of 3 sources depending on which exists:
* - $_POST['et_post_id']
* - $_GET['post']
* - get_the_ID()
* Similar to get_the_ID() but in reverse order and statically callable.
*
* @since 3.17.2
*
* @return int|bool
*/
public static function get_current_post_id() {
if ( wp_doing_ajax() && isset( $_POST['et_post_id'] ) ) {
return absint( $_POST['et_post_id'] );
}
if ( isset( $_POST['post'] ) ) {
return absint( $_POST['post'] );
}
return get_the_ID();
}
/**
* Get the current ID depending on the current request.
*
* @return int|bool
*/
public function get_the_ID() {
$post_id = get_the_ID();
// try to get post id from get_post_ID()
if ( false !== $post_id ) {
return $post_id;
}
if ( wp_doing_ajax() ) {
// get the post ID if loading data for VB
return isset( $_POST['et_post_id'] ) ? absint( $_POST['et_post_id'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
}
// fallback to $_GET['post'] to cover the BB data loading
return isset( $_GET['post'] ) ? absint( $_GET['post'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
}
/**
* Setup the advanced styles manager
*
* {@internal
* Before the styles manager was implemented, the advanced styles were output inline in the footer.
* That resulted in them being the last styles parsed by the browser, thus giving them higher
* priority than other styles on the page. With the styles manager, the advanced styles are
* enqueued at the very end of the
. This is for backwards compatibility (to maintain
* the same priority for the styles as before).}}
*/
private static function _setup_advanced_styles_manager() {
if ( et_core_page_resource_is_singular() ) {
$post_id = et_core_page_resource_get_the_ID();
} else {
$post_id = 0; // It doesn't matter because we're going to force inline styles.
}
$is_preview = is_preview() || is_et_pb_preview();
$forced_in_footer = $post_id && et_builder_setting_is_on( 'et_pb_css_in_footer', $post_id );
$forced_inline = ! $post_id || $is_preview || $forced_in_footer || et_builder_setting_is_off( 'et_pb_static_css_file', $post_id );
$unified_styles = ! $forced_inline && ! $forced_in_footer;
$resource_owner = $unified_styles ? 'core' : 'builder';
$resource_slug = $unified_styles ? 'unified' : 'module-design';
$resource_slug .= $unified_styles && et_builder_post_is_of_custom_post_type( $post_id ) ? '-cpt' : '';
// If the post is password protected and a password has not been provided yet,
// no content (including any custom style) will be printed.
// When static css file option is enabled this will result in missing styles.
if ( ! $forced_inline && post_password_required() ) {
$forced_inline = true;
}
if ( $is_preview ) {
// Don't let previews cause existing saved static css files to be modified.
$resource_slug .= '-preview';
}
self::$advanced_styles_manager = et_core_page_resource_get( $resource_owner, $resource_slug, $post_id, 40 );
if ( ! $forced_inline && ! $forced_in_footer && self::$advanced_styles_manager->has_file() ) {
// This post currently has a fully configured styles manager.
return;
}
self::$advanced_styles_manager->forced_inline = $forced_inline;
self::$advanced_styles_manager->write_file_location = 'footer';
if ( $forced_in_footer || $forced_inline ) {
// Restore legacy behavior--output inline styles in the footer.
self::$advanced_styles_manager->set_output_location( 'footer' );
}
// Schedule callback to run in the footer so we can pass the module design styles to the page resource.
add_action( 'wp_footer', array( 'ET_Builder_Element', 'set_advanced_styles' ), 19 );
// Add filter for the resource data so we can prevent theme customizer css from being
// included with the builder css inline on first-load (since its in the head already).
add_filter( 'et_core_page_resource_get_data', array( 'ET_Builder_Element', 'filter_page_resource_data' ), 10, 3 );
}
/**
* Passes the module design styles for the current page to the advanced styles manager.
* {@see 'wp_footer' (19) Must run before the style manager's footer callback}
*/
public static function set_advanced_styles() {
$styles = self::get_style() . self::get_style( true );
if ( et_core_is_builder_used_on_current_request() ) {
$styles .= et_pb_get_page_custom_css();
}
if ( ! $styles ) {
return;
}
// Pass styles to page resource which will handle their output
self::$advanced_styles_manager->set_data( $styles, 40 );
}
/**
* Filters the unified page resource data. The data is an array of arrays of strings keyed by
* priority. The builder's styles are set with a priority of 40. Here we want to make sure
* only the builder's styles are output in the footer on first-page load so we aren't
* duplicating the customizer and custom css styles which are already in the .
* {@see 'et_core_page_resource_get_data'}
*/
public static function filter_page_resource_data( $data, $context, $resource ) {
global $wp_current_filter;
if ( 'inline' !== $context || ! in_array( 'wp_footer', $wp_current_filter ) ) {
return $data;
}
if ( false === strpos( $resource->slug, 'unified' ) ) {
return $data;
}
if ( 'footer' !== $resource->location ) {
// This is the first load of a page that doesn't currently have a unified static css file.
// The theme customizer and custom css have already been inlined in the using the
// unified resource's ID. It's invalid HTML to have duplicated IDs on the page so we'll
// fix that here since it only applies to this page load anyway.
$resource->slug = $resource->slug . '-2';
}
return isset( $data[40] ) ? array( 40 => $data[40] ) : array();
}
/**
* Get the slugs for all current builder modules.
*
* @since 3.0.85
*
* @param string $post_type Get module slugs for this post type. If falsy, all slugs are returned.
*
* @return array
*/
public static function get_module_slugs_by_post_type( $post_type = 'post' ) {
if ( $post_type ) {
if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) {
// We get all modules when post type is not enabled so that posts that have
// had their post type support disabled still load all necessary modules.
return array_keys( self::get_modules() );
}
return self::$_module_slugs_by_post_type[ $post_type ];
}
return self::$_module_slugs_by_post_type;
}
/**
* Get whether the module has Visual Builder support or not
*
* @since 3.1
*
* @return bool
*/
function has_vb_support() {
return 'off' !== $this->vb_support;
}
/**
* Create Factory objects
*
* @return void
*/
function set_factory_objects() {
// Load features fields.
$this->text_shadow = ET_Builder_Module_Fields_Factory::get( 'TextShadow' );
}
/**
* Populates {@see self::$fields_unprocessed}.
*/
function set_fields() {
$this->fields_unprocessed = $this->get_complete_fields();
// Add _builder_version field to all modules
$this->fields_unprocessed['_builder_version'] = array( 'type' => 'skip' );
// Add _dynamic_attributes field to all modules.
$this->fields_unprocessed['_dynamic_attributes'] = array( 'type' => 'skip' );
if ( $this->_is_official_module ) {
return;
}
// 3rd-Party module backwards compatability starts here
foreach ( $this->fields_unprocessed as $field => $info ) {
if ( isset( $info['depends_to'] ) ) {
$this->fields_unprocessed[ $field ]['depends_on'] = $info['depends_to'];
}
if ( isset( $info['depends_default'] ) && $info['depends_default'] && ! isset( $info['depends_show_if'] ) ) {
$this->fields_unprocessed[ $field ]['depends_show_if'] = 'on';
$message = "You're Doing It Wrong! Setting definition for {$field} includes deprecated parameter: 'depends_default'. Use 'show_if' instead.";
et_debug( $message );
}
// Process renderer of string type only.
if ( isset( $info['renderer'] ) && is_string( $info['renderer'] ) ) {
$original_renderer = $info['renderer'];
$updated_field_type = $info['renderer'];
// convert renderer into type
switch ( $info['renderer'] ) {
case 'et_builder_include_categories_option' :
case 'et_builder_include_categories_shop_option' :
$updated_field_type = 'categories';
break;
case 'et_builder_get_widget_areas' :
$updated_field_type = 'select_sidebar';
break;
case 'et_pb_get_font_icon_list' :
case 'et_pb_get_font_down_icon_list' :
$updated_field_type = 'select_icon';
break;
case 'et_builder_get_gallery_settings' :
$updated_field_type = 'upload_gallery';
break;
case 'et_builder_generate_center_map_setting' :
$updated_field_type = 'center_map';
break;
}
$this->fields_unprocessed[ $field ]['type'] = $updated_field_type;
if ( 'et_pb_get_font_down_icon_list' === $info['renderer'] ) {
$this->fields_unprocessed[ $field ]['renderer_options'] = array( 'icons_list' => 'icon_down', );
}
// Output developer warning if renderer was converted to type
if ( $original_renderer !== $updated_field_type ) {
$message = "You're Doing It Wrong! Module setting definition for {$field} has a deprecated value: ";
$message .= "'{$original_renderer}' for parameter 'renderer'. Use '{$updated_field_type}' instead.";
et_debug( $message );
}
}
// Normalize `affects` field names if needed.
if ( isset( $info['affects'] ) ) {
$affects_original = $this->fields_unprocessed[ $field ]['affects'];
$this->fields_unprocessed[ $field ]['affects'] = array();
// BB supports comma separated list of affected fields, convert it to array of fields if this is the case.
// Some plugins use combination of various lists, handle all of them
foreach( $affects_original as $affect_item ) {
if ( strpos( $affect_item, ',' ) !== false ) {
$this->fields_unprocessed[ $field ]['affects'] = array_merge( $this->fields_unprocessed[ $field ]['affects'], explode( ',', str_replace( ' ', '', $affect_item ) ) );
} else {
$this->fields_unprocessed[ $field ]['affects'][] = $affect_item;
}
}
array_walk( $this->fields_unprocessed[ $field ]['affects'], array( $this, 'normalize_affect_fields' ) );
}
if ( 'content_new' === $field ) {
$this->fields_unprocessed['content'] = $this->fields_unprocessed['content_new'];
unset( $this->fields_unprocessed['content_new'] );
$message = "You're Doing It Wrong! Setting definition for {$field} includes deprecated parameter: 'content_new'. Use 'content' instead.";
et_debug( $message );
}
// convert old color pickers to the new ones supporting alpha channel
if ( 'color' === self::$_->array_get( $info, 'type' ) ) {
$info['type'] = 'color-alpha';
$this->fields_unprocessed[ $field ] = $info;
$message = "You're Doing It Wrong! You're using wrong type for the '" . $field . "'. It should be 'color-alpha' instead of 'color'.";
et_debug( $message, 4, false );
}
// convert input type to text
if ( 'input' === self::$_->array_get( $info, 'type' ) ) {
$info['type'] = 'text';
$this->fields_unprocessed[ $field ] = $info;
$message = "You're Doing It Wrong! Setting definition for {$field} has a deprecated value: 'input' for parameter: 'type'. Use 'text' instead.";
et_debug( $message );
}
// Normalize default values
if ( isset( $info['default'] ) ) {
$this->fields_unprocessed[ $field ]['default'] = $this->_normalize_field_default( $field, $info['default'] );
}
}
// Set default values in field definitions based on the legacy defaults "rules"
if ( isset( $this->fields_defaults ) ) {
foreach ( $this->fields_defaults as $field => $value ) {
if ( ! isset( $this->fields_unprocessed[ $field ] ) ) {
continue;
}
$condition = is_array( $value ) ? self::$_->array_get( $value, '1' ) : false;
$set_default_on_front = 'only_default_setting' !== $condition;
$default = $this->_normalize_field_default( $field, $value );
// Always set default value if exists. Only default_on_front should be conditional
$this->fields_unprocessed[ $field ]['default'] = $default;
if ( ! $set_default_on_front ) {
continue;
}
$has_default = isset( $this->fields_unprocessed[ $field ]['default'] );
if ( ! $has_default || $this->fields_unprocessed[ $field ]['default'] !== $default ) {
$this->fields_unprocessed[ $field ]['default_on_front'] = $default;
}
}
}
// Legacy Defaults Rule #4 (AKA: longest-running undetected bug in the codebase):
// Fields listed in whitelisted_fields that aren't in fields_defaults lose their definitions
if ( isset( $this->whitelisted_fields ) ) {
$disable_whitelisted_fields = isset( $this->force_unwhitelisted_fields ) && $this->force_unwhitelisted_fields;
if ( ! $disable_whitelisted_fields && ! is_admin() && ! et_fb_is_enabled() ) {
foreach ( $this->whitelisted_fields as $field ) {
if ( isset( $this->fields_defaults ) && array_key_exists( $field, $this->fields_defaults ) ) {
continue;
}
$this->fields_unprocessed[ $field ] = array();
}
}
}
}
protected function _normalize_field_default( $field, $default_value) {
$normalized_value = is_array( $default_value ) ? $default_value[0] : $default_value;
// normalize default value depends on field type
switch ( $this->fields_unprocessed[ $field ]['type'] ) {
case 'yes_no_button':
if ( is_numeric( $normalized_value ) ) {
$normalized_value = (bool) $normalized_value ? 'on' : 'off';
$message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be either 'on' or 'off'.";
et_debug( $message, 4 , false );
}
break;
case 'color-alpha':
if ( is_numeric( $normalized_value ) ) {
$normalized_value = '';
$message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be string value.";
et_debug( $message, 4 , false );
}
// Make sure provided HEX code is a valid color code
if ( strpos( $normalized_value, '#' ) === 0 && ! in_array( strlen( $normalized_value ), array( 4, 7 ) ) ) {
$normalized_value = '';
$message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be valid hex color code.";
et_debug( $message, 4 , false );
}
break;
}
return $normalized_value;
}
/**
* Normalize `affects` fields name if needed.
* Some 3rd party modules use `#et_pb_` format which is wrong and doesn't work in VB, but works in BB.
* Convert it to correct format and output notice for developer
*
* @return void
*/
function normalize_affect_fields( &$field_name ) {
if ( strpos( $field_name, '#et_pb_' ) !== false ) {
// Truncate field name from the string wherever it's placed
$new_field_name = substr( $field_name, strpos( $field_name, '#et_pb_' ) + 7 );
$message = "You're Doing It Wrong! You're using wrong name for 'affects' attribute. It should be '" . $new_field_name . "' instead of '" . $field_name . "'";
$field_name = $new_field_name;
et_debug( $message, 4, false );
}
// content_new renamed to content, so rename it in affected fields list as well
if ( $field_name === 'content_new' ) {
$field_name = 'content';
}
}
/**
* Finalizes the configuration of {@see self::$fields_unprocessed}.
* Includes filter and fields processing for Visual Builder
*
* @return void
*/
protected function _finalize_all_fields() {
$fields_before_filter = $this->fields_unprocessed;
/**
* Filters module fields.
*
* @since 3.1
*
* @param array $fields_unprocessed See {@see self::$fields_unprocessed}.
*/
$this->fields_unprocessed = apply_filters( "et_pb_all_fields_unprocessed_{$this->slug}", $this->fields_unprocessed );
// Check if this is an AJAX request since this is how VB and BB loads the initial module data
// et_fb_enabled() always returns `false` here
if ( ! wp_doing_ajax() ) {
return;
}
foreach ( array_keys( $this->fields_unprocessed ) as $field_name ) {
$field_info = $this->fields_unprocessed[ $field_name ];
$affected_fields = self::$_->array_get( $field_info, 'affects', array() );
foreach ( $affected_fields as $affected_field ) {
if ( ! isset( $this->fields_unprocessed[ $affected_field ] ) ) {
continue;
}
if ( ! isset( $this->fields_unprocessed[ $affected_field ]['depends_on'] ) ) {
$this->fields_unprocessed[ $affected_field ]['depends_on'] = array();
}
$this->fields_unprocessed[ $affected_field ]['depends_on'][] = $field_name;
// Set `depends_show_if = on` if no condition defined for the affected field for backward compatibility with old plugins
if ( ! isset( $this->fields_unprocessed[ $affected_field ]['depends_show_if'] ) && ! isset( $this->fields_unprocessed[ $affected_field ]['depends_show_if_not'] ) ) {
// Deprecation notice has already been logged for this.
$this->fields_unprocessed[ $affected_field ]['depends_show_if'] = 'on';
}
}
// Unset renderer to avoid errors in VB because of errors in 3rd party plugins
// BB compat. Still need this data, so leave it for BB
if ( self::is_loading_vb_data() && isset( $this->fields_unprocessed[ $field_name ]['renderer'] ) ) {
unset( $this->fields_unprocessed[ $field_name ]['renderer'] );
}
}
// determine custom fields added via filter and add specific flag to identify them in VB
$keys_before_filter = array_keys( $fields_before_filter );
$keys_after_filter = array_keys( $this->fields_unprocessed );
$added_fields = array_diff( $keys_after_filter, $keys_before_filter );
if ( ! empty( $added_fields ) ) {
foreach ( $added_fields as $key ) {
$this->fields_unprocessed[ $key ]['vb_support'] = false;
}
}
}
/**
* Determine if current request is VB Data Request by checking $_POST['action'] value
*
* @return bool
*/
private function is_loading_vb_data() {
return isset( $_POST['action'] ) && 'et_fb_retrieve_builder_data' === $_POST['action']; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
}
private function register_post_type( $post_type ) {
$this->post_types[] = $post_type;
self::$parent_modules[ $post_type ] = array();
self::$child_modules[ $post_type ] = array();
}
/**
* Double quote are saved as "%22" in shortcode attributes.
* Decode them back into "
*
* @param array $enabled_dynamic_attributes
*
* @return void
*/
private function _decode_double_quotes( $enabled_dynamic_attributes ) {
if ( ! isset( $this->props ) ) {
return;
}
// need to encode HTML entities in Admin Area( for BB ) if Visual Editor disabled for the user.
$need_html_entities_decode = is_admin() && ! user_can_richedit();
$shortcode_attributes = array();
$font_icon_options = array( 'font_icon', 'button_icon', 'button_one_icon', 'button_two_icon', 'hover_icon' );
foreach ( $this->props as $attribute_key => $attribute_value ) {
// decode HTML entities and remove trailing and leading quote if needed
$processed_attr_value = $need_html_entities_decode ? trim( htmlspecialchars_decode( $attribute_value, ENT_QUOTES ), '"' ) : $attribute_value;
// the icon shortcodes are fine.
if ( in_array( $attribute_key, $font_icon_options, true ) ) {
$shortcode_attributes[ $attribute_key ] = $processed_attr_value;
// icon attributes must not be str_replaced
continue;
}
// Set empty TinyMCE content '<br /> ' as empty string.
$field_type = self::$_->array_get( $this->fields_unprocessed, "{$attribute_key}.type" );
if ( 'tiny_mce' === $field_type && 'ltbrgtbr' === preg_replace( '/[^a-z]/', '', $processed_attr_value ) ) {
$processed_attr_value = '';
}
// URLs are weird since they can allow non-ascii characters so we escape those separately.
// Also make sure the attribute is not powered by dynamic content so we do not escape
// the JSON value as if it is a raw url.
$is_dynamic_content_attribute = $this->_is_dynamic_value( $attribute_key, $attribute_value, $enabled_dynamic_attributes );
if ( ! $is_dynamic_content_attribute && in_array( $attribute_key, array( 'url', 'button_link', 'button_url' ), true ) ) {
$shortcode_attributes[ $attribute_key ] = esc_url_raw( $processed_attr_value );
} else {
$shortcode_attributes[ $attribute_key ] = str_replace( array( '%22', '%92', '%91', '%93' ), array( '"', '\\', '[', ']' ), $processed_attr_value );
}
}
$this->props = $shortcode_attributes;
}
/**
* Provide a way for sub-class to access $this->_render_count without a chance to alter its value
*
* @return int
*/
protected function render_count() {
return $this->_render_count;
}
/**
* Bumps the render count for this module instance and the module instance whose slug is
* set as {@see self::$_bumps_render_count} (if any).
*
* @since 3.10
*/
protected function _bump_render_count() {
$this->_render_count++;
if ( $this->_bumps_render_count ) {
$module = self::get_module( $this->_bumps_render_count, $this->get_post_type() );
$module->_render_count++;
}
}
/**
* check whether ab testing enabled for current module and calculate whether it should be displayed currently or not
*
* @return bool
*/
private function _is_display_module( $shortcode_atts ) {
$ab_subject_id = isset( $shortcode_atts['ab_subject_id'] ) && '' !== $shortcode_atts['ab_subject_id'] ? $shortcode_atts['ab_subject_id'] : false;
// return true if testing is disabled or current module has no subject id.
if ( ! $ab_subject_id ) {
return true;
}
return $this->_check_ab_test_subject( $ab_subject_id );
}
/**
* check whether the current module should be displayed or not
*
* @return bool
*/
private function _check_ab_test_subject( $ab_subject_id = false ) {
global $et_pb_ab_subject;
if ( ! $ab_subject_id ) {
return true;
}
return $ab_subject_id === $et_pb_ab_subject;
}
/**
* Resets indexes used when generating element addresses.
*
* @param string $content
* @param bool $force
*
* @return string
*/
public static function reset_element_indexes( $content = '', $force = false ) {
if ( ! $force && ( ! self::$can_reset_element_indexes || ! is_main_query() ) ) {
return $content;
}
$slugs = self::get_parent_slugs_regex();
if ( $content && ! preg_match( "/{$slugs}/", $content ) ) {
// At least one builder element should be present.
return $content;
}
global $wp_current_filter;
if ( in_array( 'the_content', $wp_current_filter ) ) {
$call_counts = array_count_values( $wp_current_filter );
if ( $call_counts['the_content'] > 1 ) {
// This is a nested call. We only want to reset indexes after the top-most call.
return $content;
}
}
self::$_current_section_index = -1;
self::$_current_row_index = -1;
self::$_current_row_inner_index = -1;
self::$_current_column_index = -1;
self::$_current_column_inner_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
if ( $force ) {
// Reset module order classes
self::$modules_order = array();
self::$inner_modules_order = array();
}
return $content;
}
/**
* Generates the element's address. Every builder element on the page is assigned an address
* based on it's index and those of it's parents using the following format:
*
* `$section.$row.$column.$module[.$module_item]`
*
* For example, if a module is the forth module in the first column in the third row in the
* second section on the page, it's address would be: `1.2.0.3` (indexes are zero-based).
*
* @since 3.1 Renamed from `_get_current_shortcode_address()` to `generate_element_address()`
* @since 3.0.60
*
* @param string render slug
*
* @return string
*/
public function generate_element_address( $render_slug = '' ) {
// Flag child module. $this->type isn't accurate in this context since some modules reuse other
// modules' render() method for rendering their output (ie. accordion item).
$is_child_module = in_array( $render_slug, self::get_child_slugs( $this->get_post_type() ) );
if ( false !== strpos( $render_slug, '_section' ) ) {
self::$_current_section_index++;
// Reset every module index inside section
self::$_current_row_index = -1;
self::$_current_row_inner_index = -1;
self::$_current_column_index = -1;
self::$_current_column_inner_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( false !== strpos( $render_slug, '_row_inner' ) ) {
self::$_current_row_inner_index++;
// Reset every module index inside row inner
self::$_current_column_inner_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( false !== strpos( $render_slug, '_row' ) ) {
self::$_current_row_index++;
// Reset every module index inside row
self::$_current_column_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( false !== strpos( $render_slug, '_column_inner' ) ) {
self::$_current_column_inner_index++;
// Reset every module index inside column inner
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( false !== strpos( $render_slug, '_column' ) && -1 === self::$_current_row_index ) {
self::$_current_column_index++;
// Reset every module index inside column of specialty section
self::$_current_row_inner_index = -1;
self::$_current_column_inner_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( false !== strpos( $render_slug, '_column' ) ) {
self::$_current_column_index++;
// Reset every module index inside column of regular section
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( $is_child_module ) {
self::$_current_module_item_index++;
} else {
self::$_current_module_index++;
// Reset module item index inside module
self::$_current_module_item_index = -1;
}
$address = self::$_current_section_index;
if ( -1 === self::$_current_row_index && -1 === self::$_current_row_inner_index ) {
// Fullwidth & Specialty (without column inner) Section's module
$parts = array( self::$_current_column_index, self::$_current_module_index );
} else if ( 0 <= self::$_current_row_inner_index ) {
// Specialty (inside column inner) Section's module
$parts = array( self::$_current_column_index, self::$_current_row_inner_index, self::$_current_column_inner_index, self::$_current_module_index );
} else {
// Regular section's module
$parts = array( self::$_current_row_index, self::$_current_column_index, self::$_current_module_index );
}
foreach ( $parts as $part ) {
if ( $part > -1 ) {
$address .= ".{$part}";
}
}
if ( $is_child_module ) {
$address .= '.' . self::$_current_module_item_index;
}
return $address;
}
/**
* Resolves conditional defaults
*
* @param array $values Fields.
* @param string $render_slug
*
* @return array
*/
function resolve_conditional_defaults( $values, $render_slug = '' ) {
// Resolve conditional defaults for the FE
$resolved = $this->get_default_props();
if ( $render_slug && $render_slug !== $this->slug ) {
if ( $module = self::get_module( $render_slug, $this->get_post_type() ) ) {
$resolved = array_merge( $resolved, $module->get_default_props() );
}
}
foreach ( $resolved as $field_name => $field_default ) {
if ( is_array( $field_default ) && 2 === count( $field_default ) && ! empty( $field_default[0] ) && is_array( $field_default[1] ) ) {
// Looks like we have a conditional default
// Get $depend_field value or use the first default if undefined.
list ( $depend_field, $conditional_defaults ) = $field_default;
reset( $conditional_defaults );
$default_key = isset( $values[ $depend_field ] ) ? $values[ $depend_field ] : key( $conditional_defaults );
// Set the resolved default
$resolved[ $field_name ] = isset( $conditional_defaults[ $default_key ] ) ? $conditional_defaults[ $default_key ] : null;
}
}
// Add hover attributes
if ( ! is_array( $values ) ) {
return $resolved;
}
foreach ( $values as $attr => $value ) {
if ( ! preg_match('~_hover(_enabled)?$~', $attr ) ) {
continue;
}
$resolved[$attr] = $value;
}
return $resolved;
}
/**
* Get wrapper settings. Combining module-defined wrapper settings with default wrapper settings
*
* @since 3.1
*
* @param string $render_slug module slug
*
* @return array
*/
protected function get_wrapper_settings( $render_slug = '' ) {
global $et_fb_processing_shortcode_object;
// The following defaults are used on both frontend & builder
$defaults = array(
'parallax_background' => '',
'video_background' => '',
'attrs' => array(),
'inner_attrs' => array(
'class' => 'et_pb_module_inner',
),
);
// The following defaults are only used on frontend. VB handles these on ETBuilderInjectedComponent based on live props
// Note: get_parallax_image_background() and video_background() have to be called before module_classname()
if ( ! $et_fb_processing_shortcode_object ) {
$use_background_image = self::$_->array_get( $this->advanced_fields, 'background.use_background_image', false );
$use_background_video = self::$_->array_get( $this->advanced_fields, 'background.use_background_video', false );
$use_module_id = self::$_->array_get( $this->props, 'module_id', '' );
// Module might disable image background
if ( $use_background_image ) {
$defaults['parallax_background'] = $this->get_parallax_image_background();
}
// Module might disable video background
if ( $use_background_video ) {
$defaults['video_background'] = $this->video_background();
}
// Module might intentionally has custom id fields (ie. Module items)
if ( $use_module_id ) {
$defaults['attrs']['id'] = $this->module_id( false );
}
$defaults['attrs']['class'] = $this->module_classname( $render_slug );
}
if ( ! $defaults['attrs'] ) {
// Make sure we get an empty object when this is output as JSON later.
$defaults['attrs'] = new stdClass;
}
// Fill empty argument attributes by default values
return wp_parse_args( $this->wrapper_settings, $defaults );
}
/**
* Wrap module's rendered output with proper module wrapper. Ensuring module has consistent
* wrapper output which compatible with module attribute and background insertion.
*
* @since 3.1
*
* @param string $output Module's rendered output
* @param string $render_slug Slug of module that is used for rendering output
*
* @return string
*/
protected function _render_module_wrapper( $output = '', $render_slug = '' ) {
$wrapper_settings = $this->get_wrapper_settings( $render_slug );
$slug = $render_slug;
$outer_wrapper_attrs = $wrapper_settings['attrs'];
$inner_wrapper_attrs = $wrapper_settings['inner_attrs'];
/**
* Filters the HTML attributes for the module's outer wrapper. The dynamic portion of the
* filter name, '$slug', corresponds to the module's slug.
*
* @since 3.1
*
* @param string[] $outer_wrapper_attrs
* @param ET_Builder_Element $module_instance
*/
$outer_wrapper_attrs = apply_filters( "et_builder_module_{$slug}_outer_wrapper_attrs", $outer_wrapper_attrs, $this );
/**
* Filters the HTML attributes for the module's inner wrapper. The dynamic portion of the
* filter name, '$slug', corresponds to the module's slug.
*
* @since 3.1
*
* @param string[] $inner_wrapper_attrs
* @param ET_Builder_Element $module_instance
*/
$inner_wrapper_attrs = apply_filters( "et_builder_module_{$slug}_inner_wrapper_attrs", $inner_wrapper_attrs, $this );
return sprintf(
'
%2$s
%3$s
%5$s
',
et_html_attrs( $outer_wrapper_attrs ),
$wrapper_settings['parallax_background'],
$wrapper_settings['video_background'],
et_html_attrs( $inner_wrapper_attrs ),
$output
);
}
/**
* Resolves the values for dynamic attributes.
*
* @since 3.17.2
*
* @param array $attrs List of attributes
*
* @return array Processed attributes with resolved dynamic values.
*/
function process_dynamic_attrs( $original_attrs ) {
global $et_fb_processing_shortcode_object;
$attrs = $original_attrs;
$enabled_dynamic_attributes = $this->_get_enabled_dynamic_attributes( $attrs );
if ( is_array( $attrs ) ) {
foreach ( $attrs as $key => $value ) {
$attrs[ $key ] = $this->_resolve_value(
$this->get_the_ID(),
$key,
$value,
$enabled_dynamic_attributes,
$et_fb_processing_shortcode_object
);
}
}
return $attrs;
}
/**
* Prepares for and then calls the module's {@see self::render()} method.
*
* @since 3.1 Renamed from `_shortcode_callback()` to `_render()`.
* @since 1.0
*
* @param array $attrs List of attributes
* @param string $content Content being processed
* @param string $render_slug Slug of module that is used for rendering output
* @param string $parent_address [description]
* @param string $global_parent [description]
* @param string $global_parent_type [description]
*
* @return string The module's HTML output.
*/
function _render( $attrs, $content = null, $render_slug, $parent_address = '', $global_parent = '', $global_parent_type = '' ) {
global $et_fb_processing_shortcode_object;
$this->attrs_unprocessed = $attrs;
$enabled_dynamic_attributes = $this->_get_enabled_dynamic_attributes( $attrs );
$attrs = $this->process_dynamic_attrs( $attrs );
$this->props = shortcode_atts( $this->resolve_conditional_defaults($attrs, $render_slug), $attrs );
$this->_decode_double_quotes( $enabled_dynamic_attributes );
$this->_maybe_remove_global_default_values_from_props();
// Some module items need to inherit value from its module parent
// This inheritance needs to be done before migration to make it compatible with migration process
$this->maybe_inherit_values();
$_address = $this->generate_element_address( $render_slug );
/**
* Filters Module Props.
*
* @param array $props Array of processed props.
* @param array $attrs Array of original shortcode attrs
* @param string $slug Module slug
* @param string $_address Module Address
* @param string $content Module content
*/
$this->props = apply_filters( 'et_pb_module_shortcode_attributes', $this->props, $attrs, $render_slug, $_address, $content );
$global_content = false;
$ab_testing_enabled = et_is_ab_testing_active();
$hide_subject_module_cached = $hide_subject_module = false;
$post_id = apply_filters( 'et_is_ab_testing_active_post_id', get_the_ID() );
$global_module_id = $this->props['global_module'];
// If the section/row/module is disabled, hide it
if ( isset( $this->props['disabled'] ) && 'on' === $this->props['disabled'] && ! $et_fb_processing_shortcode_object ) {
return;
}
// need to perform additional check and some modifications in case AB testing enabled
if ( $ab_testing_enabled ) {
// check if ab testing enabled for this module and if it shouldn't be displayed currently
$hide_subject_module = ! $et_fb_processing_shortcode_object && ! $this->_is_display_module( $this->props ) && ! et_pb_detect_cache_plugins();
// add class to the AB testing subject if needed
if ( isset( $this->props['ab_subject_id'] ) && '' !== $this->props['ab_subject_id'] ) {
$subject_class = sprintf( ' et_pb_ab_subject et_pb_ab_subject_id-%1$s_%2$s',
esc_attr( $post_id ),
esc_attr( $this->props['ab_subject_id'] )
);
$this->props['module_class'] = isset( $this->props['module_class'] ) && '' !== $this->props['module_class'] ? $this->props['module_class'] . $subject_class : $subject_class;
if ( et_pb_detect_cache_plugins() ) {
$hide_subject_module_cached = true;
}
}
// add class to the AB testing goal if needed
if ( isset( $this->props['ab_goal'] ) && 'on' === $this->props['ab_goal'] ) {
$goal_class = sprintf( ' et_pb_ab_goal et_pb_ab_goal_id-%1$s', esc_attr( $post_id ) );
$this->props['module_class'] = isset( $this->props['module_class'] ) && '' !== $this->props['module_class'] ? $this->props['module_class'] . $goal_class : $goal_class;
}
}
//override module attributes for global module. Skip that step while processing Frontend Builder object
if ( ! empty( $global_module_id ) && ! $et_fb_processing_shortcode_object ) {
$global_module_data = et_pb_load_global_module( $global_module_id );
if ( '' !== $global_module_data ) {
$unsynced_global_attributes = get_post_meta( $global_module_id, '_et_pb_excluded_global_options' );
$use_updated_global_sync_method = ! empty( $unsynced_global_attributes );
$unsynced_options = ! empty( $unsynced_global_attributes[0] ) ? json_decode( $unsynced_global_attributes[0], true ) : array();
$content_synced = $use_updated_global_sync_method && ! in_array( 'et_pb_content_field', $unsynced_options );
// support legacy selective sync system
if ( ! $use_updated_global_sync_method ) {
$content_synced = ! isset( $this->props['saved_tabs'] ) || false !== strpos( $this->props['saved_tabs'], 'general' ) || 'all' === $this->props['saved_tabs'];
}
if ( $content_synced ) {
$global_content = et_pb_get_global_module_content( $global_module_data, $render_slug );
}
if ( in_array($render_slug, array('et_pb_code', 'et_pb_fullwidth_code')) ) {
$global_content = _et_pb_code_module_prep_content($global_content);
}
// cleanup the shortcode string to avoid the attributes messing with content
$global_content_processed = false !== $global_content ? str_replace( $global_content, '', $global_module_data ) : $global_module_data;
$global_atts = shortcode_parse_atts( et_pb_remove_shortcode_content( $global_content_processed, $this->slug ) );
// reset module addresses because global items will be processed once again and address will be incremented wrongly
if ( false !== strpos( $render_slug, '_section' ) ) {
self::$_current_section_index--;
self::$_current_row_index = -1;
self::$_current_row_inner_index = -1;
self::$_current_column_index = -1;
self::$_current_column_inner_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( false !== strpos( $render_slug, '_row_inner' ) ) {
self::$_current_row_index--;
self::$_current_column_inner_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else if ( false !== strpos( $render_slug, '_row' ) ) {
self::$_current_row_index--;
self::$_current_column_index = -1;
self::$_current_module_index = -1;
self::$_current_module_item_index = -1;
} else {
self::$_current_module_index--;
self::$_current_module_item_index = -1;
}
// Always unsync 'next_background_color' and 'prev_background_color' options for global sections
// They should be dynamic and reflect color of top and bottom sections
if ( $render_slug === 'et_pb_section' ) {
$unsynced_options = array_merge( $unsynced_options, array( 'next_background_color', 'prev_background_color' ) );
}
foreach( $this->props as $single_attr => $value ) {
if ( isset( $global_atts[$single_attr] ) && ! in_array( $single_attr, $unsynced_options ) ) {
// replace %22 with double quotes in options to make sure it's rendered correctly
$this->props[ $single_attr] = is_string( $global_atts[ $single_attr] ) && ! array_intersect( array( "et_pb_{$single_attr}", $single_attr ), $this->dbl_quote_exception_options ) ? str_replace( '%22', '"', $global_atts[ $single_attr] ) : $global_atts[ $single_attr];
}
}
$enabled_dynamic_attributes_global = $this->_get_enabled_dynamic_attributes( $this->props );
$this->props = $this->process_dynamic_attrs( $this->props );
$this->_decode_double_quotes( $enabled_dynamic_attributes_global );
}
}
self::set_order_class( $render_slug );
$this->before_render();
$this->content_unprocessed = (false !== $global_content ? $global_content : $content);
$content = $this->_resolve_value(
$this->get_the_ID(),
'content',
$this->content_unprocessed,
$enabled_dynamic_attributes,
$et_fb_processing_shortcode_object
);
$content = apply_filters( 'et_pb_module_content', $content, $this->props, $attrs, $render_slug, $_address, $global_content );
// Set empty TinyMCE content '<br /> ' as empty string.
if ( 'ltbrgtbr' === preg_replace( '/[^a-z]/', '', $content ) ) {
$content = '';
}
if ( $et_fb_processing_shortcode_object ) {
$this->content = et_pb_fix_shortcodes( $content, $this->use_raw_content );
} else {
// Line breaks should be converted before do_shortcode to avoid legit rendered shortcode
// line breaks being trimmed into one line and causing issue like broken javascript code
if ( $this->use_raw_content ) {
$content = et_builder_convert_line_breaks( et_builder_replace_code_content_entities( $content ) );
}
$this->props['content'] = $this->content = ! ( isset( $this->is_structure_element ) && $this->is_structure_element ) ? do_shortcode( et_pb_fix_shortcodes( $content, $this->use_raw_content ) ) : '';
}
// Restart classname on shortcode callback. Module class is only called once, not on every
// shortcode module appearance. Thus classname construction need to be restarted on each
// module callback
$this->classname = array();
if ( method_exists( $this, 'shortcode_atts' ) ) {
// Deprecated. Do not use this!
$this->shortcode_atts();
}
$this->process_additional_options( $render_slug );
$this->process_custom_css_fields( $render_slug );
// load inline fonts if needed
if ( isset( $this->props['inline_fonts'] ) ) {
$this->process_inline_fonts_option( $this->props['inline_fonts'] );
}
// Automatically add slug as classname for module that uses other module's shortcode callback
// This has to be added first because some classname is position-sensitive and used for
// JS-based calculation (i.e. .et_pb_column in column inner)
if ( $this->slug !== $render_slug ) {
$this->add_classname( $this->slug );
}
// Automatically add default classnames
$this->add_classname( array(
'et_pb_module',
$render_slug,
ET_Builder_Element::get_module_order_class( $render_slug ),
) );
// Automatically added user-defined classname if there's any
if ( isset( $this->props['module_class'] ) && '' !== $this->props['module_class'] ) {
$this->add_classname( explode( ' ', $this->props['module_class'] ) );
}
$animation_style = isset( $this->props['animation_style'] ) ? $this->props['animation_style'] : false;
$animation_repeat = isset( $this->props['animation_repeat'] ) ? $this->props['animation_repeat'] : 'once';
$animation_direction = isset( $this->props['animation_direction'] ) ? $this->props['animation_direction'] : 'center';
$animation_duration = isset( $this->props['animation_duration'] ) ? $this->props['animation_duration'] : '500ms';
$animation_delay = isset( $this->props['animation_delay'] ) ? $this->props['animation_delay'] : '0ms';
$animation_intensity = isset( $this->props["animation_intensity_{$animation_style }"] ) ? $this->props["animation_intensity_{$animation_style }"] : '50%';
$animation_starting_opacity = isset( $this->props['animation_starting_opacity'] ) ? $this->props['animation_starting_opacity'] : '0%';
$animation_speed_curve = isset( $this->props['animation_speed_curve'] ) ? $this->props['animation_speed_curve'] : 'ease-in-out';
// Check if this is an AJAX request since this is how VB loads the initial module data
// et_fb_enabled() always returns `false` here
if ( $animation_style && 'none' !== $animation_style && ! wp_doing_ajax() ) {
// Fade doesn't have direction
if ( 'fade' === $animation_style ) {
$animation_direction = '';
}
if ( in_array( $animation_direction, array( 'top', 'right', 'bottom', 'left' ) ) ) {
$animation_style .= ucfirst( $animation_direction );
}
$module_class = ET_Builder_Element::get_module_order_class( $render_slug );
if ( $module_class ) {
et_builder_handle_animation_data( array(
'class' => esc_attr( trim( $module_class ) ),
'style' => esc_html( $animation_style ),
'repeat' => esc_html( $animation_repeat ),
'duration' => esc_html( $animation_duration ),
'delay' => esc_html( $animation_delay ),
'intensity' => esc_html( $animation_intensity ),
'starting_opacity' => esc_html( $animation_starting_opacity ),
'speed_curve' => esc_html( $animation_speed_curve ),
) );
}
// Try to apply old method for plugins without vb support
if ( 'on' !== $this->vb_support ) {
add_filter( "{$render_slug}_shortcode_output", array( $this, 'add_et_animated_class' ), 10, 2 );
}
$this->add_classname( 'et_animated' );
}
// Add "et_hover_enabled" class to elements that have at least one hover prop enabled
if ( et_has_hover_enabled( $this->props ) ) {
$this->add_classname( 'et_hover_enabled' );
}
// Setup link options
$link_option_url = isset( $this->props['link_option_url'] ) ? $this->props['link_option_url'] : '';
$link_option_url_new_window = isset( $this->props['link_option_url_new_window'] ) ? $this->props['link_option_url_new_window'] : false;
if ( '' !== $link_option_url ) {
$module_class = ET_Builder_Element::get_module_order_class( $render_slug );
if ( $module_class ) {
et_builder_handle_link_options_data( array(
'class' => trim( $module_class ),
'url' => esc_url( $link_option_url ),
'target' => 'on' === $link_option_url_new_window ? '_blank' : '_self',
) );
}
$this->add_classname( 'et_clickable' );
}
// Hide module on specific screens if needed
if ( isset( $this->props['disabled_on'] ) && '' !== $this->props['disabled_on'] ) {
$disabled_on_array = explode( '|', $this->props['disabled_on'] );
$i = 0;
$current_media_query = 'max_width_767';
foreach( $disabled_on_array as $value ) {
if ( 'on' === $value ) {
ET_Builder_Module::set_style( $render_slug, array(
'selector' => '%%order_class%%',
'declaration' => 'display: none !important;',
'media_query' => ET_Builder_Element::get_media_query( $current_media_query ),
) );
}
$i++;
$current_media_query = 1 === $i ? '768_980' : 'min_width_981';
}
}
$render_method = $et_fb_processing_shortcode_object ? 'render_as_builder_data' : 'render';
$output = $this->{$render_method}( $attrs, $content, $render_slug, $parent_address, $global_parent, $global_parent_type );
// Wrap 3rd party module rendered output with proper module wrapper
// @TODO implement module wrapper on official module
if ( 'on' === $this->vb_support && 'render' === $render_method && ! $this->_is_official_module ) {
$output = $this->_render_module_wrapper( $output, $render_slug );
}
/**
* Filters every builder modules shortcode output.
*
* @since 3.1
*
* @param string $output
* @param string $module_slug
* @param object $this
*/
$output = apply_filters( 'et_module_shortcode_output', $output, $render_slug, $this );
/**
* Filters builder module shortcode output. The dynamic portion of the filter name, `$render_slug`,
* refers to the slug of the module for which the shortcode output was generated.
*
* @since 3.0.87
*
* @param string $output
* @param string $module_slug
*/
$output = apply_filters( "{$render_slug}_shortcode_output", $output, $render_slug );
$this->_bump_render_count();
if ( $hide_subject_module ) {
return '';
}
if ( $hide_subject_module_cached ) {
$previous_subjects_cache = get_post_meta( $post_id, 'et_pb_subjects_cache', true );
if ( empty( $previous_subjects_cache ) ) {
$previous_subjects_cache = array();
}
if ( empty( $this->template_name ) ) {
$previous_subjects_cache[ $this->props['ab_subject_id'] ] = $output;
} else {
$previous_subjects_cache[ $this->props['ab_subject_id'] ] = $this->output();
}
// update the subjects cache in post meta to use it later
update_post_meta( $post_id, 'et_pb_subjects_cache', $previous_subjects_cache );
// generate the placeholder to output on front-end instead of actual content
$subject_placeholder = sprintf( '', esc_attr( $this->props['ab_subject_id'] ) );
return $subject_placeholder;
}
// Do not use `template_name` while processing object for VB
if ( $et_fb_processing_shortcode_object || empty( $this->template_name ) ) {
return $output;
}
return $this->output();
}
/**
* Add "et_animated" class using filter. Obsolete method and only applied to old 3rd party modules without `modules_classname()` method
*
* @param string $output
* @param string $module_slug
*
* @return string
*/
function add_et_animated_class( $output, $module_slug ) {
if ( in_array( $module_slug, ET_Builder_Element::$uses_module_classname ) ) {
return $output;
}
remove_filter( "{$module_slug}_shortcode_output", array( $this, 'add_et_animated_class' ), 10 );
return preg_replace( "/class=\"(.*?{$module_slug}_\d+.*?)\"/", 'class="$1 et_animated"', $output, 1 );
}
/**
* Delete attribute values that are equal to the global default value (if one exists).
*
* @return void
*/
protected function _maybe_remove_global_default_values_from_props() {
$fields = $this->fields_unprocessed;
$must_print_fields = array( 'text_orientation' );
/**
* Filters Must Print attributes array.
* Must Print attributes - attributes which defaults should always be printed on Front End
*
* @deprecated
*
* @param array $must_print_fields Array of attribute names.
*/
$must_print_fields = apply_filters( $this->slug . '_must_print_attributes', $must_print_fields );
foreach ( $fields as $field_key => $field_settings ) {
$global_setting_name = $this->get_global_setting_name( $field_key );
$global_setting_value = ET_Global_Settings::get_value( $global_setting_name );
if ( ! $global_setting_value || in_array( $field_key, $must_print_fields ) ) {
continue;
}
$attr_value = self::$_->array_get( $this->props, $field_key, '' );
if ( $attr_value && $attr_value === $global_setting_value ) {
$this->props[ $field_key ] = '';
}
}
}
// intended to be overridden as needed
function maybe_inherit_values() {}
/**
* Like {@see self::render()}, but sources the output from a template file. The template name
* should be set in {@see self::$template_name}.
*
* Note: this functionality is not currently supported by the Visual Builder. Pages containing
* modules that use this method to render their output cannot be edited using the Visual Builder
* at this time. However, full support will be added in the coming months.
*
* @since 3.1 Renamed from `shortcode_output()` to `output()`
* @since 2.4.6
*
* @return string
*/
function output() {
if ( empty( $this->template_name ) ) {
return '';
}
if ( method_exists( $this, 'shortcode_output' ) ) {
// Backwards compatibility
return $this->__call( 'shortcode_output', array() );
}
$this->props['content'] = $this->content;
extract( $this->props );
ob_start();
require( locate_template( $this->template_name . '.php' ) );
return ob_get_clean();
}
/**
* Generates HTML data attributes from an array of props.
*
* @since 3.1 Rename from `shortcode_atts_to_data_atts()` to `props_to_html_data_attrs()`
* @since 1.0
*
* @param array $props
*
* @return string
*/
public function props_to_html_data_attrs( $props = array() ) {
if ( empty( $props ) ) {
return '';
}
$output = array();
foreach ( $props as $attr ) {
$output[] = 'data-' . esc_attr( $attr ) . '="' . esc_attr( $this->props[ $attr ] ) . '"';
}
return implode( ' ', $output );
}
/**
* This method is called before {@self::_render()} for rows, columns, and modules. It can
* be overridden by elements that need to perform any tasks before rendering begins.
*
* @since 3.1 Renamed from `pre_shortcode_content()` to `before_render()`.
* @since 1.0
*/
public function before_render() {
if ( method_exists( $this, 'pre_shortcode_content' ) ) {
// Backwards compatibility
$this->__call( 'pre_shortcode_content', array() );
}
}
/**
* Generates the module's HTML output based on {@see self::$props}. This method should be
* overridden in module classes.
*
* @since 3.1 Renamed from `shortcode_callback()` to `render()`.
* @since 1.0
*
* @param array $attrs List of unprocessed attributes
* @param string $content Content being processed
* @param string $render_slug Slug of module that is used for rendering output
*
* @return string The module's HTML output.
*/
public function render( $attrs, $content = null, $render_slug ) {
if ( method_exists( $this, 'shortcode_callback' ) ) {
// Backwards compatibility
return $this->__call( 'shortcode_callback', array( $attrs, $content, $render_slug ) );
}
return '';
}
/**
* Generates data used to render the module in the builder.
* See {@see self::render()} for parameter info.
*
* @since 3.1 Renamed from `_shortcode_passthru_callback()` to `render_as_builder_data()`
* @since 3.0.0
*
* @return array|string An array when called during AJAX request, an empty string otherwise.
*/
public function render_as_builder_data( $atts, $content = null, $render_slug, $parent_address = '', $global_parent = '', $global_parent_type = '' ) {
global $post;
// this is called during pageload, but we want to ignore that round, as this data will be built and returned on separate ajax request instead
et_core_nonce_verified_previously();
if ( ! isset( $_POST['action'] ) ) {
return '';
}
$attrs = array();
$fields = $this->process_fields( $this->fields_unprocessed );
$global_content = false;
$function_name_processed = et_fb_prepare_tag( $render_slug );
$unsynced_global_attributes = array();
$use_updated_global_sync_method = false;
$global_module_id = isset( $atts['global_module'] ) ? $atts['global_module'] : false;
$is_global_template = false;
if ( $render_slug && $render_slug !== $this->slug ) {
if ( $rendering_module = self::get_module( $render_slug, $this->get_post_type() ) ) {
$fields = array_merge( $fields, $this->process_fields( $rendering_module->fields_unprocessed ) );
}
}
// Add support of new selective sync feature for library modules in VB
if ( isset( $_POST['et_post_type'], $_POST['et_post_id'], $_POST['et_layout_type'] ) && 'et_pb_layout' === $_POST['et_post_type'] && 'module' === $_POST['et_layout_type'] ) {
$template_scope = wp_get_object_terms( $_POST['et_post_id'], 'scope' );
$is_global_template = ! empty( $template_scope[0] ) && 'global' === $template_scope[0]->slug;
if ( $is_global_template ) {
$global_module_id = $_POST['et_post_id'];
}
}
//override module attributes for global module
if ( ! empty( $global_module_id ) ) {
if ( ! in_array( $render_slug, array( 'et_pb_section', 'et_pb_row', 'et_pb_row_inner', 'et_pb_column', 'et_pb_column_inner' ) ) ) {
$processing_global_module = $global_module_id;
$unsynced_global_attributes = get_post_meta( $processing_global_module, '_et_pb_excluded_global_options' );
$use_updated_global_sync_method = ! empty( $unsynced_global_attributes );
}
$global_module_data = et_pb_load_global_module( $global_module_id );
if ( '' !== $global_module_data ) {
$unsynced_options = ! empty( $unsynced_global_attributes[0] ) ? json_decode( $unsynced_global_attributes[0], true ) : array() ;
$content_synced = $use_updated_global_sync_method && ! in_array( 'et_pb_content_field', $unsynced_options );
$is_module_fully_global = $use_updated_global_sync_method && empty( $unsynced_options );
$unsynced_legacy_options = array();
// support legacy selective sync system
if ( ! $use_updated_global_sync_method ) {
$content_synced = ! isset( $atts['saved_tabs'] ) || false !== strpos( $atts['saved_tabs'], 'general' ) || 'all' === $atts['saved_tabs'];
$is_module_fully_global = ! isset( $atts['saved_tabs'] ) || 'all' === $atts['saved_tabs'];
}
if ( $content_synced && ! $is_global_template ) {
$global_content = et_pb_get_global_module_content( $global_module_data, $function_name_processed );
// When saving global rows from specialty sections, they get saved as et_pb_row instead of et_pb_row_inner.
// Handle this special case when parsing to avoid empty global row content.
if ( empty( $global_content ) && 'et_pb_row_inner' === $function_name_processed ) {
$global_content = et_pb_get_global_module_content( $global_module_data, 'et_pb_row' );
}
}
// remove the shortcode content to avoid conflicts of parent attributes with similar attrs from child modules
if ( false !== $global_content ) {
$global_content_processed = str_replace( $global_content, '', $global_module_data );
} else {
$global_content_processed = $global_module_data;
}
// Ensuring that all possible attributes exist to avoid remaining child attributes being used by global parents' attributes
// Do that only in case the module is fully global
if ( $is_module_fully_global ) {
$global_atts = wp_parse_args(
shortcode_parse_atts( et_pb_remove_shortcode_content( $global_content_processed, $this->slug ) ),
array_map( '__return_empty_string', $this->whitelisted_fields )
);
} else {
$global_atts = shortcode_parse_atts( $global_content_processed );
}
// Run et_pb_module_shortcode_attributes filter to apply migration system on attributes of global module
$global_atts = apply_filters( 'et_pb_module_shortcode_attributes', $global_atts, $atts, $this->slug, $this->generate_element_address( $render_slug ), $content );
foreach( $this->props as $single_attr => $value ) {
if ( isset( $global_atts[$single_attr] ) && ! in_array( $single_attr, $unsynced_options ) ) {
// replace %22 with double quotes in options to make sure it's rendered correctly
if ( ! $is_global_template ) {
$this->props[ $single_attr ] = is_string( $global_atts[ $single_attr ] ) && ! array_intersect( array( "et_pb_{$single_attr}", $single_attr ), $this->dbl_quote_exception_options ) ? str_replace( '%22', '"', $global_atts[ $single_attr ] ) : $global_atts[ $single_attr ];
}
} else if ( ! $use_updated_global_sync_method ) {
// prepare array of unsynced options to migrate the legacy modules to new system
$unsynced_legacy_options[] = $single_attr;
} else {
$unsynced_global_attributes[0] = $unsynced_options;
}
}
// migrate unsynced options to the new selective sync method
if ( ! $use_updated_global_sync_method ) {
$unsynced_global_attributes[0] = $unsynced_legacy_options;
// check the content and add it into list if needed.
if ( ! $content_synced ) {
$unsynced_global_attributes[0][] = 'et_pb_content_field';
}
} else {
$unsynced_global_attributes[0] = $unsynced_options;
}
} else {
// remove global_module attr if it doesn't exist in DB
$this->props['global_module'] = '';
$global_parent = '';
}
}
foreach( $this->props as $shortcode_attr_key => $shortcode_attr_value ) {
if ( isset( $fields[ $shortcode_attr_key ]['type'] ) && 'computed' === $fields[ $shortcode_attr_key ]['type'] ) {
$field = $fields[ $shortcode_attr_key ];
$depends_on = array();
if ( isset( $field['computed_depends_on'] ) ) {
foreach ( $field['computed_depends_on'] as $depends_on_field ) {
$dependency_value = $this->props[ $depends_on_field ];
if ( '' === $dependency_value ) {
if ( isset( $this->fields_unprocessed[ $depends_on_field]['default'] ) ) {
$dependency_value = $this->fields_unprocessed[ $depends_on_field ]['default'];
}
}
$depends_on[ $depends_on_field ] = $dependency_value;
}
}
if ( isset( $field['computed_variables'] ) ) {
$depends_on['computed_variables'] = $field['computed_variables'];
}
if ( ! is_callable( $field['computed_callback'] ) ) {
wp_die( esc_html( $shortcode_attr_key . ' Callback:' . $field['computed_callback'] . ' is not callable.... ' ) );
}
$value = call_user_func( $field['computed_callback'], $depends_on );
} else {
$value = $shortcode_attr_value;
}
// dont set the default, unless, lol, the value is literally 'default'
if ( isset( $fields[ $shortcode_attr_key ]['default'] ) && $value === $fields[ $shortcode_attr_key ]['default'] && $value !== 'default' ) {
$value = '';
}
// generic override, disabled=off is an unspoken default
if ( $shortcode_attr_key === 'disabled' && $shortcode_attr_value === 'off' ) {
$value = '';
}
// this override is necessary becuase et_pb_column and et_pb_column_inner type default is 4_4 and will get stomped
// above since its default, but we need it explicitly set anyways, so we force set it
if ( in_array( $render_slug, array( 'et_pb_column', 'et_pb_column_inner' ) ) && $shortcode_attr_key === 'type' ) {
$value = $shortcode_attr_value;
}
if ( '' !== $value ) {
$attrs[$shortcode_attr_key] = is_string($value) ? html_entity_decode($value) : $value;
}
}
// Format FB component path
// TODO, move this to class method and property, and allow both to be overridden
$component_path = str_replace( 'et_pb_' , '', $function_name_processed );
$component_path = str_replace( '_', '-', $component_path );
$_i = isset( $atts['_i'] ) ? $atts['_i'] : 0;
$address = isset( $atts['_address'] ) ? $atts['_address'] : '0';
// set the global parent if exists
if ( ( ! isset( $attrs['global_module'] ) || '' === $attrs['global_module'] ) && '' !== $global_parent ) {
$attrs['global_parent'] = $global_parent;
}
if ( isset( $this->is_structure_element ) && $this->is_structure_element ) {
$this->vb_support = 'on';
}
$processed_content = false !== $global_content ? $global_content : $this->content;
$content = array_key_exists( 'content', $this->fields_unprocessed ) || 'et_pb_code' === $function_name_processed || 'et_pb_fullwidth_code' === $function_name_processed ? $processed_content : et_fb_process_shortcode( $processed_content, $address, $global_parent, $global_parent_type );
// Global Code module content should be decoded before passing to VB.
$is_global_code = in_array( $function_name_processed, array( 'et_pb_code', 'et_pb_fullwidth_code' ) );
$prepared_content = $content;
if ( ( ! is_array( $content ) && $this->vb_support !== 'on' && ! $this->has_line_breaks( $content ) ) || $is_global_code ) {
$prepared_content = html_entity_decode( $content, ENT_COMPAT, 'UTF-8' );
}
if ( empty( $attrs ) ) {
// Visual Builder expects $attrs to be an object.
// Associative array converted to an object by wp_json_encode correctly, but empty array is not and it causes issues.
$attrs = new stdClass();
}
$module_type = $this->type;
$render_count = $this->_render_count;
$child_title_var = isset( $this->child_title_var ) ? $this->child_title_var : '';
$child_title_fallback_var = isset( $this->child_title_fallback_var ) ? $this->child_title_fallback_var : '';
$advanced_setting_title_text = isset( $this->advanced_setting_title_text ) ? $this->advanced_setting_title_text : '';
// If this is a shop module use the Shop module render count
// Shop module creates a new class instance which resets the $_render_count value
// ( see get_shop_html() method of ET_Builder_Module_Shop class in main-modules.php )
// so we use a static property to track its proper render count
if ( 'et_pb_shop' === $render_slug ) {
$render_count = self::$_shop_render_count;
self::$_shop_render_count++;
}
// Ensuring that module which uses another module's template (i.e. accordion item uses toggle's
// component) has correct values for class properties where it makes a difference. This is covered on front-end, but it causes inheriting
// module uses its template's value on render_as_builder_data()
if ( isset( $rendering_module, $rendering_module->type ) ) {
$module_type = $rendering_module->type;
$child_title_var = isset( $rendering_module->child_title_var ) ? $rendering_module->child_title_var : $child_title_var;
$child_title_fallback_var = isset( $rendering_module->child_title_fallback_var ) ? $rendering_module->child_title_fallback_var : $child_title_fallback_var;
$advanced_setting_title_text = isset( $rendering_module->advanced_setting_title_text ) ? $rendering_module->advanced_setting_title_text : $advanced_setting_title_text;
}
// Build object.
$object = array(
'_i' => $_i,
'_order' => $_i,
// TODO make address be _address, its conflicting with 'address' prop in map module... (not sure how though, they are in diffent places...)
'address' => $address,
'child_slug' => $this->child_slug,
'vb_support' => $this->vb_support,
'parent_address' => $parent_address,
'shortcode_index' => $render_count,
'type' => $render_slug,
'component_path' => $component_path,
'main_css_element' => $this->main_css_element,
'attrs' => $attrs,
'content' => $prepared_content,
'is_module_child' => 'child' === $module_type,
'is_official_module' => $this->_is_official_module,
'child_title_var' => $child_title_var,
'child_title_fallback_var' => $child_title_fallback_var,
'advanced_setting_title_text' => $advanced_setting_title_text,
'wrapper_settings' => $this->get_wrapper_settings( $render_slug ),
);
if ( ! empty( $unsynced_global_attributes ) ) {
$object['unsyncedGlobalSettings'] = $unsynced_global_attributes[0];
}
if ( $is_global_template ) {
$object['libraryModuleScope'] = 'global';
}
if ( isset( $this->module_items_config ) ) {
$object['module_items_config'] = $this->module_items_config;
}
return $object;
}
/**
* Determine if provided string contain line-breaks (`\r\n`)
*
* @param string $content String to check
*
* @return bool
*/
function has_line_breaks( $content ) {
return count( preg_split('/\r\n*\n/', trim( $content ), -1, PREG_SPLIT_NO_EMPTY ) ) > 1;
}
// intended to be overridden as needed
function additional_render( $attrs, $content = null, $render_slug ) {
if ( method_exists( $this, 'additional_shortcode_callback' ) ) {
// Backwards compatibility
$this->__call( 'additional_shortcode_callback', array( $attrs, $content, $render_slug ) );
}
}
// intended to be overridden as needed
function predefined_child_modules(){}
/**
* Generate global setting name
*
* @param string $option_slug Option slug
*
* @return string Global setting name in the following format: "module_slug-option_slug"
*/
public function get_global_setting_name( $option_slug ) {
$global_setting_name = sprintf(
'%1$s-%2$s',
isset( $this->global_settings_slug ) ? $this->global_settings_slug : $this->slug,
$option_slug
);
return $global_setting_name;
}
/**
* Add global default values to all fields, if they don't have defaults set
*
* @return void
*/
protected function _maybe_add_global_defaults() {
// Don't add default settings to "child" modules
if ( 'child' === $this->type ) {
return;
}
$fields = $this->fields_unprocessed;
$ignored_keys = array(
'custom_margin',
'custom_padding',
);
// Font color settings have custom_color set to true, so add them to ignored keys array
if ( isset( $this->advanced_fields['fonts'] ) && is_array( $this->advanced_fields['fonts'] ) ) {
foreach ( $this->advanced_fields['fonts'] as $font_key => $font_settings ) {
$ignored_keys[] = sprintf( '%1$s_text_color', $font_key );
}
}
$ignored_keys = apply_filters( 'et_builder_add_defaults_ignored_keys', $ignored_keys );
foreach ( $fields as $field_key => $field_settings ) {
if ( in_array( $field_key, $ignored_keys ) ) {
continue;
}
$global_setting_name = $this->get_global_setting_name( $field_key );
$global_setting_value = ET_Global_Settings::get_value( $global_setting_name );
if ( ! isset( $field_settings['default'] ) && $global_setting_value ) {
$fields[ $field_key ]['default'] = $global_setting_value;
// Mark this default as global so VB won't print it to replicate FE behaviour
$fields[ $field_key ]['is_global_default'] = true;
}
}
$this->fields_unprocessed = $fields;
}
protected function _add_additional_fields() {
// Setup has_advanced_fields property to adjust advanced options visibility on
// module that has no VB support to avoid sudden advanced options appearances
$this->has_advanced_fields = isset( $this->advanced_fields );
// Advanced options are added by default unless module explicitly disabled it
$this->advanced_fields = $this->has_advanced_fields ? $this->advanced_fields : array();
// Advanced options have to be array
if ( ! is_array( $this->advanced_fields ) ) {
return;
}
$this->_add_font_fields();
$this->_add_background_fields();
$this->_add_borders_fields();
$this->_add_button_fields();
$this->_add_box_shadow_fields();
$this->_add_text_fields();
$this->_add_max_width_fields();
$this->_add_margin_padding_fields();
// Add filter fields to modules
$this->_add_filter_fields();
// Add divider fields to section modules.
$this->_add_divider_fields();
// Add animation fields to all modules
$this->_add_animation_fields();
$this->_add_additional_transition_fields();
// Add text shadow fields to all modules
$this->_add_text_shadow_fields();
// Add link options to all modules
$this->_add_link_options_fields();
if ( ! isset( $this->_additional_fields_options ) ) {
return false;
}
$additional_options = $this->_additional_fields_options;
// Add hover field indication
$additional_options['hover_enabled'] = array(
'type' => 'skip',
'default' => 0,
);
if ( ! empty( $additional_options ) ) {
// delete second level advanced options default values
if ( isset( $this->type ) && 'child' === $this->type && apply_filters( 'et_pb_remove_child_module_defaults', true ) ) {
foreach ( $additional_options as $name => $settings ) {
if ( isset( $additional_options[ $name ]['default'] ) && ! isset( $additional_options[ $name ]['default_on_child'] ) ) {
$additional_options[ $name ]['default'] = '';
}
}
}
$this->fields_unprocessed = array_merge( $this->fields_unprocessed, $additional_options );
}
}
protected function _add_font_fields() {
// Font fields are added by default if module has partial or full VB support
if ( $this->has_vb_support() ) {
$this->advanced_fields['fonts'] = self::$_->array_get( $this->advanced_fields, 'fonts', array(
'module' => array(
'label' => esc_html__( 'Module', 'custom_module' ),
'line_height' => array(
'default' => floatval( et_get_option( 'body_font_height', '1.7' ) ) . 'em',
),
'font_size' => array(
'default' => absint( et_get_option( 'body_font_size', '14' ) ) . 'px',
),
),
) );
} else if ( ! $this->has_advanced_fields ) {
// Disable if module doesn't set advanced_fields property and has no VB support
return;
}
// Font settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'fonts' ) ) ) {
return;
}
$advanced_font_options = $this->advanced_fields['fonts'];
$additional_options = array();
$defaults = array(
'all_caps' => 'off',
);
foreach ( $advanced_font_options as $option_name => $option_settings ) {
$advanced_font_options[ $option_name ]['defaults'] = $defaults;
}
$this->advanced_fields['fonts'] = $advanced_font_options;
$font_options_count = 0;
foreach ( $advanced_font_options as $option_name => $option_settings ) {
$font_options_count++;
$option_settings = wp_parse_args( $option_settings, array(
'label' => '',
'font_size' => array(),
'letter_spacing' => array(),
'font' => array(),
'text_align' => array(),
) );
$toggle_disabled = isset( $option_settings['disable_toggle'] ) && $option_settings['disable_toggle'];
$tab_slug = isset( $option_settings['tab_slug'] ) ? $option_settings['tab_slug'] : 'advanced';
$toggle_slug = '';
if ( ! $toggle_disabled ) {
$toggle_slug = isset( $option_settings['toggle_slug'] ) ? $option_settings['toggle_slug'] : $option_name;
$sub_toggle = isset( $option_settings['sub_toggle'] ) ? $option_settings['sub_toggle'] : '';
if ( ! isset( $option_settings['toggle_slug'] ) ) {
$font_toggle = array(
$option_name => array(
'title' => sprintf( '%1$s %2$s', esc_html( $option_settings['label'] ), esc_html__( 'Text', 'et_builder' ) ),
'priority' => 50 + $font_options_count,
),
);
$this->_add_settings_modal_toggles( $tab_slug, $font_toggle );
}
}
if ( isset( $option_settings['header_level'] ) ) {
$additional_options["{$option_name}_level"] = array(
'label' => sprintf( esc_html__( '%1$s Heading Level', 'et_builder' ), $option_settings['label'] ),
'type' => 'multiple_buttons',
'option_category' => 'font_option',
'options' => array(
'h1' => array( 'title' => 'H1', 'icon' => 'text-h1', ),
'h2' => array( 'title' => 'H2', 'icon' => 'text-h2', ),
'h3' => array( 'title' => 'H3', 'icon' => 'text-h3', ),
'h4' => array( 'title' => 'H4', 'icon' => 'text-h4', ),
'h5' => array( 'title' => 'H5', 'icon' => 'text-h5', ),
'h6' => array( 'title' => 'H6', 'icon' => 'text-h6', ),
),
'default' => isset( $option_settings['header_level']['default'] ) ? $option_settings['header_level']['default'] : 'h2',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'advanced_fields' => true,
);
if ( isset( $option_settings['header_level']['computed_affects'] ) ) {
$additional_options["{$option_name}_level"]['computed_affects'] = $option_settings['header_level']['computed_affects'];
}
}
if ( ! isset( $option_settings['hide_font'] ) || ! $option_settings['hide_font'] ) {
$additional_options["{$option_name}_font"] = wp_parse_args( $option_settings['font'], array(
'label' => sprintf( esc_html__( '%1$s Font', 'et_builder' ), $option_settings['label'] ),
'type' => 'font',
'group_label' => esc_html__( $option_settings['label'] ),
'option_category' => 'font_option',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
) );
// add reference to the obsolete "all caps" option if needed
if ( isset( $option_settings['use_all_caps'] ) && $option_settings['use_all_caps'] ) {
$additional_options["{$option_name}_font"]['attributes'] = array( 'data-old-option-ref' => "{$option_name}_all_caps" );
}
// set the depends_show_if parameter if needed
if ( isset( $option_settings['depends_show_if'] ) ) {
$additional_options["{$option_name}_font"]['depends_show_if'] = $option_settings['depends_show_if'];
}
}
if ( ! isset( $option_settings['hide_text_align'] ) || ! $option_settings['hide_text_align'] ) {
$additional_options["{$option_name}_text_align"] = wp_parse_args( $option_settings['text_align'], array(
'label' => sprintf( esc_html__( '%1$s Text Alignment', 'et_builder' ), $option_settings['label'] ),
'type' => 'text_align',
'option_category' => 'layout',
'options' => et_builder_get_text_orientation_options( array( 'justified' ), array( 'justify' => 'Justified' ) ),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'advanced_fields' => true,
) );
}
if ( ! isset( $option_settings['hide_text_color'] ) || ! $option_settings['hide_text_color'] ) {
$additional_options["{$option_name}_text_color"] = array(
'label' => sprintf( esc_html__( '%1$s Text Color', 'et_builder' ), $option_settings['label'] ),
'type' => 'color-alpha',
'option_category' => 'font_option',
'custom_color' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
);
// add reference to the obsolete color option if needed
if ( self::$_->array_get( $option_settings, 'text_color.old_option_ref' ) ) {
$additional_options["{$option_name}_text_color"]['attributes'] = array( 'data-old-option-ref' => "{$option_settings['text_color']['old_option_ref']}" );
}
// set default value if defined
if ( self::$_->array_get( $option_settings, 'text_color.default' ) ) {
$additional_options["{$option_name}_text_color"]['default'] = $option_settings['text_color']['default'];
}
// set the depends_show_if parameter if needed
if ( isset( $option_settings['depends_show_if'] ) ) {
$additional_options["{$option_name}_text_color"]['depends_show_if'] = $option_settings['depends_show_if'];
}
}
if ( ! isset( $option_settings['hide_font_size'] ) || ! $option_settings['hide_font_size'] ) {
$additional_options["{$option_name}_font_size"] = wp_parse_args( $option_settings['font_size'], array(
'label' => sprintf( esc_html__( '%1$s Text Size', 'et_builder' ), $option_settings['label'] ),
'type' => 'range',
'option_category' => 'font_option',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'default_unit' => 'px',
'mobile_options' => true,
'range_settings' => array(
'min' => '1',
'max' => '100',
'step' => '1',
),
'hover' => 'tabs',
) );
// set the depends_show_if parameter if needed
if ( isset( $option_settings['depends_show_if'] ) ) {
$additional_options["{$option_name}_font_size"]['depends_show_if'] = $option_settings['depends_show_if'];
}
if ( isset( $option_settings['header_level'] ) ) {
$header_level_default = isset( $option_settings['header_level']['default'] ) ? $option_settings['header_level']['default'] : 'h2';
$additional_options["{$option_name}_font_size"]['default_value_depends'] = "{$option_name}_level";
$additional_options["{$option_name}_font_size"]['default_values_mapping'] = array(
'h1' => '30px',
'h2' => '26px',
'h3' => '22px',
'h4' => '18px',
'h5' => '16px',
'h6' => '14px',
);
// remove default font-size for default header level to use option default
unset( $additional_options["{$option_name}_font_size"]['default_values_mapping'][ $header_level_default ] );
}
$additional_options["{$option_name}_font_size_tablet"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_font_size_phone"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_font_size_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
if ( ! isset( $option_settings['hide_text_color'] ) || ! $option_settings['hide_text_color'] ) {
$additional_options["{$option_name}_text_color"] = array(
'label' => sprintf( esc_html__( '%1$s Text Color', 'et_builder' ), $option_settings['label'] ),
'type' => 'color-alpha',
'option_category' => 'font_option',
'custom_color' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'hover' => 'tabs',
);
// add reference to the obsolete color option if needed
if (self::$_->array_get($option_settings, 'text_color.old_option_ref')) {
$additional_options["{$option_name}_text_color"]['attributes'] = array( 'data-old-option-ref' => "{$option_settings['text_color']['old_option_ref']}" );
}
// set default value if defined
if (self::$_->array_get($option_settings, 'text_color.default')) {
$additional_options["{$option_name}_text_color"]['default'] = $option_settings['text_color']['default'];
}
// set the depends_show_if parameter if needed
if ( isset( $option_settings['depends_show_if'] ) ) {
$additional_options["{$option_name}_text_color"]['depends_show_if'] = $option_settings['depends_show_if'];
}
}
if ( ! isset( $option_settings['hide_letter_spacing'] ) || ! $option_settings['hide_letter_spacing'] ) {
$additional_options["{$option_name}_letter_spacing"] = wp_parse_args( $option_settings['letter_spacing'], array(
'label' => sprintf( esc_html__( '%1$s Letter Spacing', 'et_builder' ), $option_settings['label'] ),
'type' => 'range',
'mobile_options' => true,
'option_category' => 'font_option',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'default' => '0px',
'default_unit' => 'px',
'range_settings' => array(
'min' => '0',
'max' => '100',
'step' => '1',
),
'hover' => 'tabs',
) );
// set the depends_show_if parameter if needed
if ( isset( $option_settings['depends_show_if'] ) ) {
$additional_options["{$option_name}_letter_spacing"]['depends_show_if'] = $option_settings['depends_show_if'];
}
$additional_options["{$option_name}_letter_spacing_tablet"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_letter_spacing_phone"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_letter_spacing_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
if ( ! isset( $option_settings['hide_line_height'] ) || ! $option_settings['hide_line_height'] ) {
$default_option_line_height = array(
'label' => sprintf( esc_html__( '%1$s Line Height', 'et_builder' ), $option_settings['label'] ),
'type' => 'range',
'mobile_options' => true,
'option_category' => 'font_option',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
'default_unit' => 'em',
'range_settings' => array(
'min' => '1',
'max' => '3',
'step' => '0.1',
),
'hover' => 'tabs',
);
if ( isset( $option_settings['line_height'] ) ) {
$additional_options["{$option_name}_line_height"] = wp_parse_args(
$option_settings['line_height'],
$default_option_line_height
);
} else {
$additional_options["{$option_name}_line_height"] = $default_option_line_height;
}
// set the depends_show_if parameter if needed
if ( isset( $option_settings['depends_show_if'] ) ) {
$additional_options["{$option_name}_line_height"]['depends_show_if'] = $option_settings['depends_show_if'];
}
$additional_options["{$option_name}_line_height_tablet"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_line_height_phone"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_line_height_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
// Add text-shadow to font options
if ( ! isset( $option_settings['hide_text_shadow'] ) || ! $option_settings['hide_text_shadow'] ) {
$option = $this->text_shadow->get_fields(array(
// Don't use an additional label for 'text' or else we'll end up with 'Text Text Shadow....'
'label' => 'text' === $option_name ? '' : $option_settings['label'],
'prefix' => $option_name,
'option_category' => 'font_option',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
));
$additional_options = array_merge( $additional_options, $option );
};
// The below option is obsolete. This code is for backward compatibility
if ( isset( $option_settings['use_all_caps'] ) && $option_settings['use_all_caps'] ) {
$additional_options["{$option_name}_all_caps"] = array(
'type' => 'hidden',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'sub_toggle' => $sub_toggle,
);
}
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
protected function _add_background_fields() {
// Background fields are added by default if module has partial or full VB support
if ( $this->has_vb_support() ) {
$this->advanced_fields['background'] = self::$_->array_get( $this->advanced_fields, 'background', array() );
} else if ( ! $this->has_advanced_fields ) {
// Disable if module doesn't set advanced_fields property and has no VB support
return;
}
// Background settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'background' ) ) ) {
return;
}
$toggle_disabled = self::$_->array_get( $this->advanced_fields, 'background.settings.disable_toggle', false );
$tab_slug = self::$_->array_get( $this->advanced_fields, 'background.settings.tab_slug', 'general' );
$toggle_slug = '';
if ( ! $toggle_disabled ) {
$toggle_slug = self::$_->array_get( $this->advanced_fields, 'background.settings.toggle_slug', 'background' );
$background_toggle = array(
'background' => array(
'title' => esc_html__( 'Background', 'et_builder' ),
'priority' => 80,
),
);
$this->_add_settings_modal_toggles( $tab_slug, $background_toggle );
}
$background_field_name = "background";
// Possible values for use_* attributes: true, false, or 'fields_only'
$defaults = array(
'has_background_color_toggle' => false,
'use_background_color' => true,
'use_background_color_gradient' => true,
'use_background_image' => true,
'use_background_video' => true,
);
$this->advanced_fields['background'] = wp_parse_args( $this->advanced_fields['background'], $defaults );
$additional_options = array();
if ( $this->advanced_fields['background']['use_background_color'] ) {
$additional_options = array_merge(
$additional_options,
$this->generate_background_options( 'background', 'color', $tab_slug, $toggle_slug )
);
}
// Use background color toggle was added on pre color-alpha era. Added for backward
// compatibility. This option's output is printed manually on render()
if ( $this->advanced_fields['background']['has_background_color_toggle'] ) {
$additional_options['use_background_color'] = array(
'label' => esc_html__( 'Use Background Color', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'color_option',
'options' => array(
'on' => esc_html__( 'Yes', 'et_builder' ),
'off' => esc_html__( 'No', 'et_builder' ),
),
'affects' => array(
'background_color',
),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'description' => esc_html__( 'Here you can choose whether background color setting above should be used or not.', 'et_builder' ),
);
}
if ( $this->advanced_fields['background']['use_background_color_gradient'] ) {
$additional_options = array_merge(
$additional_options,
$this->generate_background_options( 'background', 'gradient', $tab_slug, $toggle_slug )
);
}
if ( $this->advanced_fields['background']['use_background_image'] ) {
$additional_options = array_merge(
$additional_options,
$this->generate_background_options( 'background', 'image', $tab_slug, $toggle_slug )
);
}
if ( $this->advanced_fields['background']['use_background_video'] ) {
$additional_options = array_merge(
$additional_options,
$this->generate_background_options( 'background', 'video', $tab_slug, $toggle_slug )
);
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
// Allow module to configure specific options
$background_options = self::$_->array_get( $this->advanced_fields, 'background.options', false );
if ( $background_options ) {
foreach ( $background_options as $option_slug => $options ) {
if ( ! is_array( $options ) ) {
continue;
}
foreach ( $options as $option_name => $option_value ) {
$additional_options[ $option_slug ][ $option_name ] = $option_value;
}
}
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
protected function _add_text_fields() {
// Text fields are added by default if module has partial or full VB support
if ( $this->has_vb_support() ) {
$this->advanced_fields['text'] = self::$_->array_get( $this->advanced_fields, 'text', array() );
} else if ( ! $this->has_advanced_fields ) {
// Disable if module doesn't set advanced_fields property and has no VB support
return;
}
// Text settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'text' ) ) ) {
return;
}
$text_settings = $this->advanced_fields['text'];
$tab_slug = isset( $text_settings['tab_slug'] ) ? $text_settings['tab_slug'] : 'advanced';
$toggle_slug = isset( $text_settings['toggle_slug'] ) ? $text_settings['toggle_slug'] : 'text';
$sub_toggle = isset( $text_settings['sub_toggle'] ) ? $text_settings['sub_toggle'] : '';
$orientation_exclude_options = isset( $text_settings['text_orientation'] ) && isset( $text_settings['text_orientation']['exclude_options'] ) ? $text_settings['text_orientation']['exclude_options'] : array();
// Make sure we can exclude text_orientation from Advanced/Text
$setting_defaults = array(
'use_text_orientation' => true,
'use_background_layout' => false,
);
$text_settings = wp_parse_args( $text_settings, $setting_defaults );
$this->_add_settings_modal_toggles( $tab_slug, array(
$toggle_slug => array(
'title' => esc_html__( 'Text', 'et_builder' ),
'priority' => 49,
),
) );
$additional_options = array();
if ( $text_settings['use_text_orientation'] ) {
$default_on_front = self::$_->array_get( $text_settings, 'options.text_orientation.default_on_front', '' );
$additional_options = array(
'text_orientation' => array(
'label' => esc_html__( 'Text Orientation', 'et_builder' ),
'type' => 'text_align',
'option_category' => 'layout',
'options' => et_builder_get_text_orientation_options( $orientation_exclude_options ),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'description' => esc_html__( 'This controls how your text is aligned within the module.', 'et_builder' ),
'advanced_fields' => true,
'default' => self::$_->array_get( $text_settings, 'options.text_orientation.default', $default_on_front ),
),
);
if ( '' !== $sub_toggle ) {
$additional_options['text_orientation']['sub_toggle'] = $sub_toggle;
}
}
// Background layout works by setting text to light/dark color. This was added before text
// color has its own colorpicker as a simple mechanism for picking color.
// New module should not use this option. This is kept for backward compatibility
if ( $text_settings['use_background_layout'] ) {
$additional_options['background_layout'] = array(
'label' => esc_html__( 'Text Color', 'et_builder' ),
'type' => 'select',
'option_category' => 'color_option',
'options' => array(
'dark' => esc_html__( 'Light', 'et_builder' ),
'light' => esc_html__( 'Dark', 'et_builder' ),
),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'hover' => 'tabs',
'description' => esc_html__( 'Here you can choose whether your text should be light or dark. If you are working with a dark background, then your text should be light. If your background is light, then your text should be set to dark.', 'et_builder' ),
);
if ( '' !== $sub_toggle ) {
$additional_options['background_layout']['sub_toggle'] = $sub_toggle;
}
}
// Allow module to configure specific options
if ( isset( $text_settings['options'] ) && is_array( $text_settings['options'] ) ) {
foreach ( $text_settings['options'] as $option_slug => $options ) {
if ( ! is_array( $options ) ) {
continue;
}
foreach ( $options as $option_name => $option_value ) {
if ( isset( $additional_options[ $option_slug ] ) ) {
$additional_options[ $option_slug ][ $option_name ] = $option_value;
}
}
}
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
/**
* Add Border & Border Radius fields to each module. Default borders option are added on with
* Borders fields group on Design tab. However, module can add more borders field by adding
* more settings on $this->advanced_fields['borders']
*
* @since 3.1
*
* {@internal
* border options are initially defined via _add_additional_border_fields() method and adding
* more border options require overwriting it on module's class. This is repetitive so
* the fields registration mechanics were simplified mimicing advanced fonts field mechanism.}
*/
protected function _add_borders_fields() {
// Disable if module doesn't set advanced_fields property and has no VB support
if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) {
return;
}
// Get borders settings. Fallback to default if needed. Borders are added to all modules by default
// unless the module explicitly disabled it
// Backward compatibility. Use `border` settings as default if exist
$legacy_border = self::$_->array_get( $this->advanced_fields, 'border', array() );
$borders_fields = self::$_->array_get( $this->advanced_fields, 'borders', array(
'default' => $legacy_border,
) );
// Borders settings have to be array
if ( ! is_array( $borders_fields ) ) {
return;
}
// Loop border settings, enable multiple border fields declaration in one place
foreach ( $borders_fields as $border_fields_name => $border_fields ) {
// Enable module to disable border options by setting it to false
if ( false === $border_fields ) {
continue;
}
// Make sure that border fields has minimum attribute required
$border_fields_defaults = array(
'tab_slug' => 'advanced',
'toggle_slug' => 'border',
);
$border_fields = wp_parse_args( $border_fields, $border_fields_defaults );
// Check for default border options
$is_default_border_options = 'default' === $border_fields_name;
if ( $is_default_border_options ) {
// Default border fields doesn't have toggle for itself, thus register new toggle
$this->_add_settings_modal_toggles( $border_fields['tab_slug'], array(
$border_fields['toggle_slug'] => array(
'title' => esc_html__( 'Border', 'et_builder' ),
'priority' => 95,
),
) );
}
// Add suffix to border fields settings
$suffix = $is_default_border_options ? '' : "_{$border_fields_name}";
$border_fields['suffix'] = $suffix;
// Assign CSS setting to advanced options
if ( isset( $border_fields['css'] ) ) {
$this->advanced_fields["border{$suffix}"]['css'] = $border_fields['css'];
}
// Add border fields to advanced_fields. Each border fields (style + radii) has its own attribute
// registered on $this->advanced_fields
self::$_->array_set( $this->advanced_fields, "border{$suffix}", $border_fields );
$this->_additional_fields_options = array_merge(
$this->_additional_fields_options,
ET_Builder_Module_Fields_Factory::get( 'Border' )->get_fields( $border_fields )
);
// Add module defined fields that needs to be added after existing border options
if ( isset( $border_fields['fields_after'] ) ) {
$this->_additional_fields_options = array_merge(
$this->_additional_fields_options,
$border_fields['fields_after']
);
}
// Loop radii and styles and add fields to advanced_fields
foreach ( array( 'border_radii', 'border_styles' ) as $border_key ) {
$border_key_name = $border_key . $suffix;
if ( isset( $this->advanced_fields["border{$suffix}"][ $border_key_name ] ) ) {
// Backward compatibility. Properly handle existing 3rd party module that
// directly defines border via direct $this->advanced_fields["border{$suffix}"]
$this->advanced_fields["border{$suffix}"][ $border_key_name ] = array_merge(
$this->advanced_fields["border{$suffix}"][ $border_key_name ],
$this->_additional_fields_options[ $border_key_name ]
);
$message = "You're Doing It Wrong! You shouldn't define border settings in 'advanced_fields' directly. All the Border settings should be defined via provided API";
et_debug( $message );
} else {
// Add border options to advanced_fields
$this->advanced_fields["border{$suffix}"][ $border_key_name ] = $this->_additional_fields_options[ $border_key_name ];
}
}
}
if ( method_exists( $this, '_add_additional_border_fields' ) ) {
// Backwards compatibility should go after all the fields added to emulate behavior of old version
$this->_add_additional_border_fields();
$message = "You're Doing It Wrong! '_add_additional_border_fields' is deprecated. All the Border settings should be defined via provided API";
et_debug( $message );
}
}
protected function _add_max_width_fields() {
// Max width fields are added by default if module has partial or full VB support
if ( $this->has_vb_support() ) {
$this->advanced_fields['max_width'] = self::$_->array_get( $this->advanced_fields, 'max_width', array() );
} else if ( ! $this->has_advanced_fields ) {
// Disable if module doesn't set advanced_fields property and has no VB support
return;
}
// Max width settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'max_width' ) ) ) {
return;
}
$max_width_settings = $this->advanced_fields['max_width'];
$tab_slug = isset( $max_width_settings['tab_slug'] ) ? $max_width_settings['tab_slug'] : 'advanced';
$toggle_slug = isset( $max_width_settings['toggle_slug'] ) ? $max_width_settings['toggle_slug'] : 'width';
$toggle_title = isset( $max_width_settings['toggle_title'] ) ? $max_width_settings['toggle_title'] : esc_html__( 'Sizing', 'et_builder' );
$toggle_priority = isset( $max_width_settings['toggle_priority'] ) ? $max_width_settings['toggle_priority'] : 80;
$setting_defaults = array(
'use_max_width' => true,
'use_module_alignment' => true,
);
$this->advanced_fields['max_width'] = wp_parse_args( $this->advanced_fields['max_width'], $setting_defaults );
$this->_add_settings_modal_toggles( $tab_slug, array(
$toggle_slug => array(
'title' => $toggle_title,
'priority' => $toggle_priority,
),
) );
// Added max width option
if ( $this->advanced_fields['max_width']['use_max_width'] ) {
$additional_options = array(
'max_width' => array(
'label' => esc_html__( 'Width', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'mobile_options' => true,
'default_on_child' => true,
'validate_unit' => true,
'default' => '100%',
'default_tablet' => '100%',
'default_unit' => '%',
'allow_empty' => true,
'range_settings' => array(
'min' => '0',
'max' => '100',
'step' => '1',
),
'hover' => 'tabs',
),
'max_width_tablet' => array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
),
'max_width_phone' => array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
),
'max_width_last_edited' => array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
),
);
}
// Added module alignment option
$allowed_children = array( 'et_pb_counter', 'et_pb_accordion_item' );
$is_excluded = isset( $this->type ) && 'child' === $this->type && ! in_array( $this->slug, $allowed_children );
if ( $this->advanced_fields['max_width']['use_module_alignment'] && ! $is_excluded ) {
$additional_options['module_alignment'] = array(
'label' => esc_html__( 'Module Alignment', 'et_builder' ),
'type' => 'text_align',
'option_category' => 'layout',
'options' => et_builder_get_text_orientation_options( array( 'justified' ) ),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
// Added max width & module alignment attributes which only make sense if both field exist
if ( $this->advanced_fields['max_width']['use_max_width'] ) {
$additional_options['max_width']['responsive_affects'] = array(
'module_alignment',
);
$additional_options['module_alignment']['depends_on'] = array(
'max_width',
et_pb_hover_options()->get_hover_field( 'max_width' ),
);
$additional_options['module_alignment']['depends_on_responsive'] = array(
'max_width',
);
$additional_options['module_alignment']['depends_show_if_not'] = array(
'',
'100%',
);
}
}
// Allow module to configure specific options
if ( isset( $max_width_settings['options'] ) && is_array( $max_width_settings['options'] ) ) {
foreach ( $max_width_settings['options'] as $option_slug => $options ) {
if ( ! is_array( $options ) ) {
continue;
}
foreach ( $options as $option_name => $option_value ) {
$additional_options[ $option_slug ][ $option_name ] = $option_value;
}
}
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
protected function _add_margin_padding_fields() {
// Margin-Padding fields are added by default if module has partial or full VB support
if ( $this->has_vb_support() ) {
$this->advanced_fields['margin_padding'] = self::$_->array_get( $this->advanced_fields, 'margin_padding', array() );
} else if ( ! $this->has_advanced_fields ) {
// Disable if module doesn't set advanced_fields property and has no VB support
return;
}
// Margin settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'margin_padding' ) ) ) {
return;
}
$additional_options = array();
$defaults = array(
'use_margin' => true,
'use_padding' => true,
);
$this->advanced_fields['margin_padding'] = wp_parse_args( $this->advanced_fields['margin_padding'], $defaults );
$tab_slug = isset( $this->advanced_fields['margin_padding']['tab_slug'] ) ? $this->advanced_fields['margin_padding']['tab_slug'] : 'advanced';
$toggle_disabled = isset( $this->advanced_fields['margin_padding']['disable_toggle'] ) && $this->advanced_fields['margin_padding']['disable_toggle'];
$toggle_slug = isset( $this->advanced_fields['margin_padding']['toggle_slug'] ) ? $this->advanced_fields['margin_padding']['toggle_slug'] : 'margin_padding';
if ( ! $toggle_disabled ) {
$margin_toggle = array(
$toggle_slug => array(
'title' => esc_html__( 'Spacing', 'et_builder' ),
'priority' => 90,
),
);
$this->_add_settings_modal_toggles( $tab_slug, $margin_toggle );
}
if ( $this->advanced_fields['margin_padding']['use_margin'] ) {
$additional_options['custom_margin'] = array(
'label' => esc_html__( 'Custom Margin', 'et_builder' ),
'type' => 'custom_margin',
'mobile_options' => true,
'option_category' => 'layout',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'hover' => 'tabs',
);
$additional_options['custom_margin_tablet'] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
);
$additional_options['custom_margin_phone'] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
// make it possible to override/add options
if ( ! empty( $this->advanced_fields['margin_padding']['custom_margin'] ) ) {
$additional_options['custom_margin'] = array_merge( $additional_options['custom_margin'], $this->advanced_fields['margin_padding']['custom_margin'] );
}
$additional_options["custom_margin_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["padding_1_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["padding_2_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["padding_3_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["padding_4_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
if ( $this->advanced_fields['margin_padding']['use_padding'] ) {
$additional_options['custom_padding'] = array(
'label' => esc_html__( 'Custom Padding', 'et_builder' ),
'type' => 'custom_padding',
'mobile_options' => true,
'option_category' => 'layout',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'hover' => 'tabs',
);
$additional_options['custom_padding_tablet'] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options['custom_padding_phone'] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
// make it possible to override/add options
if ( ! empty( $this->advanced_fields['margin_padding']['custom_padding'] ) ) {
$additional_options['custom_padding'] = array_merge( $additional_options['custom_padding'], $this->advanced_fields['margin_padding']['custom_padding'] );
}
$additional_options["custom_padding_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
protected function _add_button_fields() {
// Disable if module doesn't set advanced_fields property and has no VB support
if ( ! $this->has_advanced_fields ) {
return;
}
// Button settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'button' ) ) ) {
return;
}
// Auto-add attributes toggle
$toggles_custom_css_tab = isset( $this->settings_modal_toggles['custom_css'] ) ? $this->settings_modal_toggles['custom_css'] : array();
if ( ! isset( $toggles_custom_css_tab['toggles'] ) || ! isset( $toggles_custom_css_tab['toggles']['attributes'] ) ) {
$this->_add_settings_modal_toggles( 'custom_css', array(
'attributes' => array(
'title' => esc_html__( 'Attributes', 'et_builder' ),
'priority' => 95,
),
) );
}
$additional_options = array();
$hover = et_pb_hover_options();
foreach ( $this->advanced_fields['button'] as $option_name => $option_settings ) {
$tab_slug = isset( $option_settings['tab_slug'] ) ? $option_settings['tab_slug'] : 'advanced';
$toggle_disabled = isset( $option_settings['disable_toggle'] ) && $option_settings['disable_toggle'];
$toggle_slug = '';
if ( ! $toggle_disabled ) {
$toggle_slug = isset( $option_settings['toggle_slug'] ) ? $option_settings['toggle_slug'] : $option_name;
$button_toggle = array(
$option_name => array(
'title' => esc_html( $option_settings['label'] ),
'priority' => 70,
),
);
$this->_add_settings_modal_toggles( $tab_slug, $button_toggle );
}
$additional_options["custom_{$option_name}"] = array(
'label' => sprintf( esc_html__( 'Use Custom Styles for %1$s ', 'et_builder' ), $option_settings['label'] ),
'type' => 'yes_no_button',
'option_category' => 'button',
'options' => array(
'off' => esc_html__( 'No', 'et_builder' ),
'on' => esc_html__( 'Yes', 'et_builder' ),
),
'affects' => array(
"{$option_name}_text_color",
"{$option_name}_text_size",
"{$option_name}_border_width",
"{$option_name}_border_radius",
"{$option_name}_letter_spacing",
"{$option_name}_spacing",
"{$option_name}_bg_color",
"{$option_name}_border_color",
"{$option_name}_use_icon",
"{$option_name}_font",
$hover->get_hover_field( "{$option_name}_text_color" ),
$hover->get_hover_field( "{$option_name}_border_color" ),
$hover->get_hover_field( "{$option_name}_border_radius" ),
$hover->get_hover_field( "{$option_name}_letter_spacing" ),
"{$option_name}_text_shadow_style", // Add Text Shadow to button options
"box_shadow_style_{$option_name}",
),
'default_on_front' => 'off',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_text_size"] = array(
'label' => sprintf( esc_html__( '%1$s Text Size', 'et_builder' ), $option_settings['label'] ),
'type' => 'range',
'range_settings' => array(
'min' => '1',
'max' => '100',
'step' => '1',
),
'option_category' => 'button',
'default' => ET_Global_Settings::get_value( 'all_buttons_font_size' ),
'default_unit' => 'px',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'mobile_options' => true,
'depends_show_if' => 'on',
'hover' => 'tabs',
);
$additional_options["{$option_name}_text_color"] = array(
'label' => sprintf( esc_html__( '%1$s Text Color', 'et_builder' ), $option_settings['label'] ),
'type' => 'color-alpha',
'option_category' => 'button',
'custom_color' => true,
'default' => '',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
'hover' => 'tabs',
);
$additional_options["{$option_name}_bg_color"] = array(
'label' => sprintf( esc_html__( '%1$s Background Color', 'et_builder' ), $option_settings['label'] ),
'type' => 'background-field',
'base_name' => "{$option_name}_bg",
'context' => "{$option_name}_bg",
'option_category' => 'button',
'custom_color' => true,
'default' => ET_Global_Settings::get_value( 'all_buttons_bg_color' ),
'default_on_front' => '',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
'background_fields' => $this->generate_background_options( "{$option_name}_bg", 'button', $tab_slug, $toggle_slug, "{$option_name}_bg_color" ),
'hover' => 'tabs',
);
$additional_options["{$option_name}_bg_color"]['background_fields']["{$option_name}_bg_color"]['default'] = ET_Global_Settings::get_value( 'all_buttons_bg_color' );
$additional_options = array_merge( $additional_options, $this->generate_background_options( "{$option_name}_bg", 'skip', $tab_slug, $toggle_slug, "{$option_name}_bg_color" ) );
$additional_options["{$option_name}_border_width"] = array(
'label' => sprintf( esc_html__( '%1$s Border Width', 'et_builder' ), $option_settings['label'] ),
'type' => 'range',
'option_category' => 'button',
'default' => ET_Global_Settings::get_value( 'all_buttons_border_width' ),
'default_unit' => 'px',
'default_on_front' => '',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
'hover' => 'tabs',
);
$additional_options["{$option_name}_border_color"] = array(
'label' => sprintf( esc_html__( '%1$s Border Color', 'et_builder' ), $option_settings['label'] ),
'type' => 'color-alpha',
'option_category' => 'button',
'custom_color' => true,
'default' => '',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
'hover' => 'tabs',
);
$additional_options["{$option_name}_border_radius"] = array(
'label' => sprintf( esc_html__( '%1$s Border Radius', 'et_builder' ), $option_settings['label'] ),
'type' => 'range',
'option_category' => 'button',
'default' => ET_Global_Settings::get_value( 'all_buttons_border_radius' ),
'default_unit' => 'px',
'default_on_front' => '',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
'hover' => 'tabs',
);
$additional_options["{$option_name}_letter_spacing"] = array(
'label' => sprintf( esc_html__( '%1$s Letter Spacing', 'et_builder' ), $option_settings['label'] ),
'type' => 'range',
'option_category' => 'button',
'default' => ET_Global_Settings::get_value( 'all_buttons_spacing' ),
'default_unit' => 'px',
'default_on_front' => '',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'mobile_options' => true,
'depends_show_if' => 'on',
'hover' => 'tabs',
);
$additional_options["{$option_name}_font"] = array(
'label' => sprintf( esc_html__( '%1$s Font', 'et_builder' ), $option_settings['label'] ),
'type' => 'font',
'option_category' => 'button',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
);
$additional_options["{$option_name}_use_icon"] = array(
'label' => sprintf( esc_html__( 'Show %1$s Icon', 'et_builder' ), $option_settings['label'] ),
'type' => 'yes_no_button',
'option_category' => 'button',
'default' => 'on',
'options' => array(
'on' => esc_html__( 'Yes', 'et_builder' ),
'off' => esc_html__( 'No', 'et_builder' ),
),
'affects' => array(
"{$option_name}_icon_color",
"{$option_name}_icon_placement",
"{$option_name}_on_hover",
"{$option_name}_icon",
),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
);
$additional_options["{$option_name}_icon"] = array(
'label' => sprintf( esc_html__( '%1$s Icon', 'et_builder' ), $option_settings['label'] ),
'type' => 'select_icon',
'option_category' => 'button',
'class' => array( 'et-pb-font-icon' ),
'default' => '',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'off',
);
$additional_options["{$option_name}_icon_color"] = array(
'label' => sprintf( esc_html__( '%1$s Icon Color', 'et_builder' ), $option_settings['label'] ),
'type' => 'color-alpha',
'option_category' => 'button',
'custom_color' => true,
'default' => '',
'hover' => 'tabs',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'off',
);
$additional_options["{$option_name}_icon_placement"] = array(
'label' => sprintf( esc_html__( '%1$s Icon Placement', 'et_builder' ), $option_settings['label'] ),
'type' => 'select',
'option_category' => 'button',
'options' => array(
'right' => esc_html__( 'Right', 'et_builder' ),
'left' => esc_html__( 'Left', 'et_builder' ),
),
'default' => 'right',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'off',
);
$additional_options["{$option_name}_on_hover"] = array(
'label' => sprintf( esc_html__( 'Only Show Icon On Hover for %1$s', 'et_builder' ), $option_settings['label'] ),
'type' => 'yes_no_button',
'option_category' => 'button',
'default' => 'on',
'options' => array(
'on' => esc_html__( 'Yes', 'et_builder' ),
'off' => esc_html__( 'No', 'et_builder' ),
),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if_not' => 'off',
);
$additional_options["{$option_name}_text_size_tablet"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_text_size_phone"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_letter_spacing_tablet"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_letter_spacing_phone"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_letter_spacing_hover_tablet"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_letter_spacing_hover_phone"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_text_size_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options["{$option_name}_letter_spacing_last_edited"] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
$additional_options[$hover->get_hover_field( "{$option_name}_letter_spacing_last_edited" )] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
if ( isset( $option_settings['use_alignment'] ) && $option_settings['use_alignment'] ) {
$additional_options["{$option_name}_alignment"] = array(
'label' => esc_html__( 'Button Alignment', 'et_builder' ),
'type' => 'text_align',
'option_category' => 'layout',
'options' => et_builder_get_text_orientation_options( array( 'justified' ) ),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
// The configurable rel attribute field is added by default
if ( ! isset( $option_settings['no_rel_attr'] ) ) {
$additional_options["{$option_name}_rel"] = array(
'label' => sprintf( esc_html__( '%1$s Relationship', 'et_builder' ), $option_settings['label'] ),
'type' => 'multiple_checkboxes',
'option_category' => 'configuration',
'options' => $this->get_rel_values(),
'description' => et_get_safe_localization( __( "Specify the value of your link's rel attribute. The rel attribute specifies the relationship between the current document and the linked document. Tip: Search engines can use this attribute to get more information about a link.", 'et_builder' ) ),
'tab_slug' => 'custom_css',
'toggle_slug' => 'attributes',
'shortcut_index' => $option_name,
);
}
// Add text-shadow to button options
$option = $this->text_shadow->get_fields(array(
'label' => $option_settings['label'],
'prefix' => $option_name,
'option_category' => 'font_option',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
));
$additional_options = array_merge( $additional_options, $option );
// Conditionally add box-shadow options to button options. Get box shadow settings for advanced button fields.
$button_box_shadow_options = self::$_->array_get( $option_settings, 'box_shadow', array() );
// Enable module to remove box shadow from advanced button fields by declaring false value to box
// shadow attribute (i.e. button module)
if ( false !== $button_box_shadow_options ) {
$button_box_shadow_options = wp_parse_args( $button_box_shadow_options, array(
'label' => esc_html__( 'Button Box Shadow', 'et_builder' ),
'option_category' => 'layout',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'depends_show_if' => 'on',
) );
// Only print box shadow styling if custom_* attribute is equal to "on" by adding show_iff attribute
$button_visibility_condition = array( "custom_{$option_name}" => 'on' );
self::$_->array_set( $button_box_shadow_options, 'css.show_if', $button_visibility_condition );
// Automatically add default box shadow fields if box shadow attribute hasn't even defined yet.
// No attribute found is considered true for default thus if this about to add the first advanced
// box shadow, add the default first
if ( ! isset( $this->advanced_fields['box_shadow'] ) ) {
$button_box_shadow_options_default = array();
self::$_->array_set( $this->advanced_fields, "box_shadow.default", $button_box_shadow_options_default );
}
// Box shadow fields are generated after button fields being added. Thus, adding $this->advanced_fields
// is sufficient to insert the box shadow fields
self::$_->array_set( $this->advanced_fields, "box_shadow.{$option_name}", $button_box_shadow_options );
}
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
protected function _add_animation_fields() {
// Animation fields are added by default on all module
$this->advanced_fields['animation'] = self::$_->array_get( $this->advanced_fields, 'animation', array() );
// Animation Disabled
if ( false === $this->advanced_fields['animation'] ) {
return;
}
$classname = get_class( $this );
if ( isset( $this->type ) && 'child' === $this->type ) {
return;
}
$this->settings_modal_toggles['advanced']['toggles']['animation'] = array(
'title' => esc_html__( 'Animation', 'et_builder' ),
'priority' => 110,
);
$additional_options = array();
$animations_intensity_fields = array(
'animation_intensity_slide',
'animation_intensity_zoom',
'animation_intensity_flip',
'animation_intensity_fold',
'animation_intensity_roll',
);
$additional_options['animation_style'] = array(
'label' => esc_html__( 'Animation Style', 'et_builder' ),
'type' => 'select_animation',
'option_category' => 'configuration',
'default' => 'none',
'description' => esc_html__( 'Pick an animation style to enable animations for this element. Once enabled, you will be able to customize your animation style further. To disable animations, choose the None option.' ),
'options' => array(
'none' => esc_html__( 'None', 'et_builder' ),
'fade' => esc_html__( 'Fade', 'et_builder' ),
'slide' => esc_html__( 'Slide', 'et_builder' ),
'bounce' => esc_html__( 'Bounce', 'et_builder' ),
'zoom' => esc_html__( 'Zoom', 'et_builder' ),
'flip' => esc_html__( 'Flip', 'et_builder' ),
'fold' => esc_html__( 'Fold', 'et_builder' ),
'roll' => esc_html__( 'Roll', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'affects' => array_merge( array(
'animation_repeat',
'animation_direction',
'animation_duration',
'animation_delay',
'animation_starting_opacity',
'animation_speed_curve',
), $animations_intensity_fields ),
);
$additional_options['animation_repeat'] = array(
'label' => esc_html__( 'Animation Repeat', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'default' => 'once',
'description' => esc_html__( 'By default, animations will only play once. If you would like to loop your animation continuously you can choose the Loop option here.' ),
'options' => array(
'once' => esc_html__( 'Once', 'et_builder' ),
'loop' => esc_html__( 'Loop', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'depends_show_if_not' => 'none',
);
$additional_options['animation_direction'] = array(
'label' => esc_html__( 'Animation Direction', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'default' => 'center',
'description' => esc_html__( 'Pick from up to five different animation directions, each of which will adjust the starting and ending position of your animated element.' ),
'options' => array(
'center' => esc_html__( 'Center', 'et_builder' ),
'left' => esc_html__( 'Right', 'et_builder' ),
'right' => esc_html__( 'Left', 'et_builder' ),
'bottom' => esc_html__( 'Up', 'et_builder' ),
'top' => esc_html__( 'Down', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'depends_show_if_not' => array( 'none', 'fade' ),
);
$additional_options['animation_duration'] = array(
'label' => esc_html__( 'Animation Duration', 'et_builder' ),
'type' => 'range',
'option_category' => 'configuration',
'range_settings' => array(
'min' => 0,
'max' => 2000,
'step' => 50,
),
'default' => '1000ms',
'description' => esc_html__( 'Speed up or slow down your animation by adjusting the animation duration. Units are in milliseconds and the default animation duration is one second.' ),
'validate_unit' => true,
'fixed_unit' => 'ms',
'fixed_range' => true,
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'depends_show_if_not' => 'none',
'reset_animation' => true,
);
$additional_options['animation_delay'] = array(
'label' => esc_html__( 'Animation Delay', 'et_builder' ),
'type' => 'range',
'option_category' => 'configuration',
'range_settings' => array(
'min' => 0,
'max' => 3000,
'step' => 50,
),
'default' => '0ms',
'description' => esc_html__( 'If you would like to add a delay before your animation runs you can designate that delay here in milliseconds. This can be useful when using multiple animated modules together.' ),
'validate_unit' => true,
'fixed_unit' => 'ms',
'fixed_range' => true,
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'depends_show_if_not' => 'none',
'reset_animation' => true,
);
foreach ( $animations_intensity_fields as $animations_intensity_field ) {
$animation_style = str_replace( 'animation_intensity_', '', $animations_intensity_field );
$additional_options[ $animations_intensity_field ] = array(
'label' => esc_html__( 'Animation Intensity', 'et_builder' ),
'type' => 'range',
'option_category' => 'configuration',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '50%',
'description' => esc_html__( 'Intensity effects how subtle or aggressive your animation will be. Lowering the intensity will create a smoother and more subtle animation while increasing the intensity will create a snappier more aggressive animation.' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'depends_show_if' => $animation_style,
'reset_animation' => true,
);
}
$additional_options['animation_starting_opacity'] = array(
'label' => esc_html__( 'Animation Starting Opacity', 'et_builder' ),
'type' => 'range',
'option_category' => 'configuration',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '0%',
'description' => esc_html__( 'By increasing the starting opacity, you can can reduce or remove the fade effect that is applied to all animation styles.' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'depends_show_if_not' => 'none',
'reset_animation' => true,
);
$additional_options['animation_speed_curve'] = array(
'label' => esc_html__( 'Animation Speed Curve', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'default' => 'ease-in-out',
'description' => esc_html__( 'Here you can adjust the easing method of your animation. Easing your animation in and out will create a smoother effect when compared to a linear speed curve.' ),
'options' => array(
'ease-in-out' => esc_html__( 'Ease-In-Out', 'et_builder' ),
'ease' => esc_html__( 'Ease', 'et_builder' ),
'ease-in' => esc_html__( 'Ease-In', 'et_builder' ),
'ease-out' => esc_html__( 'Ease-Out', 'et_builder' ),
'linear' => esc_html__( 'Linear', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'depends_show_if_not' => 'none',
);
if ( isset( $this->slug ) && 'et_pb_fullwidth_menu' === $this->slug ) {
$additional_options['dropdown_menu_animation'] = array(
'label' => esc_html__( 'Dropdown Menu Animation', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'options' => array(
'fade' => esc_html__( 'Fade', 'et_builder' ),
'expand' => esc_html__( 'Expand', 'et_builder' ),
'slide' => esc_html__( 'Slide', 'et_builder' ),
'flip' => esc_html__( 'Flip', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'default' => 'fade',
);
}
// Move existing "Animation" section fields under the new animations UI
if ( isset( $this->slug ) && 'et_pb_fullwidth_portfolio' === $this->slug ) {
$additional_options['auto'] = array(
'label' => esc_html__( 'Automatic Carousel Rotation', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'affects' => array(
'auto_speed',
),
'depends_show_if' => 'on',
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'If you the carousel layout option is chosen and you would like the carousel to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ),
'default' => 'off',
);
$additional_options['auto_speed'] = array(
'label' => esc_html__( 'Automatic Carousel Rotation Speed (in ms)', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( "Here you can designate how fast the carousel rotates, if 'Automatic Carousel Rotation' option is enabled above. The higher the number the longer the pause between each rotation. (Ex. 1000 = 1 sec)", 'et_builder' ),
'default' => '7000',
);
}
if ( isset( $this->slug ) && 'et_pb_fullwidth_slider' === $this->slug ) {
$additional_options['auto'] = array(
'label' => esc_html__( 'Automatic Animation', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'affects' => array(
'auto_speed',
'auto_ignore_hover',
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ),
'default' => 'off',
);
$additional_options['auto_speed'] = array(
'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ),
'default' => '7000',
);
$additional_options['auto_ignore_hover'] = array(
'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ),
'default' => 'off',
);
}
if ( isset( $this->slug ) && 'et_pb_fullwidth_post_slider' === $this->slug ) {
$additional_options['auto'] = array(
'label' => esc_html__( 'Automatic Animation', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'affects' => array(
'auto_speed',
'auto_ignore_hover',
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ),
'default' => 'off',
);
$additional_options['auto_speed'] = array(
'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ),
'default' => '7000',
);
$additional_options['auto_ignore_hover'] = array(
'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ),
'default' => 'off',
);
}
if ( isset( $this->slug ) && 'et_pb_gallery' === $this->slug ) {
$additional_options['auto'] = array(
'label' => esc_html__( 'Automatic Animation', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'affects' => array(
'auto_speed',
),
'depends_show_if' => 'on',
'depends_on' => array(
'fullwidth',
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ),
'default' => 'off',
);
$additional_options['auto_speed'] = array(
'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ),
'default' => '7000',
);
}
if ( isset( $this->slug ) && 'et_pb_blurb' === $this->slug ) {
$additional_options['animation'] = array(
'label' => esc_html__( 'Image/Icon Animation', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'options' => array(
'top' => esc_html__( 'Top To Bottom', 'et_builder' ),
'left' => esc_html__( 'Left To Right', 'et_builder' ),
'right' => esc_html__( 'Right To Left', 'et_builder' ),
'bottom' => esc_html__( 'Bottom To Top', 'et_builder' ),
'off' => esc_html__( 'No Animation', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'This controls the direction of the lazy-loading animation.', 'et_builder' ),
'default' => 'top',
);
}
if ( isset( $this->slug ) && 'et_pb_slider' === $this->slug ) {
$additional_options['auto'] = array(
'label' => esc_html__( 'Automatic Animation', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'affects' => array(
'auto_speed',
'auto_ignore_hover',
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ),
'default' => 'off',
);
$additional_options['auto_speed'] = array(
'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ),
'default' => '7000',
);
$additional_options['auto_ignore_hover'] = array(
'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ),
'default' => 'off',
);
}
if ( isset( $this->slug ) && 'et_pb_post_slider' === $this->slug ) {
$additional_options['auto'] = array(
'label' => esc_html__( 'Automatic Animation', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'affects' => array(
'auto_speed',
'auto_ignore_hover',
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'If you would like the slider to slide automatically, without the visitor having to click the next button, enable this option and then adjust the rotation speed below if desired.', 'et_builder' ),
'default' => 'off',
);
$additional_options['auto_speed'] = array(
'label' => esc_html__( 'Automatic Animation Speed (in ms)', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( "Here you can designate how fast the slider fades between each slide, if 'Automatic Animation' option is enabled above. The higher the number the longer the pause between each rotation.", 'et_builder' ),
'default' => '7000',
);
$additional_options['auto_ignore_hover'] = array(
'label' => esc_html__( 'Continue Automatic Slide on Hover', 'et_builder' ),
'type' => 'yes_no_button',
'option_category' => 'configuration',
'depends_show_if' => 'on',
'options' => array(
'off' => esc_html__( 'Off', 'et_builder' ),
'on' => esc_html__( 'On', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'Turning this on will allow automatic sliding to continue on mouse hover.', 'et_builder' ),
'default' => 'off',
);
}
if ( isset( $this->slug ) && 'et_pb_team_member' === $this->slug ) {
$additional_options['animation'] = array(
'label' => esc_html__( 'Animation', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'No Animation', 'et_builder' ),
'fade_in' => esc_html__( 'Fade In', 'et_builder' ),
'left' => esc_html__( 'Left To Right', 'et_builder' ),
'right' => esc_html__( 'Right To Left', 'et_builder' ),
'top' => esc_html__( 'Top To Bottom', 'et_builder' ),
'bottom' => esc_html__( 'Bottom To Top', 'et_builder' ),
),
'tab_slug' => 'advanced',
'toggle_slug' => 'animation',
'description' => esc_html__( 'This controls the direction of the lazy-loading animation.', 'et_builder' ),
'default' => 'off',
);
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
private function _add_additional_transition_fields() {
$this->settings_modal_toggles['custom_css']['toggles']['hover_transitions'] = array(
'title' => esc_html__( 'Transitions', 'et_builder' ),
'priority' => 120,
);
$additional_options = array();
$additional_options['hover_transition_duration'] = array(
'label' => esc_html__( 'Transition Duration', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 2000,
'step' => 50,
),
'default' => '300ms',
'default_on_child' => true,
'validate_unit' => true,
'fixed_unit' => 'ms',
'fixed_range' => true,
'tab_slug' => 'custom_css',
'toggle_slug' => 'hover_transitions',
'depends_default' => null,
'description' => esc_html__( 'This controls the transition duration of the hover animation.', 'et_builder' ),
);
$additional_options['hover_transition_delay'] = array(
'label' => esc_html__( 'Transition Delay', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 300,
'step' => 50,
),
'default' => '0ms',
'default_on_child' => true,
'validate_unit' => true,
'fixed_unit' => 'ms',
'fixed_range' => true,
'tab_slug' => 'custom_css',
'toggle_slug' => 'hover_transitions',
'depends_default' => null,
'description' => esc_html__( 'This controls the transition delay of the hover animation.', 'et_builder' ),
);
$additional_options['hover_transition_speed_curve'] = array(
'label' => esc_html__( 'Transition Speed Curve', 'et_builder' ),
'type' => 'select',
'option_category' => 'layout',
'default' => 'ease',
'default_on_child' => true,
'description' => esc_html__( 'This controls the transition speed curve of the hover animation.', 'et_builder' ),
'options' => array(
'ease-in-out' => esc_html__( 'Ease-In-Out', 'et_builder' ),
'ease' => esc_html__( 'Ease', 'et_builder' ),
'ease-in' => esc_html__( 'Ease-In', 'et_builder' ),
'ease-out' => esc_html__( 'Ease-Out', 'et_builder' ),
'linear' => esc_html__( 'Linear', 'et_builder' ),
),
'tab_slug' => 'custom_css',
'toggle_slug' => 'hover_transitions',
'depends_default' => null,
);
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
/**
* Add CSS filter controls (i.e. saturation, brightness, opacity) to the `_additional_fields_options` array.
*
* @return void
*/
protected function _add_filter_fields() {
// Filter fields are added by default if module has partial or full VB support
if ( $this->has_vb_support() ) {
$this->advanced_fields['filters'] = self::$_->array_get( $this->advanced_fields, 'filters', array() );
} else if ( ! $this->has_advanced_fields ) {
// Disable if module doesn't set advanced_fields property and has no VB support
return;
}
// Module has to explicitly set false to disable filters options
if ( false === self::$_->array_get( $this->advanced_fields, 'filters', false ) ) {
return;
}
$filter_settings = self::$_->array_get( $this->advanced_fields, 'filters' );
$tab_slug = self::$_->array_get( $filter_settings, 'tab_slug', 'advanced' );
$toggle_slug = self::$_->array_get( $filter_settings, 'toggle_slug','filters' );
$toggle_name = self::$_->array_get( $filter_settings, 'toggle_name', esc_html__( 'Filters', 'et_builder' ) );
$this->_add_settings_modal_toggles( $tab_slug, array(
$toggle_slug => array(
'title' => $toggle_name,
'priority' => 105,
),
) );
$additional_options = array();
$additional_options['filter_hue_rotate'] = array(
'label' => esc_html__( 'Hue', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 359,
'step' => 1,
),
'default' => '0deg',
'default_on_child' => true,
'description' => esc_html__( 'Shift all colors by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => 'deg',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['filter_saturate'] = array(
'label' => esc_html__( 'Saturation', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 200,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how intense the color saturation should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['filter_brightness'] = array(
'label' => esc_html__( 'Brightness', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 200,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how bright the colors should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['filter_contrast'] = array(
'label' => esc_html__( 'Contrast', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 200,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how distinct bright and dark areas should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['filter_invert'] = array(
'label' => esc_html__( 'Invert', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '0%',
'default_on_child' => true,
'description' => esc_html__( 'Invert the hue, saturation, and brightness by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['filter_sepia'] = array(
'label' => esc_html__( 'Sepia', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '0%',
'default_on_child' => true,
'description' => esc_html__( 'Travel back in time by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['filter_opacity'] = array(
'label' => esc_html__( 'Opacity', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how transparent or opaque this should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['filter_blur'] = array(
'label' => esc_html__( 'Blur', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 50,
'step' => 1,
),
'default' => '0px',
'default_unit' => 'px',
'default_on_child' => true,
'description' => esc_html__( 'Blur by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => 'px',
'fixed_range' => true,
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
'hover' => 'tabs',
);
$additional_options['mix_blend_mode'] = array(
'label' => esc_html__( 'Blend Mode', 'et_builder' ),
'type' => 'select',
'option_category' => 'layout',
'default' => 'normal',
'default_on_child' => true,
'description' => esc_html__( 'Modify how this element blends with any layers beneath it. To reset, choose the "Normal" option.' ),
'options' => array(
'normal' => esc_html__( 'Normal', 'et_builder' ),
'multiply' => esc_html__( 'Multiply', 'et_builder' ),
'screen' => esc_html__( 'Screen', 'et_builder' ),
'overlay' => esc_html__( 'Overlay', 'et_builder' ),
'darken' => esc_html__( 'Darken', 'et_builder' ),
'lighten' => esc_html__( 'Lighten', 'et_builder' ),
'color-dodge' => esc_html__( 'Color Dodge', 'et_builder' ),
'color-burn' => esc_html__( 'Color Burn', 'et_builder' ),
'hard-light' => esc_html__( 'Hard Light', 'et_builder' ),
'soft-light' => esc_html__( 'Soft Light', 'et_builder' ),
'difference' => esc_html__( 'Difference', 'et_builder' ),
'exclusion' => esc_html__( 'Exclusion', 'et_builder' ),
'hue' => esc_html__( 'Hue', 'et_builder' ),
'saturation' => esc_html__( 'Saturation', 'et_builder' ),
'color' => esc_html__( 'Color', 'et_builder' ),
'luminosity' => esc_html__( 'Luminosity', 'et_builder' ),
),
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
'reset_animation' => false,
);
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
// Maybe add child filters (i.e. targeting only images within a module)
if ( ! isset( $this->advanced_fields['filters']['child_filters_target'] ) ) {
return;
}
$child_filter = $this->advanced_fields['filters']['child_filters_target'];
$additional_child_options = array(
'child_filter_hue_rotate' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Hue', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 359,
'step' => 1,
),
'default' => '0deg',
'default_on_child' => true,
'description' => esc_html__( 'Shift all colors by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => 'deg',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_filter_saturate' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Saturation', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 200,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how intense the color saturation should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_filter_brightness' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Brightness', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 200,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how bright the colors should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_filter_contrast' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Contrast', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 200,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how distinct bright and dark areas should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_filter_invert' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Invert', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '0%',
'default_on_child' => true,
'description' => esc_html__( 'Invert the hue, saturation, and brightness by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_filter_sepia' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Sepia', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '0%',
'default_on_child' => true,
'description' => esc_html__( 'Travel back in time by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_filter_opacity' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Opacity', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 100,
'step' => 1,
),
'default' => '100%',
'default_on_child' => true,
'description' => esc_html__( 'Define how transparent or opaque this should be.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => '%',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_filter_blur' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Blur', 'et_builder' ),
'type' => 'range',
'option_category' => 'layout',
'range_settings' => array(
'min' => 0,
'max' => 50,
'step' => 1,
),
'default' => '0px',
'default_on_child' => true,
'description' => esc_html__( 'Blur by this amount.', 'et_builder' ),
'validate_unit' => true,
'fixed_unit' => 'px',
'fixed_range' => true,
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
'child_mix_blend_mode' => array(
'label' => esc_html__( 'Image', 'et_builder' ) . ' ' . esc_html__( 'Blend Mode', 'et_builder' ),
'type' => 'select',
'option_category' => 'layout',
'default' => 'normal',
'default_on_child' => true,
'description' => esc_html__( 'Modify how this element blends with any layers beneath it. To reset, choose the "Normal" option.' ),
'options' => array(
'normal' => esc_html__( 'Normal', 'et_builder' ),
'multiply' => esc_html__( 'Multiply', 'et_builder' ),
'screen' => esc_html__( 'Screen', 'et_builder' ),
'overlay' => esc_html__( 'Overlay', 'et_builder' ),
'darken' => esc_html__( 'Darken', 'et_builder' ),
'lighten' => esc_html__( 'Lighten', 'et_builder' ),
'color-dodge' => esc_html__( 'Color Dodge', 'et_builder' ),
'color-burn' => esc_html__( 'Color Burn', 'et_builder' ),
'hard-light' => esc_html__( 'Hard Light', 'et_builder' ),
'soft-light' => esc_html__( 'Soft Light', 'et_builder' ),
'difference' => esc_html__( 'Difference', 'et_builder' ),
'exclusion' => esc_html__( 'Exclusion', 'et_builder' ),
'hue' => esc_html__( 'Hue', 'et_builder' ),
'saturation' => esc_html__( 'Saturation', 'et_builder' ),
'color' => esc_html__( 'Color', 'et_builder' ),
'luminosity' => esc_html__( 'Luminosity', 'et_builder' ),
),
'tab_slug' => $child_filter['tab_slug'],
'toggle_slug' => $child_filter['toggle_slug'],
'reset_animation' => false,
'hover' => 'tabs',
),
);
if ( isset( $child_filter['depends_show_if'] ) ) {
foreach ( $additional_child_options as $option => $value ) {
$additional_child_options[ $option ]['depends_show_if'] = $child_filter['depends_show_if'];
}
}
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_child_options );
}
/**
* Add the divider options to the additional_fields_options array.
*/
protected function _add_divider_fields() {
if ( ! $this->has_vb_support() && ! $this->has_advanced_fields ) {
return;
}
// Make sure we only add this to sections.
if ( 'et_pb_section' !== $this->slug ) {
return;
}
$tab_slug = 'advanced';
$toggle_slug = 'dividers';
$divider_toggle = array(
$toggle_slug => array(
'title' => esc_html__( 'Dividers', 'et_builder' ),
'priority' => 65,
),
);
// Add the toggle sections.
$this->_add_settings_modal_toggles( $tab_slug, $divider_toggle );
if ( ! isset( $this->advanced_fields['dividers'] ) ) {
$this->advanced_fields['dividers'] = array();
}
$additional_options = ET_Builder_Module_Fields_Factory::get( 'Divider' )->get_fields( array(
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
) );
// Return our merged options and toggles.
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
/**
* Add additional Text Shadow fields to all modules
*/
protected function _add_text_shadow_fields() {
// Get text shadow settings. Fallback to default if needed
$this->advanced_fields['text_shadow'] = self::$_->array_get( $this->advanced_fields, 'text_shadow', array(
'default' => array(),
) );
// Text shadow settings have to be array
if ( ! is_array( $this->advanced_fields['text_shadow'] ) ) {
return;
}
// Loop test settings, do multiple text shadow field declaration in one palce
foreach ( $this->advanced_fields['text_shadow'] as $text_shadow_name => $text_shadow_fields ) {
// Enable module to disable text shadow. Also disable text shadow if no text group is
// found because default text shadow lives on text group
if ( 'default' === $text_shadow_name && ( false === $text_shadow_fields || empty( $this->settings_modal_toggles['advanced']['toggles']['text'] ) ) ) {
return;
}
if ( 'default' !== $text_shadow_name ) {
// Automatically add prefix and toggle slug
$text_shadow_fields['prefix'] = $text_shadow_name;
$text_shadow_fields['toggle_slug'] = $text_shadow_name;
}
// Add text shadow fields
$this->_additional_fields_options = array_merge(
$this->_additional_fields_options,
$this->text_shadow->get_fields( $text_shadow_fields )
);
}
}
/**
* Add box shadow fields based on configuration on $this->advanced_fields['box_shadow']
*
* @since 3.1
*/
protected function _add_box_shadow_fields() {
// Box shadow fields are added by default to all modules
$this->advanced_fields['box_shadow'] = self::$_->array_get( $this->advanced_fields, 'box_shadow', array(
'default' => array(),
) );
// Box shadow settings have to be array
if ( ! is_array( $this->advanced_fields['box_shadow'] ) ) {
return;
}
// Loop box shadow settings
foreach ( $this->advanced_fields['box_shadow'] as $fields_name => $settings ) {
// Enable module to disable box shadow
if ( false === $settings ) {
continue;
}
$is_box_shadow_default = 'default' === $fields_name;
// Add Box Shadow toggle for default Box Shadow fields
if ( $is_box_shadow_default ) {
$this->settings_modal_toggles['advanced']['toggles']['box_shadow'] = array(
'title' => esc_html__( 'Box Shadow', 'et_builder' ),
'priority' => 100,
);
}
// Ensure box settings has minimum settings required
$settings = wp_parse_args( $settings, array(
'option_category' => 'layout',
'tab_slug' => 'advanced',
'toggle_slug' => 'box_shadow',
) );
// Automatically add suffix attribute
$settings['suffix'] = $is_box_shadow_default ? '' : "_{$fields_name}";
// Add default Box Shadow fields
$this->_additional_fields_options = array_merge(
$this->_additional_fields_options,
ET_Builder_Module_Fields_Factory::get( 'BoxShadow' )->get_fields( $settings )
);
}
}
public function get_transition_box_shadow_fields_css_props( $module = 'default' ) {
/**
* @var ET_Builder_Module_Field_BoxShadow $box_shadow
*/
$box_shadow = ET_Builder_Module_Fields_Factory::get( 'BoxShadow' );
$selector = self::$_->array_get( $this->advanced_fields, "box_shadow.$module.css.main", '%%order_class%%' );
$overlay = self::$_->array_get( $this->advanced_fields, "box_shadow.$module.css.overlay", false );
$suffix = 'default' == $module ? '' : "_$module";
if ( in_array( $overlay, array( 'inset', 'always' ) ) ) {
$selector .= ', ' . $box_shadow->get_overlay_selector( $selector );
}
return array(
"box_shadow_horizontal{$suffix}" => array( 'box-shadow' => $selector, ),
"box_shadow_vertical{$suffix}" => array( 'box-shadow' => $selector, ),
"box_shadow_blur{$suffix}" => array( 'box-shadow' => $selector, ),
"box_shadow_spread{$suffix}" => array( 'box-shadow' => $selector, ),
"box_shadow_color{$suffix}" => array( 'box-shadow' => $selector, ),
);
}
public function get_transition_text_shadow_fields_css_props( $module = null ) {
$source = null == $module ? 'text.css' : "fonts.$module.css";
$default = self::$_->array_get( $this->advanced_fields, "$source.main", '%%order_class%%' );
$selector = self::$_->array_get( $this->advanced_fields, "$source.text_shadow", $default );
$prefix = null == $module ? '' : "{$module}_";
return array(
"{$prefix}text_shadow_horizontal_length" => array( 'text-shadow' => $selector, ),
"{$prefix}text_shadow_vertical_length" => array( 'text-shadow' => $selector, ),
"{$prefix}text_shadow_blur_strength" => array( 'text-shadow' => $selector, ),
"{$prefix}text_shadow_color" => array( 'text-shadow' => $selector, ),
);
}
public function get_transition_filters_fields_css_props( $module = null ) {
$slug = empty( $module ) ? 'filter' : 'child_filter';
$source = empty( $module ) ? 'filters.css.main' : "$module.css.main";
$filters = array( 'hue_rotate', 'saturate', 'brightness', 'contrast', 'invert', 'sepia', 'opacity', 'blur', );
$fields = array();
$main = self::$_->array_get( $this->advanced_fields, $source, '%%order_class%%' );
$selector = $module ? self::$_->array_get( $this->advanced_fields, 'filters.child_filters_target.css.main', $main ) : $main;
foreach ( $filters as $filter ) {
$fields[ "{$slug}_{$filter}" ] = array( 'filter' => $selector, );
}
return $fields;
}
public function get_transition_borders_fields_css_props( $module = 'default' ) {
$suffix = 'default' == $module ? '' : "_$module";
$radius = self::$_->array_get( $this->advanced_fields, "borders.$module.css.main.border_radii", '%%order_class%%' );
$style = self::$_->array_get( $this->advanced_fields, "borders.$module.css.main.border_styles", '%%order_class%%' );
return array(
"border_radii{$suffix}" => array( 'border-radius' => implode( ', ', array( $radius, ) ) ),
"border_width_all{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_color_all{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_width_top{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_color_top{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_width_right{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_color_right{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_width_bottom{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_color_bottom{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_width_left{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
"border_color_left{$suffix}" => array( 'border' => implode( ', ', array( $style, ) ), ),
);
}
public function get_transition_margin_padding_fields_css_props( $module = null ) {
$key = empty($module) ? '' : "$module.";
$suffix = empty($module) ? '' : "_$module";
$margin = self::$_->array_get( $this->advanced_fields, "margin_padding.{$key}css.margin", '%%order_class%%' );
$padding = self::$_->array_get( $this->advanced_fields, "margin_padding.{$key}css.padding", '%%order_class%%' );
return array(
"custom_margin{$suffix}" => array( 'margin' => implode( ', ', array( $margin, ) ) ),
"custom_padding{$suffix}" => array( 'padding' => implode( ', ', array( $padding, ) ), ),
);
}
public function get_transition_font_fields_css_props() {
$items = self::$_->array_get( $this->advanced_fields, 'fonts' );
if ( !is_array( $items ) ) {
return array();
}
$font_options = array(
array( 'option' => 'text_color', 'slug' => 'color', 'prop' => 'color', ),
array( 'option' => 'font_size', 'slug' => 'font_size', 'prop' => 'font-size', ),
array( 'option' => 'line_height', 'slug' => 'line_height', 'prop' => 'line-height', ),
array( 'option' => 'letter_spacing', 'slug' => 'letter_spacing', 'prop' => 'letter-spacing', ),
array( 'option' => 'text_shadow_horizontal_length', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ),
array( 'option' => 'text_shadow_vertical_length', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ),
array( 'option' => 'text_shadow_blur_strength', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ),
array( 'option' => 'text_shadow_color', 'slug' => 'text_shadow', 'prop' => 'text-shadow', ),
);
$fields = array();
foreach ( $items as $item => $field ) {
foreach ( $font_options as $key ) {
$fields["{$item}_{$key['option']}"] = array(
$key['prop'] => self::$_->array_get(
$field,
"css.{$key['slug']}",
self::$_->array_get( $field, 'css.main', '%%order_class%%' )
)
);
}
}
return $fields;
}
public function get_transition_image_fields_css_props() {
$fields = array();
$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 );
return $fields;
}
public function get_transition_button_fields_css_props() {
$buttons = self::$_->array_get( $this->advanced_fields, 'button', array() );
$fields = array();
if ( empty( $buttons ) ) {
return array();
}
foreach ( $buttons as $key => $button ) {
$selector = self::$_->array_get( $button, 'css.main', '%%order_class%%' );
$field = array(
"{$key}_text_color" => array( 'color' => $selector, ),
"{$key}_text_size" => array(
'font-size' => $selector,
'line-height' => $selector,
'padding' => $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( ', ',
array(
$selector,
$this->add_suffix_to_selectors( ' > .box-shadow-overlay', $selector ),
) ),
),
);
$fields = array_merge( $fields, $field );
}
return $fields;
}
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
return array(
'gutter_width' => array(
'width' => $gutter_selector,
'margin' => $gutter_selector,
'padding' => $gutter_selector,
)
);
}
public function get_transition_fields_css_props() {
$default = $this->main_css_element;
$text_main = self::$_->array_get( $this->advanced_fields, 'text.css.main', $default );
$fields = array(
'background_layout' => array( 'color' => $text_main, ),
'background' => array(
'background-color' => self::$_->array_get( $this->advanced_fields,
'background.css.main',
$default ),
),
'max_width' => array( 'max-width' => $default, ),
'text_color' => array(
'color' => self::$_->array_get( $this->advanced_fields,
'text.css.color',
$text_main ),
),
);
$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_font_fields_css_props(), $fields );
$fields = array_merge( $this->get_transition_gutter_fields_css_props(), $fields );
return apply_filters( 'et_builder_hover_transitions_map', $fields );
}
/**
* Add link options fields to all modules
*
* @since 3.15.1
*/
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
return;
}
// Link options settings have to be array
if ( ! is_array( self::$_->array_get( $this->advanced_fields, 'link_options' ) ) ) {
return;
}
$this->settings_modal_toggles['general']['toggles']['link_options'] = array(
'title' => esc_html__( 'Link', 'et_builder' ),
'priority' => 70,
);
$additional_options = array();
// 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
switch ( $this->slug ) {
case 'et_pb_section':
$url_label = esc_html__( 'Section Link URL', 'et_builder' );
$target_label = esc_html__( 'Section Link Target', 'et_builder' );
break;
case 'et_pb_row':
case 'et_pb_row_inner':
$url_label = esc_html__( 'Row Link URL', 'et_builder' );
$target_label = esc_html__( 'Row Link Target', 'et_builder' );
break;
default:
$url_label = esc_html__( 'Module Link URL', 'et_builder' );
$target_label = esc_html__( 'Module Link Target', 'et_builder' );
break;
}
$additional_options['link_option_url'] = array(
'label' => $url_label,
'type' => 'text',
'toggle_slug' => 'link_options',
'description' => esc_html__( 'When clicked the module will link to this URL.', 'et_builder' ),
'dynamic_content' => 'url',
);
$additional_options['link_option_url_new_window'] = array(
'label' => $target_label,
'type' => 'select',
'option_category' => 'configuration',
'options' => array(
'off' => esc_html__( 'In The Same Window', 'et_builder' ),
'on' => esc_html__( 'In The New Tab', 'et_builder' ),
),
'toggle_slug' => 'link_options',
'description' => esc_html__( 'Here you can choose whether or not your link opens in a new window', 'et_builder' ),
'default_on_front' => 'off',
);
$this->_additional_fields_options = array_merge( $this->_additional_fields_options, $additional_options );
}
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();
$selectors = array();
$transitions = array();
$hover = et_pb_hover_options();
// We need to loop transitions array so cases of prefixed prop names can also be caught
foreach ( $transitions_map as $prop_name => $css_props ) {
// To build a proper regex "{prefix}" is replaced with ".*?" as it might not always be present
$prop_name_regexed = str_replace( '{slug}', '.*?', $prop_name );
$match_regex = "~^{$prop_name_regexed}{$hover->get_enabled_suffix()}$~";
// Get a list of props matching the pattern above (should never be more than one match actually)
$prop_matches = preg_grep( $match_regex, array_keys( $this->props ) );
if ( empty( $prop_matches ) ) {
continue;
}
// Reset the array keys to make sure the first (and only) match is $prop_matches[0]
$prop_matches = array_values( $prop_matches );
// Continue if somehow {property_name}__hover_enabled is not "on"
if ( 'on' !== $this->props[ $prop_matches[0] ] ) {
continue;
}
// 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 ) ) {
return;
}
// Create a separate transition definition for each needed transition to avoid using "all"
$transition_css = array();
$duration = et_pb_transition_options()->get_duration( $this->props );
$easing = et_pb_transition_options()->get_easing( $this->props );
$delay = et_pb_transition_options()->get_delay( $this->props );
foreach ( array_unique( $transitions ) as $transition ) {
$transition_css[] = "{$transition} {$duration} {$easing} {$delay}";
$transition_css[] = sprintf(
'%1$s %2$s %3$s %4$s',
esc_attr($transition),
esc_attr($duration),
esc_attr($easing),
esc_attr($delay)
);
}
$transition_declaration = 'transition: ' . implode( ', ', $transition_css ) . ';';
self::set_style( $function_name, array(
'selector' => implode( ', ', array_unique( $selectors ) ),
'declaration' => esc_html( $transition_declaration )
) );
}
protected function _add_custom_css_fields() {
if ( isset( $this->custom_css_tab ) && ! $this->custom_css_tab ) {
return;
}
$custom_css_fields_processed = array();
$current_module_unique_class = '.' . $this->slug . '_' . "<%= typeof( module_order ) !== 'undefined' ? module_order : '' %>";
$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(
'before' => array(
'label' => esc_html__( 'Before', 'et_builder' ),
'selector' => ':before',
'no_space_before_selector' => true,
),
'main_element' => array(
'label' => esc_html__( 'Main Element', 'et_builder' ),
),
'after' => array(
'label' => esc_html__( 'After', 'et_builder' ),
'selector' => ':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(
'label' => sprintf(
'%1$s:%2$s%3$s%4$s',
$option['label'],
! $selector_contains_module_class ? $main_css_element_output : '',
! isset( $option['no_space_before_selector'] ) && isset( $option['selector'] ) ? ' ' : '',
$selector_output
),
'type' => 'custom_css',
'tab_slug' => 'custom_css',
'toggle_slug' => 'custom_css',
'no_colon' => true,
);
// update toggle slug for $this->custom_css_fields
$this->custom_css_fields[ $slug ]['toggle_slug'] = 'custom_css';
// 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 );
}
$default_custom_css_toggles = array(
'classes' => esc_html__( 'CSS ID & Classes', 'et_builder' ),
'custom_css' => esc_html__( 'Custom CSS', 'et_builder' ),
);
$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 );
}
private function _get_fields() {
$this->fields = array();
$this->fields = $this->fields_unprocessed;
$this->fields = $this->process_fields( $this->fields );
$this->fields = apply_filters( 'et_builder_module_fields_' . $this->slug, $this->fields );
foreach ( $this->fields as $field_name => $field ) {
$this->fields[ $field_name ] = apply_filters('et_builder_module_fields_' . $this->slug . '_field_' . $field_name, $field );
$this->fields[ $field_name ]['name'] = $field_name;
}
return $this->fields;
}
/**
* Checks if the field value equals its default value
*
* @param string $name Field name.
* @param mixed $value Field value.
*
* @return bool
*/
protected function _is_field_default( $name, $value ) {
if ( ! isset( $this->fields_unprocessed[ $name ] ) ) {
// field does not exist
return false;
}
$field = $this->fields_unprocessed[ $name ];
$default = self::$_->array_get( $field, 'default', '' );
$default_on_front = self::$_->array_get( $field, 'default_on_front', 'not_found' );
if ( 'not_found' !== $default_on_front ) {
return $default_on_front === $value;
}
if ( is_array( $default ) && ! empty( $default[0] ) && is_array( $default[1] ) ) {
// This is a conditional default. Let's try to resolve it.
list ( $depend_field, $conditional_defaults ) = $default;
$default_key = self::$_->array_get( $this->props, $depend_field, key( $conditional_defaults ) );
$default = self::$_->array_get( $conditional_defaults, $default_key, null );
}
return $default === $value;
}
// intended to be overridden as needed
function process_fields( $fields ) {
return apply_filters( 'et_pb_module_processed_fields', $fields, $this->slug );
}
/**
* Get the settings fields data for this element.
*
* @since 1.0
* @todo Finish documenting return value's structure.
*
* @return array[] {
* Settings Fields
*
* @type mixed[] $setting_field_key {
* Setting Field Data
*
* @type string $type Setting field type.
* @type string $id CSS id for the setting.
* @type string $label Text label for the setting. Translatable.
* @type string $description Description for the settings. Translatable.
* @type string $class Optional. Css class for the settings.
* @type string[] $affects Optional. The keys of all settings that depend on this setting.
* @type string[] $depends_on Optional. The keys of all settings that this setting depends on.
* @type string $depends_show_if Optional. Only show this setting when the settings
* on which it depends has a value equal to this.
* @type string $depends_show_if_not Optional. Only show this setting when the settings
* on which it depends has a value that is not equal to this.
* ...
* }
* ...
* }
*/
function get_fields() { return array(); }
/**
* Get module defined fields + automatically generated fields
*
* @internal Added to make get_fields() lighter. Initially added during BFB's 3rd party support
*
* @return array
*/
function get_complete_fields() {
$fields = $this->get_fields();
$responsive_suffixes = array( 'tablet', 'phone', 'last_edited' );
// Loop fields and modify it if needed
foreach ( $fields as $field_name => $field ) {
// Automatically generate responsive fields
if ( isset( $field['responsive'] ) && $field['responsive'] ) {
// Get tab and toggle slugs value
$tab_slug = isset( $field['tab_slug'] ) ? $field['tab_slug'] : '';
$toggle_slug = isset( $field['toggle_slug'] ) ? $field['toggle_slug'] : '';
foreach ( $responsive_suffixes as $responsive_suffix ) {
$responsive_field_name = "{$field_name}_{$responsive_suffix}";
$fields[ $responsive_field_name ] = array(
'type' => 'skip',
'tab_slug' => $tab_slug,
'toggle_slug' => $toggle_slug,
);
}
}
}
// Add general fields for modules. Don't add them to module item and column
if ( ( ! isset( $this->type ) || 'child' !== $this->type ) && 'et_pb_column' !== $this->slug ) {
$fields = array_merge( $fields, apply_filters( 'et_builder_module_general_fields', array(
'disabled_on' => array(
'label' => esc_html__( 'Disable on', 'et_builder' ),
'type' => 'multiple_checkboxes',
'options' => array(
'phone' => esc_html__( 'Phone', 'et_builder' ),
'tablet' => esc_html__( 'Tablet', 'et_builder' ),
'desktop' => esc_html__( 'Desktop', 'et_builder' ),
),
'additional_att' => 'disable_on',
'option_category' => 'configuration',
'description' => esc_html__( 'This will disable the module on selected devices', 'et_builder' ),
'tab_slug' => 'custom_css',
'toggle_slug' => 'visibility',
),
'admin_label' => array(
'label' => esc_html__( 'Admin Label', 'et_builder' ),
'type' => 'text',
'description' => esc_html__( 'This will change the label of the module in the builder for easy identification.', 'et_builder' ),
'toggle_slug' => 'admin_label',
),
'module_id' => array(
'label' => esc_html__( 'CSS ID', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'tab_slug' => 'custom_css',
'toggle_slug' => 'classes',
'option_class' => 'et_pb_custom_css_regular',
),
'module_class' => array(
'label' => esc_html__( 'CSS Class', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'tab_slug' => 'custom_css',
'toggle_slug' => 'classes',
'option_class' => 'et_pb_custom_css_regular',
),
) ) );
}
return $fields;
}
/**
* Get configuration for module's advanced fields. This method is meant to be overridden in module classes.
*
* @since 3.1
*
* @return array[] {@see self::$advanced_fields}
*/
public function get_advanced_fields_config() {
return $this->advanced_fields;
}
/**
* Get configuration for module's custom css fields. This method is meant to be overridden in module classes.
*
* @since 3.1
*
* @return array[] {@see self::$custom_css_fields}
*/
public function get_custom_css_fields_config() {
return $this->custom_css_fields;
}
/**
* Get custom tabs for the module's settings modal. This method is meant to be overridden in module classes.
*
* @since 3.1
*
* @return array[] {@see self::$settings_modal_tabs}
*/
public function get_settings_modal_tabs() {
return $this->settings_modal_tabs;
}
/**
* Get toggles for the module's settings modal. This method is meant to be overridden in module classes.
*
* @since 3.1
*
* @return array[] {@see self::$settings_modal_toggles}
*/
public function get_settings_modal_toggles() {
return $this->settings_modal_toggles;
}
/**
* Generate column fields.
*
* @param number $column_number number of column
* @param array $base_fields base fields for column
*
* @return array column fields
*/
function get_column_fields( $column_number = 1, $base_fields = array() ) {
$fields = array();
// Loop column's base fields
foreach ( $base_fields as $field_name => $field ) {
// Loop (number of column) times
for ( $index = 1; $index <= $column_number; $index++ ) {
// Some attribute's id is not located at the bottom of the attribute name
if ( isset( $field['has_custom_index_location'] ) && $field['has_custom_index_location'] ) {
$column_name = str_replace( '%column_index%', $index, $field_name );
} else {
$column_name = "{$field_name}_{$index}";
}
$fields[ $column_name ] = array(
'type' => 'skip',
);
// Most column field is an empty-type attribute. Non-empty attribute are likely
// attribute for computed field which needs to have suffix ID
if ( ! empty( $field ) ) {
// Append suffix to the module variable
foreach ( $field as $attr_name => $attr_value ) {
if ( 'has_custom_index_location' === $attr_name ) {
continue;
}
if ( is_array( $attr_value ) && 'computed_callback' !== $attr_name ) {
$attr_value = $this->_append_suffix( $attr_value, $index );
}
$fields[ $column_name ][ $attr_name ] = $attr_value;
}
}
}
}
return $fields;
}
/**
* Append suffix to simple array value
*
* @param array $values array value
* @param string $suffix intended suffix for output's array
*
* @return array suffixed value
*/
function _append_suffix( $values, $suffix ) {
$output = array();
foreach ( $values as $value ) {
$output[] = "{$value}_{$suffix}";
}
return $output;
}
/**
* Returns module style priority.
*
* @return int
*/
function get_style_priority() {
return $this->_style_priority;
}
function get_post_type() {
global $post, $et_builder_post_type;
if ( isset( $_POST['et_post_type'] ) && ! $et_builder_post_type ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
$et_builder_post_type = sanitize_text_field( $_POST['et_post_type'] );
}
if ( is_a( $post, 'WP_POST' ) && ( is_admin() || ! isset( $et_builder_post_type ) ) ) {
return $post->post_type;
} else {
return isset( $et_builder_post_type ) ? $et_builder_post_type : 'post';
}
}
static function optimize_bb_chunk( $content ) {
if ( ! ET_BUILDER_OPTIMIZE_TEMPLATES ) {
return $content;
}
return str_replace( self::$_unique_bb_strip, '', $content );
}
static function get_unique_bb_key( $content ) {
if ( ! ET_BUILDER_OPTIMIZE_TEMPLATES ) {
return $content;
}
$content = self::optimize_bb_chunk( $content );
if ( isset( self::$_unique_bb_keys_map[ $content ] ) ) {
$key = self::$_unique_bb_keys_map[ $content ];
} else {
self::$_unique_bb_keys_values[] = $content;
$key = count( self::$_unique_bb_keys_values ) - 1;
self::$_unique_bb_keys_map[ $content ] = $key;
}
$content = "";
return $content;
}
function wrap_settings_option( $option_output, $field, $name = '' ) {
$depends = false;
$new_depends = isset( $field['show_if'] ) || isset( $field['show_if_not'] );
$depends_attr = '';
if ( isset( $field['depends_show_if'] ) || isset( $field['depends_show_if_not'] ) ) {
$depends = true;
if ( isset( $field['depends_show_if_not'] ) ) {
$depends_show_if_not = is_array( $field['depends_show_if_not'] ) ? implode( ',', $field['depends_show_if_not'] ) : $field['depends_show_if_not'];
$depends_attr = sprintf( ' data-depends_show_if_not="%s"', esc_attr( $depends_show_if_not ) );
} else {
$depends_attr = sprintf( ' data-depends_show_if="%s"', esc_attr( $field['depends_show_if'] ) );
}
}
if ( isset( $field['depends_on_responsive'] ) ) {
$depends_attr .= sprintf( ' data-depends_on_responsive="%s"', esc_attr( implode( ',', $field['depends_on_responsive'] ) ) );
}
// Overriding background color's attribute, turning it into appropriate background attributes
if ( isset( $field['type'] ) && isset( $field['name' ] ) && 'background_color' === $field['name'] && ! self::$_->array_get( $field, 'skip_background_ui' ) ) {
$field['type'] = 'background';
// Removing depends default variable which hides background color for unified background field UI
if ( isset( $field['depends_show_if'] ) ) {
unset( $field['depends_show_if'] );
}
}
$output = sprintf(
'%6$s