version 4.13.0

This commit is contained in:
2021-12-07 11:08:05 +00:00
commit cb26d2c0c4
1285 changed files with 254735 additions and 0 deletions

View File

@ -0,0 +1,14 @@
<?php
/**
* Helper class that provides necessary functions for managing alignment option
*
* Class ET_Builder_Module_Helper_Alignment
*/
class ET_Builder_Module_Helper_Alignment extends ET_Builder_Module_Helper_Sizing {
public function get_raw_field() {
return 'module_alignment';
}
}

View File

@ -0,0 +1,751 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Background helper methods.
* This is abstraction of `ET_Builder_Element->process_advanced_background_options()` method which is
* intended for module that needs to extend module background mechanism with few modification
* (eg. post slider which needs to apply module background on individual slide that has featured
* image).
*
* @since 4.3.3
* @since 4.6.0 Add sticky style support
*
* @todo Use `ET_Builder_Module_Helper_Background->get_background_style()` for `ET_Builder_Element->process_advanced_background_options()`
*
* Class ET_Builder_Module_Helper_Background
*/
class ET_Builder_Module_Helper_Background {
public static function instance() {
static $instance;
return $instance ? $instance : $instance = new self();
}
/**
* Get prop name alias. Some background settings (eg. button's gradient background enable) might
* use slightly different prop name to store background config;
*
* @since 4.6.0
*
* @param array $aliases Aliases.
* @param string $prop_name Prop name.
*
* @return string
*/
public function get_prop_name_alias( $aliases = array(), $prop_name = '' ) {
// If no aliases given, simply return the prop name because it has no alias.
if ( empty( $aliases ) ) {
return $prop_name;
}
return et_()->array_get( $aliases, $prop_name, $prop_name );
}
/**
* Get gradient properties based on given props
*
* @since 4.3.3
*
* @param array $props Module's props
* @param string $base_prop_name Background base prop name
* @param string $suffix Background base prop name's suffix
*
* @return array
*/
function get_gradient_properties( $props, $base_prop_name, $suffix ) {
return array(
'type' => et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_type{$suffix}", '', true ),
'direction' => et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_direction{$suffix}", '', true ),
'radial_direction' => et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_direction_radial{$suffix}", '', true ),
'color_start' => et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_start{$suffix}", '', true ),
'color_end' => et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_end{$suffix}", '', true ),
'start_position' => et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_start_position{$suffix}", '', true ),
'end_position' => et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_end_position{$suffix}", '', true ),
);
}
/**
* Get gradient properties for hover mode
*
* @since 4.3.3
* @since 4.6.0 add capability to look for sticky style's gradient
*
* @param array $props Module's props
* @param string $base_prop_name Background base prop name
* @param array $gradient_properties_desktop {
* @type string $mode
* @type string $type
* @type string $direction
* @type string $radial_direction
* @type string $color_start
* @type string $color_end
* @type string $start_position
* @type string $end_position
* }
*
* @return array
*/
public function get_gradient_mode_properties( $mode, $props, $base_prop_name, $gradient_properties_desktop = array() ) {
$helper = et_builder_get_helper( $mode );
if ( ! $mode ) {
return false;
}
// Desktop value as default.
$gradient_type_desktop = et_()->array_get( $gradient_properties_desktop, 'type', '' );
$gradient_direction_desktop = et_()->array_get( $gradient_properties_desktop, 'direction', '' );
$gradient_radial_direction_desktop = et_()->array_get( $gradient_properties_desktop, 'radial_direction', '' );
$gradient_color_start_desktop = et_()->array_get( $gradient_properties_desktop, 'color_start', '' );
$gradient_color_end_desktop = et_()->array_get( $gradient_properties_desktop, 'color_end', '' );
$gradient_start_position_desktop = et_()->array_get( $gradient_properties_desktop, 'start_position', '' );
$gradient_end_position_desktop = et_()->array_get( $gradient_properties_desktop, 'end_position', '' );
$gradient_overlays_image_desktop = et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_color_gradient_overlays_image", '', true );
// Mode value.
$gradient_type_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_type", $props, $gradient_type_desktop );
$gradient_direction_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_direction", $props, $gradient_direction_desktop );
$gradient_direction_radial_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_direction_radial", $props, $gradient_radial_direction_desktop );
$gradient_start_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_start", $props, $gradient_color_start_desktop );
$gradient_end_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_end", $props, $gradient_color_end_desktop );
$gradient_start_position_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_start_position", $props, $gradient_start_position_desktop );
$gradient_end_position_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_end_position", $props, $gradient_end_position_desktop );
$gradient_overlays_image_mode = $helper->get_raw_value( "{$base_prop_name}_color_gradient_overlays_image", $props, $gradient_overlays_image_desktop );
return array(
'type' => '' !== $gradient_type_mode ? $gradient_type_mode : $gradient_type_desktop,
'direction' => '' !== $gradient_direction_mode ? $gradient_direction_mode : $gradient_direction_desktop,
'radial_direction' => '' !== $gradient_direction_radial_mode ? $gradient_direction_radial_mode : $gradient_radial_direction_desktop,
'color_start' => '' !== $gradient_start_mode ? $gradient_start_mode : $gradient_color_start_desktop,
'color_end' => '' !== $gradient_end_mode ? $gradient_end_mode : $gradient_color_end_desktop,
'start_position' => '' !== $gradient_start_position_mode ? $gradient_start_position_mode : $gradient_start_position_desktop,
'end_position' => '' !== $gradient_end_position_mode ? $gradient_end_position_mode : $gradient_end_position_desktop,
);
}
/**
* Get background gradient style based on properties given
*
* @since 4.3.3
*
* @param array $args {
* @type string $type
* @type string $direction
* @type string $radial_direction
* @type string $color_start
* @type string $color_end
* @type string $start_position
* @type string $end_position
* }
*
* @return string
*/
function get_gradient_style( $args ) {
$default_gradient = array(
'type' => ET_Global_Settings::get_value( 'all_background_gradient_type' ),
'direction' => ET_Global_Settings::get_value( 'all_background_gradient_direction' ),
'radial_direction' => ET_Global_Settings::get_value( 'all_background_gradient_direction_radial' ),
'color_start' => ET_Global_Settings::get_value( 'all_background_gradient_start' ),
'color_end' => ET_Global_Settings::get_value( 'all_background_gradient_end' ),
'start_position' => ET_Global_Settings::get_value( 'all_background_gradient_start_position' ),
'end_position' => ET_Global_Settings::get_value( 'all_background_gradient_end_position' ),
);
$defaults = apply_filters( 'et_pb_default_gradient', $default_gradient );
$args = wp_parse_args( array_filter( $args ), $defaults );
$direction = 'linear' === $args['type'] ? $args['direction'] : "circle at {$args['radial_direction']}";
$start_position = et_sanitize_input_unit( $args['start_position'], false, '%' );
$end_position = et_sanitize_input_unit( $args['end_position'], false, '%' );
return esc_html(
"{$args['type']}-gradient(
{$direction},
{$args['color_start']} ${start_position},
{$args['color_end']} ${end_position}
)"
);
}
/**
* Get individual background image style
*
* @since 4.3.3
*
* @param string $attr Background attribute name
* @param string $base_prop_name Base background prop name
* @param string $suffix Attribute name suffix
* @param array $props Module props
* @param array $fields_definition Module's fields definition
* @param bool $is_prev_image_active Whether previous background image is active or not
*
* @return string
*/
function get_image_style( $attr, $base_prop_name, $suffix = '', $props = array(), $fields_definition = array(), $is_prev_image_active = true ) {
// Get default style
$default = et_()->array_get( $fields_definition, "{$base_prop_name}_{$attr}.default", '' );
// Get style
$style = et_pb_responsive_options()->get_any_value( $props, "{$base_prop_name}_{$attr}{$suffix}", $default, ! $is_prev_image_active );
return $style;
}
/**
* Get background UI option's style based on given props and prop name
*
* @since 4.3.3
* @since 4.6.0 Add sticky style support.
*
* @todo Further simplify this method; Break it down into more encapsulated methods
*
* @param array $args {
* @type string $base_prop_name
* @type array $props
* @type string $important
* @type array $fields_Definition
* @type string $selector
* @type string $selector_hover
* @type string $selector_sticky
* @type number $priority
* @type string $function_name
* @type bool $has_background_color_toggle
* @type bool $use_background_color
* @type bool $use_background_color_gradient
* @type bool $use_background_image
* @type bool $use_background_video
* @type bool $use_background_color_reset
* @type bool $use_background_image_parallax
* }
*/
function get_background_style( $args = array() ) {
// Default settings
$defaults = array(
'base_prop_name' => 'background',
'props' => array(),
'important' => '',
'fields_definition' => array(),
'selector' => '',
'selector_hover' => '',
'selector_sticky' => '',
'priority' => '',
'function_name' => '',
'has_background_color_toggle' => false,
'use_background_color' => true,
'use_background_color_gradient' => true,
'use_background_image' => true,
'use_background_video' => true,
'use_background_color_reset' => true,
'use_background_image_parallax' => true,
'prop_name_aliases' => array(),
);
// Parse arguments
$args = wp_parse_args( $args, $defaults );
// Break argument into variables
$base_prop_name = $args['base_prop_name'];
$props = $args['props'];
$important = $args['important'];
$fields_definition = $args['fields_definition'];
$selector = $args['selector'];
$priority = $args['priority'];
$function_name = $args['function_name'];
// Possible values for use_background_* variables are true, false, or 'fields_only'
$has_color_toggle_options = $args['has_background_color_toggle'];
$use_gradient_options = $args['use_background_color_gradient'];
$use_image_options = $args['use_background_image'];
$use_color_options = $args['use_background_color'];
$use_color_reset_options = $args['use_background_color_reset'];
// Prop name aliases. Some background element uses different prop name (eg. button background).
$prop_name_aliases = $args['prop_name_aliases'];
// Save processed background. These will be compared with the smaller device background
// processed value to avoid rendering the same styles.
$processed_color = '';
$processed_image = '';
$gradient_properties_desktop = array();
$processed_image_blend = '';
$gradient_overlays_image_desktop = 'off';
// Store background images status because the process is extensive.
$image_status = array(
'desktop' => false,
'tablet' => false,
'phone' => false,
);
// Helper.
$responsive = et_pb_responsive_options();
// Parsed prop name, in case it has aliases.
$base_prop_name_parsed = $this->get_prop_name_alias( $prop_name_aliases, $base_prop_name );
// Background Desktop, Tablet, and Phone.
foreach ( $responsive->get_modes() as $device ) {
$is_desktop = 'desktop' === $device;
$suffix = ! $is_desktop ? "_{$device}" : '';
$style = '';
// Conditionals
$has_gradient = false;
$has_image = false;
$has_gradient_and_image = false;
$is_gradient_disabled = false;
$is_image_disabled = false;
// Ensure responsive settings is enabled on mobile.
if ( ! $is_desktop && ! $responsive->is_responsive_enabled( $props, $base_prop_name_parsed ) ) {
continue;
}
// Styles output
$image_style = '';
$color_style = '';
$images = array();
$gradient_overlays_image = 'off';
// A. Background Gradient.
if ( $use_gradient_options && 'fields_only' !== $use_gradient_options ) {
$use_gradient = $responsive->get_inheritance_background_value(
$props,
$this->get_prop_name_alias( $prop_name_aliases, "use_{$base_prop_name}_color_gradient" ),
$device,
$base_prop_name,
$fields_definition
);
// 1. Ensure gradient color is active.
if ( 'on' === $use_gradient ) {
$gradient_overlays_image = $responsive->get_any_value( $props, "{$base_prop_name}_color_gradient_overlays_image{$suffix}", '', true );
$gradient_properties = $this->get_gradient_properties( $props, $base_prop_name, $suffix );
// Will be used as default of Gradient hover.
if ( $is_desktop ) {
$gradient_properties_desktop = $gradient_properties;
$gradient_overlays_image_desktop = $gradient_overlays_image;
}
// Save background gradient into background images list.
$background_gradient = $this->get_gradient_style( $gradient_properties );
$images[] = $background_gradient;
// Flag to inform Background Color if current module has Gradient.
$has_gradient = true;
} elseif ( 'off' === $use_gradient ) {
$is_gradient_disabled = true;
}
}
// B. Background Image.
if ( $use_image_options && 'fields_only' !== $use_image_options ) {
$image = $responsive->get_inheritance_background_value( $props, "{$base_prop_name}_image", $device, $base_prop_name, $fields_definition );
$parallax = $responsive->get_any_value( $props, "parallax{$suffix}", 'off' );
// Background image and parallax status.
$is_image_active = '' !== $image && 'on' !== $parallax;
$image_status[ $device ] = $is_image_active;
// 1. Ensure image exists and parallax is off.
if ( $is_image_active ) {
// Flag to inform Background Color if current module has Image.
$has_image = true;
// Check previous Background image status. Needed to get the correct value.
$is_prev_image_active = true;
if ( ! $is_desktop ) {
$is_prev_image_active = 'tablet' === $device ?
$image_status['desktop'] :
$image_status['tablet'];
}
// Size.
$image_size = $this->get_image_style( 'size', $base_prop_name, $suffix, $props, $fields_definition, $is_prev_image_active );
if ( '' !== $image_size ) {
$style .= sprintf( 'background-size: %1$s; ', esc_html( $image_size ) );
}
// Position.
$image_position = $this->get_image_style( 'position', $base_prop_name, $suffix, $props, $fields_definition, $is_prev_image_active );
if ( '' !== $image_position ) {
$style .= sprintf(
'background-position: %1$s; ',
esc_html( str_replace( '_', ' ', $image_position ) )
);
}
// Repeat.
$image_repeat = $this->get_image_style( 'repeat', $base_prop_name, $suffix, $props, $fields_definition, $is_prev_image_active );
if ( '' !== $image_repeat ) {
$style .= sprintf( 'background-repeat: %1$s; ', esc_html( $image_repeat ) );
}
// Blend.
$image_blend = $this->get_image_style( 'blend', $base_prop_name, $suffix, $props, $fields_definition, $is_prev_image_active );
$image_blend_inherit = $responsive->get_any_value( $props, "{$base_prop_name}_blend{$suffix}", '', true );
$image_blend_default = et_()->array_get( $fields_definition, "{$base_prop_name}_blend.default", '' );
if ( '' !== $image_blend_inherit ) {
// Don't print the same image blend style.
if ( '' !== $image_blend ) {
$style .= sprintf( 'background-blend-mode: %1$s; ', esc_html( $image_blend ) );
}
// Reset - If background has image and gradient, force background-color: initial.
if ( $has_gradient && $has_image && $use_color_reset_options !== 'fields_only' && $image_blend_inherit !== $image_blend_default ) {
$has_gradient_and_image = true;
$color_style = 'initial';
$style .= sprintf( 'background-color: initial%1$s; ', esc_html( $important ) );
}
$processed_image_blend = $image_blend;
}
// Only append background image when the image is exist.
$images[] = sprintf( 'url(%1$s)', esc_html( $image ) );
} elseif ( '' === $image ) {
// Reset - If background image is disabled, ensure we reset prev background blend mode.
if ( '' !== $processed_image_blend ) {
$style .= 'background-blend-mode: normal; ';
$processed_image_blend = '';
}
$is_image_disabled = true;
}
}
if ( ! empty( $images ) ) {
// The browsers stack the images in the opposite order to what you'd expect.
if ( 'on' !== $gradient_overlays_image ) {
$images = array_reverse( $images );
}
// Set background image styles only it's different compared to the larger device.
$image_style = join( ', ', $images );
if ( $processed_image !== $image_style ) {
$style .= sprintf(
'background-image: %1$s%2$s;',
esc_html( $image_style ),
$important
);
}
} elseif ( ! $is_desktop && $is_gradient_disabled && $is_image_disabled ) {
// Reset - If background image and gradient are disabled, reset current background image.
$image_style = 'initial';
$style .= sprintf(
'background-image: %1$s%2$s;',
esc_html( $image_style ),
$important
);
}
// Save processed background images.
$processed_image = $image_style;
// C. Background Color.
if ( $use_color_options && 'fields_only' !== $use_color_options ) {
$use_color_value = $responsive->get_any_value( $props, "use_{$base_prop_name}_color{$suffix}", 'on', true );
if ( ! $has_gradient_and_image && 'off' !== $use_color_value ) {
$color = $responsive->get_inheritance_background_value( $props, "{$base_prop_name}_color", $device, $base_prop_name, $fields_definition );
$color = ! $is_desktop && '' === $color ? 'initial' : $color;
$color_style = $color;
if ( '' !== $color && $processed_color !== $color ) {
$style .= sprintf(
'background-color: %1$s%2$s; ',
esc_html( $color ),
esc_html( $important )
);
}
} elseif ( $has_color_toggle_options && 'off' === $use_color_value && ! $is_desktop ) {
// Reset - If current module has background color toggle, it's off, and current mode
// it's not desktop, we should reset the background color.
$style .= sprintf(
'background-color: initial %1$s; ',
esc_html( $important )
);
}
}
// Save processed background color.
$processed_color = $color_style;
// Render background styles.
if ( '' !== $style ) {
// Add media query parameter.
$background_args = array();
if ( ! $is_desktop ) {
$current_media_query = 'tablet' === $device ? 'max_width_980' : 'max_width_767';
$background_args['media_query'] = ET_Builder_Element::get_media_query( $current_media_query );
}
$el_style = array(
'selector' => $selector,
'declaration' => rtrim( $style ),
'priority' => $priority,
);
ET_Builder_Element::set_style( $function_name, wp_parse_args( $background_args, $el_style ) );
}
}
// Background Modes (Hover & Sticky).
$modes = array( 'hover', 'sticky' );
foreach ( $modes as $mode ) {
// Get helper.
$helper = et_builder_get_helper( $mode );
// Bail if no helper.
if ( ! $helper ) {
continue;
}
// Get selector.
$selector_mode = $args[ "selector_{$mode}" ];
// If no fixed selector defined, prepend / append default selector.
if ( '' === $selector_mode ) {
if ( 'hover' === $mode ) {
$selector_mode = $helper->add_hover_to_selectors( $selector );
} elseif ( 'sticky' === $mode ) {
$is_sticky_module = $helper->is_sticky_module( $props );
$selector_mode = $helper->add_sticky_to_order_class( $selector, $is_sticky_module );
}
}
// Check if mode is enabled.
if ( $helper->is_enabled( $this->get_prop_name_alias( $prop_name_aliases, $base_prop_name ), $props ) ) {
$images_mode = array();
$style_mode = '';
$has_gradient_mode = false;
$has_image_mode = false;
$has_gradient_and_image_mode = false;
$is_gradient_mode_disabled = false;
$is_image_mode_disabled = false;
$gradient_overlays_image_mode = 'off';
// Background Gradient Mode (Hover / Sticky).
// This part is little bit different compared to responsive implementation. In
// this case, mode is enabled on the background field, not on the each of those
// fields. So, built in function get_value() doesn't work in this case.
// Temporarily, we need to fetch the the value from get_raw_value().
if ( $use_gradient_options && 'fields_only' !== $use_gradient_options ) {
$use_gradient_mode = $responsive->get_inheritance_background_value(
$props,
$this->get_prop_name_alias( $prop_name_aliases, "use_{$base_prop_name}_color_gradient" ),
$mode,
$base_prop_name,
$fields_definition
);
// 1. Ensure gradient color is active and values are not null.
if ( 'on' === $use_gradient_mode ) {
// Flag to inform BG Color if current module has Gradient.
$has_gradient_mode = true;
$gradient_values_mode = $this->get_gradient_mode_properties(
$mode,
$props,
$base_prop_name,
$gradient_properties_desktop
);
$gradient_mode = $this->get_gradient_style( $gradient_values_mode );
$images_mode[] = $gradient_mode;
$gradient_overlays_image_desktop = $responsive->get_any_value(
$props,
"{$base_prop_name}_color_gradient_overlays_image",
'',
true
);
$gradient_overlays_image_mode = $helper->get_raw_value(
"{$base_prop_name}_color_gradient_overlays_image",
$props,
$gradient_overlays_image_desktop
);
} elseif ( 'off' === $use_gradient_mode ) {
$is_gradient_mode_disabled = true;
}
}
// Background Image Mode (Hover / Sticky).
// This part is little bit different compared to responsive implementation. In
// this case, mode is enabled on the background field, not on the each of those
// fields. So, built in function get_value() doesn't work in this case.
// Temporarily, we need to fetch the the value from get_raw_value().
if ( $use_image_options && 'fields_only' !== $use_image_options ) {
$image_mode = $responsive->get_inheritance_background_value(
$props,
"{$base_prop_name}_image",
$mode,
$base_prop_name,
$fields_definition
);
$parallax_mode = $helper->get_raw_value( 'parallax', $props );
if ( '' !== $image_mode && null !== $image_mode && 'on' !== $parallax_mode ) {
// Flag to inform BG Color if current module has Image.
$has_image_mode = true;
// Size.
$image_size_mode = $helper->get_raw_value( "{$base_prop_name}_size", $props );
$image_size_desktop = et_()->array_get( $props, "{$base_prop_name}_size", '' );
$is_same_image_size = $image_size_mode === $image_size_desktop;
if ( empty( $image_size_mode ) && ! empty( $image_size_desktop ) ) {
$image_size_mode = $image_size_desktop;
}
if ( ! empty( $image_size_mode ) && ! $is_same_image_size ) {
$style_mode .= sprintf(
'background-size: %1$s; ',
esc_html( $image_size_mode )
);
}
// Position.
$image_position_mode = $helper->get_raw_value( "{$base_prop_name}_position", $props );
$image_position_desktop = et_()->array_get( $props, "{$base_prop_name}_position", '' );
$is_same_image_position = $image_position_mode === $image_position_desktop;
if ( empty( $image_position_mode ) && ! empty( $image_position_desktop ) ) {
$image_position_mode = $image_position_desktop;
}
if ( ! empty( $image_position_mode ) && ! $is_same_image_position ) {
$style_mode .= sprintf(
'background-position: %1$s; ',
esc_html( str_replace( '_', ' ', $image_position_mode ) )
);
}
// Repeat.
$image_repeat_mode = $helper->get_raw_value( "{$base_prop_name}_repeat", $props );
$image_repeat_desktop = et_()->array_get( $props, "{$base_prop_name}_repeat", '' );
$is_same_image_repeat = $image_repeat_mode === $image_repeat_desktop;
if ( empty( $image_repeat_mode ) && ! empty( $image_repeat_desktop ) ) {
$image_repeat_mode = $image_repeat_desktop;
}
if ( ! empty( $image_repeat_mode ) && ! $is_same_image_repeat ) {
$style_mode .= sprintf(
'background-repeat: %1$s; ',
esc_html( $image_repeat_mode )
);
}
// Blend.
$image_blend_mode = $helper->get_raw_value( "{$base_prop_name}_blend", $props );
$image_blend_default = et_()->array_get( $fields_definition, "{$base_prop_name}_blend.default", '' );
$image_blend_desktop = et_()->array_get( $props, "{$base_prop_name}_blend", '' );
$is_same_image_blend = $image_blend_mode === $image_blend_desktop;
if ( empty( $image_blend_mode ) && ! empty( $image_blend_desktop ) ) {
$image_blend_mode = $image_blend_desktop;
}
if ( ! empty( $image_blend_mode ) ) {
// Don't print the same background blend.
if ( ! $is_same_image_blend ) {
$style_mode .= sprintf(
'background-blend-mode: %1$s; ',
esc_html( $image_blend_mode )
);
}
// Force background-color: initial.
if ( $has_gradient_mode && $has_image_mode && $image_blend_mode !== $image_blend_default ) {
$has_gradient_and_image_mode = true;
$style_mode .= sprintf( 'background-color: initial%1$s; ', esc_html( $important ) );
}
}
// Only append background image when the image is exist.
$images_mode[] = sprintf( 'url(%1$s)', esc_html( $image_mode ) );
} elseif ( '' === $image_mode ) {
$is_image_mode_disabled = true;
}
}
if ( ! empty( $images_mode ) ) {
// The browsers stack the images in the opposite order to what you'd expect.
if ( 'on' !== $gradient_overlays_image_mode ) {
$images_mode = array_reverse( $images_mode );
}
$style_mode .= sprintf(
'background-image: %1$s%2$s;',
esc_html( join( ', ', $images_mode ) ),
$important
);
} elseif ( $is_gradient_mode_disabled && $is_image_mode_disabled ) {
$style_mode .= sprintf(
'background-image: initial %1$s;',
$important
);
}
// Background Color Mode (Hover / Sticky).
if ( $use_color_options && 'fields_only' !== $use_color_options ) {
$use_color_mode_value = $helper->get_raw_value( "use_{$base_prop_name}_color", $props );
$use_color_mode_value = ! empty( $use_color_mode_value ) ?
$use_color_mode_value :
et_()->array_get( $props, "use_{$base_prop_name}_color", 'on' );
if ( ! $has_gradient_and_image_mode && 'off' !== $use_color_mode_value ) {
$color_mode = $responsive->get_inheritance_background_value(
$props,
"{$base_prop_name}_color",
$mode,
$base_prop_name,
$fields_definition
);
$color_mode = '' !== $color_mode ? $color_mode : 'transparent';
if ( '' !== $color_mode ) {
$style_mode .= sprintf(
'background-color: %1$s%2$s; ',
esc_html( $color_mode ),
esc_html( $important )
);
}
} elseif ( $has_color_toggle_options && 'off' === $use_color_mode_value ) {
// Reset - If current module has background color toggle, it's off, and current mode
// it's not desktop, we should reset the background color.
$style .= sprintf(
'background-color: initial %1$s; ',
esc_html( $important )
);
}
}
// Render background mode styles.
if ( '' !== $style_mode ) {
$el_style = array(
'selector' => $selector_mode,
'declaration' => rtrim( $style_mode ),
'priority' => $priority,
);
ET_Builder_Element::set_style( $function_name, $el_style );
}
}
}
}
}

View File

@ -0,0 +1,138 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
// Include dependency for ResponsiveOptions.
if ( ! function_exists( 'et_pb_responsive_options' ) ) {
require_once 'ResponsiveOptions.php';
}
// Include dependency for HoverOptions.
if ( ! function_exists( 'et_pb_hover_options' ) ) {
require_once 'HoverOptions.php';
}
/**
* Background layout helper methods.
*
* @since 4.0.7
*
* Class ET_Builder_Module_Helper_BackgroundLayout
*/
class ET_Builder_Module_Helper_BackgroundLayout {
public static function instance() {
static $instance;
return $instance ? $instance : $instance = new self();
}
/**
* Get background layout class names.
*
* @since 4.0.7
*
* @param array $attrs
* @param boolean $is_skip_desktop Not all modules need to print desktop background layout.
* @param boolean $is_text_color Not all modules need text color layout class name.
*
* @return array
*/
public function get_background_layout_class( $attrs, $is_skip_desktop = false, $is_text_color = false ) {
// Background layout values.
$background_layouts = et_pb_responsive_options()->get_property_values( $attrs, 'background_layout' );
$background_layout = et_()->array_get( $background_layouts, 'desktop', '' );
$background_layout_tablet = et_()->array_get( $background_layouts, 'tablet', '' );
$background_layout_phone = et_()->array_get( $background_layouts, 'phone', '' );
$background_layout_hover = et_pb_hover_options()->get_value( 'background_layout', $attrs, 'light' );
// Background layout class names.
$background_layout_class_names = ! $is_skip_desktop ? array( "et_pb_bg_layout_{$background_layout}" ) : array();
if ( ! empty( $background_layout_tablet ) ) {
$background_layout_class_names[] = "et_pb_bg_layout_{$background_layout_tablet}_tablet";
}
if ( ! empty( $background_layout_phone ) ) {
$background_layout_class_names[] = "et_pb_bg_layout_{$background_layout_phone}_phone";
}
// Text color class names.
if ( $is_text_color ) {
if ( 'light' === $background_layout ) {
$background_layout_class_names[] = 'et_pb_text_color_dark';
}
if ( 'light' === $background_layout_tablet ) {
$background_layout_class_names[] = 'et_pb_text_color_dark_tablet';
}
if ( 'light' === $background_layout_phone ) {
$background_layout_class_names[] = 'et_pb_text_color_dark_phone';
}
}
return $background_layout_class_names;
}
/**
* Get background layout data attributes.
*
* @since 4.0.7
*
* @param array $attrs
*
* @return string
*/
public function get_background_layout_attrs( $attrs ) {
// Background layout data attributes is only needed by hover or sticky effect.
if ( ! et_pb_hover_options()->is_enabled( 'background_layout', $attrs ) && ! et_pb_sticky_options()->is_enabled( 'background_layout', $attrs ) ) {
return '';
}
// Background layout values.
$background_layouts = et_pb_responsive_options()->get_property_values( $attrs, 'background_layout' );
$background_layout = et_()->array_get( $background_layouts, 'desktop', '' );
$background_layout_tablet = et_()->array_get( $background_layouts, 'tablet', '' );
$background_layout_phone = et_()->array_get( $background_layouts, 'phone', '' );
$background_layout_hover = et_pb_hover_options()->get_value( 'background_layout', $attrs, '' );
$background_layout_sticky = et_pb_sticky_options()->get_value( 'background_layout', $attrs, '' );
$data_background_layout = sprintf(
' data-background-layout="%1$s"',
esc_attr( $background_layout )
);
if ( ! empty( $background_layout_hover ) ) {
$data_background_layout .= sprintf(
' data-background-layout-hover="%1$s"',
esc_attr( $background_layout_hover )
);
}
if ( ! empty( $background_layout_sticky ) ) {
$data_background_layout .= sprintf(
' data-background-layout-sticky="%1$s"',
esc_attr( $background_layout_sticky )
);
}
if ( ! empty( $background_layout_tablet ) ) {
$data_background_layout .= sprintf(
' data-background-layout-tablet="%1$s"',
esc_attr( $background_layout_tablet )
);
}
if ( ! empty( $background_layout_phone ) ) {
$data_background_layout .= sprintf(
' data-background-layout-phone="%1$s"',
esc_attr( $background_layout_phone )
);
}
return $data_background_layout;
}
}

View File

@ -0,0 +1,158 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
// Include dependency for ResponsiveOptions.
if ( ! function_exists( 'et_pb_responsive_options' ) ) {
require_once 'ResponsiveOptions.php';
}
/**
* Font helper methods.
*
* @since 4.0
*
* Class ET_Builder_Module_Helper_Font
*/
class ET_Builder_Module_Helper_Font {
public static function instance() {
static $instance;
return $instance ? $instance : $instance = new self();
}
/**
* Check if current font is Default or not.
*
* @since 4.0
*
* @param array $attrs
* @param string $name
* @param string $device
*
* @return boolean
*/
public function is_font_default( $attrs, $name, $device = 'desktop' ) {
return 'Default' === $this->get_font_value( $attrs, $name, $device );
}
/**
* Check if current font is empty or not.
*
* @since 4.0
*
* @param array $attrs
* @param string $name
* @param string $device
*
* @return boolean
*/
public function is_font_empty( $attrs, $name, $device = 'desktop' ) {
return '' === $this->get_font_value( $attrs, $name, $device );
}
/**
* Get font value based on device.
*
* @since 4.0
*
* @param array $attrs
* @param string $name
* @param string $device
*
* @return string
*/
public function get_font_value( $attrs, $name, $device = 'desktop' ) {
$value = et_pb_responsive_options()->get_property_value( $attrs, $name, '', $device, true );
$value_pieces = ! empty( $value ) && is_string( $value ) ? explode( '|', $value ) : array();
return et_()->array_get( $value_pieces, 0, '' );
}
/**
* Get custom breakpoint by font value.
*
* There is a case where tablet and phone use Default font. Default font means the element will
* use the original or font defined on Theme Customizer. It's different with empty string which
* means the font will be inherited from the larger device. So, when current device use non
* default font, we should check smaller device uses default font or not. If the smaller device
* use default font, we have to render current font inclusidely on current device, something
* likes desktop_only, tablet_only, or desktop_tablet_only.
*
* @since 4.0
*
* @param array $attrs
* @param string $name
* @param string $device
* @param string $default_breakpoint
*
* @return string
*/
public function get_breakpoint_by_font_value( $attrs, $name, $device = 'desktop', $default_breakpoint = '' ) {
// Bail early if current $device value is default or empty.
if ( $this->is_font_default( $attrs, $name, $device ) || $this->is_font_empty( $attrs, $name, $device ) ) {
return $default_breakpoint;
}
// Phone - There is no smaller $device than phone, no need to check.
if ( 'phone' === $device ) {
return $default_breakpoint;
}
$is_phone_default = $this->is_font_default( $attrs, $name, 'phone' );
$is_tablet_default = $this->is_font_default( $attrs, $name, 'tablet' );
// Tablet.
if ( 'tablet' === $device ) {
// Return breakpoint for tablet only if phone uses default, otherwise return default.
return $is_phone_default ? et_pb_responsive_options()->get_breakpoint_by_device( 'tablet_only' ) : $default_breakpoint;
}
// Desktop.
if ( $is_tablet_default ) {
// Return breakpoint for desktop only if tablet uses default.
return et_pb_responsive_options()->get_breakpoint_by_device( 'desktop_only' );
} elseif ( $is_phone_default ) {
// Return breakpoint for desktop & only if tablet uses default.
return et_pb_responsive_options()->get_breakpoint_by_device( 'desktop_tablet_only' );
}
return $default_breakpoint;
}
/**
* Get font selector based on settings.
*
* @since 4.0
*
* @param array $option_settings
* @param string $main_css_element
*
* @return string
*/
public function get_font_selector( $option_settings, $main_css_element ) {
// Get main CSS selector.
$main_selector = et_()->array_get( $option_settings, 'css.main', $main_css_element );
$limited_main_selector = et_()->array_get( $option_settings, 'css.limited_main', '' );
$font_selector = et_()->array_get( $option_settings, 'css.font', '' );
// Use different selector for plugin if defined.
if ( et_builder_has_limitation( 'use_limited_main' ) && ! empty( $limited_main_selector ) ) {
$main_selector = $limited_main_selector;
}
// Use font selector if it's specified
if ( ! empty( $font_selector ) ) {
$main_selector = $font_selector;
}
// Join all the main selectors if it's an array.
if ( is_array( $main_selector ) ) {
$main_selector = implode( ', ', $main_selector );
}
return $main_selector;
}
}

View File

@ -0,0 +1,13 @@
<?php
/**
* Helper class that provides necessary functions for managing height option
*
* Class ET_Builder_Module_Helper_Height
*/
class ET_Builder_Module_Helper_Height extends ET_Builder_Module_Helper_Sizing {
public function get_raw_field() {
return 'height';
}
}

View File

@ -0,0 +1,217 @@
<?php if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Hover Options helper methods
*
* Class ET_Builder_Module_Hover_Options
*/
class ET_Builder_Module_Helper_Hover_Options {
private static $instance;
public static function get() {
if ( empty( self::$instance ) ) {
self::$instance = new ET_Builder_Module_Helper_Hover_Options();
}
return self::$instance;
}
private function util_get( $key, $list, $default = null ) {
return ET_Core_Data_Utils::instance()->array_get( $list, $key, $default );
}
/**
* Returns `__hover`
*
* @return string
*/
public function get_suffix() {
return '__hover';
}
/**
* Return `__hover_enabled`
*
* @return string
*/
public function get_enabled_suffix() {
return '__hover_enabled';
}
/**
* Returns the field original name by removing the `__hover` or `__hover_enabled` suffix if it exists.
*
* @param string $name
*
* @return string
*/
public function get_field_base_name( $name ) {
// Do not use rtim as it removes by character not by string
// So cases like `key__hoveree` will be reduced to `key`
$regex = "/(.*)({$this->get_suffix()}|{$this->get_enabled_suffix()})$/";
$replace = '${1}';
return preg_replace( $regex, $replace, $name );
}
/**
* Check if the setting has enabled hover options
*
* @param string $setting
* @param array $attrs
*
* @return bool
*/
public function is_enabled( $setting, $attrs ) {
$name = in_array( $setting, [ 'background_color', 'background_image' ], true ) ? 'background' : $setting;
$field = $this->get_hover_enabled_field( $name );
$value = ! empty( $attrs[ $field ] ) ? $attrs[ $field ] : '';
$result = ! empty( $value ) && strpos( $value, 'on' ) === 0;
return $result;
}
/**
* Check if hover settings are enabled on one of the options list.
*
* @since 4.5.1
*
* @param array $attrs All module attributes.
* @param array $list Options list.
* @return boolean Hover settings status.
*/
public function is_any_hover_enabled( $attrs, $list ) {
// Ensure list is not empty and valid array.
if ( empty( $list ) || ! is_array( $list ) ) {
return false;
}
// Check the hover status one by one.
$is_any_hover_enabled = false;
foreach ( $list as $name ) {
if ( $this->is_enabled( $name, $attrs ) ) {
$is_any_hover_enabled = true;
break;
}
}
return $is_any_hover_enabled;
}
/**
* Returns the hover setting field name
* E.g.: get_hover_enabled_field('test') => 'test__hover'
*
* @param string $setting
*
* @return string
*/
public function get_hover_field( $setting ) {
return "{$this->get_field_base_name($setting)}{$this->get_suffix()}";
}
/**
* Returns the hover enabled setting field name
* E.g.: get_hover_enabled_field('test') => 'test__hover_enabled'
*
* @param string $setting
*
* @return string
*/
public function get_hover_enabled_field( $setting ) {
return "{$this->get_field_base_name($setting)}{$this->get_enabled_suffix()}";
}
/**
* Returns setting hover value if hover is enabled;
* If it does not exist, return $default specified value
*
* @param string $setting
* @param array $attrs
* @param mixed $default
*
* @return mixed
*/
public function get_value( $setting, $attrs, $default = null ) {
return $this->is_enabled( $setting, $attrs )
? $this->get_raw_value( $setting, $attrs, $default )
: $default;
}
/**
* Returns setting hover value if hover is enabled for a compose option;
* If it does not exist, return $default specified value
*
* @param string $setting
* @param string $option
* @param array $attrs
* @param mixed $default
*
* @return mixed
*/
public function get_compose_value( $setting, $option, $attrs, $default = null ) {
return $this->is_enabled( $option, $attrs )
? $this->get_raw_value( $setting, $attrs, $default )
: $default;
}
/**
* Returns setting hover value;
* If it does not exist, return $default specified value
*
* @param string $setting
* @param array $attrs
* @param mixed $default
*
* @return mixed
*/
public function get_raw_value( $setting, $attrs, $default = null ) {
return $this->util_get( $this->get_hover_field( $setting ), $attrs, $default );
}
/**
* Adds `:hover` in selector at the end of the selector
* E.g: add_hover_to_selectors('%%order_class%%% .image') >>> '%%order_class%%% .image:hover'
*
* @since 4.6.0 moved the order of `-` in capturing group 4's character set so it captures
* `::-` prefixed pseudo selector like `::-moz-placeholder` correctly
*
* @param string $selector
*
* @return string
*/
public function add_hover_to_selectors( $selector ) {
$selectors = explode( ',', $selector );
$selectors = array_map( 'trim', $selectors );
// Add hover to the end of the selector, but prevent specific situations like this:
// .my-class:after => .my-class:after:hover, should be .my-class:hover:after
$selectors = preg_replace( '/(.+\s)*([^\::?]+)((::?[-|a-z|\(|\)|\[|\]]+)+)?$/i', '$1$2:hover$3', $selectors );
return implode( ', ', $selectors );
}
/**
* Adds `:hover` in selector after `%%order_class%%`
* E.g: add_hover_to_order_class('%%order_class%%% .image') >>> '%%order_class%%%:hover .image'
*
* @param string $selector
*
* @return string
*/
public function add_hover_to_order_class( $selector ) {
$selectors = explode( ',', $selector );
$selectors = array_map( 'trim', $selectors );
// Add hover to the end of the selector, but prevent specific situations like this:
// .my-class:after => .my-class:after:hover, should be .my-class:hover:after
$selectors = preg_replace( '/(.*%%order_class%%[^\s|^:]*)(.*)/i', '$1:hover$2', $selectors );
return implode( ', ', $selectors );
}
}

View File

@ -0,0 +1,14 @@
<?php
/**
* Helper class that provides necessary functions for managing max height option
*
* Class ET_Builder_Module_Helper_Max_Height
*/
class ET_Builder_Module_Helper_Max_Height extends ET_Builder_Module_Helper_Sizing {
public function get_raw_field() {
return 'max_height';
}
}

View File

@ -0,0 +1,14 @@
<?php
/**
* Helper class that provides necessary functions for managing max width option
*
* Class ET_Builder_Module_Helper_Max_Width
*/
class ET_Builder_Module_Helper_Max_Width extends ET_Builder_Module_Helper_Sizing {
public function get_raw_field() {
return 'max_width';
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
* ET_Builder_Module_Helper_Media class file.
*
* @class ET_Builder_Module_Helper_Media
* @package Divi\Builder
*/
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Class ET_Builder_Module_Helper_Media.
*
* Module helpers for media (image).
*
* @since 4.6.4
*/
class ET_Builder_Module_Helper_Media {
/**
* Return instance of current class.
*
* @return ET_Builder_Module_Helper_Media
*/
public static function instance() {
static $instance;
return $instance ? $instance : $instance = new self();
}
/**
* Get image attachment class.
*
* - wp-image-{$id}
* Add `wp-image-{$id}` class to let `wp_filter_content_tags()` fill in missing
* height and width attributes on the image. Those attributes are required to add
* loading "lazy" attribute on the image. WP doesn't have specific method to only
* generate this class. It's included in get_image_tag() to generate image tags.
*
* @since 4.6.4
*
* @param array $attrs All module attributes.
* @param string $source_key Key of image source.
* @param integer $attachment_id Attachment ID. Optional.
*
* @return string
*/
public function get_image_attachment_class( $attrs, $source_key, $attachment_id = 0 ) {
$attachment_class = '';
// 1.a. Find attachment ID by URL. Skip if the source key is empty.
if ( ! empty( $source_key ) ) {
$attachment_src = et_()->array_get( $attrs, $source_key, '' );
$attachment_id = et_get_attachment_id_by_url( $attachment_src );
}
// 1.b. Generate attachment ID class.
if ( $attachment_id > 0 ) {
$attachment_class = "wp-image-{$attachment_id}";
}
return $attachment_class;
}
}

View File

@ -0,0 +1,14 @@
<?php
/**
* Helper class that provides necessary functions for managing max height option
*
* Class ET_Builder_Module_Helper_Min_Height
*/
class ET_Builder_Module_Helper_Min_Height extends ET_Builder_Module_Helper_Sizing {
public function get_raw_field() {
return 'min_height';
}
}

View File

@ -0,0 +1,124 @@
<?php
class ET_Builder_Module_Helper_Multi_Value {
private static $instance;
public static function instance() {
return self::$instance ? self::$instance : ( self::$instance = new self() );
}
public function get_delimiter() {
return '|';
}
/**
* Parses an array and transforms it in an valid multi value array
*
* @param array $value
* @param int $elements
*
* @return array
*/
public function parse( array $value, $elements = null ) {
$length = (int) $elements;
if ( ! $elements || count( $value ) === (int) $length ) {
return $value;
}
$new = array();
for ( $i = 0; $i < (int) $length; $i ++ ) {
$new[ $i ] = isset( $value[ $i ] ) && $value[ $i ] !== null ? $value[ $i ] : '';
}
return array_map( 'strval', $new );
}
/**
* Splits the multi value string in to an array of primitive values
* User can provide also the required number of elements that value must have
* In case the array original length will be larger then the required elements number
* the array will be cut from head to tail
* In cas the array length will be shorter, the array tail will be filled with empty strings `''`,
* till array length will match the requested elements number
*
* @param string $value
* @param int $elements
*
* @return array
*/
public function split( $value, $elements = null ) {
return $this->parse( explode( $this->get_delimiter(), $value ), $elements );
}
/**
* Takes an array and converts it to a valid multi value
* Provide the `elements` parameter to get the result string of the necessary length
*
* @param array $value
* @param int $elements
*
* @return string
*/
public function to_value( array $value, $elements = null ) {
return implode( $this->get_delimiter(), $this->parse( $value, $elements ) );
}
/**
* Check if the multi value is not empty.
* A multi value is empty when all sub values are empty strings
*
* @param string $value
*
* @return bool
*/
public function has_value( $value ) {
return trim( implode( '', $this->split( $value ) ) ) !== '';
}
/**
* Merges two multi values in to one.
* If value1 nth element is empty, value2 nth element will be used
*
* @param $value_1
* @param $value_2
* @param int $elements
*
* @return string
*/
public function merge( $value_1, $value_2, $elements = null ) {
$v1 = $this->split( $value_1, $elements );
$v2 = $this->split( $value_2, $elements );
$max = max( count( $v1 ), count( $v2 ) );
$new = array();
for ( $i = 0; $i < $max; $i ++ ) {
$new[ $i ] = ! isset( $v1[ $i ] ) ? $v2[ $i ] : et_builder_get_or( $v1[ $i ], $v2[ $i ] );
}
return $this->to_value( $new, $elements );
}
/**
* Sets a value at specific position in provided multiValue.
*
* @param int $key
* @param string $value
* @param string $motion_value
* @param int $elements
*
* @return string
*/
public function set( $key, $value, $motion_value, $elements = null ) {
$arr = $this->split( $motion_value, $elements );
if ( ! isset( $arr[ $key ] ) ) {
return $motion_value;
}
$arr[ $key ] = $value;
return implode( $this->get_delimiter(), $arr );
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,731 @@
<?php
/**
* Option Templates helper methods.
*
* @since 3.28
*
* Class ET_Builder_Module_Helper_OptionTemplate
*/
class ET_Builder_Module_Helper_OptionTemplate {
private $map = array();
private $templates = array();
private $data = array();
private $cache = array();
private $tab_slug_map = array();
public $template_prefix = '%t';
protected static $_ = null;
public static function instance() {
static $instance;
return $instance ? $instance : $instance = new self();
}
private function __construct() {
self::$_ = ET_Core_Data_Utils::instance();
}
private function uniq( $prefix, $content ) {
$key = md5( $prefix . serialize( $content ) );
if ( isset( $this->map[ $key ] ) ) {
return $this->map[ $key ];
}
return ( $this->map[ $key ] = $this->template_prefix . $key );
}
/**
* Determine whether option template is enabled on current request or not
*
* @since 3.28
*
* @return bool
*/
public function is_enabled() {
// Option template tends to be enabled on most request to speed up performance
$status = true;
// Option template is disabled on:
// 1. AJAX request for fetching classic builder (BB)'s module data. BB data is shipped as
// optimized template markup which is rendered on server then sent as string. Hence
// Option Template's sent-config-rebuild-on-js won't be usable for BB
// 2. BB's editing page. BB edit page scans for field dependency and generates visibility
// setting on `window.et_pb_module_field_dependencies` variable for field depency thus
// actual field should be rendered here instead of templateId
if ( et_builder_is_loading_bb_data() || et_builder_is_bb_page() ) {
$status = false;
}
/**
* Filters option template status
*
* @since 3.28
*
* @param bool $status
*/
return apply_filters( 'et_builder_option_template_is_active', $status );
}
/**
* Determine whether given field name is option template field based on its first two characters
*
* @since 3.28
*
* @return bool
*/
public function is_option_template_field( $field_name = '' ) {
return $this->template_prefix === substr( $field_name, 0, 2 );
}
public function has( $key ) {
return isset( $this->templates[ $key ] );
}
public function add( $key, $template ) {
$fields_template = array_merge( $template, et_pb_responsive_options()->create( $template ) );
// Populate tab_slug of given template because advance fields can be rendered on any tab
foreach ( $fields_template as $field_name => $field ) {
if ( isset( $field['tab_slug'] ) ) {
$tab_slug = '' === $field['tab_slug'] ? 'advanced' : $field['tab_slug'];
if ( ! isset( $this->tab_slug_map[ $tab_slug ] ) ) {
$this->tab_slug_map[ $tab_slug ] = array( $key );
continue;
}
if ( ! in_array( $key, $this->tab_slug_map[ $tab_slug ] ) ) {
$this->tab_slug_map[ $tab_slug ][] = $key;
}
}
}
$this->templates[ $key ] = $fields_template;
}
public function create( $key, $config, $return_template_id = false ) {
$data = array( $key, $config );
$id = $this->uniq( $key, $data );
// Alternative, this will save the values directly in the Module $this->unprocessed_fields
// instead of this Calls $this->data and hence require a simpler logic.
// Theoretically it should require more memory but blackfire begs to differ.
$this->data[ $id ] = $data;
// Return as template id instead of id => key if needed
if ( $return_template_id ) {
return $id;
}
return array( $id => $key );
}
/**
* Create placeholders for template's params
*
* @return string[]
*/
public function placeholders( $config, $idx = 1, $path = array() ) {
$placeholders = array();
foreach ( $config as $key => $value ) {
if ( is_array( $value ) ) {
// Prepend current key as path so placeholder later can correctly fetch correct
// value from template data using dot notation path (both lodash get() or utils's
// array_get() support this).
$path[] = $key;
$value = $this->placeholders( $value, $idx, $path );
} else {
// Prepend dot notation path as prefix if needed
$prefix = empty( $path ) ? '' : implode( '.', $path ) . '.';
$value = "%%{$prefix}{$key}%%";
}
$placeholders[ $key ] = $value;
$idx++;
}
return $placeholders;
}
/**
* Get module's data
*
* @return array[]
*/
public function all() {
return $this->data;
}
/**
* Get templates
*
* @since 3.28
*
* @return array
*/
public function templates() {
return $this->templates;
}
/**
* Set `$this->data` property from external source (ie: static field definition cache).
*
* @since 3.28
*
* @param array $cached_data
*/
public function set_data( $cached_data = array() ) {
$this->data = wp_parse_args(
$cached_data,
$this->data
);
}
/**
* Set `$this->templates` property from external source (ie: static field definition cache).
*
* @since 3.28
*
* @param array $cached_template
*/
public function set_templates( $cached_templates = array() ) {
$this->templates = wp_parse_args(
$cached_templates,
$this->templates
);
}
/**
* Set `$this->tab_slug_map` from external source (ie: static field definition cache).
*
* @since 3.29
*
* @param array $cached_tab_slug_map
*/
public function set_tab_slug_map( $cached_tab_slug_map = array() ) {
$this->tab_slug_map = wp_parse_args(
$cached_tab_slug_map,
$this->tab_slug_map
);
}
/**
* Get template data based on given template id
*
* @since 3.28
*
* @param string $template_id
*
* @return array
*/
public function get_data( $template_id = '' ) {
return isset( $this->data[ $template_id ] ) ? $this->data[ $template_id ] : array();
}
/**
* Get template based on given template type
*
* @since 3.28
*
* @param string $type
*
* @return array
*/
public function get_template( $type = '' ) {
return isset( $this->templates[ $type ] ) ? $this->templates[ $type ] : array();
}
/**
* Get hashed cache key based on params given
*
* @since 3.28
*
* @param mixed $params
*
* @return string
*/
public function get_cache_key( $params ) {
$params = is_string( $params ) ? $params : serialize( $params );
return md5( $params );
}
/**
* Get cached value
* Return null if no cached value found
*
* @since 3.28
*
* @param string $name
* @param string $key
*
* @return mixed
*/
public function get_cache( $name, $key ) {
if (
! empty( $this->cache )
&& ! empty( $this->cache[ $name ] )
&& ! empty( $this->cache[ $name ][ $key ] )
) {
return $this->cache[ $name ][ $key ];
}
return null;
}
/**
* Set value to be cached
*
* @since 3.28
*
* @param string $name
* @param string $key
* @param mixed $value
*
* @return void
*/
public function set_cache( $name, $key, $value ) {
self::$_->array_set( $this->cache, "{$name}.{$key}", $value );
}
/**
* Get placeholder of given template
*
* @since 3.28
*
* @param string $template
*
* @return array|bool
*/
public function get_template_placeholder( $template ) {
// Check for cached result first for faster performance
$cache_name = 'template_placeholder';
$cache_key = $this->get_cache_key( $template );
$cache = $this->get_cache( $cache_name, $cache_key );
if ( ! is_null( $cache ) ) {
return $cache;
}
preg_match( '/(?<=%%).*(?=%%)/', $template, $placeholder );
// Cache result
$this->set_cache( $cache_name, $cache_key, $placeholder );
return $placeholder;
}
/**
* Get tab slug maps
*
* @since 3.29
*
* @return array
*/
public function get_tab_slug_map() {
return $this->tab_slug_map;
}
public function is_template_inside_tab( $tab_name, $template_type ) {
// Template which has `%%tab_slug%%` tab_slug can exist on any tab
if ( in_array( $template_type, self::$_->array_get( $this->tab_slug_map, '%%tab_slug%%', array() ) ) ) {
return true;
}
if ( in_array( $template_type, self::$_->array_get( $this->tab_slug_map, $tab_name, array() ) ) ) {
return true;
}
return false;
}
public function rebuild_string_placeholder( $template, $data = array(), $settings = array() ) {
// Placeholder settings
$default_settings = array(
'suffix' => '',
'remove_suffix_if_empty' => false,
);
$placeholder_settings = wp_parse_args( $settings, $default_settings );
// Check for cached result first for faster performance
$cache_name = 'string_placeholder';
$cache_key = $this->get_cache_key( array( $template, $data, $placeholder_settings ) );
$cache = $this->get_cache( $cache_name, $cache_key );
if ( ! is_null( $cache ) ) {
return $cache;
}
// Get placeholder
$placeholder = is_string( $template ) ? $this->get_template_placeholder( $template ) : false;
// If found, replace placeholder with correct value from data
if ( is_array( $placeholder ) && isset( $placeholder[0] ) ) {
// Get placeholder replacement
$replacement = ! empty( $data[1] ) && ! empty( $data[1][ $placeholder[0] ] ) ? $data[1][ $placeholder[0] ] : '';
// Pass null as empty string; null as attribute affect builder differently.
// Attribute with empty string will be omitted later.
if ( is_null( $replacement ) ) {
$replacement = '';
}
// If placeholder is identical to template, return replacement early. This also
// handles the case where replacement as array type
if ( "%%{$placeholder[0]}%%" === $template ) {
// Cache result
$this->set_cache( $cache_name, $cache_key, $replacement );
return $replacement;
}
// Get placeholder suffix
$has_suffix = '' === $replacement && $placeholder_settings['remove_suffix_if_empty'];
$suffix = $has_suffix ? $placeholder_settings['suffix'] : '';
// Make sure replacement is string before proceed;
if ( is_string( $replacement ) ) {
$rebuilt_string = str_replace( "%%{$placeholder[0]}%%{$suffix}", $replacement, $template );
// Cache result
$this->set_cache( $cache_name, $cache_key, $rebuilt_string );
return $rebuilt_string;
}
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $template );
return $template;
}
public function rebuild_preset_placeholder( $template, $data = array(), $settings = array() ) {
// Check for cached result first for faster performance
$cache_name = 'preset_placeholder';
$cache_key = $this->get_cache_key( array( $template, $data, $settings ) );
$cache = $this->get_cache( $cache_name, $cache_key );
if ( ! is_null( $cache ) ) {
return $cache;
}
$rebuild_attr = array();
foreach ( $template as $preset_attr_key => $preset_attr_value ) {
// Object inside preset array mostly contains fields attribute which its object key
// contains placeholder while its object value contains actual value without placeholder.
if ( is_array( $preset_attr_value ) ) {
$rebuilt_preset_attr_object = array();
foreach ( $preset_attr_value as $name => $value ) {
$object_item_name = $this->rebuild_string_placeholder( $name, $data, $settings );
$rebuilt_preset_attr_object[ $object_item_name ] = $value;
}
$rebuild_attr[ $preset_attr_key ] = $rebuilt_preset_attr_object;
continue;
}
$rebuild_attr[ $preset_attr_key ] = $preset_attr_value;
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $rebuild_attr );
return $rebuild_attr;
}
public function rebuild_composite_structure_placeholder( $template_type, $template, $data = array() ) {
// Check for cached result first for faster performance
$cache_name = 'composite_structure_placeholder';
$cache_key = $this->get_cache_key( array( $template_type, $template, $data ) );
$cache = $this->get_cache( $cache_name, $cache_key );
if ( ! is_null( $cache ) ) {
return $cache;
}
$rebuilt_composite_structure_field = $template;
// Replaces placeholder with actual value on border's nested composite structure fields
if ( 'border' === $template_type ) {
// Reset `controls` attribute output
$rebuilt_composite_structure_field['controls'] = array();
// Loop composite structure's original `controls` from template
foreach ( $template['controls'] as $field_name => $field ) {
$rebuilt_field_name = $this->rebuild_string_placeholder( $field_name, $data );
$rebuilt_field = $field;
// Loop field on composite structure controls
foreach ( $rebuilt_field as $attr_name => $attr_value ) {
$settings = array(
'suffix' => 'label' === $attr_name ? ' ' : '',
'remove_suffix_if_empty' => 'label' === $attr_name,
);
$rebuilt_field[ $attr_name ] = $this->rebuild_string_placeholder( $attr_value, $data, $settings );
}
$rebuilt_composite_structure_field['controls'][ $rebuilt_field_name ] = $rebuilt_field;
}
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $rebuilt_composite_structure_field );
return $rebuilt_composite_structure_field;
}
public function rebuild_field_attr_value( $attr_name, $attr_value, $template_data ) {
// Check for cached result first for faster performance
$cache_name = 'field_attr_value';
$cache_key = $this->get_cache_key( array( $attr_name, $attr_value, $template_data ) );
$cache = $this->get_cache( $cache_name, $cache_key );
if ( ! is_null( $cache ) ) {
return $cache;
}
$template_type = ! empty( $template_data[0] ) ? $template_data[0] : '';
$prefix = ! empty( $template_data[1]['prefix'] ) && ! empty( $template_data[1]['prefix'] ) ? $template_data[1]['prefix'] : '';
// Certain advanced field (ie. Text Shadow) automatically adds underscore
$auto_add_prefix_underscore = isset( $template_data[0] ) && 'text_shadow' === $template_data[0] && '' === $prefix;
// 1. Field attribute value's type is string
if ( is_string( $attr_value ) ) {
$placeholder_has_space_suffix = 'label' === $attr_name && in_array( $template_type, array( 'border', 'text_shadow' ) );
$settings = array(
'suffix' => $placeholder_has_space_suffix ? ' ' : '',
'remove_suffix_if_empty' => $placeholder_has_space_suffix ? true : false,
);
$rebuilt_placeholder = $this->rebuild_string_placeholder( $attr_value, $template_data, $settings );
// Cache result
$this->set_cache( $cache_name, $cache_key, $rebuilt_placeholder );
return $rebuilt_placeholder;
}
// 2. Field attribute value's type is array (sequential)
if ( is_array( $attr_value ) && isset( $attr_value[0] ) ) {
$rebuild_attr_value = array();
foreach ( $attr_value as $array_value ) {
// Array consists of string is most likely used for defining field relationship
// such as `show_if` attribute; Replace prefix and suffix placeholder with
// placeholder replacement also consider that text_shadow advanced field
// automatically adds underscore after prefix so it needs to be adjusted as well
if ( is_string( $array_value ) ) {
$settings = array(
'suffix' => '_',
'remove_suffix_if_empty' => $auto_add_prefix_underscore,
);
$rebuild_attr_value[] = $this->rebuild_string_placeholder( $array_value, $template_data, $settings );
} elseif ( 'presets' === $attr_name ) {
// Handle preset attribute specifically due to how it is structured
$settings = array(
'suffix' => '_',
'remove_suffix_if_empty' => $auto_add_prefix_underscore,
);
$rebuild_attr_value[] = $this->rebuild_preset_placeholder( $array_value, $template_data, $settings );
} else {
// Non string and `presets` attribute less likely contains placeholder
$rebuild_attr_value[] = $array_value;
}
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $rebuild_attr_value );
return $rebuild_attr_value;
}
// 3. Field attribute value's type is array (associative)
if ( is_array( $attr_value ) && ! isset( $attr_value[0] ) ) {
$attr_object = array();
// Loop existing attrValue and populate the rebuilt result on `attrObject`.
foreach ( $attr_value as $item_key => $item_value ) {
$attr_object_key = $this->rebuild_string_placeholder( $item_key, $template_data );
// Replaces placeholder with actual value on border's nested composite structure fields
if ( 'composite_structure' === $attr_name ) {
$item_value = $this->rebuild_composite_structure_placeholder( $template_type, $item_value, $template_data );
}
$attr_object[ $attr_object_key ] = $item_value;
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $attr_object );
return $attr_object;
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $attr_value );
// 4. Unknown attribute value type; directly pass it
return $attr_value;
}
public function rebuild_field_template( $template_id, $parent_template_id = false ) {
// Check for cached result first for faster performance
$cache_name = 'field_template';
$cache_key = $parent_template_id ? "{$template_id}-inherits-{$parent_template_id}" : $template_id;
$cache = $this->get_cache( $cache_name, $cache_key );
if ( ! is_null( $cache ) ) {
return $cache;
}
$fields = array();
$template_data = $this->get_data( $template_id );
// No fields will be found without template data. Return early;
if ( empty( $template_data ) ) {
return $fields;
}
$template_type = ! empty( $template_data[0] ) ? $template_data[0] : '';
$template_settings = ! empty( $template_data[1] ) ? $template_data[1] : array();
$prefix = ! empty( $template_settings['prefix'] ) ? $template_settings['prefix'] : '';
$parent_template_data = false;
// If rebuilt parent template is inside another templateId (ie. Text Shadow inside Font)
// its placeholder is passed for data; The expected structure becomes
// `[templateType, parentTemplateSettings]` instead of `[templateType, templateSettings]`;
// Thus get parent template's settings and use it
if ( $parent_template_id ) {
$parent_template_data = $this->get_data( $parent_template_id );
$parent_template_settings = ! empty( $parent_template_data[1] ) ? $parent_template_data[1] : true;
$template_settings_inherits_from_parant = array();
foreach ( $template_settings as $name => $value ) {
$placeholder = $this->get_template_placeholder( $value );
if ( is_array( $placeholder ) && isset( $placeholder[0] ) ) {
$template_settings_inherits_from_parant[ $name ] = self::$_->array_get(
$parent_template_settings,
$placeholder[0],
$value
);
}
}
$parent_template_data = array(
$template_type,
$template_settings_inherits_from_parant,
);
}
// Get fields template for given template type
$fields_template = $this->get_template( $template_type );
// Loop fields template and replace placeholder with actual value
foreach ( $fields_template as $field_name_template => $field_template ) {
// Certain advanced field (ie. Text Shadow) automatically adds underscore
// Related template type needs to be adjusted
$remove_suffix_if_empty = 'text_shadow' === $template_type && '' === $prefix;
// Replace field attribute name's placeholder
$field_template_data = $parent_template_id ? $parent_template_data : $template_data;
$field_name = $this->rebuild_string_placeholder(
$field_name_template,
$field_template_data,
array(
'remove_suffix_if_empty' => $remove_suffix_if_empty,
// placeholder's suffix, not placeholder named %%suffix%%
'suffix' => '_',
)
);
// Replace field attribute value's placeholder
$field = array();
if ( is_array( $field_template ) ) {
foreach ( $field_template as $attr_name => $attr_value ) {
$rebuilt_attr_value = $this->rebuild_field_attr_value(
$attr_name,
$attr_value,
$field_template_data
);
// Omit attribute with empty value: existance of attribute even with empty
// string value is handled differently in many field (ie, `show_if`)
if ( '' === $rebuilt_attr_value ) {
continue;
}
$field[ $attr_name ] = $rebuilt_attr_value;
}
} else {
$fields = array_merge(
$fields,
$this->rebuild_field_template( $field_name_template, $template_id )
);
}
// `name` attribute is dynamically added based on field's array key
$field['name'] = $field_name;
// Populate rebuilt field
$fields[ $field_name ] = $field;
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $fields );
return $fields;
}
public function rebuild_default_props( $template_id ) {
// Check for cached result first for faster performance
$cache_name = 'default_props';
$cache_key = $template_id;
$cache = $this->get_cache( $cache_name, $cache_key );
if ( ! is_null( $cache ) ) {
return $cache;
}
$default_props = array();
$rebuilt_fields = $this->rebuild_field_template( $template_id );
foreach ( $rebuilt_fields as $field_name => $field ) {
$value = '';
if ( isset( $field['composite_type'], $field['composite_structure'] ) ) {
require_once ET_BUILDER_DIR . 'module/field/attribute/composite/Parser.php';
$composite_atts = ET_Builder_Module_Field_Attribute_Composite_Parser::parse( $field['composite_type'], $field['composite_structure'] );
$default_props = array_merge( $default_props, $composite_atts );
} else {
if ( isset( $field['default_on_front'] ) ) {
$value = $field['default_on_front'];
} elseif ( isset( $field['default'] ) ) {
$value = $field['default'];
}
$default_props[ $field_name ] = $value;
}
}
// Cache result
$this->set_cache( $cache_name, $cache_key, $default_props );
return $default_props;
}
}

View File

@ -0,0 +1,90 @@
<?php if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Overflow helper methods
*
* Class ET_Builder_Module_Helper_Overflow
*/
class ET_Builder_Module_Helper_Overflow {
const OVERFLOW_DEFAULT = '';
const OVERFLOW_VISIBLE = 'visible';
const OVERFLOW_HIDDEN = 'hidden';
const OVERFLOW_SCROLL = 'scroll';
const OVERFLOW_AUTO = 'auto';
private static $instance;
public static function get() {
if ( empty( self::$instance ) ) {
return self::$instance = new self();
}
return self::$instance;
}
/**
* Returns overflow settings X axis field
*
* @param string $prefix
*
* @return string
*/
public function get_field_x( $prefix = '' ) {
return $prefix . 'overflow-x';
}
/**
* Returns overflow settings Y axis field
*
* @param string $prefix
*
* @return string
*/
public function get_field_y( $prefix = '' ) {
return $prefix . 'overflow-y';
}
/**
* Return overflow X axis value
*
* @param array $props
* @param mixed $default
* @param string $prefix
*
* @return string
*/
public function get_value_x( $props, $default = null, $prefix = '' ) {
return et_()->array_get( $props, $this->get_field_x( $prefix ), $default );
}
/**
* Return overflow Y axis value
*
* @param array $props
* @param mixed $default
* @param string $prefix
*
* @return string
*/
public function get_value_y( $props, $default = null, $prefix = '' ) {
return et_()->array_get( $props, $this->get_field_y( $prefix ), $default );
}
/**
* Returns overflow valid values
*
* @return array
*/
public function get_overflow_values() {
return array(
self::OVERFLOW_DEFAULT,
self::OVERFLOW_VISIBLE,
self::OVERFLOW_HIDDEN,
self::OVERFLOW_AUTO,
self::OVERFLOW_SCROLL,
);
}
}

View File

@ -0,0 +1,101 @@
<?php if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Overlay helper methods.
*
* Class ET_Builder_Module_Helper_Overlay
*/
class ET_Builder_Module_Helper_Overlay {
/**
* Get an overlay html tag's attributes.
*
* @since 3.29
*
* @param array $args
*
* @return array
*/
public static function get_attributes( $args ) {
$attributes = array();
if ( ! empty( $args['icon'] ) ) {
$attributes['data-icon'] = et_pb_extended_process_font_icon( $args['icon'] );
}
if ( ! empty( $args['icon_tablet'] ) ) {
$attributes['data-icon-tablet'] = et_pb_extended_process_font_icon( $args['icon_tablet'] );
}
if ( ! empty( $args['icon_phone'] ) ) {
$attributes['data-icon-phone'] = et_pb_extended_process_font_icon( $args['icon_phone'] );
}
if ( ! empty( $args['icon_sticky'] ) ) {
$attributes['data-icon-sticky'] = et_pb_extended_process_font_icon( $args['icon_sticky'] );
}
return $attributes;
}
/**
* Render an overlay html tag's attributes.
*
* @since 3.29
*
* @param array $args
*
* @return string
*/
public static function render_attributes( $args ) {
$attributes = self::get_attributes( $args );
$html = array();
foreach ( $attributes as $attribute => $value ) {
$html[] = sprintf(
'%1$s="%2$s"',
et_core_intentionally_unescaped( $attribute, 'fixed_string' ),
et_core_esc_previously( $value )
);
}
return implode( ' ', $html );
}
/**
* Render an overlay html tag.
*
* @since 3.29
*
* @param array $args
*
* @return string
*/
public static function render( $args ) {
$attributes = et_core_esc_previously( self::render_attributes( $args ) );
$classes = array( 'et_overlay' );
if ( ! empty( $args['icon'] ) ) {
$classes[] = 'et_pb_inline_icon';
}
if ( ! empty( $args['icon_tablet'] ) ) {
$classes[] = 'et_pb_inline_icon_tablet';
}
if ( ! empty( $args['icon_phone'] ) ) {
$classes[] = 'et_pb_inline_icon_phone';
}
if ( ! empty( $args['icon_sticky'] ) ) {
$classes[] = 'et_pb_inline_icon_sticky';
}
return sprintf(
'<span class="%1$s"%2$s></span>',
et_core_intentionally_unescaped( implode( ' ', $classes ), 'fixed_string' ),
( '' !== $attributes ? ' ' . $attributes : '' )
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
<?php
/**
* Helper class that provides necessary functions for managing Sizing option
*
* Class ET_Builder_Module_Helper_Sizing
*/
abstract class ET_Builder_Module_Helper_Sizing {
/**
* @var string The prefix string that may be added to field name
*/
private $prefix;
/**
* Return raw field name to create the field
*
* @return string
*/
abstract public function get_raw_field();
public function __construct( $prefix = '' ) {
$this->prefix = $prefix;
}
/**
* Returns sizing options fields prefix
*
* @return string
*/
public function get_prefix() {
return $this->prefix;
}
/**
* Returns field name of the sizing option
*
* @return string
*/
public function get_field() {
return $this->get_prefix() . $this->get_raw_field();
}
/**
* Check if the sizing feature option is enabled
*
* @return bool
*/
public function is_enabled() {
return true;
}
/**
* Returns sizing value
*
* @param array $props
* @param string $default
*
* @return string
*/
public function get_value( array $props, $default = '' ) {
return (string) et_()->array_get( $props, $this->get_field(), $default );
}
}

View File

@ -0,0 +1,137 @@
<?php
/**
* Class ET_Builder_Module_Helper_Slider
*/
class ET_Builder_Module_Helper_Slider {
/**
* Returns slider arrows CSS selector
*
* @since 3.25.3
*
* @param string $prefix
*
* @return string
*/
public function get_arrows_selector( $prefix = '%%order_class%%' ) {
return implode(
',',
array(
"$prefix .et-pb-slider-arrows .et-pb-arrow-prev",
"$prefix .et-pb-slider-arrows .et-pb-arrow-next",
)
);
}
/**
* Returns slider dots CSS selector
*
* @since 3.25.3
*
* @param string $prefix
*
* @return string
*/
public function get_dots_selector( $prefix = '%%order_class%%' ) {
return "$prefix .et-pb-controllers a, $prefix .et-pb-controllers .et-pb-active-control";
}
/**
* Reapply (fullwidth) post slider's module background on slide item which has featured image
*
* @since 4.3.3
*
* @param array $args {
* @type int $slide_post_id
* @type string|bool $post_featured_image
* @type string $render_slug
* @type array $props
* }
*/
public static function reapply_module_background_on_slide( $args = array() ) {
$defaults = array(
'slide_post_id' => 0,
'post_featured_image' => false,
'render_slug' => '',
'props' => array(),
);
// Parse argument
$args = wp_parse_args( $args, $defaults );
// Create slide class
$slide_id_class = "et_pb_post_slide-{$args['slide_post_id']}";
// Reapply background color (affecting blend mode), gradient (can be placed on top of image
// creating overlay-effect), and images (gradient is actually image) if:
// 1. Featured image exist on current slide
// 2. Featured image is shown (responsive)
// 3. Featured image placement is placed on background
// 4. Parallax (responsive) is off
// 1. Exit if featured image doesn't exist on current slide
if ( ! $args['post_featured_image'] ) {
return;
}
$props = $args['props'];
// 2. Exit if featured image is not shown
$is_show_image_responsive = et_pb_responsive_options()->is_responsive_enabled( $props, 'show_image' );
$is_featured_image_shown = $is_show_image_responsive ?
in_array( 'on', et_pb_responsive_options()->get_property_values( $props, 'show_image' ) ) :
'on' === et_()->array_get( $props, 'show_image' );
if ( ! $is_featured_image_shown ) {
return;
}
// 3. Exit if feature image is not placed in background
if ( 'background' !== et_()->array_get( $props, 'image_placement' ) ) {
return;
}
// 4. Exit if parallax is activated
$is_parallax_responsive = et_pb_responsive_options()->is_responsive_enabled( $props, 'parallax' );
$is_parallax_active = $is_parallax_responsive ?
in_array( 'on', et_pb_responsive_options()->get_property_values( $props, 'parallax' ) ) :
'on' === et_()->array_get( $props, 'parallax' );
if ( $is_parallax_active ) {
return;
}
// Process background
$props['background_image'] = $args['post_featured_image'];
$props['background_enable_image'] = 'on';
// Background responsive is generally set via background_last_edited instead of each background
// type's *_last_edited; when background's responsive active and no background image is set,
// background-image property will be set to `initial` and featured image on current image got
// removed on current breakpoint. Thus, Set background image responsive attribute on current
// background_image attribute to keep it visible
if ( et_pb_responsive_options()->is_responsive_enabled( $props, 'background' ) ) {
$props['background_image_last_edited'] = '';
$props['background_image_tablet'] = $args['post_featured_image'];
$props['background_image_phone'] = $args['post_featured_image'];
}
if ( et_builder_is_hover_enabled( 'background', $props ) ) {
$props['background_image__hover'] = $args['post_featured_image'];
}
et_pb_background_options()->get_background_style(
array(
'props' => $props,
'selector' => "%%order_class%% .{$slide_id_class}",
'selector_hover' => "%%order_class%%:hover .{$slide_id_class}",
'function_name' => $args['render_slug'],
)
);
return;
}
}

View File

@ -0,0 +1,353 @@
<?php
/**
* Sticky Helper
*
* @package Divi
* @sub-package Builder
* @since 4.6.0
*/
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Sticky Options helper methods
*
* @since 4.6.0
*
* Class ET_Builder_Module_Sticky_Options
*/
class ET_Builder_Module_Helper_Sticky_Options {
/**
* Class instance object
*
* @var object Class instance.
*/
private static $instance;
/**
* Get instance of ET_Builder_Module_Sticky_Options.
*
* @since 4.6.0
*
* @return object|ET_Builder_Module_Sticky_Options
*/
public static function get() {
if ( empty( self::$instance ) ) {
self::$instance = new ET_Builder_Module_Helper_Sticky_Options();
}
return self::$instance;
}
/**
* Get Sticky field suffix
*
* @since 4.6.0
*
* @return string
*/
public function get_suffix() {
return '__sticky';
}
/**
* Get Sticky field enabled suffix
*
* @since 4.6.0
*
* @return string
*/
public function get_enabled_suffix() {
return '__sticky_enabled';
}
/**
* Returns the field original name by removing the `_sticky` or `__sticky_enabled` suffix if it exists.
*
* @since 4.6.0
*
* @param string $name Field name.
*
* @return string
*/
public function get_field_base_name( $name ) {
$regex = "/(.*)({$this->get_suffix()}|{$this->get_enabled_suffix()})$/";
$replace = '${1}';
return preg_replace( $regex, $replace, $name );
}
/**
* Get valid sticky_position which implies module is sticky element
*
* @since 4.6.0
*
* @return array
*/
public function get_valid_sticky_positions() {
return array( 'top', 'bottom', 'top_bottom' );
}
/**
* Check if the setting has enabled sticky options
*
* @since 4.6.0
*
* @param string $setting Field name.
* @param array $attrs Module attributes.
*
* @return bool
*/
public function is_enabled( $setting, $attrs ) {
$name = 'background_color' === $setting ? 'background' : $setting;
$field = $this->get_sticky_enabled_field( $name );
$value = ! empty( $attrs[ $field ] ) ? $attrs[ $field ] : '';
return ! empty( $value ) && strpos( $value, 'on' ) === 0;
}
/**
* Check if current module is inside sticky module
*
* @since 4.6.2
*
* @return bool
*/
public function is_inside_sticky_module() {
global $is_inside_sticky_module;
return $is_inside_sticky_module;
}
/**
* Check if module with given attributes is a sticky module. Need to consider responsive value:
* desktop might have non sticky element value but its smaller breakpoint has sticky element value
*
* @since 4.6.0
*
* @param array $attrs Module attributes.
*
* @return bool
*/
public function is_sticky_module( $attrs ) {
// No nested sticky element.
if ( $this->is_inside_sticky_module() ) {
return false;
}
// Bail if there is fields which its selected value are incompatible to sticky mechanism.
if ( $this->has_incompatible_attrs( $attrs ) ) {
return false;
}
$sticky_position = et_pb_responsive_options()->get_checked_property_value( $attrs, 'sticky_position', 'none', true );
// Non responsive.
if ( is_string( $sticky_position ) ) {
return in_array( $sticky_position, $this->get_valid_sticky_positions(), true );
}
if ( ! is_array( $sticky_position ) ) {
return false;
}
// Responsive.
$is_sticky = false;
foreach ( $sticky_position as $device => $position ) {
if ( in_array( $position, $this->get_valid_sticky_positions(), true ) ) {
$is_sticky = true;
break;
}
}
return $is_sticky;
}
/**
* Returns the field / setting name with sticky suffix
* E.g.: get_sticky_enabled_field('test') => 'test__sticky'
*
* @since 4.6.0
*
* @param string $setting Field name.
*
* @return string
*/
public function get_sticky_field( $setting ) {
return "{$this->get_field_base_name($setting)}{$this->get_suffix()}";
}
/**
* Returns the sticky enabled setting field name
* E.g.: get_sticky_enabled_field('test') => 'test__sticky_enabled'
*
* @since 4.6.0
*
* @param string $setting Field name.
*
* @return string
*/
public function get_sticky_enabled_field( $setting ) {
return "{$this->get_field_base_name($setting)}{$this->get_enabled_suffix()}";
}
/**
* Returns setting value for sticky if enabled, otherwise return the default value
*
* @since 4.6.0
*
* @param string $setting Field name.
* @param array $attrs Module attributes.
* @param mixed $default Default value.
*
* @return mixed
*/
public function get_value( $setting, $attrs, $default = null ) {
return $this->is_enabled( $setting, $attrs )
? $this->get_raw_value( $setting, $attrs, $default )
: $default;
}
/**
* Returns setting sticky value if sticky is enabled for a compose option;
* If it does not exist, return $default specified value
*
* @since 4.6.0
*
* @param string $setting Field name.
* @param string $option Option.
* @param array $attrs Module attributes.
* @param mixed $default Default value.
*
* @return mixed
*/
public function get_compose_value( $setting, $option, $attrs, $default = null ) {
return $this->is_enabled( $option, $attrs )
? $this->get_raw_value( $setting, $attrs, $default )
: $default;
}
/**
* Returns setting sticky raw value;
* If it does not exist, return $default specified value
*
* @since 4.6.0
*
* @param string $setting Field name.
* @param array $attrs Module attributes.
* @param mixed $default Default value.
*
* @return mixed
*/
public function get_raw_value( $setting, $attrs, $default = null ) {
return et_()->array_get( $attrs, $this->get_sticky_field( $setting ), $default );
}
/**
* Adds sticky state selector prefix before given selectors
*
* @since 4.6.0
*
* @param string|array $selector CSS Selector.
* @param bool $is_sticky Whether current module is sticky or not, based on
* `sticky_position` prop value.
* @param bool $return_string Return modified selector as string or not.
*
* @return string
*/
public function add_sticky_to_selectors( $selector, $is_sticky = true, $return_string = true ) {
$selectors = is_array( $selector ) ? $selector : explode( ',', $selector );
$space = $is_sticky ? '' : ' ';
$prefix = ".et_pb_sticky{$space}";
$prefixed_selector = array();
foreach ( $selectors as $selector ) {
$prefixed_selector[] = $prefix . trim( $selector );
}
return $return_string ? implode( ', ', $prefixed_selector ) : $prefixed_selector;
}
/**
* Add sticky state selector prefix to given selector
*
* @since 4.6.0
*
* @param string $selector CSS Selector.
* @param bool $is_sticky whether current module is sticky or not, based on `sticky_position`
* prop value.
*
* @return string
*/
public function add_sticky_to_order_class( $selector, $is_sticky = true ) {
$selectors = explode( ',', $selector );
$selectors = array_map( 'trim', $selectors );
// If current selector is sticky module, sticky selector is directly attached; if it isn't
// it is safe to assume that the sticky selector is one of its parent DOM, hence the space.
if ( $is_sticky ) {
$selectors = preg_replace( '/(%%order_class%%)/i', '.et_pb_sticky$1', $selectors, 1 );
} else {
$selectors = preg_replace( '/(%%order_class%%)/i', '.et_pb_sticky $1', $selectors, 1 );
}
return implode( ', ', $selectors );
}
/**
* Check if given attrs has incompatible attribute value which makes sticky mechanism can't
* be used on current module
*
* @since 4.6.0
*
* @param array $attrs Module attributes.
*
* @return bool
*/
public function has_incompatible_attrs( $attrs = array() ) {
$incompatible = false;
$fields = $this->get_incompatible_fields();
foreach ( $fields as $name => $options ) {
// Get attribute value of current incompatible field from attributes.
$attr = ! empty( $attrs[ $name ] ) ? $attrs[ $name ] : false;
// If the value exist on current incompatible field's options, stop loop and return true.
if ( in_array( $attr, $options, true ) ) {
$incompatible = true;
break;
}
}
return $incompatible;
}
/**
* List of fields and its value which prevent sticky mechanism to work due to how it behaves
*
* @since 4.6.0
*
* @return array
*/
public function get_incompatible_fields() {
return array(
// Position Options.
'positioning' => array( 'absolute', 'fixed' ),
// Motion Effects.
'scroll_vertical_motion_enable' => array( 'on' ),
'scroll_horizontal_motion_enable' => array( 'on' ),
'scroll_fade_enable' => array( 'on' ),
'scroll_scaling_enable' => array( 'on' ),
'scroll_rotating_enable' => array( 'on' ),
'scroll_blur_enable' => array( 'on' ),
);
}
}

View File

@ -0,0 +1,372 @@
<?php
/**
* Style Processor
*
* @package Divi
* @sub-package Builder
* @since 4.6.0
*/
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
// Include dependency for ResponsiveOptions.
if ( ! function_exists( 'et_pb_responsive_options' ) ) {
require_once 'ResponsiveOptions.php';
}
/**
* Icon Font helper methods.
*
* @since 4.6.0
*
* Class ET_Builder_Module_Helper_Style_Processor
*/
class ET_Builder_Module_Helper_Style_Processor {
/**
* Custom `generate_styles()` processor for responsive, hover, and sticky styles of `icon_font_size`
* attributes which sets right property value of font icon in accordion/toggle title.
*
* @since 4.6.0
*
* @used-by ET_Builder_Module_Accordion->render()
* @used-by ET_Builder_Module_Toggle->render()
*
* @param string $selector CSS Selector.
* @param string|array $option_value Option value.
* @param array $args Arguments.
* @param string $option_type Option type (responsive|sticky|hover).
*/
public static function process_toggle_title_icon_font_size( $selector, $option_value, $args, $option_type ) {
$icon_font_size_default = '16px'; // Default toggle icon size.
if ( 'responsive' === $option_type ) {
$icon_font_size_right_values = array();
foreach ( $option_value as $device => $value ) {
$icon_font_size_active = isset( $option_value[ $device ] ) ? $option_value[ $device ] : 0;
if ( ! empty( $icon_font_size_active ) && $icon_font_size_active !== $icon_font_size_default ) {
$icon_font_size_active_int = (int) $icon_font_size_active;
$icon_font_size_active_unit = str_replace( $icon_font_size_active_int, '', $icon_font_size_active );
$icon_font_size_active_diff = (int) $icon_font_size_default - $icon_font_size_active_int;
if ( 0 !== $icon_font_size_active_diff ) {
// 2 is representation of left & right sides.
$icon_font_size_right_values[ $device ] = round( $icon_font_size_active_diff / 2 ) . $icon_font_size_active_unit;
}
}
}
// Icon Font Size.
et_pb_responsive_options()->generate_responsive_css(
$option_value,
$selector,
$args['css_property'],
$args['render_slug']
);
// Right property.
et_pb_responsive_options()->generate_responsive_css(
$icon_font_size_right_values,
$selector,
'right',
$args['render_slug']
);
} elseif ( in_array( $option_type, array( 'sticky', 'hover' ), true ) ) {
$helper = 'sticky' === $option_type ? et_pb_sticky_options() : et_pb_hover_options();
$is_enabled = $helper->is_enabled( $args['base_attr_name'], $args['attrs'] );
if ( $is_enabled && $option_value !== $icon_font_size_default && '' !== $option_value ) {
$icon_font_size_mode_int = (int) $option_value;
$icon_font_size_mode_unit = str_replace( $icon_font_size_mode_int, '', $option_value );
$icon_font_size_mode_diff = (int) $icon_font_size_default - $icon_font_size_mode_int;
// Icon Font Size.
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $selector,
'declaration' => sprintf(
'font-size:%1$s;',
esc_html( $option_value )
),
)
);
if ( 0 !== $icon_font_size_mode_diff ) {
// 2 is representation of left & right sides.
$icon_font_size_right_mode = round( $icon_font_size_mode_diff / 2 ) . $icon_font_size_mode_unit;
// Right property.
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $selector,
'declaration' => sprintf(
'right:%1$s;',
esc_html( $icon_font_size_right_mode )
),
)
);
}
}
}
}
/**
* Prepare and set icon styles(CSS properties `font-family` and `content`)
* for the specified $selector.
*
* @since ?
*
* @param string $icon_value Extended Icon data.
* @param string $render_slug Module slug.
* @param string $selector CSS selector for icon container.
* @param string $render_type If that param equal `icon_font_family_and_content` then CSS propert `content` will be also set.
* @param string $media_query Media query name e.g max_width_767, max_width_980.
* @param bool $important Is CSS decalration should containt `!important`.
*
* @return void.
*/
private static function _set_icon_styles( $icon_value, $render_slug, $selector, $render_type, $media_query, $important ) {
$css_decalration_and_values['font-family:%1$s;'] = et_pb_get_icon_font_family( $icon_value );
$css_decalration_and_values['font-weight:%1$s;'] = et_pb_get_icon_font_weight( $icon_value );
if ( 'icon_font_family_and_content' === $render_type ) {
$css_decalration_and_values['content:%1$s;'] = et_pb_get_extended_icon_value_for_css( $icon_value );
};
foreach ( $css_decalration_and_values as $declaration => $value ) {
ET_Builder_Element::set_style(
$render_slug,
array(
'selector' => $selector,
'declaration' => sprintf(
$declaration,
$value . ( $important ? ' !important' : '' )
),
'media_query' => $media_query,
)
);
}
}
/**
* Custom `generate_styles()` processor for extended icon's css style such as `font-family` or `content`.
*
* @since ?
*
* @param string $selector CSS Selector.
* @param string|array $option_value Option value.
* @param array $args Arguments.
* @param string $option_type Option type (responsive|sticky|hover).
*
* @return void.
*/
public static function process_extended_icon( $selector, $option_value, $args, $option_type ) {
$is_important = ! empty( $args['important'] );
if ( ! empty( $args['utility_arg'] ) && in_array( $args['utility_arg'], array( 'icon_font_family_and_content', 'icon_font_family' ), true ) ) {
$type = $args['utility_arg'];
} else {
return;
}
if ( 'responsive' === $option_type ) {
foreach ( $option_value as $breakpoint => $icon_value ) {
if ( empty( $icon_value ) ) {
continue;
}
$media_query = 'general';
if ( 'tablet' === $breakpoint ) {
$media_query = ET_Builder_Element::get_media_query( 'max_width_980' );
} elseif ( 'phone' === $breakpoint ) {
$media_query = ET_Builder_Element::get_media_query( 'max_width_767' );
}
self::_set_icon_styles( $icon_value, $args['render_slug'], $selector, $type, $media_query, $is_important );
}
} elseif ( in_array( $option_type, array( 'sticky', 'hover' ), true ) ) {
$helper = 'sticky' === $option_type ? et_pb_sticky_options() : et_pb_hover_options();
$is_enabled = $helper->is_enabled( $args['base_attr_name'], $args['attrs'] );
if ( $is_enabled && ! empty( $option_value ) ) {
self::_set_icon_styles( $option_value, $args['render_slug'], $selector, $type, 'general', $is_important );
}
}
}
/**
* Custom `generate_styles()` processor for responsive, hover, and sticky styles of
* `icon_font_size` attributes which sets css properties for social media follow's icon and
* its dimension.
*
* @since 4.6.0
*
* @used-by ET_Builder_Module_Social_Media_Follow->render()
* @used-by ET_Builder_Module_Social_Media_Follow_Item->render()
*
* @param string $selector CSS Selector.
* @param string|array $option_value Option value.
* @param array $args Arguments.
* @param string $option_type Option type (responsive|sticky|hover).
*/
public static function process_social_media_icon_font_size( $selector, $option_value, $args, $option_type ) {
if ( 'responsive' === $option_type ) {
foreach ( $option_value as $font_size_key => $font_size_value ) {
if ( '' === $font_size_value ) {
continue;
}
$media_query = 'general';
if ( 'tablet' === $font_size_key ) {
$media_query = ET_Builder_Element::get_media_query( 'max_width_980' );
} elseif ( 'phone' === $font_size_key ) {
$media_query = ET_Builder_Element::get_media_query( 'max_width_767' );
}
$font_size_value_double = et_builder_multiply_value_has_unit( $font_size_value, 2, 0 );
// Icon.
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $selector,
'declaration' => sprintf(
'font-size:%1$s; line-height:%2$s; height:%2$s; width:%2$s;',
esc_html( $font_size_value ),
esc_html( $font_size_value_double )
),
'media_query' => $media_query,
)
);
// Icon Wrapper.
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $args['selector_wrapper'],
'declaration' => sprintf(
'height:%1$s; width:%1$s;',
esc_html( $font_size_value_double )
),
'media_query' => $media_query,
)
);
}
} elseif ( in_array( $option_type, array( 'sticky', 'hover' ), true ) ) {
$helper = 'sticky' === $option_type ? et_pb_sticky_options() : et_pb_hover_options();
$is_enabled = $helper->is_enabled( $args['base_attr_name'], $args['attrs'] );
if ( $is_enabled && '' !== $option_value ) {
$option_value_double = et_builder_multiply_value_has_unit( $option_value, 2, 0 );
// Selector wrapper isn't default argument so it needs to be turned into sticky /
// hover selector manually here.
$selector_wrapper = 'hover' === $option_type ?
$helper->add_hover_to_selectors( $args['selector_wrapper'] ) :
$helper->add_sticky_to_selectors( $args['selector_wrapper'], $args['is_sticky_module'] );
// Icon.
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $selector,
'declaration' => sprintf(
'font-size:%1$s; line-height:%2$s; height:%2$s; width:%2$s;',
esc_html( $option_value ),
esc_html( $option_value_double )
),
)
);
// Icon Wrapper.
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $selector_wrapper,
'declaration' => sprintf(
'height:%1$s; width:%1$s;',
esc_html( $option_value_double )
),
)
);
}
}
}
/**
* Custom `generate_styles()` processor for responsive, hover, and sticky styles of
* `icon_font_size` attributes which sets the size of overlay icon.
*
* @since 4.6.0
*
* @used-by ET_Builder_Module_Testimonial->render()
* @used-by ET_Builder_Module_Video->render()
* @used-by ET_Builder_Module_Video_Slider->render()
* @used-by ET_Builder_Module_Video_Slider_Item->render()
*
* @param string $selector CSS Selector.
* @param string|array $value Option value.
* @param array $args Arguments.
* @param string $option_type Option type (responsive|sticky|hover).
*/
public static function process_overlay_icon_font_size( $selector, $value, $args, $option_type ) {
$declaration_format = '' !== $args['processor_declaration_format'] ?
$args['processor_declaration_format'] :
'font-size:%1$s; line-height:%1$s; margin-top:-%2$s; margin-left:-%2$s;';
if ( 'responsive' === $option_type ) {
foreach ( $value as $breakpoint => $font_size_value ) {
if ( '' === $font_size_value ) {
continue;
}
$media_query = 'general';
if ( 'tablet' === $breakpoint ) {
$media_query = ET_Builder_Element::get_media_query( 'max_width_980' );
} elseif ( 'phone' === $breakpoint ) {
$media_query = ET_Builder_Element::get_media_query( 'max_width_767' );
}
$font_size_value_half = et_builder_multiply_value_has_unit( $font_size_value, 0.5, 0 );
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $selector,
'declaration' => sprintf(
$declaration_format,
esc_html( $font_size_value ),
esc_html( $font_size_value_half )
),
'media_query' => $media_query,
)
);
}
} elseif ( in_array( $option_type, array( 'sticky', 'hover' ), true ) ) {
$helper = 'sticky' === $option_type ? et_pb_sticky_options() : et_pb_hover_options();
$is_enabled = $helper->is_enabled( $args['base_attr_name'], $args['attrs'] );
if ( $is_enabled && '' !== $value ) {
$value_half = et_builder_multiply_value_has_unit( $value, 0.5, 0 );
ET_Builder_Element::set_style(
$args['render_slug'],
array(
'selector' => $selector,
'declaration' => sprintf(
$declaration_format,
esc_html( $value ),
esc_html( $value_half )
),
)
);
}
}
}
}

View File

@ -0,0 +1,110 @@
<?php if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
/**
* Transition Options helper methods
*
* Class ET_Builder_Module_Transition_Options
*/
class ET_Builder_Module_Helper_Transition_Options {
private static $instance;
public static function get() {
if ( empty( self::$instance ) ) {
self::$instance = new ET_Builder_Module_Helper_Transition_Options();
}
return self::$instance;
}
/**
* Return transition value.
*
* @since 3.23 Add $device param to support responsive settings.
*
* @param string $key
* @param array $list
* @param string $default
* @param string $device
*
* @return void
*/
private function get_value( $key, $list, $default = null, $device = 'desktop' ) {
$value = (string) ET_Core_Data_Utils::instance()->array_get( $list, $key );
if ( 'desktop' !== $device ) {
$responsive = ET_Builder_Module_Helper_ResponsiveOptions::instance();
$is_enabled = $responsive->is_responsive_enabled( $list, $key );
$value = $is_enabled ? $responsive->get_any_value( $list, "{$key}_{$device}", $value, true ) : $value;
}
return '' === $value ? $default : $value;
}
/**
* Returns the module transition duration,
* In case the setting is empty, a default value is returned
*
* @since 3.23 Add $device param to support responsive settings.
*
* @param array $props
* @param string $device
*
* @return string
*/
public function get_duration( $props, $device = 'desktop' ) {
return $this->get_value( 'hover_transition_duration', $props, '300ms', $device );
}
/**
* Returns the module transition speed curve,
* In case the setting is empty, a default value is returned
*
* @since 3.23 Add $device param to support responsive settings.
*
* @param array $props
* @param string $device
*
* @return string
*/
public function get_easing( $props, $device = 'desktop' ) {
return $this->get_value( 'hover_transition_speed_curve', $props, 'ease', $device );
}
/**
* Returns the module transition transition delay,
* In case the setting is empty, a default value is returned
*
* @since 3.23 Add $device param to support responsive settings.
*
* @param array $props
* @param string $device
*
* @return string
*/
public function get_delay( $props, $device = 'desktop' ) {
return $this->get_value( 'hover_transition_delay', $props, '0ms', $device );
}
/**
* Return transition styles.
*
* @since 3.23 Add $device param to support responsive settings.
*
* @param string $property
* @param array $props
* @param string $device
*
* @return string
*/
public function get_style( $property, $props, $device = 'desktop' ) {
$duration = $this->get_duration( $props, $device = 'desktop' );
$easing = $this->get_easing( $props, $device = 'desktop' );
$delay = $this->get_delay( $props, $device = 'desktop' );
return "{$property} {$duration} {$easing} {$delay}";
}
}

View File

@ -0,0 +1,14 @@
<?php
/**
* Helper class that provides necessary functions for managing width option
*
* Class ET_Builder_Module_Helper_Width
*/
class ET_Builder_Module_Helper_Width extends ET_Builder_Module_Helper_Sizing {
public function get_raw_field() {
return 'width';
}
}

View File

@ -0,0 +1,986 @@
<?php
/**
* WooCommerce Module Helper
*
* @package Divi
* @sub-package Builder
* @since 3.29
*/
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access forbidden.' );
}
if ( et_is_woocommerce_plugin_active() ) {
/**
* Class ET_Builder_Module_Helper_Woocommerce_Modules
*
* Shared code between all Woo Modules.
*/
class ET_Builder_Module_Helper_Woocommerce_Modules {
/**
* Returns TRUE if the Product attribute value is valid.
*
* Valid values are Product Ids, `current` and `latest`.
*
* @param string $maybe_product_id
*
* @return bool
*/
public static function is_product_attr_valid( $maybe_product_id ) {
if ( empty( $maybe_product_id ) ) {
return false;
}
if ( absint( $maybe_product_id ) === 0
&& ! in_array( $maybe_product_id, array( 'current', 'latest' ) ) ) {
return false;
}
return true;
}
/**
* Gets the Product Id by the given Product prop value.
*
* @param string $valid_product_attr
*
* @return int
*/
public static function get_product_id_by_prop( $valid_product_attr ) {
if ( ! self::is_product_attr_valid( $valid_product_attr ) ) {
return 0;
}
if ( 'current' === $valid_product_attr ) {
$current_post_id = ET_Builder_Element::get_current_post_id();
if ( et_theme_builder_is_layout_post_type( get_post_type( $current_post_id ) ) ) {
// We want to use the latest product when we are editing a TB layout.
$valid_product_attr = 'latest';
}
}
if ( ! in_array(
$valid_product_attr,
array(
'current',
'latest',
)
) && false === get_post_status( $valid_product_attr ) ) {
$valid_product_attr = 'latest';
}
if ( 'current' === $valid_product_attr ) {
$product_id = ET_Builder_Element::get_current_post_id();
} elseif ( 'latest' === $valid_product_attr ) {
$args = array(
'limit' => 1,
'post_status' => array( 'publish', 'private' ),
'perm' => 'readable',
);
$products = wc_get_products( $args );
if ( ! empty( $products ) ) {
$product_id = $products[0]->get_id();
} else {
return 0;
}
} elseif ( is_numeric( $valid_product_attr ) && 'product' !== get_post_type( $valid_product_attr ) ) {
// There is a condition that $valid_product_attr value passed here is not the product ID.
// For example when you set product breadcrumb as Blurb Title when building layout in TB.
// So we get the most recent product ID in date descending order.
$query = new WC_Product_Query(
array(
'limit' => 1,
'orderby' => 'date',
'order' => 'DESC',
'return' => 'ids',
'status' => array( 'publish' ),
)
);
$products = $query->get_products();
if ( $products && ! empty( $products[0] ) ) {
$product_id = absint( $products[0] );
} else {
$product_id = absint( $valid_product_attr );
}
} else {
$product_id = absint( $valid_product_attr );
}
return $product_id;
}
/**
* Gets the Product (WC_Product) by the value stored in the Product attribute.
*
* @see WC_Product
*
* @param string $maybe_product_id The Value stored in the Product attribute using VB.
*
* @return false|WC_Product
*/
public static function get_product( $maybe_product_id ) {
$product_id = self::get_product_id_by_prop( $maybe_product_id );
/*
* No need to check `wc_get_product()` exists since this Class is defined only when
* WooCommerce is active.
*/
$product = wc_get_product( $product_id );
if ( empty( $product ) ) {
return false;
}
return $product;
}
/**
* Gets the Product ID.
*
* @see WC_Product
*
* @param string $maybe_product_id The Value stored in the Product attribute using VB.
*
* @return int WP_Product ID.
*/
public static function get_product_id( $maybe_product_id ) {
$product = self::get_product( $maybe_product_id );
if ( ! $product ) {
return 0;
}
return $product->get_id();
}
/**
* Get reusable WooCommerce field definition
*
* @since 3.29
*
* @param string $name Field template name.
* @param array $attrs Attribute that need to be inserted into field definition.
* @param array $unset Attribute that need to be removed from field definition.
*
* @return array
*/
public static function get_field( $name, $attrs = array(), $unset = array() ) {
switch ( $name ) {
case 'product':
$field = array(
'label' => esc_html__( 'Product', 'et_builder' ),
'type' => 'select_product',
'option_category' => 'basic_option',
'description' => esc_html__( 'Here you can select the Product.', 'et_builder' ),
'toggle_slug' => 'main_content',
'searchable' => true,
'displayRecent' => false,
'default' => 'current',
'post_type' => 'product',
'computed_affects' => array(
'__product',
),
);
break;
case 'product_filter':
$field = array(
'label' => esc_html__( 'Filter By', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'options' => array(
'newest' => esc_html__( 'Newest', 'et_builder' ),
),
'toggle_slug' => 'main_content',
'description' => esc_html__( 'Here you can filter the Products.', 'et_builder' ),
'default' => 'newest',
'show_if' => array(
'product' => '-1',
),
'computed_affects' => array(
'__product',
),
);
break;
case 'posts_number':
$field = array(
'default' => '12',
'label' => esc_html__( 'Product Count', 'et_builder' ),
'type' => 'text',
'option_category' => 'configuration',
'description' => esc_html__( 'Define the number of products that should be displayed per page.', 'et_builder' ),
'computed_affects' => array(
'__product',
),
'toggle_slug' => 'main_content',
);
break;
case 'columns_number':
$field = array(
'label' => esc_html__( 'Column Layout', 'et_builder' ),
'type' => 'select',
'option_category' => 'layout',
'options' => array(
'6' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '6' ) ),
'5' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '5' ) ),
'4' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '4' ) ),
'3' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '3' ) ),
'2' => sprintf( esc_html__( '%1$s Columns', 'et_builder' ), esc_html( '2' ) ),
'1' => esc_html__( '1 Column', 'et_builder' ),
),
'default' => '0',
'description' => esc_html__( 'Choose how many columns to display.', 'et_builder' ),
'computed_affects' => array(
'__product',
),
'toggle_slug' => 'main_content',
);
break;
case 'orderby':
$field = array(
'label' => esc_html__( 'Order', 'et_builder' ),
'type' => 'select',
'option_category' => 'configuration',
'options' => array(
'default' => esc_html__( 'Default Sorting', 'et_builder' ),
'menu_order' => esc_html__( 'Sort by Menu Order', 'et_builder' ),
'popularity' => esc_html__( 'Sort By Popularity', 'et_builder' ),
'date' => esc_html__( 'Sort By Date: Oldest To Newest', 'et_builder' ),
'date-desc' => esc_html__( 'Sort By Date: Newest To Oldest', 'et_builder' ),
'price' => esc_html__( 'Sort By Price: Low To High', 'et_builder' ),
'price-desc' => esc_html__( 'Sort By Price: High To Low', 'et_builder' ),
),
'default_on_front' => 'default',
'description' => esc_html__( 'Choose how your products should be ordered.', 'et_builder' ),
'computed_affects' => array(
'__shop',
),
'toggle_slug' => 'main_content',
);
break;
default:
$field = array();
break;
}
// Added custom attribute(s).
if ( ! empty( $attrs ) ) {
$field = wp_parse_args( $attrs, $field );
}
// Remove default attribute(s).
if ( ! empty( $unset ) ) {
foreach ( $unset as $unset_attr ) {
unset( $field[ $unset_attr ] );
}
}
return $field;
}
/**
* Gets the Reviews title.
*
* @since 3.29
*
* @param WC_Product $product The Product Post.
*
* @return string
*/
public static function get_reviews_title( $product ) {
$reviews_title = '';
if ( ! ( $product instanceof WC_Product ) ) {
return $reviews_title;
}
$count = $product->get_review_count();
if ( $count ) {
$reviews_title = sprintf(
esc_html( _n( '%1$s review for %2$s', '%1$s reviews for %2$s', $count, 'et_builder' ) ),
esc_html( $count ),
'<span>' . $product->get_title() . '</span>'
);
} else {
$reviews_title = esc_html__( 'Reviews', 'et_builder' );
}
return $reviews_title;
}
/**
* Gets the Reviews comment form.
*
* @since 3.29
*
* @param WC_Product $product The Product Post.
* @param WP_Comment[] $comments Array of Comment objects.
*
* @return string
*/
public static function get_reviews_comment_form( $product, $comments ) {
$has_reviews = empty( $comments ) ? false : true;
ob_start();
?>
<?php
if ( get_option( 'woocommerce_review_rating_verification_required' ) === 'no' ||
wc_customer_bought_product( '', get_current_user_id(), $product->get_id() ) ) :
?>
<div id="review_form_wrapper">
<div id="review_form">
<?php
$commenter = wp_get_current_commenter();
$comment_form = array(
'title_reply' => $has_reviews ? esc_html__( 'Add a review', 'et_builder' ) : sprintf( esc_html__( 'Be the first to review &ldquo;%s&rdquo;', 'woocommerce' ), get_the_title( $product->get_id() ) ),
'title_reply_to' => esc_html__( 'Leave a Reply to %s', 'et_builder' ),
'title_reply_before' => '<span id="reply-title" class="comment-reply-title">',
'title_reply_after' => '</span>',
'comment_notes_after' => '',
'fields' => array(
'author' => '<p class="comment-form-author">' . '<label for="author">' . esc_html__( 'Name', 'woocommerce' ) . '&nbsp;<span class="required">*</span></label> ' .
'<input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30" required /></p>',
'email' => '<p class="comment-form-email"><label for="email">' . esc_html__( 'Email', 'woocommerce' ) . '&nbsp;<span class="required">*</span></label> ' .
'<input id="email" name="email" type="email" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30" required /></p>',
),
'label_submit' => esc_html__( 'Submit', 'et_builder' ),
'submit_button' => '<button name="%1$s" type="submit" id="%2$s" class="et_pb_button %3$s" />%4$s</button>',
'logged_in_as' => '',
'comment_field' => '',
);
if ( $account_page_url = wc_get_page_permalink( 'myaccount' ) ) {
/* translators: %s opening and closing link tags respectively */
$comment_form['must_log_in'] = '<p class="must-log-in">' . sprintf( esc_html__( 'You must be %1$slogged in%2$s to post a review.', 'woocommerce' ), '<a href="' . esc_url( $account_page_url ) . '">', '</a>' ) . '</p>';
}
if ( get_option( 'woocommerce_enable_review_rating' ) === 'yes' ) {
$comment_form['comment_field'] = '<div class="comment-form-rating"><label for="rating">' . esc_html__( 'Your rating', 'et_builder' ) . '</label><select name="rating" id="rating" required>
<option value="">' . esc_html__( 'Rate&hellip;', 'et_builder' ) . '</option>
<option value="5">' . esc_html__( 'Perfect', 'et_builder' ) . '</option>
<option value="4">' . esc_html__( 'Good', 'et_builder' ) . '</option>
<option value="3">' . esc_html__( 'Average', 'et_builder' ) . '</option>
<option value="2">' . esc_html__( 'Not that bad', 'et_builder' ) . '</option>
<option value="1">' . esc_html__( 'Very poor', 'et_builder' ) . '</option>
</select></div>';
}
$comment_form['comment_field'] .= '<p class="comment-form-comment"><label for="comment">' . esc_html__( 'Your review', 'et_builder' ) . '&nbsp;<span class="required">*</span></label><textarea id="comment" name="comment" cols="45" rows="8" required></textarea></p>';
comment_form(
apply_filters( 'woocommerce_product_review_comment_form_args', $comment_form ),
$product->get_id()
);
?>
</div>
</div>
<?php else : ?>
<p class="woocommerce-verification-required"><?php esc_html_e( 'Only logged in customers who have purchased this product may leave a review.', 'woocommerce' ); ?></p>
<?php endif; ?>
<?php
return ob_get_clean();
}
/**
* Gets the formatted weight markup for the given Product Id.
*
* @param int $product_id Product Id.
*
* @return string
*/
public static function get_weight_formatted( $product_id ) {
$product = self::get_product( $product_id );
$markup = '';
if ( ! $product ) {
return $markup;
}
return ( $product->has_weight() ) ? wc_format_weight( $product->get_weight() ) : $markup;
}
/**
* Gets the formatted dimension markup for the given Product Id.
*
* @param int $product_id Product Id.
*
* @return string
*/
public static function get_dimensions_formatted( $product_id ) {
$product = self::get_product( $product_id );
$markup = '';
if ( ! $product ) {
return $markup;
}
return ( $product->has_dimensions() ) ? wc_format_dimensions( $product->get_dimensions( false ) ) : $markup;
}
/**
* Filters the $outer_wrapper_attrs.
* Adds 'data-background-layout' and 'data-background-layout-hover' attributes if needed.
*
* @since 3.29
*
* @param array $outer_wrapper_attrs Key value pairs of outer wrapper attributes.
* @param ET_Builder_Element $this_class Module's class.
*
* @return array filtered $outer_wrapper_attrs.
*/
public static function maybe_add_background_layout_data( $outer_wrapper_attrs, $this_class ) {
$background_layout = et_()->array_get( $this_class->props, 'background_layout', '' );
$background_layout_hover = et_pb_hover_options()->get_value( 'background_layout', $this_class->props, 'light' );
$background_layout_hover_enabled = et_pb_hover_options()->is_enabled( 'background_layout', $this_class->props );
if ( $background_layout_hover_enabled ) {
$outer_wrapper_attrs['data-background-layout'] = esc_attr( $background_layout );
$outer_wrapper_attrs['data-background-layout-hover'] = esc_attr( $background_layout_hover );
}
return $outer_wrapper_attrs;
}
/**
* Processes the Background Layout options for Woocommerce Modules.
* Adds Background Layout related classes.
* Adds Filter for $outer_wrapper_attrs, so required data attributes can be added for specific modules.
*
* @since 3.29
*
* @param string $render_slug Module's render slug.
* @param ET_Builder_Element $this_class Module's class.
*
* @return void.
*/
public static function process_background_layout_data( $render_slug, $this_class ) {
$background_layout = et_()->array_get( $this_class->props, 'background_layout', '' );
$background_layout_values = et_pb_responsive_options()->get_property_values( $this_class->props, 'background_layout' );
$background_layout_tablet = et_()->array_get( $background_layout_values, 'tablet', '' );
$background_layout_phone = et_()->array_get( $background_layout_values, 'phone', '' );
$this_class->add_classname( "et_pb_bg_layout_{$background_layout}" );
if ( ! empty( $background_layout_tablet ) ) {
$this_class->add_classname( "et_pb_bg_layout_{$background_layout_tablet}_tablet" );
}
if ( ! empty( $background_layout_phone ) ) {
$this_class->add_classname( "et_pb_bg_layout_{$background_layout_phone}_phone" );
}
add_filter( "et_builder_module_{$render_slug}_outer_wrapper_attrs", array( 'ET_Builder_Module_Helper_Woocommerce_Modules', 'maybe_add_background_layout_data' ), 10, 2 );
}
/**
* Processes the Button Icon options for Woocommerce Modules.
* Adds et_pb_woo_custom_button_icon class if needed.
* Adds Filter for $outer_wrapper_attrs, so required button icon attributes can be added for specific modules.
*
* @since 3.29
*
* @param string $render_slug Module's render slug.
* @param ET_Builder_Element $this_class Module's class.
*
* @return void.
*/
public static function process_custom_button_icons( $render_slug, $this_class ) {
$button_custom = $this_class->props['custom_button'];
$custom_icon_values = et_()->array_get( $this_class->props, 'button_icon', '' );
// Exit if no custom styles or icons defined for button.
if ( 'on' !== $button_custom || '' === $custom_icon_values ) {
return;
}
$this_class->add_classname( 'et_pb_woo_custom_button_icon' );
add_filter( "et_builder_module_{$render_slug}_outer_wrapper_attrs", array( 'ET_Builder_Module_Helper_Woocommerce_Modules', 'add_custom_button_icons' ), 10, 2 );
}
/**
* Filters the $outer_wrapper_attrs.
* Adds 'data-button-class', 'data-button-icon', 'data-button-icon-tablet' and 'data-button-icon-phone' attributes if needed.
*
* @since 3.29
*
* @param array $outer_wrapper_attrs Key value pairs of outer wrapper attributes.
* @param ET_Builder_Element $this_class Module's class.
*
* @return array filtered $outer_wrapper_attrs.
*/
public static function add_custom_button_icons( $outer_wrapper_attrs, $this_class ) {
$custom_icon_values = et_pb_responsive_options()->get_property_values( $this_class->props, 'button_icon' );
$custom_icon = isset( $custom_icon_values['desktop'] ) ? $custom_icon_values['desktop'] : '';
$custom_icon_tablet = isset( $custom_icon_values['tablet'] ) ? $custom_icon_values['tablet'] : '';
$custom_icon_phone = isset( $custom_icon_values['phone'] ) ? $custom_icon_values['phone'] : '';
if ( '' !== $custom_icon || '' !== $custom_icon_tablet || '' !== $custom_icon_phone ) {
$outer_wrapper_attrs['data-button-class'] = esc_attr( $this_class->get_button_classname() );
$outer_wrapper_attrs['data-button-icon'] = esc_attr( et_pb_process_font_icon( $custom_icon ) );
$outer_wrapper_attrs['data-button-icon-tablet'] = esc_attr( et_pb_process_font_icon( $custom_icon_tablet ) );
$outer_wrapper_attrs['data-button-icon-phone'] = esc_attr( et_pb_process_font_icon( $custom_icon_phone ) );
}
return $outer_wrapper_attrs;
}
/**
* Gets the columns default.
*
* @return string
*/
public static function get_columns_posts_default() {
return array(
'filter',
'et_builder_get_woo_default_columns',
);
}
/**
* Gets the columns default value for the current Product.
*
* @return string
*/
public static function get_columns_posts_default_value() {
$post_id = et_core_page_resource_get_the_ID();
$post_id = $post_id ? $post_id : (int) et_()->array_get( $_POST, 'current_page.id' );
$page_layout = get_post_meta( $post_id, '_et_pb_page_layout', true );
if ( $page_layout && 'et_full_width_page' !== $page_layout && ! ET_Builder_Element::is_theme_builder_layout() ) {
return '3'; // Set to 3 if page has sidebar.
}
/*
* Default number is based on the WooCommerce plugin default value.
*
* @see woocommerce_output_related_products()
*/
return '4';
}
/**
* Gets the Title header tag.
*
* WooCommerce version influences the returned header.
*
* @return string
*/
public static function get_title_header() {
$header = 'h3';
if ( ! et_is_woocommerce_plugin_active() ) {
return $header;
}
global $woocommerce;
if ( version_compare( $woocommerce->version, '3.0.0', '>=' ) ) {
$header = 'h2';
}
return $header;
}
/**
* Gets the Title selector.
*
* WooCommerce changed the title tag from h3 to h2 in v3.0.0
*
* @uses ET_Builder_Module_Helper_Woocommerce_Modules::get_title_header()
*
* @return string
*/
public static function get_title_selector() {
return sprintf( 'li.product %s', self::get_title_header() );
}
/**
* Appends Data Icon attribute to the Outer wrapper.
*
* @param array $outer_wrapper_attrs Key value pairs of outer wrapper attributes.
* @param mixed $this_class Module's class.
*
* @return array
*/
public static function output_data_icon_attrs( $outer_wrapper_attrs, $this_class ) {
$hover_icon = et_()->array_get( $this_class->props, 'hover_icon', '' );
$hover_icon_values = et_pb_responsive_options()->get_property_values( $this_class->props, 'hover_icon' );
$hover_icon_tablet = et_()->array_get( $hover_icon_values, 'tablet', '' );
$hover_icon_phone = et_()->array_get( $hover_icon_values, 'phone', '' );
$hover_icon_sticky = et_pb_sticky_options()->get_value( 'hover_icon', $this_class->props );
$overlay_attributes = ET_Builder_Module_Helper_Overlay::get_attributes(
array(
'icon' => $hover_icon,
'icon_tablet' => $hover_icon_tablet,
'icon_phone' => $hover_icon_phone,
'icon_sticky' => $hover_icon_sticky,
)
);
return array_merge( $outer_wrapper_attrs, $overlay_attributes );
}
/**
* Return all possible product tabs.
* See woocommerce_default_product_tabs() in woocommerce/includes/wc-template-functions.php
*
* @return array
*/
public static function get_default_product_tabs() {
$tabs = array(
'description' => array(
'title' => esc_html__( 'Description', 'et_builder' ),
'priority' => 10,
'callback' => 'woocommerce_product_description_tab',
),
'additional_information' => array(
'title' => esc_html__( 'Additional information', 'et_builder' ),
'priority' => 20,
'callback' => 'woocommerce_product_additional_information_tab',
),
'reviews' => array(
'title' => esc_html__( 'Reviews', 'et_builder' ),
'priority' => 30,
'callback' => 'comments_template',
),
);
// Add custom tabs on default for theme builder.
if ( et_builder_tb_enabled() ) {
et_theme_builder_wc_set_global_objects();
$tabs = apply_filters( 'woocommerce_product_tabs', $tabs );
et_theme_builder_wc_reset_global_objects();
}
return $tabs;
}
public static function get_default_tab_options() {
$tabs = self::get_default_product_tabs();
$options = array();
foreach ( $tabs as $name => $tab ) {
if ( ! isset( $tab['title'] ) ) {
continue;
}
$options[ $name ] = array(
'value' => $name,
'label' => 'reviews' === $name ? esc_html__( 'Reviews', 'et_builder' ) :
esc_html( $tab['title'] ),
);
}
return $options;
}
/**
* Get calculated star rating width based on letter spacing value.
*
* WooCommerce's .star-rating uses `em` based width on float layout;
* any additional width caused by letter-spacing makes the calculation incorrect;
* thus the `width: calc()` overwrite.
*
* @param string $value
*
* @return string
*/
public static function get_rating_width_style( $value ) {
$value = et_builder_process_range_value( $value );
$property_value = 'calc(5.4em + (' . $value . ' * 4))';
return sprintf( 'width: %1$s;', esc_html( $property_value ) );
}
/**
* Get margin properties & values based on current alignment status.
*
* Default star alignment is not controlled by standard text align system. It uses float to control
* how stars symbol will be displayed based on the percentage. It's not possible to convert it to
* simple text align. We have to use margin left & right to set the alignment.
*
* @param string $align
* @param string $mode
*
* @return string
*/
public static function get_rating_alignment_style( $align, $mode = 'desktop' ) {
// Bail early if mode is desktop and alignment is left or justify.
if ( 'desktop' === $mode && in_array( $align, array( 'left', 'justify' ) ) ) {
return array();
}
$margin_properties = array(
'center' => array(
'left' => 'auto',
'right' => 'auto',
),
'right' => array(
'left' => 'auto',
'right' => '0',
),
);
// By default (left or justify), the margin will be left: inherit and right: auto.
$margin_left = et_()->array_get( $margin_properties, "{$align}.left", '0' );
$margin_right = et_()->array_get( $margin_properties, "{$align}.right", 'auto' );
return sprintf(
'margin-left: %1$s !important; margin-right: %2$s !important;',
esc_html( $margin_left ),
esc_html( $margin_right )
);
}
/**
* Get specific star rating style based on property type.
*
* @param string $type
* @param string $value
* @param string $mode
*
* @return array
*/
public static function get_rating_style( $type, $value, $mode = 'desktop' ) {
$style = array();
switch ( $type ) {
case 'rating_letter_spacing':
$style = self::get_rating_width_style( $value );
break;
case 'rating_text_align':
$style = self::get_rating_alignment_style( $value, $mode );
break;
}
return $style;
}
/**
* Set styles for Woo's .star-rating element.
*
* @since 3.29
*
* @param string $render_slug
* @param array $attrs
* @param string $selector
* @param string $hover_selector
*
* @return void
*/
public static function add_star_rating_style( $render_slug, $attrs, $selector = '%%order_class%% .star-rating', $hover_selector = '%%order_class%%:hover .star-rating', $props = array() ) {
// Supported star rating properties will be handled here.
if ( ! is_array( $props ) || empty( $props ) ) {
$props = array( 'rating_letter_spacing', 'rating_text_align' );
}
foreach ( $props as $prop ) {
// Get raw value.
$values = et_pb_responsive_options()->get_property_values( $attrs, $prop );
$hover_value = et_pb_hover_options()->get_value( $prop, $attrs, '' );
$processed_values = array();
// Get specific style value for desktop, tablet, and phone.
foreach ( $values as $device => $value ) {
if ( empty( $value ) ) {
continue;
}
$processed_values[ $device ] = self::get_rating_style( $prop, $value, $device );
}
// Generate style for desktop, tablet, and phone.
et_pb_responsive_options()->declare_responsive_css(
$processed_values,
$selector,
$render_slug
);
// Generate style for hover.
if ( et_builder_is_hover_enabled( $prop, $attrs ) && ! empty( $hover_value ) ) {
ET_Builder_Element::set_style(
$render_slug,
array(
'selector' => $hover_selector,
'declaration' => self::get_rating_style( $prop, $hover_value, 'hover', true ),
)
);
}
}
}
/**
* Get the product default.
*
* @return array
*/
public static function get_product_default() {
return array(
'filter',
'et_builder_get_woo_default_product',
);
}
/**
* Get the product default value for the current post type.
*
* @return string
*/
public static function get_product_default_value() {
$post_id = et_core_page_resource_get_the_ID();
$post_id = $post_id ? $post_id : (int) et_()->array_get( $_POST, 'current_page.id' );
$post_type = get_post_type( $post_id );
if ( 'product' === $post_type || et_theme_builder_is_layout_post_type( $post_type ) ) {
return 'current';
}
return 'latest';
}
/**
* Converts the special chars in to their entities to be used in :before or :after
* pseudo selector content.
*
* @param string $chars
*
* @since 4.0
* @see https://github.com/elegantthemes/Divi/issues/16976
*
* @return string
*/
public static function escape_special_chars( $chars ) {
switch ( trim( $chars ) ) {
case '&':
return '\0026';
case '>':
case '&#8221;>&#8221;':
return '\003e';
default:
return $chars;
}
}
/**
* Gets the WooCommerce Tabs defaults.
*
* Implementation based on
*
* @see https://github.com/elegantthemes/submodule-builder/pull/6568
*
* @since 4.4.2
*
* @return array
*/
public static function get_woo_default_tabs() {
return array(
'filter',
'et_builder_get_woo_default_tabs',
);
}
/**
* Gets the WooCommerce Tabs options for the given Product.
*
* @since 4.4.2
*
* @return string
*/
public static function get_woo_default_tabs_options() {
$maybe_product_id = self::get_product_default_value();
$product_id = self::get_product( $maybe_product_id );
$current_product = wc_get_product( $product_id );
if ( ! $current_product ) {
return '';
}
global $product, $post;
$original_product = $product;
$original_post = $post;
$product = $current_product;
$post = get_post( $product->get_id() );
$tabs = apply_filters( 'woocommerce_product_tabs', array() );
// Reset global $product.
$product = $original_product;
$post = $original_post;
if ( ! empty( $tabs ) ) {
return implode( '|', array_keys( $tabs ) );
}
return '';
}
/**
* Sets the Display type to render only Products.
*
* @since 4.1.0
*
* @see https://github.com/elegantthemes/Divi/issues/17998
*
* @used-by ET_Builder_Module_Woocommerce_Related_Products::render()
* @used-by ET_Builder_Module_Woocommerce_Upsells::render()
*
* @param string $option_name
* @param string $display_type
*
* @return string
*/
public static function set_display_type_to_render_only_products( $option_name, $display_type = '' ) {
$existing_display_type = get_option( $option_name );
update_option( $option_name, $display_type );
return $existing_display_type;
}
/**
* Resets the display type to the existing value.
*
* @since 4.1.0
*
* @see https://github.com/elegantthemes/Divi/issues/17998
*
* @used-by ET_Builder_Module_Woocommerce_Related_Products::render()
* @used-by ET_Builder_Module_Woocommerce_Upsells::render()
*
* @param $option_name
* @param $display_type
*/
public static function reset_display_type( $option_name, $display_type ) {
update_option( $option_name, $display_type );
}
}
add_filter(
'et_builder_get_woo_default_columns',
array(
'ET_Builder_Module_Helper_Woocommerce_Modules',
'get_columns_posts_default_value',
)
);
add_filter(
'et_builder_get_woo_default_product',
array(
'ET_Builder_Module_Helper_Woocommerce_Modules',
'get_product_default_value',
)
);
add_filter(
'et_builder_get_woo_default_tabs',
array(
'ET_Builder_Module_Helper_Woocommerce_Modules',
'get_woo_default_tabs_options',
)
);
}

View File

@ -0,0 +1,24 @@
<?php
class ET_Builder_Module_Helper_Motion_Blur extends ET_Builder_Module_Helper_Motion_Sanitizer {
/**
* @param string $value
*
* @return string
*/
protected function sanitize( $value ) {
$unit = et_pb_get_value_unit( $value );
$unit = in_array( $unit, $this->get_units() ) ? $unit : $this->get_default_unit();
return (float) $value . $unit;
}
protected function get_units() {
return array( 'cm', 'em', 'mm', 'in', 'pc', 'pt', 'px', 'rem' );
}
protected function get_default_unit() {
return 'px';
}
}

View File

@ -0,0 +1,369 @@
<?php
/**
* Motion Helper class.
*
* @package Divi
* @subpackage Builder
*/
/**
* Motion Helpers.
*/
class ET_Builder_Module_Helper_Motion_Motions {
private $START_LIMIT = 0;
private $START_MIDDLE = 1;
private $END_MIDDLE = 2;
private $END_LIMIT = 3;
private $START_VALUE = 4;
private $MIDDLE_VALUE = 5;
private $END_VALUE = 6;
private $LENGTH = 7;
private static $instance;
public static function instance() {
return self::$instance ? self::$instance : ( self::$instance = new self() );
}
/**
* Returns the Motion Effect value. Which is the merge of Saved and Default values.
*
* @param string $value, $default_value
*
* @return string
*/
public function getValue( $saved_value, $default_value ) {
if ( $saved_value === $default_value ) {
return $saved_value;
}
$saved_array = explode( '|', $saved_value );
$default_array = explode( '|', $default_value );
if ( sizeof( $saved_array ) !== sizeof( $default_array ) ) {
return $saved_value;
}
return implode( '|', array_map( array( 'ET_Builder_Module_Helper_Motion_Motions', 'getNonEmpty' ), $saved_array, $default_array ) );
}
/**
* Returns the non-empty value or default.
*
* @param string $value, $default
*
* @return string
*/
public static function getNonEmpty( $value, $default ) {
if ( '' === $value ) {
return $default;
}
return $value;
}
/**
* Returns start limit.
*
* @param string $value
*
* @return int
*/
public function getStartLimit( $value ) {
return $this->get( $this->START_LIMIT, $value );
}
/**
* Set start limit.
*
* If limit:
* - is not a numeric value, return original motionValue
* - is lower then 0, set limit to 0
* - is higher then start middle, set limit equal to start middle
*
* @param int $value
* @param string $multi_value
*
* @return string
*/
public function setStartLimit( $value, $multi_value ) {
$value = $this->to_int( $value, $this->getStartLimit( $multi_value ) );
$ranged = $this->range( 0, $this->getStartMiddle( $multi_value ), $value );
return $this->set( $this->START_LIMIT, $ranged, $multi_value );
}
/**
* Returns start limit.
*
* @param string $value
*
* @return int
*/
public function getEndLimit( $value ) {
return $this->get( $this->END_LIMIT, $value );
}
/**
* Set end limit.
*
* If limit:
* - is not a numeric value, return original motionValue
* - is lower then end middle, set limit equal to end middle
* - is higher then 100, set limit equal to 100
*
* @param int $value
* @param string $multi_value
*
* @return string
*/
public function setEndLimit( $value, $multi_value ) {
$value = $this->to_int( $value, $this->getEndLimit( $multi_value ) );
$ranged = $this->range( $this->getEndMiddle( $multi_value ), 100, $value );
return $this->set( $this->END_LIMIT, $ranged, $multi_value );
}
/**
* Get start middle.
*
* @param $value
*
* @return int
*/
public function getStartMiddle( $value ) {
return $this->get( $this->START_MIDDLE, $value );
}
/**
* Set start middle limit.
*
* If limit:
* - is not a numeric value, return original motionValue
* - is lower then start limit, set limit equal to start limit
* - is higher then end middle, set limit equal to end middle
*
* @param int $value
* @param string $multi_value
*
* @return string
*/
public function setStartMiddle( $value, $multi_value ) {
$value = $this->to_int( $value, $this->getStartMiddle( $multi_value ) );
$ranged = $this->range( $this->getStartLimit( $value ), $this->getEndMiddle( $value ), $value );
return $this->set( $this->START_MIDDLE, $ranged, $multi_value );
}
/**
* Get end middle.
*
* @param $value
*
* @return int
*/
public function getEndMiddle( $value ) {
return $this->get( $this->END_MIDDLE, $value );
}
/**
* Set end middle limit.
*
* If limit:
* - is not a numeric value, return original motionValue
* - is lower then start middle limit, set limit equal to start middle limit
* - is higher then end limit, set limit equal to end limit
*
* @param int $value
* @param string $multi_value
*
* @return string
*/
public function setEndMiddle( $value, $multi_value ) {
$value = $this->to_int( $value, $this->getEndMiddle( $multi_value ) );
$ranged = $this->range( $this->getStartMiddle( $value ), $this->getEndLimit( $value ), $value );
return $this->set( $this->END_MIDDLE, $ranged, $multi_value );
}
/**
* Returns option value for start.
*
* @param string $value
*
* @return string
*/
public function getStartValue( $value ) {
return $this->get( $this->START_VALUE, $value );
}
/**
* Sets option value for start.
*
* @param string $value
* @param string $multi_value
*
* @return string
*/
public function setStartValue( $value, $multi_value ) {
return $this->set( $this->START_VALUE, $value, $multi_value );
}
/**
* Returns option value for middle.
*
* @param string $value
*
* @return string
*/
public function getMiddleValue( $value ) {
return $this->get( $this->MIDDLE_VALUE, $value );
}
/**
* Sets option value for middle.
*
* @param string $value
* @param string $multi_value
*
* @return string
*/
public function setMiddleValue( $value, $multi_value ) {
return $this->set( $this->MIDDLE_VALUE, $value, $multi_value );
}
/**
* Returns option value for end.
*
* @param string $value
*
* @return string
*/
public function getEndValue( $value ) {
return $this->get( $this->END_VALUE, $value );
}
/**
* Sets option value for end.
*
* @param string $value
* @param string $multi_value
*
* @return string
*/
public function setEndValue( $value, $multi_value ) {
return $this->set( $this->END_VALUE, $value, $multi_value );
}
/**
* Same as Multi.merge, but applies the elements parameter
*
* @param string $value_1
* @param string $value_2
*
* @return string
*/
public function merge( $value_1, $value_2 ) {
return $this->to_value( $this->split( $this->multi()->merge( $value_1, $value_2, $this->LENGTH ) ) );
}
/**
* Parses array value and converts it to a valid motion array
* - array length should be 7
* - first 4 values should be numeric values
* - first 4 values should respect 0-100 boundaries
* - first 4 values should be ordered in ascending order
* - last 3 values should be string values
*
* @param array $value
*
* @return array
*/
protected function parse( array $value ) {
$arr = $this->multi()->parse( $value, $this->LENGTH );
$range = array();
$range[ $this->START_LIMIT ] = $this->to_int( array_shift( $arr ), 0 );
$range[ $this->START_MIDDLE ] = $this->to_int( array_shift( $arr ), 50 );
$range[ $this->END_MIDDLE ] = $this->to_int( array_shift( $arr ), 50 );
$range[ $this->END_LIMIT ] = $this->to_int( array_shift( $arr ), 100 );
sort( $range, SORT_NUMERIC );
$range[ $this->START_LIMIT ] = max( $range[ $this->START_LIMIT ], 0 );
$range[ $this->END_LIMIT ] = min( $range[ $this->END_LIMIT ], 100 );
$range[ $this->START_MIDDLE ] = max( $range[ $this->START_MIDDLE ], $range[ $this->START_LIMIT ] );
$range[ $this->END_MIDDLE ] = min( $range[ $this->END_LIMIT ], $range[ $this->END_MIDDLE ] );
return array_merge( $range, $arr );
}
/**
* Converts a value to a valid motion array value.
*
* @param string $value
*
* @return array
*/
protected function split( $value ) {
return $this->parse( $this->multi()->split( $value, $this->LENGTH ) );
}
/**
* Converts a value to a valid motion string value.
*
* @param array $value
*
* @return string
*/
protected function to_value( array $value ) {
return $this->multi()->to_value( $this->parse( $value ), $this->LENGTH );
}
/**
* @return ET_Builder_Module_Helper_Multi_Value
*/
protected function multi() {
return ET_Builder_Module_Helper_Multi_Value::instance();
}
/**
* Returns specific key value
*
* @param int $key
* @param string $value
*
* @return mixed
*/
protected function get( $key, $value ) {
$arr = $this->parse( $this->multi()->split( $value, $this->LENGTH ) );
return $arr[ $key ];
}
/**
* @param int $key
* @param $value
* @param string $multi_value
*
* @return string
*/
protected function set( $key, $value, $multi_value ) {
return $this->multi()->set( $key, $value, $multi_value, $this->LENGTH );
}
private function to_int( $value, $default ) {
return is_numeric( $value ) ? (int) $value : $default;
}
/**
* @param int $min
* @param int $max
* @param int $value
*
* @return int
*/
private function range( $min, $max, $value ) {
return min( $max, max( $min, $value ) );
}
}

View File

@ -0,0 +1,13 @@
<?php
class ET_Builder_Module_Helper_Motion_Opacity extends ET_Builder_Module_Helper_Motion_Sanitizer {
/**
* @param string $value
*
* @return string
*/
protected function sanitize( $value ) {
return min( 100, max( 0, (int) $value ) ) . '%';
}
}

View File

@ -0,0 +1,13 @@
<?php
class ET_Builder_Module_Helper_Motion_Rotate extends ET_Builder_Module_Helper_Motion_Sanitizer {
/**
* @param string $value
*
* @return string
*/
protected function sanitize( $value ) {
return min( 0, (int) $value ) . 'deg';
}
}

View File

@ -0,0 +1,62 @@
<?php
abstract class ET_Builder_Module_Helper_Motion_Sanitizer extends ET_Builder_Module_Helper_Motion {
/**
* @param string $value
*
* @return string
*/
public function getStartValue( $value ) {
return $this->sanitize( parent::getStartValue( $value ) );
}
/**
* @param string $value
* @param string $multi_value
*
* @return string
*/
public function setStartValue( $value, $multi_value ) {
return parent::setStartValue( $this->sanitize( $value ), $multi_value );
}
/**
* @param string $value
*
* @return string
*/
public function getMiddleValue( $value ) {
return $this->sanitize( parent::getMiddleValue( $value ) );
}
/**
* @param string $value
* @param string $multi_value
*
* @return string
*/
public function setMiddleValue( $value, $multi_value ) {
return parent::setMiddleValue( $this->sanitize( $value ), $multi_value );
}
/**
* @param string $value
*
* @return string
*/
public function getEndValue( $value ) {
return $this->sanitize( parent::getEndValue( $value ) );
}
/**
* @param string $value
* @param string $multi_value
*
* @return string
*/
public function setEndValue( $value, $multi_value ) {
return parent::setEndValue( $this->sanitize( $value ), $multi_value );
}
abstract protected function sanitize( $value );
}

View File

@ -0,0 +1,13 @@
<?php
class ET_Builder_Module_Helper_Motion_Scale extends ET_Builder_Module_Helper_Motion_Sanitizer {
/**
* @param string $value
*
* @return string
*/
protected function sanitize( $value ) {
return min( 0, (int) $value );
}
}

View File

@ -0,0 +1,13 @@
<?php
class ET_Builder_Module_Helper_Motion_Translate extends ET_Builder_Module_Helper_Motion_Sanitizer {
/**
* @param string $value
*
* @return string
*/
protected function sanitize( $value ) {
return (float) $value;
}
}